iOS开发之ApplePay

作者: Alexander | 来源:发表于2016-03-20 21:42 被阅读638次

    前言

    美国时间于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全部是打钩,说明配置成功.
    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 <SVProgressHUD.h>
    #import <PassKit/PassKit.h>
    
    
    @interface ViewController ()<PKPaymentAuthorizationViewControllerDelegate>
    
    /**
     * 支付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 <Foundation/Foundation.h>
    #import <AddressBook/ABRecord.h>
    
    @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<NSString *> *supportedNetworks;
    
    //  处理方式(3DS是必须填的(可以用逻辑或||添加其他的方式)) 商家的支付处理能力
    @property (nonatomic, assign) PKMerchantCapability merchantCapabilities;
    
     // 表示用户所要购买的所有商品列表,数组中的最后一个不是商品,而是所有商品价格的总和.
    @property (nonatomic, copy) NSArray<PKPaymentSummaryItem *> *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<PKShippingMethod *> *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

      本文链接:https://www.haomeiwen.com/subject/kskklttx.html