结账 4.3.7

结账 4.3.7

结账.com 集成Mobile SDK 团队 @结账 维护。



 
依赖
CheckoutEventLoggerKit~> 1.2.4
风险= 3.0.2
 

结账 4.3.7

  • 作者:
  • 结账.com 集成

Frames iOS

CocoaPods Compatible GitHub release (latest by date) GitHub release (latest by date)

Bitrise Platform license

要求

  • 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包管理器),但如果出于任何原因这不适合您,我们也支持CocoapodsCarthage

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 的示例。

 

开始

本节假设您已完成初始集成。如果您还没有,请先完成集成

1. 导入 Frames

如果不确定在哪里做,显示旅程的 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)
    }
}

 

5. 使用我们的PaymentFormFactory来生成ViewController

使用第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上

请注意,自行构建UI可以将您置于PCI合规性的较高等级。为了保持在PCI合规性的最低级别,我们建议使用以下列出的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.swiftCustomStyle1.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_urlfailure_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