微信公众号支付开发流程备忘

xiaoxiao2021-02-27  415

原文地址: 

http://itindex.net/detail/55309-微信-公众

相比支付宝支付,微信公众号支付的实现以及过程真的是比较复杂,而且坑多,都是血泪史。   首先,需要登录微信公众平台,  https://mp.weixin.qq.com   查看微信支付的开发配置,这里就可以看到对应的支付授权目录以及测试目录,可以选择使用线上作为支付测试,但是不推荐。使用测试授权目录时,注意需要设置测试白名单,规定哪些人可以进行支付测试。           当然,我们有微信公众号,就肯定也便拥有了H5网站。公众号支付采用的支付方式属于JSAPI方式,查看JSAPI网页支付是否已经开通了权限,并配置好支付授权目录,该目录必须是发起支付的页面的精确目录,子目录下无法正常调用支付。   根据微信支付的文档,商户系统和微信支付系统主要交互:   商户server调用统一下单接口请求订单,api参见公共api【统一下单API】商户server接收支付通知,api参见公共api【支付结果通知API】商户server查询支付结果,api参见公共api【查询订单API】

 

统一下单API

  参考公众号支付中的统一下单相关文档:   http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1   商户系统先调用该接口在微信支付服务后台生成预支付交易单,商户订单号为商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号,由商户自定义生成,微信支付要求商户订单号保持唯一性(建议根据当前系统时间加随机序列来生成订单号)。重新发起一笔支付要使用原订单号,避免重复支付;已支付过或已调用关单、撤销(请见后文的API列表)的订单号不能重新发起支付。   统一下单流程完成后,最主要是根据前几步骤生成的相关参数,获取对应的PrepayId,   请求的url为统一下单api:    https://api.mch.weixin.qq.com/pay/unifiedorder   请求的参数: <xml> <appid><![CDATA[wx1exxxx]]></appid> <body><![CDATA[JSAPI_payment_test]]></body> <mch_id>1242312122</mch_id> <nonce_str><![CDATA[6aghlqz18duhfebole531dce0r7bw0td]]></nonce_str> <notify_url><![CDATA[http://xxxx.com/xxx]]></notify_url> <openid><![CDATA[ogGCluNRaxBTNFWZzS_kH-rRez_Q]]></openid> <out_trade_no><![CDATA[nraxbtnfwzzskhrrezq1434590817259]]></out_trade_no> <spbill_create_ip><![CDATA[119.161.230.131]]></spbill_create_ip> <total_fee>1</total_fee> <trade_type><![CDATA[JSAPI]]></trade_type> <sign><![CDATA[F415B11A1C1B4894085FD703CBD14B71]]></sign> </xml>     将参数以POST方式发送给统一下单URL,返回值仍然也是xml格式:     <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[xxx]]></appid> <mch_id><![CDATA[1212]]></mch_id> <nonce_str><![CDATA[amxU3MOLatSWVzua]]></nonce_str> <sign><![CDATA[E458BE2C4F23C6F22B7561E74F41DEEF]]></sign><result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201506180927207ee0b107300739613144]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>     我们只需获取其中的prepay_id即可;如果没有获得,直接返回错误信息。   上面所说的参数中,appid和mch_id属于公众号以及商户号id,申请公众号以及开通支付时就已经确定;notify_url属于微信支付服务器向服务端回调的接口(后续会用到);spbill_create_id是用户的ip;total_fee是付款总额;trade_type如果是公众号支付写死为JSAPI;out_trade_no是商户订单号,可在服务端通过文档中的算法生成即可;nonce_str是本次请求支付的随机字符串,最多32位。   不确定的就只有两项:   openid, 关于公众号中如何获取openid可以查看相关文档,在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。   在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的开发者中心页配置授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL:  https://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html,授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。  

获取Openid

  在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:   https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect   若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。   公众号支付首先要设置微信支付的链接,通过该链接,就可以调用到微信后端以及服务端,支付的链接格式如下:   https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri={{url}}/wxpay/payModelAndView?parameterName=${xxxx}&response_type=code&scope=snsapi_base&state=123#wechat_redirect     scope设置为snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),虽然是snsapi_userinfo的时候可以获取更多信息,但需要弹出授权界面,而且我们也不需要获取那么多信息。   实际上需要微信服务器进行回调才能实现,而回调的redirect_uri为:   ${url}/wxpay/payModelAndView?     其中appid即为注册的微信公众服务号ID,url参数即为当前网站的url,并带上coach_product_id,传送给回调地址,url需要进行URLDecoder,微信服务器会回调该服务。  

