微信app支付
https://blog.csdn.net/Zhihua_W/article/details/80606089
出于兴趣写了一个纯支付的模块,有兴趣的同学可以去看看(戳我)
———————————分隔线———————————–
微信APP支付大致的流程和支付宝APP支付有很大不同(想了解支付宝APP支付的同学点这里),其中略坑的一点就是MD5加密的方法需要自己写,好在微信官方给出了测试地址。
前置准备
微信的东西比支付宝要复杂一点,要先去注册开发者账号,然后一系列巴拉巴拉……,然后得到下面列出的我们开发需要的参数,下面的参数中TOKEN和CERT_PATH是需要退款和设计到其他功能才需要的,如果只做APP支付功能的同学可以忽略。
APP_ID : 服务号的应用ID
APP_SECRET : 服务号的应用密钥
TOKEN : 服务号的配置token
MCH_ID : 商户号
API_KEY : API密钥
SIGN_TYPE : 签名加密方式
CERT_PATH : 微信支付证书名称、
支付流程
具体的流程大概就是后台拿到(或生成)自己的订单号后,拿着一堆参数按ASCII码(即abc…)排序后用MD5加密后调用微信的服务器地址拿到一个prepayId,再将prepayId和当前的参数再次排序和加密(二次加密)生成sign后返回前端进行支付,支付完成后,微信的后台会回调我们的后台接口。
Java代码
maven依赖
`<!-- 微信支付依赖 -->
<dependency>
<groupId>org.xmlpull</groupId>
<artifactId>xmlpull</artifactId>
<version>1.1.3.1 </version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.ning/async-http-client -->
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.8.13</version>
</dependency>`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Java代码
@ResponseBody
@RequestMapping(value="/wxpaySign",method=RequestMethod.POST)
public JsonResult getWXPaySign(double totalAmount,String userId,String out_trade_no,String ip)
throws JDOMException, IOException{
//预备参数
WXPayDTO wxPayDTO = new WXPayDTO();
String subject = "******";
String body = "******";
String nonceStr = setNonceStr();
long timeStamp = (long)CommonUtil.getSecondTimestamp(new Date());
String packageStr = "Sign=WXPay";
String total_fee = String.valueOf(CommonUtil.double100ToInt(totalAmount));
SortedMap<String,String> payMap = genOrderData(PayConstants.WX_APP_ID,body,out_trade_no,
nonceStr,packageStr,PayConstants.WX_PARTNER_ID,total_fee,subject,ip);
//设置WXPayDTO
wxPayDTO.setAppId(PayConstants.WX_APP_ID);
//商家id
wxPayDTO.setPartnerId(PayConstants.WX_PARTNER_ID);
wxPayDTO.setPrepayId(out_trade_no); //订单
wxPayDTO.setNonceStr(nonceStr); //设置随机串
wxPayDTO.setTimeStamp(timeStamp); //设置时间戳
wxPayDTO.setPackageStr(packageStr);
wxPayDTO.setSign(payMap.get("sign"));
wxPayDTO.setPrepayId(payMap.get("prepayid"));
return new JsonResult(Message.M2000,wxPayDTO);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private SortedMap<String, String> genOrderData(String appId, String body, String out_trade_no,
String nonceStr, String packageStr, String partnerId,String total_fee,String subject,String ip)
throws JDOMException, IOException {
SortedMap<String, String> paraMap = new TreeMap<String,String>();
//按照官方文档的要求,这里参数名ASCII码从小到大排序(字典序);
paraMap.put("appid", appId);
paraMap.put("body",body);
paraMap.put("mch_id",partnerId); //微信商户账号
paraMap.put("nonce_str",nonceStr); //32位不重复的编号
paraMap.put("notify_url",
PayConstants.WX_Notify_Url); //设置一个回调的地址
paraMap.put("out_trade_no", out_trade_no); //订单号
paraMap.put("spbill_create_ip", ip);//前端传回来的ip
paraMap.put("total_fee",total_fee); //设置金额
paraMap.put("trade_type","APP"); //方式为APP支付
//用上面封装的参数生成sign
String sign = PayCommonUtil.createSign("UTF-8", paraMap,PayConstants.WX_API_KEY);
//这里的sign用二次签名生成的sign
paraMap.put("sign",sign);
//统一下单地址
//将上面集合内容转成xml
String xml = getRequestXML(paraMap);
String xmlStr = HttpUtil.postData(PayConstants.WX_Notify_Url,xml);
//将返回的xml转换成map
Map map = XMLUtil.doXMLParse(xmlStr);
//预付商品id
String prepay_id = (String) map.get("prepay_id");
//这里要对参数进行二次签名
SortedMap<String, String> secParaMap = new TreeMap<String,String>();
secParaMap.put("appid", appId);
secParaMap.put("partnerid",partnerId);
secParaMap.put("prepayid", prepay_id);
//这里不确定是不是要重新生成一个随机串,再改
secParaMap.put("noncestr", nonceStr);
secParaMap.put("timestamp", CommonUtil.getTimeStamp());
secParaMap.put("package", "Sign=WXPay");
String secSign = PayCommonUtil.createSign("UTF-8", secParaMap, PayConstants.WX_API_KEY);
secParaMap.put("sign", secSign);
secParaMap.put("prepayid", prepay_id);
return secParaMap;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
支付回调
微信的支付回调是用流形式传输的,所以要先对流形式的数据进行解析再得到我们需要的东西。
@RequestMapping(value = "payNotifyUrl", produces = "application/json;charset=UTF-8")
@ResponseBody
public String payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
//读取参数
InputStream inputStream ;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s ;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> m = new HashMap<String, String>();
m = XMLUtil.doXMLParse(sb.toString());
//过滤空 设置 TreeMap
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = m.get(parameter);
String v = "";
if(null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
//判断签名是否正确
if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,PayConstants.WX_API_KEY)) {
//------------------------------
//处理业务开始
//------------------------------
String resXml = "";
if("SUCCESS".equals((String)packageParams.get("result_code"))){
// 这里是支付成功
//////////执行自己的业务逻辑////////////////
String mch_id = (String)packageParams.get("mch_id");
String openid = (String)packageParams.get("openid");
String is_subscribe = (String)packageParams.get("is_subscribe");
String out_trade_no = (String)packageParams.get("out_trade_no");
String total_fee = (String)packageParams.get("total_fee");
System.out.println("mch_id:"+mch_id);
System.out.println("openid:"+openid);
logger.info("is_subscribe:"+is_subscribe);
logger.info("out_trade_no:"+out_trade_no);
logger.info("total_fee:"+total_fee);
//////////执行自己的业务逻辑////////////////
logger.info("支付成功");
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
//------------------------------
//处理业务完毕
//------------------------------
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else{
logger.info("通知签名验证失败");
}
return "SUCCESS";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
代码中包含一些常用的工具类,是从别人的资源来下载来的,觉得很好用。
---------------------
作者:J_Howie
来源:CSDN
原文:https://blog.csdn.net/Dream__Snow/article/details/79098630
版权声明:本文为博主原创文章,转载请附上博文链接!