快速上手,使用SDK只需三步即可接入“被掃支付”
第一步,初始化SDK
//--------------------------------------------------------------------
//第一步:初始化SDK(只需全局初始化一次即可)
//--------------------------------------------------------------------
WXPay.initSDKConfiguration(
//簽名算法需要用到的秘鑰
"40a8f8aa8ebe45a40bdc4e0f7307bc66",
//公眾賬號(hào)ID,成功申請(qǐng)公眾賬號(hào)后獲得
"wxf5b5e87a6a0fde94",
//商戶ID,成功申請(qǐng)微信支付功能之后通過官方發(fā)出的郵件獲得
"10000097",
//子商戶ID,受理模式下必填;
"",
//HTTP證書在服務(wù)器中的路徑,用來加載證書用
"C:/wxpay_scanpay_java_cert/10000097.p12",
//HTTP證書的密碼,默認(rèn)等于MCHID
"10000097"
);
第二步,準(zhǔn)備好提交給API的數(shù)據(jù)(scanPayReqData
)
//--------------------------------------------------------------------
//第二步:準(zhǔn)備好提交給API的數(shù)據(jù)(scanPayReqData)
//--------------------------------------------------------------------
//1)創(chuàng)建一個(gè)可以用來生成數(shù)據(jù)的bridge,這里用的是一個(gè)專門用來測試用的Bridge,商戶需要自己實(shí)現(xiàn)
BridgeForScanPayBusinessTest bridge = new BridgeForScanPayBusinessTest();
//2)從bridge里面拿到數(shù)據(jù),構(gòu)建提交被掃支付API需要的數(shù)據(jù)對(duì)象
ScanPayReqData scanPayReqData = new ScanPayReqData(
//這個(gè)是掃碼終端設(shè)備從用戶手機(jī)上掃取到的支付授權(quán)號(hào),有效期是1分鐘
bridge.getAuthCode(),
//要支付的商品的描述信息,用戶會(huì)在支付成功頁面里看到這個(gè)信息
bridge.getBody(),
//支付訂單里面可以填的附加數(shù)據(jù),API會(huì)將提交的這個(gè)附加數(shù)據(jù)原樣返回
bridge.getAttach(),
//商戶系統(tǒng)內(nèi)部的訂單號(hào),32個(gè)字符內(nèi)可包含字母, 確保在商戶系統(tǒng)唯一
bridge.getOutTradeNo(),
//訂單總金額,單位為“分”,只能整數(shù)
bridge.getTotalFee(),
//商戶自己定義的掃碼支付終端設(shè)備號(hào),方便追溯這筆交易發(fā)生在哪臺(tái)終端設(shè)備上
bridge.getDeviceInfo(),
//訂單生成的機(jī)器IP
bridge.getSpBillCreateIP(),
//訂單生成時(shí)間,格式為yyyyMMddHHmmss,如2009年12月25日9點(diǎn)10分10秒表示為20091225091010
bridge.getTimeStart(),
//訂單失效時(shí)間,格式同上
bridge.getTimeExpire(),
//商品標(biāo)記,微信平臺(tái)配置的商品標(biāo)記,用于優(yōu)惠券或者滿減使用
bridge.getGoodsTag()
);
第三步,準(zhǔn)備好一個(gè)用來處理各種結(jié)果分支的監(jiān)聽器(resultListener
)
//--------------------------------------------------------------------
//第三步:準(zhǔn)備好一個(gè)用來處理各種結(jié)果分支的監(jiān)聽器(resultListener)
//--------------------------------------------------------------------
//這個(gè)是Demo里面寫的一個(gè)默認(rèn)行為,商戶需要根據(jù)自身需求來進(jìn)行完善
DefaultScanPayBusinessResultListener resultListener = new DefaultScanPayBusinessResultListener();
//--------------------------------------------------------------------
//搞定以上三步后執(zhí)行業(yè)務(wù)邏輯
//--------------------------------------------------------------------
WXPay.doScanPayBusiness(scanPayReqData,resultListener);
Demo簡單說明
最佳實(shí)踐
- 被掃支付業(yè)務(wù)流程最佳實(shí)踐
- 支付業(yè)務(wù)邏輯分支處理最佳實(shí)踐
- 商戶系統(tǒng)接入SDK最佳實(shí)踐
- 商戶系統(tǒng)部署最佳實(shí)踐
高級(jí)自定義
“被掃支付”高級(jí)知識(shí)
Demo包含的內(nèi)容
Demo里面需要大家關(guān)注的主要有三個(gè)地方:
四個(gè)業(yè)務(wù)Demo,位于
src/main/java/com/tencent/business/
目錄里面,這些demo將會(huì)教大家如何調(diào)用SDK里面封裝好的業(yè)務(wù)邏輯。橋接器
bridge
,位于src/main/java/com/tencent/bridge/
目錄里面。里面目前有4個(gè)bridge,分別對(duì)應(yīng)4個(gè)業(yè)務(wù)demo。這個(gè)東西是用來對(duì)接商戶系統(tǒng)邏輯,產(chǎn)生SDK請(qǐng)求所需要的特定參數(shù)用的,請(qǐng)大家按照API文檔的說明實(shí)現(xiàn)這些參數(shù)的產(chǎn)生邏輯。監(jiān)聽器
listener
,位于src/main/java/com/tencent/listener/
目錄里面,這幾個(gè)listener
都是對(duì)業(yè)務(wù)邏輯各種可能返回事件的默認(rèn)處理,商戶需要自己實(shí)現(xiàn)更加具體的處理邏輯。
Demo依賴的配置項(xiàng)
- 打開demo工程里的
wxpay.properties
文件可以看到里面有6個(gè)配置項(xiàng)(該demo里面用的是一個(gè)mchid為10000097的測試號(hào))
這些關(guān)鍵配置項(xiàng)的作用分別為:
- | 名稱 | 用途 | 來源 |
---|---|---|---|
1 | KEY | 簽名算法需要用到的秘鑰 | 成功申請(qǐng)微信支付功能之后通過官方發(fā)出的郵件獲得 |
2 | APPID | 公眾賬號(hào)ID | 成功申請(qǐng)公眾賬號(hào)后獲得 |
3 | MCHID | 商戶ID | 成功申請(qǐng)微信支付功能之后通過官方發(fā)出的郵件獲得 |
4 | SUBMCHID | 子商戶ID | 受理模式下必須要有的一個(gè)子商戶ID |
5 | CERT_LOCAL_PATH | HTTP證書在服務(wù)器中的路徑,用來加載證書用 | 成功申請(qǐng)微信支付功能之后通過官方發(fā)出的郵件獲得“HTTPS證書”,這個(gè)配置項(xiàng)就是“HTTP證書”在服務(wù)器上所部署的路徑(demo中需要的證書文件就是docs/https_cert_for_test/文件夾中的10000097.cert) |
6 | CERT_PASSWORD | HTTP證書的密碼,默認(rèn)等于MCHID | 成功申請(qǐng)微信支付功能之后通過官方發(fā)出的郵件獲得 |
這些配置項(xiàng)用來對(duì)SDK進(jìn)行一次初始化的時(shí)候使用。初始化方法見上面的“第一步,初始化SDK”
Demo需要商戶自己實(shí)現(xiàn)的IBridge

