微信支付开发关键点技术解析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

微信⽀付开发关键点技术解析
微信⽀付是由微信及财付通联合推出的移动⽀付创新产品。

如今,随着微信⽀付的全⾯开放,相关需求也越来越多,很多开发⼈员进⾏微信⽀付开发及商家申请微信⽀付时,⾯
临着诸多疑惑。

本⽂将结合微信⽀付接⼝开发的实践,从⽀付申请到各主要接⼝的使⽤⽅法等⽅⾯介绍微信⽀付的关键点技术。

URL设置
⽬前,微信⽀付只能由通过微认证的服务号进⾏申请,订阅号及未认证的服务号均⽆法申请。

登录微信公众平台后台,在左侧的栏⽬中可找到“微信⽀付”,点击进⼊申请界⾯,
可以看到第⼀项中的“商户基本资料”,点击右侧的“填写”按钮后就进⼊了微信⽀付设置界⾯。

微信⽀付的⽬录及URL没有固定的设置⽅法,具体还需要根据⾃⼰的需求来定,表1是⽅倍⼯作室的⽅案。

表1 微信⽀付⽬录及URL设置
需要注意的是,如果使⽤上述⽅法,要将域名换成⾃⼰的域名,其他的结构及层次可不变。

这⾥所有的URL没有填写实际的⽂件名,⽬的是为了兼容不同的开发语⾔或框架。

⽐如⽬录下的默认⽂件既可能是index.php,也可能是index.aspx。

微信⽀付申请完成后,便进⼊了微信⽀付测试阶段,需要填写⽀付测试⽬录,测试⽬录可以填写为:
/wxpay/test/。

接⼝开发
JS API⽀付
JS API⽀付的实现⽐较简单,官⽅也提供了Demo,在此基础上修改部分参数即可。

修改后的
⼀个⽰例如下:
[js]
1. <?php
2. include_once("WxPayHelper.php");
3. $commonUtil = new CommonUtil();
4. $wxPayHelper = new WxPayHelper();
5. $wxPayHelper->setParameter("bank_type", "WX");
6. $wxPayHelper->setParameter("body", "微信⽀付开发教程");
7. $wxPayHelper->setParameter("partner", PARTNERID);
8. $wxPayHelper->setParameter("out_trade_no", $commonUtil->create_noncestr());
9. $wxPayHelper->setParameter("total_fee", "1");
10. $wxPayHelper->setParameter("fee_type", "1");
11. $wxPayHelper->setParameter("notify_url",
12. "/wxpay/notify/");
13. $wxPayHelper->setParameter("spbill_create_ip", $_SERVER['REMOTE_ADDR']);
14. $wxPayHelper->setParameter("input_charset", "GBK");
15. $biz_package=$wxPayHelper->create_biz_package();
16. ?>
上述代码中,主要修改了两个参数:notify_url为接收交易通知的路径,这个⼀定要改为⾃⼰服务器上的⼀个路径;spbill_create_ip为⽤户客户端的IP,不改关系也不⼤,不过改
⼀下更规范些。

JS API⽀付是⽹页内的⽀付,通过调⽤微信⽀付控件来实现⽀付。

如果要⽤作真实产品场景的⽀付,只需要修改⼀下产品名称及费⽤即可,对于涉及到快递费⽤的交易,需要注
意订单的总⾦额为商品费⽤和物流费⽤的和。

如果微信⽀付时提⽰Access Denied,通常有以下原因:参数填写不正确、⽀付⽬录结构不正确、没有加⼊⽩名单权限。

需要对照检查⼀下,才能找到具体原因并进⾏纠正。

Native⽀付
Native(原⽣)⽀付就是常说的扫描⼆维码⽀付。

这种⽀付⾸先需要商户定义符合Native⽀付规范的URL,也就是Native⽀付URL,同时在微信后台POST商户后台时需要提供
package内容。

Native⽀付的开发分为三步。

⽣成Native⽀付的URL
Native⽀付URL是⼀系列具有“weixin://wxpay/bizpayurl?”前缀的URL,同时后⾯紧跟着⼀系列辨别商户的键值对。

原⽣URL由wxPayHelper类中的create_native_url()⽅法实现,实现代码如下:
[js]
1. <p><?php</p><p>include_once("WxPayHelper.php");$wxPayHelper = new WxPayHelper();</p><p>$productid = "1234567890";echo $wxPayHelper-
>create_native_url($productid);?></p>
其中productid是商品唯⼀ID,开发⼈员需要定义并维护⾃⼰的商品ID,这个ID与⼀张订单等价,微信后台凭借该ID通过POST商户后台获取交易信息。

