提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔
前言
近期公司開展關于蘋果支付的相關業務,與之前不同的是,以前后臺直接獲取第三方Wallet封裝好的接口獲取支付地址,H5頁面直接跳轉使用ApplePay支付就行了,相當于第三方直接和銀行達成合作;如今,需要我們和銀行合作,自己去拉取ApplePay支付。沒錯,又是一個學習和摸索的一個階段,哈哈哈哈,如果有不對的地方,歡迎大家指正!!
一、前期工作
注冊蘋果開發者賬號
創建一個AppId
完成商戶驗證,配置商戶號:銀行需與 Apple 交換加密證書,用于驗證支付請求的合法性和安全性,同時配置商戶號Merchant Identity并綁定支付的域名。
生產環境必須使用HTTPS,沙盒可以使用HTTP,但商戶驗證仍需HTTPS
PS:銀行提供支付處理證書,使用該證書提交到Apple開發者后臺用于獲取商戶身份證書。
具體包括:
- 商戶身份證書(Merchant Identity Certificate):由銀行協助商戶在 Apple 開發者后臺申請,用于前端喚起 Apple Pay 時的身份驗證。
支付處理證書(Payment Processing Certificate):銀行需支持解密 Apple Pay 生成的支付令牌(Token),通常由銀行提供證書或密鑰給商戶的支付服務商。
二、前端后端支付流程
1.初始校驗
作用:驗證是否存在ApplePay內核,存在則顯示ApplePay支付方式
?內核主要是喚起 Apple Pay 界面、收集用戶支付信息(如銀行卡、金額)、生成加密的 “支付令牌(Payment Token)” 并傳遞給后端。
// 檢驗ApplePay環境 - 針對存在applePay內核時,才顯示ApplePay支付方式
async checkApplePaySupport() {if (!window.ApplePaySession) {//瀏覽器不支持 Apple Paythis.isApplePaySupported = falsereturn}try {// 檢測當前設備是否支持 Apple Pay 功能const canMakePayments = await ApplePaySession.canMakePayments(); // 用于檢查設備是否支持 Apple Pay 并且 用戶已添加至少一張有效支付卡。const canMakePaymentsWithActiveCard = await ApplePaySession.canMakePaymentsWithActiveCard(this.merchantIdentifier); if (canMakePayments && canMakePaymentsWithActiveCard) {this.isApplePaySupported = true} else if (!canMakePayments) { // 設備不支持 Apple Paythis.isApplePaySupported = falseconsole.log('設備不支持 Apple Pay')} else { // 未添加支付有效卡this.isApplePaySupported = trueconsole.log('未添加支付有效卡')}} catch (error) {// 檢查失敗: ${error.message}`this.isApplePaySupported = falseconsole.log(error.message)}// console.log('是否支持Apple',this.isApplePaySupported)// // 測試支持的支付網絡 - 低版本不支持-所以干脆去掉了// ApplePaySession.getApplePayCapability({// merchantIdentifier: this.merchantIdentifier,// supportedNetworks: ['visa', 'masterCard', 'chinaUnionPay', 'amex', 'jcb']// })// .then(capabilities => console.log('支持的網絡:', capabilities.supportedNetworks))// .catch(error => console.error('檢測失敗:', error));
},
?2.?商戶驗證流程
- 前端創建會話?
ApplePaySession,
設置會話最長等待時長 - 前端觸發
onvalidatemerchant
商戶驗證事件 - 前端將獲取到的?
validationURL
?通過后端提供的商戶驗證接口傳遞給后端 - 后端使用
validationURL
與商戶證書向 Apple 服務器請求商戶信息?merchantSession
- 后端返回?
merchantSession
?給前端,確保?merchantIdentifier
?與前端一致(返回的MID可能是hash字符串) - 前端調用?
session.completeMerchantValidation
?完成商戶驗證
3.?支付驗證流程
- 用戶授權支付后,觸發?
onpaymentauthorized
?事件 - 前端將支付令牌?
paymentToken
?發送到后端 - 后端將支付令牌轉發給支付網關(如 Stripe、銀聯)
- 后端返回支付結果給前端
- 前端調用?
session.completePayment
?通知 Apple Pay 支付狀態
?用戶授權支付動作:指的是輸入了支付密碼這個動作,當初因為測試機沒有卡,一直無法觸發支付驗證事件,找了半天原因。
// ApplePay支付
applePayClicked() {this.session = null// 預支付信息const paymentRequest = {countryCode: 'HK',// 交易的國家currencyCode: 'HKD',// 貨幣supportedNetworks: ['visa', 'masterCard', 'chinaUnionPay', 'amex', 'jcb'],// supportedNetworks 列出支持的卡 chinaUnionPay 銀聯merchantCapabilities: ['supports3DS'],// 支持的支付特性total: { label: 'XXX電子支付', // 支付的標簽和金額amount: 100.00,type: 'final' },}// 1.創建ApplePay支付會話this.session = new window.ApplePaySession(3, paymentRequest);// 設置會話最長等待時間this.session.timeoutInterval = 120; // 單位:秒// 2.觸發商戶驗證事件 - 獲取validationURL,傳遞給后臺this.session.onvalidatemerchant = (event) => {this.validateMerchant(event.validationURL)};// 3.支付授權事件 - 用戶授權支付后,觸發?onpaymentauthorized?事件this.session.onpaymentauthorized = async(event) => {console.log('=== 支付授權 ===');this.info = event // 3.1 獲取paymentTokenlet paymentToken = event.payment.token.paymentData; this.paymentToken = JSON.stringify(paymentToken) || ''// 3.2 前端將支付令牌paymentToken+訂單信息發送到后端 let initPayResult = await this.goPay();if(initPayResult =='fail'){ // this.goPay() 提交訂單信息,接口返回錯誤時,關閉當前會話this.session.completePayment(window.ApplePaySession.STATUS_FAILURE);return}// 3.3 輪詢訂單查詢接口try{ this.applyPayRes = searchApi({orderId:this.id});let res = await this.applyPayResif(res.status===0){ // 3.4 查詢到訂單結果 - 需要completePayment 通知 Apple Pay 支付狀態await this.session.completePayment(window.ApplePaySession.STATUS_SUCCESS); // 必須調用!!!// 跳轉成功界面uni.navigateTo({url:'XXXXXURL',})}else{await this.session.completePayment(window.ApplePaySession.STATUS_FAILURE);this.openPopup('該訂單查詢結果失敗')}}catch(error){console.log('支付報錯',error)this.applyPayRes = null;this.session.completePayment(window.ApplePaySession.STATUS_FAILURE);}finally{ // 輪詢完成或失敗時才重置實例this.applePayRes = null}};// 4.錯誤事件this.session.onerror = (error) => {console.error('Apple Pay 錯誤:', error);this.openPopup('支付錯誤: ${error.message}')};// 5.取消事件this.session.oncancel = () => {console.log('用戶取消支付');this.cancelPolling() // 取消輪詢 this.session = null;};// 啟動支付流程this.session.begin();
},
// 2.1 ApplePay商戶驗證(調用后端 API)- 實現商戶驗證邏輯
validateMerchant(validationURL) {return new Promise((resolve, reject) => {uni.request({url: '/XXXX/validate',method: 'POST',data: { validationURL },success: (res) => {if (res.data.success) { // res.data.success =truelet merchantSession = res.data.dataif (this.validateMerchantSession(merchantSession)) { // 校驗接口返回字段// 2.3 完成商戶驗證this.session.completeMerchantValidation(merchantSession);resolve(merchantSession);} else {reject(new Error('商戶會話數據無效'));}} else {reject(new Error(res.data.message));}},fail: (err) => {console.error('商戶驗證請求失敗:', err);reject(err);}});});
},// 2.2 驗證商戶會話接口返回數據
validateMerchantSession(sessionData) {const requiredFields = ['signature', 'merchantIdentifier', 'domainName', 'expiresAt'];for (const field of requiredFields) {if (!sessionData[field]) {console.error(`缺少必需字段: ${field}`);return false;}}// 驗證域名一致性if (sessionData.domainName !== window.location.hostname) {console.error(`域名不匹配: ${sessionData.domainName} vs ${window.location.hostname}`);return false;}// 驗證有效期if (sessionData.expiresAt < Date.now()) {console.error('商戶會話已過期');return false;}return true;
}
三、調試與部署注意事項
1.證書配置?
- 確保后端持有有效的 Apple Pay 商戶證書和私鑰
- 證書需與商戶 ID 和域名匹配
2.HTTPS要求
- 生產環境必須使用 HTTPS
- 沙盒環境可以使用 HTTP,但商戶驗證仍需 HTTPS
3.地區與貨幣
- 針對香港或內地用戶,對應的國家地區countryCode與貨幣currencyCode是不一樣的,香港countryCode:'HK',currencyCode:'HKD';而澳門是'MO' 與'MOP'
- 確保支付網關支持香港或內地地區的貨幣和卡組織
- PS:一定要注意卡卡卡!!!我們做的香港業務,內地支付卡是無效的!所以需要包含對應地區的支付卡,哪怕內地卡是可以輸入密碼,但仍然無法獲取后臺所需要的paymentToken參數,甚至就跟陷入無限循環一樣,不扣錢但也無法獲取參數。
4.商戶ID一致性
- 確保?
merchantIdentifier
?與證書中的商戶 ID 一致
商戶Id如果不正確會導致拉取ApplePay會話后,可進行商戶驗證,但無法觸發支付驗證并且界面立刻關閉。后端在商戶驗證的接口中返回merchantIdentifier
?是一串hash數字,經過加密的,無需前端處理,直接將獲取得到的數據進行商戶校驗即可。
5.使用completePayment 告知ApplePay支付結果
支付查詢到結果后使用completePayment告知ApplePay會話結果;如果不調用會導致會話持續處于 "處理中" 狀態。
我就是沒調用告知結果,付完錢一直擱那溜溜轉了很久,最終界面顯示取消支付,立刻關閉。
四、報錯記錄:
1. payment Services Exception merchantId=XXX not registered for domain=XXX 報錯
- 商戶 ID 未注冊:商戶 ID(XXX)可能未在支付服務提供商處完成注冊或激活。
- 域名未配置:支付服務提供商可能未將?
https://xxx.com
?添加到該商戶 ID 的允許域名列表中。 - 配置延遲:新注冊的商戶 ID 或域名配置可能需要時間生效(例如,需等待幾分鐘至幾小時)。
- 域名不匹配:檢查 URL 是否包含端口號、子域名等額外信息。
- 測試環境與生產環境混淆:確認使用的商戶 ID 和 API 密鑰對應正確的環境(測試 / 生產)。
2.?ApplePay報錯:must create a new ApplePaySession from a user gesture handler;
嚴格的用戶手勢要求:Apple Pay 會話必須直接在用戶點擊事件的處理函數中創建,不能通過 Promise、setTimeout、await 或其他異步方式延遲創建。(我就是在點擊時先進行了訂單查詢的動作,再去創建ApplePay會話,存在異步操作,導致ApplePay會話都不創建了!!!)
避免預創建會話:不要在頁面加載時或其他非交互時機創建?
ApplePaySession
?實例。驗證用戶交互:確保你的按鈕或其他交互元素確實被用戶點擊后才觸發會話創建。
3.商戶驗證后會話立即關閉,提示未完成付款?
? ?1.?支付請求參數配置錯誤
countryCode
?與?currencyCode
?不匹配(如使用?HK
?國家代碼但貨幣為?CNY
)supportedNetworks
?中無用戶已添加的有效卡類型total
?金額格式或精度不符合要求
? ?2.?商戶驗證流程問題
- 商戶證書與實際域名不匹配
merchantSession
?中的簽名或時間戳無效- 后端驗證接口響應超時
? ?3.?設備或用戶環境問題
- 設備未添加有效支付卡
- 支付卡已過期或被凍結
- iOS 設置中的 Apple Pay 權限異常