玩转苹果自带支付

当你决定在线买一些东西的时候,可能会有一种现代特有的焦虑感涌上心头。虽然没有精确的单词来表达这种焦虑,但如果只想简单的描述的话,可以是:“我的信用卡在哪里?它的号码是多少?我真的需要买这个东西吗?”

当你在一个iOS设备上购物时,这种令人抓狂的感觉会放大:你很有可能没有随身携带你的信用卡,而且手里拿着信用卡还要在手机上输入信息这种操作相当有难度,我觉得应该留给体操选手和宇航员用来展示他们高超的技艺(当然,我是在开玩笑,但是我也愿意打赌苹果公司已经在某个实验室做过这个测试)。

如果你是一位开发者,并且你的App里接受信用卡付款,这个不利的因素将直接影响你的收入。

Apple Pay改变了这一切。尽管很多线下商店把它们的大部分注意力集中在实体付款(如客户可以使用他们的iPhone在支持NFC的终端付款),但除此之外,iOS开发者同样获得了一个极好的机会去提高他们App里的支付体验。

提醒:如果你在你的APP中销售的是电子产品或者虚拟货币,你应该使用内购方式而不是App Pay去销售你的东西(见的App Store Review Guidelines 11.2节)。你可以使用Apple Pay销售你的实体商品和服务。

获取苹果商户ID(Merchant ID)

在做任何测试之前,你必须先注册一个苹果商户ID。而在你做这件事之前,你还需要选择一个支付提供商用来处理你的信用卡流程。苹果公司在他们的Apple Pay开发者页面提供了一份推荐的公司名单(注:我在Stripe公司工作,这个公司是推荐名单中的一个,但本文中的代码不依赖于你选择的任何特定供应商)。你的供应商应该有一个详细的指导,用来告诉你在他们的平台如何设置和使用Apple Pay,整个流程将是这样的:

1.前往苹果开发者中心的Certificates, Identifiers, and Profiles部分并且创建一个新的商家ID。
2.接下来,前往选择证书菜单,并创建一个新的苹果支付证书。这需要向苹果公司上传证书签名请求(CSR)。当你注册一个支付处理,他们通常会为你提供一个CSR使用。你可以使用CSR通过这个指导生成自己私有的证书,但你的付款处理程序将无法解密它,你需要在以后重新生成。
3.在Xcode中,打开你的项目设置中的“Capabilities”部分,然后将“Apple Pay”选项打开。你可能需要从提供的列表中选择之前创建的商家ID。

创建第一次交易

Apple Pay只支持可以使用Apple Pay的iOS设备(如iPhone6/6+,iPad Mini 3,iPad Air 2)。此外,你需要先添加苹果支付授权,才能在你的应用程序中进行测试(在“获取苹果商家ID”中所述)。如果你想在模拟器上模拟它的行为,你可以在Github上找到一个模仿它的功能(测试信用卡的详细消息)的测试库

一旦你准备好了商家帐户,那么开始使用Apple Pay将会非常简单。当你的验证超时时,你首先需要先看你正在运行的设备是否支持Apple Pay,接着看你的客户是否已经将信用卡添加在Passbook:

let paymentNetworks = [PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa]
if PKPaymentAuthorizationViewController.canMakePaymentsUsingNetworks(paymentNetworks) {
    // ?Pay is available!
} else {
    // Show your own credit card form.
}

假设Apple Pay是可用的,下一个步骤将是调用PKPaymentRequest。它是描述你从客户那里要求收取的费用。如果你的付款请求发生在美国,这里你需要设置一些默认选项,以后也无需改变:

let request = PKPaymentRequest()
request.supportedNetworks = [PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa]
request.countryCode = "US"
request.currencyCode = "USD"
request.merchantIdentifier = "#Replace me with your Apple Merchant ID#"
request.merchantCapabilities = .Capability3DS

接下来,使用paymentSummaryItem属性来描述用户真正买的商品。这需要包含一系列的PKPaymentSummaryItem所组成的数组,这个数组包括标签和数量。他们类似于收据上的行项目(这个是我们立刻就可以看到)。

let wax = PKPaymentSummaryItem(label: "Mustache Wax", amount: NSDecimalNumber(string: "10.00"))
let discount = PKPaymentSummaryItem(label: "Discount", amount: NSDecimalNumber(string: "-1.00"))