上述代码⽣成的URL如下
所⽰:
[js]
1. weixin://wxpay/bizpayurl?appid=wxb489e8caeabcdefg&noncestr=BBvdr5atZ9D7s08X&produc
2. tid=1234567890&sign=e15d2466a85cd62b530e2f690604e7502f67ccb5&timestamp=1408025996
⽣成URL的⼆维码有了上述⽀付链接后,还要把它转成⼆维码,PHP QR Code是⼀个开源的⼆维码⽣成类库,可使⽤它来⽣成上述Native URL,代码如下:
[js]
1. <?php
2. include 'phpqrcode.php';$productid = "1234567890";
3. $filename = $productid.".png";
4. $nativeurl = "weixin://wxpay/bizpayurl?ap
5. pid=wxb489e8caeabcdefg&noncestr=BBvdr5atZ9D7s08X&productid=1234567890&sign=e15d2466a85cd62b530e2f690604e7502f67ccb5&timestamp=1408025996";QRcode::png($nativeurl, $fil
6. ?>
PHP QR Code的使⽤很简单,配置⼀下URL和⽂件名就可以了。

执⾏上述代码,就会在当前⽬录下⽣成⼀个1234567890.png的⼆维码图⽚⽂件。

Navive⽀付回调URL
在前⾯说过,Native⽀付的回调URL设置为/wxpay/native/,当⽤户扫描上述⼆维码时,会调⽤该回调URL。

URL需要调⽤订单信息Package返回给⽤
户,⽽该Package是由WxPayHelper类的 create_native_package()实现,调⽤代码如下:
[js]
1. <?php
2. include_once("WxPayHelper.php");$commonUtil = new CommonUtil();
3. $wxPayHelper = new WxPayHelper();
4. $wxPayHelper->setParameter("bank_type", "WX");
5. $wxPayHelper->setParameter("body", "微信⽀付开发教程");
6. $wxPayHelper->setParameter("partner", PARTNERID);
7. $wxPayHelper->setParameter("out_trade_no", $commonUtil->create_noncestr());
8. $wxPayHelper->setParameter("total_fee", "1");
9. $wxPayHelper->setParameter("fee_type", "1");
10. $wxPayHelper->setParameter("notify_url", "/wxpay/notify/");
11. $wxPayHelper->setParameter("spbill_create_ip", $_SERVER['REMOTE_ADDR']);
12. $wxPayHelper->setParameter("input_charset", "GBK");
13. $native_package = $wxPayHelper->create_native_package();
14. echo $native_package;
15. ?>
上述代码中,参数的配置和JS API⽀付⼀样,只是最后调⽤的⽀付⽅式不⼀样。

与此同时,微信公众平台将会向回调URL推送XML格式的数据。

这些数据中包含签名字段,可以⽤来验证是否是真正的⽀付⼆维码,但这个验证的必要性不是很⼤。

⽽回调
URL也会返回⼀个XML格式的数据给微⽤户,⽤户才能看到他所交易的商品信息的内容,这个XML的格式如下:
[js]
1. <xml>
2. <AppId><![CDATA[wxb489e8caeabcdefg]]></AppId>
3. <Package><!
[CDATA[bank_type=WX&body=%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B&fee_type=1&input_charset=GBK&notify_
4. u r l = h t t p % 3 A % 2 F % 2 F w w w . d o u c u b e .
5. com%2Fwxpay%2Fnotify%2F&out_trade_no=RaurRyM00lk9JZ8H&partner=1201234567&spbill_create_ip=58.60.3.185&total_fee=1&sign=C580F2994F7A4DA6E31AA89549DEB494]]>
</Package>
6. <TimeStamp>1408027935</TimeStamp><NonceStr><![CDATA[7omKw6AMZOq8022u]]></NonceStr>
7. <RetCode>0</RetCode><RetErrMsg><![CDATA[ok]]></RetErrMsg>
8. <AppSignature><![CDATA[e01a70076d66e5a37f19aedc5074611b7d472882]]></AppSignature>
9. <SignMethod><![CDATA[sha1]]></SignMethod>
10. </xml>
如果商品已过期或有其他错误,则可以在上述返回XML数据中的RetCode和RetErrMsg中体现出来。

