iOS-Apple Pay準(zhǔn)備工作-申請(qǐng)MerchantID及對(duì)應(yīng)證書
在App中接入Apple Pay有兩種方式,一種是使用CUP SDK等第三方SDK,另一種就是使用iOS的PassKit Framework和銀聯(lián)的接口接入。前一種方法,開(kāi)發(fā)成本低,接入簡(jiǎn)單,但對(duì)于Payment Sheet定制化程度不夠。而第二種開(kāi)發(fā)成本較高。需要對(duì)Payment Sheet的邏輯和異常情況做好相應(yīng)的UI處理。同時(shí)在后臺(tái)也需要做好支付信息解密、銀聯(lián)接口的交互以及訂單狀態(tài)處理。
這里主要介紹第二種方式,第一種方式可以到相關(guān)網(wǎng)站查看SDK集成指南。
Demo 地址 https://github.com/alanwangke213/ApplePayDemo.git
Apple Pay 授權(quán)流程圖

授權(quán)流程圖
Apple Pay 的集成需要做好版本和機(jī)型的適配。Apple Pay 只能運(yùn)作于iPhone 9.2以上版本的iPhone6/6p及以上型號(hào)或版本在2.1以上的Apple Watch的設(shè)備上。
一、為App設(shè)置Apple Pay功能
1.在開(kāi)發(fā)中賬號(hào)中注冊(cè)一個(gè)merchant ID;
2.創(chuàng)建CSR(CertificateSigningRequest)證書并提交到開(kāi)發(fā)中賬號(hào)中;
3.在Xcode的Capabilities里使能Apple Pay,并添加merchant ID

Capabilities中使能Apple Pay
二、Progress flow
1.加載內(nèi)購(gòu)商品的支付方式時(shí),如果當(dāng)前設(shè)備不支持Apple Pay需要隱藏Apple Pay支付按鈕
applePayButton.hidden = ![PKPaymentAuthorizationViewController canMakePayments];
2.如果當(dāng)前設(shè)備未設(shè)置/當(dāng)前設(shè)備設(shè)置的支付銀行卡無(wú)法在商戶提供的支付平臺(tái)支付,則隱藏Apple Pay支付按鈕,可以顯示Set Apple Pay按鈕(可選),提醒用戶進(jìn)行設(shè)置Apple Pay。
applePayButton.hidden= ![PKPaymentAuthorizationViewControllercanMakePaymentsUsingNetworks:self.supportedPaymentNetworks];
applePaySetBtn.hidden = !applePaySetBtn.hidden;
注:顯示的Apple Pay按鈕和Set Apple Pay按鈕圖片需要按照官方文檔 要求設(shè)置
3.導(dǎo)入PassKit框架
Apple Pay 所需的類都包含在PassKit框架中,需要導(dǎo)入該框架
#import <PassKit/PassKit.>
4.創(chuàng)建一個(gè)PKPaymentRequest,該request需要包括所有商品和服務(wù)費(fèi)用的,例如郵寄費(fèi),稅或者折扣等
// 1. 創(chuàng)建 payment rquestPKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.merchantIdentifier = ApplePaySwagMerchantID;
request.supportedNetworks = self.supportedPaymentNetworks;
request.merchantCapabilities = PKMerchantCapabilityCredit|PKMerchantCapabilityDebit|PKMerchantCapability3DS|PKMerchantCapabilityEMV;
request.countryCode = @"CN";
request.currencyCode = @"CNY";
需要根據(jù)不同的商品類型來(lái)設(shè)置requiredShippingAddressFields,如果使電子/虛擬商品(一般為提取/下載鏈接),則顯示聯(lián)系人郵箱。如果為實(shí)物,則顯示聯(lián)系人地址、手機(jī)號(hào)以及郵箱
// 判斷requiredShippingAddressFields
switch (_swag.swagType) {
case Delivered:
request.requiredShippingAddressFields = PKAddressFieldAll;
break;
case Electronic:
request.requiredShippingAddressFields = PKAddressFieldEmail;
break;
}
根據(jù)不同的商品類型,也判斷是否顯示郵寄方式:
// request 的 shippingMethods數(shù)組NSMutableArray <PKShippingMethod *>*shippingMethods = [NSMutableArray array];
switch (_swag.swagType) {
case Delivered:
for (ShippingMethod *shippingMethod in self.shipMethods) {
PKShippingMethod *method = [[PKShippingMethod alloc] init];
method.label = shippingMethod.title;
method.amount = shippingMethod.price;
method.identifier = shippingMethod.title;
method.detail = shippingMethod.methodDescription;
[shippingMethods addObject:method];
}
_shippingMethods = shippingMethods;
request.shippingMethods = shippingMethods;
break;
case Electronic:
break;
}