let totalAmount = wax.amount.decimalNumberByAdding(discount.amount)
                            .decimalNumberByAdding(shipping.amount)
let total = PKPaymentSummaryItem(label: "NSHipster", amount: totalAmount)

request.paymentSummaryItems = [wax, discount, shipping, total]

请注意,这里您可以指定零或负数价格,用做优惠券的使用或其它信息。然而,总量的要求是必须大于零的。你将会注意到,我们使用PKShippingMethod这个方法(从PKPaymentSummaryItem继承)来描述我们的送货方式。下面我们会更详细的讲解。

接下来,我们结合 PKPaymentRequest创建PKPaymentAuthorizationViewController的示例来向客户展示支付清单 (在这个例子中,所有这些代码都位于隐藏在支付背后的UIViewController里面)。

let viewController = PKPaymentAuthorizationViewController(paymentRequest: request)
viewController.delegate = self
presentViewController(viewController, animated: true, completion: nil)

一些需要注意的地方:

1.视图控制器不完全占据屏幕(在这种情况下,蓝色的背景是我们应用程序的一部分)。你可以通过更新后台视图控制器让PKPaymentAuthorizationViewController可见。
2.所有的文本自动大写。
3.把最后一行从剩余部分分离出来的目的是显示你的总收入。标签将自动在前面加上“PAY”,所以这里通常使用公司名称。
4.整个UI是通过Remote View Controller来展现的。这意味着,在你给的PKPaymentRequest之外,以其他的方式展现或修改这个视图的内容是不可能的。
PKPaymentAuthorizationViewControllerDelegate

实际上为了处理由PKPaymentAuthorizationViewController返回的付款信息,您需要实现PKPaymentAuthorizationViewControllerDelegate这个协议。它有两个必须实现的方法,分别如下:
-(void)paymentAuthorizationViewController:didAuthorizePayment:completion:
-(void)paymentAuthorizationViewControllerDidFinish:

要了解这些方法的工作原理,我们需要看看一个Apple Pay交易具体是如何工作的:

1.写一个如上所述的PKPaymentAuthorizationViewController。
2.客户同意使用Touch ID购买(或者在失败了3次之后通过输入自己的密码购买)。
3.指纹图标变成一个带有“Processing”的旋转标签
4.你的代理将接收paymentAuthorizationViewController:didAuthorizePayment:completion: callback回调。
5.你的应用程序与付款进程进行异步通信,网站后台实际上是对这些付款细节的代办。一旦付款结束,你根据返回的结果调用PKPaymentAuthorizationStatus.Success或PKPaymentAuthorizationStatus.Failure以完成处理。
6.把PKPaymentAuthorizationViewController旋转动画到成功或失败图标。如果成功的话,用户将会收到一个从PassBook发出的表明从用户信用卡消费的通知。
7.你的代理会接收paymentAuthorizationViewControllerDidFinish:方法的回调。它是负责调用用来切换支付页面的dismissViewControllerAnimated:这个方法的。

// MARK: - PKPaymentAuthorizationViewControllerDelegate

func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController!, didAuthorizePayment payment: PKPayment!, completion: ((PKPaymentAuthorizationStatus) -> Void)!) {
    // Use your payment processor‘s SDK to finish charging your customer.
    // When this is done, call completion(PKPaymentAuthorizationStatus.Success)
}

func paymentAuthorizationViewControllerDidFinish(controller: PKPaymentAuthorizationViewController!) {
    dismissViewControllerAnimated(true, completion: nil)
}

在这里, processPayment:payment completion: 这个方法是你自己的代码,并会利用你的支付处理程序中的SDK来完成交易。

动态的送货方式和价格

如果你的客户使用Apple Pay购买实体商品,你可能要为他们提供不同送货选项。你可以在PKPaymentRequest这个方法设置shippingMethods选项做到这一点。然后,你可以执行PKPaymentAuthorizationViewControllerDelegate代理中的可选方法paymentAuthorizationViewController:didSelectShippingMethod:completion:给用户的选择作出响应。这个方法遵循类似上述didAuthorizePayment方法描述的模式,在这里你可以通过这个异步回调更新PKPaymentSummaryItem数组,这个数组中包含客户所需的送货方式。 (还记得继承自PKPaymentSummaryItem的PKShippingMethod方法吗?在这里是非常有用的!)

