Apple Pay在2月18号终于在中国上线了,对iOS开发者(尤其是b2b,b2c等平台的开发者)来说有多了一种要集成的支付方式。翻翻Apple的官方文档中只找到了swift版本的demo(由此可以看出apple对于swift的推广是有多么坚定。所以在平时的休息时间学习swift还是很有必要的)。言归正传,今天在这里介绍下。使用OC完成Apple Pay的集成使用。
相对于其他第三方的支付。对于Apple Pay的集成可以说相对简单的很多。毕竟是苹果的亲儿子在集成方面很多都由苹果自己封装完成了。作为开发者仅仅只需要配置些环境、传递相关的购物请求。就可以轻松完成支付。
PS:在Xcode7.0之后就可以在模拟器上进行支付的测试。
在支付过程中,只要用户授权支付,Apple Pay就会对支付的信息进行加密(防止第三方未经授权访问),将支付请求发送到安全组件中(在用户设备上的特定芯片)。安全芯片通过指定的银行卡和商户名等数据来创建一个加密后的token。这个token之后将会传递到Apple的服务器上。苹果服务器又会使用你设置的商户标识(merchant identity certificate:在member center中心设置)进行再次加密。最后服务器将会返回此token进行后续操作。关于Apple Pay的安全问题这边有文档的介绍:
https://www.apple.com/business/docs/iOS_Security_Guide.pdf
集成步骤:
1。配置环境
使用Apple Pay功能时,你首先需要一个商户的id标识用户支付。一个
merchant ID 只包含一个公钥和证书。
merchant ID通常被用于支付过程中加密支付信息的一部分。在使用Apple Pay之前,你需要先注册一个
merchant ID并且配置好证书
注册merchant ID:
- 进入开发者中心。选择Certificates, Identifiers & Profiles.
- 在Identifiers下选中merchant ID.
- 选择添加按钮
- 输入描述信息和identifier.
- done
为
merchant ID添加证书的配置:
- 进入开发者中心。选择Certificates, Identifiers & Profiles.
- 在Identifiers下选中merchant ID.
- 从merchant ID列表中选中之前创建的ID。点击编辑
- 点击其中的Create Certificate按钮。选择或生成我们的CSR文件(certificate signing request ),点击继续.
- 选择创建好的csr文件。点击生成
- 下载生成好的证书。双击安装。
PS:安装完成后可能会发现显示”此证书是由未知颁发机构签名的”。这时需要我们去
apple.com/certificateauthority下载
WWDR intermediate certificate - G2 和 Apple Root CA - G2
ok,现在在member center 中的设置告一段落。转战Xcode。
在Xcode中配置相对简单,只要做下图配置。switch打开Apple Pay。选中在member center中设置好的merchant Id
至此,相关的环境配置完成。成功配置后再app ids可以看到自己项目对应的apple pay 是可用的
下面就是代码部分了,在苹果支付的sdk中我们可以看到很多都是从8.x开始的,但由于在中国此前并未开放Apple Pay 所以中国的都是从9.2(
目前支持Apple Pay的设备包括
iPhone6s
、iPhone6s Plus、iPhone6、iPhone6 Plus、iPad Pro、iPad Air 2、iPad mini 4、iPad mini 3和Apple Watch)开始支持.
在进行Apple Pay时我们仅仅需要将拼接好一个支付请求(
PKPaymentRequest
)传递给一个支付的控制器(
PKPaymentAuthorizationViewController
)中,然后在回调的代理中处理用户的操作结果就行了。
在使用Apple Pay支付之前可使用
PKPaymentAuthorizationViewController中
canMakePayments检测设备是否支持Apple Pay。
externNSString *const PKPaymentNetworkAmexNS_AVAILABLE(NA,8_0);
externNSString *const PKPaymentNetworkChinaUnionPayNS_AVAILABLE(NA,9_2);
externNSString *const PKPaymentNetworkDiscoverNS_AVAILABLE(NA,9_0);
externNSString *const PKPaymentNetworkInteracNS_AVAILABLE(NA,9_2);
externNSString *const PKPaymentNetworkMasterCardNS_AVAILABLE(NA,8_0);
externNSString *const PKPaymentNetworkPrivateLabelNS_AVAILABLE(NA,9_0);
externNSString *const PKPaymentNetworkChinaUnionPayNS_AVAILABLE(NA,9_2);
externNSString *const PKPaymentNetworkDiscoverNS_AVAILABLE(NA,9_0);
externNSString *const PKPaymentNetworkInteracNS_AVAILABLE(NA,9_2);
externNSString *const PKPaymentNetworkMasterCardNS_AVAILABLE(NA,8_0);
externNSString *const PKPaymentNetworkPrivateLabelNS_AVAILABLE(NA,9_0);
externNSString *const PKPaymentNetworkVisaNS_AVAILABLE(NA,8_0);)
通过是否支持来决定是否显示苹果支付的按钮。显示Apple Pay支付按钮时一般使用系统自带的
PKPaymentButton 类.也可以在使用自定义的按钮。但需要按照苹果的规范来使用
Apple Pay Identity Guidelines
.如果不支持的话不应该继续present支付控制器。而应该引导用户开启支付或者进行绑卡
注意:如果你的app主要是基于web开发的,那么在进行Apple Pay的使用时你需要将支付请求转移原生的iOS代码上。下面是Apple 官方给出的一段demo
-
// Called when the web view tries to load "myShoppingApp:buyItem"
-
-(void)webView:(nonnullWKWebView*)webView
-
decidePolicyForNavigationAction:(nonnullWKNavigationAction*)navigationAction
-
decisionHandler:(nonnullvoid(^)(WKNavigationActionPolicy))decisionHandler{
-
// Get the URL for the selected link.
-
NSURL*URL=navigationAction.request.URL;
-
// If the scheme and resource specifier match those defined by your app,
-
// handle the payment in native iOS code.
-
if([URL.schemeisEqualToString:@"myShoppingApp"]&&
-
[URL.resourceSpecifierisEqualToString:@"buyItem"]){
-
// Create and present the payment request here.
-
// The web view ignores the link.
-
decisionHandler(WKNavigationActionPolicyCancel);
-
}
-
// Otherwise the web view loads the link.
-
decisionHandler(WKNavigationActionPolicyAllow);
-
}
-
支付请求:
/// 创建
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
货币和区域信息:
request.currencyCode = @"CNY";
request.countryCode = @“CN";
request.merchantIdentifier = @"merchant.com.example”; ///
merchantIdentifier与之前在member center设置和Xcode中设置的相同
支付信息的摘要条目:
通过
PKPaymentSummaryItem
可以给用户描述不同的交易信息。主要包括:小计、折扣、物流运费、税和总计。当然如果你的app不涉及到额外的费用(如:运费和税),你可以只使用总计。然后在你app中的其它地方描述花费详细情况。
-
// 12.75 subtotal -
NSDecimalNumber*subtotalAmount=[NSDecimalNumberdecimalNumberWithMantissa:1275exponent:-2isNegative:NO];
-
self.subtotal=[PKPaymentSummaryItemsummaryItemWithLabel:@"Subtotal"amount:subtotalAmount];
-
// 2.00 discount
-
NSDecimalNumber*discountAmount=[NSDecimalNumberdecimalNumberWithMantissa:200exponent:-2isNegative:YES];
self.discount = [PKPaymentSummaryItem summaryItemWithLabel:@"Discount" amount:discountAmount];
Label:用来向用户描述该条目信息
amount: 对应该条目的花费情况。费用的货币单位都是使用之前
currencyCode设置的。
PS:记得对于打折或是优惠券的Item时amount的数字使用负数。同时由于使用double、float型的时会存在精度的缺失。所以关于支付方面苹果全部使用的是NSDecimalNumber类。
一般最后添加的Item都为总计花费(grand total)。总计花费的条目与其它的条目不同的是它以公司名称作为label。且它的amount是经过其它item中amount的计算得来。最后将所有的条目
summaryItems
赋值给
paymentSummaryItems
-
NSDecimalNumber*totalAmount=[NSDecimalNumberzero];
-
totalAmount=[totalAmountdecimalNumberByAdding:subtotalAmount];
-
totalAmount=[totalAmountdecimalNumberByAdding:discountAmount];
-
self.total=[PKPaymentSummaryItemsummaryItemWithLabel:@"My Company Name"amount:totalAmount];
-
self.summaryItems=@[self.subtotal,self.discount,self.total];
request.paymentSummaryItems = self.summaryItems;
快递物流方式:
当存在多种的快递方式时可以使用
PKShippingMethod类来创建不同的快递选项。
PKShippingMethod
*shipping = [
PKShippingMethod
summaryItemWithLabel
:
@
“普通
快递
"
amount
:[
NSDecimalNumber
zero
]];
shipping.detail = @"
快件将在两周内送达
"
;
shipping.
identifier
=
@“standard-express"
;
Label:同样描述该条目的意义,可以是普通快递、次日达等。
amount:快递花费。如果和上面代码一样则显示免费。需要多少运费需自行填写
detail:详细的快递描述。可以:24小时送达。等。自己斟酌
identifier:仅用于自己app来标识快递类型。易于调试(可选)。
PS:因为不同快递的有效范围和收费均有可能不同所以在用户每次更换地址和快递方式时需要更新花费信息。这些在下面的代理方法中讲解
指定支持的银行卡种和交易处理协议:(根据需要设置)
-
request.supportedNetworks = @[PKPaymentNetworkAmex,
PKPaymentNetworkChinaUnionPay, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa];
-
// Supports 3DS only 一般使用3DS
-
request.merchantCapabilities=PKMerchantCapability3DS;
-
// Supports both 3DS and EMV
-
request.merchantCapabilities=PKMerchantCapability3DS| PKMerchantCapabilityEMV;
账单地址设置:(根据需要设置可以不用)
-
request.requiredBillingAddressFields = PKAddressFieldEmail;
request.requiredBillingAddressFields = PKAddressFieldEmail | PKAddressFieldPostalAddress;
完成一次Apple Pay的支付主要是通过两个主体配合来完成:授权的控制器(
authorization view controller) 和 对应的代理对象(
delegate)。
controller主要完成两件事情:帮助用户完善账单和收货信息。再就是辅助用户完成授权支付。而delegate方法会在用户操作完成后进行相应的界面刷新。显示最新的信息。如在用户变更了账单和收货信息。或是完成支付时。代理的回调都会被触发。
PS:注意代理的方法。可能会触发多次。且代理的回调执行的顺序是依赖于用户的操作。
1.弹出支付授权窗口:
-
PKPaymentAuthorizationViewController*viewController=[[PKPaymentAuthorizationViewControlleralloc]initWithPaymentRequest:request];
-
if(!viewController){/* ... Handle error ... */ }
-
viewController.delegate=self;
-
[selfpresentViewController:viewControlleranimated:YEScompletion:nil];
///完成的代理回调
paymentAuthorizationViewControllerDidFinish:用户完成支付或者取消支付都会触发。
-
- (void) paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
-
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
当用户提供或是修改了运送信息:收货地址、配送方式(在真机下OC的代码好像在真机上才有效)
paymentAuthorizationViewController:didSelectShippingContact:completion:
和
paymentAuthorizationViewController:didSelectShippingMethod:completion:方法就会被调用。使用这两个方法进行支付请求的信息更新
-
-(void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController*)controller
-
didSelectShippingContact:(CNContact*)contact
-
completion:(void(^)(PKPaymentAuthorizationStatus,NSArray*,NSArray*))completion
-
{
-
self.selectedContact=contact;
-
[selfupdateShippingCost];
-
NSArray*shippingMethods=[selfshippingMethodsForContact:contact];
-
completion(PKPaymentAuthorizationStatusSuccess,shippingMethods,self.summaryItems);
-
}
-
-(void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController*)controller
-
didSelectShippingMethod:(PKShippingMethod*)shippingMethod
-
completion:(void(^)(PKPaymentAuthorizationStatus,NSArray*))completion
-
{
-
self.selectedShippingMethod=shippingMethod;
[self updateShippingCost];
completion(PKPaymentAuthorizationStatusSuccess, self.summaryItems);
}
支付授权完成:
当用户对一个支付请求进行授权时。相应的框架会通过苹果的服务器和设备自身的安全组件生成一个payment token。开发者可以通过
paymentAuthorizationViewController:didAuthorizePayment:completion:将Token返回我们自己的服务器中进行解析。也可以使用苹果合作的第三方(
https://developer.apple.com/apple-pay/
)进行解析。
授权的主要流程包括:
- 相应的框架将支付请求发送到设备的安全组件中。
- 安全组件将指定支付的卡和商户信息进行加密。然后发回给对应的框架。框架再将其发送到苹果服务器
- 苹果服务器会使用你的商户id证书进行再次加密然后返回到你的设备。这样此token只有你和使用共享你商户id证书的人可以读取。
- 当token返回时调用
paymentAuthorizationViewController:didAuthorizePayment:completion:代理方法
- - (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
-
didAuthorizePayment:(PKPayment *)payment
completion:(void (^)(PKPaymentAuthorizationStatus))completion
-
{
-
NSError*error;
-
ABMultiValueRefaddressMultiValue=ABRecordCopyValue(payment.billingAddress,kABPersonAddressProperty);
-
NSDictionary*addressDictionary=(__bridge_transferNSDictionary*)ABMultiValueCopyValueAtIndex(addressMultiValue,0);
-
NSData*json=[NSJSONSerializationdataWithJSONObject:addressDictionaryoptions:NSJSONWritingPrettyPrintederror:&error];
-
// ... Send payment token, shipping and billing address, and order information to your server ...
-
PKPaymentAuthorizationStatusstatus;// From your server
-
completion(status);
-
}
ps:参考代码下载(因为删除了对应的merchant需自行配置仅供参考):https://github.com/yuwuchiao/FishPay