微信公众号支付
业务流程
这个微信官网说的还是很详细的,还配了图。我还要再说一遍。
用户点击一个支付按钮-->{后台一大推处理}-->用户看到了一个输入密码的界面,包含金额等一些信息-->用户输入密码后出来一个支付成功的页面(这部分流程都是微信自己完成的,我们什么都不用做)-->返回系统自己的页面(总不能让用户一直看着一个支付完成的页面吧。花了钱,正心疼的,赶紧跳转啊~一会后悔了,申请退款怎么整。可怜的工程师还得开发退款功能)
开发流程
发现了这个。nonce ==number used once.恍然大悟的赶脚。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xml> <appid>wxb1427ebebexxxxxx</appid> <body>XXX费用</body> <device_info>WEB</device_info> <mch_id>132186xxxx</mch_id> <nonce_str>6AED000AF86A084F9CB0264161E29DD3</nonce_str> <notify_url>https://一个域名/api/wechatPay/jsapiPayNotify</notify_url> <openid>oo8WUt0taCqjt552htW1vw-xxxxx</openid> <out_trade_no>1</out_trade_no> <sign>各种排序+key生成的那个sign</sign> <total_fee>1</total_fee> <trade_type>JSAPI</trade_type> </xml>
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wxb1427ebebexxxxxx]]></appid> <mch_id><![CDATA[132186xxxx]]></mch_id> <device_info><![CDATA[WEB]]></device_info> <nonce_str><![CDATA[Hh4LFHUUvtDYtNdp]]></nonce_str> <sign><![CDATA[079F8A915FD3044F4A17D75F4945E955]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx20160617155030d9e6a0e48b0533061255]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
我们需要的,就是这货
prepay_id
获取到这货之后,第一步骤已经结束了,可以去喝个茶,吃个冰棍,小庆祝一下。
2、H5调起微信支付的内置JS
nonceStr ==反正我用的跟刚才签名是同一个随机字符串。理论上不用应该也没有关系的,勤快的小伙伴可以试试
NB:生成prepay_id时appid是小写的i,生成paySign时,appId是大写的I
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : appId, //公众号名称,由商户传入 "timeStamp":timeStamp, //时间戳,自1970年以来的秒数 "nonceStr" : nonceStr, //随机串 "package" : Package, "signType" :signType, //微信签名方式: "paySign" : paySign //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { window.location.replace("index.html"); } } ); }
上述代码中的,红色部分,修改成你想去的页面即可。是不是好奇replace是什么鬼。移步这里,看一下:http://www.xuebuyuan.com/2140432.html
4,最后一部分啦。fighting
该部分有以下3小步骤
1)解析传过来的流信息,通过重新签名的方式验证流中包含的信息的正确性。就是判断这个信息到底是不是微信发的
2)return_code和result_code都是SUCCESS的话,处理商户自己的业务逻辑。就是订单的支付状态啊等一些信息。
3)告诉微信,我收到你的返回值了。不用在发了。
关于以上三点的解释。微信官方是这么说的
//支付完成后,微信会把相关支付和用户信息发送到商户设定的通知URL, //验证签名,并回应微信。 //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败, //微信会通过一定的策略(如30分钟共8次)定期重新发起通知, //尽可能提高通知的成功率,但微信不保证通知最终能成功。 //商户自行增加处理流程, //例如:更新订单状态 //例如:数据库操作 //例如:推送支付完成信息
还记得我们在第一步生成预支付id(prepay_id时的那个notify_url吗。如果不记得了,请往上翻。如果当时只是随便写了一个,那么这会需要去改一改了。)
一个能访问的到的action.同样地址需要外网能访问的到。没有试ip好不好使。开发这部分功能的时候,运维同学已经配置了测试域名。好开心啊,终于不用在纠结于一些交互配置了。
和支付宝不同,微信返回的是流。和支付宝不同,微信返回的是流。和支付宝不同,微信返回的是流。重要的事情说三遍
解析之后,得到的格式是这样子的
<xml><appid><![CDATA[wxb1427ebebeeaxxxx]]></appid> <bank_type><![CDATA[CFT]]></bank_type> <cash_fee><![CDATA[1]]></cash_fee> <device_info><![CDATA[WEB]]></device_info> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe> <mch_id><![CDATA[132186xxxx]]></mch_id> <nonce_str><![CDATA[07FC15C9D169EE48573EDD749D25945D]]></nonce_str> <openid><![CDATA[oo8WUt0taCqjt552htW1vw-xxxxx]]></openid> <out_trade_no><![CDATA[你的订单编号]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[E69940B3EDC437CB5A181210D523806E]]></sign> <time_end><![CDATA[20160621134204]]></time_end> <total_fee>1</total_fee> <trade_type><![CDATA[JSAPI]]></trade_type> <transaction_id><![CDATA[400386200120160621763973xxxx]]></transaction_id> </xml>
==========================================20170726更新====================================
微信目前有鼓励金支付,在支付过程中支付金额大于鼓励金金额既可以使用。如果在支付时使用了鼓励金,则回调的流的xml格式如下
<xml> <appid><![CDATA[XXXXXX]]></appid> <bank_type><![CDATA[CFT]]></bank_type> <cash_fee><![CDATA[1]]></cash_fee> <coupon_count><![CDATA[1]]></coupon_count> <coupon_fee>30</coupon_fee> <coupon_fee_0><![CDATA[30]]></coupon_fee_0> <coupon_id_0><![CDATA[2000000001082991244]]></coupon_id_0> <device_info><![CDATA[WEB]]></device_info> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe> <mch_id><![CDATA[1321867101]]></mch_id> <nonce_str><![CDATA[D0AC1ED0C5CB9ECBCA3D2496EC1AD984]]></nonce_str> <openid><![CDATA[oo8WUt0AjI8G9TNb5W3wmGUxVV1U]]></openid> <out_trade_no><![CDATA[XXXXXX]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[9895XXXXX91E1D3F710A3D021147]]></sign> <time_end><![CDATA[20170726184839]]></time_end> <total_fee>31</total_fee> <trade_type><![CDATA[JSAPI]]></trade_type> <transaction_id><![CDATA[40027220012017072XXX93391176]]></transaction_id> </xml>
注意上述代码中阴影的4行代码。
微信返回的
coupon_count
coupon_fee>
coupon_fee_0
coupon_id_0
的这4个参数中coupon_fee_0和coupon_id_0,官方给出的参数是coupon_id_$n和coupon_fee_$n,$n为下标,从0开始编号。笔者在处理验签时是将xml转化成了JAVA对象,对于这种动态的参数目前没有什么好的解决方案,目前因为只有_0,所以只定义了一个参数叫做_0,万能的网友在使用的过程中如果有好的解决方案,请不吝赐教。
============================================20170726更新=================================
对以上第一点和第三点做个解释。
再次吐槽一下。微信真的很喜欢用签名啊。整个过程,3遍签名。也是醉了。
1)我们看到上述微信返回的xml中含有很多字段。使用上述xml中,处sign意外的值+key,进行签名。你没有看错。包含result_code和return_code。
微信的官方对于签名有解释。
原谅我真的好久不学语文了。真的没理解这句话,是用微信回调函数中传的参数,进行重新签名。傻傻的,还在想,用第二次签名是的参数进行签名,时间戳怎么办,要不要存在数据库里面。
将获得的签名与xml中的sign对比,如果相同,证明是微信返回的通知。如果不同,你的通知地址可能被黑客破解了。要不要告诉老板呢,告诉老板了,我怎么解决呢。
2)商户逻辑处理,不解释
3)告诉微信,我收到了你的通知,不需要在发送了。
怎么告诉微信呢。我翻遍了微信的文档,也没有找到回复微信通知这个url。
经人指导,再一次的刷新了认知观。用response.
我是这么写的:(20170727修改)
返回参数参照微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
String xml = "<xml> <return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
response.getWriter().write(xml);
--------------------------------------------------------------
微信公众号支付--JSAPI的开发思路和一下参数的具体解释,全部完成了。