官网 apple pay 配置(https://developer.apple.com/cn/help/account/configure-app-capabilities/configure-apple-pay)
一、创建 merchantID
1、登录开发者账号(https://developer.apple.com/account/resources/identifiers),创建 merchantID
经过上面的 5 个步骤创建merchantID
成功,下面就是进行merchantID
的配置
点击第一个上传文件,上传的文件是付款处理证书。
官网创建付款处理证书(https://developer.apple.com/cn/help/account/configure-app-capabilities/configure-apple-pay#create-a-payment-processing-certificate)
点击第二个上传文件(on the web),上传的文件是付款处理证书。
官网创建付款处理证书(https://developer.apple.com/cn/help/account/configure-app-capabilities/configure-apple-pay#create-a-payment-processing-certificate)
上传证书成功示例
二、关联 merchantID
1、登录开发者账号(https://developer.apple.com/account/resources/identifiers),选择自己的项目 ID
2、选择 apple pay 权限
3、关联成功示例
三、项目配置
1、项目xcode代码配置
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.******</string>
</array>
四、项目核心代码
1、设置支付请求包含的地区和货币信息
static NSString * _Nonnull const kApplepay_merchantID = @"merchant.******"; //这是之前你设置好的Merchant ID
static NSString * _Nonnull const kApplepay_currencyCode = @"CNY"; //人民币是CNY
static NSString * _Nonnull const kApplepay_countryCode = @"CN"; //中国是CN
2、判断是否可以支付
在创建支付请求之前,通过调用PKPaymentAuthorizationViewController
类的canMakePaymentsUsingNetworks:
方法,确定用户是否能够使用您支持的网络进行支付。要检查Apple Pay是否受到该设备硬件和家长控制的支持,可以使用canMakePayments
方法。
注意:PKPaymentAuthorizationController
类执行与PKPaymentAuthorizationViewController
类相同的角色,但它不依赖于UIKit框架。这意味着授权控制器可以用于视图控制器不能使用的地方(例如,在watchOS应用程序或intent扩展中)。
如果canMakePayments
返回NO
,则该设备不支持Apple Pay。不要显示Apple Pay按钮。回到另一种支付方式。
如果canMakePayments
返回YES
,而canMakePaymentsUsingNetworks:
返回NO
,说明设备支持Apple Pay,但是用户没有为任何请求的网络添加卡。您可以选择显示一个支付设置按钮,提示用户设置他的卡。只要用户点击此按钮,就开始设置新卡的过程(例如,通过调用openPaymentSetup
方法,会跳转到wallet)。
只要用户按下Apple Pay按钮,就必须开始支付授权过程。在提交付款请求之前,不能要求用户执行任何其他任务。例如,如果用户需要输入折扣代码,您必须在他按下Apple Pay按钮之前输入。(意思是只要按下Apple Pay按钮,就只能执行付款流程,其他需要用户操作的事情放在之前去做)
3、支付请求包含的支付清单
由PKPaymentSummaryItem
类表示的付款摘要项向用户描述付款请求的不同部分。使用少量的汇总项目——通常是小计、折扣、运费、税金和总金额。如果您没有任何额外的费用(例如,运费或税),只需使用购买的总额。在应用程序的其他地方提供逐项成本的详细信息。
每个摘要项都有一个标签和一个金额,如清单3-1所示。标签是用户可读的项目摘要描述。金额为相应的支付金额。付款请求中的所有金额都使用付款请求中指定的货币。对于折扣或优惠券,将金额设置为负数。
清单3-1:创建支付清单
// 89.99 subtotal
NSDecimalNumber *subtotalAmount = [NSDecimalNumber decimalNumberWithMantissa:8999 exponent:-2 isNegative:NO];
self.subtotal = [PKPaymentSummaryItem summaryItemWithLabel:@"Subtotal" amount:subtotalAmount];
// 3.00 discount
NSDecimalNumber *discountAmount = [NSDecimalNumber decimalNumberWithMantissa:300 exponent:-2 isNegative:YES];
self.discount = [PKPaymentSummaryItem summaryItemWithLabel:@"Discount" amount:discountAmount];
注意:付款汇总项使用
NSDecimalNumber
类将金额存储为基数为10
的数量。这个类的实例可以通过显式地指定尾数和指数(如代码清单所示)来创建,也可以通过提供数量作为字符串并指定区域设置来创建。例如,在财务计算中一定要使用基数为10
的数字,以确定5%
的折扣金额。
虽然看起来更方便,但是IEEE浮点数据类型(float
和Double
)不适合进行财务计算。这些数据类型使用以2
为基础的数字表示,这意味着一些十进制数字不能被精确地表示—例如,0.42
必须近似为0.41999
循环。这种近似可能导致财务计算返回不正确的结果。
(NSDecimalNumber
一般用于和钱打交道的数据存储,因为普通的浮点是有误差的,NSDecimalNumber
弥补了这个问题)
列表中的最后一个付款汇总项是总金额。通过添加所有其他汇总项的金额来计算总金额。总计与其他汇总项的显示方式不同:使用您的公司名称作为其标签,并使用所有其他汇总项的总额作为其金额。使用paymentSummaryItems
属性将付款摘要项添加到付款请求中。
如果您不知道授权支付时的实际成本(例如出租车费用),则使用PKPaymentSummaryItemTypePending
类型和0.0
金额创建一个小计汇总项目。对于总金额,使用正的非零金额和PKPaymentSummaryItemTypePending
类型。然后,系统将成本显示为pending
,没有数字数量。
注意:总金额不能是
0
或者负数。
NSDecimalNumber *totalAmount = [NSDecimalNumber zero];
totalAmount = [totalAmount decimalNumberByAdding:subtotalAmount];
totalAmount = [totalAmount decimalNumberByAdding:discountAmount];
self.total = [PKPaymentSummaryItem summaryItemWithLabel:@"My Company Name" amount:totalAmount];
self.summaryItems = @[self.subtotal, self.discount, self.total];
request.paymentSummaryItems = self.summaryItems;
4、Shipping Method
为每个可用的传送方法创建PKShippingMethod
的实例。与其他付款汇总项一样,shipping方法具有用户可读的标签,比如Standard shipping或Next Day shipping,以及表示运费的金额。与其他摘要项不同的是,运输方法还有一个详细的属性,比如“7月29日前到达”或“24小时内到达”,这就解释了运输方法之间的区别。
要区分委托方法中的传递方法,请使用标识符属性。此属性仅供您的应用程序使用—框架将其视为不透明值,并且它不会出现在UI中。在创建每个传送方法时,为其分配唯一标识符。为了便于调试,可以使用简短或缩写的字符串,如“discount”、“standard”或“next-day”。
有些运输方法不是在所有地区都可用,或者对于不同的地址有不同的成本。当用户选择送货地址或方法时,可以更新此信息,如委托更新送货方法和费用中所述。
5、支付处理机制的支持设置
通过使用字符串常量数组填充supportedNetworks
属性来指示您支持哪些支付网络。通过为merchantCapabilities
性属性设置一个值来指示您支持哪些支付处理协议。你必须支持3DS
;只有在支持Apple Pay的情况下才能指定EMV
。
request.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkDiscover, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa];
// Supports 3DS only
request.merchantCapabilities = PKMerchantCapability3DS;
// Supports both 3DS and EMV (add EMV only if you support Apple Pay in China)
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
6、运输和计费信息设置
填充付款授权视图控制器的requiredBillingAddressFields和requiredShippingAddressFields属性,以指示需要哪些账单和发货信息。当您呈现这个视图控制器时,它会提示用户提供所请求的帐单和发货信息。
request.requiredBillingAddressFields = PKAddressFieldEmail;
request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
注意:只要求填写所需要的信息。请求不必要的信息会给事务增加不必要的复杂性。每一个额外的步骤都增加了用户取消支付的可能性。
如果您有最新的账单和发货联系方式,您可以在付款请求中设置这些信息。Apple Pay默认使用这些信息;但是,用户仍然可以选择其他联系方式作为支付授权过程的一部分。
PKContact *contact = [[PKContact alloc] init];
NSPersonNameComponents *name = [[NSPersonNameComponents alloc] init];
name.givenName = @"Leo";
name.familyName = @"Appleseed";
contact.name = name;
CNMutablePostalAddress *address = [[CNMutablePostalAddress alloc] init];
address.street = @"666888 Street";
address.city = @"ShenZhen";
address.state = @"GA";
address.postalCode = @"000555";
contact.postalAddress = address;
request.shippingContact = contact;
注意:地址信息可以来自iOS中的各种来源。在使用信息之前一定要验证它。
7、设置完整的支付信息示例代码
- (void)configPaymentInformation {
//开始配置支付信息
self.payRequest = [[PKPaymentRequest alloc] init];
self.payRequest.countryCode = @"US"; //国家代码
self.payRequest.currencyCode = @"USD"; //RMB的币种代码
self.payRequest.merchantIdentifier = @"merchant.com.aspiraconnect.demo";//申请的merchantID
self.payRequest.supportedNetworks = self.supportedNetworkCards; //用户可以进行支付的银行卡
self.payRequest.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV;
//设置支持的交易处理协议, 3DS必须支持, EMV为可选
//payRequest.requiredShippingAddressFields = \
PKAddressFieldPostalAddress | PKAddressFieldPhone | PKAddressFieldName;
//设置发货地址
self.payRequest.requiredShippingAddressFields = PKAddressFieldNone;
//空发货地址
self.payRequest.shippingMethods = @[];
NSDecimalNumber *totalAmount = \
[NSDecimalNumber decimalNumberWithString:@"0.01"];//创建金额
PKPaymentSummaryItem *total = \
[PKPaymentSummaryItem summaryItemWithLabel:@"My Company Name" amount:totalAmount];
self.summaryItems = [NSMutableArray arrayWithArray:@[total]];
self.payRequest.paymentSummaryItems = self.summaryItems;
}
8、支付回调
官网支付回调数据(https://developer.apple.com/documentation/passkit/apple_pay/payment_token_format_reference?language=objc)
HJApplePayManage *payManage = [HJApplePayManage payWithAmount:totalAmount request:nil isTest:self.isTest presentingVc:self.root authed:^(PKPayment * _Nullable payment, NSString *creditType) {
id dic = [NSJSONSerialization JSONObjectWithData:payment.token.paymentData options:NSJSONReadingMutableLeaves error:nil];
// 处理 apple pay 支付回调数据和项目后台数据进行交互
}];
9、申请黑盒测试 apple ID,添加测试 apply pay 卡数据(https://www.jianshu.com/p/a63756023f53)
实际项目应用代码
.h 文件
#import <PassKit/PassKit.h>
static NSString * _Nonnull const kApplepay_merchantID = @"merchant.******";
static NSString * _Nonnull const kApplepay_currencyCode = @"CNY";
static NSString * _Nonnull const kApplepay_countryCode = @"CN";
typedef void(^shouldVerifyBlock)(PKPayment * __nullable payment, NSString * __nullable creditType);
@interface HJApplePayManage : NSObject
/// 判断苹果支付是否可用
+ (BOOL)canMakePayments;
+ (instancetype)payWithAmount:(NSString *)amount
request:(PKPaymentRequest * _Nullable)request
isTest:(BOOL)isTest
presentingVc:(UIViewController *)presentingVc
authed:(shouldVerifyBlock)authBlock;
/// 通知applePayManage接口处理完毕
- (void)serviceVerifyed:(BOOL)verifyed applePayFinished:(dispatch_block_t)applePayFinished;
.m 文件
#import "HJApplePayManage.h"
#import "NSNumber+SPUtilsExtras.h"
API_AVAILABLE(ios(11.0))
@interface HJApplePayManage () <PKPaymentAuthorizationViewControllerDelegate>
@property (nonatomic, copy) void(^authCompletionBlock)(PKPaymentAuthorizationResult *result);
@property (nonatomic, copy) shouldVerifyBlock didAuthorizeBlock;
@property (nonatomic, copy) dispatch_block_t didFinishBlock;
#if Zprd
// 上线环境
#else
// 非 上线环境
@property (nonatomic, assign) BOOL isTest;
#endif
@end
@implementation HJApplePayManage
#pragma mark - Public methods
/*
判断是否可以支付
在创建支付请求之前,通过调用PKPaymentAuthorizationViewController类的canMakePaymentsUsingNetworks:方法,确定用户是否能够使用您支持的网络进行支付。要检查Apple Pay是否受到该设备硬件和家长控制的支持,可以使用canMakePayments方法。
注意:PKPaymentAuthorizationController类执行与PKPaymentAuthorizationViewController类相同的角色,但它不依赖于UIKit框架。这意味着授权控制器可以用于视图控制器不能使用的地方(例如,在watchOS应用程序或intent扩展中)。
如果canMakePayments返回NO,则该设备不支持Apple Pay。不要显示Apple Pay按钮。回到另一种支付方式。
如果canMakePayments返回YES,而canMakePaymentsUsingNetworks:返回NO,说明设备支持Apple Pay,但是用户没有为任何请求的网络添加卡。您可以选择显示一个支付设置按钮,提示用户设置他的卡。只要用户点击此按钮,就开始设置新卡的过程(例如,通过调用openPaymentSetup方法,会跳转到wallet)。
只要用户按下Apple Pay按钮,就必须开始支付授权过程。在提交付款请求之前,不能要求用户执行任何其他任务。例如,如果用户需要输入折扣代码,您必须在他按下Apple Pay按钮之前输入。(意思是只要按下Apple Pay按钮,就只能执行付款流程,其他需要用户操作的事情放在之前去做)
*/
+ (BOOL)canMakePayments {
if(![PKPaymentAuthorizationViewController canMakePayments]){
NSLog(@"cannot pay");
return NO;
}
if(![PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:@[PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, PKPaymentNetworkChinaUnionPay]]) {
NSLog(@"Wallet did not add a savings / credit card for this payment network");
return NO;
}
NSLog(@"can pay");
return YES;
}
+ (instancetype)payWithAmount:(NSString *)amount
request:(PKPaymentRequest * _Nullable)request
isTest:(BOOL)isTest
presentingVc:(UIViewController *)presentingVc
authed:(shouldVerifyBlock)authBlock
{
HJApplePayManage *manage = [HJApplePayManage new];
manage.isTest = isTest;
[manage applePayWithAmount:amount request:request presentingVc:presentingVc];
manage.didAuthorizeBlock = authBlock;
return manage;
}
/// 通知applePayManage接口处理完毕
- (void)serviceVerifyed:(BOOL)verifyed applePayFinished:(dispatch_block_t)applePayFinished
{
self.didFinishBlock = applePayFinished;
PKPaymentAuthorizationResult *authResult;
if (verifyed) {
authResult = [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil];
} else {
authResult = [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:nil];
}
if (self.authCompletionBlock) self.authCompletionBlock(authResult);
}
#pragma mark - ApplePay PaymentRequest
- (void)applePayWithAmount:(NSString *)amount request:(PKPaymentRequest *)request presentingVc:(UIViewController *)presentingVc
{
if (!request) {
request = [[PKPaymentRequest alloc] init];
}
request.merchantIdentifier = kApplepay_merchantID;
request.currencyCode = kApplepay_currencyCode;
request.countryCode = kApplepay_countryCode;
// 支持的支付网络
//(PKPaymentNetworkChinaUnionPay iOS9.2开始支持)
if (@available(iOS 12.1.1, *)) {
request.supportedNetworks = @[PKPaymentNetworkMada, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, PKPaymentNetworkChinaUnionPay];
} else {
request.supportedNetworks = @[PKPaymentNetworkMasterCard, PKPaymentNetworkVisa, PKPaymentNetworkChinaUnionPay];
}
// 支付请求金额信息列表
NSDecimalNumber *topupAmount = [NSDecimalNumber decimalNumberWithString:amount];
PKPaymentSummaryItem *topupItem = [PKPaymentSummaryItem summaryItemWithLabel:@"支付总金额" amount:topupAmount];
PKPaymentSummaryItem *total = [PKPaymentSummaryItem summaryItemWithLabel:@"Demo" amount:topupAmount type:PKPaymentSummaryItemTypeFinal];
request.paymentSummaryItems = @[topupItem, total];
// 3DS支付方式是必须支持的,EMV方式是可选的, PKMerchantCapabilityCredit(信用卡) 充值钱包类项目要注意信用卡不能支持,防止套现违规操作
#if Zprd
// 上线环境
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV | PKMerchantCapabilityDebit;
#else
// 非 上线环境
if (self.isTest) {
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV | PKMerchantCapabilityDebit | PKMerchantCapabilityCredit;
}
else {
request.merchantCapabilities = PKMerchantCapability3DS | PKMerchantCapabilityEMV | PKMerchantCapabilityDebit;
}
#endif
// 存储额外信息
//使用applicationData属性来存储一些在你的应用中关于这次支付请求的唯一标识信息,比如一个购物车的标识符。在用户授权支付之后,这个属性的哈希值会出现在这次支付的token中。
// request.applicationData = [@"购物车ID: Demo" dataUsingEncoding:NSUTF8StringEncoding];
// 设置 只有指定地区发行的卡才可以进行支付,不是这个地区的卡不能选择支付
// 不设置这个默认全部国家的卡都可以
#if Zprd
// 上线环境
NSSet<NSString *> *supportedCountries = [NSSet setWithArray:@[kApplepay_countryCode]];
request.supportedCountries = supportedCountries;
#else
// 非 上线环境
if (self.isTest) {
}
else {
NSSet<NSString *> *supportedCountries = [NSSet setWithArray:@[kApplepay_countryCode]];
request.supportedCountries = supportedCountries;
}
#endif
PKPaymentAuthorizationViewController *paymentPane = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
paymentPane.delegate = self;
paymentPane.definesPresentationContext = YES;
[presentingVc presentViewController:paymentPane animated:YES completion:nil];
}
#pragma mark - ApplePay PKPaymentAuthorizationViewControllerDelegate
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment:(PKPayment *)payment
handler:(void (^)(PKPaymentAuthorizationResult *result))completion API_AVAILABLE(ios(11.0), watchos(4.0)) {
self.authCompletionBlock = completion;
if (self.didAuthorizeBlock) {
self.didAuthorizeBlock(payment, payment.token.paymentMethod.network);
}
}
/// 因(PKPaymentNetworkMada iOS12.1.1开始支持) 暂不适配iOS11以下
//- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
// didAuthorizePayment:(PKPayment *)payment
// completion:(void (^)(PKPaymentAuthorizationStatus status))completion API_DEPRECATED("Use paymentAuthorizationViewController:didAuthorizePayment:handler: instead to provide more granular errors", ios(8.0, 11.0)) {
//
//}
/**
* 当授权成功之后或者取消授权之后会调用这个代理方法
*/
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
[controller dismissViewControllerAnimated:YES completion:^{
if (self.didFinishBlock) {
self.didFinishBlock();
}
} ];
}