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

你可能感兴趣的:(iOS)