前言
美国时间于2014年10月20日苹果公司正式推出Applepay支付功能,直到2016年2月18日凌晨5:00, Apple Pay 才正式登陆中国.也就是说,在国内想要使用Applepay支付功能必须要满足几个条件:1,设备的支付环境必须是要iPhone6以上的设备,并且系统要是iOS9以后才能使用银联卡,在国内只有少数的APP才有ApplePay支付功能
点击了解更多ApplePay详情!
1, 简单介绍ApplePay
Applepay是苹果公司在2014年秋季新品发布会上发布的一种基于NFC的手机支付功能.可以通过TouchID或者Passcode两种方式进行支付.用户可使用存储在iPhone 6, 6p等设备上的信用卡和借记卡支付证书来授权支付,也就是说,该功能只能在iphone 6以上的设备才能使用.
ApplePay除了可以线下支付还能线下支付,不需要网络就可以支付,而且更加安全(这是与国内最流行的微信支付和支付宝支付的区别之一).
关于ApplePay的安全性以及如何使用ApplePay可以去官网上的文档查询(前言中已给出链接).
ApplePay是在2016年2月18日正式登陆中国市场,所以,目前支持使用ApplePay功能的App还不是很多,但是我相信以后一定会和微信支付和支付宝支付一样普及的,因为它更加方便,更加安全(鉴于库克目前正在和美国FBI对战中,说明Apple公司很注重用户的隐私,尽管iphone还有很多bugo)...
2, 集成ApplePay步骤
- 配置支付环境
- 创建Xcode项目,并设置好对应的BundleID(需要在开发者中心用到)
- 到苹果开发者中心注册并配置一个商业标识符(Merchant ID)
- 1, 登录您的Apple ID,然后到账号中,进入配置证书选项,添加Apple ID, 然后在下面勾选Apple Pay选项
- 2, 配置Merchant ID
- 3, 为Merchant ID配置证书,并且下载证书,双击点击安装到钥匙串中.
- 4, 检查安装到钥匙串中的证书是否已经过期了,如果过去了那么就重新下载证书.
- 5, 将配置的Merchant ID绑定到APP ID中
- 注意: 上面配置证书是需要付费的,所以说的比较简陋,等以后有了自己的账号,再附上配置Merchant ID图片.
3, 配置Xcode项目环境
- 1, 点击项目名称,然后点击Capablilties,将ApplePay功能打开,将配置的Merchant ID添加进去,如果下面的Steps全部是打钩,说明配置成功.
4, 代码实现
- 思路分析
/*
分析 : 使用Applepay的前提是必须要导入一个特有的框架PassKit,包含所有支付的方法和属性
思路 : 美国时间于2014年10月20日苹果公司正式推出Applepay支付功能,直到2016年2月18日凌晨5:00, Apple Pay 业务在中国才正式上线.也就是说,在内的想要使用Applepay支付功能必须要满足几个条件:1,设备的支付环境必须是要iPhone6以上的设备,并且系统要是iOS9以后才能使用银联卡,在国内只有少数的APP才有ApplePay支付功能,所以在正式购买商品之前,需要做几个判断.具体步骤如下:
步骤:
1, 首先需要判断当前环境是否满足ApplePay支付功能,如果不满足,那么隐藏支付按钮
2, 如果可以使用ApplePay支付功能,还需要判断是否添加了银行卡,如果没有添加银行卡,那么创建ApplePay特有的按钮,监听按钮的点击事件,当用户点击按钮之后,跳转到添加银行卡界面.
3, 如果前面两个条件都满足了,那么就可以直接购买商品了,所以创建支付特有的按钮,监听按钮的点击,当用户点击支付按钮后,购买商品,配置支付商品的相关信息.
*/
/**
* 导入头文件
*/
#import "ViewController.h"
#import
#import
@interface ViewController ()
/**
* 支付View
*/
@property (weak, nonatomic) IBOutlet UIView *ApplepayView;
@end
- 具体实现
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1,判断当前的环境是否能够使用Applepay支付功能
if (![PKPaymentAuthorizationViewController canMakePayments]) {
// 提示用户当前设备不支持ApplePay
[SVProgressHUD showErrorWithStatus:@"当前设备不支持ApplePay功能"];
// 隐藏支付按钮
self.ApplepayView.hidden = YES;
}else if (![PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard, PKPaymentNetworkAmex]])
{
// 来到这里表示当前的设备是可以使用ApplePay功能,但是没有绑定银行卡,无法完成支付,这里需要注意,ApplePay是在iOS9之后才在中国上线的,所以,需要是iOS9以上的设备才能支持银联卡.所以我们需要给wallet钱包添加银行卡.
// 提示用户需要绑定银行卡
[SVProgressHUD showErrorWithStatus:@"请添加绑定银行卡类型"];
/**
* 创建支付使用的按钮,两个参数都是枚举值,直接点进头文件即可,创建特定的支付按钮,有两个方法,一个对象
* 方法,一个类方法,这里使用类方法,快捷方便一点
*/
PKPaymentButton *payButton = [PKPaymentButton buttonWithType:PKPaymentButtonTypeSetUp style:PKPaymentButtonStyleWhiteOutline];
/**
* 监听按钮的点击,当用户点击按钮之后,直接跳转到添加银行卡界面
*/
[payButton addTarget:self action:@selector(jump2MakePaymentsUsingNetworks) forControlEvents:UIControlEventTouchUpInside];
/**
* 将创建的按钮添加到定义的View中
*/
[self.ApplepayView addSubview:payButton];
} else
{
/**
* 来到这里表示当前用户的设备支持ApplePay功能,并且在wallet钱包中已经绑定好了银行卡,直接点击购买按
* 钮即可.
*/
// 创建支付按钮
PKPaymentButton *payButton = [PKPaymentButton buttonWithType:PKPaymentButtonTypeBuy style:PKPaymentButtonStyleBlack];
/**
* 监听按钮的点击
*/
[payButton addTarget:self action:@selector(purchase) forControlEvents:UIControlEventTouchUpInside];
/**
* 添加支付按钮
*/
[self.ApplepayView addSubview:payButton];
}
}
注意 : PKPaymentNetworkChinaUnionPay银联卡的使用前提
事件的监听
- (void)jump2MakePaymentsUsingNetworks {
/**
* 跳转到添加银行卡界面,系统直接就给我们提供了一个方法,直接创建界面,然后open即可
*/
PKPassLibrary *library = [[PKPassLibrary alloc] init];
/**
* 跳转到绑定银行卡界面
*/
[library openPaymentSetup];
}
- 注意 : 添加银行卡界面是系统内部提供的,我们只需要创建,调用方法使用即可.
- 购买商品
- (void)purchase {
/**
* 来到这里,说明可以直接支付购买商品,但是在支付的之前,还需要创建一个支付请求,给它授权,然后配置一些必要信
* 息,必要信息如下
*/
/**
* 创建一个支付请求
*/
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
/**
* 配置商户ID:换句话说就是商店的标识,用于区分商店的ID
*/
request.merchantIdentifier = @"这个ID就是您在苹果开发者中心申请的商户ID,具体上面有介绍";
/**
* 当前用户所在的国家的国际编码:中国的国际编码是"CN"
*/
request.countryCode = @"CN";
/**
* 当前用户使用的货币编码 : 人民币的国际编码是"CNY"
*/
request.currencyCode = @"CNY";
/**
* 商家所支持的网络有哪些? 换句话说就是可以使用什么类型的卡支付,支持的网络返回的是一个数组
* 一定要注意,如果用户使用的是中国的银联卡,必须要求设备是6/6s以上,系统要保证在iOS9以上
* PKPaymentNetworkChinaUnionPay(中国银联卡)
*/
request.supportedNetworks = @[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard];
/**
* 关于支付后商家的处理方式,这里还有一个坑,那就是PKMerchantCapability3DS必须填写,如果还有其他的方式
* 可以使用'|'逻辑或.
*/
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
/**
* 是否显示账单上地址等信息,默认是不显示的,值是个枚举值,表示全部显示
*/
request.requiredBillingAddressFields = PKAddressFieldAll;
/**
* 是否显示快递单上地址等信息,默认是不显示的
*/
request.requiredShippingAddressFields = PKAddressFieldAll;
/**
* 配置用户要购买的商品列表,提供了两个类方法
*/
NSDecimalNumber *price1 = [NSDecimalNumber decimalNumberWithString:@"5999"]; // 价格
PKPaymentSummaryItem *item1 = [PKPaymentSummaryItem summaryItemWithLabel:@"iphone 6" amount:price1]; // 商品
NSDecimalNumber *price2 = [NSDecimalNumber decimalNumberWithString:@"6999"]; // 价格
PKPaymentSummaryItem *item2 = [PKPaymentSummaryItem summaryItemWithLabel:@"iphone 6s" amount:price2]; // 商品
NSDecimalNumber *price3 = [NSDecimalNumber decimalNumberWithString:@"商品价格之和"]; // 价格
PKPaymentSummaryItem *item3 = [PKPaymentSummaryItem summaryItemWithLabel:@"WilliamAlex的Walliet钱包" amount:price3];
/**
* 用户所要购买的商品列表,这里又有一个坑,数组中的最后一个元素,不是商品价格,而是所有商品的总价格
*/
request.paymentSummaryItems = @[item1, item2, item3];
/**
* 配置物流运输方式,同样是提供了类方法
*/
NSDecimalNumber *price4 = [NSDecimalNumber decimalNumberWithString:@"20.00"]; // 价格
PKShippingMethod *method1 = [PKShippingMethod summaryItemWithLabel:@"顺丰快递" amount:price4];
// 注意: 这里还需要配置两个属性,程序会直接崩掉
method1.identifier = @"shunfeng";
method1.detail = @"贵的很吖";
NSDecimalNumber *price5 = [NSDecimalNumber decimalNumberWithString:@"10.00"]; // 价格
PKShippingMethod *method2 = [PKShippingMethod summaryItemWithLabel:@"京东快递" amount:price5];
// 注意: 这里还需要配置两个属性,程序会直接崩掉
method1.identifier = @"京东";
method1.detail = @"24小时内送达";
NSDecimalNumber *price6 = [NSDecimalNumber decimalNumberWithString:@"15.00"]; // 价格
PKShippingMethod *method3 = [PKShippingMethod summaryItemWithLabel:@"韵达快递" amount:price6];
// 注意: 这里还需要配置两个属性,程序会直接崩掉
method1.identifier = @"yunda";
method1.detail = @"只能呵呵了...";
/**
* 配置快递的类型,是个枚举值,根据公司要求填写即可
*/
request.shippingType = PKShippingTypeDelivery;
/**
* 配置额外的信息,可选商户提供有关付款申请的信息。这是一个订单或购物车标识符的例子。它将签署包括在所产生
* 的pkpaymenttoken。@"goodsID=Alex" : 随便填写
*/
request.applicationData = [@"goodsID=Alex" dataUsingEncoding:NSUTF8StringEncoding];
request.shippingMethods = @[method1, method2, method3];
/**
* 做到这里,我们将基本的信息都填完了,但是我们还需要做的是监听授权是否成功,监听授权是否成功的方法是协议中
* 的方法,所以需要遵守协议,实现协议中的方法
*/
PKPaymentAuthorizationViewController *authorizationVc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
// 设置代理
authorizationVc.delegate = self;
// 跳转到支付界面
[self presentViewController:authorizationVc animated:YES completion:nil];
}
注意 : 在这个方法中,一些基本信息是必须要设置的,所以,如果不设置,程序会直接崩溃,当遇到程序崩溃时不要害怕,勇敢面对(o),根据打印信息设置即可.
授权代理方法
#pragma mark - 授权代理
/**
* 注意:协议中的两个方法是必须要实现的
*/
/**
* 调用时刻 : 当授权成功时,就会调用该方法
参数 1: 授权控制器
参数 2: 授权对象
参数 3: 一个block回调, 我们需要执行这个代码块, 来告诉系统当前的支付状态是否成功.
*/
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus status))completion
{
// 来到这里,我们拿到支付信息, 发送给服务器处理, 处理完毕之后, 服务器会返回一个状态, 告诉客户端,是否支付成功, 然后由客户端进行处理
BOOL isScuressful = YES;
if (isScuressful) { // 如果有值,执行回调block代码块,说明授权成功
completion(PKPaymentAuthorizationStatusSuccess);
// 提示用户支付成功
[SVProgressHUD showSuccessWithStatus:@"支付成功"];
} else
{ // 授权失败
completion(PKPaymentAuthorizationStatusFailure);
// 提示用户支付失败
[SVProgressHUD showErrorWithStatus:@"支付失败"];
}
}
- 当用户取消授权或者授权失败就会调下面的方法
/**
* 调用时刻: 授权失败,或者是取消授权就会调用该方法
*/
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
{
[self dismissViewControllerAnimated:controller completion:nil];
}
- 将头文件中常用方法与属性翻译过来了,不对之处请请提出来
//
// PKPaymentRequest.h
//
// Copyright (c) 2014, Apple Inc. All rights reserved.
//
#import
#import
@class PKContact;
NS_ASSUME_NONNULL_BEGIN
typedef NS_OPTIONS(NSUInteger, PKMerchantCapability) {
PKMerchantCapability3DS = 1UL << 0, // Merchant supports 3DS
PKMerchantCapabilityEMV = 1UL << 1, // Merchant supports EMV
PKMerchantCapabilityCredit NS_ENUM_AVAILABLE_IOS(9_0) = 1UL << 2, // Merchant supports credit
PKMerchantCapabilityDebit NS_ENUM_AVAILABLE_IOS(9_0) = 1UL << 3 // Merchant supports debit
} NS_ENUM_AVAILABLE(NA, 8_0);
typedef NS_OPTIONS(NSUInteger, PKAddressField) {
PKAddressFieldNone = 0UL, // No address fields required.
PKAddressFieldPostalAddress = 1UL << 0, // Full street address including name, street, city, state/province, postal code, country.
PKAddressFieldPhone = 1UL << 1,
PKAddressFieldEmail = 1UL << 2,
PKAddressFieldName NS_ENUM_AVAILABLE_IOS(8_3) = 1UL << 3,
PKAddressFieldAll = (PKAddressFieldPostalAddress|PKAddressFieldPhone|PKAddressFieldEmail|PKAddressFieldName)
} NS_ENUM_AVAILABLE(NA, 8_0);
typedef NS_ENUM(NSUInteger, PKShippingType) {
PKShippingTypeShipping,
PKShippingTypeDelivery,
PKShippingTypeStorePickup,
PKShippingTypeServicePickup
} NS_ENUM_AVAILABLE(NA, 8_3);
typedef NS_ENUM(NSUInteger, PKPaymentSummaryItemType) {
PKPaymentSummaryItemTypeFinal, // The payment summary item's amount is known to be correct
PKPaymentSummaryItemTypePending // The payment summary item's amount is estimated or unknown - e.g, a taxi fare
} NS_ENUM_AVAILABLE(NA, 9_0);
// 商品名称以及其价格
@interface PKPaymentSummaryItem : NSObject
+ (instancetype)summaryItemWithLabel:(NSString *)label amount:(NSDecimalNumber *)amount;
+ (instancetype)summaryItemWithLabel:(NSString *)label amount:(NSDecimalNumber *)amount type:(PKPaymentSummaryItemType)type NS_AVAILABLE(NA, 9_0);
@property (nonatomic, copy) NSString *label;
// 商品的价格
@property (nonatomic, copy) NSDecimalNumber *amount;
// Defaults to PKPaymentSummaryItemTypeFinal
// Set to PKPaymentSummaryItemTypePending if the amount of the item is not known at this time
@property (nonatomic, assign) PKPaymentSummaryItemType type NS_AVAILABLE(NA, 9_0);
@end
@interface PKShippingMethod : PKPaymentSummaryItem
// 物流标识
@property (nonatomic, copy, nullable) NSString *identifier;
// 物流的详细描述
@property (nonatomic, copy, nullable) NSString *detail;
@end
@interface PKPaymentRequest : NSObject
// 必须匹配一个商户ID,区别不同商店的标识符
@property (nonatomic, copy) NSString *merchantIdentifier;
// 所在国家的国际标准编码
@property (nonatomic, copy) NSString *countryCode;
// 表示由商家支持的支付网络, 即什么类型的卡支持使用ApplePay支付
@property (nonatomic, copy) NSArray *supportedNetworks;
// 处理方式(3DS是必须填的(可以用逻辑或||添加其他的方式)) 商家的支付处理能力
@property (nonatomic, assign) PKMerchantCapability merchantCapabilities;
// 表示用户所要购买的所有商品列表,数组中的最后一个不是商品,而是所有商品价格的总和.
@property (nonatomic, copy) NSArray *paymentSummaryItems;
// 支持该功能的货币编码
@property (nonatomic, copy) NSString *currencyCode;
// 显示账单上的地址信息(显示哪些?)默认是不显示的
@property (nonatomic, assign) PKAddressField requiredBillingAddressFields;
// 如果商家已经在文件上有一个帐单地址。
@property (nonatomic, assign, nullable) ABRecordRef billingAddress NS_DEPRECATED_IOS(8_0, 9_0, "Use billingContact instead");
@property (nonatomic, retain, nullable) PKContact *billingContact NS_AVAILABLE_IOS(9_0);
// 是否显示快递单上的地址等信息,默认是不显示的
@property (nonatomic, assign) PKAddressField requiredShippingAddressFields;
// 如果商检已经有了一个发货地址,在这里设置
@property (nonatomic, assign, nullable) ABRecordRef shippingAddress NS_DEPRECATED_IOS(8_0, 9_0, "Use shippingContact instead");
@property (nonatomic, retain, nullable) PKContact *shippingContact NS_AVAILABLE_IOS(9_0);
// 支持商品运输的物流方式有哪些.
@property (nonatomic, copy, nullable) NSArray *shippingMethods;
// 显示物流运输的方式,默认是PKShippingTypeShipping
@property (nonatomic, assign) PKShippingType shippingType NS_AVAILABLE_IOS(8_3);
// 可选商户提供有关付款申请的信息。这是一个订单或购物车标识符的例子。它将签署包括在所产生的pkpaymenttoken。
@property (nonatomic, copy, nullable) NSData *applicationData;
@end
NS_ASSUME_NONNULL_END
支付授权的流程:
框架发送支付请求给安全模块,只有安全模块可以访问存储在设备上的标记化的卡信息。
安全模块把特定的卡和商家等支付数据加密,以保证只有苹果可以读取,然后发送给框架。框架会将这些数据发送给苹果。
苹果服务器再次加密这些支付数据,以保证只有商家可以读取。然后服务器对它进行签名,生成支付token,然后发送给设备。
框架调用相应的代理方法并传入这个token,然后你的代理方法传送token给你的服务器。
服务器接收到token后的一般处理流程
验证支付数据的哈希表和签名
为加密过的支付数据解码
向支付处理系统提交支付数据
向订单追踪系统提交订单
处理支付请求时,你有两个选择;你既可以利用支付平台处理支付请求,也可以自己实现支付请求处理流程。一个常用的支付平台可以完成上述大部分操作。