圖2A為實(shí)體商品,顯示了送貨地址,郵寄方式以及收貨人聯(lián)系方式,圖2B為虛擬物品,只顯示了用戶的郵箱
// 設(shè)置paymentSummaryItem數(shù)組
_paymentSummaryItems = [self calculateSummaryItemsFromSwag:_swag withShippingMethod:self.shippingMethods.firstObject];
request.paymentSummaryItems = _paymentSummaryItems;
用于顯示商品列表,其中paymentSummaryItem的lastObject為商家Item,amount為所有商品、tax、shipping以及discounts的總和。
Demo中的calculateSummaryItemsFromSwag: withShippingMethod:方法提供了計(jì)算summaryItems數(shù)組的邏輯。
5.展示支付授權(quán)界面PKAuthorizationViewController,該界面需要將request和需要的提示信息展示給用戶,例如shipping或者billing address。
// 授權(quán)控制權(quán)
PKPaymentAuthorizationViewController *paymentVC = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
paymentVC.delegate = self;
[self presentViewController:paymentVC animated:YES completion:nil];
6.選擇付款卡會(huì)push出選擇付款卡控制器,顯示所有已綁定的銀行卡。當(dāng)選擇卡片會(huì)調(diào)用代理方法
paymentAuthorizationViewController:didSelectPaymentMethod:completion:該方法,需要實(shí)現(xiàn)completion完成回調(diào),否則會(huì)卡在payment processing界面

付款卡列表
7.點(diǎn)擊送貨cell會(huì)push出送貨地址列表界面,選擇送貨地址后會(huì)調(diào)用paymentAuthorizationViewController:didSelectShippingMethod:completion:代理方法

收貨地址列表
該方法,也需要實(shí)現(xiàn)completion完成回調(diào),否則會(huì)卡在payment processing界面
8.點(diǎn)擊 郵寄方式cell會(huì)Push出Shipping method列表

郵寄方式列表
當(dāng)選擇其它郵寄方式會(huì)調(diào)用 paymentAuthorizationViewController :didSelectShippingMethod : completion:代理方法,在方法中需要做好相關(guān)的request.paymentSummaryItems數(shù)組,重新計(jì)算列表信息和總價(jià)。Demo中的calculateSummaryItemsFromSwag: withShippingMethod:方法提供了計(jì)算該數(shù)組的基本邏輯。
9.當(dāng)用戶輸入TouchID,且授權(quán)通過(guò)后,Apple Pay 將使用Secure Element安全元件芯片處理銀行卡信息,再有secure enclave產(chǎn)生獨(dú)特的授權(quán)標(biāo)志符
10.將授權(quán)標(biāo)志符發(fā)送給蘋果的服務(wù)器,再使用MerChante identifier certificate進(jìn)行二次加密后將token反回給app做其它處理。
11.在paymentAuthorizationViewController:didAuthorizePayment: completion:代理方法中可以獲取payment信息,其中包涵了payment token。
12.獲取到payment token后可以將其發(fā)送給后臺(tái)進(jìn)行其他處理

授權(quán)處理中

授權(quán)失敗

授權(quán)成功
在授權(quán)成功之后將支付相關(guān)信息發(fā)送到Apple服務(wù)器進(jìn)行加密,再通過(guò)代理方法paymentAuthorizationViewController:didAuthorizePayment:completion:獲得到PKPayment中的PKPaymentToken。
這個(gè)是Apple Pay的授權(quán)流程的介紹,后續(xù)的還需要將paymentToken中的paymentData字段數(shù)據(jù)(加密過(guò)的)發(fā)送到自己的服務(wù)器。服務(wù)器進(jìn)行解密操作之后提取出需要的信息,組織好銀聯(lián)接口報(bào)文,完成交易。
上述的paymentData的內(nèi)容是Json格式的二進(jìn)制流,服務(wù)器在收到數(shù)據(jù)后進(jìn)行解析。其中的header.wrappedKey是使用非對(duì)稱加密算法加密過(guò)的對(duì)稱密鑰。使用在蘋果開(kāi)發(fā)者后臺(tái)配置merchantID時(shí)的私鑰進(jìn)行解密,會(huì)得到對(duì)稱密鑰。使用對(duì)稱密鑰對(duì)data字段包含的加密數(shù)據(jù)進(jìn)行解密,可以得到Apple返回的與支付相關(guān)的信息。此信息時(shí)加密的,包含了用戶支付的卡號(hào)和PIN碼等信息,理論上只有銀聯(lián)能解密出真正的內(nèi)容。商戶看不到具體的信息。服務(wù)器端將這些信息組織成銀聯(lián)需要的報(bào)文內(nèi)容,然后調(diào)用銀聯(lián)的扣款接口,完成扣款。
注意:paymentData里面有一個(gè)交易金額字段,該字段返回的數(shù)據(jù)并不是實(shí)際支付的金額。在組織銀聯(lián)報(bào)文的時(shí)候一定要注意不能直接使用該字段作為扣款金額的值。
在調(diào)用銀聯(lián)扣款接口的時(shí)候需要注意,在組織好報(bào)文并調(diào)用銀聯(lián)接口發(fā)送給銀聯(lián)之后,銀聯(lián)的接口返回結(jié)果同時(shí)有同步和異步兩種方式。如果同步返回成功,則表示銀聯(lián)成功收到并開(kāi)始處理該扣款請(qǐng)求,并不代表扣款成功。扣款成功實(shí)在異步里返回的。比如,銀行卡被凍結(jié),PIN錯(cuò)誤,余額不足等原因可能造成扣款失敗??梢栽谡{(diào)用銀聯(lián)扣款接口后,如果幾秒內(nèi)未收到異步回調(diào),使用銀聯(lián)的交易流水號(hào)去輪詢調(diào)用銀聯(lián)的交易狀態(tài)接口來(lái)確保此次交易結(jié)果。