微信公众号支付整体流程记录备忘

标签: 微信 公众 记录 | 发表时间:2016-03-14 16:45 | 作者:brandNewUser
出处:http://www.iteye.com

 

 

相比支付宝支付,微信公众号支付的实现以及过程真的是比较复杂,而且坑多,都是血泪史。
 
首先,需要登录微信公众平台, https://mp.weixin.qq.com
 
查看微信支付的开发配置,这里就可以看到对应的支付授权目录以及测试目录,可以选择使用线上作为支付测试,但是不推荐。使用测试授权目录时,注意需要设置测试白名单,规定哪些人可以进行支付测试。
 


 
 
 
当然,我们有微信公众号,就肯定也便拥有了H5网站。公众号支付采用的支付方式属于JSAPI方式,查看JSAPI网页支付是否已经开通了权限,并配置好支付授权目录,该目录必须是发起支付的页面的精确目录,子目录下无法正常调用支付。
 
根据微信支付的文档,商户系统和微信支付系统主要交互:
 
  1. 商户server调用统一下单接口请求订单,api参见公共api【统一下单API】
  2. 商户server接收支付通知,api参见公共api【支付结果通知API】
  3. 商户server查询支付结果,api参见公共api【查询订单API】

 

统一下单API

 
参考公众号支付中的统一下单相关文档:
 
商户系统先调用该接口在微信支付服务后台生成预支付交易单,商户订单号为商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号,由商户自定义生成,微信支付要求商户订单号保持唯一性(建议根据当前系统时间加随机序列来生成订单号)。重新发起一笔支付要使用原订单号,避免重复支付;已支付过或已调用关单、撤销(请见后文的API列表)的订单号不能重新发起支付。
 
统一下单流程完成后,最主要是根据前几步骤生成的相关参数,获取对应的PrepayId,
 
请求的url为统一下单api: 
 
 
请求的参数:
<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}}%2Fwxpay%2FpayModelAndView?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调起支付的所有参数,返回给前端。参考微信公众平台相关文档:
 
 
 
其中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>
 
如果在服务端主动查询订单,可以查看对应的文档来进行操作,这里坑比较少就不详细说明了:
 
 
 

 

 


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [微信 公众 记录] 推荐:

微信公众号支付整体流程记录备忘

- - 移动开发 - ITeye博客
相比支付宝支付,微信公众号支付的实现以及过程真的是比较复杂,而且坑多,都是血泪史. 首先,需要登录微信公众平台, https://mp.weixin.qq.com. 查看微信支付的开发配置,这里就可以看到对应的支付授权目录以及测试目录,可以选择使用线上作为支付测试,但是不推荐. 使用测试授权目录时,注意需要设置测试白名单,规定哪些人可以进行支付测试.

浅谈微信公众平台

- - 微博之博
过去的这个周末,应该大部分人都在家里调戏 微信公共 平台. 有人幸灾乐祸,有人稍作恐慌:芸芸众生相. 注册尝试了一个微信 公众平台,总结一句话:龙生龙,凤生凤. 以前曾经有人说中国的互联网,要么生要么死要么被腾讯. 企鹅家族优良的传统,势必不会放过 微博帝国的3亿用户群的潜在商业价值,所以这次爱起哄的企鹅家族,又一次决定露出起哄天性,悄悄的推出了微信公众平台.

微信公众平台开发(一)

- - BlogJava-首页技术区
  开始微信公众平台的开发,我们首先要了解微信平台可以帮助我们做哪些事情. 使用您的公众账号登陆http://mp.weixin.qq.com/,选择菜单--高级功能-开发模式--查看文档,即能看到微信公众平台目前所能开发的功能. 接受用户发送给您公众账号的消息. 需要特别说明的是,发送消息和回复消失是一个连贯的过程,只能在一个对话中完成.

微信公众平台接口开发

- - CSDN博客互联网推荐文章
随着微信公众平台的开放,微信营销推广也越发受到重视. 现在企业越来越注重求职者是否拥有“微信公众平台接口开发”的经验. 现在参考资料介绍下微信公众平台接口开发模式:. 首先你得有个微信公众平台账号,注册地址:http://mp.weixin.qq.com/. 开发者提交信息后,微信服务器将发送GET请求到填写的URL上,GET请求携带四个参数:.

微信公众平台开发入门

- - Web前端 - ITeye博客
在这篇微信公众平台开发教程中,我们假定你已经有了PHP语言程序、MySQL数据库、计算机网络通讯、及HTTP/XML/CSS/JS等基础. 我们将使用微信公众账号方倍工作室作为讲解的例子,二维码见底部. 本系列教程将引导你完成如下任务:. 我们使用SAE新浪云计算平台作为服务器资源,并且申请PHP环境+MySQL数据库作为程序运行环境.

微信公众平台增加微信小店功能 可快速开店

- - TechWeb 今日焦点 RSS阅读
微信公众平台增加微信小店功能 可快速开店(TechWeb配图). 【TechWeb报道】5月29日消息,腾讯今日发布消息称,微信公众平台更新增加了微信小店功能,微信小店基于微信支付,包括添加商品、商品管理、订单管理、货架管理、维权等功能,开发者可使用接口批量添加商品,快速开店. 已接入微信支付的公众号,可在服务中心中申请开通微信小店功能.

微信公众平台:移动推送(Push)平台

- - 天涯海阁|Web2.0Share
之前曾经写过一个文章《 移动互联网的未来:基于情境/兴趣的推送》,至今我也觉得这个在未来会是有着巨大想想空间的移动生活方式. 之前其实也有不少初创企业在Push这个领域努力,不过效果却都不好,所以其实本质而言,要构建Push这样的平台,需要吸引大量的内容、媒体、品牌来提供Push内容,而对于内容、媒体、品牌而言,最在意的是这个平台有没有用户,而微信可能已经到了可以去构建这样一个信息推送平台的阶段.

一个程序员关注的微信公众账号

- - ITeye博客
春节回家的时候在家里无聊,开始玩微信,后面接触微信公众账号,陆续的关注了很多,每天至少会花半小时在这些账号的文章上面,有的文笔不错,有的十分简洁,但是都比较真实,值得一看,下面把认为不错的账号推荐一下. 我是一名程序员,关注产品、技术、移动以及八卦,如果和我口味类似,这些账号可以关注一下. 微信号:WebNotes.

微信公众平台为什么会火

- - 月光博客
  在某IT评论网站看到一篇号称是微信公共平台干货分享的文章,立马点进去查看,结果却大失所望. 仅仅是微信公共平台如何开通的介绍. 文章名字可以适当夸大,但必须真有内容,否则你就是在浪费别人的时间. 今天讨论为什么 微信公共平台能火. 微信从一诞生就拥有真实的基因,一开始就严格限定与手机号绑定. 微信可以说是中国最严谨的实名认证社交平台.

推荐几个值得关注的微信公众号

- - 望月的博客
微信公众平台推出有一段时间了,然而相信和我一样没有怎么搞懂微信公众号到底是个什么东西的大有人在,不止是我们,许多拥有微信公众号甚至被官方认证了的都还没有搞懂怎么很好的使用它. 于是,我收集了一些目前个人或者朋友们觉得不错的微信工种号,推荐给大家,希望还不知道什么是微信公众号的能够有所了解,还不知道怎么使用它的能够有所启发.