<正则吃饺子> :关于微信支付的简单总结说明(一)
关于支付,一直想参与开发,现在根据项目中已有及参见的微信开发文档,将自己对于微信开发的流程进行简单的总结,以备后用和帮助后来者。
一、相关官方文档
微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
二、参考博文
(待添加,等我找找..)
三、自己参看文档时候的简单文档整理,
ps:只为了方便自己记忆和联想
四、根据官方文档中,标记后台主要做的工作流程
五、参看项目代码时候,整理的demo,来源于网络
(1)MD5Util ---- 生成签名时候使用
package com.weixin.test; import java.security.MessageDigest; /**
* 微信测试MD5
* @author Administrator
*
*/
public class MD5Util { private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i])); return resultSb.toString();
} private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
} public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
} private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
(2)PayTest
package com.weixin.test; import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap; public class PayTest { // http://mch.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3
private static String Key = "192006250b4c09247ec02edce69f6a2d"; /**
* @param args
*/
public static void main(String[] args) {
System.out.println(">>>模拟微信支付<<<");
System.out.println("==========华丽的分隔符==========");
// 微信api提供的参数
String appid = "wxd930ea5d5a258f4f";
String mch_id = "10000100";
String device_info = "1000";
String body = "test";
String nonce_str = "ibuaiVcKdpRxkhJA"; SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
parameters.put("appid", appid);
parameters.put("mch_id", mch_id);
parameters.put("device_info", device_info);
parameters.put("body", body);
parameters.put("nonce_str", nonce_str); String characterEncoding = "UTF-8";
String weixinApiSign = "9A0A8659F005D6984697E2CA0A9CF3B7";
System.out.println("微信的签名是:" + weixinApiSign);
String mySign = createSign(characterEncoding, parameters);
System.out.println("我 的签名是:" + mySign); if (weixinApiSign.equals(mySign)) {
System.out.println("恭喜你成功了~");
} else {
System.out.println("不行啊,再接再厉~");
} String userAgent = "Mozilla/5.0(iphone;CPU iphone OS 5_1_1 like Mac OS X) AppleWebKit/534.46(KHTML,like Geocko) Mobile/9B206 MicroMessenger/5.0"; char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger") + 15); System.out.println("微信的版本号:" + new String(new char[] { agent }));
} /**
* 微信支付签名算法sign
* @param characterEncoding
* @param parameters
* @return
*/
@SuppressWarnings("unchecked")
public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + Key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
} }
六、结合项目,微信支付流程解析
主要为微信支付官方文档提供的流程中,对应于 4、5、6、10、11
(1)网页内请求生成支付订单。主要工作就是前端(app)在将商品添加到购物车后下单购买或者立即购买时,在后台系统中所生成的一个未支付订单。
下面只是项目中一个业务处理,可以忽略,仅做参考和方便记忆。
/**
* 生成订单
*
* @param order
* @param baseUser
* @return
*/
@Override
public Response<String> createRechargeOrder(RechargeOrder order, BaseUser baseUser) {
Response<String> response = new Response<>(); try {
if (baseUser == null) {
throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName());
}
if (order == null) {
throw new ArgumentException(ResultCode.ERROR_ORDER_SAVE_NULL.getName());
}
if (!checkAmount(order.getActuralAmount(), order.getRechargeAmount())) {
throw new ArgumentException("支付金额异常");
} order.setCustomerName(baseUser.getName());
order.setCustomerId(baseUser.getId());
if("customer".equals(baseUser.getType())){
Response<MemberCustomerUser> res = customerService.getCustomerById(baseUser.getId());
if (null != res && res.isSuccess()) {
order.setCustomerPhone(res.getResult().getMobilePhone());
}
}else if("shop".equals(baseUser.getType())){
MemberShopUser user = new MemberShopUser();
user.setId(baseUser.getId());
Response<MemberShopUser> res = terminalShopService.getShopUserById(user);
if (null != res && res.isSuccess()) {
order.setCustomerPhone(res.getResult().getMobilePhone());
}
}
if (StringUtils.isBlank(order.getSource())) {
order.setSource("");
}
//订单状态:01未支付
order.setOrderCode("10");
order.setOrderName("未支付");
//付款状态:待付款
order.setPayStatus("0");
rechargeOrderManager.createRechargeOrder(order);
//记录订单状态日志
if (order.getOrderId() != null && !"".equals(order.getOrderId())) {
ConvenienceOrderLog orderLog = new ConvenienceOrderLog();
orderLog.setOrderId(String.valueOf(order.getOrderId()));
orderLog.setStatusCode(10);
orderLog.setStatusName("未支付");
orderLog.setStatusUserId(baseUser.id);
orderLog.setStatusUser(baseUser.name);
rechargeOrderManager.saveConvenienceOrderLog(orderLog);
}
response.setResult(String.valueOf(order.getOrderId()));
} catch (ArgumentException a) {
//参数异常不写log日志
response.setError(a.getMessage());
} catch (BusinessException b) {
response.setError(b.getMessage());
log.error("保存订单异常!原因:{}", Throwables.getStackTraceAsString(b));
} catch (Exception e) {
response.setError(e.getMessage());
log.error("保存订单异常!原因:{}", Throwables.getStackTraceAsString(e));
}
return response;
}
(2)调用统一下单api,交易请求,获取微信后台返回的交易支付预付单,对返回信息处理后,返回给前端(app)
/**
* 微信支付 交易请求,生成预付单 -====== 公众号
*
* @param orderId
* @param baseUser
* @return
*/
@Override
public Response<Map<String, String>> weChatPayRequest(String orderId, String openId, BaseUser baseUser) {
Response<Map<String, String>> response = new Response<>();
Map<String, String> map = new HashMap<>();
try {
if (Strings.isNullOrEmpty(orderId)) {
throw new ArgumentException("交易编号为空");
}
if (Strings.isNullOrEmpty(openId)) {
throw new ArgumentException("openId为空");
}
if (baseUser == null) {
throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName());
}
Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
int amount = 0;
if (res.isSuccess() && res.getResult() != null) {
amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
} else {
throw new BusinessException(res.getError());
}
//生成请求参数XML--公众号
String xml = this.createWeChatPayXml(orderId, openId, amount);
//微信支付统一下单接口
String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
//发起请求
String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "");
log.error(result);
//解析返回结果,重新处理以发给前端(app)
Document doc = DocumentHelper.parseText(result);
Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
map.put("appid", (String) resultMap.get("appid"));
map.put("mch_id", (String) resultMap.get("mch_id"));
map.put("prepay_id", (String) resultMap.get("prepay_id"));
map.put("package", "prepay_id=" + resultMap.get("prepay_id"));
Long l = new Date().getTime() / 1000;
Integer timestamp = Integer.parseInt(l.toString());
map.put("timestamp", timestamp.toString());
map.put("nonceStr", "aaronlovem" + timestamp.toString());
// createPaySignStr() ---- 生成支付签名-公众号,注意 :需要跟发起支付时候的,生成签名的参数规则一致。总之,需要跟前端提交时候,微信生成的签名一致。
String paySign = createPaySignStr(map.get("nonceStr").toString(), map.get("package"), map.get("timestamp"));
map.put("paySign", paySign);
map.put("openId", openId);
response.setResult(map);
} else {
response.setSuccess(false);
response.setError((String) resultMap.get("return_msg"));
}
} catch (ArgumentException a) {
response.setError(a.getMessage());
} catch (BusinessException b) {
response.setError(b.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
} catch (RuntimeException c) {
response.setError(c.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
} catch (Exception e) {
response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
}
//返回给前端,微信支付订单信息
return response;
}
一个生成签名的方式,还有其他方式,仅做记录,如下:
/**
* 生成支付签名-公众号
*
* @return
*/
public String createPaySignStr(String nonceStr, String packageStr, String timestamp) throws Exception {
String[] arr = new String[5];
arr[0] = "appId=suwxb1d7b09dsaf81c92eb";
arr[1] = "nonceStr=" + nonceStr;
arr[2] = "package=" + packageStr;
arr[3] = "signType=MD5";
arr[4] = "timeStamp=" + timestamp;
Arrays.sort(arr);
String param = "";
for (int i = 0; i < arr.length; i++) {
param += arr[i] + "&";
}
param += "key=我是key呵呵呵呵呵";
log.error("微信支付请求参数:" + param);
return MD5Util.MD5Encode(param, null).toUpperCase();
} /**
* 生成支付签名-B端、C端
*
* @param noncestr
* @param packagestr
* @param prepayid
* @param timestamp
* @return
*/
public String createWeChatGoPayXmlForAPP(String noncestr, String packagestr, String prepayid, String timestamp, String type) {
String appId = "";
String partnerId = "";
String key = "";
if ("B2C".equals(type)) {
appId = "suwxb1d7bf09af81c92eb";
partnerId = "1322218523201";
key = "我是key";
} else if ("B2B".equals(type)) {
appId = "suwxb1d7b09af81c92eb";
partnerId = "1352713d383902";
key = "我是key啊";
}
String[] arr = new String[6];
arr[0] = "appid=" + appId;
arr[1] = "noncestr=" + noncestr;
arr[2] = "package=" + packagestr;
arr[3] = "partnerid=" + partnerId;
arr[4] = "prepayid=" + prepayid;
arr[5] = "timestamp=" + timestamp;
Arrays.sort(arr);
String param = "";
for (int i = 0; i < arr.length; i++) {
param += arr[i] + "&";
}
param += "key=" + key;
log.error("微信支付请求参数:" + param);
return MD5Util.MD5Encode(param, null).toUpperCase();
}
b、c的交易请求,app支付:大体与公众号支付类似,参数不一致
/**
* 微信支付 交易请求,生成预付单,C端
*
* @param orderId
* @param baseUser
* @return
*/
@Override
public Response<Map<String, String>> weChatPayRequestForC(String orderId, BaseUser baseUser) {
Response<Map<String, String>> response = new Response<Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
try {
if (Strings.isNullOrEmpty(orderId)) {
throw new ArgumentException("交易编号为空");
}
Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
int amount = 0;
if (res.isSuccess() && res.getResult() != null) {
amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
} else {
throw new BusinessException(res.getError());
}
String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2C");
String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "");
log.error(result);
Document doc = DocumentHelper.parseText(result);
Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
Map<String, String> param = new HashMap<String, String>();
param.put("appid", (String) resultMap.get("appid"));
param.put("partnerid", (String) resultMap.get("mch_id"));
param.put("prepayid", (String) resultMap.get("prepay_id"));
param.put("package", "Sign=WXPay");
Long l = new Date().getTime() / 1000;
Integer timestamp = Integer.parseInt(l.toString());
param.put("noncestr", "xxxxxB2Capp" + timestamp.toString());
param.put("timestamp", timestamp.toString());
param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2C"));
response.setResult(param);
} else {
response.setSuccess(false);
response.setError((String) resultMap.get("return_msg"));
}
} catch (ArgumentException a) {
response.setError(a.getMessage());
} catch (BusinessException b) {
response.setError(b.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
} catch (RuntimeException c) {
response.setError(c.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
} catch (Exception e) {
response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
}
return response;
} @Override
public Response<Map<String, String>> weChatPayRequestForB(String orderId, BaseUser baseUser) {
Response<Map<String, String>> response = new Response<Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
try {
if (Strings.isNullOrEmpty(orderId)) {
throw new ArgumentException("交易编号为空");
}
Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
int amount = 0;
if (res.isSuccess() && res.getResult() != null) {
amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
} else {
throw new BusinessException(res.getError());
}
String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2B");
String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
result = result.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "");
log.error(result);
Document doc = DocumentHelper.parseText(result);
Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
Map<String, String> param = new HashMap<String, String>();
param.put("appid", (String) resultMap.get("appid"));
param.put("partnerid", (String) resultMap.get("mch_id"));
param.put("prepayid", (String) resultMap.get("prepay_id"));
param.put("package", "Sign=WXPay");
Long l = new Date().getTime() / 1000;
Integer timestamp = Integer.parseInt(l.toString());
param.put("noncestr", "xxxxB2Bapp" + timestamp.toString());
param.put("timestamp", timestamp.toString());
param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2B"));
response.setResult(param);
} else {
response.setSuccess(false);
response.setError((String) resultMap.get("return_msg"));
}
} catch (ArgumentException a) {
response.setError(a.getMessage());
} catch (BusinessException b) {
response.setError(b.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
} catch (RuntimeException c) {
response.setError(c.getMessage());
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
} catch (Exception e) {
response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
}
return response;
}
(3)告知微信处理通知支付结果。此为,我们在后台发起微信支付请求生成预订单时候,传入的回调函数的方法。微信后台在处理完微信支付结果后,会发起请求,调用我们已经准备好的回调函数地址。
回调函数中,可以具体处理下我们商户系统中的一些业务,比如更新订单状态,提醒消息等等.....处理完成,返回微信后台处理结果。
对应于微信支付下单api接口参数中的,如下:
/**
* 微信支付回调函数
*
* @param weChatPayReturn
* @return
*/
@Override
public String getWeChatPayReturnForOrder(WeChatPayReturn weChatPayReturn) {
log.error("微信普通订单回调,参数为:" + weChatPayReturn.toString());
System.out.println("微信普通订单回调,参数为:" + weChatPayReturn.toString());
boolean flag = false;
try {
if (null != weChatPayReturn) {
//保存微信回调信息
rechargeOrderManager.saveRechargeOrderWeChatPayInfo(weChatPayReturn);
if (true) {
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
flag = rechargeOrderManager.getWeChatPayReturnForOrder(weChatPayReturn);
if (flag) {
String successStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
return successStr;
} else {
return "fail";
}
}
} else {
throw new ArgumentException("微信回调对象为空!");
}
} catch (ArgumentException a) {
//参数异常不写log日志
log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(a));
} catch (BusinessException b) {
log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(b));
} catch (Exception e) {
log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(e));
}
return "fail";
}
注意:文章中所列出的代码不一定是最好的,只提供流程思路,方便理解和记忆。具体的实现方式,自己把握。比如,在生成支付签名时候,有多种方式来实现。
<正则吃饺子> :关于微信支付的简单总结说明(一)的更多相关文章
- <正则吃饺子>:关于集合的简单整理总结
项目中用到的集合不可谓不多,对于自己的一次面试,要求说下自己用过的集合,自己开始说的并不系统也不完整,一直耿耿于怀,特整理一下,以备后期之用和帮助后来者. package com.love.malin ...
- <正则吃饺子>:关于java中垃圾回收技术的简单学习总结
知识介绍来自网络,后面会根据继续学习进行补充和适当的修改,谢谢!原文地址:http://www.importnew.com/26821.html#comment-578355 java中的垃圾回收机制 ...
- <正则吃饺子> :关于微信支付的简单总结说明(二)
关于微信退款 一.官方文档 申请退款:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6 二.退款流程 ...
- <正则吃饺子>:关于java中对内存部分的简单总结整理
在项目和一些群讨论中,经常看到对内存的处理,但是,自己确是一知半解的,基于此,就把这部分的知识简单的整理了下,知识点来源于网络博文,也一一标明出处,谢谢. package com.love.malin ...
- <正则吃饺子> :关于 Matcher 的 replaceAll 的简单使用
在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh replaceAll public String replaceAll(String ...
- <正则吃饺子> :关于oracle 中 with的简单使用
oracle中 with的简单使用介绍,具体可以参见其他的博文介绍,在这里只是简单的介绍: with 构建了一个临时表,类似于存储过程中的游标,我是这么理解的. 一.数据准备: select * fr ...
- <正则吃饺子> :关于oracle 中 exists 、not exists 的简单使用
话不多说,简单的总结而已.网络上很多很详细介绍. 例如,博文:http://blog.csdn.net/zhiweianran/article/details/7868894 当然这篇也是转载的,原 ...
- <正则吃饺子> :关于Guava中 Joiner 和 Splitter 的简单使用
在现在项目中经常看到 这两个类的使用,开始时候不明白具体是做的什么事情,就单独拿出来学习下了,参照了网上的博文,这里主要是简单的讲讲用法. 具体对这两个类,不做过多介绍,有个在线文档,需要的可以自己去 ...
- <正则吃饺子> :关于Collections中 比较器的简单使用
在线文档地址: http://tool.oschina.net/apidocs/apidoc?api=jdk-zh sort public static <T extends Comparabl ...
随机推荐
- PowerBuilder -- 键盘对应的枚举值
KeyCode values for keyboard keys Type of key KeyCode values and descriptions Mouse buttons KeyLeftBu ...
- 在diy的文件系统上创建文件的流程
[0]README 0.1) source code are from orange's implemention of a os , and for complete code , please v ...
- 研究下JavaScript中的Rest參数和參数默认值
研究下JavaScript中的Rest參数和參数默认值 本文将讨论使 JavaScript 函数更有表现力的两个特性:Rest 參数和參数默认值. Rest 參数 通常,我们须要创建一个可变參数的函数 ...
- Go 语言中的数组是一种 值类型(不像 C/C++ 中是指向首元素的指针)
the-way-to-go_ZH_CN/07.1.md at master · Unknwon/the-way-to-go_ZH_CN https://github.com/Unknwon/the-w ...
- css多余字符显示省略号
width:300px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; ;
- mybatis 运算符转义收录
在ibatis配置文件写SQL语句的时候对于一些比如“<”,">","<>","&"," ' &q ...
- 我的Java开发学习之旅------>Java经典排序算法之二分插入排序
一.折半插入排序(二分插入排序) 将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法.在处理A[i]时,A[0]--A[i-1]已经按关键码值排好序.所谓折半比较, ...
- 破解powerdesigner教程
点Tool
- python get post模拟请求
1.使用get方式时.url相似例如以下格式: &op=bind GET报问头例如以下: &n=asa HTTP/1.1 Accept: */* Accept-Lang ...
- java ClassLoader类加载器
原文 首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java ...