通过code换取网页授权access_token

  首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。   尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。   redirect_url中就可以获取到对应的request Parameter,其中的code就是我们需要的编码。   获取code后,请求以下链接获取access_token:    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code   注意此URL必须在微信浏览器中打开,redirect_uri设置为当前controller方法对应的restful接口。   发送过来的code可以通过request.getParameter(“code”)来获取,如果没有生成该code,不能继续进行。   如果请求失败,记得别让用户还能重复使用这个code调用后台代码。也即,到支付页面后不能刷新。   返回的数据为json格式,如下:   { "access_token": "OezXcEiiBSKSxW0eoylIeGhaJjUxzVpRR4o6hX-jAhOn160_GRNWPwzcWR_QSO4gbjzWHPV6zuNazuJp3spc2gptHLcR-g2QetMKeDGZ3IJD6PbJCf2YKyw6k4aeiFbdJgfJgNBXKfZ0dPb98IKR_w", "expires_in": 7200, "refresh_token": "OezXcEiiBSKSxW0eoylIeGhaJjUxzVpRR4o6hX-jAhOn160_GRNWPwzcWR_QSO4g7r7Y2BQy_p7bmrjxH8YN3scFXn7C4fUnNn9AFDcz_qW5ErAi4Lp9p18PcLv60yUtOBSwd8MfDIKap12lVExOAg", "openid": "ogGCluNRaxBTNFWZzS_kH-rRez_Q", "scope": "snsapi_base" }   其中,我们只需要获取其openid,然后进行下一步操作。  

进行签名:sign

  就剩下一个参数了sign,属于签名,关于签名的算法见文档:  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3,算法用java进行实现如下:     public String getSign(Map<String, String> items, String APISecret) throws NoSuchAlgorithmException, UnsupportedEncodingException { Map<String, String> tmp = new TreeMap<String, String>(items); StringBuilder sb = new StringBuilder(); for(Map.Entry<String, String> entry : tmp.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if(StringUtils.isEmpty(value)) continue; sb.append(key).append("=").append(value).append("&"); } sb.append("key=").append(APISecret); return publicService.padStr(new BigInteger(1, MessageDigest.getInstance("MD5").digest(sb.toString().getBytes(CharEncoding.UTF_8))).toString(16).toUpperCase(), "0", 32); }     使用TreeMap对key进行字符串字典排序,附加上对应的key,后进行MD5计算并拍成32位并补零然后转成大写。   微信提供相关接口在线签名验证工具:  https://pay.weixin.qq.com/wiki/tools/signverify/。   有了签名就可以进行统一下单相关操作了。   生成公众号支付接口所使用的jsapi调起支付的所有参数,返回给前端。参考微信公众平台相关文档:   http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7     其中package使用的是上一步的prepay_id=?,本系统中paySign采用MD5算法,其中的paySign采用统一签名生成算法来计算完成。  

保存至服务端本地/数据库,页面发起支付

  将用户信息,产品信息,生成的订单保存至数据库,以便在我方能够查询到该记录。   //将userId和out_trade_no等信息写入payment_result表 paymentPublicService.insertStubToPaymentResultTable(userId, PaymentResult.CHANNEL.WEIXIN, coachProductId, outTradeNo);   在从服务端转到页面上之后,再发起支付调用,跳转至付款页面   微信支付需要在回调之后跳转至付款页面(通过调试发现,这个最终付款界面还是必须存在的)。   页面中会调用真正的付款功能。   $(function(){ alert("xxxxxxxx"); callPay(); function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : '${appId}', //公e众号名称,由商户传入 "timeStamp": '${timeStamp}', //时间戳,自1970年以来的秒数 "nonceStr" : '${nonceStr}', //随机串 "package" : '${package1}', //预支付ID参数 "signType" : '${signType}', //微信签名方式: "paySign" : '${paySign}' //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { alert("支付成功"); window.location.href="/student/student_booking"; }else if(res.err_msg == "get_brand_wcpay_request:cancel" ){ alert("支付过程中用户取消"); window.location.href="student_pay.jsp"; }else{ alert('支付失败'); window.location.href="student_pay.jsp"; } } ); } function callPay(){ if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); } } })     生成完成之后,就可以进行支付,支付完成后,微信服务端就会通过设置的notify_url来进行回调通知,此时数据库端的订单信息就可以填充完整。  

支付结果通知

  可以查看对应的回调结果:  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7。   发送过来的Request,得到对应的xml           String xml = IOUtils.toString(request.getInputStream(), CharEncoding.UTF_8);     商户处理后同步返回给微信参数,根据回调通知API,需要返回如下xml,才能让微信服务器确认已经接受到notify消息,否则微信服务器会多次retry调用我们的接口:   <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> </xml>   如果在服务端主动查询订单,可以查看对应的文档来进行操作,这里坑比较少就不详细说明了:   https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2  
转载请注明原文地址: https://www.6miu.com/read-4991.html

最新回复(0)