從上圖可見IBridge橋接器其實(shí)就是定義了請(qǐng)求API時(shí)需要提交的各種參數(shù)的產(chǎn)生接口,這些接口跟商戶自己的系統(tǒng)是緊密結(jié)合的,商戶自己需要根據(jù)具體業(yè)務(wù)系統(tǒng)的實(shí)際情況,按照API文檔定義的格式來產(chǎn)生相應(yīng)的參數(shù)給到調(diào)用API時(shí)使用。
舉個(gè)例子,IBridge里面定義了一個(gè)非常關(guān)鍵的接口,叫g(shù)etAuthCode(),這個(gè)接口的作用就是用來返回一個(gè)合法的“授權(quán)碼”供調(diào)用API時(shí)用。
/**
* 獲取auth_code,這個(gè)是掃碼終端設(shè)備從用戶手機(jī)上掃取到的支付授權(quán)號(hào),這個(gè)號(hào)是跟用戶用來支付的銀行卡綁定的,有效期是1分鐘
* @return 授權(quán)碼
*/
public String getAuthCode(){
//由于這個(gè)authCode有效期只有1分鐘,所以實(shí)際測試SDK的時(shí)候也可以手動(dòng)將微信刷卡界面一維碼下的那串?dāng)?shù)字輸入進(jìn)來
return "120242957324236112";
}
以上只是簡單的hardcode
(用來先簡單手動(dòng)輸入“授權(quán)碼”調(diào)試API是否能正常返回?cái)?shù)據(jù)時(shí)用),實(shí)際上商戶自己在實(shí)現(xiàn)這個(gè)接口的時(shí)候就需要根據(jù)自己實(shí)際系統(tǒng)來進(jìn)行設(shè)計(jì)了,例如需要去監(jiān)聽“掃碼槍”等具備一維碼/二維碼掃描功能的外設(shè),當(dāng)成功掃描到這串“授權(quán)碼”的時(shí)候,將其保存下來,然后觸發(fā)提交支付的API調(diào)用,調(diào)用時(shí)讓IBridge
橋接器中的getAuthCode()
接口取得剛剛掃描到的授權(quán)碼,作為參數(shù)傳給支付API。
被掃支付業(yè)務(wù)流程最佳實(shí)踐
被掃支付整個(gè)完成流程將會(huì)涉及到“查詢”和“撤銷”等請(qǐng)求,這里給出建議實(shí)現(xiàn)的流程供大家參考,SDK里面的ScanPayBusiness
就是按照這個(gè)流程來設(shè)計(jì)的:

從上圖可見主要流程分為四種情況:
- 直接扣款成功:直接返回成功
- 扣款明確失敗:走撤銷流程,返回失敗(建議提示具體的失敗信息,指示用戶進(jìn)行下一步操作)
- 需輸入密碼:走查單流程,如果查詢了一定時(shí)間還是沒有成功則當(dāng)失敗處理,直接走撤銷
- 扣款未知失敗:先走查單流程,如果查詢了一定時(shí)間還是沒有成功則當(dāng)失敗處理,直接走撤銷
兩個(gè)關(guān)鍵流程解釋:
- 有限次查詢流程:這里會(huì)根據(jù)設(shè)定的“查詢次數(shù)”(用戶可以自定義)和“查詢間隔”來進(jìn)行輪詢,超過了查詢次數(shù)之后還是沒有查詢到“支付成功”的情況會(huì)自動(dòng)轉(zhuǎn)入“撤銷流程”
- 撤銷流程:這里會(huì)根據(jù)設(shè)定的“查詢間隔”進(jìn)行不停地輪詢撤銷API,API會(huì)通過recall字段來告訴商戶側(cè)需不需要繼續(xù)輪詢,如果“recall=Y”或是“撤銷結(jié)果成功”都表示不需要再輪詢了,然后到達(dá)“支付失敗”的最終狀態(tài)。
(以上最佳實(shí)踐已經(jīng)在SDK的ScanPayBusiness
里面封裝好了)
支付業(yè)務(wù)邏輯分支處理最佳實(shí)踐

