文章將描述如何在iOS App中開(kāi)發(fā)支持中國(guó)用戶的Apple Pay,包括前后端的一些處理邏輯如支付信息解密。
關(guān)于Apple Pay的一些基本認(rèn)識(shí)和相應(yīng)API,具體可以參照Apple Pay官方文檔,這里不多說(shuō)。
Apple Pay近期才引入中國(guó),并針對(duì)中國(guó)增加了一些接口,如使用銀聯(lián)卡。這些接口在iOS9.2系統(tǒng)中,因此要開(kāi)發(fā)此功能請(qǐng)先準(zhǔn)備Xcode7.2或以上、iPhone真機(jī)iOS9.2或以上。可以看一下效果:

下面直接進(jìn)入正題。
首先,配置環(huán)境。可以參考這篇文章http://www.open-open.com/lib/view/open1422324034345.html
配置環(huán)境。其中需要注意的是創(chuàng)建merchantID的時(shí)候,會(huì)有一個(gè)選項(xiàng)表明創(chuàng)建的商戶ID是中國(guó)還是美國(guó),當(dāng)然這里選擇中國(guó)。
接下來(lái)創(chuàng)建支付請(qǐng)求。先說(shuō)一下Apple Pay支付流程:用戶驗(yàn)證完指紋或者密碼->手機(jī)里的安全元件把支付卡信息、支付金額、配送地址等信息加密為token1,并上傳至蘋(píng)果服務(wù)器->蘋(píng)果服務(wù)器用生成商戶ID對(duì)應(yīng)的公鑰加密token1,得到token2并進(jìn)行簽名->蘋(píng)果將token2通過(guò)代理方法回傳給客戶端->用對(duì)應(yīng)的私鑰解密token2得到支付信息。對(duì)應(yīng)的支付信息格式以及說(shuō)明可以參考Payment Token Format。
創(chuàng)建支付請(qǐng)求涉及的主要類如PKPaymentAuthorizationViewController和PKPaymentRequest等,其中可以添加聯(lián)系電話、送貨地址及配送費(fèi)(本文不贅述,可參考官方文檔)等,具體使用可以參考這里。
***是否能使用Apple Pay的API
+ (BOOL)canMakePayments;
+ (BOOL)canMakePaymentsUsingNetworks:(NSArray*)supportedNetworks capabilities:(PKMerchantCapability)capabilties;
+ (BOOL)canMakePaymentsUsingNetworks:(NSArray*)supportedNetworks; //該方法除設(shè)備、網(wǎng)絡(luò)不支持等情況下會(huì)返回NO,用戶未在Wallet中綁卡的情況下也會(huì)返回NO
創(chuàng)建支付信息:
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
PKPaymentSummaryItem *total = [PKPaymentSummaryItemsummaryItemWithLabel:@"收款商戶" amount:[NSDecimalNumber decimalNumberWithString:@"1"]]; // 金額
request.paymentSummaryItems = @[total];
request.countryCode = @"CN"; // process支付的中國(guó)
request.currencyCode = @"CNY"; // 金額展示為人民幣格式
request.supportedNetworks = @[PKPaymentNetworkChinaUnionPay]; // 中國(guó)銀聯(lián)
request.merchantIdentifier = @"merchant.com.company.test";
request.merchantCapabilities = PKMerchantCapabilityEMV | PKMerchantCapability3DS;
PKPaymentAuthorizationViewController *paymentSheet = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
if (paymentSheet) {
[self presentViewController:paymentSheet animated:YES completion:nil];
paymentSheet.delegate = self;
}
代理方法
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion{
NSData *data = payment.token.paymentData;
NSDictionary *dicFormatToken = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves|NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:nil];
// 將dicFormatToken異步解密,并支付
// ...
// 將支付結(jié)果回調(diào)給蘋(píng)果
if (/*payment is complete*/) {
completion(PKPaymentAuthorizationStatusSuccess);
}
}
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller{
[controller dismissViewControllerAnimated:YES completion:nil];
}
解密Token得到支付信息。
蘋(píng)果回調(diào)的token格式如下:
{
"data" : "....",
"header" : {
"publicKeyHash" : "....",
"transactionId" : "......",
"wrappedKey" : "......"
},
"signature" : ".....";
"version" : "RSA_v1"
}
***解密前需要對(duì)signature驗(yàn)簽,驗(yàn)證此次支付的正確和安全性。蘋(píng)果官方的驗(yàn)簽步驟如下:
1. Get Apple G3 root CA certificate from this URL: https://www.apple.com/certificateauthority/ and load the certificate into a key store
2. Build a certificate path from certificate chain in PKCS 7 signature
3. Verify the certificate path is valid using the key store built on step 1.
4. the signature was computed on input: Base64.decode(wrappedKey)+Base64.decode(encryptedData)+Hex.decode(transactionID). It is a PKCS7 signaure. Signature's algorithm, Apple's In-App payment server's signing key certificate and it's sub-ca certificate are included in the signature tag in JSON.
***待簽名成功之后,就可以解密token了。用商戶私鑰解密wrappedKey得到對(duì)稱秘鑰,用對(duì)稱秘鑰AES解密data就得到支付信息了。解密得到的支付信息中包含卡號(hào)(可卡bin的虛擬卡號(hào))、支付金額、銀行卡密碼等信息。這個(gè)時(shí)候就可以利用這些信息開(kāi)始發(fā)起支付并扣款了。
得到支付結(jié)果后,利用代理方法中的complete塊回調(diào)給蘋(píng)果,蘋(píng)果根據(jù)相應(yīng)的支付狀態(tài)展示給用戶。目前蘋(píng)果對(duì)于支付狀態(tài)的處理只有支付成功、失敗、無(wú)效賬單地址、無(wú)效郵寄地址、無(wú)效聯(lián)系方式、以及iOS9.2開(kāi)始支持的銀行卡密碼錯(cuò)誤、密碼鎖定、未輸入密碼。
