Frames iOS
要求
- iOS 12.0+
- Xcode 12.4+
- Swift 5.3+
文档
Frames for iOS 为 Checkout.com 的支付基础设施对其消费者数据进行 tokenization。我们抽象了接收付款、构建支付 UI 和处理敏感数据的复杂性。
-
集成: 指导如何在您的 iOS 应用中消费我们的 SDK
-
示例项目: 我们在测试每种提供的分发方法的同时创建了项目,以展示我们 SDK 中可用的一系列功能
-
入门: 开始测试您在应用程序 UI 中可以实现的内容
-
个性化: 自定义 UI 成为您的应用的一部分
-
其他功能: 我们如何帮助解决 Apple Pay 和 3D Secure 挑战
-
迁移:迁移:如果您之前使用过3.5.x版本(或更低版本)
更多集成信息可以在Checkout文档中找到。
您可以在我们的Jazzy文档上找到Frames API引用(点击这里)。
集成
我们已经尽力支持iOS上最常见的分发方法。我们强烈推荐使用SPM(Swift包管理器),但如果出于任何原因这不适合您,我们也支持Cocoapods和Carthage。
Swift包管理器
Swift包管理器与Swift构建系统集成,以自动下载、编译和链接依赖项的过程。自从Xcode 11以来,它应该能够在最新的Xcode项目中直接使用,并在近年来得到了社区的广泛支持。这是我们为Frames iOS推荐的分发方法,也是最容易集成、更新和构建的一种。
如果您以前从未使用过它,请从Apple的将包依赖项添加到您的应用程序的逐步指南开始。只需在添加依赖项时使用此存储库的URL(https://github.com/checkout/frames-ios)即可。
CocoaPods
CocoaPods是Apple项目的传统依赖项管理器。我们支持它,但建议使用SPM,因为它是Apple首选的依赖项管理器。
确保您的机器上已安装cocoapods,请运行
$ pod --version
任何版本高于1.10.0的都是好消息。如果您尚未安装或不受支持,请按照Cocoapods入门指南操作。
当您的机器上有有效版本的 Cocoapods 时,要将 Frames 集成到您的 Xcode 项目中,请更新您的 Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '12.0'
use_frameworks!
# PhoneNumberKit has stopped publishing Cocoapods releases to Public Registry
# This workaround enables application to get the releases straight from source repository
pod 'PhoneNumberKit', :git => 'https://github.com/marmelroy/PhoneNumberKit'
target '<Your Target Name>' do
pod 'Frames', '~> 4'
end
然后,在终端中运行以下命令
$ pod install
要更新您的现有 Cocoapods 依赖项,请使用
$ pod update
Carthage
Carthage 是一个去中心化的依赖项管理器,它构建您的依赖项并提供二进制框架。
如果您尚未安装,请安装 Carthage
要使用 Carthage 将 Frames 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它
github "checkout/frames-ios" ~> 4.0
运行 carthage update --use-xcframeworks
来构建框架,并将构建好的 Frames
拖入您的 Xcode 项目。
示例项目
我们的示例应用程序展示了我们预构建的 UI 和我们的 SDK 如何工作。您可以通过克隆存储库(直接通过 git 或使用建议的集成方法)来本地运行此项目。
我们的示例应用程序还测试了支持的集成方法(SPM、Cocoapods、Carthage),因此如果您在该处有任何问题,它们应提供可工作的示例。您可以在存储库的根目录中找到它们,分别位于相应的文件夹中
- iOS 示例框架(使用 Cocoapods 分发)
- iOS 示例框架 SPM(SPM 分发)
- iOS 示例框架 Carthage(Carthage 分发)
运行后,您将找到主屏幕并带有多个设计选项。我们尝试制作对比明显的支付 UI 来给您一个想法。我们还尝试以最简单的方式编写代码来跟踪和了解每个 UI 味道是如何创建的。只需从 HomeViewController.swift
开始,并跟随代码中的按钮动作(《@IBAction`)即可获取一些关于我们如何实现那些 UI 的示例。
开始
本节假设您已完成初始集成。如果您还没有,请先完成集成。
Frames
1. 导入 如果不确定在哪里做,显示旅程的 ViewController 是一个好起点
import Frames
2. 准备负责框架配置的对象
这是逻辑配置
- 确保您收到请求数据的访问权限
- 在我们输入阶段预先验证支持的计划
- 预填用户信息(可选,但如果能够提供,可能会大大提升用户体验)
/**
This is optional and can use nil instead of this property.
But if you can provide these details for your user you can
- make their checkout experience easier by prefilling fields they may need to do
- improve acceptance success for card tokenisation
*/
let country = Country(iso3166Alpha2: "GB")
let address = Address(
addressLine1: "221B Baker Street",
addressLine2: "Marylebone",
city: "London",
state: "London",
zip: "NW1 6XE",
country: country)
let phone = Phone(number: "+44 2072243688",
country: country)
let billingFormData = BillingForm(
name: "Amazing Customer",
address: address,
phone: phone)
let configuration = PaymentFormConfiguration(
apiKey: "<Your Public Key>",
environment: .sandbox,
supportedSchemes: [.visa, .maestro, .mastercard],
billingFormData: billingFormData)
3. 准备UI的样式
我们将在稍后再介绍 个性化设置,现在我们将使用默认样式
// Style applied on Card input screen (Payment Form)
let paymentFormStyle = DefaultPaymentFormStyle()
// Style applied on Billing input screen (Billing Form)
let billingFormStyle = DefaultBillingFormStyle()
// Frames Style
let style = PaymentStyle(
paymentFormStyle: paymentFormStyle,
billingFormStyle: billingFormStyle)
4. 准备流程完成后的响应
如果用户未取消流程完成流程,将调用完成处理程序,如果成功则带有卡令牌,如果失败则带有错误
let completion: ((Result<TokenDetails, TokenRequestError>) -> Void) = { result in
switch result {
case .failure(let failure):
if failure == .userCancelled {
// Depending on needs, User Cancelled can be handled as an individual failure to complete, an error, or simply a callback that control is returned
print("User has cancelled")
} else {
print("Failed, received error", failure.localizedDescription)
}
case .success(let tokenDetails):
print("Success, received token", tokenDetails.token)
}
}
PaymentFormFactory
来生成ViewController
5. 使用我们的使用第2、3和4步中的属性,现在创建ViewController
let framesViewController = PaymentFormFactory.buildViewController(
configuration: configuration, // Step 2
style: style, // Step 3
completionHandler: completion // Step 4
)
6. 向用户展示ViewController
我们已创建所需的ViewController,以启用您的用户的完整令牌化。让我们展示它。
/**
We are assuming you started the Walkthrough from the presenting ViewController
and that a Navigation Controller is available
You will need to make minor adjustments otherwise.
For the best experience we recommend embedding the presenting ViewController inside an UINavigationController
*/
navigationController?.pushViewController(framesViewController, animated: true)
自定义样式
所有自定义需要在创建ViewController之前完成。一旦将样式提交到工厂,所做的任何更改都不会反映在UI上
按照复杂程度的顺序,我们从以下开始
修改默认样式
在我们的入门示例中,我们使用了默认样式以快速完成工作。如果您主要寻找的就是这些,那么您可能会很高兴地知道,每个组件都是可变的,您可以轻松地自定义单个属性。这也在我们Factory.swift
中的示例项目内作为示例使用,在getDefaultPaymentViewController
方法中。
示例
var paymentFormStyle = DefaultPaymentFormStyle()
// Change background of page
paymentFormStyle.backgroundColor = UIColor.darkGray
// Change card number input placeholder value
paymentFormStyle.expiryDate.textfield.placeholder = "00 / 00"
// Add custom border style around the Payment Button
if var payButton = paymentFormStyle.payButton as? DefaultPayButtonFormStyle {
let payButtonBorder = DefaultBorderStyle(
cornerRadius: 26,
borderWidth: 3,
normalColor: .black,
focusColor: .clear,
errorColor: .red,
corners: [.bottomLeft, .topRight])
payButton.borderStyle = payButtonBorder
paymentFormStyle.payButton = payButton
}
// Change Payment button text
paymentFormStyle.payButton.text = "Pay £54.63"
如果您需要重写许多值,我们不推荐这种做法,因为这需要您逐一识别并更改每个属性。但这对小幅调整可能适用。
使用主题
在我们的演示项目中,我们还在ThemeDemo.swift
中演示了这种方法。通过主题,我们的目标是提供一个设计系统,您可以使用它通过提供数量很少的属性来创建完整的UI样式,我们将这些属性共享到子组件。由于您可能不完全同意我们的映射,您仍然可以在之后单独更改每个组件(如修改默认示例所示)。
// Declare the theme object with the minimum required properties
var theme = Theme(
primaryFontColor: UIColor(red: 0 / 255, green: 204 / 255, blue: 45 / 255, alpha: 1),
secondaryFontColor: UIColor(red: 177 / 255, green: 177 / 255, blue: 177 / 255, alpha: 1),
buttonFontColor: .green,
errorFontColor: .red,
backgroundColor: UIColor(red: 23 / 255, green: 32 / 255, blue: 30 / 255, alpha: 1),
errorBorderColor: .red)
// Add border and corner radius around text inputs
theme.textInputBackgroundColor = UIColor(red: 36 / 255.0, green: 48 / 255.0, blue: 45 / 255.0, alpha: 1.0)
theme.textInputBorderRadius = 4
// Build complete payment form by providing only texts
var paymentFormStyle = theme.buildPaymentForm(
headerView: theme.buildPaymentHeader(title: "Payment details",
subtitle: "Accepting your favourite payment methods"),
addBillingButton: theme.buildAddBillingSectionButton(text: "Add billing details",
isBillingAddressMandatory: false,
titleText: "Billing details"),
billingSummary: theme.buildBillingSummary(buttonText: "Change billing details",
titleText: "Billing details"),
cardNumber: theme.buildPaymentInput(isTextFieldNumericInput: true,
titleText: "Card number",
errorText: "Please enter valid card number"),
expiryDate: theme.buildPaymentInput(textFieldPlaceholder: "__ / __",
isTextFieldNumericInput: false,
titleText: "Expiry date",
errorText: "Please enter valid expiry date"),
securityCode: theme.buildPaymentInput(isTextFieldNumericInput: true,
titleText: "CVV date",
errorText: "Please enter valid security code"),
payButton: theme.buildPayButton(text: "Pay now"))
// Override a custom property from the resulting payment form style
paymentFormStyle.payButton.disabledTextColor = UIColor.lightGray
let billingFormStyle = theme.buildBillingForm(
header: theme.buildBillingHeader(title: "Billing information",
cancelButtonTitle: "Cancel",
doneButtonTitle: "Done"),
cells: [.fullName(theme.buildBillingInput(text: "", isNumericInput: false, isMandatory: false, title: "Your name")),
.addressLine1(theme.buildBillingInput(text: "", isNumericInput: false, isMandatory: true, title: "Address")),
.city(theme.buildBillingInput(text: "", isNumericInput: false, isMandatory: true, title: "City")),
.country(theme.buildBillingCountryInput(buttonText: "Select your country", title: "Country")),
.phoneNumber(theme.buildBillingInput(text: "", isNumericInput: true, isMandatory: true, title: "Phone number"))])
我们认为这种方法在UI的精细控制和简单的、简洁的代码之间取得了很好的平衡。字体大小甚至使用了preferredFont(forTextStyle: ...).pointSize
以给您提供与您用户的设备首选项匹配的字体大小。然而,如果您仍然认为映射需要过多的定制,我们最终的方案可能更适合您的口味。
声明所有组件
这绝对不是一种简单的方法,但这确实是完全定制每个属性并发现定制程度的方法,当您在导航中你会发现。您在演示项目中可以找到Style.swift
和CustomStyle1.swift
文件,它们遵循这种方法。
如果决定这样做,请尝试
- 编译器帮助。Xcode的自动完成可以帮助您从最高级别导航到最低级别的定制选项
let style = PaymentStyle(paymentFormStyle: <#T##PaymentFormStyle#>,
billingFormStyle: <#T##BillingFormStyle#>)
- 协议是关键词。从上述代码开始,参数将一直传递到最低级别的协议对象。
// You will need to prepare your objects that conform to the required protocols
struct MyPaymentFormStyle: PaymentFormStyle {
var backgroundColor: UIColor = ...
var headerView: PaymentHeaderCellStyle = ...
var editBillingSummary: BillingSummaryViewStyle? = ...
var addBillingSummary: CellButtonStyle? = ...
var cardholderInput: CellTextFieldStyle? = ...
var cardNumber: CellTextFieldStyle = ...
var expiryDate: CellTextFieldStyle = ...
var securityCode: CellTextFieldStyle? = ...
var payButton: ElementButtonStyle = ...
}
// Then feed them to your end PaymentStyle
let style = PaymentStyle(paymentFormStyle: MyPaymentFormStyle(),
billingFormStyle: <#T##BillingFormStyle#>)
其他特性
处理3D Secure
当你从服务器发送3D Secure扣费请求时,你将收到一个3D Secure URL。这个URL可以从JSON响应中的_links.redirect.href
获取。然后,你可以将3D Secure URL传递给ThreedsWebViewController
以处理验证。
重定向URL(success_url
和failure_url
)在Checkout.com中心设置,但可以从你服务器发送的扣费请求中覆盖。提供正确的URL对于确保支付流程成功至关重要。
让我们设想我们现在正在YourViewController.swift
内部工作,并处理3DS挑战
// Ensure you know the fail & success URLs
private enum Constants {
static let successURL = URL(string: "http://example.com/success")!
static let failureURL = URL(string: "http://example.com/failure")
}
// Prepare the service
let checkoutAPIService = CheckoutAPIService(publicKey: "<Your Public Key>", environment: .sandbox)
// Create the ThreedsWebViewController
let threeDSWebViewController = ThreedsWebViewController(
checkoutAPIService: checkoutAPIService,
// If the payment response provided new success_url or failure_url, use those. Otherwise default to Checkout provided values as documented previously
successUrl: serverOverridenSuccessURL ?? Constants.successURL,
failUrl: serverOverridenFailureURL ?? Constants.failureURL)
threeDSWebViewController.delegate = self
threeDSWebViewController.authURL = challengeURL // This is coming from the payment response
// Present threeDSWebViewController
present(threeDSWebViewController, animated: true, completion: nil)
之前我们已经添加了threeDSWebViewController.delegate = self
这一行。这将引发编译器错误,因为我们现在需要让YourViewController
符合所需协议。这样做,我们能够找到挑战的结果并做出相应的反应
extension YourViewController: ThreedsWebViewControllerDelegate {
func threeDSWebViewControllerAuthenticationDidSucceed(_ threeDSWebViewController: ThreedsWebViewController, token: String?) {
// Congratulations, the Challenge was successful !
threeDSWebViewController.dismiss(animated: true, completion: nil)
}
func threeDSWebViewControllerAuthenticationDidFail(_ threeDSWebViewController: ThreedsWebViewController) {
// Oooops, the payment failed !
threeDSWebViewController.dismiss(animated: true, completion: nil)
}
}
使用Apple Pay
我们可以处理来自Apple Pay的PKPayment
令牌数据,这会将Apple Pay令牌转换为Checkout.com令牌,以便你在后端支付。
// Prepare the service
let checkoutAPIService = CheckoutAPIService(publicKey: "<Your Public Key>", environment: .sandbox)
func handle(payment: PKPayment) {
// Get the data containing the encrypted payment information.
let paymentData = payment.token.paymentData
// Request an Apple Pay token.
checkoutAPIService.createToken(.applePay(ApplePay(paymentData))) { result in
switch result {
case .success(let tokenDetails):
// Congratulations, payment token is available
case .failure(let error):
// Ooooops, an error ocurred. Check `error.localizedDescription` for hint to what went wrong
}
}
}
电话号码验证
账单地址的电话号码验证将使用设备本地设置的电话号码的前缀。例如,英国号码将自动加上+44前缀。
如果用户想要输入一个与设备本地不同的电话号码(例如,当设备本地设置为英国时输入美国号码),在输入电话号码时应首先提供国家代码(例如,+1)。
从3.X.X迁移到4.X.X
3DS和Apple Pay处理不受影响,因此使用它们应该仍然可以正常工作。
如果您正在使用4.0.0版本之前的Frames iOS(我们强烈建议使用最新版本),此更新将引入破坏性更改,需要一些开发时间。
由于我们努力大幅改善Frames的用户界面并使您能够对其进行高度自定义,所以所需的处理方法也大不相同。这将需要您
- 从代码库中移除旧版本Frames的使用。这可能是移除一个屏幕或创建仅用于支持旧版本Frames的几个其他对象的好机会!
- 开始使用
我们想指出v4+版本给我们的SDK带来的巨大利益,例如
- 可根据用户在支付流程中无缝过渡而自定义的用户界面
- 更新的并改进的验证逻辑,与我们的支持的卡片支付方式一致
- 使用我们更新的用户界面为您的客户提供额外的安全好处
许可证
Frames iOS采用MIT许可证发布。有关详细信息,请参阅LICENSE。