关于 Apple Pay
apple pay 是一种移动支付技术,它可以让用户将自己在现实生活中购物、享受服务的支付信息提供给我们,它是一种方便而且安全的支付方式。
使用 Apple Pay 的 APP 需要在 Xcode 中打开 Apple Pay 功能,同时需要注册一个商户ID,并设置好加密密钥,这个密钥是用来将支付数据安全地发送到我们自己的服务器。
在开始一次支付时,APP 需要创建一个支付请求。这个支付请求不仅包含了已购买的服务或者商品的一些合计,同时也包含了税金、配送费或者是折扣等一些附加费用的信息。将这个请求传递给一个payment authorization view controller ,这个控制器将支付请求的信息显示出来,并提示用户一些必要信息,例如配送地址或者账单寄送地址。在用户与这个view controller 交互时,会调用delegate来更新支付请求。
一旦用户对这次支付授权完成,Apple Pay 会将支付信息加密,以防止未授权的第三方来访问这些信息。在用户的设备上,Apple Pay 发送了一个支付请求到 Secure Element,SE 是在设备上专门保证安全的一块芯片。SE 会将用户指定银行卡信息和商户信息添加到支付请求中去,并且创建一个加密的支付 token 。然后将这个 token 发送到苹果服务器,苹果服务器会使用我们提供的商户ID证书将支付请求再次加密。最后服务器会将这个请求返回到应用中来进行后续的操作。苹果服务器不会访问或者保存支付 token,服务器只是简单的将 token 用开发者的证书再次加密。
大部分情况下,应用将加密过的支付 token 发送到第三方支付平台解密并完成支付。也可以在自己的服务器上完成界面和支付的过程。
配置支付环境
首先我们需要一个 Merchant ID,这样 Apple Pay 才能够识别出接收支付的对象。公钥和证书会跟我们的 Merchant ID 关联,在加密支付信息操作时会用到它们。使用 Apple Pay 之前要先注册一个 Merchant ID 并且配置好证书。
http://developer.apple.com/account 在这里注册 Merchant ID 和配置证书。
然后在工程的 Capabilities 窗口中将 Apple Pay 打开,选中想要该应用使用的 Merchant ID。
创建支付请求
支付请求是 PKPaymentRequest 类的一个实例。一个支付请求由以下几部分组成:一个为用户描述支付项目的摘要列表、一个可用的配送信息列表、一段提示用户提供配送信息的文案,以及一些商户信息和支付平台的信息。
判断用户是否可用 Apple Pay 支付
在创建支付请求之前,要先调用 PKPaymentAuthorizationViewController 类中的 canMakePaymentUsingNetworks: 方法来判定用户能不能用我们提供的支付系统来进行支付。如果要检查用户的设备硬件是否支持 Apple Pay ,或者检查是否因“家长控制”功能开启而导致不能支付的情况,那么要调用 canMakePayments 方法。
如果canMakePayments返回了NO,说明这个设备不支持 Apple Pay ,这时候就不应该显示 Apple Pay 按钮,而是通过其他支付方式来支付。
如果 canMakePayments 返回了 YES 但是 canMakePaymentUsingNetworks: 返回NO时,说明设备支持 Apple Pay ,但是用户没有为任何支付网络系统添加银行卡。此时我们可以视情况来显示一个支付设置按钮来提示用户设置新的银行卡,用户点击设置时需要调用 openPaymentSetup 方法。
支付请求要包含货币以及区域信息
在一个支付请求中所有项目的金额必须使用同一种货币,货币种类要在 PKPaymentRequest 的 currencyCode 属性中指定。使用 ISO 统一的三字符编码来规定指定的货币种类,例如 USD ,CNY。请求当中的国家编码表明了此次购买的位置处于哪个国家,或者处理购买支付的国家。使用 ISO 两字符编码来表示国家,例如US、CN。
请求中指定的Merchant ID 必须是包含在应用的 Merchant ID 列表当中。
支付请求包含一个支付摘要项目的列表
支付摘要项目是用 PKPaymentSummaryItem 类的对象表示的,每一项都描述了支付请求的不同部分。要尽量控制摘要项目的数量,比如费用小计、折扣金额、配送信息、税金、总计金额等。如果没有这些额外费用的话,就直接显示总金额。不要把这些摘要子项的内容一个一个在支付请求中列出来,应该在别的地方显示给用户。
每一个摘要项目都含有一个标签和一个金额,所有的金额都是表示支付请求当中指定货币类型的金额,如果是支付请求中包含折扣或者优惠券,那么要用负数来表示金额。表示金额的时候要使用 NSDecimalNumber 这个类,因为 NSDecimalNumber 使用的是以10为底数,指定尾数和指数的方式来表示数值,财务运算中不适合使用浮点型的数来表示数额,因为有些十进制的数额不能用浮点型精确的表示出来,例如0.42可能会被以0.419999...的循环小数来表示,这就会造成计算错误。
通过支付请求的 paymentSummaryItems 属性来向该请求的摘要项目列表中添加支付摘要项目。支付摘要项目列表是一个数组,它的最后一项用来表示这个支付请求的总金额,总金额是需要我们自己计算的,通过把各个摘要项目相加而得到总金额。总金额的显示方式会跟其他摘要项目有一些差别,这里需要使用公司的名称作为总金额的标题。
配送方式是一种特殊的摘要项目
每一种可用的配送方式都需要创建一个 PKShippingMethod 实例。它同其它的摘要项目一样具有标签和金额,标签是易于用户识别不同配送方式的文案,例如“次日送达”或者“标准配送”,金额表示不同配送方式的费用。PKShippingMethod 对象有两个属性,identifier 是配送方式的唯一标识符,detail 是各个配送方式的描述。
指明应用所支持的支付处理机制
通过字符串常量数组扩充 supportedNetworks 属性来指明我们的应用所支持的支付网络系统。通过设置 merchantCapabilities 属性的值来指明我们所支持的支付处理协议。必须支持 3DS (Three Domain Secure)协议,对于 EMV 的支持是可选的。
额外信息的储存
使用 applicationData 属性在应用中储存关于本次支付的一些标识信息,例如购物车的唯一标识符。这个值对于系统来说是不可见的,在用户授权支付以后,这个属性会以一个 hash 值的形式出现在支付请求的 token 当中。
支付的授权
授权支付的处理是由支付授权控制器和它的 delegate 共同协作完成的。支付授权控制器处理两件事:它让用户选择支付请求中所必须的账单和配送信息,并让用户对这次支付进行授权。当用户与支付授权控制器交互时,delegate的方法就会被调用,以便应用能够更新信息的显示。并且在用户授权支付之后 delegate 方法也会被调用。这些 delegate 方法可能会被多次调用,调用的顺序取决于用户的操作顺序。
在授权支付的过程中,所有的 delegate 方法在被调用时都会传入一个 completion block 参数,支付授权控制器会等待它的 delegate 响应完前一个 delegate 方法之后才回去调用另一个 delegate 方法,delegate 通过执行参数中的 block 来完成响应。
在 completion block 中携带的参数可以让我们根据可用的信息来指定当前的支付状态。如果当前的交易状态没有问题,那么就传入 PKPaymentAuthorizationStatusSuccess 参数,否则需要传入一个能识别问题的参数。
在创建 PKPaymentAuthorizationViewController 实例时,需要在初始化传入支付请求。随后为这个 view controller 设置 delegate 并将这个 view controller 显示出来。用户与这个view controller 交互时会回调 delegate 方法。
通过 paymentAuthorizationViewController:didSelectShippingContact:completion: 方法和paymentAuthorizationViewController:didSelectShippingMethod:completion: 方法来回调 delegate 更新配送联系信息和配送方式,我们需要在delegate 中更新配送费用。
当支付请求被授权时支付token会被创建
用户授权了支付请求后,PK 框架通过跟苹果服务器和 Secure Element 的协作来创建支付 token 。我们需要在 paymentAuthorizationViewController:didAuthorizePayment:completion: 这个 delegate 方法中,把支付 token 连同我们需要的所有与交易相关的信息发送到自己的服务器上,例如购物车唯一标识符或者配送地址等等信息。这个过程大致如下:
- PK 框架把支付请求发送到 Secure Element 。只有 Secure Element才可以访问到用户设备上已经被符号化的支付卡卡号。
- Secure Element 把指定的卡信息和商户等等支付数据加密成苹果能读取的形式,然后将加密后的数据发送到框架中,框架再把加密过的数据发送到苹果的服务器。
- 苹果的服务器会使用开发者提供的商户ID证书将这些支付数据再次加密。再次加密完成后,苹果服务器会对支付数据签名并生成 token 发送到用户的设备上,只有拥有商户ID证书的人才能读取加密过的 token 。
- PK 框架会把这个 token 通过调用 paymentAuthorizationViewController:didAuthorizePayment:completion: 方法来回调 delegate ,我们拿到 token 后要发送到自己的服务器。
在我们自己的服务器上的操作是根据不同情况来决定的,我们可以自己处理支付或者使用其他的支付平台来处理支付。不论是哪一种情况,我们自己的服务器都要处理支付的订单并将支付状态返回到设备上,在 paymentAuthorizationViewController:didAuthorizePayment:completion: 方法中处理支付状态,并在 completion block 中将状态传递给支付授权控制器。
授权结束后释放支付授权控制器
在 PassKit 框架显示交易状态之后,支付授权控制器会调用 delegate 的方法 paymentAuthorizationViewControllerDidFinish: ,在这个 delegate 方法的实现中,delegate 要将支付授权控制器关闭,然后再显示应用自己的订单支付确认页面。
支付处理
处理支付包含下列几个步骤:
- 把支付信息和订单所需要的其他信息一起发送到我们自己的服务器
- 验证支付数据当中的哈希表和签名
- 将加密的支付数据解密出来
- 向支付处理系统提交支付数据
- 向我们自己的订单系统提交订单
我们有两种方式来处理支付:我们可以利用支付平台来处理支付,或者我们自己实现支付处理过程。一个支付处理平台基本上就能完成上述的大部分操作。
要读取、验证和处理支付信息,就必须知道一部分密码学知识,比如计算SHA—1哈希表,读取和验证 PKCS#7 签名和Diffie-Hellman 椭圆曲线密钥交换。如果我们不具有这些密码学的知识背景,那么可以考虑使用第三方支付平台来完成这些操作。
用来处理支付的信息是一种嵌套式的数据结构,支付 token 是一个 PKPaymentToken 类的实例。它的 paymentData 属性的值是一个 JSON 字典。这个 JSON 字典的 Header 包含用来验证支付信息和加密支付数据。加密的支付信息包含诸如订单总额、持卡人姓名和用来指定支付处理协议的其他信息。
相关知识文档:
Apple Pay Programming Guide
WWDC 2015 Apple Pay Within Apps
Getting Started With Apple Pay
PKPaymentAuthorizationViewController Class Reference
iOS Security Guide