Apple Pay支付流程詳解
前不久Apple Pay開始了,大家做好接入的準備了嗎?今天寫了個demo,悲催的是開發者賬號上裝置滿了,只能用模擬器給大家看了。。。疑問是:在開發Apple Pay的時候,Xcode 7無法 無證書真機除錯嗎?求解
0、 建新工程,忽略
由於想要使用Apple Pay,需要用到Apple頒佈的merchant證書,所以要從申請APP ID開始:
1、在developer.apple建立App ID,如圖:
根據工程的bundle id建立App ID,其他資訊不多說,注意要在App Services欄,勾選 “Apple Pay”,如下圖:
當建立完成後,查詢到你的app id,結果如下圖:
發現Apple Pay狀態是黃色“configurable”,怎麼啟用呢,點選“Edit”,找到“Apple Pay”,點選對應的“Edit”,結果如圖:
這就是讓我們選擇一個Merchant 證書,圖中的兩個是我建立的,那麼大家可能是空的,如果是空的,這就需要去建立Merchant 證書了,然後繼續這一步驟,為該App ID選擇繫結一個Merchant證書。
建立Merchant證書方法:從左側欄建立App IDs那找到Merchant IDs,點進去,新增一個ID!查詢到該Merchant IDs,點選“Edit”,選擇“YES”:
下面就是生產證書的步驟了,不多說,根據操作continue就行啦!!!最後我們會在本地得到一個.cer檔案,雙擊即可。
2、配置Apple Pay到工程,如圖:
如圖操作,開啟Apple Pay的開關,新增一個Merchant ID,如果沒有你建立的,重新整理一下,再選擇即可。如果下面steps報紅,問題可能是剛剛建立的Merchant ID並沒有和App ID繫結,點選fix,一般都沒問題了。
3、主要程式碼如下
import PassKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 支付按鈕
let btn = UIButton(type: UIButtonType.Custom)
btn.frame = CGRectMake(60 , 100, 200, 50)
btn.center = self.view.center
btn.setBackgroundImage(UIImage(named: "ApplePayBTN_64pt__whiteLine_textLogo_"), forState: UIControlState.Normal)
btn.addTarget(self, action: "ApplePay", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func ApplePay() {
// 檢測是否支援ApplePay
if PKPaymentAuthorizationViewController.canMakePayments() {
print("support ApplePay")
// 建立一個支付request
let pkPayRequest = PKPaymentRequest()
// 建立3個商品item,label為商品資訊,amount為價格
let pkPayItem1 = PKPaymentSummaryItem(label: "Lamborghini LP650", amount: NSDecimalNumber(string: "1"))
let pkPayItem2 = PKPaymentSummaryItem(label: "La Ferrari", amount: NSDecimalNumber(string: "1"))
let pkPayItem3 = PKPaymentSummaryItem(label: "Shelby Super Car", amount: NSDecimalNumber(string: "1"))
// 商品總計,label為商家資訊,amount為總金額,type為型別,Final表示總和,Pending則表示不清楚金額 “---”
let pkPayItem4 = PKPaymentSummaryItem(label: "Das Auto", amount: NSDecimalNumber(string: "3"), type: PKPaymentSummaryItemType.Final)
// 為支付request配置屬性
pkPayRequest.paymentSummaryItems = [pkPayItem1, pkPayItem2, pkPayItem3, pkPayItem4]
// CN 表示中國的標準國家編碼,CNY 表示人民幣
pkPayRequest.countryCode = "CN"
pkPayRequest.currencyCode = "CNY"
//支援的卡支付網路 iOS 9.2新增了PKPaymentNetworkChinaUnionPay
pkPayRequest.supportedNetworks = [PKPaymentNetworkVisa]
// Merchant ID
pkPayRequest.merchantIdentifier = "merchant.com.example.lbapplepaydemo"
/*
PKMerchantCapability.Credit NS_ENUM_AVAILABLE_IOS(9_0) = 1UL << 2, // 支援信用卡
PKMerchantCapability.Debit NS_ENUM_AVAILABLE_IOS(9_0) = 1UL << 3 // 支援借記卡
*/
// 支付處理標準
pkPayRequest.merchantCapabilities = PKMerchantCapability.CapabilityCredit
// 增加資訊
// 卡牌賬單地址、
// pkPayRequest.requiredBillingAddressFields = PKAddressField.Email
// 郵寄地址
pkPayRequest.requiredShippingAddressFields = PKAddressField.PostalAddress
pkPayRequest.shippingType = PKShippingType.Delivery
// 郵寄方式
// label 快遞資訊, amount 價格
let method1 = PKShippingMethod(label: "順豐快遞", amount: NSDecimalNumber(string: "12"))
// 使用identifier屬性來在代理方法中區分不同的配送方式,這個屬性只會在你的應用內使用--框架看不到這個屬性,並且它也不會出現在UI中。在建立配送方式時為其分配一個獨一無二的識別符號。為了方便除錯,可使用文字縮寫,比如"discount", "standard", 或者 "next-day".
method1.identifier = "sf"
method1.detail = "全國包郵"
let method2 = PKShippingMethod(label: "圓通快遞", amount: NSDecimalNumber(string: "10"))
method2.identifier = "yt"
method2.detail = "全國包郵"
pkPayRequest.shippingMethods = [method1, method2]
// 建立支付VC
let pkVC = PKPaymentAuthorizationViewController(paymentRequest: pkPayRequest)
pkVC.delegate = self
// present支付控制器
self.presentViewController(pkVC, animated: true, completion: { () -> Void in
})
}
else {
print("該裝置不支援支付")
}
}
}
// PKPaymentVC Delegate
extension ViewController: PKPaymentAuthorizationViewControllerDelegate {
// 支付狀態
func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: (PKPaymentAuthorizationStatus) -> Void) {
print(payment.token)
let asyncSuccessful:Bool = false;
/**
將支付令牌token,payment上傳伺服器端,驗證。。。。
*/
// 根據返回,判斷成功與否
if asyncSuccessful {
completion(PKPaymentAuthorizationStatus.Success)
print("支付成功")
}
else {
completion(PKPaymentAuthorizationStatus.Failure)
print("支付失敗")
}
}
// 支付完成
func paymentAuthorizationViewControllerDidFinish(controller: PKPaymentAuthorizationViewController) {
controller .dismissViewControllerAnimated(true) { () -> Void in
}
}
}
如果手機上wallet已經有了使用者的賬單和配送資訊,可以直接在支付請求中使用它們。但是儘管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);
額外資訊怎麼辦?使用reques.applicationData來儲存一些在你的應用中關於這次支付請求的唯一標識資訊,比如一個購物車的識別符號。在使用者授權支付之後,這個屬性的雜湊值會出現在這次支付的token中。
到此已經完成了Apple Pay的接入 ^ _ ^
4、處理支付的其他代理詳解:
支付授權過程是由支付授權view controller和它的代理協作完成的。支付授權view controller做了兩件事情:它讓使用者選擇支付請求所必需的賬單和配送資訊,還有讓使用者最終授權同意這次支付。當用戶和view controller互動時,代理方法就會被呼叫,這樣你的應用就可以不斷地更新顯示的資訊–例如在配送地址更改後更新配送費用。使用者最終授權支付請求之後代理方法同樣也會被呼叫。
注意:在實現這些方法時注意,這些方法可能會被多次呼叫,而它們被呼叫的順序取決於使用者的行為的順序。
當用戶提供配送資訊之後,授權view controller 會呼叫paymentAuthorizationViewController:didSelectShippingAddress:completion: 和 paymentAuthorizationViewController:didSelectShippingMethod:completion:這兩個代理方法。在這兩個方法中根據最新資訊來更新支付請求。
func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didSelectShippingMethod shippingMethod: PKShippingMethod, completion: (PKPaymentAuthorizationStatus, [PKPaymentSummaryItem]) -> Void) {
print(shippingMethod.detail)
completion(PKPaymentAuthorizationStatus.Success, self.summaryItems)
}
func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didSelectShippingAddress address: ABRecord, completion: (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Void) {
print(address)
completion(PKPaymentAuthorizationStatus.Success, self.shippingMethods,self.summaryItems)
}
當支付被授權後,支付token會被建立
當用戶最終授權了一個支付請求,框架會通過與蘋果伺服器和嵌入在裝置中的一個安全模組進行通訊,生成一個支付token。然後你在paymentAuthorizationViewController:didAuthorizePayment:completion:方法中將這個token和其它一些你需要用來處理這次購買的資訊–例如配送地址和購物車標識–傳送給你的伺服器。這個過程是這樣的:
框架傳送支付請求給安全模組,只有安全模組可以訪問儲存在裝置上的標記化的卡資訊。
安全模組把特定的卡和商家等支付資料加密,以保證只有蘋果可以讀取,然後傳送給框架。框架會將這些資料傳送給蘋果。
蘋果伺服器再次加密這些支付資料,以保證只有商家可以讀取。然後伺服器對它進行簽名,生成支付token,然後傳送給裝置。
框架呼叫相應的代理方法並傳入這個token,然後你的代理方法傳送token給你的伺服器。
至於你的伺服器採取的行為要取決於你是自己處理這次支付或者你是和其它支付平臺合作來進行支付處理。不管怎樣,你的伺服器處理這個訂單然後傳送一個狀態資訊給裝置,代理方法會把這個狀態資訊傳送給completion塊,像在“Processing a Payment”中討論過的。
func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: (PKPaymentAuthorizationStatus) -> Void) {
print(payment.token)
let asyncSuccessful:Bool = false;
/**
將支付令牌payment上傳伺服器端,驗證簽名,獲取token?
*/
let error:NSError
let addressMultiValue = ABRecordCopyValue(payment.billingContact, kABPersonAddressProperty) as! ABMultiValue
let addressDictionary = ABMultiValueCopyValueAtIndex(addressMultiValue, 0) as! AnyObject
do {
let json = try NSJSONSerialization.dataWithJSONObject(addressDictionary, options: NSJSONWritingOptions.PrettyPrinted)
}
catch {
}
// 根據返回,判斷成功與否
if asyncSuccessful {
completion(PKPaymentAuthorizationStatus.Success)
print("支付成功")
}
else {
completion(PKPaymentAuthorizationStatus.Failure)
print("支付失敗")
}
}
5、 支付處理
處理一個支付請求涉及以下幾個步驟:
把支付資訊,以及支付流程+所需的其他資訊,一起傳送給你的伺服器
驗證支付資料的雜湊表和簽名
為加密過的支付資料解碼
向支付處理系統提交支付資料
向訂單追蹤系統提交訂單
處理支付請求時,你有兩個選擇:你既可以利用支付平臺處理支付請求,也可以自己實現支付請求處理流程。一個常用的支付平臺可以完成上述大部分操.
Demo 地址:https://github.com/jakajacky/ApplePayDemo.git