Apple Pay的使用
關(guān)于Apple Pay 具體繼承步驟也可參考: 網(wǎng)址
配置支付環(huán)境創(chuàng)建支付請(qǐng)求授權(quán)支付支付處理Part 1 關(guān)于Apple Pay
Apple Pay是一種移動(dòng)支付技術(shù),它能夠讓用戶以一種便捷安全的方式為現(xiàn)實(shí)世界中購買的商品和服務(wù)付款。
在Xcode和蘋果開發(fā)者會(huì)員中心中配置Apple Pay
使用Apple Pay的APP需要一項(xiàng)特殊的權(quán)限,該權(quán)限可以在開發(fā)者會(huì)員中心和Xcode中開啟。你同樣需要注冊(cè)一個(gè)商業(yè)標(biāo)示,并設(shè)置密鑰;在給服務(wù)器發(fā)送支付信息時(shí),這些密匙可以確保數(shù)據(jù)的安全傳輸。
相關(guān)章節(jié):配置支付環(huán)境(Configuring Your Environment,后面會(huì)提到)
用戶授權(quán)支付請(qǐng)求
支付請(qǐng)求就是描述當(dāng)前進(jìn)行的購買操作,包括支付金額。你把支付請(qǐng)求發(fā)送給一個(gè)授權(quán)支付的視圖控制器;該試圖控制器呈現(xiàn)相關(guān)請(qǐng)求內(nèi)容,并提示用戶需要輸入的信息,例如配送地址或者賬單地址。接著,當(dāng)用戶與視圖控制器交互,并提供新的支付信息時(shí),APP會(huì)調(diào)用支付請(qǐng)求的委托,繼續(xù)執(zhí)行支付流程。
相關(guān)章節(jié):創(chuàng)建支付請(qǐng)求(Creating a Payment Request),授權(quán)支付(Authorizing a Payment)(后面會(huì)提到)
服務(wù)器處理支付請(qǐng)求
Apple Pay會(huì)對(duì)支付信息進(jìn)行加密處理,以防止未獲授權(quán)的第三方獲取用戶的支付信息。你可以在自己的服務(wù)器上完成整個(gè)支付流程,也可以在自己的服務(wù)器上使用第三方支付平臺(tái)來解碼支付信息,并完成支付處理。
關(guān)于支持Apple Pay的支付平臺(tái)信息,請(qǐng)參考developer.apple.com/apple-pay/
相關(guān)章節(jié):處理支付請(qǐng)求(Processing a Payment)
Part 2 配置支付環(huán)境
一個(gè)商用ID標(biāo)識(shí)可以幫助Apple Pay識(shí)別你,讓你能夠接受付款。在支付信息加密的過程中,把公匙和證書與ID標(biāo)示關(guān)聯(lián)起來進(jìn)行加密是必不可少的一步。在APP使用Apple Pay之前,你首先得注冊(cè)一個(gè)商用ID,并配置它的相關(guān)證書。
注冊(cè)商用ID標(biāo)示
- 1.在開發(fā)者會(huì)員中心,選擇“Certificates,Identifiers&Profiles”
- 2.在Identifiers下,選擇Merchant IDs
- 3.在右上角點(diǎn)擊"+"按鈕
- 4.在Description欄、ID欄輸入相應(yīng)信息,點(diǎn)擊"Continue"
- 5.瀏覽下配置參數(shù),點(diǎn)擊"Register"
- 6.點(diǎn)擊"Done"
為你的ID標(biāo)示配置一個(gè)證書
- 1.在開發(fā)者會(huì)員中心,選擇"Certificates,Identifiers&Profiles"
- 2.在Identifiers下,選擇Merchant IDs
- 3.選擇列表中的ID標(biāo)示,點(diǎn)擊Edit
- 4.點(diǎn)擊"Create Certificate",按照指示獲取或生成簽名證書請(qǐng)求(CSR),點(diǎn)擊"Continue"
- 5.點(diǎn)擊"Choose File",選擇你的CSR,點(diǎn)擊"Generate"
- 6.點(diǎn)擊"Download"下載證書,點(diǎn)擊"Done"
如果KeyChain Access中顯示了警示信息,表示未知授權(quán)簽發(fā)證書或者無效證書發(fā)行人,那么要確保你已經(jīng)在鑰匙鏈中安裝了WWDR中級(jí)證書-G2和Apple Root CA-G2。你可以在這個(gè)地方下載這些東西:apple.com/certificateauthority.
為了在Xcode中啟用Apple Pay,打開APP工程文件的Capabilities面板。在Apple Pay這行將開關(guān)按鈕設(shè)置為"ON",接著選擇APP需要使用的ID標(biāo)示。
注意:在APP排錯(cuò)時(shí),偶爾手動(dòng)啟用Apple Pay很管用。請(qǐng)按照以下步驟手動(dòng)啟用Apple Pay:
- 1.在會(huì)員中心,選擇Certificates,Identifiers& Profiles
- 2.在Identifiers下,選擇App IDs
- 3.選擇列表中的app ID,點(diǎn)擊"Edit"
- 4.選擇 Apple Pay ,點(diǎn)擊"Edit"
- 5.選擇你需要使用的ID標(biāo)示,點(diǎn)擊"Continue"
- 6.瀏覽配置參數(shù),點(diǎn)擊"Assign"
- 7.點(diǎn)擊"Done"
Part 3 創(chuàng)建支付請(qǐng)求
創(chuàng)建支付請(qǐng)求
支付請(qǐng)求是PKPaymentRequest類的實(shí)例,它的組成部分包括一個(gè)用來表示將要購買的項(xiàng)目的摘要,一個(gè)可用的配送方式列表,一個(gè)表示用戶需要提供的配送信息的描述,以及一些商家和支付平臺(tái)的信息。
判定用戶是否能夠支付
在創(chuàng)建支付請(qǐng)求之前,要首先通過調(diào)用PKPaymentAuthorizationViewController 類里的canMakePaymentsUsingNetworks:方法來判斷用戶是否能夠使用你提供的支付網(wǎng)絡(luò)進(jìn)行支付。如果要判斷用戶的硬件是否支持Apple Pay或者是否因?yàn)榧议L(zhǎng)控制而不能支付,請(qǐng)使用canMakePayments 方法。
如果用戶不能進(jìn)行支付,那就不要顯示支付按鈕,相應(yīng)的應(yīng)該退回到其它支付方式。
支付請(qǐng)求包含貨幣和地區(qū)信息
所有的匯總金額應(yīng)該使用同一種貨幣,貨幣的信息可使用PKPaymentRequest類的currencyCode屬性進(jìn)行指定。像"USD"這樣,使用3個(gè)字符格式的ISO貨幣編碼。
一個(gè)支付請(qǐng)求里的國家代碼表示了這次購買發(fā)生的國家或者將要在這個(gè)國家處理這次支付。像"US"這樣,使用2個(gè)字符格式的ISO國家編碼。
在支付請(qǐng)求里指定的商用ID必須匹配應(yīng)用中指定的商用ID列表之一。
request.currencyCode = @"USD";
request.countryCode = @"US";
request.merchantIdentifier = @"merchant.com.example";
支付請(qǐng)求包含一個(gè)支付摘要項(xiàng)目的列表
支付摘要項(xiàng)目,屬于PKPaymentSummaryItem 類,描述了支付請(qǐng)求的不同部分。在一個(gè)支付請(qǐng)求里不要使用太多的摘要項(xiàng)目---典型的項(xiàng)目像比如小計(jì)金額、折扣信息、配送信息、含稅信息以及總計(jì)金額等。如果你想要提供更詳細(xì)的支付項(xiàng)目列表,可以在你應(yīng)用的其它地方提供。
每一個(gè)摘要項(xiàng)目會(huì)有一個(gè)標(biāo)簽和數(shù)額,就像在代碼列表3-1中顯示的那樣。標(biāo)簽文本是一個(gè)用戶可閱讀的摘要項(xiàng)目描述信息,數(shù)額是相對(duì)應(yīng)的支付數(shù)額。在一個(gè)支付請(qǐng)求中所有的數(shù)額都要使用在這個(gè)請(qǐng)求中指定的貨幣。對(duì)于折扣或優(yōu)惠券,則需要把數(shù)額設(shè)成負(fù)數(shù)。
Listing 3-1創(chuàng)建支付項(xiàng)目
// 12.75 subtotal
NSDecimalNumber *subtotalAmount = [NSDecimalNumber decimalNumberWithMantissa:1275 exponent:-2 isNegative:NO];
self.subtotal = [PKPaymentSummaryItem summaryItemWithLabel:@"Subtotal" amount:subtotalAmount];
// 2.00 discount
NSDecimalNumber *discountAmount = [NSDecimalNumber decimalNumberWithMantissa:200 exponent:-2 isNegative:YES];
self.discount = [PKPaymentSummaryItem summaryItemWithLabel:@"Discount" amount:discountAmount];
注意
這里使用NSDecimalNumber類來存儲(chǔ)摘要項(xiàng)目的數(shù)額,它是一個(gè)以10為底數(shù)的數(shù)值。可以使用指定尾數(shù)和指數(shù)的方式(像代碼中那樣)來創(chuàng)建這個(gè)類的實(shí)例,也可以通過指定字符串和locale來實(shí)例化,字符串指定了相應(yīng)的數(shù)值。這里總是使用以10為底數(shù)的數(shù)值來做財(cái)務(wù)計(jì)算--例如當(dāng)需要計(jì)算5%折扣掉的金額時(shí)。
盡管有時(shí)使用其它的計(jì)數(shù)方法更方便,但是像float或者Double這樣的IEEE浮點(diǎn)數(shù)類型是不適合作財(cái)務(wù)計(jì)算的,這些數(shù)據(jù)類型使用的是以2為底數(shù)的數(shù)值表示方法,這就表示有一些十進(jìn)制數(shù)值不能準(zhǔn)確得被表示--例如0.42必須以0.41999這樣的循環(huán)小數(shù)來近似表示,而這種近似表示常常會(huì)造成財(cái)務(wù)計(jì)算的錯(cuò)誤結(jié)果。
在這個(gè)摘要項(xiàng)目列表中的最后一個(gè)是總計(jì)金額。這個(gè)金額是通過把所有其它金額相加而得到。總計(jì)的顯示方法和其它的摘要項(xiàng)目不同:應(yīng)該使用你公司的名稱做為其標(biāo)簽,使用所有其它項(xiàng)目的金額總和做為金額。使用paymentSummaryItems屬性將這些摘要項(xiàng)目加入支付請(qǐng)求。
// 10.75 grand total
NSDecimalNumber *totalAmount = [NSDecimalNumber zero];
totalAmount = [totalAmount decimalNumberByAdding:subtotalAmount];
totalAmount = [totalAmount decimalNumberByAdding:discountAmount];
self.total = [PKPaymentSummaryItem summaryItemWithLabel:@"My Company Name" amount:totalAmount];
self.summaryItems = @[self.subtotal, self.discount, self.total];
request.paymentSummaryItems = self.summaryItems;
配送方式是一種特殊的摘要項(xiàng)目
對(duì)于每一種可用的配送方式創(chuàng)建一個(gè)PKShippingMethod的實(shí)例。就像其它支付摘要項(xiàng)目一樣,配送方式包含用戶易于辨別的標(biāo)簽,比如"標(biāo)準(zhǔn)配送"或者"第二天配送",還有一個(gè)金額來表示配送費(fèi)用。與其它摘要項(xiàng)目不同的是,配送方式還有一個(gè)detail屬性--像"7月29日到達(dá)"或者"24小時(shí)之內(nèi)配送"等--可以用來解釋各個(gè)配送方式之間的區(qū)別。
使用identifier屬性來在代理方法中區(qū)分不同的配送方式,這個(gè)屬性只會(huì)在你的應(yīng)用內(nèi)使用--框架看不到這個(gè)屬性,并且它也不會(huì)出現(xiàn)在UI中。在創(chuàng)建配送方式時(shí)為其分配一個(gè)獨(dú)一無二的標(biāo)識(shí)符。為了方便調(diào)試,可使用文本縮寫,比如"discount", "standard", 或者 "next-day".
有一些配送方式在某些地區(qū)可能不適用,或者有不同的價(jià)格,你可以在用戶選擇配送地址或配送方式的代理方法時(shí)更新這些信息,就像Your Delegate Updates Shipping Methods and Costs描述的一樣。
指定你支持的支付方式
通過在supportedNetworks屬性中填入字符串常量數(shù)組來指定你支持的支付網(wǎng)絡(luò)。通過指定merchantCapabilities屬性來指定你支持的支付處理標(biāo)準(zhǔn),3DS支付方式是必須支持的,EMV方式是可選的。
商家支持的支付處理標(biāo)準(zhǔn)使用標(biāo)識(shí)位來進(jìn)行組合,像下面這樣:
request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa];
// Supports 3DS only
request.merchantCapabilities = PKMerchantCapability3DS;
// Supports both 3DS and EMV
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
指示所需配送信息和賬單信息
通過填充 requiredBillingAddressFields 和 requiredShippingAddressFields屬性來指定所需賬單信息和配送地址信息。當(dāng)你顯示一個(gè)視圖控制器時(shí),它會(huì)提示用戶輸入所需內(nèi)容。這些字段常量可以像下面這樣進(jìn)行組合來設(shè)置這些屬性:
request.requiredBillingAddressFields = PKAddressFieldEmail;
request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
如果你已經(jīng)有了用戶的賬單和配送信息,可以直接在支付請(qǐng)求中使用它們。但是盡管Apple Pay默認(rèn)使用了這些信息,用戶仍然可以在授權(quán)支付的過程中修改這些信息
ABRecordRef record = ABPersonCreate();
CFErrorRef error;
BOOL success;
success = ABRecordSetValue(record, kABPersonFirstNameProperty, @"John", &error);
if (!success) { }
success = ABRecordSetValue(record, kABPersonLastNameProperty, @"Appleseed", &error);
if (!success) { }
ABMultiValueRef shippingAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
NSDictionary *addressDictionary = @{
(NSString *) kABPersonAddressStreetKey: @"1234 Laurel Street",
(NSString *) kABPersonAddressCityKey: @"Atlanta",
(NSString *) kABPersonAddressStateKey: @"GA",
(NSString *) kABPersonAddressZIPKey: @"30303"
};
ABMultiValueAddValueAndLabel(shippingAddress,
(__bridge CFDictionaryRef) addressDictionary,
kABOtherLabel,
nil);
success = ABRecordSetValue(record, kABPersonAddressProperty, shippingAddress, &error);
if (!success) { }
request.shippingAddress = record;
CFRelease(shippingAddress);
CFRelease(record);
存儲(chǔ)額外信息
使用applicationData屬性來存儲(chǔ)一些在你的應(yīng)用中關(guān)于這次支付請(qǐng)求的唯一標(biāo)識(shí)信息,比如一個(gè)購物車的標(biāo)識(shí)符。在用戶授權(quán)支付之后,這個(gè)屬性的哈希值會(huì)出現(xiàn)在這次支付的token中。
part 4 授權(quán)支付
支付授權(quán)過程是由支付授權(quán)view controller和它的代理協(xié)作完成的。支付授權(quán)view controller做了兩件事情:它讓用戶選擇支付請(qǐng)求所必需的賬單和配送信息,還有讓用戶最終授權(quán)同意這次支付。當(dāng)用戶和view controller交互時(shí),代理方法就會(huì)被調(diào)用,這樣你的應(yīng)用就可以不斷地更新顯示的信息--例如在配送地址更改后更新配送費(fèi)用。用戶最終授權(quán)支付請(qǐng)求之后代理方法同樣也會(huì)被調(diào)用。
注意:在實(shí)現(xiàn)這些方法時(shí)注意,這些方法可能會(huì)被多次調(diào)用,而它們被調(diào)用的順序取決于用戶的行為的順序。
在所有這個(gè)授權(quán)過程中被調(diào)用的代理方法中,都會(huì)有一個(gè)completion block被做為參數(shù)之一傳入,支付授權(quán)view controller會(huì)在一個(gè)代理方法執(zhí)行完畢(通過調(diào)用completion塊)后再調(diào)用另一個(gè)代理方法。唯一的例外是paymentAuthorizationViewControllerDidFinish:方法:它不包含completion block,所以它可以在任何時(shí)候被調(diào)用。
這個(gè)completion block有一個(gè)傳入?yún)?shù),基于現(xiàn)有的可用信息,你可以通過這個(gè)參數(shù)并指定這次交易的狀態(tài)。如果這次交易沒有任何問題,傳入PKPaymentAuthorizationStatusSuccess,否則,你要傳入一個(gè)識(shí)別問題的值。
通過在PKPaymentAuthorizationViewController類的構(gòu)造方法中傳入一個(gè)支付請(qǐng)求來對(duì)它進(jìn)行實(shí)例化,然后給這個(gè)視圖控制器設(shè)置一個(gè)代理,就可以把它展示給用戶了。
PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
if (!viewController) { }
viewController.delegate = self;
[self presentViewController:viewController animated:YES completion:nil];
當(dāng)用戶與這個(gè)視圖控制器進(jìn)行交互時(shí),它的代理方法會(huì)被調(diào)用。
通過代理更新配送方式和費(fèi)用
當(dāng)用戶提供配送信息之后,授權(quán)view controller 會(huì)調(diào)用paymentAuthorizationViewController:didSelectShippingAddress:completion: 和paymentAuthorizationViewController:didSelectShippingMethod:completion:這兩個(gè)代理方法。在這兩個(gè)方法中根據(jù)最新信息來更新支付請(qǐng)求。
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingAddress:(ABRecordRef)address
completion:(void (^)(PKPaymentAuthorizationStatus, NSArray *, NSArray *))completion
{
self.selectedShippingAddress = address;
[self updateShippingCost];
NSArray *shippingMethods = [self shippingMethodsForAddress:address];
completion(PKPaymentAuthorizationStatusSuccess, shippingMethods, self.summaryItems);
}
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingMethod:(PKShippingMethod *)shippingMethod
completion:(void (^)(PKPaymentAuthorizationStatus, NSArray *))completion
{
self.selectedShippingMethod = shippingMethod;
[self updateShippingCost];
completion(PKPaymentAuthorizationStatusSuccess, self.summaryItems);
}
當(dāng)支付被授權(quán)后,支付token會(huì)被創(chuàng)建
當(dāng)用戶最終授權(quán)了一個(gè)支付請(qǐng)求,框架會(huì)通過與蘋果服務(wù)器和嵌入在設(shè)備中的一個(gè)安全模塊進(jìn)行通信,生成一個(gè)支付token。然后你在paymentAuthorizationViewController:didAuthorizePayment:completion:方法中將這個(gè)token和其它一些你需要用來處理這次購買的信息--例如配送地址和購物車標(biāo)識(shí)--發(fā)送給你的服務(wù)器。這個(gè)過程是這樣的:
- 框架發(fā)送支付請(qǐng)求給安全模塊,只有安全模塊可以訪問存儲(chǔ)在設(shè)備上的標(biāo)記化的卡信息。
- 安全模塊把特定的卡和商家等支付數(shù)據(jù)加密,以保證只有蘋果可以讀取,然后發(fā)送給框架??蚣軙?huì)將這些數(shù)據(jù)發(fā)送給蘋果。
- 蘋果服務(wù)器再次加密這些支付數(shù)據(jù),以保證只有商家可以讀取。然后服務(wù)器對(duì)它進(jìn)行簽名,生成支付token,然后發(fā)送給設(shè)備。
- 框架調(diào)用相應(yīng)的代理方法并傳入這個(gè)token,然后你的代理方法傳送token給你的服務(wù)器。
至于你的服務(wù)器采取的行為要取決于你是自己處理這次支付或者你是和其它支付平臺(tái)合作來進(jìn)行支付處理。不管怎樣,你的服務(wù)器處理這個(gè)訂單然后傳送一個(gè)狀態(tài)信息給設(shè)備,代理方法會(huì)把這個(gè)狀態(tài)信息傳送給completion塊,像在“Processing a Payment”中討論過的。
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus))completion
{
NSError *error;
ABMultiValueRef addressMultiValue = ABRecordCopyValue(payment.billingAddress, kABPersonAddressProperty);
NSDictionary *addressDictionary = (__bridge_transfer NSDictionary *) ABMultiValueCopyValueAtIndex(addressMultiValue, 0);
NSData *json = [NSJSONSerialization dataWithJSONObject:addressDictionary options:NSJSONWritingPrettyPrinted error: &error];
// ... Send payment token, shipping and billing address, and order information to your server ...
PKPaymentAuthorizationStatus status; // From your server
completion(status);
}
在代理方法中釋放授權(quán)View Controller
在框架顯示交易狀態(tài)之后,授權(quán)View Controller會(huì)調(diào)用代理paymentAuthorizationViewControllerDidFinish:的方法。在這個(gè)方法的實(shí)現(xiàn)中,先釋放授權(quán)頁面控制器再顯示你自己的訂單確認(rèn)頁面。
- (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
Part 5 支付處理
處理一個(gè)支付請(qǐng)求涉及以下幾個(gè)步驟:
- 把支付信息,以及支付流程+所需的其他信息,一起發(fā)送給你的服務(wù)器。
- 驗(yàn)證支付數(shù)據(jù)的哈希表和簽名
- 為加密過的支付數(shù)據(jù)解碼
- 向支付處理系統(tǒng)提交支付數(shù)據(jù)
- 向訂單追蹤系統(tǒng)提交訂單
處理支付請(qǐng)求時(shí),你有兩個(gè)選擇;你既可以利用支付平臺(tái)處理支付請(qǐng)求,也可以自己實(shí)現(xiàn)支付請(qǐng)求處理流程。一個(gè)常用的支付平臺(tái)可以完成上述大部分操作。
讀取,驗(yàn)證,以及處理支付信息需要有一定的相關(guān)密碼知識(shí),例如計(jì)算SHA-1哈希表,讀取和驗(yàn)證PKCS#7簽名,執(zhí)行Elliptic Curve Diffie-Hellman密匙交換。如果沒有一定的密碼學(xué)背景,你可以考慮使用第三方支付平臺(tái)來完成這些操作。
關(guān)于支持Apple Pay支付平臺(tái)的更多信息,請(qǐng)參考developer.apple.com/apple-pay/
處理支付請(qǐng)求所用的信息擁有一種嵌套式的數(shù)據(jù)結(jié)構(gòu),如下圖。支付令牌是PKPaymentToken類的實(shí)例。其paymentData屬性值是一個(gè)JSON詞典,它的頭文件信息可以用來驗(yàn)證和加密支付數(shù)據(jù)。加密過的數(shù)據(jù)信息包括支付金額、持卡人姓名,以及一些其他指定的支付處理協(xié)議。