例如:RetCode为其他⾮0值,RetErrMsg为“该商品已下架”。

交易通知
在上述JS API或Native⽀付完成后,将向/wxpay/notify/发送交易通知,并且带上URL参数,⼀个完整的带参数URL如下:
[js]
1. <a href="/wxpay/notify/index.php?discount=0&fee_type=1&input_charset=GBK&notify_id=xhLwKoKHzIQeMSQrEMJ7WXJNxyPKaUmxsn--
xLtq4FT7LkAeFe-
IHd_ARlj7kdyYUavoFfz5v2We9P6GEIv7zGgoVlT4gP2I&out_trade_no=omeDreZkCTQOuZSB&partner=1201234567&product_fee=1&sign=D18E640BDEC42424C4233B18CDBA88C2&sign_ty 2. <a href="/wxpay/notify/index.php?discount=0&fee_type=1&input_charset=GBK&notify_id=xhLwKoKHzIQeMSQrEMJ7WXJNxyPKaUmxsn--
xLtq4FT7LkAeFe-
IHd_ARlj7kdyYUavoFfz5v2We9P6GEIv7zGgoVlT4gP2I&out_trade_no=omeDreZkCTQOuZSB&partner=1201234567&product_fee=1&sign=D18E640BDEC42424C4233B18CDBA88C2&sign_ty discount=0&fee_type=1&input_charset=GBK&notify_id=xhLwKoKHzIQeMSQrEMJ7WXJNxyPKaUmxsn--xLtq4FT7LkAeFe-
IHd_ARlj7kdyYUavoFfz5v2We9P6GEIv7zGgoVlT4gP2I&out_trade_no=omeDreZkCTQOuZSB&partner=1201234567&product_fee=1&sign=D18E640BDEC42424C4233B18CDBA88C2&sign_ty 3. 34567201408143324765725&transport_fee=0
同时,微信还发送POST数据,XML格式如下:
[js]
1. <xml>
2. <OpenId><![CDATA[oWWVStzuQl6Gz-pj39_Gk1lvnfoY]]></OpenId>
3. <AppId><![CDATA[wxb489e8caeabcdefg]]></AppId>
4. <IsSubscribe>1</IsSubscribe>
5. <TimeStamp>1407980575</TimeStamp>
6. <NonceStr><![CDATA[WW8xQ6th6ybgy0lF]]></NonceStr>
7. <AppSignature><![CDATA[30e70187f5c50586394293cacd2f6c1caac95727]]></AppSignature>
8. <SignMethod><![CDATA[sha1]]></SignMethod>
9. </xml>
注意,URL和XML中包含了此次交易的很多重要信息,其中有三项参数,分别是商户订单号out_trade_no,交易号transaction_id及XML数据中的OpenID,这⼏个参数将在后续
很多接⼝中使⽤到。

订单查询
订单查询API的URL为:
h t t p s : / / a p i . w e i x i n . q q . c o m / p a y /orderquery?access_token=xxxxxx
URL中的参数只包含微信公众平台凭证access_token,⽽订单查询的真正数据是放在PostData中的,格式如下:
[js]
1. {
2. "appid":"wwwwb4f85f3a797777",
3. "package":"out_trade_no=11122&partner=1900090055&sign=4e8d0df3da0c3d0df38f",
4. "timestamp":"1369745073",
5. "app_signature":"53cca9d47b883bd4a5c85a9300df3da0cb48565c",
6. "sign_method":"sha1"
7. }
订单查询这⼀接⼝,开发⽂档中并没有给出Demo,所以需要⾃⼰来实现。

其中关键点是⽣成参数package中的sign和app_signature。

其中,sign是对参数字典序排序并使
⽤“&”联合起来,最后加上&key=partnerkey(唯⼀分配),进⾏md5运算,再转成全⼤写,最终得到sign。

⽽app_signature则是根据⽀付签名(paySign)⽣成⽅法中所讲的签
名⽅式⽣成,参加签名字段为:appid、 appkey、package、timestamp。

