本文翻译整理自:About Apple Pay (更新时间:2017-03-16)
https://developer.apple.com/library/archive/ApplePay_Guide/index.html#//apple_ref/doc/uid/TP40014764
文章目录
- 一、关于 Apple Pay
- 1、使用 Apple Pay
- 2、测试 Apple Pay 交易
- 二、配置您的环境
- 三、创建付款请求
- 1、决定用户是否可以付款
- 2、从基于 Web 的界面进行桥接
- 3、付款请求包括货币和地区信息
- 4、付款请求包含付款摘要项目列表
- 5、Shipping 方式是特殊的付款摘要项目
- 6、表明您支持的付款处理机制
- 7、说明需要哪些运输和账单信息
- 8、存储其他信息
- 四、授权付款
- 您的代理更新运输方式和费用
- 当付款获得授权时,将创建付款令牌
- 您的代理解散了付款授权视图控制器
- 五、处理付款
一、关于 Apple Pay
Apple Pay 是一种移动支付技术,它为用户提供了一种简单而安全的方式,让他们可以在 iOS 应用、watchOS 应用和 Safari 网站上为现实世界的商品和服务付款。
本编程指南讨论了 iOS 应用中的 Apple Pay。
对于 web 上的 Apple Pay,请参阅Apple Pay JS。
对于应用内提供的数字商品和服务,请参阅*应用内购买编程指南*。
1、使用 Apple Pay
使用 Apple Pay 的应用需要在 Xcode 中启用 Apple Pay 功能。
您还需要注册商家 ID 并创建付款处理证书(Payment Processing certificate),这是一个加密密钥,用于安全地将付款数据发送到您的服务器。
要发起付款,您的应用会创建一个付款请求。
此请求包括所购买服务和商品的小计,以及任何额外的税费、运费或折扣费用。
将此请求传递给付款授权视图控制器,该视图控制器向用户显示该请求并提示输入任何所需信息,例如送货地址或帐单地址。
当用户与视图控制器交互时,您的代理会被调用来更新请求。
用户授权付款后,Apple Pay 会立即加密付款信息,以防止未经授权的第三方访问这些信息。
在设备上,Apple Pay 将付款请求发送到安全元件,这是用户设备上的专用芯片。
安全元件会添加指定卡和商家的付款数据,从而创建加密的付款令牌。
然后,它会将此令牌传递到 Apple 的服务器,在那里使用您的付款处理证书对其进行重新加密。
最后,服务器将令牌传回您的应用进行处理。
Apple 的服务器绝不会访问或存储付款令牌。
服务器只会使用您的证书重新加密令牌。
此过程可让您的应用安全地加密付款信息,而无需在应用中分发您的付款处理证书。
有关 Apple Pay 安全性的更多信息,请参阅iOS 安全指南。
在大多数情况下,您的应用会将加密的付款令牌传递给第三方支付解决方案提供商,以解密和处理付款。
但是,如果您的团队有现有的支付基础设施,您可以在自己的服务器上解密和处理付款。
有关支持 Apple Pay 的支付解决方案提供商的信息,请参阅Apple Pay - Apple Developer。
2、测试 Apple Pay 交易
使用 Apple Pay 沙盒环境通过测试支付卡测试您的交易。
- 在 App Store Connect 中创建一个测试账户。
此账户可用于 App Store 和 Apple Pay 测试。 - 在有效的测试设备上,使用测试帐户登录 iCloud。
- 在 Wallet 应用中,使用手动输入添加新卡。
登录和退出 iCloud 帐户会移除您的卡。
测试卡只能在沙盒环境中使用。
此外,沙盒环境仅测试您的应用与测试卡网络之间的连接。
它不会测试您的应用与支付解决方案提供商之间的连接。
有关更多信息,请参阅Apple Pay 沙盒测试。
二、配置您的环境
商家 ID 可向 Apple Pay 证明您能够接受付款。
与您的商家 ID 关联的付款处理证书用于加密付款信息。
在您的应用可以使用 Apple Pay 之前,您需要注册商家 ID 并创建其付款处理证书。
注册商户 ID
- 在会员中心,选择“Certificates, Identifiers & Profiles.”。
- 在标识符下,选择商家 ID。
- 单击右上角的添加按钮 (+)。
- 输入描述和标识符,然后单击继续。
- 检查设置,然后单击“注册”。
- 单击完成。
创建付款处理证书
- 在会员中心,选择“Certificates, Identifiers & Profiles.”。
- 在标识符下,选择商家 ID。
- 从列表中选择商家 ID,然后单击编辑。
- 在付款处理证书部分,单击创建证书。
按照说明获取或生成证书签名请求 (CSR),然后单击继续。 - 单击“选择文件”,选择您的 CSR,然后单击“生成”。
- 单击“下载”下载证书,然后单击“完成”。
如果您在 Keychain Access 中看到警告,提示证书由未知机构签名或颁发者无效,请确保您在钥匙串中安装了 WWDR intermediate 证书 - G2 和 Apple Root CA - G2。
您可以从apple.com/certificateauthority 下载它们。
要在 Xcode 中为您的应用启用 Apple Pay,请打开“功能”窗格。
选择 Apple Pay 行中的开关,然后选择您希望应用使用的商家 ID。
笔记
进行故障排除时,手动启用 Apple Pay 有时很有帮助。请按照以下步骤手动启用 Apple Pay:
- 在会员中心,选择“ Certificates, Identifiers & Profiles”。
- 在“标识符”下,选择“应用程序 ID”。
- 从列表中选择应用程序 ID,然后单击“编辑”。
- 选择 Apple Pay,然后单击“编辑”。
- 选择您要使用的商家ID,然后单击继续。
- 检查设置,然后单击“分配”。
- 单击完成。
三、创建付款请求
付款请求是PKPaymentRequest
类的实例。
付款请求包含一系列摘要项,这些摘要项向用户描述要付款的内容、可用的送货方式列表、用户需要提供的送货信息的描述以及有关商家和付款处理商的信息。
1、决定用户是否可以付款
在创建付款请求之前,请通过调用PKPaymentAuthorizationViewController
类的canMakePaymentsUsingNetworks:
方法 确定用户是否能够使用您支持的网络进行付款。
要检查此设备的硬件和家长控制是否支持 Apple Pay,请使用canMakePayments
方法。
笔记
PKPaymentAuthorizationController
类执行与PKPaymentAuthorizationViewController
类相同的角色,但它不依赖于 UIKit 框架。
这意味着授权控制器可以在视图控制器无法使用的地方使用(例如,在 watchOS 应用程序中或在 Intents 扩展中)。
如果canMakePayments
返回NO
,则表示设备不支持 Apple Pay。
不显示 Apple Pay 按钮。而是转而使用其他付款方式。
如果canMakePayments
返回YES
但canMakePaymentsUsingNetworks:
返回NO
,则表示设备支持 Apple Pay,但用户尚未为任何请求的网络添加卡。
您可以选择显示付款设置按钮,提示用户设置他们的卡。
一旦用户点击此按钮,就会启动设置新卡的过程(例如,通过调用方法openPaymentSetup
)。
否则,一旦用户按下 Apple Pay 按钮,您就必须开始付款授权流程。
在显示付款请求之前,不要要求用户执行任何其他任务。
例如,如果用户需要输入折扣代码,您必须在他们按下 Apple Pay 按钮之前要求输入该代码。
要在 iOS 8.3 或更高版本上创建用于发起付款请求的 Apple Pay 品牌按钮,请使用PKPaymentButton
类。
有关使用 Apple Pay 按钮和付款标记的更多指南,请参阅iOS 人机界面指南 中的 Apple Pay。
2、从基于 Web 的界面进行桥接
如果您的应用使用基于 Web 的界面购买商品和服务,则您必须先将请求从 Web 界面移至原生 iOS 代码,然后再处理 Apple Pay 交易。
例 3-1显示了处理来自 Web 视图的请求所需的步骤。
例 3-1从网页视图购买物品
// 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);
}
3、付款请求包括货币和地区信息
付款请求中的所有汇总金额都使用相同的货币,该货币使用PKPaymentRequest
的 currencyCode
属性指定。
请使用三字符 ISO 货币代码,例如USD
。
付款请求的国家代码表示购买发生地或购买处理地。
请使用两个字符的 ISO 国家代码,例如US
。
您在付款请求中设置的商家 ID 必须与您应用权利中的某个商家 ID 相匹配。
request.currencyCode = @"USD";
request.countryCode = @"US";
request.merchantIdentifier = @"merchant.com.example";
4、付款请求包含付款摘要项目列表
付款摘要项(由PKPaymentSummaryItem
类表示)向用户描述付款请求的不同部分。
使用少量摘要项 - 通常是小计、任何折扣、运费、税费和总计。
如果您没有任何额外费用(例如运费或税费),则只需使用购买总额。
在应用的其他地方提供每件商品费用的详细信息。
每个汇总项都有一个标签和一个金额,如例 3-2 所示。
标签是项目汇总内容的用户可读描述。
金额是相应的付款金额。
付款请求中的所有金额都使用付款请求中指定的货币。
对于折扣或优惠券,请将金额设置为负数。
例 3-2 创建付款摘要项
// 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
类 将金额存储为十进制数量。
可以通过明确指定尾数和指数(如代码例所示)或通过提供字符串数量并指定语言环境来创建此类的实例。
始终使用十进制数字进行财务计算 - 例如,确定 5% 折扣的金额。
尽管看起来更方便,但 IEEE 浮点数据类型(例如float
和 )Double
并不适合财务计算。
这些数据类型使用以 2 为基数的数字表示,这意味着某些十进制数无法准确表示 - 例如,0.42 必须近似为 0.41999 循环。
这种近似值可能会导致财务计算返回不正确的结果。
列表中的最后一个付款摘要项是总计。
通过将所有其他摘要项的金额相加来计算总计金额。
总计的显示方式与其他摘要项不同:使用您公司的名称作为其标签,并使用所有其他摘要项金额的总和作为其金额。
使用属性将付款摘要项添加到付款请求中paymentSummaryItems
。
如果您不知道授权付款时的实际费用(例如出租车费),请使用PKPaymentSummaryItemTypePending
类型 和0.0
金额制作小计汇总项。
对于总计,请使用正非零金额和PKPaymentSummaryItemTypePending
类型。
然后系统会将费用显示为待处理,不显示数字金额。
笔记:总计不能为零或负值。
// 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;
5、Shipping 方式是特殊的付款摘要项目
为每种可用的送货方式创建一个PKShippingMethod
实例。
与其他付款摘要项一样,送货方式也有一个用户可读的标签,例如“标准送货”或“次日送货”,以及一个运费金额。
与其他摘要项不同的是,送货方式还具有一个detail
属性,例如“7 月 29 日前送达”或“24 小时内发货”,用于说明送货方式之间的差异。
要在代理方法中区分送货方式,请使用identifier
属性。
此属性仅由您的应用使用 - 框架将其视为不透明值,并且不会显示在 UI 中。
在创建送货方式时,请为其分配一个唯一标识符。
为便于调试,请使用简短或缩写的字符串,例如“discount”、“standard”或“next-day”。
有些送货方式并非在所有地区都可用,或者不同地址的费用也不同。
您可以在用户选择送货地址或送货方式时更新此信息,如Your Delegate Updates Shipping Methods and Costs 中所述。
6、表明您支持的付款处理机制
通过使用字符串常量数组 填充supportedNetworks
属性来指示您支持哪些支付网络。
通过设置merchantCapabilities
属性的值,来指示您支持哪些支付处理协议。
您必须支持 3DS;仅当您在中国支持 Apple Pay 时才指定 EMV。
商家功能是位掩码,组合如下:
request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkDiscover, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa];
// Supports 3DS only
request.merchantCapabilities = PKMerchantCapability3DS;
// Supports both 3DS and EMV (add EMV only if you support Apple Pay in China)
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
7、说明需要哪些运输和账单信息
填充付款授权视图控制器的requiredBillingAddressFields
和requiredShippingAddressFields
属性,以指示需要哪些帐单和送货信息。
当您显示此视图控制器时,它会提示用户提供所请求的帐单和送货信息。
字段常量组合如下,以设置这些属性的值:
request.requiredBillingAddressFields = PKAddressFieldEmail;
request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
笔记
仅请求处理付款和交付产品或服务所需的账单和送货信息。
请求不必要的信息会增加交易的复杂性。
每增加一个步骤,用户取消付款的可能性就会增加。
如果您有最新的账单和送货联系信息,您可以在付款请求中设置这些信息。
Apple Pay 默认使用此信息;但是,用户仍然可以在付款授权过程中选择其他联系信息。
PKContact *contact = [[PKContact alloc] init];
NSPersonNameComponents *name = [[NSPersonNameComponents alloc] init];
name.givenName = @"John";
name.familyName = @"Appleseed";
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 中,地址信息可以来自各种来源。使用前请务必验证信息。
8、存储其他信息
要存储特定于您的应用的付款请求信息(例如购物车标识符),请使用applicationData
属性。
系统将此属性视为不透明值。
用户授权付款请求后,应用程序数据的哈希值将显示在付款令牌中。
四、授权付款
付款授权流程是付款授权视图控制器与其代理之间的协作。
付款授权视图控制器执行两项操作:它让用户选择付款请求所需的账单和送货信息,并让用户授权进行付款。
当用户与视图控制器交互时,将调用代理方法,以便您的应用可以更新显示的信息 - 例如,在选择送货地址时更新运费。
用户授权付款请求后,也会调用代理。
笔记:当您实现代理方法时,请记住它们可以被调用多次,并且调用的顺序取决于用户操作的顺序。
在授权过程中调用的所有代理方法都会传递一个完成块作为其参数之一。
付款授权视图控制器会等待其代理完成对一个方法的响应(通过调用完成块),然后再调用任何其他代理方法。
paymentAuthorizationViewControllerDidFinish:
方法是唯一的例外:它不需要完成块,并且可以随时调用。
完成块接受一个参数,该参数允许您根据可用信息指定事务的当前状态。
如果事务没有问题,则传递值PKPaymentAuthorizationStatusSuccess
;否则,传递一个标识问题的值。
要创建PKPaymentAuthorizationViewController
类的实例,请将付款请求传递给视图控制器的初始化程序。
为视图控制器设置一个代理,然后呈现它。
笔记
PKPaymentAuthorizationController
类执行与PKPaymentAuthorizationViewController
类相同的角色,但它不依赖于 UIKit 框架。
这意味着授权控制器可以在视图控制器无法使用的地方使用(例如,在 watchOS 应用程序中或在 Intents 扩展中)。
要使用PKPaymentAuthorizationController
类,请分配一个采用 PKPaymentAuthorizationControllerDelegate
协议的对象作为代理,并调用presentWithCompletion:
方法以显示付款表。
完成后,调用dismissWithCompletion:
方法以关闭付款表。
否则,API 是相同的。
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 沙盒环境在带有测试卡的设备上测试您的应用。
尽管这些技术提供了快速、便捷的方法来测试您的代码,但您仍然需要在实际的物理设备上使用生产卡彻底测试 Apple Pay。
您的代理更新运输方式和费用
当用户提供送货信息时,授权视图控制器会调用您的代理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:
中提供的送货信息是匿名的。
返回的联系人包含足够的信息来计算运费,而不会泄露有关用户的敏感信息。
直到用户批准付款后,您才能获得用户的完整送货信息。
此外,联系人中可用的数据因国家/地区而异,并且可能因版本而异。
请务必对您的应用进行适当的测试。
当付款获得授权时,将创建付款令牌
当用户授权付款请求时,框架会与 Apple 的服务器和安全元件协作,创建一个付款令牌。
您可以在paymentAuthorizationViewController:didAuthorizePayment:completion:
代理方法中,将此付款令牌以及处理购买所需的任何其他信息(例如送货地址和购物车标识符)发送到您的服务器。
该过程如下:
- 框架将支付请求发送到安全元件。
只有安全元件才能访问标记化的特定设备支付卡号。 - 安全元件会汇总指定卡和商户的支付数据,对其进行加密,以便只有 Apple 可以读取,然后将其发送给框架。
框架随后会将支付数据发送给 Apple 的服务器。 - Apple 的服务器使用您的付款处理证书重新加密付款数据。
只有您和与您共享付款处理证书的人才能读取该令牌。
然后,服务器签署付款令牌,并将其返回给设备。 - 框架通过调用
paymentAuthorizationViewController:didAuthorizePayment:completion:
方法将令牌传递给您的代理。
您的代理将令牌发送到您的服务器。
服务器上的操作取决于您是自行处理付款还是使用付款平台。
在这两种情况下,您的服务器都会处理订单并将状态发送回设备,然后您的代理会将该状态传递给其完成处理程序,如处理付款 中所述。
- (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);
}
您的代理解散了付款授权视图控制器
框架显示交易状态后,授权视图控制器将调用您的代理的 paymentAuthorizationViewControllerDidFinish:
方法。
在您的实现中,关闭授权视图控制器,然后显示您自己的特定于应用程序的订单确认页面。
- (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
五、处理付款
处理付款涉及几个步骤:
- 将付款信息以及处理订单所需的其他信息发送到您的服务器
- 验证支付数据的哈希值和签名
- 解密加密的支付数据
- 将支付数据提交至支付处理网络
- 将订单提交至订单跟踪系统
您有两种处理付款的方式:您可以利用支付平台来处理付款,也可以自己处理付款。
支付处理平台通常会处理上面列出的大部分步骤。
读取、验证和处理付款信息需要了解加密的几个领域,例如计算 SHA-1 哈希、读取和验证 PKCS #7 签名以及执行椭圆曲线 Diffie-Hellman 密钥交换。
如果您没有加密背景,请考虑使用为您执行这些操作的支付平台。
有关支持 Apple Pay 的支付平台的信息,请参阅 developer.apple.com/apple-pay/ 。
用于处理付款的信息具有嵌套数据结构,如图5-1所示。
付款令牌是该类的一个PKPaymentToken
实例。
其属性的值paymentData
是一个 JSON 字典,该字典具有用于验证的信息的标头和加密的付款数据。
加密数据包括金额和持卡人姓名等信息以及用于特定付款处理协议的其他信息。
图5-1支付数据结构
有关支付数据结构格式的详细信息,请参阅*支付令牌格式参考*。
2024-06-14(五)