快速上手,使用SDK只需三步即可接入“被掃支付”
第一步,初始化SDK
//--------------------------------------------------------------------
//第一步:初始化SDK(只需全局初始化一次即可)
//--------------------------------------------------------------------
WXPay.initSDKConfiguration(
//簽名算法需要用到的秘鑰
"40a8f8aa8ebe45a40bdc4e0f7307bc66",
//公眾賬號(hào)ID,成功申請(qǐng)公眾賬號(hào)后獲得
"wxf5b5e87a6a0fde94",
//商戶ID,成功申請(qǐng)微信支付功能之后通過(guò)官方發(fā)出的郵件獲得
"10000097",
//子商戶ID,受理模式下必填;
"",
//HTTP證書(shū)在服務(wù)器中的路徑,用來(lái)加載證書(shū)用
"C:/wxpay_scanpay_java_cert/10000097.p12",
//HTTP證書(shū)的密碼,默認(rèn)等于MCHID
"10000097"
);
第二步,準(zhǔn)備好提交給API的數(shù)據(jù)(scanPayReqData)
//--------------------------------------------------------------------
//第二步:準(zhǔn)備好提交給API的數(shù)據(jù)(scanPayReqData)
//--------------------------------------------------------------------
//1)創(chuàng)建一個(gè)可以用來(lái)生成數(shù)據(jù)的bridge,這里用的是一個(gè)專門(mén)用來(lái)測(cè)試用的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ì)在支付成功頁(yè)面里看到這個(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è)用來(lái)處理各種結(jié)果分支的監(jiān)聽(tīng)器(resultListener)
//--------------------------------------------------------------------
//第三步:準(zhǔn)備好一個(gè)用來(lái)處理各種結(jié)果分支的監(jiān)聽(tīng)器(resultListener)
//--------------------------------------------------------------------
//這個(gè)是Demo里面寫(xiě)的一個(gè)默認(rèn)行為,商戶需要根據(jù)自身需求來(lái)進(jìn)行完善
DefaultScanPayBusinessResultListener resultListener = new DefaultScanPayBusinessResultListener();
//--------------------------------------------------------------------
//搞定以上三步后執(zhí)行業(yè)務(wù)邏輯
//--------------------------------------------------------------------
WXPay.doScanPayBusiness(scanPayReqData,resultListener);
Demo簡(jiǎn)單說(shuō)明
最佳實(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è)東西是用來(lái)對(duì)接商戶系統(tǒng)邏輯,產(chǎn)生SDK請(qǐng)求所需要的特定參數(shù)用的,請(qǐng)大家按照API文檔的說(shuō)明實(shí)現(xiàn)這些參數(shù)的產(chǎn)生邏輯。監(jiān)聽(tīng)器
listener,位于src/main/java/com/tencent/listener/目錄里面,這幾個(gè)listener都是對(duì)業(yè)務(wù)邏輯各種可能返回事件的默認(rèn)處理,商戶需要自己實(shí)現(xiàn)更加具體的處理邏輯。
Demo依賴的配置項(xiàng)
- 打開(kāi)demo工程里的
wxpay.properties文件可以看到里面有6個(gè)配置項(xiàng)(該demo里面用的是一個(gè)mchid為10000097的測(cè)試號(hào))
這些關(guān)鍵配置項(xiàng)的作用分別為:
| - | 名稱 | 用途 | 來(lái)源 |
|---|---|---|---|
| 1 | KEY | 簽名算法需要用到的秘鑰 | 成功申請(qǐng)微信支付功能之后通過(guò)官方發(fā)出的郵件獲得 |
| 2 | APPID | 公眾賬號(hào)ID | 成功申請(qǐng)公眾賬號(hào)后獲得 |
| 3 | MCHID | 商戶ID | 成功申請(qǐng)微信支付功能之后通過(guò)官方發(fā)出的郵件獲得 |
| 4 | SUBMCHID | 子商戶ID | 受理模式下必須要有的一個(gè)子商戶ID |
| 5 | CERT_LOCAL_PATH | HTTP證書(shū)在服務(wù)器中的路徑,用來(lái)加載證書(shū)用 | 成功申請(qǐng)微信支付功能之后通過(guò)官方發(fā)出的郵件獲得“HTTPS證書(shū)”,這個(gè)配置項(xiàng)就是“HTTP證書(shū)”在服務(wù)器上所部署的路徑(demo中需要的證書(shū)文件就是docs/https_cert_for_test/文件夾中的10000097.cert) |
| 6 | CERT_PASSWORD | HTTP證書(shū)的密碼,默認(rèn)等于MCHID | 成功申請(qǐng)微信支付功能之后通過(guò)官方發(fā)出的郵件獲得 |
這些配置項(xiàng)用來(lái)對(duì)SDK進(jìn)行一次初始化的時(shí)候使用。初始化方法見(jiàn)上面的“第一步,初始化SDK”
Demo需要商戶自己實(shí)現(xiàn)的IBridge

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

