你的用戶在結(jié)算頁(yè)面反復(fù)退出,不是因?yàn)椴幌敫犊睿侵Ц督缑婕虞d失敗或報(bào)錯(cuò)后無(wú)人給出清晰的引導(dǎo)。在移動(dòng)端,支付環(huán)節(jié)的摩擦直接轉(zhuǎn)化為訂單流失,因此接入微信和支付寶支付絕不能止步于“調(diào)通接口”,必須對(duì)兩端的流程、簽名機(jī)制、異步通知處理有完整的掌控。
支付能力不等于支付體驗(yàn):先理解兩套體系的共同骨架
無(wú)論你接的是微信支付還是支付寶,APP 端支付的底層邏輯都遵循“商戶后臺(tái)發(fā)起訂單 → 第三方支付平臺(tái)生成預(yù)支付交易 → APP 調(diào)起支付控件 → 用戶完成付款 → 第三方異步通知商戶后臺(tái) → 商戶更新訂單狀態(tài)”這一鏈條。兩者的接入流程相似,但參數(shù)命名、簽名算法、回調(diào)方式各有差異,混合接入時(shí)最容易出錯(cuò)的地方就是把這些差異當(dāng)作細(xì)節(jié)忽略。
你需要先完成三項(xiàng)前置工作:
- 簽約與開(kāi)通: 分別在微信支付商戶平臺(tái)和支付寶開(kāi)放平臺(tái)注冊(cè)企業(yè)賬號(hào),提交營(yíng)業(yè)執(zhí)照等信息,開(kāi)通 APP 支付產(chǎn)品。
- 獲取核心憑證: 微信支付會(huì)給你 AppID、商戶號(hào)(mchid)、API 密鑰(key) 以及證書(shū)(用于退款等敏感操作);支付寶會(huì)給你 AppID、商戶私鑰、支付寶公鑰(或證書(shū))。
- 配置應(yīng)用包名與簽名: 微信開(kāi)放平臺(tái)需要填寫(xiě)應(yīng)用的包名和應(yīng)用簽名;支付寶開(kāi)放平臺(tái)同樣要求在你創(chuàng)建的應(yīng)用中綁定應(yīng)用包名。這一步如果填錯(cuò),調(diào)起支付時(shí)會(huì)直接報(bào)“應(yīng)用未簽約”或“缺少參數(shù)”。
客戶端調(diào)起支付的正確節(jié)奏:服務(wù)端下單,客戶端憑票據(jù)拉起
不要試圖在 APP 里直接拼接金額、商戶號(hào)等參數(shù)發(fā)起支付,那等于把商戶密鑰暴露在前端。正確做法是客戶端向你的業(yè)務(wù)服務(wù)器請(qǐng)求預(yù)支付訂單,由服務(wù)端完成簽名,返回一個(gè)“支付憑據(jù)”給 APP,APP 再依此調(diào)起微信或支付寶的 SDK。
微信支付(APP 端)
微信支付 SDK 要求 APP 拿到一組簽名字段才能調(diào)起。這組字段由服務(wù)端生成,通常包括如下參數(shù)(以 V2 版為例,V3 流程更安全但調(diào)起參數(shù)形式類(lèi)似):
{
"appid": "YOUR_APPID",
"partnerid": "YOUR_MCHID",
"prepayid": "wx20220301123456abcdef...",
"package": "Sign=WXPay",
"noncestr": "隨機(jī)字符串",
"timestamp": "1646118000",
"sign": "簽名值"
}
服務(wù)端生成這些參數(shù)的步驟:
- 調(diào)用微信支付統(tǒng)一下單接口,使用商戶號(hào)和密鑰簽名,傳入商品描述、商戶訂單號(hào)、金額、通知地址等,獲取
prepayid(預(yù)支付交易會(huì)話標(biāo)識(shí))。 - 重新生成隨機(jī)字符串、時(shí)間戳,并按照微信規(guī)定的簽名算法(鍵值對(duì)按字典序排列,拼接 key,使用 MD5 或 HMAC-SHA256 加密)生成二次簽名。
- 將上述 JSON 返回給 APP。
APP 端使用微信 SDK 調(diào)起支付(Android 示例):
// 將服務(wù)端返回的 payReqData 映射為 PayReq 對(duì)象
PayReq request = new PayReq();
request.appId = payReqData.getString("appid");
request.partnerId = payReqData.getString("partnerid");
request.prepayId = payReqData.getString("prepayid");
request.packageValue = payReqData.getString("package");
request.nonceStr = payReqData.getString("noncestr");
request.timeStamp = payReqData.getString("timestamp");
request.sign = payReqData.getString("sign");
api.sendReq(request); // api 為 IWXAPI 實(shí)例
支付結(jié)果需要實(shí)現(xiàn) WXPayEntryActivity 回調(diào)接收。
支付寶(APP 端)
支付寶把這一步抽象為“訂單字符串”。服務(wù)端調(diào)用 alipay.trade.app.pay API,返回一條完整的簽名請(qǐng)求串,APP 只需把這個(gè)字符串原樣丟給 SDK 即可。服務(wù)端必須使用商戶私鑰對(duì)請(qǐng)求參數(shù)進(jìn)行簽名,支付寶再用你的公鑰(或上傳的證書(shū))驗(yàn)簽。
服務(wù)端返回的字符串示例:
app_id=2021000123456789&format=JSON&method=alipay.trade.app.pay&charset=utf-8&sign_type=RSA2×tamp=2022-03-01 12:34:56&version=1.0¬ify_url=https://your-domain.com/notify/alipay&biz_content={"out_trade_no":"ORDER20220301","total_amount":"0.01","subject":"測(cè)試商品","product_code":"QUICK_MSECURITY_PAY"}&sign=SignedString...
APP 調(diào)起代碼(Android):
String orderInfo = responseFromServer;
// taskService 為 PayTask 實(shí)例
PayTask alipayTask = new PayTask(this.activity);
Map<String, String> result = alipayTask.payV2(orderInfo, true);
支付結(jié)果通過(guò)返回的 Map 中的 resultStatus 判斷,“9000”代表成功。
異步通知才是真正的支付確認(rèn)點(diǎn)
APP 端回調(diào)只是對(duì)用戶交互的即時(shí)反饋,不能當(dāng)作資金到賬的判定依據(jù)。你的后臺(tái)必須正確處理微信支付和支付寶發(fā)送的異步通知(notify),并在此環(huán)節(jié)完成訂單狀態(tài)更新、發(fā)放權(quán)益等核心業(yè)務(wù)。
兩者均要求你的通知接口返回特定字符串(微信為 XML 或 JSON 中的特定字段,支付寶要求輸出 "success"),否則會(huì)持續(xù)重發(fā)。常見(jiàn)事故是通知接口設(shè)計(jì)成冪等之前就收到重復(fù)回調(diào),導(dǎo)致重復(fù)發(fā)貨。因此應(yīng)在處理通知時(shí)先查詢訂單狀態(tài),未支付則更新并執(zhí)行后續(xù)邏輯,已處理則直接返回成功響應(yīng)。
簽名驗(yàn)證是通知處理的首要步驟:
- 微信支付: 使用商戶平臺(tái)配置的 API 密鑰對(duì)回調(diào)參數(shù)進(jìn)行簽名校驗(yàn),注意區(qū)分 V2(XML 簽名)和 V3(頭部簽名+驗(yàn)簽密鑰)。
- 支付寶: 使用平臺(tái)提供的公鑰或證書(shū)對(duì)回調(diào)參數(shù)驗(yàn)簽,推薦使用 SDK 的
AlipaySignature.rsaCheckV1或?qū)?yīng)版本方法。
混合接入的邊界條件與風(fēng)險(xiǎn)檢查清單
- 支付金額的一致性: 統(tǒng)一下單時(shí)的金額必須與訂單實(shí)付金額一致,且最終扣款方一定是支付平臺(tái)。不要在服務(wù)端拼接參數(shù)時(shí)引入第三方匯率轉(zhuǎn)換邏輯。
- 訂單號(hào)隔離: 微信支付和支付寶的商戶訂單號(hào)允許使用相同格式,但如果兩個(gè)渠道共用同一編號(hào)體系,需確保在你的數(shù)據(jù)庫(kù)中通過(guò)(訂單號(hào) + 渠道)唯一標(biāo)識(shí)。
- 應(yīng)用切后臺(tái)與未完成支付: 用戶可能跳轉(zhuǎn)銀行 APP 后未完成驗(yàn)證就切回你的 APP。你需要在 APP 切到前臺(tái)時(shí)查詢?cè)摴P訂單的最新?tīng)顟B(tài),并引導(dǎo)用戶繼續(xù)支付或查看訂單。
- 私鑰與密鑰存儲(chǔ): 私鑰絕不能出現(xiàn)在客戶端或公共倉(cāng)庫(kù)。服務(wù)端從環(huán)境變量、密鑰管理服務(wù)或安全容器中獲取,證書(shū)文件設(shè)定最小訪問(wèn)權(quán)限。
- 退款與對(duì)賬: 接入支付只是起點(diǎn)。你需要提前設(shè)計(jì)退款接口的調(diào)用權(quán)限,并建立每日對(duì)賬流程,檢查通知遺漏的“單邊賬”。
接下來(lái)你該做什么
如果你正準(zhǔn)備從零接入,建議分三步推進(jìn):先用真實(shí)商戶號(hào)在測(cè)試環(huán)境跑通一次“下單→支付→回調(diào)”的閉環(huán),寫(xiě)出包含簽名驗(yàn)簽和冪等處理的自動(dòng)化測(cè)試;然后將接入代碼封裝為獨(dú)立支付服務(wù),避免支付邏輯散落在多個(gè)業(yè)務(wù)模塊;最后在生產(chǎn)環(huán)境開(kāi)啟最小金額的真實(shí)交易,觀察一到三個(gè)工作日,確認(rèn)對(duì)賬無(wú)誤再全量放開(kāi)。
支付集成的完成標(biāo)志,不是 APP 能彈起支付界面,而是你的異步通知處理從未漏單。