从一无所知到小程序支付开发,用了三天时间,看了很多网上的案例(主要是微信官方的文档实在是渣),下来整理一下自己的微信支付的详细步骤跟流程,写下来省的以后找不到东西。
1.前期准备
微信公众平台:https://mp.weixin.qq.com/
微信商户平台:https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
微信支付签名校验工具:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=20_1
小程序的开发需要在微信公众平台上注册小程序,以获取AppID和Secret,并且需要在微信商户平台中注册认证商户资质(其实就是微信对商户的备案与接口调用权限控制,)商户平台注册审核通过会给商户分配一个mch_id (10位数字字符串),并且在商户平台设置接口加密秘钥(mch_key :自己生成设置的32位随机字符串,在使用中为key)在微信支付流程中需要作为私钥进行加密使用
2.平台接入
商户平台,是企业或个人开发或使用微信支付功能必须注册的,因为这里面会有你的应用产生的所有交易(支付流水),并需要进行管理,比如退款,比如周结算,比如报表等是用来管理应用产生的支付的所有流水记录,并对接入微信支付功能的应用进行授权认证(比如小程序,公众号,H5,APP等多平台应用程序进行关联),具体关联办法是在商户平台后台对指定类型的应用进行关联绑定AppId,这个APPID是微信对H5,APP,小程序,公众号等程序认证颁发的具有唯一性,微信旗下的相关程序会有AppId和Secret两个值,作为微信对该应用的授权识别用,
3.测试开发
首先小程序的支付肯定是建立在小程序用户的授权登录后的
关于小程序的授权登录中的坑,我下一篇博文会进行详述,在此先略过,小程序的授权登录使用wx.login获取临时js_code,交给后台服务器端请求微信服务器验证js_code的有效性,验证成功返回该微信用户对该小程序的唯一标示openId,与会话标示session_key,后端将这两个值保存并传回前段,前段调用wx.getUserInfo()获取用户信息(坑1:获取用户信息自2018.5月以后不再支持弹窗wx.openAuthSetting()获取,推荐使用
//支付类型
String trade_type = "JSAPI"; String spbill_create_ip = HttpUtils.getIp(request);
//随机字符串
String randomString = StringUtils.getRandomString(32);
//数字签名
String sign = "";
//商品描述
String body = "用户支付的商品简述";
//商品订单号
String orderId = “自己系统生成的订单号”;
//微信支付结果回调结果通知URL
String callbackurl = wxPayCallBackUrl;
//支付金额(分) 1元 = 100分
String total_fee = "";
//微信官方文档给出将统一下单接口所需要的非空参数key进行Ascal码排序,并按排序顺序将参数名与参数值凭拼成
// key=value&key1=value1&key2=value2...格式的待加密字符串,凭借完成需要在字符串最后面拼上"&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 这里的KEY就是商户平台设置的API秘钥mch_key
SortedMap params = new TreeMap<>();
params.put("appid", appId);
params.put("mch_id", mch_Id);
params.put("nonce_str", randomString);
params.put("body", body);
params.put("out_trade_no", orderId);
params.put("openid", openId);
params.put("total_fee", total_fee);
params.put("spbill_create_ip", spbill_create_ip);
params.put("notify_url", callbackurl);
params.put("trade_type", trade_type);
//待加密字符串拼接完成后按微信要求进行MD5加密得到签名
sign = sortMapTools(params);
params.put("sign",sign );
//将所有统一下单接口需要的参数值进行xml转化拼接
//这里我用的比较笨的办法,但好处是不容易出错
String xml = "" + "" + appId + " " + "" + body + "" +
"" + "" + mch_Id + " " + "" + randomString + " " +
"" + callbackurl + " " + "" + openId + " " +
"" + orderId + " " + "" + spbill_create_ip + " " +
"" + Integer.parseInt(total_fee) + "" + " " + "" + trade_type + " " +
"" + sign + " " + " ";
String result = "";
System.out.println("xml---->" + xml);
try {
//http请求微信统一下单接口
result = HttpUtil.doPost(wxCreatOrderAPI, xml);
} catch (Exception e) {
logger.info("统一下单接口情求错误----------------------------------->" + result);
e.printStackTrace();
}
//坑3.返回的全是xml字符串,且不能被JDOM,SAM,DOM4J等解析的xml
logger.info("result----------------------------------->" + result);
//生成二次签名需要的随机字符串
//没错,统一下单接口调用成功后给前端返回的的参数仍需要进行加密验签
String randomString1 = StringUtils.getRandomString(32);
if(result.contains("prepay_id")&&result.contains(" ")){
String prepay_id = result.substring(result.indexOf(""), result.indexOf(" ")).substring(20,56);
//生成二次签名需要的时间戳
Long timeStamp=System.currentTimeMillis();
SortedMap map=new TreeMap();
map.put("appId",appId);
map.put("timeStamp",timeStamp);
map.put("nonceStr",randomString1);
//这里的package其实就是微信在自己服务器中缓存的预付单消息数据,在外界智能接触到后台调用统一下单接口返回的prepay_id(个人猜测这个应该是生成的支付单数据的key)
//这里需要注意package的value需要用"prepay_id="进行拼接,否则报错
map.put("package","prepay_id="+prepay_id);
map.put("signType","MD5");
String reSign = sortMapTools(map);
logger.info("解析订单ID值--------------------------->>>>"+prepay_id);
logger.info("二次签名字符串------------------------->>>>"+reSign);
map.put("paySign",reSign);
map.put("prepay_id=",prepay_id);
resultMap.put("code",200);
resultMap.put("data",map);
resultMap.put("message","请求成功");
resultMap.put("error","{}");
return resultMap;
}
到这后端在微信支付流程中的活基本就干完了,对没错,就这一个稍微复杂的交互后段就完成自己的任务了,总结就是将小程序前台的支付请求拿来自己生成一下自己的订单,然后报送给微信生成预付单,之后将微信返回的预付单进行加密验签给前端,前端识别正确的签名以及预付单ID就可以继续下来的操作,重点**支付流程**
前端微信支付代码:
wx.requestPayment({
'timeStamp': data.timeStamp,
'nonceStr': data.nonceStr,
'package': data.package,
'signType': 'MD5',
'paySign': data.paySign,
success: function (event) { 进行支付成功后的业务处理},
fail: function(err){ 支付失败的逻辑处理}
之后说明一下小程序开发工具以为内嵌虚拟机并不能拉起微信应用所以会生成一个需要支付的二维码,这个在手机上不会这样
4.回顾感悟
微信支付中的坑不算很多,难点在于对统一下单接口调用时候的参数签名加密与对返回参数在进行二次签名加密,就是说我们的后端在收到前段的下单请求后之后的请求微信服务器统一下单接口以及返回给前端的参数都是需要签名加密的(先排序,在拼接key=xxxxxxxxxxxx,之后MD5,最后在封装xml格式数据),前端的开发流程反而更为简单易处理