相关代码实现如下所⽰:
[js]
1. $sign= strtoupper(md5("out_trade_no=JfuKdiBig4zZnE4n&partner=1201234567&key=asdfas
2. dfasdfasdfasdfasdfasdfasdf"));
3.
4. $package = "out_trade_no=JfuKdiBig4zZnE4n&partner=1201234567&sign=".$sign;
5. $obj['appid'] = "wx0000000000000000";
6. $obj['appkey'] = "8mruTNOGeX8OVUlIYxIyw6kxCRvdJENpWpw8mruTNOGeX8OVUlIYxIyw6kxCRvd
7. JENpWpw8mruTNOGeX8OVUlIYxIyw6kxCRvdJENpWpw8mruTNOGeX8OVUlIYxIyw6k";
8. $obj['package'] = $package;
9. $obj['timestamp'] = time();
10. $WxPayHelper = new WxPayHelper();//get_biz_sign函数为protected类型,可改为public
11. $app_signature = $WxPayHelper->get_biz_sign($obj);
发货通知
发货通知API的URL为:
h t t p s : / / a p i . w e i x i n . q q . c o m / p a y /delivernotify?access_token=xxxxxx
URL中的参数只包含微信公众平台凭证access_token,⽽发货通知的真正数据放在PostData中,格式如下:
[js]
1. {
2. "appid":"wwwwb4f85f3a797777","openid":"oX99MDgNcgwnz3zFN3DNmo8uwa-w",
3. "transid":"111112222233333",
4. "out_trade_no":"555666uuu",
5. "deliver_timestamp":"1369745073",
6. "deliver_status":"1",
7. "deliver_msg":"ok",
8. "app_signature":"53cca9d47b883bd4a5c8
9. <span style="font-family: Helvetica, T ahoma, A rial, s ans-serif; font-size: 14px;">5a9300df3da0cb48565c",</span><p>&nbsp;"sign_method":"sha1"
10. }</p>
发货通知也没有Demo,需要⾃⼰开发实现,其中的关键点也是⽣成app_signature,它根据⽀付签名(paySign)⽣成⽅法中所讲的签名⽅式⽣成,参加签名字段为:appid、appkey、openid、transid、out_trade_no、deliver_timestamp、 deliver_status、deliver_msg。

实现代码如下所⽰:
[js]
1. $deliver_timestamp = time();
2. $obj['appid'] = APPID;
3. $obj['appkey'] = APPKEY;
4. $obj['openid'] = "oWWVStzuQl6Gz-pj39_Gk1lvnfoY"; //交易通
5. 知XML中获得$obj['transid'] = "1201234567201408143324765725"; //jsapi中⽣成,交易通
6. 知URL中获得$obj['out_trade_no'] = "omeDreZkCTQOuZSB"; //jsapi中⽣成,交易通
7. 知URL中获得$obj['deliver_timestamp'] = $deliver_timestamp;
8. $obj['deliver_status'] = "1";
9. $obj['deliver_msg'] = "ok";
10. $WxPayHelper = new WxPayHelper();
11. $app_signature = $WxPayHelper->get_biz_
12. sign($obj);
告警通知
告警通知的URL为申请微信⽀付时设置的/wxpay/alarm/,微信后台将向
该URL推送包含PostData的XML数据,数据中包含错误类型、错误描述、错误详情等信息。

告警数据在接收后需要写⼊到系统告警模块中,并要求商户尽快做出处理,以免影响线上经营。

维权通知
维权通知的URL为申请微信⽀付时设置的/wxpay/rights/,⽤户在新增投诉单及确认处理完毕投诉后,微信后台都会向该URL推送包含PostData的XML 数据,数据中包含维权内容信息。

维权通知是被动接收到的通知,接收到后,最好能使⽤模版消息提醒⾃⼰,以免错过处理时限。

标记投诉处理
标记客户投诉处理状态API的URL为:https:///payfeedback/update?access_token=xxxxx&openid=XXXX&feedbackid=xxxx
URL中的参数包含微信公众平台凭证access_token,客户投诉对应的单号feedbackid,以及OpenID。

填好参数后访问该URL即可返回“标记成功”的通知。

收货地址共享
收货地址共享的开发是微信⽀付开发中最复杂的部分,主要原因有:官⽅没有Demo;开发⽂档含糊不清;签名算法与之前的不⼀致,需要⾃⼰新实现;JS API回调后不能给出错误原因提⽰,调试没有⽅向感,需要开发者对⾼级接⼝中的OAuth2.0过程⾮常精通。

收货地址共享的完整实现步骤如下。

设置授权回调域名
OAuth2.0授权页⾯域名的配置在公众平台⽹站→开发者中⼼→接⼝权限表→⾼级接⼝→OAuth2.0⽹页授权中设置,将域名设置成微信⽀付授权⽬录中的域名,如。

构造请求授权回调URL
请求OAuth2.0授权的URL如下:
请求授权参数说明如表2所⽰。

表2 请求授权参数
这⾥,构造请求接⼝如下:
其中,/wxpay/getAddress.php是获取共享收获地址的页⾯。

作⽤域使⽤ snsapi_base,⽤户访问上述请求接⼝之后,将会跳转到页⾯
/wxpay /getAddress.php?code=02feab18436a5704c395c1b2e0451547&state=1。

获取共享收货地址
在getAddress.php页⾯,⾸先需要获取授权Access Token,这个Access Token是OAuth2.0授权时获得的,不是⾃定义菜单实现时的那个Access Toekn。

实现代码如下:
[js]
1. $appid = APPID;
2. $appsecret = APPSERCERT;
3. $code = $_GET["code"];
4. $access_token_url = "https://api.weixin.
5. /sns/oauth2/access_token?appid=$ap
6. pid&secret=$appsecret&code=$code&grant_
7. type=authorization_code";
8. $access_token_json = file_get_
9. contents($access_token_url);
10. $ a c c e s s _ t o k e n _ a r r a y = j s o n _
11. decode($access_token_json, true);
12. $access_token = $access_token_
13. array['access_token'];
然后需要计算出地址签名,参与addrSign签名的字段包括:appid、url(当前⽹页URL,包含code和state参数)、 timestamp、noncestr、accessToken(⽤户OAuth2.0授权凭证)。

这⾥scope、signType不参与签名。

这是共享收获地址中最关键的⼀步,它对所有待签名参数按照字段名ASCII码从⼩到⼤排序(字典序)后,使⽤URL键值对的格式(即key1=value1& amp;key2=value2……)拼接成字符串string1。

然后对string1作签名算法,字段名和字段值都采⽤原始值,并进⾏URL转义。

具体签名算法为addrSign = SHA1(string1)。

其代码如下:
[js]
1. $commonUtil = new CommonUtil();
2. $noncestr = $commonUtil->create_noncestr();
3. $timestamp = time();
4. $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
5. $wxPayHelper = new WxPayHelper();
6. $obj['appId'] = $appid;
7. $obj['url'] = $url;
8. $obj['timestamp'] = $timestamp;
9. $obj['noncestr'] = $noncestr;
10. $obj['accessToken'] = $access_token;//参数⼩写
11. foreach ($obj as $k => $v){ $obj2[strtolower($k)] = $v; }//字典序排序
12. ksort($obj2);//URL键值对拼成字符串
13. $ b i z S t r i n g = $ c o m m o n U t i l ->formatBizQueryParaMap($obj2, false);//sha1签名
14. $signature = sha1($bizString);
所有字段的值都获取成功以后,就赋值成收货地址接⼝的JS API中的变量值。

其他接⼝
其他接⼝还有退款接⼝、退款查询接⼝、对账单下载等,他们都有Demo,配置好后即可使⽤。

其中唯⼀要注意的就是退款接⼝的开发中pem证书的⽣成⽅法。

在退款接⼝的开发中,需要把pfx证书转换pem证书,转换后将pem⽂件作为私钥。

这需要⽤到OpenSSL这⼀⼯具,⼀般Linux已⾃带该功能。

在 Linux下的转换命令如下:
[js]
1. [root@FANGBEI wxpay]# openssl pkcs12 -in 1220220000.pfx -out 1220220000.pem
2. Enter Import Password:MAC verified OK
3. Enter PEM pass phrase:
4. Verifying - Enter PEM pass phrase:
5. [root@FANGBEI wxpay]# lltotal 8-rw-r--r-- 1 root root 4011 Aug 14 15:31 1220220000.pem-rw-r--r-- 1 root root 2717 Aug 14 15:28 1220220000.pfx
6. [root@FANGBEI wxpay]#
转换过程中需要输⼊商户ID来解密旧证书,然后设置新密码来加密新证书,新密码将在退款程序中配置使⽤。

总结
微信⽀付的开发⽂档中对部分关键技术阐述不详,不利于开发者快速理解上⼿。

本⽂从申请微信⽀付时⽬录及URL设置到各种接⼝开发中的核⼼部分都做了讲解,希望能为微信⽀付的开发⼈员提供帮助,加快开发速度。

相关文档
最新文档