iOS开发之ApplePay

前言

美国时间于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全部是打钩,说明配置成功.
iOS开发之ApplePay_第1张图片
Alex.png

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后的一般处理流程

  • 验证支付数据的哈希表和签名

  • 为加密过的支付数据解码

  • 向支付处理系统提交支付数据

  • 向订单追踪系统提交订单

  • 处理支付请求时,你有两个选择;你既可以利用支付平台处理支付请求,也可以自己实现支付请求处理流程。一个常用的支付平台可以完成上述大部分操作。

你可能感兴趣的:(iOS开发之ApplePay)