下面是我们之前示例的修改版本,作为视图控制器和辅助函数的计算属性实现:

var paymentRequest: PKPaymentRequest {
    let request = ... // initialize as before

    let freeShipping = PKShippingMethod(label: "Free Shipping", amount: NSDecimalNumber(string: "0"))
    freeShipping.identifier = "freeshipping"
    freeShipping.detail = "Arrives in 6-8 weeks"

    let expressShipping = PKShippingMethod(label: "Express Shipping", amount: NSDecimalNumber(string: "10.00"))
    expressShipping.identifier = "expressshipping"
    expressShipping.detail = "Arrives in 2-3 days"

    request.shippingMethods = [freeShipping, expressShipping]
    request.paymentSummaryItems = paymentSummaryItemsForShippingMethod(freeShipping)

    return request
}

func paymentSummaryItemsForShippingMethod(shipping: PKShippingMethod) -> ([PKPaymentSummaryItem]) {
    let wax = PKPaymentSummaryItem(label: "Mustache Wax", amount: NSDecimalNumber(string: "10.00"))
    let discount = PKPaymentSummaryItem(label: "Discount", amount: NSDecimalNumber(string: "-1.00"))

    let totalAmount = wax.amount.decimalNumberByAdding(discount.amount)
                                .decimalNumberByAdding(shipping.amount)
    let total = PKPaymentSummaryItem(label: "NSHipster", amount: totalAmount)

    return [wax, discount, shipping, total]
}

// MARK: - PKPaymentAuthorizationViewControllerDelegate

func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController!, didSelectShippingMethod shippingMethod: PKShippingMethod!, completion: ((PKPaymentAuthorizationStatus, [AnyObject]!) -> Void)!) {
    completion(PKPaymentAuthorizationStatus.Success, paymentSummaryItemsForShippingMethod(shippingMethod))
}

在这个例子中,客户会选择免费配送或快递,随着他们选择的改变,价格也会相应的调整。

别急,后面还有更多!

与其提供一些固定费率的配送选项,你可以让用户自行选择送货地址,并在其基础上动态的计算运费。为了达到目的,首先你需要在你的PKPaymentRequest方法中设定要求的requiredShippingAddressFields属性。这可以是电子邮件,电话号码和地址的任意组合。

另外,如果你不需要用户的详细通讯地址而是需要收集一些联系方式(如发送收据的email地址),这样做是一个很好的方法。

当设置了送货地址这个字段,将在支付用户界面出现一个新的“送货地址”,以便允许客户选择之前保存的地址。每次用户选择时,paymentAuthorizationViewController:didSelectShippingAddress:completion:将消息发送到你的PKPaymentAuthorizationViewControllerDelegate代理。

在这里,你应该为选择的地址计算相应的费用,然后调用带有3个参数的completion回调:

回调的结果

1.如果成功调用PKPaymentAuthorizationStatus.Success
2.如果出现连接错误调用PKPaymentAuthorizationStatus.Failure
3.如果API返回一个空的数组调用InvalidShippingPostalAddress (即该收货地址是不可用的)
4.数组PKShippingMethods代表用户可用的收货地址。
5.新数组PKPaymentSummaryItems包含一个送货方法。

我已经搭建了一个非常简单的用来查询给定地址运费的EasyPost API的Web后台。源码 获得。

这里则是一个查询此API的函数,用了Alamofire

import AddressBook
import PassKit
import Alamofire