從上圖可見(jiàn)主要流程分為四種情況:
- 直接扣款成功:直接返回成功
- 扣款明確失?。鹤叱蜂N(xiāo)流程,返回失?。ńㄗh提示具體的失敗信息,指示用戶進(jìn)行下一步操作)
- 需輸入密碼:走查單流程,如果查詢了一定時(shí)間還是沒(méi)有成功則當(dāng)失敗處理,直接走撤銷(xiāo)
- 扣款未知失?。合茸卟閱瘟鞒?,如果查詢了一定時(shí)間還是沒(méi)有成功則當(dāng)失敗處理,直接走撤銷(xiāo)
兩個(gè)關(guān)鍵流程解釋:
- 有限次查詢流程:這里會(huì)根據(jù)設(shè)定的“查詢次數(shù)”(用戶可以自定義)和“查詢間隔”來(lái)進(jìn)行輪詢,超過(guò)了查詢次數(shù)之后還是沒(méi)有查詢到“支付成功”的情況會(huì)自動(dòng)轉(zhuǎn)入“撤銷(xiāo)流程”
- 撤銷(xiāo)流程:這里會(huì)根據(jù)設(shè)定的“查詢間隔”進(jìn)行不停地輪詢撤銷(xiāo)API,API會(huì)通過(guò)recall字段來(lái)告訴商戶側(cè)需不需要繼續(xù)輪詢,如果“recall=Y”或是“撤銷(xiāo)結(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ì)檢測(cè)傳過(guò)去的每一個(gè)參數(shù)是否合法,或是看API能否被正常訪問(wèn)
void onFailByReturnCodeError(ScanPayResData scanPayResData);
//API返回ReturnCode為FAIL,支付API系統(tǒng)返回失敗,請(qǐng)檢測(cè)Post給API的數(shù)據(jù)是否規(guī)范合法
void onFailByReturnCodeFail(ScanPayResData scanPayResData);
//支付請(qǐng)求API返回的數(shù)據(jù)簽名驗(yàn)證失敗,有可能數(shù)據(jù)被篡改了
void onFailBySignInvalid(ScanPayResData scanPayResData);
//用戶用來(lái)支付的二維碼已經(jīng)過(guò)期,提示收銀員重新掃一下用戶微信“刷卡”里面的二維碼
void onFailByAuthCodeExpire(ScanPayResData scanPayResData);
//授權(quán)碼無(wú)效,提示用戶刷新一維碼/二維碼,之后重新掃碼支付"
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種屬于程序邏輯問(wèn)題,建議商戶自己做好日志監(jiān)控,發(fā)現(xiàn)問(wèn)題要及時(shí)讓工程師進(jìn)行定位處理; -
onFailByAuthCodeExpire、onFailByAuthCodeInvalid、onFailByMoneyNotEnough這三種屬于用戶自身的問(wèn)題,建議商戶把具體出錯(cuò)信息提示給用戶,指導(dǎo)用戶進(jìn)行下一步操作。(具體出錯(cuò)信息可以通過(guò)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來(lái)保證安全性,所以這里的支付請(qǐng)求必須由商戶的后臺(tái)系統(tǒng)來(lái)發(fā)起;
- 商戶系統(tǒng)跟SDK的對(duì)接主要就是實(shí)現(xiàn)IBridge里面的接口;
-
從本demo里面有JUnit單元測(cè)試用例,商戶開(kāi)發(fā)者可以參考下這個(gè)示例;
img
高級(jí)自定義:1)自定義查詢流程和撤銷(xiāo)流程
商戶可以根據(jù)自己的實(shí)際需要來(lái)配置支付業(yè)務(wù)流程中的“查詢流程”的“查詢次數(shù)”和“查詢間隔”;“撤銷(xiāo)流程”的“查詢間隔”,例如:
//自定義調(diào)用查詢接口的間隔
scanPayBusiness.setWaitingTimeBeforePayQueryServiceInvoked(3000);
//自定義調(diào)用查詢接口的次數(shù)
scanPayBusiness.setPayQueryLoopInvokedCount(1);
//自定義調(diào)用撤銷(xiāo)接口的間隔
scanPayBusiness.setWaitingTimeBeforeReverseServiceInvoked(2000);
高級(jí)自定義:2)使用自己的Https請(qǐng)求器
可能有些商戶自己系統(tǒng)里面已經(jīng)擁有自己封裝得很完善的Https請(qǐng)求器了,想讓SDK的服務(wù)統(tǒng)一都走自己的Https請(qǐng)求器來(lái)發(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|證書(shū)要求|調(diào)用申請(qǐng)退款、撤銷(xiāo)訂單接口需要商戶證書(shū)
8|判斷邏輯|先判斷協(xié)議字段返回,再判斷業(yè)務(wù)返回,最后判斷交易狀態(tài)
支付驗(yàn)證密碼規(guī)則
- 支付金額>300元的交易需要驗(yàn)證用戶支付密碼;
- 用戶賬號(hào)每天最多有10筆交易可以免密,超過(guò)后需要驗(yàn)證密碼;
- 微信支付后臺(tái)判斷用戶支付行為有異常情況,符合免密規(guī)則的交易也會(huì)要求驗(yàn)證密碼;