微信支付關鍵點技術解析

本文將結合微信支付接口開發的實踐,從支付申請到各主要接口的使用方法等方面介紹微信支付的關鍵點技術。11月15日微信開發者大會(深圳)上招行信用卡服務號、南航服務號/企業號及長安汽車企業號等將分享實戰案例。

【編者按】由CSDN和《程序員》雜誌聯合舉辦的2014微信開發者大會,在經歷了北京站的成功後,應廣大微信開發者的強烈要求,主辦方將於11月15日在深圳舉辦微信開發者大會,不僅有南航微信服務號、 招商銀行信用卡服務號,還有長安汽車微信企業號等衆多知名微信開發領域技術專家和典型開發商都將帶來精彩演講(點擊報名)。

目前報名處於特惠票價階段,通過申請加入CSDN CTO俱樂部即可享受8折購票價格(票款中均含豪華午餐),在10月28日之前完成購票和付款流程的同學均將獲贈微信技術開發圖書一本+北京站講師演講視頻和講義(PDF)+全套深圳站講師講義(PDF)。


微信支付是由微信及財付通聯合推出的移動支付創新產品。如今,隨着微信支付的全面開放,相關需求也越來越多,很多開發人員進行微信支付開發及商家申請微信支付時,面臨着諸多疑惑。本文將結合微信支付接口開發的實踐,從支付申請到各主要接口的使用方法等方面介紹微信支付的關鍵點技術。

URL設置

目前,微信支付只能由通過微認證的服務號進行申請,訂閱號及未認證的服務號均無法申請。登錄微信公衆平臺後臺,在左側的欄目中可找到“微信支付”,點擊進入申請界面,可以看到第一項中的“商戶基本資料”,點擊右側的“填寫”按鈕後就進入了微信支付設置界面。

微信支付的目錄及URL沒有固定的設置方法,具體還需要根據自己的需求來定,表1是方倍工作室的方案。


表1 微信支付目錄及URL設置

需要注意的是,如果使用上述方法,要將域名換成自己的域名,其他的結構及層次可不變。這裏所有的URL沒有填寫實際的文件名,目的是爲了兼容不同的開發語言或框架。比如目錄下的默認文件既可能是index.php,也可能是index.aspx。微信支付申請完成後,便進入了微信支付測試階段,需要填寫支付測試目錄,測試目錄可以填寫爲:http://www.doucube.com/wxpay/test/。

接口開發

JS API支付

JS API支付的實現比較簡單,官方也提供了Demo,在此基礎上修改部分參數即可。修改後的

一個示例如下:

  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. "http://www.doucube.com/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()方法實現,實現代碼如下: 

  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如下所示:

  1. weixin://wxpay/bizpayurl?appid=wxb489e8caeabcdefg&noncestr=BBvdr5atZ9D7s08X&produc  
  2. tid=1234567890&sign=e15d2466a85cd62b530e2f690604e7502f67ccb5&timestamp=1408025996  

生成URL的二維碼有了上述支付鏈接後,還要把它轉成二維碼,PHP QR Code是一個開源的二維碼生成類庫,可使用它來生成上述Native URL,代碼如下:

  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, $filename, "L", "5", 2);  
  6. ?>  

PHP QR Code的使用很簡單,配置一下URL和文件名就可以了。執行上述代碼,就會在當前目錄下生成一個1234567890.png的二維碼圖片文件。

Navive支付回調URL

在前面說過,Native支付的回調URL設置爲http://www.doucube.com/wxpay/native/,當用戶掃描上述二維碼時,會調用該回調URL。URL需要調用訂單信息Package返回給用戶,而該Package是由WxPayHelper類的create_native_package()實現,調用代碼如下:

  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""http://www.doucube.com/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的格式如下:

  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支付完成後,將向http://www.doucube.com/wxpay/notify/發送交易通知,並且帶上URL參數,一個完整的帶參數URL如下:

  1. <a href="http://www.doucube.com/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_type=MD5&time_end=20140814094255&total_fee=1&trade_mode=1&trade_state=0&transaction_id=12012">  
  2. <a href="http://www.doucube.com/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_type=MD5&time_end=20140814094255&total_fee=1&trade_mode=1&trade_state=0&transaction_id=12012</a">http://www.doucube.com/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_type=MD5&time_end=20140814094255&total_fee=1&trade_mode=1&trade_state=0&transaction_id=12012</a</a>>  
  3. 34567201408143324765725&transport_fee=0  

同時,微信還發送POST數據,XML格式如下:

  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中的,格式如下:

  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。相關代碼實現如下所示:

  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中,格式如下:

  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, ​Tahoma, ​Arial, ​sans-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。實現代碼如下所示:

  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爲申請微信支付時設置的http://www.doucube.com/wxpay/alarm/,微信後臺將向

該URL推送包含PostData的XML數據,數據中包含錯誤類型、錯誤描述、錯誤詳情等信息。告警數據在接收後需要寫入到系統告警模塊中,並要求商戶儘快做出處理,以免影響線上經營。

維權通知

維權通知的URL爲申請微信支付時設置的http://www.doucube.com/wxpay/rights/,用戶在新增投訴單及確認處理完畢投訴後,微信後臺都會向該URL推送包含PostData的XML數據, 數據中包含維權內容信息。維權通知是被動接收到的通知,接收到後,最好能使用模版消息提醒自己,以免錯過處理時限。

標記投訴處理

標記客戶投訴處理狀態API的URL爲:https://api.weixin.qq.com/payfeedback/update?access_token=xxxxx&openid=XXXX&feedbackid=xxxx

URL中的參數包含微信公衆平臺憑證access_token,客戶投訴對應的單號feedbackid,以及OpenID。填好參數後訪問該URL即可返回“標記成功”的通知。

收貨地址共享

收貨地址共享的開發是微信支付開發中最複雜的部分,主要原因有:官方沒有Demo;開發文檔含糊不清;簽名算法與之前的不一致,需要自己新實現;JS API回調後不能給出錯誤原因提示,調試沒有方向感,需要開發者對高級接口中的OAuth2.0過程非常精通。收貨地址共享的完整實現步驟如下。

設置授權回調域名

OAuth2.0授權頁面域名的配置在公衆平臺網站→開發者中心→接口權限表→高級接口→OAuth2.0網頁授權中設置,將域名設置成微信支付授權目錄中的域名,如www.doucube.com。

構造請求授權回調URL

請求OAuth2.0授權的URL如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

請求授權參數說明如表2所示。


表2 請求授權參數

這裏,構造請求接口如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb489e8caeabcdefg&redirect_uri=http://www.doucube.com/wxpay/getAddress.php&response_type=code&scope=snsapi_base&state=1#wechat_redirect

其中,http://www.doucube.com/wxpay/getAddress.php是獲取共享收穫地址的頁面。作用域使用snsapi_base,用戶訪問上述請求接口之後,將會跳轉到頁面http://www.doucube.com/wxpay/getAddress.php?code=02feab18436a5704c395c1b2e0451547&state=1。

獲取共享收貨地址

在getAddress.php頁面,首先需要獲取授權Access Token,這個Access Token是OAuth2.0授權時獲得的,不是自定義菜單實現時的那個Access Toekn。實現代碼如下:

  1. $appid = APPID;  
  2. $appsecret = APPSERCERT;  
  3. $code = $_GET["code"];  
  4. $access_token_url = "https://api.weixin.  
  5. qq.com/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)。其代碼如下:

  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下的轉換命令如下:

  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設置到各種接口開發中的核心部分都做了講解,希望能爲微信支付的開發人員提供幫助,加快開發速度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章