0 說明
本文里說的微信公眾號支付對接指的是對接第三方支付平台的微信公眾號支付接口。 非微信支付官方文檔里的公眾號支付開發者文檔那樣的對接。不過,畢竟騰訊會把一部分渠道放給銀行或有支付牌照的支付機構,所以,第三方的公眾號支付也是在騰訊微信公眾號支付的基礎上做的改造。所以,基本的請求參數、簽名機制、響應參數、交互流程、數據格式都一致的。
1 引言
.....
2 方案概述
2.1 行業背景
微信支付,是基於微信客戶端提供的支付服務功能。同時向商戶提供銷售經營分析、賬戶和資金管理的功能支持。用戶通過掃描二維碼、反掃二維碼等多種方式調起微信支付模塊完成支付。
【二維碼支付正掃和反掃的區別在哪里】
正掃:即收款碼支付,也就是商戶提供收款二維碼,而消費者用手機APP掃碼支付
反掃:即付款碼支付,也就是消費者提供付款二維碼,而商戶使用掃描槍掃碼收款
2.1.1公眾號支付
公眾號支付是用戶在微信中打開商戶的H5頁面,商戶在H5頁面通過調用微信支付提供的JSAPI接口調起微信支付模塊完成支付。應用場景有:
◆ 用戶在微信公眾賬號內進入商家公眾號,打開某個主頁面,完成支付
◆ 用戶的好友在朋友圈、聊天窗口等分享商家頁面連接,用戶點擊鏈接打開商家頁面,完成支付
◆ 將商戶頁面轉換成二維碼,用戶掃描二維碼后在微信瀏覽器中打開頁面后完成支付
【兩種實現方式】:原生態js支付和封裝形式
2.2 業務實現流程
2.2.1 公眾賬號支付業務
微信內網頁支付時序圖
從圖可知,商戶系統涉及到的交互操作:
1. 生成圖文消息鏈接或二維碼
4. 生成商戶訂單
5. 調用統一下單API------------------------
6. 生成JSAPI頁面調用的支付參數並簽名
10.異步通知商戶結果-----------------------
13.查詢后台支付結果
14.調用查詢API,查詢支付結果--------------
3 數據格式
xml
4 數字簽名
4.2簽名算法
MD5簽名
MD5是一種摘要生成算法,通過在簽名原始串后加上商戶通信秘鑰,進行MD5運算,形成的摘要字符串即為簽名結果。
4.2.1生成隨機數算法
微信支付API接口協議中包含字段nonce_str,主要保證簽名不可預測。我們推薦生成隨機數算法如下:調用隨機數函數生成,將得到的值轉換為字符串。
5 補單機制
通知重試機制
6 公眾賬號支付接口
6.1 初始化請求接口
6.1.1 業務功能
初始化JSAPI 請求,通過生成token_id 來進行交互驗證。
字段名 | 變量名 | 必填 | 類型 | 說明 |
是否原生態 | is_raw | √ | string(1) | 是否原生態 |
用戶openid | sub_openid | - | string(128) | 微信用戶關注商家公眾號的openid(注: 使用測試商戶號不需要傳用戶openid; 切換正式的商戶號需獲取openid,並把獲取的openid 值傳給sub_openid。在切換成正式商戶號傳sub_openid 參數前,需提供正式商戶號和公眾號(服務號)appid 由渠道方配置,如果沒有配置的話,會報sub_appid and su_openid not match 錯誤,導致無法正常調用接口)---------后文介紹如何獲取openid |
前台地址 | callback_url | - | string(255) | 交易完成后跳轉的URL,需給絕對路徑,255 字符內格式 如:http://wap.tenpay.com/callback.asp 注:該地址只作為前端頁面的一個跳轉,需使用notify_url 通知結果作為支付最終結果。---------掃碼支付無此參數,即不需要商戶上送交易完成的h5頁面的。公眾號支付這個參數可選,商戶上送此頁面也可做一些營銷活動 |
是否限制信用卡 | limit_credit_pay |
- | String(32) | 限定用戶使用微信支付時能否使用信用卡,值為1,禁用信用卡;值為0 或者不傳此參數則不禁用---------默認是支持信用卡的,所以此字段可不上送, 微信個人轉賬是不支持信用卡消費的,這正是與公眾號支付和掃碼支付最大的區別 |
6.1.2 交互模式
請求:后台請求交互模式
返回結果+通知:后台請求交互模式+后台通知交互模式
注意:一般失敗的結果不簽名。
是否原生態is_raw 否String(1) 值為1:是(對應文檔6.2 一節);
值為0:否(對應文檔6.3 一節);不傳默認是0
6.2 原生態js 支付接口-----------------------原生態js支付實例:可以關注“1號外賣”這個公眾號或者直接在手機微信里錢包中的手機充值查看
6.3 公眾賬號JS 支付接口------------這是所謂的“封裝形式”(H5網頁支付模式)
6.3.1 業務功能
初始化JSAPI 請求,通過生成token_id 來進行交互驗證。
如調用時是用的原生態js 支付,此接口可以忽略
6.4 JS 支付通知接口
同《威富通掃碼支付接口文檔V1.4.2.pdf》6.2 掃碼通知接口
——————【重要】首先必看.txt——————————————————————————————————————————
1.開發時可以先使用測試商戶號和密鑰(demo中也都有寫)
測試
商戶號:'7551000001'
密鑰:'9d101c97133837e13dde2d32a5054abb'
2.測試商戶號有支付金額1元的限制,正式商戶號不會有
3.文檔中請求接口時傳的參數,必填為是的參數是必須要傳的(如有缺少會報錯),必填為否的參數可以傳也可以不傳
4.返回參數中必填為是的參數是一定會返回的,必填為否的參數則不一定返回,必須以實際接收到的參數為准
5.注意看文檔里提供了兩種實現方式,PHP和C#版本demo中實現的是我們封裝的形式,調用支付請求接口獲取到token_id,然后組裝成https://pay.swiftpass.cn/pay/jspay?token_id=9a0610bc519e782e6275e8c7dd94a445&showwxtitle=1這樣的鏈接在服務號中調起支付(用戶點擊頁面中的微信支付按鈕時實際上就是點擊的這個鏈接),JAVA版本則原生態js支付和封裝形式都有實現,原生態支付時是調用支付請求接口獲取到pay_info,取其中的參數去調用微信jsapi(這種方式最后調起微信時跟官方原生接口一致)
封裝形式只需點擊鏈接,簡單些,實例:可以關注“廣州市站”這個公眾號具體看看
原生態js支付實例:可以關注“1號外賣”這個公眾號或者直接在手機微信里錢包中的手機充值查看
6.demo中用測試商戶號不需要傳用戶openid,即sub_openid參數置空
但切換正式的商戶號時調用接口請求參數必須給sub_openid參數傳入openid,同時請提供正式商戶號和公眾號(服務號)appid由我方配置,如果沒有配置的話,會報sub_appid and su_openid not match錯誤,導致無法正常調用接口
獲取openid 指導文檔地址:http://www.cnblogs.com/txw1958/p/weixin76-user-info.html
獲取公眾號appid指導文檔地址:http://jingyan.baidu.com/article/6525d4b12af618ac7c2e9468.html
——————公眾號支付兩種實現模式的說明———————————————————————————————————————
公眾號支付流程是先調用接口文檔6.1一節初始化請求接口獲取到相應的返回值token_id和pay_info(對應的值是json 格式字符串,僅當is_raw=1時才返回),返回參數按 XML 的格式示例如下圖:
然后下一個步驟分為了兩個不同的模式:6.2一節原生態js支付和6.3一節公眾賬號JS支付
6.2原生態js支付
調用微信jspai方法,此方法只能在微信內置瀏覽器調用,在其他瀏覽器中無效,示例如下(注:示例代碼中appId這六個參數有大小寫,對應的值就是初始化請求接口中返回參數pay_info的值):
function jsApiCall() { WeixinJSBridge.invoke( 'getBrandWCPayRequest',{ "appId" : "wx1f87d44db95cba7a", //公眾號名稱,由商戶傳入
"timeStamp": "1468591622013", //時間戳,自1970 年以來的秒數
"nonceStr" : "1468591622013", //隨機串
"package" : "prepay_id=wx201607152207013ae4e376760784153308", "signType" : "MD5", //微信簽名方式:
"paySign" : "AD5A9E19D38002461812E09C0910A815" //微信簽名,
},function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { // 此處可以使用此方式判斷前端返回,微信團隊鄭重提示:res.err_msg 將在用戶支付成功后返回ok,但並不保證它絕對可靠,。
document.location.href="pay_success.jsp"; } /* for(var i in res){ alert(res[i]); } */ } ); }
最后在網頁代碼里調用這個js方法就可調起微信支付比如:
<button type="button" onclick="jsApiCall()" >微信支付</button>
但想正常彈出支付密碼框,商戶方開發人員必須提供自己的支付授權目錄由服務商配置好,支付授權目錄即為jspai方法代碼所在頁面的文件路徑,如http://xxxx.com/zhifu/jsapi.html,授權目錄就是http://xxxx.com/zhifu/(如果沒有配置授權目錄的話,支付時會無法彈出密碼框或者提示當前頁面URL未注冊)
6.3公眾賬號JS支付
這種模式是由我們進行了封裝,用6.1一節初始化請求接口獲取到的token_id值組裝成https://pay.swiftpass.cn/pay/jspay?token_id=1315838a57d0c83df0b62816220da070&showwxtitle=1這樣的鏈接在手機微信中調起支付(用戶點擊頁面中的微信支付按鈕時實際上就是點擊的這個鏈接,可以將鏈接放在手機微信文件傳輸助手去點擊測試,注意token_id的值要更換有效的),這種模式不用實現jsapi方法,也不要提供支付授權目錄。
對比6.2一節原生態js支付,直接點擊鏈接的這個形式在支付彈出確認支付的彈出框時會多一個上面綠色下面白色背景頁面,實際上這個背景頁面是我們封裝好的支付授權目錄頁面(對應固定的授權目錄https://pay.swiftpass.cn/pay/),這也是不用商戶提供支付授權目錄的原因所在。
效果如圖:
總的來說,封裝的鏈接形式開發時簡單些,且無需商戶提供自己的授權目錄(由我們固定配置https://pay.swiftpass.cn/pay/),但體驗可能沒有原生態js支付好。
參考:
微信支付開發文檔 https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付公眾號支付開發文檔 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
微信公眾號支付H5調用詳解(附代碼)http://blog.csdn.net/qq_28590639/article/details/49099275
——————獲取微信用戶的openid———————————————————————————————————————
關於用戶openid:在關注者與公眾號產生消息交互后,公眾號可獲得關注者的OpenID(加密后的微信號,每個用戶對每個公眾號的OpenID是唯一的。對於不同公眾號,同一用戶的openid不同)。
獲取openid屬於微信公眾平台開發的范疇,可參考以下地址:http://www.cnblogs.com/txw1958/p/weixin76-user-info.html 第三節《通過OAuth2.0方式不彈出授權頁面獲得用戶基本信息》
step1:配置回調域名
登陸微信公眾平台,菜單“設置”→“公眾號設置”→功能設置→網頁授權域名
用戶在網頁授權頁同意授權給公眾號后,微信會將授權數據傳給一個回調頁面,回調頁面需在此域名下,以確保安全可靠。
以上定義似乎會把人帶入誤區,我一開始被整懵了。配了個回調地址,其實不用,只需要配置授權訪問的域名就ok了,這里我配置的是testpcenter.shenbianhui.cn。注意,要保證域名可訪問並且要把MP_verify_****.txt放到站點相應的目錄下,否則點擊“確認”按鈕會提示的。
step2:構造微信用戶訪問的url:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb8239ab824d12860&redirect_uri=http://testpcenter.shenbianhui.cn/QRCodeDemo/WeixinJSPay.aspx&response_type=code&scope=snsapi_base&state=1#wechat_redirect
其中,頁面URL中的 scope=snsapi_base 表示應用授權作用域為不彈出授權頁面,直接跳轉。
這時,我們在redirect_uri指向的頁面“http://testpcenter.shenbianhui.cn/QRCodeDemo/WeixinJSPay.aspx”程序里,就可獲得get方式的兩個參數值code和state。
step3:根據code獲取openid:
請求url:https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxb8639ab824d12861&secret=0e8d1234fd5345da5ea8e5fab61abcd7&code=02a9bed29b2185a9f0ed3a48fe56e700&grant_type=authorization_code
返回值:{"access_token":"k2iC-Bfce_1ukB1UffUnAB8AnFvGp_8_lvKiMTKF_hILcjjbKpFRrtmWJ5KeWvPOxEu6wsqvT4-oQzYyXVMM__sfTCBJycWupAOLdEXOtrM","expires_in":7200,"refresh_token":"g39EWv6L4Fl7PVo859QPMw_VIMCVMCTco30Lk_-t35QP_mVhQjzvGlXk7MYk8nwkUsc-PxpT2a_kxcel5EAcwv41YizmdH7Hi7o57BIkKj4","openid":"o48_Ct5YigM7JDZ6x3Havr4kgzQQ","scope":"snsapi_base"}
注意,二般情況下,當code非法時,返回的是{"errcode":40029,"errmsg":"invalid code, hints: [ req_id: 1.Dnsa0402th10 ]"}
程序代碼如下:
public partial class WeixinJSPay : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { tbOpenId.Text = GetOpenIdByCode(); } /// <summary>
/// 微信用戶訪問時,獲取其openid /// </summary>
private string GetOpenIdByCode() { /* * 給微信用戶的請求地址: https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb8239ab824d12860&redirect_uri=http://testpcenter.shenbianhui.cn/QRCodeDemo/WeixinJSPay.aspx&response_type=code&scope=snsapi_base&state=1#wechat_redirect */ LogHelper.Write("--------:" + Request.Url);//示例:http://testpcenter.shenbianhui.cn/QRCodeDemo/WeixinJSPay.aspx?code=001bxIJx1Pi1ge0bZpLx1AAAJx1bxIJb&state=1
string code = Request["code"]; if (string.IsNullOrEmpty(code)) return "未獲取到code參數"; string url = string.Format( "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", "wxb8239ab824d12860", "0e8d4313fd5345da5ea8e5fab61ddae7", code); LogHelper.Write("--------請求openid:" + url); string resultJson = CommonModel.WebCommon.SendStreamStr("", url); LogHelper.Write("--------響應報文:" + resultJson); dynamic model = JsonConvert.DeserializeObject<dynamic>(resultJson); if (model.openid == null) return "未獲取到openid"; return model.openid; } }