iOS checkout page

Introduction

Elements team has built a simple checkout page designed using Elements' core SDK + TreasureUI (FlexibleUI) SDK. TreasurePay is a host that combines both of the SDKs and implemented basic functionalities of a payment checkout page.

Requirements

The Elements iOS Checkout UI a.k.a TreasurePay SDK requires Xcode 11 or later and is compatible with apps targeting iOS 13 or above.

Configuration

TreasurePay

TreasurePay class is the main interface that communicates between client application and SDK. You can initialize TreasurePay in the following way.

let elementsEnv = Environment.sandbox(clientToken: "Your client token goes here...") // this is elements' payment sdk environment.
let treasureEnv = TreasureEnvironment.sandbox(clientToken: "Your client token goes here...") // this is TreasureUI sdk environment.
let treasurePay = TreasurePay(configuration: .init(elementsEnvironment: elementsEnv , treasureEnvironment: treasureEnv))

TreasureDependencies

Similar to TreasureUI sdk, you can apply the same dependencies to TreasurePay.

ExternalActionHandler

final class PaymentFlowViewController: UIViewController {
  
 init() {
   ...
   super.init(nibName: nil, bundle: nil)
   ...
   treasurePay.externalActionHandler = self
 }
  
  private func replaceScreen(viewController: UIViewController) {
    viewController.willMove(toParent: self)
    addChild(viewController)
    view.addSubview(viewController.view)
    viewController.view.snp.remakeConstraints { make in
      make.edges.equalToSuperview()
    }
    currentViewController?.willMove(toParent: nil)
    currentViewController?.view.removeFromSuperview()
    currentViewController?.removeFromParent()
    viewController.didMove(toParent: self)
    currentViewController = viewController
  }
}

extension PaymentFlowViewController: TreasureHostActionHandler {

  // Load context from your backend.
  private func loadContext(completion: @escaping (Context) -> Void) {
    apiClient.perform(FetchContextRequest()) { [weak self] result in
      switch result {
      case .success(let response):
        completion(response.context)
      case .failure(let error):
        print(error)
      }
    }
  }
  
  /// Handle external action received from Elements SDK
  /// - Parameter externalAction: action param returned from Elements SDK
  ///
  /// External actions could be
  ///   - set a payment method as default
  ///   - remove a payment method
  ///   - ... from what business defined in the schema
  /// When received the action from treasure sdk, we should perform business side logic
  /// i.e. trigger network request calls, then refresh the view.
  /// Example:
  ///   1. User wants to remove a payment method
  ///   2. Elements SDK received user's tapping event.
  ///   3. Upon tapped, this function will get triggered and a type string **remove_payment_method**
  ///      with additional data param is returned (including payment method id and etc.)
  ///   4. We trigger `paymentMethodsViewModel.paymentAPIService.deletePaymentMethod(paymentMethod.paymentMethodId)`
  ///   5. Backend will perform logic related to deleting a payment method + refresh the screen
  ///      to get latest payment methods from the user.
  ///   6. Pass back the data to Elements SDK and it will render the new screen.
  private func handle(externalAction: ExternalAction) {
    guard let actionType = TreasurePayExampleActions(rawValue: externalAction.actionType) else {
      print("Error: undecodable external action type.")
      return
    }
    switch actionType {
    case .refreshContext:
      loadContext(completion: { [weak self] context in
        self?.treasurePay.setContext(context: context)
      })
    case .refreshView:
      self.loadContext(completion: { [weak self] context in
        self?.treasurePay.load(completion: { [weak self] vc in
          self?.treasurePay.setContext(context: context)
          self?.replaceScreen(viewController: vc)
        })
      })
    }
  }
}

Start TreasurePay

  1. First you need to load business side context from your backend.
  2. Now we can load the checkout ui resources from TreasurePay
  3. Finally after we obtained both the context and checkout ui resource file, we can replace our current screen with the viewController that TreasurePay returns upon completion.
override func viewDidLoad() {
  super.viewDidLoad()
  // ... do ui related work (setupUI) etc.

  self.loadContext(completion: { [weak self] context in
    self?.treasurePay.load(completion: { [weak self] vc in
      self?.treasurePay.setContext(context: context)
      self?.replaceScreen(viewController: vc)
    })
  })
}

Example App

Clone this repo and run pod install and then open TreasurePay.xcworkspace. The demo app demonstrated how to use TreasurePay .