前言
美國時間于2014年10月20日蘋果公司正式推出Applepay支付功能盟戏,直到2016年2月18日凌晨5:00, Apple Pay 才正式登陸中國.也就是說,在國內(nèi)想要使用Applepay支付功能必須要滿足幾個條件:1,設(shè)備的支付環(huán)境必須是要iPhone6以上的設(shè)備,并且系統(tǒng)要是iOS9以后才能使用銀聯(lián)卡,在國內(nèi)只有少數(shù)的APP才有ApplePay支付功能
1, 簡單介紹ApplePay
Applepay是蘋果公司在2014年秋季新品發(fā)布會上發(fā)布的一種基于NFC的手機(jī)支付功能.可以通過TouchID或者Passcode兩種方式進(jìn)行支付.用戶可使用存儲在iPhone 6, 6p等設(shè)備上的信用卡和借記卡支付證書來授權(quán)支付,也就是說,該功能只能在iphone 6以上的設(shè)備才能使用.
ApplePay除了可以線下支付還能線下支付,不需要網(wǎng)絡(luò)就可以支付,而且更加安全(這是與國內(nèi)最流行的微信支付和支付寶支付的區(qū)別之一).
關(guān)于ApplePay的安全性以及如何使用ApplePay可以去官網(wǎng)上的文檔查詢(前言中已給出鏈接).
ApplePay是在2016年2月18日正式登陸中國市場,所以,目前支持使用ApplePay功能的App還不是很多,但是我相信以后一定會和微信支付和支付寶支付一樣普及的,因?yàn)樗臃奖?更加安全(鑒于庫克目前正在和美國FBI對戰(zhàn)中,說明Apple公司很注重用戶的隱私,盡管iphone還有很多bugo)...
2, 集成ApplePay步驟
- 配置支付環(huán)境
- 創(chuàng)建Xcode項(xiàng)目,并設(shè)置好對應(yīng)的BundleID(需要在開發(fā)者中心用到)
- 到蘋果開發(fā)者中心注冊并配置一個商業(yè)標(biāo)識符(Merchant ID)
- 1, 登錄您的Apple ID,然后到賬號中,進(jìn)入配置證書選項(xiàng),添加Apple ID, 然后在下面勾選Apple Pay選項(xiàng)
- 2, 配置Merchant ID
- 3, 為Merchant ID配置證書,并且下載證書,雙擊點(diǎn)擊安裝到鑰匙串中.
- 4, 檢查安裝到鑰匙串中的證書是否已經(jīng)過期了,如果過去了那么就重新下載證書.
- 5, 將配置的Merchant ID綁定到APP ID中
- 注意: 上面配置證書是需要付費(fèi)的,所以說的比較簡陋,等以后有了自己的賬號,再附上配置Merchant ID圖片.
3, 配置Xcode項(xiàng)目環(huán)境
- 1, 點(diǎn)擊項(xiàng)目名稱,然后點(diǎn)擊Capablilties,將ApplePay功能打開,將配置的Merchant ID添加進(jìn)去,如果下面的Steps全部是打鉤,說明配置成功.
4, 代碼實(shí)現(xiàn)
- 思路分析
/*
分析 : 使用Applepay的前提是必須要導(dǎo)入一個特有的框架PassKit,包含所有支付的方法和屬性
思路 : 美國時間于2014年10月20日蘋果公司正式推出Applepay支付功能限书,直到2016年2月18日凌晨5:00珊肃, Apple Pay 業(yè)務(wù)在中國才正式上線.也就是說,在內(nèi)的想要使用Applepay支付功能必須要滿足幾個條件:1,設(shè)備的支付環(huán)境必須是要iPhone6以上的設(shè)備,并且系統(tǒng)要是iOS9以后才能使用銀聯(lián)卡,在國內(nèi)只有少數(shù)的APP才有ApplePay支付功能,所以在正式購買商品之前,需要做幾個判斷.具體步驟如下:
步驟:
1, 首先需要判斷當(dāng)前環(huán)境是否滿足ApplePay支付功能,如果不滿足,那么隱藏支付按鈕
2, 如果可以使用ApplePay支付功能,還需要判斷是否添加了銀行卡,如果沒有添加銀行卡,那么創(chuàng)建ApplePay特有的按鈕,監(jiān)聽按鈕的點(diǎn)擊事件,當(dāng)用戶點(diǎn)擊按鈕之后,跳轉(zhuǎn)到添加銀行卡界面.
3, 如果前面兩個條件都滿足了,那么就可以直接購買商品了,所以創(chuàng)建支付特有的按鈕,監(jiān)聽按鈕的點(diǎn)擊,當(dāng)用戶點(diǎn)擊支付按鈕后,購買商品,配置支付商品的相關(guān)信息.
*/
/**
* 導(dǎo)入頭文件
*/
#import "ViewController.h"
#import <SVProgressHUD.h>
#import <PassKit/PassKit.h>
@interface ViewController ()<PKPaymentAuthorizationViewControllerDelegate>
/**
* 支付View
*/
@property (weak, nonatomic) IBOutlet UIView *ApplepayView;
@end
- 具體實(shí)現(xiàn)
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1,判斷當(dāng)前的環(huán)境是否能夠使用Applepay支付功能
if (![PKPaymentAuthorizationViewController canMakePayments]) {
// 提示用戶當(dāng)前設(shè)備不支持ApplePay
[SVProgressHUD showErrorWithStatus:@"當(dāng)前設(shè)備不支持ApplePay功能"];
// 隱藏支付按鈕
self.ApplepayView.hidden = YES;
}else if (![PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard, PKPaymentNetworkAmex]])
{
// 來到這里表示當(dāng)前的設(shè)備是可以使用ApplePay功能,但是沒有綁定銀行卡,無法完成支付,這里需要注意,ApplePay是在iOS9之后才在中國上線的,所以,需要是iOS9以上的設(shè)備才能支持銀聯(lián)卡.所以我們需要給wallet錢包添加銀行卡.
// 提示用戶需要綁定銀行卡
[SVProgressHUD showErrorWithStatus:@"請?zhí)砑咏壎ㄣy行卡類型"];
/**
* 創(chuàng)建支付使用的按鈕,兩個參數(shù)都是枚舉值,直接點(diǎn)進(jìn)頭文件即可,創(chuàng)建特定的支付按鈕,有兩個方法,一個對象
* 方法,一個類方法,這里使用類方法,快捷方便一點(diǎn)
*/
PKPaymentButton *payButton = [PKPaymentButton buttonWithType:PKPaymentButtonTypeSetUp style:PKPaymentButtonStyleWhiteOutline];
/**
* 監(jiān)聽按鈕的點(diǎn)擊,當(dāng)用戶點(diǎn)擊按鈕之后,直接跳轉(zhuǎn)到添加銀行卡界面
*/
[payButton addTarget:self action:@selector(jump2MakePaymentsUsingNetworks) forControlEvents:UIControlEventTouchUpInside];
/**
* 將創(chuàng)建的按鈕添加到定義的View中
*/
[self.ApplepayView addSubview:payButton];
} else
{
/**
* 來到這里表示當(dāng)前用戶的設(shè)備支持ApplePay功能,并且在wallet錢包中已經(jīng)綁定好了銀行卡,直接點(diǎn)擊購買按
* 鈕即可.
*/
// 創(chuàng)建支付按鈕
PKPaymentButton *payButton = [PKPaymentButton buttonWithType:PKPaymentButtonTypeBuy style:PKPaymentButtonStyleBlack];
/**
* 監(jiān)聽按鈕的點(diǎn)擊
*/
[payButton addTarget:self action:@selector(purchase) forControlEvents:UIControlEventTouchUpInside];
/**
* 添加支付按鈕
*/
[self.ApplepayView addSubview:payButton];
}
}
注意 : PKPaymentNetworkChinaUnionPay銀聯(lián)卡的使用前提
事件的監(jiān)聽
- (void)jump2MakePaymentsUsingNetworks {
/**
* 跳轉(zhuǎn)到添加銀行卡界面,系統(tǒng)直接就給我們提供了一個方法,直接創(chuàng)建界面,然后open即可
*/
PKPassLibrary *library = [[PKPassLibrary alloc] init];
/**
* 跳轉(zhuǎn)到綁定銀行卡界面
*/
[library openPaymentSetup];
}
- 注意 : 添加銀行卡界面是系統(tǒng)內(nèi)部提供的,我們只需要創(chuàng)建,調(diào)用方法使用即可.
- 購買商品
- (void)purchase {
/**
* 來到這里,說明可以直接支付購買商品,但是在支付的之前,還需要創(chuàng)建一個支付請求,給它授權(quán),然后配置一些必要信
* 息,必要信息如下
*/
/**
* 創(chuàng)建一個支付請求
*/
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
/**
* 配置商戶ID:換句話說就是商店的標(biāo)識,用于區(qū)分商店的ID
*/
request.merchantIdentifier = @"這個ID就是您在蘋果開發(fā)者中心申請的商戶ID,具體上面有介紹";
/**
* 當(dāng)前用戶所在的國家的國際編碼:中國的國際編碼是"CN"
*/
request.countryCode = @"CN";
/**
* 當(dāng)前用戶使用的貨幣編碼 : 人民幣的國際編碼是"CNY"
*/
request.currencyCode = @"CNY";
/**
* 商家所支持的網(wǎng)絡(luò)有哪些? 換句話說就是可以使用什么類型的卡支付,支持的網(wǎng)絡(luò)返回的是一個數(shù)組
* 一定要注意,如果用戶使用的是中國的銀聯(lián)卡,必須要求設(shè)備是6/6s以上,系統(tǒng)要保證在iOS9以上
* PKPaymentNetworkChinaUnionPay(中國銀聯(lián)卡)
*/
request.supportedNetworks = @[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard];
/**
* 關(guān)于支付后商家的處理方式,這里還有一個坑,那就是PKMerchantCapability3DS必須填寫,如果還有其他的方式
* 可以使用'|'邏輯或.
*/
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
/**
* 是否顯示賬單上地址等信息,默認(rèn)是不顯示的,值是個枚舉值,表示全部顯示
*/
request.requiredBillingAddressFields = PKAddressFieldAll;
/**
* 是否顯示快遞單上地址等信息,默認(rèn)是不顯示的
*/
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];
/**
* 用戶所要購買的商品列表,這里又有一個坑,數(shù)組中的最后一個元素,不是商品價格,而是所有商品的總價格
*/
request.paymentSummaryItems = @[item1, item2, item3];
/**
* 配置物流運(yùn)輸方式,同樣是提供了類方法
*/
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小時內(nèi)送達(dá)";
NSDecimalNumber *price6 = [NSDecimalNumber decimalNumberWithString:@"15.00"]; // 價格
PKShippingMethod *method3 = [PKShippingMethod summaryItemWithLabel:@"韻達(dá)快遞" amount:price6];
// 注意: 這里還需要配置兩個屬性,程序會直接崩掉
method1.identifier = @"yunda";
method1.detail = @"只能呵呵了...";
/**
* 配置快遞的類型,是個枚舉值,根據(jù)公司要求填寫即可
*/
request.shippingType = PKShippingTypeDelivery;
/**
* 配置額外的信息,可選商戶提供有關(guān)付款申請的信息纪岁。這是一個訂單或購物車標(biāo)識符的例子毁欣。它將簽署包括在所產(chǎn)生
* 的pkpaymenttoken卒密。@"goodsID=Alex" : 隨便填寫
*/
request.applicationData = [@"goodsID=Alex" dataUsingEncoding:NSUTF8StringEncoding];
request.shippingMethods = @[method1, method2, method3];
/**
* 做到這里,我們將基本的信息都填完了,但是我們還需要做的是監(jiān)聽授權(quán)是否成功,監(jiān)聽授權(quán)是否成功的方法是協(xié)議中
* 的方法,所以需要遵守協(xié)議,實(shí)現(xiàn)協(xié)議中的方法
*/
PKPaymentAuthorizationViewController *authorizationVc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
// 設(shè)置代理
authorizationVc.delegate = self;
// 跳轉(zhuǎn)到支付界面
[self presentViewController:authorizationVc animated:YES completion:nil];
}
注意 : 在這個方法中,一些基本信息是必須要設(shè)置的,所以,如果不設(shè)置,程序會直接崩潰,當(dāng)遇到程序崩潰時不要害怕,勇敢面對(o),根據(jù)打印信息設(shè)置即可.
授權(quán)代理方法
#pragma mark - 授權(quán)代理
/**
* 注意:協(xié)議中的兩個方法是必須要實(shí)現(xiàn)的
*/
/**
* 調(diào)用時刻 : 當(dāng)授權(quán)成功時,就會調(diào)用該方法
參數(shù) 1: 授權(quán)控制器
參數(shù) 2: 授權(quán)對象
參數(shù) 3: 一個block回調(diào), 我們需要執(zhí)行這個代碼塊, 來告訴系統(tǒng)當(dāng)前的支付狀態(tài)是否成功.
*/
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus status))completion
{
// 來到這里,我們拿到支付信息, 發(fā)送給服務(wù)器處理, 處理完畢之后, 服務(wù)器會返回一個狀態(tài), 告訴客戶端,是否支付成功, 然后由客戶端進(jìn)行處理
BOOL isScuressful = YES;
if (isScuressful) { // 如果有值,執(zhí)行回調(diào)block代碼塊,說明授權(quán)成功
completion(PKPaymentAuthorizationStatusSuccess);
// 提示用戶支付成功
[SVProgressHUD showSuccessWithStatus:@"支付成功"];
} else
{ // 授權(quán)失敗
completion(PKPaymentAuthorizationStatusFailure);
// 提示用戶支付失敗
[SVProgressHUD showErrorWithStatus:@"支付失敗"];
}
}
- 當(dāng)用戶取消授權(quán)或者授權(quán)失敗就會調(diào)下面的方法
/**
* 調(diào)用時刻: 授權(quán)失敗,或者是取消授權(quán)就會調(diào)用該方法
*/
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
{
[self dismissViewControllerAnimated:controller completion:nil];
}
- 將頭文件中常用方法與屬性翻譯過來了,不對之處請請?zhí)岢鰜?/li>
//
// 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
// 物流標(biāo)識
@property (nonatomic, copy, nullable) NSString *identifier;
// 物流的詳細(xì)描述
@property (nonatomic, copy, nullable) NSString *detail;
@end
@interface PKPaymentRequest : NSObject
// 必須匹配一個商戶ID,區(qū)別不同商店的標(biāo)識符
@property (nonatomic, copy) NSString *merchantIdentifier;
// 所在國家的國際標(biāo)準(zhǔn)編碼
@property (nonatomic, copy) NSString *countryCode;
// 表示由商家支持的支付網(wǎng)絡(luò), 即什么類型的卡支持使用ApplePay支付
@property (nonatomic, copy) NSArray<NSString *> *supportedNetworks;
// 處理方式(3DS是必須填的(可以用邏輯或||添加其他的方式)) 商家的支付處理能力
@property (nonatomic, assign) PKMerchantCapability merchantCapabilities;
// 表示用戶所要購買的所有商品列表,數(shù)組中的最后一個不是商品,而是所有商品價格的總和.
@property (nonatomic, copy) NSArray<PKPaymentSummaryItem *> *paymentSummaryItems;
// 支持該功能的貨幣編碼
@property (nonatomic, copy) NSString *currencyCode;
// 顯示賬單上的地址信息(顯示哪些?)默認(rèn)是不顯示的
@property (nonatomic, assign) PKAddressField requiredBillingAddressFields;
// 如果商家已經(jīng)在文件上有一個帳單地址缀台。
@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);
// 是否顯示快遞單上的地址等信息,默認(rèn)是不顯示的
@property (nonatomic, assign) PKAddressField requiredShippingAddressFields;
// 如果商檢已經(jīng)有了一個發(fā)貨地址,在這里設(shè)置
@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);
// 支持商品運(yùn)輸?shù)奈锪鞣绞接心男?
@property (nonatomic, copy, nullable) NSArray<PKShippingMethod *> *shippingMethods;
// 顯示物流運(yùn)輸?shù)姆绞?默認(rèn)是PKShippingTypeShipping
@property (nonatomic, assign) PKShippingType shippingType NS_AVAILABLE_IOS(8_3);
// 可選商戶提供有關(guān)付款申請的信息。這是一個訂單或購物車標(biāo)識符的例子哮奇。它將簽署包括在所產(chǎn)生的pkpaymenttoken膛腐。
@property (nonatomic, copy, nullable) NSData *applicationData;
@end
NS_ASSUME_NONNULL_END
支付授權(quán)的流程:
框架發(fā)送支付請求給安全模塊,只有安全模塊可以訪問存儲在設(shè)備上的標(biāo)記化的卡信息鼎俘。
安全模塊把特定的卡和商家等支付數(shù)據(jù)加密哲身,以保證只有蘋果可以讀取,然后發(fā)送給框架贸伐÷砂眨框架會將這些數(shù)據(jù)發(fā)送給蘋果。
蘋果服務(wù)器再次加密這些支付數(shù)據(jù)棍丐,以保證只有商家可以讀取误辑。然后服務(wù)器對它進(jìn)行簽名,生成支付token歌逢,然后發(fā)送給設(shè)備巾钉。
框架調(diào)用相應(yīng)的代理方法并傳入這個token,然后你的代理方法傳送token給你的服務(wù)器秘案。
服務(wù)器接收到token后的一般處理流程
驗(yàn)證支付數(shù)據(jù)的哈希表和簽名
為加密過的支付數(shù)據(jù)解碼
向支付處理系統(tǒng)提交支付數(shù)據(jù)
向訂單追蹤系統(tǒng)提交訂單
處理支付請求時砰苍,你有兩個選擇潦匈;你既可以利用支付平臺處理支付請求,也可以自己實(shí)現(xiàn)支付請求處理流程赚导。一個常用的支付平臺可以完成上述大部分操作凰锡。