func addressesForRecord(record: ABRecord) -> [[String: String]] {
    var addresses: [[String: String]] = []
    let values: ABMultiValue = ABRecordCopyValue(record, kABPersonAddressProperty).takeRetainedValue()
    for index in 0.. Void) {
    let parameters = [
        "street": address[kABPersonAddressStreetKey] ?? "",
        "city": address[kABPersonAddressCityKey] ?? "",
        "state": address[kABPersonAddressStateKey] ?? "",
        "zip": address[kABPersonAddressZIPKey] ?? "",
        "country": address[kABPersonAddressCountryKey] ?? ""
    ]

    Alamofire.request(.GET, "http://example.com", parameters: parameters)
             .responseJSON { (_, _, JSON, _) in
                if let rates = JSON as? [[String: String]] {
                    let shippingMethods = map(rates) { (rate) -> PKShippingMethod in
                        let identifier = rate["id"]
                        let carrier = rate["carrier"] ?? "Unknown Carrier"
                        let service = rate["service"] ?? "Unknown Service"
                        let amount = NSDecimalNumber(string: rate["amount"])
                        let arrival = rate["formatted_arrival_date"] ?? "Unknown Arrival"

                        let shippingMethod = PKShippingMethod(label: "\(carrier) \(service)", amount: amount)
                        shippingMethod.identifier = identifier
                        shippingMethod.detail = arrival

                        return shippingMethod
                    }
                }
             }
}

有了这个,就可以简单的实现PKPaymentAuthorizationViewControllerDelegate这个代理:

func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController!, didSelectShippingAddress record: ABRecord!, completion: ((PKPaymentAuthorizationStatus, [AnyObject]!, [AnyObject]!) -> Void)!) {
    if let address = addressesForRecord(record).first {
        fetchShippingMethodsForAddress(address) { (shippingMethods) in
            switch shippingMethods?.count {
            case .None:
                completion(PKPaymentAuthorizationStatus.Failure, nil, nil)
            case .Some(0):
                completion(PKPaymentAuthorizationStatus.InvalidShippingPostalAddress, nil, nil)
            default:
                completion(PKPaymentAuthorizationStatus.Success, shippingMethods, self.paymentSummaryItemsForShippingMethod(shippingMethods!.first!))
            }
        }
    } else {
        completion(PKPaymentAuthorizationStatus.Failure, nil, nil)
    }
}

现在,用户可以根据他们的居住地址来选择收货地址和方式。他们最终选择的shippingAddress和shippingMethod将在paymentAuthorizationViewController:didAuthorizePayment:completion:方法中作为PKPayment的属性。

这篇文章中的所有源码公布在 https://github.com/jflinter/ApplePayExample

尽管Apple Pay只公开了少量的API,但是它的可用范围十分广泛,你可以在你的App中自定义适当的结账流程。它甚至允许你建立新的流程,如让用户不需要创建账号就能买东西。

随着越来越多的应用开始使用Apple Pay(并且越来越多的用户拥有了支持它的设备),我相信它将成为iOS应用中一种很普遍的支付方式。

有什么技术问题可以加我微信:

时间: 2024-07-30 06:21:37

玩转苹果自带支付的相关文章

带你玩转Visual Studio——带你高效管理代码

上一篇文章带你玩转Visual Studio--带你高效开发通过对VAssistX优秀插件的讲解,让我们掌握了快速开发C++代码的技能.然而大部分的程序都不是一个人的开发的,是由一个团队的多个人一起开发的,大型的系统还可能由多个不同的团队分包进行开发.多人进行协作开发时,代码的管理就显得及为重要,需要借助代码管理工具的辅助,这种工具又称为版本控制系统. 目前主流的版本控制系统有: CVS:是一个用于代码版本控制的自由软件,它是一个比较早出现的工具,由于它有很多自身的缺陷,现在几乎被SVN所取代了

带你玩转Visual Studio——带你发布自己的工程库

上一篇文章带你玩转Visual Studio--带你高效管理代码通过对VisualSVN优秀插件的讲解,让我们掌握了在集成开发环境VS中快捷高效地管理代码的技能.然而我们开发的程序并不总是直接地生成可执行的软件,我们可能只是开发某个大型系统的一个组件,也可能是开发某个软件的内核SDK提供给上层的应用程序调用,在开发的过程中我们也可能会用到第三方的开源库.那如果将自己的程序编译成程序库给调用方用呢?又如何在自己的程序中引用第三方库呢?这将是这篇文章要讲的内容--发布自己的工程库. 什么是程序库?

带你玩转Visual Studio——带你了解VC++各种类型的工程

