最近做了一个微信支付的功能,整理下思路(主要是校验逻辑):
先上图微信H5支付的官方流程图:
自己梳理了下流程:
① 用户请求支付---->② 前端页面请求后台接口---->③ 后台处理订单逻辑(请求微信支付校验接口,成功后返回相应的校验数据)---->④ 后台将校验数据返回给前端页面---->⑤ 前端页面封装数据调用微信支付接口---->⑥ 微信将结果回调到前端请求中配置的回调接口---->⑦ 回调接口接收到数据进行再次校验并处理支付后的逻辑
第③步 所校验的参数:
// 随机字符串
parameters.put("nonce_str", RandomUtil.generateRandomCharAndNumber(10));
// 商品名称
parameters.put("body", orderName);
// 商户系统内部的订单号(自己生成的订单号)
String out_trade_no = orderId;
parameters.put("out_trade_no", out_trade_no);
// 订单金额以分为单位,只能为整数
parameters.put("total_fee", total);
// 客户端本地ip
parameters.put("spbill_create_ip", request.getRemoteAddr());
// 支付通知异步回调地址
parameters.put("notify_url", P_NOTIFY_URL);
// 交易类型 source:H5和小程序 都是“JSAPI”,
parameters.put("trade_type", source);
//商家数据包attach(区分来源渠道) 该字段可带参数过去 回调的时候会原样返回 回调可通过该字段进行数据校验并且处理
parameters.put("attach", attach);
// MD5进行签名,必须为UTF-8编码,注意上面几个参数名称的大小写
String sign = createSign("UTF-8", parameters , apikey);
parameters.put("sign", sign);
// 生成xml结构的数据,用于统一下单接口的请求
String requestXML = getRequestXml(parameters);
log.info("统一下单接口请求数据requestXML={}", requestXML);
/** 开始请求统一下单接口,获取预支付prepay_id */
String result = HttpUtil.doPost(UNI_URL, requestXML);
第④步 将返回的参数进行拆分 返回给前端:
// 解析微信返回的信息,以Map形式存储便于取值
Map
String prepayId = map.get("prepay_id");
获取预支付prepay_id之后,需要再次进行签名,参数有:appId,partnerId,prepayId,nonceStr,timeStamp,
package
SortedMap
params.put("package", "prepay_id=" + prepayId);
params.put("signType", "MD5");
sign = createSign("UTF-8", params, apikey);
params.put("sign", sign);
params.put("partnerid", ModuleConstants.P_MCH_ID);
params.put("prepayid", prepayId);
将params 返回给前端 前端根据这个返回的参数 发起支付调用
后面就是根据回调在回调接口里面接收 返回的数据进行内部业务逻辑处理之后的业务
附上 生成签名sign方法:
/**
* 签名
*
* @param characterEncoding
* @param parameters
* @return
*/
public static String createSign(String characterEncoding, SortedMap
StringBuffer sb = new StringBuffer();
Set
Iterator
while (it.hasNext()) {
Map.Entry
String k = (String) entry.getKey();
Object v = entry.getValue();
/** 如果参数为key或者sign,则不参与加密签名 */
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
/** 支付密钥必须参与加密,放在字符串最后面 */
sb.append("key=" + apikey);
log.info("签名原始字符串:{}", sb);
/** 记得最后一定要转换为大写 */
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
log.info("签名后的字符串:{}", sign);
return sign;
}
附加微信H5支付官网地址:
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
看到一个说明的更详细的文章,在这里记录下:
https://blog.csdn.net/wtdask/article/details/75646968