之前把Apple Pay的文档全部翻译了一遍,最近也是接触到了真实的项目开发。发现开发的过程还是和官方文档中的介绍有偏差,所以我会先将官方文档中的步骤有疑惑的地方记录下来,然后介绍在实际开发中的不同点。
1 Apple Pay 官方翻译文档
2 Apple Pay开发流程
3 Apple Pay实际开发
1 Apple Pay 官方翻译文档
官方与Apple Pay相关的文档主要包括 :
Apple Pay Programming Guide(官方编程指南):详细的讲解了在应用中的Apple Pay开发流程以及一定的解释
Apple Pay Programming Guide
Apple Pay官方编程指南(中文翻译)
PassKit (Apple Pay的支付框架):介绍了Apple Pay开发中所需要的类以及方法(已经有iOS 11的beta类和方法)
2 Apple Pay开发流程
任何功能的开发都是需要在iTunes Connect中申请相关证书并且在Xcode 中将项目的Capabilities选项开启。以下是在代码开发的过程中的流程以及出现一些疑惑点:
步骤一 判断用户是否能使用Apple Pay支付
首先需要理解的是,用户不能使用Apple Pay支付有各种各种的原因,但是苹果为我们找到两类可控的原因:
1.设备是否支持,是否开启家长控制
[PKPaymentAuthorizationController canMakePayments];
[PKPaymentAuthorizationViewController canMakePayments];
2.是否在该网络下配置可供支付的卡
[PKPaymentAuthorizationController canMakePaymentsUsingNetworks:@[PKPaymentNetworkVisa,PKPaymentNetworkChinaUnionPay,PKPaymentNetworkQuicPay]];
[PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:@[PKPaymentNetworkVisa,PKPaymentNetworkChinaUnionPay,PKPaymentNetworkQuicPay]];
直观的看到上面的两个代码会出现三个疑惑点:
① PKPaymentAuthorizationController 和PKPaymentAuthorizationViewController 分别用在什么地方?
__WATCHOS_AVAILABLE(3.0) __IOS_AVAILABLE(10.0)
@interface PKPaymentAuthorizationController : NSObject
NS_CLASS_AVAILABLE_IOS(8_0)
@interface PKPaymentAuthorizationViewController : UIViewController
PKPaymentAuthorizationViewController和PKPaymentAuthorizationController扮演着相同的角色,而PKPaymentAuthorizationController并不依赖于UIKit框架。这就意味着AuthorizationController能用在视图控制器无法使用的地方(在watchOS或者 SiriKit extensions中)
② Apple Pay支持的Network包括哪些?
typedef NSString * PKPaymentNetwork NS_EXTENSIBLE_STRING_ENUM;
// American Express. 美国运通(外资信用卡)
PKPaymentNetwork const PKPaymentNetworkAmex
//China Union Pay 中国银联
PKPaymentNetwork const PKPaymentNetworkChinaUnionPay
// Discover 发现卡(美国泛使)
PKPaymentNetwork const PKPaymentNetworkDiscover
//加拿大Interac银行卡
PKPaymentNetwork const PKPaymentNetworkInterac
// MasterCard 万事达
PKPaymentNetwork const PKPaymentNetworkMasterCard
// Store credit and debit cards.信用卡和借记卡
PKPaymentNetwork const PKPaymentNetworkPrivateLabel
// Visa.支付卡(全球)
PKPaymentNetwork const PKPaymentNetworkVisa
//Japan Credit Bureau( 日本信用局)
PKPaymentNetwork const PKPaymentNetworkJCB
// Suica 日本交通卡
PKPaymentNetwork const PKPaymentNetworkSuica
//信用卡(日本)
PKPaymentNetwork const PKPaymentNetworkQuicPay
(如果有人知道这两个的意思,可以告诉我)
PKPaymentNetwork const PKPaymentNetworkIDCredit
PKPaymentNetwork const PKPaymentNetworkCarteBancaire
③判断的顺序
对于应用而言,第一当然是判断当前设备是否支持,如果支持再判断在应用支持的网络中是否配置了相应的支付卡
if ([ApplePayManager isApplePaySupport]) {
if ([ApplePayManager isContainApplePayCard]) {
NSLog(@"设备支持并且配置想要卡片");
} else {
NSLog(@"设备支持但在支持的网络下未配置卡片");
}
} else {
NSLog(@"设备不支持或者家长控制");
}
步骤二 根据判断结果显示不同Apple Pay按钮
对于设备硬件不支持或者家长控制的条件下,苹果官方推荐的做法是 ----- 不显示任何的按钮
而对于设备支持的条件下,苹果要求通过PKPaymentButton类进行设置.
NS_CLASS_AVAILABLE_IOS(8_3) @interface PKPaymentButton : UIButton
+ (instancetype)buttonWithType:(PKPaymentButtonType)buttonType style:(PKPaymentButtonStyle)buttonStyle;
- (instancetype)initWithPaymentButtonType:(PKPaymentButtonType)type paymentButtonStyle:(PKPaymentButtonStyle)style NS_AVAILABLE_IOS(9_0) NS_DESIGNATED_INITIALIZER;
@end
PKPaymentButton支付按钮由两个属性:
PKPaymentButtonType 按钮类型
PKPaymentButtonStyle 按钮风格
步骤三 点击按钮进行不同的处理
对于在未提供支持网络下的卡片时,苹果推荐进入wallet中设置并且会自动检测是否登录iCloud账号。
PKPassLibrary *library = [[PKPassLibrary alloc] init];
[library openPaymentSetup];
在这一步中会出现一个疑惑点:
很多人知道在iTunes connect中添加沙盒测试账号,但是如何添加一张可使用的支付卡?
① 区域设置
参考官方文档,会发现能进行沙盒测试的区域并不包括中国。所以如果设备的地区设置为中国,设置中根本没有wallet&Apple Pay一栏:
并且打开钱包应用,发现也无法设置Apple Pay:
所以,测试的第一步,设置为苹果支持测试的区域。
① 添加测试卡
设置好区域后,wallet&Apple Pay一栏会出现在设置界面中:
点击之后添加苹果推荐的测试卡号。但是我测试了好几十个都会出现以下的问题:
去stack overflow上搜索答案的时候,发现有很多的人在不同区域都出现这样的问题。有些答案说要确认是测试账号或者重新登录再退出,但是试过都没有起作用,下面的答案可能有点解惑:
stack overflow解答
要使用真实的卡。苹果推荐不要使用开发者个人卡并且Apple Pay不支持国内的发卡行,所以使用该方法测试Apple Pay并不行的通。而且实际的开发中也并不会这么使用,具体的做法我会在后面说明。
对于在支持网络下已配置卡时,便能直接发送请求
PKPaymentRequest类主要的属性:
@interface PKPaymentRequest : NSObject
// 该平台支持的网络
+ (NSArray<PKPaymentNetwork> *)availableNetworks;
//开发者申请的商业标识符
@property (nonatomic, copy) NSString *merchantIdentifier;
// 两位的ISO城市代码
@property (nonatomic, copy) NSString *countryCode;
// 商家支持的网络
@property (nonatomic, copy) NSArray<PKPaymentNetwork> *supportedNetworks;
// 商家支持的支持处理网络
@property (nonatomic, assign) PKMerchantCapability merchantCapabilities;
//PKPaymentSummaryItem对象显示给用户,最后一项应该是总额
@property (nonatomic, copy) NSArray<PKPaymentSummaryItem *> *paymentSummaryItems;
//三位的ISO货币代码
@property (nonatomic, copy) NSString *currencyCode;
//账单地址,默认为PKAddressFieldNone.
@property (nonatomic, assign) PKAddressField requiredBillingAddressFields;
@property (nonatomic, strong, nullable) PKContact *billingContact;
//运输定制,默认为PKAddressFieldNone.
@property (nonatomic, assign) PKAddressField requiredShippingAddressFields;
@property (nonatomic, strong, nullable) PKContact *shippingContact
//商家支持的运输方法
@property (nonatomic, copy, nullable) NSArray<PKShippingMethod *> *shippingMethods;
//运输的显示模式,默认为PKShippingTypeShipping
@property (nonatomic, assign) PKShippingType shippingType
①举例而言,当用户想要购买一件300块打折50元的衬衫时:
//shirt 300元 折扣为50元
NSDecimalNumber *shirt = [NSDecimalNumber decimalNumberWithMantissa:300 exponent:0 isNegative:NO];
NSDecimalNumber *discount = [NSDecimalNumber decimalNumberWithMantissa:50 exponent:0 isNegative:YES];
NSDecimalNumber *total = [shirt decimalNumberByAdding:discount];
PKPaymentSummaryItem *shirtItem = [PKPaymentSummaryItem summaryItemWithLabel:@"shirt" amount:shirt];
PKPaymentSummaryItem *shirtDiscount = [PKPaymentSummaryItem summaryItemWithLabel:@"discount" amount:discount];
PKPaymentSummaryItem *totalItem = [PKPaymentSummaryItem summaryItemWithLabel:@"李周" amount:total type:PKPaymentSummaryItemTypeFinal];
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.paymentSummaryItems = @[shirtItem,shirtDiscount,totalItem];
request.supportedNetworks = @[PKPaymentNetworkVisa,PKPaymentNetworkChinaUnionPay,PKPaymentNetworkQuicPay];
request.currencyCode = @"CNY";
request.countryCode = @"CN";
request.merchantIdentifier = @"merchant.com.lizhou.Payment";
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
上面的例子很容易理解,但是一眼看过去对merchantCapabilities属性有疑惑点:该值设置的到底是什么?
该属性设置的是交易处理协议,苹果强调的是你必须支持3-D安全协议;可选是否支持EMV协议(国内要选择支持)。
// 3-D Secure protocol. (国外安全支付模式)Visa的
PKMerchantCapability3DS
//EMV protocol.协议(EMV标准,国际三大银行卡组织同时发起制定的银行卡从磁条卡向智能IC卡转移的技术标准)
PKMerchantCapabilityEMV
// Support for credit cards.支持信用卡
PKMerchantCapabilityCredit
// Support for debit cards.支持借记卡
PKMerchantCapabilityDebit
②添加运输的信息
如果商家要求设置运输信息,包括姓名、联系电话、地址:
request.requiredShippingAddressFields = PKAddressFieldPostalAddress | PKAddressFieldPhone | PKAddressFieldName;
如果用户并没有设置过运输要求信息,则会出现以下界面:
当然,苹果推荐在出现支付界面前设置好一切信息,不要让用户做一些别的事情,以免取消支付。
设置商家支持的运输方式:
request.requiredShippingAddressFields = PKAddressFieldPostalAddress | PKAddressFieldPhone | PKAddressFieldName;
PKShippingMethod *freeMethod = [PKShippingMethod summaryItemWithLabel:@"包邮" amount:[NSDecimalNumber zero]];
freeMethod.identifier = @"Free";
freeMethod.detail = @"最慢的咯";
PKShippingMethod *YunMethod = [PKShippingMethod summaryItemWithLabel:@"韵达快递" amount:[NSDecimalNumber decimalNumberWithMantissa:10 exponent:0 isNegative:NO]];
YunMethod.detail = @"2-3天哦";
YunMethod.identifier = @"YunDa";
request.shippingMethods = @[freeMethod,YunMethod];
最后介绍在弹出的支付弹窗中的一些操作回调方法:
//在支付验证前,但是在用户输入密码或者Touch ID前
- (void)paymentAuthorizationViewControllerWillAuthorizePayment:(PKPaymentAuthorizationViewController *)controller
//选择送货地址回调
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingMethod:(PKShippingMethod *)shippingMethod
completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray<PKPaymentSummaryItem *> *summaryItems))completion;
//选择送货方式回调
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingContact:(PKContact *)contact
completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray<PKShippingMethod *> *shippingMethods,
NSArray<PKPaymentSummaryItem *> *summaryItems))completion
//选择支付卡回调
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectPaymentMethod:(PKPaymentMethod *)paymentMethod
completion:(void (^)(NSArray<PKPaymentSummaryItem *> *summaryItems))completion
//付款成功,将数据发送给你的服务器
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus))completion
//交易结束
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller;
3 Apple Pay实际开发
其实看了上面官网的文档之后,我一直很好奇的是:
钱到底是怎么没的?不需要和服务器进行沟通吗?
后来才知道,Apple Pay只是一种支付的工具,并不参与到支付的流程中,也就是不会收取任何的费用。所以如果在国内需要使用银行卡进行Apple Pay的支付,可以去银联的官方进行产品签约,收取的手续费用当然是依据你们公司与银联的沟通情况。
并且银联SDK内部直接对Apple Pay进行封装了,所以整个流程就是:
①应用内 ->订单相关数据 ->服务器 ->封装成订单 ->银联
②银联记录提供的订单并生成标识订单的TN号返回给服务器,发送给应用。
③应用调用银联SDK中的方法将TN号回传给银联,银联会将结果信息回传
所以:
1 在银联的SDK方法中很容易发现:如何在测试环境和生成环境中切换
2 服务器向银联进行下单返回TN的时候还未进行支付,直到应用调用SDK中的方法才进行真正的支付
3 银联对Apple pay支付进行了很强的封装,所以直接调用银联的返回结果状态进行判断即可
其实之前老说Apple Pay、Apple Pay的,但是现在才能很明白其中的机制,才知道苹果提供的只是支付工具。所以在实际开发的时候一定要和相应的机构签约,而不是去想:Apple pay本身到底如何ba