小程序支付交互的官方流程图如下,其实很清晰的描述了整个过程。
宏(微信号:宏8410)特意去练习了这个过程,现在整个过程整理如下:
首先,准备几样东西。
小程序APPID(appid)
商户编号(mchid)
商家设置的密钥。
配置小程序时的秘密
是小程序-服务器——微信服务器-服务器-小程序。
小程序发起三个请求,两次向微信服务器(wx.login和wx.requestPayment),一次向服务器(wx.request)。
服务器发起两个请求并加密成签名,这两个请求都是对微信服务器的请求,一个请求获取openid,一个统一订单获取预付费_id。加密过程主要是通过MD5然后生成签名参数符号。
签名参数符号有两个主要用途。一种是向微信服务器请求预付款时使用这个参数,另一种是返回小程序调用确认付款,签名生成是通过用MD5加密appid OpenID/prevable _ id等参数进行的。
调用wx.login获取jscode,并通过wx.request将jscode发送到后台
wx . log in({ success : function(RES){ if(RES . code){//在这里获取jscode,然后传递给后台处理wx . request({ URL 3360 ' https://www.voopapi/getdata? Js _ code=' RES . codedata : } },method3360' get ',success 3360 function(RES){//预付费在后台处理成功,在这里可以获取paySign等参数确认支付需求})});};});
首先,根据传递的jsCode向微信服务器请求session_key和openid。
公共字符串GetSessionKeyOpenId(字符串appid,字符串secret,字符串js_code){ var url=string。format(' https://API . weixin . QQ . com/SNS/jscode 2 session?appid={ 0 } secret={ 1 } js _ code={ 2 } grant _ type=authorization _ code ',appid,secret,js _ code);var request=WebRequest。将(url)创建为HttpWebRequestvar响应=请求。GetResponse();var respStream=响应。GetResponseStream();var res=字符串。空的;使用(var reader=new stream reader(resstream,Encoding。UTF8)) { res=reader。农村电气化管理局
dToEnd(); } return res;}返回数据
{”session_key”:”a6Td9fWKZx8LkPFCsGA==”,”expires_in”:7200,”openid”:”oujEK0QoA0wI_DDyE5660”}
其次根据openid,appid,mch_id等参数封装成一个字符串后用MD5加密后向微信服务器统一下单
var paramter = new WXPayParameters(); paramter.AppId = shopinfo.AppId; paramter.Body = shopinfo.ShopName + "-" + orderpool.OrderPoolCode; paramter.MchId = shopinfo.MchId; paramter.Nonce = WXHandleBll.GetNoncestr(); paramter.Notify_Url = shopinfo.Notify_Url; paramter.Out_Trade_No = orderpool.OrderPoolCode; paramter.Spbill_Create_Ip = "127.0.0.1"; paramter.Total_Fee = "1";//(orderpool.NetAmt).ToString(); 暂时1分,用于测试 paramter.Trade_Type = shopinfo.Trade_type; paramter.Key = shopinfo.Key; var param = WXHandleBll.GetOrderUnifiedParam(reslogion.openid, paramter);
MD5加密得出签名sign
public string GetOrderUnifiedParam(string openid, WXPayParameters param) { //参与统一下单签名的参数,除最后的key外,已经按参数名ASCII码从小到大排序 var unifiedorderSignParam = string.Format("appid={0}&body={1}&mch_id={2}&nonce_str={3}¬ify_url={4}&openid={5}&out_trade_no={6}&spbill_create_ip={7}&total_fee={8}&trade_type={9}&key={10}" , param.AppId, param.Body, param.MchId, param.Nonce, param.Notify_Url, openid, param.Out_Trade_No, param.Spbill_Create_Ip, param.Total_Fee, param.Trade_Type, param.Key); //MD5 var unifiedorderSign = GetMD5(unifiedorderSignParam).ToUpper(); //构造统一下单的请求参数 var requestParam = string.Format(@"<xml> <appid>{0}</appid> <body>{1}</body> <mch_id>{2}</mch_id> <nonce_str>{3}</nonce_str> <notify_url>{4}</notify_url> <openid>{5}</openid> <out_trade_no>{6}</out_trade_no> <spbill_create_ip>{7}</spbill_create_ip> <total_fee>{8}</total_fee> <trade_type>{9}</trade_type> <sign>{10}</sign></xml>", param.AppId, param.Body, param.MchId, param.Nonce, param.Notify_Url, openid, param.Out_Trade_No, param.Spbill_Create_Ip, param.Total_Fee, param.Trade_Type, unifiedorderSign); return requestParam; }
将要MD5的字符串
appid=w362e8457f1ca&body=中国特馆-O2017041200015&mch_id=14107872&nonce_str=819F462C255C424244317¬ify_url=http://yourdomain.com/notifyurl&openid=oujEK0QoAI_DDyE5660&out_trade_no=O20170015&spbill_create_ip=127.0.0.1&total_fee=1&trade_type=JSAPI&key=4537751044EE2113393A
MD5后的签名
DFWGFGF81D73DBA86C645D8A3
签名规则注意事项(must)
参数名ASCII码从小到大排序(字典序);
如果参数的值为空不参与签名;
参数名区分大小写;
验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
微信接口可能增加字段,验证签名时必须支持增加的扩展字段
再着,向微信服务再次进行请求(统一下单),获取预支付信息
var RespayXML = WXHandleBll.DoPost(param, "https://api.mch.weixin.qq.com/pay/unifiedorder");
发送的XML样式
<xml>rn <appid>w3e843377f9a</appid> rn <body>中国馆-O2017041200015</body>rn <mch_id>1445576552</mch_id> rn <nonce_str>819F465763A252422644317</nonce_str>rn <notify_url>http://voopaip.com/notifyurl</notify_url>rn<openid>oujEK0Q3oAuwI_DDyE5660</openid>rn <out_trade_no>O2017200015</out_trade_no>rn <spbill_create_ip>127.0.0.1</spbill_create_ip>rn <total_fee>1</total_fee>rn <trade_type>JSAPI</trade_type>rn <sign>A5381D73DBA86D3DC645D8A3</sign>rn </xml>rn最后,根据统一下单请求返回的参数prepay_id再加上appid等参数封装成一个字符串后用MD5再得出一个签名参数,并且处理其它参数后全部返回小程序端所需要的参数 //3.统一下单后拿到的xml结果 var payRes = XDocument.Parse(RespayXML); var ResXML = payRes.Element("xml"); //序列化相应参数返回给小程序 var res = WXHandleBll.GetPayRequestParam(ResXML, paramter.AppId, paramter.Key); public WXPayRequesEntity GetPayRequestParam(XElement root, string appid, string key) { //当return_code 和result_code都为SUCCESS时才有我们要的prepay_id if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS") { var package = "prepay_id=" + root.Element("prepay_id").Value; var nonceStr = GetNoncestr(); var signType = "MD5"; var timeStamp = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString(); var paySignParam = string.Format("appId={0}&nonceStr={1}&package={2}&signType={3}&timeStamp={4}&key={5}", appid, nonceStr, package, signType, timeStamp, key); var paySign = GetMD5(paySignParam).ToUpper(); var payEntity = new WXPayRequesEntity { package = package, nonceStr = nonceStr, paySign = paySign, signType = signType, timeStamp = timeStamp }; return payEntity; } return new WXPayRequesEntity(); }
返回XML样式
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[w2e84759cca]]></appid> <mch_id><![CDATA[141008772]]></mch_id> <nonce_str><![CDATA[brDijFx44Xx4Nl]]></nonce_str> <sign><![CDATA[690985862E39F75F03D447E3C9]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[w201702219c9a14b79b753854012]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
wx.requestPayment({ timeStamp: res.data.Body.Data.timeStamp, nonceStr: res.data.Body.Data.nonceStr, package: res.data.Body.Data.package, signType: 'MD5', paySign: res.data.Body.Data.paySign, success: function (res) { //确认成功 }});
收集的一些支付坑,方便遇到问题可供查阅
微信支付接口要严格区分大小写。在获取预付单数据的时候,timestamp没有区分大小写,导致支付的收银台一直调用不起来。
签名操作,一定是要配合appId,appid商户号KEY是否正确,参与签名的字符串是否按照要求排序,是否是UTF8格式(实在不行可重置一下),在具体签名方法说明中,可以看出key是在签名参数按照ASCII大小排序完再拼接上去的,
返回错误说total_fee参数为空,如果total_fee参数不为空,可能是package格式不对应该为”prepay_id=”+prepay_id
统一下单接口是xml(这个不只是小程序,公众号也是),返回值也是xml格式需要自己获取prepay_id,
签名算法要带上key,最后要转换成大些
微信支付的sign算法也要带上appid(这个不科学,深坑)
签名算法一定不要用json拼接key
签名MD5加密,网上有些算法是错误的,自己写完还需要在线MD5加密工具进行校验(我采坑一下午,怎么看我写的怎么对,就是出不来,原因就是MD5工具使用错误,坑爹- - )
统一下单签名appid,wx.requestPayment签名appId(大小写必须区分,真是找瞎我钛合金狗眼- - )
wx.requestPayment中package参数必须是package:”prepay_id=wx21**“,不然,会出现调用支付JSAPI缺少appid/total_fee
total_fee为分,并且是int
生成随机数和时间戳一定要保证签名与上传参数一致
微信小程序trade_type=JSAPI,openid参数必传
wx.requestPayment生成签名有appId,请求的时候没有appId