ScanPayBusiness
里面的ResultListener
接口定義了支付流程中可能走到的8個(gè)分支,分別是:
public interface ResultListener {
//API返回ReturnCode不合法,支付請(qǐng)求邏輯錯(cuò)誤,請(qǐng)仔細(xì)檢測傳過去的每一個(gè)參數(shù)是否合法,或是看API能否被正常訪問
void onFailByReturnCodeError(ScanPayResData scanPayResData);
//API返回ReturnCode為FAIL,支付API系統(tǒng)返回失敗,請(qǐng)檢測Post給API的數(shù)據(jù)是否規(guī)范合法
void onFailByReturnCodeFail(ScanPayResData scanPayResData);
//支付請(qǐng)求API返回的數(shù)據(jù)簽名驗(yàn)證失敗,有可能數(shù)據(jù)被篡改了
void onFailBySignInvalid(ScanPayResData scanPayResData);
//用戶用來支付的二維碼已經(jīng)過期,提示收銀員重新掃一下用戶微信“刷卡”里面的二維碼
void onFailByAuthCodeExpire(ScanPayResData scanPayResData);
//授權(quán)碼無效,提示用戶刷新一維碼/二維碼,之后重新掃碼支付"
void onFailByAuthCodeInvalid(ScanPayResData scanPayResData);
//用戶余額不足,換其他卡支付或是用現(xiàn)金支付
void onFailByMoneyNotEnough(ScanPayResData scanPayResData);
//支付失敗
void onFail(ScanPayResData scanPayResData);
//支付成功
void onSuccess(ScanPayResData scanPayResData);
}
Demo里面用到的DefaultScanPayBusinessResultListener
就是實(shí)現(xiàn)了以上這8個(gè)接口。
這里有幾點(diǎn)處理建議:
-
onFailByReturnCodeError
、onFailByReturnCodeFail
、onFailBySignInvalid
這3種屬于程序邏輯問題,建議商戶自己做好日志監(jiān)控,發(fā)現(xiàn)問題要及時(shí)讓工程師進(jìn)行定位處理; -
onFailByAuthCodeExpire
、onFailByAuthCodeInvalid
、onFailByMoneyNotEnough
這三種屬于用戶自身的問題,建議商戶把具體出錯(cuò)信息提示給用戶,指導(dǎo)用戶進(jìn)行下一步操作。(具體出錯(cuò)信息可以通過scanPayResData.getErr_code_des()
獲取得到)
商戶系統(tǒng)接入SDK最佳實(shí)踐
- 生成一個(gè)新的訂單
out_trade_no
- 輸入訂單金額
total_fee
- 啟動(dòng)掃碼槍功能供用戶進(jìn)行掃碼
- 掃碼器獲取授權(quán)碼
auth_code
,并回傳給SDK - SDK提交支付請(qǐng)求
-
SDK處理API返回的數(shù)據(jù)
img
商戶系統(tǒng)部署最佳實(shí)踐
- 由于整套系統(tǒng)必須采用HTTPS來保證安全性,所以這里的支付請(qǐng)求必須由商戶的后臺(tái)系統(tǒng)來發(fā)起;
- 商戶系統(tǒng)跟SDK的對(duì)接主要就是實(shí)現(xiàn)IBridge里面的接口;
-
從本demo里面有JUnit單元測試用例,商戶開發(fā)者可以參考下這個(gè)示例;
img
高級(jí)自定義:1)自定義查詢流程和撤銷流程
商戶可以根據(jù)自己的實(shí)際需要來配置支付業(yè)務(wù)流程中的“查詢流程”的“查詢次數(shù)”和“查詢間隔”;“撤銷流程”的“查詢間隔”,例如:
//自定義調(diào)用查詢接口的間隔
scanPayBusiness.setWaitingTimeBeforePayQueryServiceInvoked(3000);
//自定義調(diào)用查詢接口的次數(shù)
scanPayBusiness.setPayQueryLoopInvokedCount(1);
//自定義調(diào)用撤銷接口的間隔
scanPayBusiness.setWaitingTimeBeforeReverseServiceInvoked(2000);
高級(jí)自定義:2)使用自己的Https請(qǐng)求器
可能有些商戶自己系統(tǒng)里面已經(jīng)擁有自己封裝得很完善的Https請(qǐng)求器了,想讓SDK的服務(wù)統(tǒng)一都走自己的Https請(qǐng)求器來發(fā)起請(qǐng)求的話,這里提供了一個(gè)配置項(xiàng)可以實(shí)現(xiàn)這個(gè)功能:
//自定義底層的HttpsRequest
Configure.setHttpsRequestClassName("com.tencent.httpsrequest.HttpsRequestForTest");
溫馨提示:自己實(shí)現(xiàn)的Https請(qǐng)求器必須實(shí)現(xiàn)IServiceRequest這個(gè)接口,可以參考SDK里面的HttpRequest的實(shí)現(xiàn)。
調(diào)用被掃支付API的協(xié)議規(guī)則
序號(hào)||
:-:|-|-
1|傳輸方式|為保證交易安全性,采用HTTPS傳輸
2|提交方式|采用POST方法提交
3|數(shù)據(jù)格式|提交和返回?cái)?shù)據(jù)都為XML格式,根節(jié)點(diǎn)名為xml
4|字符編碼|統(tǒng)一采用UTF-8字符編碼
5|簽名算法|MD5
6|簽名要求|請(qǐng)求和接收數(shù)據(jù)均需要校驗(yàn)簽名,簽名的方法在SDK里面已經(jīng)封裝好了
7|證書要求|調(diào)用申請(qǐng)退款、撤銷訂單接口需要商戶證書
8|判斷邏輯|先判斷協(xié)議字段返回,再判斷業(yè)務(wù)返回,最后判斷交易狀態(tài)
支付驗(yàn)證密碼規(guī)則
- 支付金額>300元的交易需要驗(yàn)證用戶支付密碼;
- 用戶賬號(hào)每天最多有10筆交易可以免密,超過后需要驗(yàn)證密碼;
- 微信支付后臺(tái)判斷用戶支付行為有異常情況,符合免密規(guī)則的交易也會(huì)要求驗(yàn)證密碼;