一、代碼實現之前先配置證書和Merchant ID :
具體操作看:http://blog.csdn.net/songchunmin_/article/details/51543356
demo地址:https://github.com/songchunmin/PayDemo
二、具體實現
/* 判定用戶是否能夠支付,在創建支付請求之前,要首先通過調用PKPaymentAuthorizationViewController 類裡的 canMakePaymentsUsingNetworks:方法來判斷用戶是否能夠使用你提供的支付網絡進行支付。 如果要判斷用戶的硬件是否支持Apple Pay或者是否因為家長控制而不能支付,請使用canMakePayments 方法。 如果用戶不能進行支付,那就不要顯示支付按鈕,相應的應該退回到其它支付方式。 */ if([PKPaymentAuthorizationViewController canMakePayments]) { NSLog(@"Woo! Can make payments!"); PKPaymentRequest *request = [[PKPaymentRequest alloc] init]; // PKPaymentSummaryItem *widget1 = [PKPaymentSummaryItem summaryItemWithLabel:@"娃哈哈" amount:[NSDecimalNumber decimalNumberWithString:@"0.01"] type:PKPaymentSummaryItemTypeFinal]; PKPaymentSummaryItem *widget2 = [PKPaymentSummaryItem summaryItemWithLabel:@"鮮牛奶" amount:[NSDecimalNumber decimalNumberWithString:@"0.01"]]; PKPaymentSummaryItem *total = [PKPaymentSummaryItem summaryItemWithLabel:@"Grand Total" amount:[NSDecimalNumber decimalNumberWithString:@"0.02"]]; //數組中,最後的對象是總價。 request.paymentSummaryItems = @[widget1, widget2, total]; //國家--一定要填寫正確,如果不知道的話,隨便輸入,控制台會列舉所有的出來, request.countryCode = @"CN"; //貨幣單位需要使用- 人民幣 request.currencyCode = @"CNY"; //Wallet所綁定的卡的類型, 銀聯記得加上,我記得在配置證書那裡選項,是否支持中國境內(大概意思),這裡不加PKPaymentNetworkChinaUnionPay直接Crash,估計和這個有關。。。 request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa,PKPaymentNetworkChinaUnionPay]; request.merchantIdentifier = @"merchant.com.scm.PayDemo"; //通過指定merchantCapabilities屬性來指定你支持的支付處理標准,3DS支付方式是必須支持的,EMV方式是可選的。 request.merchantCapabilities = PKMerchantCapabilityEMV; //設置後,如果用戶之前沒有填寫過,那麼會要求用戶必須填寫才能夠使用Apple Pay // request.requiredShippingAddressFields = PKAddressFieldPostalAddress | PKAddressFieldPhone | PKAddressFieldEmail | PKAddressFieldName; //顯示支付信息的控制器 self.paymentPane = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request]; self.paymentPane.delegate = self; [self presentViewController:_paymentPane animated:TRUE completion:nil]; } else { NSLog(@"This device cannot make payments"); } } /** * 用戶發送付款請求後會調用該方法。在這個方法中發送相關的支付信息到你的服務器,最後通過服務器來處理。如果服務期處理成功, * 那麼需要調用 completion 的block 並且傳入 PKPaymentAuthorizationStatusSuccess 的標記即可。 * 如果服務器處理不成功,那麼傳一個其他的標記就可以了 */ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus status))completion { NSLog(@"Payment was authorized: %@", payment); // do an async call to the server to complete the payment. // See PKPayment class reference for object parameters that can be passed BOOL asyncSuccessful = FALSE; // When the async call is done, send the callback. // Available cases are: // PKPaymentAuthorizationStatusSuccess, // Merchant auth'd (or expects to auth) the transaction successfully. // PKPaymentAuthorizationStatusFailure, // Merchant failed to auth the transaction. // // PKPaymentAuthorizationStatusInvalidBillingPostalAddress, // Merchant refuses service to this billing address. // PKPaymentAuthorizationStatusInvalidShippingPostalAddress, // Merchant refuses service to this shipping address. // PKPaymentAuthorizationStatusInvalidShippingContact // Supplied contact information is insufficient. if(asyncSuccessful) { completion(PKPaymentAuthorizationStatusSuccess); // do something to let the user know the status NSLog(@"Payment was successful"); // [Crittercism endTransaction:@"checkout"]; } else { completion(PKPaymentAuthorizationStatusFailure); // do something to let the user know the status NSLog(@"Payment was unsuccessful"); // [Crittercism failTransaction:@"checkout"]; }三、注意事項
創建支付請求
支付請求是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.75subtotal NSDecimalNumber*subtotalAmount=[NSDecimalNumberdecimalNumberWithMantissa:1275exponent:-2isNegative:NO]; self.subtotal=[PKPaymentSummaryItemsummaryItemWithLabel:@"Subtotal"amount:subtotalAmount]; //2.00discount NSDecimalNumber*discountAmount=[NSDecimalNumberdecimalNumberWithMantissa:200exponent:-2isNegative:YES]; self.discount=[PKPaymentSummaryItemsummaryItemWithLabel:@"Discount"amount:discountAmount];
注意
這裡使用NSDecimalNumber類來存儲摘要項目的數額,它是一個以10為底數的數值。可以使用指定尾數和指數的方式(像代碼中那樣)來創建這個類的實例,也可以通過指定字符串和locale來實例化,字符串指定了相應的數值。這裡總是使用以10為底數的數值來做財務計算--例如當需要計算5%折扣掉的金額時。
盡管有時使用其它的計數方法更方便,但是像float或者Double這樣的IEEE浮點數類型是不適合作財務計算的,這些數據類型使用的是以2為底數的數值表示方法,這就表示有一些十進制數值不能准確得被表示--例如0.42必須以0.41999這樣的循環小數來近似表示,而這種近似表示常常會造成財務計算的錯誤結果。
在這個摘要項目列表中的最後一個是總計金額。這個金額是通過把所有其它金額相加而得到。總計的顯示方法和其它的摘要項目不同:應該使用你公司的名稱做為其標簽,使用所有其它項目的金額總和做為金額。使用paymentSummaryItems屬性將這些摘要項目加入支付請求。
//10.75grandtotal NSDecimalNumber*totalAmount=[NSDecimalNumberzero]; totalAmount=[totalAmountdecimalNumberByAdding:subtotalAmount]; totalAmount=[totalAmountdecimalNumberByAdding:discountAmount]; self.total=[PKPaymentSummaryItemsummaryItemWithLabel:@"MyCompanyName"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]; //Supports3DSonly request.merchantCapabilities=PKMerchantCapability3DS; //Supportsboth3DSandEMV request.merchantCapabilities=PKMerchantCapability3DS|PKMerchantCapabilityEMV;
指示所需配送信息和賬單信息
通過填充requiredBillingAddressFields和requiredShippingAddressFields屬性來指定所需賬單信息和配送地址信息。當你顯示一個視圖控制器時,它會提示用戶輸入所需內容。這些字段常量可以像下面這樣進行組合來設置這些屬性:
request.requiredBillingAddressFields=PKAddressFieldEmail; request.requiredBillingAddressFields=PKAddressFieldEmail|PKAddressFieldPostalAddress;
如果你已經有了用戶的賬單和配送信息,可以直接在支付請求中使用它們。但是盡管Apple Pay默認使用了這些信息,用戶仍然可以在授權支付的過程中修改這些信息。
ABRecordRefrecord=ABPersonCreate(); CFErrorReferror; BOOLsuccess; success=ABRecordSetValue(record,kABPersonFirstNameProperty,@"John",&error); if(!success){/*...handleerror...*/} success=ABRecordSetValue(record,kABPersonLastNameProperty,@"Appleseed",&error); if(!success){/*...handleerror...*/} ABMultiValueRefshippingAddress=ABMultiValueCreateMutable(kABMultiDictionaryPropertyType); NSDictionary*addressDictionary=@{ (NSString*)kABPersonAddressStreetKey:@"1234LaurelStreet", (NSString*)kABPersonAddressCityKey:@"Atlanta", (NSString*)kABPersonAddressStateKey:@"GA", (NSString*)kABPersonAddressZIPKey:@"30303" }; ABMultiValueAddValueAndLabel(shippingAddress, (__bridgeCFDictionaryRef)addressDictionary, kABOtherLabel, nil); success=ABRecordSetValue(record,kABPersonAddressProperty,shippingAddress,&error); if(!success){/*...handleerror...*/} 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=[[PKPaymentAuthorizationViewControlleralloc]initWithPaymentRequest:request]; if(!viewController){/*...Handleerror...*/} viewController.delegate=self; [selfpresentViewController:viewControlleranimated:YEScompletion: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; [selfupdateShippingCost]; NSArray*shippingMethods=[selfshippingMethodsForAddress:address]; completion(PKPaymentAuthorizationStatusSuccess,shippingMethods,self.summaryItems); } -(void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController*)controller didSelectShippingMethod:(PKShippingMethod*)shippingMethod completion:(void(^)(PKPaymentAuthorizationStatus,NSArray*))completion { self.selectedShippingMethod=shippingMethod; [selfupdateShippingCost]; completion(PKPaymentAuthorizationStatusSuccess,self.summaryItems); }
當支付被授權後,支付token會被創建
當用戶最終授權了一個支付請求,框架會通過與蘋果服務器和嵌入在設備中的一個安全模塊進行通信,生成一個支付token。然後你在paymentAuthorizationViewController:didAuthorizePayment:completion:方法中將這個token和其它一些你需要用來處理這次購買的信息--例如配送地址和購物車標識--發送給你的服務器。這個過程是這樣的:
框架發送支付請求給安全模塊,只有安全模塊可以訪問存儲在設備上的標記化的卡信息。
安全模塊把特定的卡和商家等支付數據加密,以保證只有蘋果可以讀取,然後發送給框架。框架會將這些數據發送給蘋果。
蘋果服務器再次加密這些支付數據,以保證只有商家可以讀取。然後服務器對它進行簽名,生成支付token,然後發送給設備。
至於你的服務器采取的行為要取決於你是自己處理這次支付或者你是和其它支付平台合作來進行支付處理。不管怎樣,你的服務器處理這個訂單然後傳送一個狀態信息給設備,代理方法會把這個狀態信息傳送給completion塊,像在“Processing a Payment”中討論過的。
-(void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController*)controller didAuthorizePayment:(PKPayment*)payment completion:(void(^)(PKPaymentAuthorizationStatus))completion { NSError*error; ABMultiValueRefaddressMultiValue=ABRecordCopyValue(payment.billingAddress,kABPersonAddressProperty); NSDictionary*addressDictionary=(__bridge_transferNSDictionary*)ABMultiValueCopyValueAtIndex(addressMultiValue,0); NSData*json=[NSJSONSerializationdataWithJSONObject:addressDictionaryoptions:NSJSONWritingPrettyPrintederror:&error]; //...Sendpaymenttoken,shippingandbillingaddress,andorderinformationtoyourserver... PKPaymentAuthorizationStatusstatus;//Fromyourserver completion(status); }
在代理方法中釋放授權View Controller
在框架顯示交易狀態之後,授權View Controller會調用代理paymentAuthorizationViewControllerDidFinish:的方法。在這個方法的實現中,先釋放授權頁面控制器再顯示你自己的訂單確認頁面。
-(void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController*)controller { [controllerdismissViewControllerAnimated:YEScompletion: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支付數據結構