1.About Apple Pay
Apple Pay是一种移动支付技术,让使用者把它们对真实的物品和服务的支付信息以一种方便和安全的方式给你。web
对于在app中给出的数字物品和服务,可查看In-App Purchase Programming Guide。json
Working with Apple Pay
使用Apple Pay功能的Apps须要在Xcode 中开启Apple Pay capabilities。也须要注册一个商家标识并设置密钥(用来加密发送支付数据给服务器)。数组
初始化支付时,app建立一个支付请求。该请求包含几乎所有的服务和商品购买,也包含任何额外的费用:税金、运费或者折扣。传送该请求给支付受权视图控制器,控制器展现该请求给用户并对任何须要的信息做出提示,例如运送地址或者帐单地址。当用户与视图控制器进行交互时,调用代理用以更新请求。安全
一旦用户受权支付,Apple Pay加密支付信息用以保护未受权的第三方访问它。在设备上,Apple Pay发送支付请求给加密元件,该元件是一个用户设备上的专用芯片。该加密元件添加支付信息为指定的卡和商家生成一个加密的支付密钥。而后传递该密钥给Apple 服务器,在服务器上使用商家标识证书(Merchant Identifier certificate)解密。最后,服务器传递该密钥返回到app中继续处理。服务器
支付密钥从不访问或者存取在Apple服务器上。该服务器仅仅可以使用证书解密密钥。这种处理使得不用加密证书app安全的加密支付信息,且必须分配商家标识证书做为app的一部分。网络
更多关于Apple Pay安全的信息,可查看iOS Security Guide。数据结构
大多数状况下,app传送加密密钥给第三发支付平台解密并处理支付。然而,若是本身的团队有已经存在的支付基础设施,能够在本身的服务器上解密并处理支付。并发
更多关于支持Apple Pay的支付平台的信息,可查看develop.apple.com/apple-pay/。app
2.Configuring Your Environment
商户ID用来判别用户是否对Apple Pay可以接受支付。与商户ID有关的公共的钥匙和证书被用来做为支付进程的一部分,用来加密支付信息。在app可使用Apple Pay以前,须要注册一个merchant ID商户ID 并配置它的证书。框架
注册商户ID(a merchant ID)
1)在会员中心(Member Center),选择Certificates,Identifiers & Profiles(http://developer.apple.com/account)选项。
2)在Identifiers选项下面,选择Merchant IDs选项。
3)点击右上角的添加按钮(+)。
4)输入一段描述和标识,点击Continue继续按钮。
5)检查设置内容,而后点击Register注册按钮。
6)点击Done 按钮,完成。
配置商户ID相应的证书
1)在会员中心(Member Center)(https://developer.apple.com),选择Certificates,Identifiers & Profiles选项。
2)在Identifiers选项下面,选择Merchant IDs选项。
3)从列表中选择商户ID(merchant ID),点击Edit编辑按钮。
4)点击Create Certificate按钮生成证书,跟随得到的说明或是生成一个CSR(certificate signing request),点击Continue按钮。
5)点击选择文件,选中CSR文件,点击Generate按钮。
6)点击Download按钮下载该证书,并点击Done按钮,完成。
若是在钥匙串使用中看见一个警告warning ,说明证书被未知的认证受权签名或者它是一个无效的发行者,确认有the WWDR intermediate certificate - G2证书并在钥匙串中安装了the Apple Root CA - G2。这些能够在apple.com/certificateauthority 下载获得。
注意:
当出现故障时,有时候这对手动开启Apple Pay功能有所帮助。跟随下面的步骤手动开启Apple Pay功能。
1)在会员中心(Member Center),选择Certificates ,Identifiers & Profiles选项。
2)在Identifiers选项下,选择App IDs选项。
3)从列表中选中app ID,点击Edit按钮。
4)选择Apple Pay选项,点击Edit按钮。
5)选择准备使用的商户IDs(merchant IDs),点击Continue按钮。
6)检查设置内容,而后点击Assign指派按钮。
7)点击Done按钮,完成。
3.Creating Payment Requests
Payment Requests支付请求是PKPaymentRequest类的实例。支付请求包含一系列描述用户的支付对象(为哪些东西付款)的概要项:可用的运送方式的列表,用户须要提供的运送信息的描述,关于商家和支付处理的信息。
Decide Whether the User Can Make Payments 判决用户是否有能力支付
在生成一个支付请求以前,经过调用PKPaymentAuthorizationViewController类中的canMakePaymentsUsingNetworks:方法判断用户是否可以使用支持的系统完成支付。使用canMakePayments方法,检查设备的硬件和父类控制是否支持Apple Pay。
若是canMakePayments返回NO,表示设备不支持Apple Pay。所以也不显示Apple Pay按钮,转到其余的支付方式。
若是canMakePayments返回YES,可是canMakePaymentsUsingNetworks:返回NO,表示设备支持Apple Pay,可是用户没有添加任何符合要求的支付系统的卡片。可选择性地去显示一个支付设置按钮,提示用户设置卡片。一旦用户点击了该按钮,初始化设置一个新卡的进程(例如:调用openPaymentSetup方法)。
另外,一旦用户按下Apple Pay按钮,必须开始支付受权进程。在展现支付请求以前不能要求用户执行其余任何任务。例如,若是用户须要输入折扣码,必须在按下Apple Pay按钮以前请求该代码。
Bridging from Web-Based Interfaces 基于网络接口进行桥接
若是app使用网络接口购买物品和服务,必须在处理Apple Pay交易以前从网络接口移动该请求到本地iOS代码。列表3-1展现了须要处理web view的请求的步骤。
Listing 3-1Buying items from a web view
// Called when the web view tries to load "myShoppingApp:buyItem" -(void)webView:(nonnull WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler { // Get the URL for the selected link. NSURL *URL = navigationAction.request.URL; // If the scheme and resource specifier match those defined by your app, // handle the payment in native iOS code. if ([URL.scheme isEqualToString:@"myShoppingApp"] && [URL.resourceSpecifier isEqualToString:@"buyItem"]) { // Create and present the payment request here. // The web view ignores the link. decisionHandler(WKNavigationActionPolicyCancel); } // Otherwise the web view loads the link. decisionHandler(WKNavigationActionPolicyAllow); }
Payment Requests Include Currency and Region Information 包括货币和地区信息的支付请求
在支付请求中全部的概要数值均使用相同的货币(指定使用PKPaymentRequest类中的currencyCode属性),均使用三个字母的ISO货币代码,例如USD。
支付请求的国家码代表在该国家发生购买操做或者在该国家购买将要被处理。使用两个字母的ISO国家码,例如US。
在支付请求中设置的商户ID(merchant ID)必须与app中的entitlement中的merchant IDs相匹配.
request.currencyCode = @"USD"; request.countryCode = @"US"; request.merchantIdentifier = @"merchant.com.example";
Payment Requests Have a List of Payment Summary Items支付请求有一个支付概要项的列表
展现在PKPaymentSummaryItem类中的支付概要项,描述给用户的支付请求的不一样部分。使用一个小部分的概要项- 典型地包含总和、任何折扣、运费、税金和最终的总和。若是没有任何其余额外的费用(例如:运费或税金),仅仅包含购物的总和。在app中提供逐项的消费细节。
每一个概要项有一个标记和数值,展现在Listing 3-2。标记是用户可读的概要项总结的描述。该数值与支付数值是一致的。在支付请求中的全部数值均使用在支付请求中指定的货币。折扣或者优惠劵的数值,则设置为负数。
当支付被受权的时候,若是不知道某个实际的费用(例如:打车费),生成一个使用PKPaymentSummaryItemTypePending类型的总和概要项而且值为0.0。该系统而后标记该费用为未决定的。
Listing 3-2Creating a payment summary item
// 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为基的数。该类的实例能够经过明确地指定尾数和指数(展现在代码列表中)或者提供一个string类型的数量并指定一个区域来建立。老是使用以10为基的数来进行金融计算,例如,肯定5%折扣的数量。
即便IEEE的浮点类型数据例如float和Double显示起来很是方便,但不适合于金融计算。这些数据类型使用以2为基的数字表示出来,这意味着一些小数数值不能被精确地表示出来。例如:0.42极可能接近于0.419999无限循环。这类近似值会形成金融计算获得错误的结果。
在列表中最后的一个支付概要项是最终的总和。经过添加全部其余的概要项数值来计算总和数值。最终总和的显示不一样于其余的概要项:使用公司的名称做为它的label内容,使用全部其余的概要项的数值之和做为它的数值。使用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;
A Shipping Method Is a Special Payment Summary Item 运送方式是一种特殊的支付概要项
为每一个适用的运送方式建立一个PKShippingMethod的实例。正如其余的支付概要项同样,运送方式有一个用户可读label内容(例如“标准的运送”或者“第二天运送”)和一个运费的数值。不像其余的概要项,运送方式也有detail属性-例如:“Arrives by July 29”或者“Ships in 24 hours”- 这说明了运送方式之间的不一样。
为了在代理方法中区分运送方式,可以使用identifier属性。该属性仅仅被用于在本身的app中-框架处理它做为一个不透明的值,也不显示在UI界面中。当建立每一个运送方式时,为它指定一个独一无二的标识。为了使调试容易些,使用摘要或者简短的string值,例如“discount”,“standard”或者“next-day”。
一些运送方式不适用于全部的区域或者对不一样的地址有不一样的运费。当用户选择一个运送地址或者方式时能够更新这些信息,正如Your Delegate Updates Shipping Methods and Costs中描述的。
Indicating Your Support Payment Processing Mechanisms 指定支持的支付处理机制
经过用string常量的数组填充supportNetworks属性来指定支持何种支付系统。经过给merchantCapabilities属性设定一个值来指定支持何种支付处理协议。必须支持3DS,支持EMV是可选的。
商家支付能力是位掩码,联合展现以下:
request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkDiscover, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa]; // Supports 3DS only request.merchantCapabilities = PKMerchantCapability3DS; // Supports both 3DS and EMV request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
Indicating What Shipping and billing Information Is Needed 指定哪些运送和帐单信息是必需的
填充支付受权视图控制器的requiredBillingAddressFields和requiredShippingAddressFields属性来指定哪些帐单和运送信息是必需的。当展现该视图控制器时,它提示用户提供要求的帐单和运送信息。这些区域的常量按照下面的方式联合设定这些属性的值:
request.requiredBillingAddressFields = PKAddressFieldEmail;
request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
注意:
仅仅请求须要用来处理支付的帐单和运送信息和传送商品或者服务。请求没必要要的信息会增长不须要的复杂性。每一个额外的步骤会增长用户简单地取消了该交易的可能性。
若是你有最新的帐单和运送联系方式信息,能够在支付请求时设置这些内容。Apple Pay会默认使用这些信息;然而,用户仍然能够选择其余的联系方式信息做为支付受权处理的一部分。
PKContact *contact = [[PKContact alloc] init]; // 联系人姓名 NSPersonNameComponents *name = [[NSPersonNameComponents alloc] init]; name.givenName = @"Chars"; name.familyName = @"Davy"; contact.name = name; // 联系人地址 CNMutablePostalAddress *address = [[CNMutablePostalAddress alloc] init]; address.street = @"1234 Laurel Street"; address.city = @"Atlanta"; address.state = @"GA"; address.postalCode = @"30303"; contact.postalAddress = address; request.shippingContact = contact;
注意:
地址信息能够来自在iOS中的普遍的输入源。在使用它以前老是验证这些信息。
Storing Additional Information 存储额外的信息
为了存储每一个app特定的支付请求的信息,例如一个购物车标识,使用applicationData属性。该属性被对待做为一个系统提供的不透明的值。当用户受权支付请求以后一大把应用数据会显示在支付密钥中。
4.Authorizing Payments
支付受权处理是支付受权视图控制器和它的代理之间的合力合做的结果。支付受权视图控制器作两件事:让用户选择支付请求中所须要的帐单和运送信息,让用户去受权支付。当用户和视图控制器交互时调用代理的方法以便app能够更新展现的信息-例如:当一个运送地址被选择时更新运送价格。在用户受权支付请求以后调用该代理。
注意:
当实现代理方法时,记得它们会被调用不少次而且它们被调用的规则取决于用户的动做顺序。
全部在受权处理期间调用的代理方法均传送一个完成的block做为它们的参数之一。在它调用其余代理方法以前,支付受权视图控制器等待代理完成响应一个方法(经过调用完成block)。paymentAuthorizationViewControllerDidFinish:方法是惟一的例外。它没有取到完成block,可是它在任什么时候候都可被调用。
完成 block取到一个论证,该论证能够指定基于可用信息的交易的当前受权状态。若是该交易没有任何问题,传送该值PKPaymentAuthorizationStatusSuccess;不然,传送一个值来标识错误。
为了建立一个PKPaymentAuthorizationViewController类的实例,传送支付请求给视图控制器的初始化。设置该视图控制器的代理,而后显示它。
PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request]; if (!viewController) { /* ... Handle error ... */ } viewController.delegate = self; [self presentViewController:viewController animated:YES completion:nil];
当用户与视图控制器交互时,视图控制器调用它的代理方法。
注意:
在Xcode 7.0或者之后,能够在模拟器上测试支付受权视图控制器。它提供了全部支持的支付系统的模拟卡片并以简单的文本形式返回虚拟的支付数据。在设备上,该数据使用商户标识加密并必须在本身的服务器上或者在支付处理时解密。
虽然模拟器提供一个快捷并且方便的方式测试本身的代码,仍然须要在真实的物理设备上彻底地测试Apple Pay。
若是使用 Xcode的早期版本,仅仅能够在设备上测试Apple Pay。
Your Delegate Updates Shipping Methods 代理更新运送方式和费用
当用户提供运送信息时,受权视图控制器调用代理的paymentAuthorizationViewController:didSelectShippingContact:completion:方法和paymentAuthorizationViewController:didSelectShippingMethod:completion:方法。使用这些方法来更新基于这些新信息的支付请求。
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingContact:(CNContact *)contact completion:(void (^) (PKPaymentAuthorizationStatus, NSArray *, NSArray *))completion{ self.selectedContact = contact; [self updateShippingCost]; NSArray *shippingMethods = [self shippingMethodsForContact:contact]; 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); }
注意:
为了维护隐私,paymentAuthorizationViewController:didSelectShippingContact:completion:方法中说起的运送信息是匿名的。返回的联系方式包含足够的信息用以计算运费,不展示用户的敏感信息。直到用户批准了该支付以后,才能获取到用户的所有运送信息,不然不能获取到。另外,联系方式中的数据能够根据不一样的国家而改变,并能够一次又一次地改变。保证以合适的方式测试app。
A Payment Token Is Created When a Payment Is Authorized 当支付被受权时生成一个支付密钥
当用户受权支付请求时,框架经过Apple 服务器和安全元件联合生成一个支付密钥。能够经过paymentAuthorizationViewController:didAuthorizePayment:completion:代理方法传送该支付密钥给本身的服务器,与其它须要的信息一块儿处理购买。例如:运送地址和购物车标识。过程以下:
1)框架发送支付请求给安全元件。只有安全元件可使用tokenized标记化的特定设备的支付卡号。
2)安全元件将含有特定卡和商户的支付数据 放在一块儿,加密它使得只有Apple 能够读取它,并发送它给框架。框架而后发送支付数据给Apple 服务器。
3)Apple 服务器使用Merchant Identifier certificate商户标识证书解密支付数据。该密钥仅仅本身和那些已分享商户标识证书给他们的人可读。而后服务器写下该支付密钥,并返回它给设备。
4)框架经过调用
paymentAuthorizationViewController:didAuthorizePayment: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); }
Your Delegate Dismisses the Payment Authorization View Controller代理dismiss支付受权视图控制器
当框架显示交易的状态以后,受权视图控制器调用代理的paymentAuthorizationViewControllerDidFinish:方法。在实施阶段,dismiss受权视图控制器而且显示本身app特定的订单确认页面。
- (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller { [controller dismissViewControllerAnimated:YES completion:nil]; }
5.Processing Payments
处理支付包括这几步:
1)发送支付信息给服务器,与须要的其它信息一块儿处理订单
2)验证支付数据的哈希表和签名
3)解密加密的支付数据
4)提交支付数据给支付处理网络
5)提交订单给订单追踪系统
两种选择处理支付:有一个支付平台的优点去处理支付,或者本身实施支付进程。一个支付处理平台通常处理大部分如上步骤。
读取、验证和处理支付信息须要对密码学的几种领域有所理解,例如,计算一个SHA-1 哈希,读取和验证一个PKCS #7类型的签名,和执行elliptic curve Diffie-Hellman key椭圆曲线密钥交换。若是没有密码学的背景,考虑使用一个支付平台来处理这些操做。更多支持Apple Pay的支付平台的信息,可查看developer.apple.com/apple-pay/。
用来处理支付的信息有一个嵌套的数据组织,正如展现在Figure 5-1中支付密钥是PKPaymentToken类的实例。它的paymentData属性的值是一个JSON字典,它有 个头部header包含用于确认和加密支付数据的信息。加密数据包含像数量、持卡人姓名和其它用于特定的支付处理协议的的信息。
Figure 5-1Payment data structure
关于支付数据结构格式的详情,可查看Payment Token Format Reference。