Overview

With version 2 of our Web SDK, Universal Checkout automatically creates and handles Payments by default. This greatly reduces the complexity and amount of boilerplate required to integrate Primer.

For backward compatibility reasons, it is still possible to manually create and resume payments. Follow this guide to setup Universal Checkout so that you handle the payment lifecycle.

Flow

flow

  1. 1
    Generate a clientToken on your backend by creating a Client Session with POST/client-session
  2. 2
    Initialize Universal Checkout with the clientToken to render the UI.
  3. 3
    Universal Checkout will generate a paymentMethodToken when the customer submits their payment data, or when they select particular payment methods.
  4. 4
    Create a payment using the paymentMethodToken via the Payments API POST/payments
  5. 5
    If the response indicates a requiredAction, you'll get a new clientToken.
  6. 6
    Pass the clientToken back to Universal Checkout to render next steps, like 3DS, and get a resumeToken.
  7. 7
    Call POST/payments/{id}/resume with the resumeToken to resume the payment and wrap things up. (If a new requiredAction is returned, you'll have to go back to step 5.)

Generate a Client Token

Get an API Key

You require an API Key to talk with our APIs. Head to the Developers area to manage your API keys.

When your account is created, we will also create an API token automatically. You can use this API key to get started.

Only client_tokens:write is required as the scope of the key.

Never share your API Key, only your backend should have access to it.

Find out more about API Keys in our API Reference

Generate a Client Session

Client token

A client session is the starting point for integrating payments at Primer. You can attach all the metadata associated with the order to the client session, and generate a clientToken, a temporary key used to initialize Universal Checkout.

The information you include in the client session is used in the Dashboard to conditionally route payments with Workflows, and activate payment methods and other features in Universal Checkout, so pass as much information as you can.

The X-Api-Version specifies the API version information. Earlier, this was supposed to be a date. For example, 2021-10-19.

This has changed post API version v2 which was represented by 2021-09-27 date.

Starting API version v2.1, the X-Api-Version needs to provide the API version as 2.1.

Depending upon the API version specified in the client-session request, your client-session will be processed accordingly with requisite features and options that are available for that version.

See API Reference Changelog for details.

Here is how the client session request to the Primer API should look like:

POST/client-session
12345678910111213
# Generate a client token with cURLcurl --location --request \  POST 'https://api.sandbox.primer.io/client-session' \  --header 'X-Api-Key: <YOUR_API_KEY>' \  --header 'X-Api-Version: 2021-10-19' \  --header 'Content-Type: application/json' \  --data '{    "orderId": "<YOUR_ORDER_ID>",    "currencyCode": "GBP",    "amount": 1200,    "customerId": "<YOUR_CUSTOMER_ID>",    "order": { "countryCode": "GB" }  }'
bash
copy

Example Response

12345678910
{  "clientToken": "<THE_CLIENT_TOKEN>",  "clientTokenExpirationDate": "2021-08-12T16:14:08.578695",  "orderId": "<YOUR_ORDER_ID>",  "currencyCode": "GBP",  "amount": 1200,  "customerId": "<YOUR_CUSTOMER_ID>",  "metadata": {},  "warnings": []}
JSON
copy
ℹ️
Make sure to pass all the information required by the payment methods and features activated on your Dashboard.

As a rule of thumb, pass as much information as you can when creating the client session. As a minimum, make sure to pass:

  • orderId
  • currencyCode
  • amount
  • order.countryCode

The clientToken is a key concept within Primer. You may receive a client token from various places but as long as you pass it to the SDK, Universal Checkout knows where to start/resume the flow.

Set up Universal Checkout

Step 1. Install the SDK

With CocoaPods

The iOS SDK is available with Cocoapods. Just add the PrimerSDK pod and run pod install .

123456
target 'MyApp' do  # Other pods...
  # Add this to your Podfile  pod 'PrimerSDK', '~> 2.0.0' # Add this lineend
ruby
copy

Then run pod install to install PrimerSDK on your workspace.

For specific versions of the SDK, please refer to the changelog.

In case you encounter an error that the bundle needs signing on Xcode 14, add the following post-install script in your podfile.

123456789
post_install do |installer|  installer.pods_project.targets.each do |target|    if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"      target.build_configurations.each do |config|          config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'      end    end  endend
ruby
copy

With Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into Xcode. In order to add PrimerSDK with Swift Package Manager;

  1. 1
    Select your project, and then navigate to Package Dependencies
  2. 2
    Click on the + button at the bottom-left of the Packages section
  3. 3
    Paste https://github.com/primer-io/primer-sdk-ios.git into the Search Bar
  4. 4
    Press Add Package
  5. 5
    Let Xcode download the package and set everything up

schema

Step 2. Initialize the SDK

Import the Primer SDK and set its delegate as shown in the following example. You have to set paymentHandling: .manual in the PrimerSettings.

1234567891011121314151617181920212223
import PrimerSDK
class MyViewController: UIViewController {
    override func viewDidLoad() {        super.viewDidLoad()
        // Set the paymentHandling to manual.        let settings = PrimerSettings(paymentHandling: .manual)        Primer.shared.configure(settings: settings, delegate: self)    }}
extension MyViewController: PrimerDelegate {
    func primerDidTokenizePaymentMethod(_ paymentMethodTokenData: PrimerPaymentMethodTokenData, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {        // Use this method to create a payment.    }
    func primerDidResumeWith(_ resumeToken: String, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {        // If needed, use this method to resume the previously created payment.    }}
swift
copy
🛠

Check the SDK API here to customize your SDK settings.

Step 3. Generate a client token

💡

Check our guide on how to set up the client session here.

Make an API call to your backend to fetch a client token. Here is a simple example of how it can be done from your view controller. Once successful store your client token for future use.

12345678
extension MyViewController {
    func requestClientToken(data: [String: Any], completion: @escaping (String?, Error?) -> Void) {        Networking.createClientSession(data: data) { (clientToken, err) in            completion(clientToken, err)        }    }}
swift
copy

Step 4. Show Universal Checkout

At this step you should have a client token available. Call the showUniversalCheckout(clientToken:) function (as shown below) to present the Universal Checkout.

123456
class MyViewController: UIViewController {
    func startUniversalCheckout() {        Primer.shared.showUniversalCheckout(clientToken: self.clientToken)    }}
swift
copy

Step 5. Handle callbacks for creating and resuming payments

Once the payment method data has been securely captured, Primer will return a uniform paymentMethodTokenData via primerDidTokenizePaymentMethod(:decisionHandler:). This can be safely passed to your server to create a payment with the Payments API.

Handle primerDidTokenizePaymentMethod(:decisionHandler:)

  • When a customer submits their payment data, the payment details are tokenized and you'll receive a paymentMethodTokenData in primerDidTokenizePaymentMethod.

  • Create a payment request with the payment method token data.

  • If the payment is successful, call decisionHandler(.succeed()) in order to display a success screen.

  • If the payment is unsuccessful, call decisionHandler(.fail(withErrorMessage: "YOUR_ERROR_MESSAGE")) to display an error / failed message.

  • Payments API may return a requiredAction with a new clientToken for additional steps. In this case, call decisionHandler(.continueWithNewClientToken(clientToken)).

12345678910111213141516171819202122
// use this method to create a payment.func primerDidTokenizePaymentMethod(_ paymentMethodTokenData: PrimerPaymentMethodTokenData, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {    // ...    // Call the resumeHandler to continue    // ...
    // Send the payment method token data to your server to create a payment    sendPaymentMethodToken(token: paymentMethodTokenData) { (res, err) in        if let err = err {            decisionHandler(.fail(withErrorMessage: "YOUR_ERROR_MESSAGE"))        } else if let res = res {            guard let requiredActionDic = res["requiredAction"] as? [String: Any],                  let clientToken = requiredActionDic["clientToken"] as? String            else {                decisionHandler(.succeed())                return            }
            decisionHandler(.continueWithNewClientToken(clientToken))        }    }}
swift
copy

Once the the above operation has finished, the SDK will return an error or a new token through its delegate. You can use this token to resume the payment, i.e. send this token to your backend.

Handle primerDidResumeWith(:decisionHandler:)

  • You will receive a resumeToken via the primerDidResumeWith(:decisionHandler:) callback if applicable.

  • Send a resume payment request with resumeToken

  • If the payment is successful, call decisionHandler(.succeed()) in order to display a success screen.

  • If the payment is unsuccessful, call decisionHandler(.fail(withErrorMessage: "YOUR_ERROR_MESSAGE")) to display an error / failed message.

  • Payments API may return a requiredAction with a new clientToken for additional steps. In this case, call decisionHandler(.continueWithNewClientToken(clientToken)).

123456789
func primerDidResumeWith(_ resumeToken: String, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {    sendResumeToken(resumeToken) { (res, err) in        if let err = err {            decisionHandler(.continueWithNewClientToken(clientToken))        } else if let res = res {            decisionHandler(.succeed())        }    }}
swift
copy
🚀

You should now be able to see Universal Checkout! The user can now interact with Universal Checkout, and you can create the payment.