上一篇文章带你玩转Visual Studio--带你新建一个工程一文中提到新建一个工程时会有很多的工程类型(图1),现在将简单介绍各种类型工程的含义和主要用途.由于这里包含的工程类型太多,有很多本人也没有接触过,有些可能理解的不太对的地方还请谅解. 图 1:New Project 理解几个概念 在开讲之前先大概理解几个概念,这是理解后面各种工程含义的基础. COM COM(Component Object Model)组件对象模型是microsoft制定的一个组件软件标准,跟unix上的CORB

iOS苹果自带UIMenuController

一.UIMenuController认识 1.默认情况下,UITextView / UITextFiled / UIWebView 都有苹果自带的有UIMenuController功能 2.UITextFiled 的弹出菜单效果系统自带的      3.当然,系统只给某些控件提供的该功能,但是我们自己给可以给指定控件添加该功能效果,至于如何添加,我们先简单学习一下,如何使用系统提供的UIMenuController功能 二.UIMenuController基本使用 如何学UIMenuContro

转: 带你玩转Visual Studio——带你理解多字节编码与Unicode码

上一篇文章带你玩转Visual Studio——带你跳出坑爹的Runtime Library坑帮我们理解了Windows中的各种类型C/C++运行时库及它的来龙去脉,这是C++开发中特别容易误入歧途的一个地方,我们对它进行了总结和归纳.本篇文章我们将继续讲解C++开发中容易混淆的另一个概念——多字节字符集与Unicode字符集. 多字节字符与宽字节字符 char与wchar_t 我们知道C++基本数据类型中表示字符的有两种:char.wchar_t. char叫多字节字符,一个char占一个字节

ios 苹果自带的get请求

一.API简介 如果你想在iOS中发送Http请求的话,有很多方式可以选择,我在这里介绍几个常见的: 1.苹果自带的API 1> Core Foundation框架中的CFNetwork API:纯C语言的API,性能非常高 2> Foundation框架中的NSURLConnection API:Objective-C的API,性能也不错,简单易用 2.第3方开源框架 1> ASIHttpRequest 2> AFNetworking 我们这个专题主要来研究NSURLConnec

ios开发中如何调用苹果自带地图导航

前段时间一直在赶项目,在外包公司工作就是命苦,天天加班不说,工作都是和工期合同挂钩的,稍微逾期就有可能被扣奖金,不谈这些伤脑筋的事情了,让我们说说iOS开发中如何调用苹果手机自带的地图. 学习如逆水行舟,不进则退.古人告诉我们要不断的反思和总结,日思则日精,月思则月精,年思则年精.只有不断的尝试和总结,才能让我们的工作和生活更加轻松愉快和美好.连着做了两个大的商城外包项目,智慧城市,搜牧通,花费了近四个月的时间,终于在反复修改后完美收工.期间的困难自不必说,以后多多总结和沟通吧.百度地图的使用之

上班我是这样玩微信的,带你一起玩?

上班我是这样玩微信的,带你一起玩? 手机用途多多,有人用来工作,有人用来聊天,有人用来记录生活,比如我就喜欢用手机拍照,咱不像专业摄影师那样牛X,但也能通过各种角度的抓取,每天也有那么几张自己感觉还不错的照片. 拍了照片肯定是想分享到朋友圈让大家伙看看咯,但是自打实习之后咱分享照片也不能那么随心所欲目中无人了,领导咱还是得尊重嘛,不然搁哪挣钱吃饭,照片还是得分享,我得刷刷存在感呐,让关注我的人知道我的动态. 大家以前都用QQ,电脑就行了,但现在微信都得使用手机,这样的话上班时间看手机就太明显了,

不止是联网!教你玩转PC自带Wi-Fi网卡

前言:Wi-Fi对于现在的智能手机来说已经是再熟悉不过的配置了,而主板自带Wi-Fi网卡的设计也越来越普及,但有些玩家可能思维还停留在“Wi-Fi网卡 = 连无线网络用的网卡,我用有线就不需要”的层次,因此我们今天就来跟大家谈谈,PC自带Wi-Fi网卡到底能够实现什么样的功能. 不止是联网!教你玩转PC自带Wi-Fi网卡 ●很多主板都自带了Wi-Fi无线网卡 七彩虹 战斧 C.A88AK旗舰版配备板载Wi-Fi网卡 Wi-Fi无线网卡以往一般常见于ITX尺寸的迷你主板上面,主要为了提升主板在客厅