本文由CocoaChina譯者xiaoying、tr培子翻譯自蘋果官方文檔:Apple Pay Programming Guide
目錄
關於Apple Pay
配置支付環境
創建支付請求
授權支付
支付處理
Part 1 關於Apple Pay
Apple Pay是一種移動支付技術,它能夠讓用戶以一種便捷安全的方式為現實世界中購買的商品和服務付款。
關於相關App裡的數字商品和服務,請參考In-App Purchase Programming Guide.
在Xcode和蘋果開發者會員中心中配置Apple Pay
使用Apple Pay的APP需要一項特殊的權限,該權限可以在開發者會員中心和Xcode中開啟。你同樣需要注冊一個商業標示,並設置密鑰;在給服務器發送支付信息時,這些密匙可以確保數據的安全傳輸。
相關章節:配置支付環境(Configuring Your Environment,後面會提到)
用戶授權支付請求
支付請求就是描述當前進行的購買操作,包括支付金額。你把支付請求發送給一個授權支付的視圖控制器;該試圖控制器呈現相關請求內容,並提示用戶需要輸入的信息,例如配送地址或者賬單地址。接著,當用戶與視圖控制器交互,並提供新的支付信息時,APP會調用支付請求的委托,繼續執行支付流程。
相關章節:創建支付請求(Creating a Payment Request),授權支付(Authorizing a Payment)(後面會提到)
服務器處理支付請求
Apple Pay會對支付信息進行加密處理,以防止未獲授權的第三方獲取用戶的支付信息。你可以在自己的服務器上完成整個支付流程,也可以在自己的服務器上使用第三方支付平台來解碼支付信息,並完成支付處理。
關於支持Apple Pay的支付平台信息,請參考developer.apple.com/apple-pay/
相關章節:處理支付請求(Processing a Payment)
Part 2 配置支付環境
一個商用ID標識可以幫助Apple Pay識別你,讓你能夠接受付款。在支付信息加密的過程中,把公匙和證書與ID標示關聯起來進行加密是必不可少的一步。在APP使用Apple Pay之前,你首先得注冊一個商用ID,並配置它的相關證書。
注冊商用ID標示
在開發者會員中心,選擇“Certificates,Identifiers&Profiles”
在Identifiers下,選擇Merchant IDs
在右上角點擊"+"按鈕
在Description欄、ID欄輸入相應信息,點擊"Continue"
浏覽下配置參數,點擊"Register"
點擊"Done"
為你的ID標示配置一個證書
在開發者會員中心,選擇"Certificates,Identifiers&Profiles"
在Identifiers下,選擇Merchant IDs
選擇列表中的ID標示,點擊Edit
點擊"Create Certificate",按照指示獲取或生成簽名證書請求(CSR),點擊"Continue"
點擊"Choose File",選擇你的CSR,點擊"Generate"
點擊"Download"下載證書,點擊"Done"
如果KeyChain Access中顯示了警示信息,表示未知授權簽發證書或者無效證書發行人,那麼要確保你已經在鑰匙鏈中安裝了WWDR中級證書-G2和Apple Root CA-G2。你可以在這個地方下載這些東西:apple.com/certificateauthority.
為了在Xcode中啟用Apple Pay,打開APP工程文件的Capabilities面板。在Apple Pay這行將開關按鈕設置為"ON",接著選擇APP需要使用的ID標示。
注意:在APP排錯時,偶爾手動啟用Apple Pay很管用。請按照以下步驟手動啟用Apple Pay:
在會員中心,選擇Certificates,Identifiers& Profiles
在Identifiers下,選擇App IDs
選擇列表中的app ID,點擊"Edit"
選擇 Apple Pay ,點擊"Edit"
選擇你需要使用的ID標示,點擊"Continue"
浏覽配置參數,點擊"Assign"
點擊"Done"
Part 3 創建支付請求
創建支付請求
支付請求是PKPaymentRequest類的實例,它的組成部分包括一個用來表示將要購買的項目的摘要,一個可用的配送方式列表,一個表示用戶需要提供的配送信息的描述,以及一些商家和支付平台的信息。
判定用戶是否能夠支付
在創建支付請求之前,要首先通過調用PKPaymentAuthorizationViewController 類裡的canMakePaymentsUsingNetworks:方法來判斷用戶是否能夠使用你提供的支付網絡進行支付。如果要判斷用戶的硬件是否支持Apple Pay或者是否因為家長控制而不能支付,請使用canMakePayments 方法。
如果用戶不能進行支付,那就不要顯示支付按鈕,相應的應該退回到其它支付方式。
支付請求包含貨幣和地區信息
所有的匯總金額應該使用同一種貨幣,貨幣的信息可使用PKPaymentRequest類的currencyCode屬性進行指定。像"USD"這樣,使用3個字符格式的ISO貨幣編碼。
一個支付請求裡的國家代碼表示了這次購買發生的國家或者將要在這個國家處理這次支付。像"US"這樣,使用2個字符格式的ISO國家編碼。
在支付請求裡指定的商用ID必須匹配應用中指定的商用ID列表之一。
request.currencyCode = @"USD"; request.countryCode = @"US"; request.merchantIdentifier = @"merchant.com.example";
支付請求包含一個支付摘要項目的列表
支付摘要項目,屬於PKPaymentSummaryItem 類,描述了支付請求的不同部分。在一個支付請求裡不要使用太多的摘要項目---典型的項目像比如小計金額、折扣信息、配送信息、含稅信息以及總計金額等。如果你想要提供更詳細的支付項目列表,可以在你應用的其它地方提供。
每一個摘要項目會有一個標簽和數額,就像在代碼列表3-1中顯示的那樣。標簽文本是一個用戶可閱讀的摘要項目描述信息,數額是相對應的支付數額。在一個支付請求中所有的數額都要使用在這個請求中指定的貨幣。對於折扣或優惠券,則需要把數額設成負數。
Listing 3-1創建支付項目
// 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類來存儲摘要項目的數額,它是一個以10為底數的數值。可以使用指定尾數和指數的方式(像代碼中那樣)來創建這個類的實例,也可以通過指定字符串和locale來實例化,字符串指定了相應的數值。這裡總是使用以10為底數的數值來做財務計算--例如當需要計算5%折扣掉的金額時。
盡管有時使用其它的計數方法更方便,但是像float或者Double這樣的IEEE浮點數類型是不適合作財務計算的,這些數據類型使用的是以2為底數的數值表示方法,這就表示有一些十進制數值不能准確得被表示--例如0.42必須以0.41999這樣的循環小數來近似表示,而這種近似表示常常會造成財務計算的錯誤結果。
在這個摘要項目列表中的最後一個是總計金額。這個金額是通過把所有其它金額相加而得到。總計的顯示方法和其它的摘要項目不同:應該使用你公司的名稱做為其標簽,使用所有其它項目的金額總和做為金額。使用paymentSummaryItems 屬性將這些摘要項目加入支付請求。
// 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;
配送方式是一種特殊的摘要項目
對於每一種可用的配送方式創建一個PKShippingMethod的實例。就像其它支付摘要項目一樣,配送方式包含用戶易於辨別的標簽,比如"標准配送"或者"第二天配送",還有一個金額來表示配送費用。與其它摘要項目不同的是,配送方式還有一個detail屬性--像"7月29日到達"或者"24小時之內配送"等--可以用來解釋各個配送方式之間的區別。
使用identifier屬性來在代理方法中區分不同的配送方式,這個屬性只會在你的應用內使用--框架看不到這個屬性,並且它也不會出現在UI中。在創建配送方式時為其分配一個獨一無二的標識符。為了方便調試,可使用文本縮寫,比如"discount", "standard", 或者 "next-day".
有一些配送方式在某些地區可能不適用,或者有不同的價格,你可以在用戶選擇配送地址或配送方式的代理方法時更新這些信息,就像Your Delegate Updates Shipping Methods and Costs描述的一樣。
指定你支持的支付方式
通過在supportedNetworks屬性中填入字符串常量數組來指定你支持的支付網絡。通過指定merchantCapabilities屬性來指定你支持的支付處理標准,3DS支付方式是必須支持的,EMV方式是可選的。
商家支持的支付處理標准使用標識位來進行組合,像下面這樣:
request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa]; // Supports 3DS only request.merchantCapabilities = PKMerchantCapability3DS; // Supports both 3DS and EMV request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
指示所需配送信息和賬單信息
通過填充 requiredBillingAddressFields 和 requiredShippingAddressFields屬性來指定所需賬單信息和配送地址信息。當你顯示一個視圖控制器時,它會提示用戶輸入所需內容。這些字段常量可以像下面這樣進行組合來設置這些屬性:
request.requiredBillingAddressFields = PKAddressFieldEmail; request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
如果你已經有了用戶的賬單和配送信息,可以直接在支付請求中使用它們。但是盡管Apple Pay默認使用了這些信息,用戶仍然可以在授權支付的過程中修改這些信息。
ABRecordRef record = ABPersonCreate(); CFErrorRef error; BOOL success; success = ABRecordSetValue(record, kABPersonFirstNameProperty, @"John", &error); if (!success) { /* ... handle error ... */ } success = ABRecordSetValue(record, kABPersonLastNameProperty, @"Appleseed", &error); if (!success) { /* ... handle error ... */ } 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) { /* ... handle error ... */ } request.shippingAddress = record; CFRelease(shippingAddress); CFRelease(record);
存儲額外信息
使用applicationData屬性來存儲一些在你的應用中關於這次支付請求的唯一標識信息,比如一個購物車的標識符。在用戶授權支付之後,這個屬性的哈希值會出現在這次支付的token中。
part 4 授權支付
支付授權過程是由支付授權view controller和它的代理協作完成的。支付授權view controller做了兩件事情:它讓用戶選擇支付請求所必需的賬單和配送信息,還有讓用戶最終授權同意這次支付。當用戶和view controller交互時,代理方法就會被調用,這樣你的應用就可以不斷地更新顯示的信息--例如在配送地址更改後更新配送費用。用戶最終授權支付請求之後代理方法同樣也會被調用。
注意:在實現這些方法時注意,這些方法可能會被多次調用,而它們被調用的順序取決於用戶的行為的順序。
在所有這個授權過程中被調用的代理方法中,都會有一個completion block被做為參數之一傳入,支付授權view controller會在一個代理方法執行完畢(通過調用completion塊)後再調用另一個代理方法。唯一的例外是paymentAuthorizationViewControllerDidFinish:方法:它不包含completion block,所以它可以在任何時候被調用。
這個completion block有一個傳入參數,基於現有的可用信息,你可以通過這個參數並指定這次交易的狀態。如果這次交易沒有任何問題,傳入PKPaymentAuthorizationStatusSuccess,否則,你要傳入一個識別問題的值。
通過在PKPaymentAuthorizationViewController類的構造方法中傳入一個支付請求來對它進行實例化,然後給這個視圖控制器設置一個代理,就可以把它展示給用戶了。
PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request]; if (!viewController) { /* ... Handle error ... */ } viewController.delegate = self; [self presentViewController:viewController animated:YES completion:nil];
當用戶與這個視圖控制器進行交互時,它的代理方法會被調用。
通過代理更新配送方式和費用
當用戶提供配送信息之後,授權view controller 會調用paymentAuthorizationViewController:didSelectShippingAddress:completion: 和 paymentAuthorizationViewController:didSelectShippingMethod:completion:這兩個代理方法。在這兩個方法中根據最新信息來更新支付請求。
- (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); }
當支付被授權後,支付token會被創建
當用戶最終授權了一個支付請求,框架會通過與蘋果服務器和嵌入在設備中的一個安全模塊進行通信,生成一個支付token。然後你在paymentAuthorizationViewController:didAuthorizePayment:completion:方法中將這個token和其它一些你需要用來處理這次購買的信息--例如配送地址和購物車標識--發送給你的服務器。這個過程是這樣的:
框架發送支付請求給安全模塊,只有安全模塊可以訪問存儲在設備上的標記化的卡信息。
安全模塊把特定的卡和商家等支付數據加密,以保證只有蘋果可以讀取,然後發送給框架。框架會將這些數據發送給蘋果。
蘋果服務器再次加密這些支付數據,以保證只有商家可以讀取。然後服務器對它進行簽名,生成支付token,然後發送給設備。
框架調用相應的代理方法並傳入這個token,然後你的代理方法傳送token給你的服務器。
至於你的服務器采取的行為要取決於你是自己處理這次支付或者你是和其它支付平台合作來進行支付處理。不管怎樣,你的服務器處理這個訂單然後傳送一個狀態信息給設備,代理方法會把這個狀態信息傳送給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); }
在代理方法中釋放授權View Controller
在框架顯示交易狀態之後,授權View Controller會調用代理paymentAuthorizationViewControllerDidFinish:的方法。在這個方法的實現中,先釋放授權頁面控制器再顯示你自己的訂單確認頁面。
- (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller { [controller dismissViewControllerAnimated:YES completion:nil]; }
Part 5 支付處理
處理一個支付請求涉及以下幾個步驟:
把支付信息,以及支付流程+所需的其他信息,一起發送給你的服務器。
驗證支付數據的哈希表和簽名
為加密過的支付數據解碼
向支付處理系統提交支付數據
向訂單追蹤系統提交訂單
處理支付請求時,你有兩個選擇;你既可以利用支付平台處理支付請求,也可以自己實現支付請求處理流程。一個常用的支付平台可以完成上述大部分操作。
讀取,驗證,以及處理支付信息需要有一定的相關密碼知識,例如計算SHA-1哈希表,讀取和驗證PKCS#7簽名,執行Elliptic Curve Diffie-Hellman密匙交換。如果沒有一定的密碼學背景,你可以考慮使用第三方支付平台來完成這些操作。
關於支持Apple Pay支付平台的更多信息,請參考developer.apple.com/apple-pay/
處理支付請求所用的信息擁有一種嵌套式的數據結構,如下圖。支付令牌是PKPaymentToken類的實例。其paymentData屬性值是一個JSON詞典,它的頭文件信息可以用來驗證和加密支付數據。加密過的數據信息包括支付金額、持卡人姓名,以及一些其他指定的支付處理協議。
關於支付數據結構格式的詳細信息,請參看:Payment Token Format Reference.
Figure 5-1支付數據結構