> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Manual payment creation

export const ApiEndpoint = ({method, path}) => {
  const getMethodColor = method => {
    switch (method?.toUpperCase()) {
      case "GET":
        return "#0285c7";
      case "POST":
        return "#008d71";
      case "PUT":
        return "#16a34a";
      case "PATCH":
        return "#808080";
      case "DELETE":
        return "#dc2626";
      default:
        return "#6b7280";
    }
  };
  const getEndpointUrl = (method, path) => {
    const endpoints = {
      "POST /client-session": "/api-reference/v2.4/api-reference/client-session-api/create-a-client-session",
      "PATCH /client-session": "/api-reference/v2.4/api-reference/client-session-api/update-client-session",
      "POST /payments": "/api-reference/v2.4/api-reference/payments-api/create-a-payment",
      "POST /payments/<YOUR-PAYMENT-ID>/resume": "/api-reference/v2.4/api-reference/payments-api/resume-payment",
      "POST /payments/<YOUR-PAYMENT-ID>/adjust-authorization": "/api-reference/v2.4/api-reference/payments-api/adjust-authorized-amount",
      "POST /payments/<PAYMENT_ID>/cancel": "/api-reference/v2.4/api-reference/payments-api/cancel-payment",
      "POST /payments/<PAYMENT_ID>/refund": "/api-reference/v2.4/api-reference/payments-api/refund-payment",
      "GET /payments/<PAYMENT_ID>": "/api-reference/v2.4/api-reference/payments-api/get-a-payment",
      "GET /payment-instruments": "/api-reference/v2.4/api-reference/payment-methods-api/list-saved-payment-methods",
      "DELETE /payment-instruments/<PAYMENT_METHOD_TOKEN>": "/api-reference/v2.4/api-reference/payment-methods-api/delete-payment-method-payment-methods-token-delete"
    };
    const key = `${method?.toUpperCase()} ${path}`;
    return endpoints[key] || "#";
  };
  const getPrettyPath = (method, path) => {
    const pathMapping = {
      "POST /payments/<YOUR-PAYMENT-ID>/resume": "/payments/{paymentId}/resume",
      "POST /payments/<YOUR-PAYMENT-ID>/adjust-authorization": "/payments/{paymentId}/adjust-authorization",
      "POST /payments/<PAYMENT_ID>/cancel": "/payments/{paymentId}/cancel",
      "POST /payments/<PAYMENT_ID>/refund": "/payments/{paymentId}/refund",
      "GET /payments/<PAYMENT_ID>": "/payments/{paymentId}",
      "DELETE /payment-instruments/<PAYMENT_METHOD_TOKEN>": "/payment-instruments/{paymentMethodToken}"
    };
    const key = `${method?.toUpperCase()} ${path}`;
    return pathMapping[key] || path;
  };
  const methodColor = getMethodColor(method);
  const url = getEndpointUrl(method, path);
  const displayPath = getPrettyPath(method, path);
  return <div style={{
    display: "inline-flex",
    alignItems: "center",
    cursor: "pointer",
    margin: "2px 0",
    padding: "4px",
    paddingRight: "6px",
    background: "rgb(245, 245, 245)",
    borderRadius: "6px",
    whiteSpace: "nowrap",
    textDecoration: "none"
  }}>
      <a href={url} target="_blank" rel="noopener noreferrer" style={{
    textDecoration: "none",
    border: "none"
  }}>
        <span style={{
    display: "inline-block",
    whiteSpace: "nowrap",
    padding: "0 8px",
    fontSize: "13px",
    lineHeight: "23px",
    borderRadius: "6px",
    fontWeight: 400,
    backgroundColor: methodColor,
    color: "white",
    border: "none"
  }}>
          {method.toUpperCase()}
        </span>
        <code style={{
    padding: "0",
    marginLeft: "4px",
    fontFamily: "monospace",
    color: methodColor,
    border: "none",
    textDecorationColor: methodColor
  }}>
          {displayPath}
        </code>
      </a>
    </div>;
};

## 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

<Frame caption="Manual payment creation flow">
  <img src="https://goat-assets.production.core.primer.io/marketing/checkout/external-docs/advanced/flow.png" />
</Frame>

1. Generate a `clientToken` on your backend by creating a *Client Session* with <ApiEndpoint method="POST" path="/client-session" />
2. Initialize Universal Checkout with the `clientToken` to render the UI.
3. Universal Checkout will generate a `paymentMethodToken` when the customer submits their payment data, or when they select particular payment methods.
4. Create a payment using the `paymentMethodToken` via the Payments API <ApiEndpoint method="POST" path="/payments" />
5. If the response indicates a `requiredAction`, you'll get a new `clientToken`.
6. Pass the `clientToken` back to Universal Checkout to render next steps, like 3DS, and get a `resumeToken`.
7. Call <ApiEndpoint method="POST" path="/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

Ensure you have [an API key configured](/api-reference/get-started/authentication). Make sure to set the following scopes for your API Key:

* `client_tokens:write`
* `transactions:authorize`

### Generate a Client Session

Follow the instructions [here](/checkout/client-session#create-a-client-session) to create a client session, which will be used to initialize the checkout.

## Set up Universal Checkout

<Tabs>
  <Tab title="Web">
    ### Step 1. Turn off automatic payment creation

    The Universal Checkout option `paymentHandling` defines how the SDK should handle payment creation and resume.

    Set `paymentHandling` to `MANUAL` to turn off automatic payment handling.

    This disables the callbacks `onBeforePayment` and `onCheckoutComplete`.

    ```typescript Typescript theme={"dark"}
    Primer.showUniversalCheckout(clientToken, {
    	/* Other options... */

    	paymentHandling: 'MANUAL',
    })
    ```

    ### Step 2. Handle callbacks for creating and resuming payments

    Two callbacks must be implemented:

    * `onTokenizeSuccess()` to create payments with `paymentMethodToken`
    * `onResumeSuccess()` to resume payments with `resumeToken`

    <br />

    ```typescript Typescript theme={"dark"}
    Primer.showUniversalCheckout(clientToken, {
    	/* Other options... */

    	paymentHanding: 'MANUAL',
    	async onTokenizeSuccess(paymentMethodTokenData, handler) {
    		// Send the Payment Method Token to your server
    		// to create a payment using Payments API
    		const response = await createPayment(paymentMethodTokenData.token)

    		// Call `handler.handleFailure` to cancel the flow and display an error message
    		if (!response) {
    			return handler.handleFailure('The payment failed. Please try with another payment method.')
    		}

    		// If a new clientToken is available, call `handler.continueWithNewClientToken` to refresh the client session.
    		// The checkout will automatically perform the action required by the Workflow.
    		if (response.requiredAction.clientToken) {
    			return handler.continueWithNewClientToken(response.requiredAction.clientToken)
    		}

    		// Display the success screen
    		return handler.handleSuccess()
    	},

    	async onResumeSuccess(resumeTokenData, handler) {
    		// Send the resume token to your server to resume the payment
    		const response = await resumePayment(resumeTokenData.resumeToken)

    		// Call `handler.handleFailure` to cancel the flow and display an error message
    		if (!response) {
    			return handler.handleFailure('The payment failed. Please try with another payment method.')
    		}

    		// If a new clientToken is available, call `handler.continueWithNewClientToken` to refresh the client session.
    		// The checkout will automatically perform the action required by the Workflow
    		if (response.requiredAction.clientToken) {
    			return handler.continueWithNewClientToken(response.requiredAction.clientToken)
    		}

    		// Display the success screen
    		return handler.handleSuccess()
    	},
    })
    ```

    #### Handle `onTokenizeSuccess()` callback

    * When a customer submits their payment data, the payment details are tokenized and you'll receive a `paymentMethodToken` in `onTokenizeSuccess()`
    * [Create a payment request](/api-reference/v2.4/api-reference/payments-api/create-a-payment) with the `paymentMethodToken`
    * If the payment is successful, call `handler.handleSuccess()` in order to display a success screen.
    * If the payment is unsuccessful, call `handler.handleFailure(errorMessage)` to display an error / failed message.
    * Payments API may return a new `clientToken` for additional steps (in the `requiredActions` on the response). In this case, call `handler.continueWithNewClientToken(clientToken)` to the checkout.

    #### Handle `onResumeSuccess()` callback

    <Warning>
      Handling `onResumeSuccess()` is required to fully support 3DS and the majority of payment methods.
    </Warning>

    * You will receive a `resumeToken` via the `onResumeSuccess()` callback if applicable
    * [Send a resume payment request](/api-reference/v2.4/api-reference/payments-api/resume-a-payment) with the `resumeToken`
    * If the payment is successful, call `handler.handleSuccess()` in order to display a success screen.
    * If the payment is unsuccessful, call `handler.handleFailure(errorMessage)` to display an error / failed message.
    * Payments API may *again* return a new `clientToken` for additional steps. In this case, call `handler.continueWithNewClientToken(clientToken)` again.
  </Tab>

  <Tab title="Android - Kotlin">
    ### Step 1. Install

    Add the following to your `app/build.gradle` file

    ```kotlin KOTLIN theme={"dark"}
    repositories {
      mavenCentral()
    }

    dependencies {
      implementation 'io.primer:android:latest.version'
    }
    ```

    For more details about SDK versions, please see our [changelog](/changelogs/android-sdk).

    It is highly recommended to add following settings to your `app/build.gradle` file:

    ```kotlin KOTLIN theme={"dark"}
    android {
        kotlinOptions {
            freeCompilerArgs += '-Xjvm-default=all'
        }
    }
    ```

    ### Step 2. Initialize the SDK

    Prepare the `PrimerCheckoutListener` that will handle the callbacks that happen during the lifecycle.

    Import the Primer SDK and set its listener as shown in the following example.

    In order to use manual payment handling, you have to set `paymentHandling` to `**PrimerPaymentHandling.MANUAL**` in the `PrimerSettings`.

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutActivity : AppCompatActivity() {

        private val listener = object : PrimerCheckoutListener {

            override fun onTokenizeSuccess(
                paymentMethodToken: PrimerPaymentMethodToken,
                decisionHandler: PrimerResumeDecisionHandler
            ) {
                  // use the received paymentMethodToken to make the payment
                  // and use decisionHandler to instruct SDK about next steps based on payment status
            }

            override fun onResumeSuccess(resumeToken: String, decisionHandler: PrimerResumeDecisionHandler) {
                 // use the received resumeToken to resume the payment
                 // and use decisionHandler to instruct SDK about next steps based on payment status
            }
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            configureCheckout()
        }

        private fun configureCheckout() {
            // Set the paymentHandling to manual
            val settings = PrimerSettings(paymentHandling = PrimerPaymentHandling.MANUAL)
            Primer.instance.configure(settings, listener)
        }
    }
    ```

    <Note>
      Check the SDK API [here](https://www.notion.so/primerio/API-Reference-e58ee6d184ff4cd7ae2f90c6b28aaaba) to customize your SDK settings.
    </Note>

    ### Step 3. Generate a client token

    <Tip>
      For more information on generating a client token, take a look at our *[client session guide](#generate-a-client-token).*
    </Tip>

    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 activity:

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutActivity : AppCompatActivity() {

        // Other code goes here

        private lateinit var viewModel: CheckoutViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            configureCheckout()
            setupViewModel()
            setupObservers()
            fetchClientToken()
        }

        private fun setupViewModel() {
            viewModel = ViewModelProvider(this).get(CheckoutViewModel::class.java)
        }

        private fun fetchClientToken() = viewModel.fetchClientToken()

        private fun setupObservers() {
            viewModel.clientToken.observe(this) { clientToken ->
                // show checkout
            }
        }
    }
    ```

    Your view model code may look something like this:

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutViewModel : ViewModel() {

        private val _clientToken = MutableLiveData<String>()
        val clientToken: LiveData<String> = _clientToken

        fun fetchClientToken() {
            // fetch your token here (ask your backend to provide token)
            _clientToken.postValue("retrieved_token")
        }
    }
    ```

    ### Step 4. Show **Universal Checkout**

    When the client token is retrieved, show Universal Checkout.

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutActivity : AppCompatActivity() {

        // other code goes here

        private fun setupObservers() {
            viewModel.clientToken.observe(this) { clientToken ->
                showUniversalCheckout(clientToken)
            }
        }

        private fun showUniversalCheckout(clientToken: String) {
            Primer.instance.showUniversalCheckout(this, clientToken)
        }
    }
    ```

    <Note>
      You should now be able to see Universal Checkout! The user can now interact with Universal Checkout.
    </Note>

    ### Step 5. Handle callbacks for creating and resuming payments

    Once the payment method data has been securely captured, Primer will return a uniform `paymentMethodToken` via `onTokenizeSuccess` that can be safely passed to your server to create a payment with the Payments API.

    #### Handle `onTokenizeSuccess` callback

    * When a customer submits their payment data, the payment details are tokenized and you'll receive a `paymentMethodToken` in `onTokenizeSuccess`.

    * [Create a payment request](/api-reference/v2.4/api-reference/payments-api/create-a-payment) with the payment method token data.

    * If the payment is successful, call `decisionHandler.handleSuccess()` in order to display a success screen.

    * If the payment is unsuccessful, call `decisionHandler.handleFailure("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)`.

    ```kotlin KOTLIN theme={"dark"}
    private val listener = object: PrimerCheckoutListener {

        override fun onTokenizeSuccess(
            paymentMethodToken: PrimerPaymentMethodToken,
            decisionHandler: PrimerResumeDecisionHandler
        ) {
            viewModel.sendPaymentMethodToken(paymentMethodToken, decisionHandler)
        }
    }
    ```

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutViewModel : ViewModel() {

      // ⚠️ remember to call decisionHandler to resume SDK flow.
      fun sendPaymentMethodToken(paymentMethodToken: PrimerPaymentMethodToken, decisionHandler: PrimerResumeDecisionHandler)  {
        val paymentResponse =  //...
        if (paymentResponse.isSuccessful()) {
          decisionHandler.handleSuccess()
        } else if (paymentResponse.isPending()) {
          decisionHandler.continueWithNewClientToken(paymentResponse.requiredAction.clientToken)
        } else {
          decisionHandler.handleFailure("Your error message.")
        }
      }
    }
    ```

    #### Handle `onResumeSuccess` callback

    Once the required actions are completed, Primer will return `resumeToken` via `onResumeSuccess` that can be safely passed to your server to resume a payment with the Payments API.

    * You will receive a `resumeToken` via the `onResumeSuccess()` callback if applicable.

    * [Send a resume payment request](/api-reference/v2.4/api-reference/payments-api/resume-a-payment) with `resumeToken`

    * If the payment is successful, call `decisionHandler.handleSuccess()` in order to display a success screen.

    * If the payment is unsuccessful, call `decisionHandler.handleFailure("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)`.

    ```kotlin KOTLIN theme={"dark"}
    private val listener = object : PrimerCheckoutListener {

            override fun onResume(resumeToken: String, decisionHandler: PrimerResumeDecisionHandler) {
                viewModel.sendResumeToken(resumeToken, decisionHandler)
            }
        }
    ```

    ```kotlin KOTLIN theme={"dark"}
    class CheckoutViewModel : ViewModel() {

      // ⚠️ remember to call decisionHandler to resume SDK flow.
      fun sendResumeToken(resumeToken: String, decisionHandler: PrimerResumeDecisionHandler)  {
        val paymentResponse =  //...
        if (paymentResponse.isSuccessful()) {
          decisionHandler.handleSuccess()
        } else if (paymentResponse.isPending()) {
          decisionHandler.continueWithNewClientToken(paymentResponse.requiredAction.clientToken)
        } else {
          decisionHandler.handleFailure("Your error message.")
        }
      }
    }
    ```
  </Tab>

  <Tab title="Android - Java">
    ### Step 1. Install

    Add the following to your `app/build.gradle` file

    ```java Java theme={"dark"}
    repositories {
      mavenCentral()
    }

    dependencies {
      implementation 'io.primer:android:latest.version'
    }
    ```

    For more details about SDK versions, please see our [changelog](/changelogs/android-sdk).

    ### Step 2. Initialize the SDK

    Prepare the `PrimerCheckoutListener` that will handle the callbacks that happen during the lifecycle.

    Import the Primer SDK and set its listener as shown in the following example.

    In order to use manual payment handling, you have to set `paymentHandling` to `**PrimerPaymentHandling.MANUAL**` in the `PrimerSettings`.

    ```java Java theme={"dark"}
    public class CheckoutActivity extends AppCompatActivity {

        private final PrimerCheckoutListener listener = new PrimerCheckoutListener() {

            @Override
            public void onTokenizeSuccess(@NonNull PrimerPaymentMethodToken primerPaymentMethodToken,
                                          @NonNull PrimerResumeDecisionHandler decisionHandler) {
                  // use the received paymentMethodToken to make the payment
                  // and use decisionHandler to instruct SDK about next steps based on payment status
            }

            @Override
            public void onResumeSuccess(@NonNull String resumeToken,
                                 @NonNull PrimerResumeDecisionHandler decisionHandler) {
                 // use the received resumeToken to resume the payment
                 // and use decisionHandler to instruct SDK about next steps based on payment status
            }
        };

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            configureCheckout();
        }

        private void configureCheckout() {
            // Set the paymentHandling to manual
            PrimerSettings settings = new PrimerSettings();
            settings.setPaymentHandling(PrimerPaymentHandling.MANUAL);

            Primer.getInstance().configure(settings, listener);
        }
    }
    ```

    <Note>
      Check the SDK API [here](https://www.notion.so/primerio/API-Reference-e58ee6d184ff4cd7ae2f90c6b28aaaba) to customize your SDK settings.
    </Note>

    ### Step 3. Generate a client token

    <Tip>
      For more information on generating a client token, take a look at our *[client session guide](#generate-a-client-token).*
    </Tip>

    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 activity:

    ```java Java theme={"dark"}
    public class CheckoutActivity extends AppCompatActivity {

    private CheckoutViewModel viewModel;

    @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            configureCheckout();
            setupViewModel();
            setupObservers();
            fetchClientToken();
        }

        private void setupViewModel() {
            viewModel = new ViewModelProvider(this).get(CheckoutViewModel.class);
        }

        private void fetchClientToken() {
            viewModel.fetchClientToken();
        }

        private void setupObservers() {
            viewModel.clientToken.observe(this, clientToken -> {

            });
        }
    }
    ```

    Your view model code may look something like this:

    ```java Java theme={"dark"}
    public class CheckoutViewModel extends ViewModel {

        private final MutableLiveData<String> _clientToken = new MutableLiveData<>();
        public LiveData<String> clientToken = _clientToken;

        public void fetchClientToken() {
            // fetch your token here (ask your backend to provide token)
            _clientToken.postValue("retrieved_token");
        }
    }
    ```

    ### Step 4. Show **Universal Checkout**

    When the client token is retrieved, show Universal Checkout.

    ```java Java theme={"dark"}
    public class CheckoutActivity extends AppCompatActivity {

        // other code goes here

        private void setupObservers() {
              viewModel.clientToken.observe(this, this::showUniversalCheckout);
        }

        private void showUniversalCheckout(String clientToken) {
              Primer.getInstance().showUniversalCheckout(this, clientToken);
        }
    }
    ```

    <Note>
      You should now be able to see Universal Checkout! The user can now interact with Universal Checkout.
    </Note>

    ### Step 5. Handle callbacks for creating and resuming payments

    Once the payment method data has been securely captured, Primer will return a uniform `paymentMethodToken` via `onTokenizeSuccess` that can be safely passed to your server to create a payment with the Payments API.

    #### Handle `onTokenizeSuccess` callback

    * When a customer submits their payment data, the payment details are tokenized and you'll receive a `paymentMethodToken` in `onTokenizeSuccess`.

    * [Create a payment request](/api-reference/v2.4/api-reference/payments-api/create-a-payment) with the payment method token data.

    * If the payment is successful, call `decisionHandler.handleSuccess()` in order to display a success screen.

    * If the payment is unsuccessful, call `decisionHandler.handleFailure("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)`.

    ```java Java theme={"dark"}
      private final PrimerCheckoutListener listener = new PrimerCheckoutListener() {

        @Override
            public void onTokenizeSuccess(@NonNull PrimerPaymentMethodToken primerPaymentMethodToken,
                                          @NonNull PrimerResumeDecisionHandler decisionHandler) {

                viewModel.sendPaymentMethodToken(primerPaymentMethodToken)
                // ⚠️ remember to call decisionHandler to resume SDK flow.
            }
        };
    ```

    #### Handle `onResumeSuccess` callback

    Once the required actions are completed, Primer will return `resumeToken` via `onResumeSuccess` that can be safely passed to your server to resume a payment with the Payments API.

    * You will receive a `resumeToken` via the `onResumeSuccess()` callback if applicable.

    * [Send a resume payment request](/api-reference/v2.4/api-reference/payments-api/resume-a-payment) with `resumeToken`

    * If the payment is successful, call `decisionHandler.handleSuccess()` in order to display a success screen.

    * If the payment is unsuccessful, call `decisionHandler.handleFailure("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)`.

    ```java Java theme={"dark"}
      private final PrimerCheckoutListener listener = new PrimerCheckoutListener() {

        @Override
            public void onResumeSuccess(@NonNull String resumeToken,
                                 @NonNull PrimerResumeDecisionHandler primerResumeDecisionHandler) {
                viewModel.sendResumeToken(resumeToken)
                // ⚠️ remember to call decisionHandler to resume SDK flow.
            }
        };
    ```
  </Tab>

  <Tab title="iOS">
    ### Step 1. Install the SDK

    #### With CocoaPods

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

    ```ruby RUBY theme={"dark"}
    target 'MyApp' do
      # Other pods...

      # Add this to your Podfile
      pod 'PrimerSDK', '~> 2.0.0' # Add this line
    end
    ```

    Then run `pod install` to install `PrimerSDK` on your workspace.

    For specific versions of the SDK, please refer to the [changelog](/changelogs/ios-sdk).

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

      ```ruby RUBY theme={"dark"}
      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
        end
      end
      ```
    </Note>

    #### 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. Select your project, and then navigate to `Package Dependencies`
    2. Click on the **+** button at the bottom-left of the `Packages` section
    3. Paste [https://github.com/primer-io/primer-sdk-ios.git](https://github.com/primer-io/primer-sdk-ios.git) into the **Search Bar**
    4. Press **Add Package**
    5. Let Xcode download the package and set everything up

    <Frame>
      <img src="https://mintcdn.com/primer-cc826789/dM-udzSll5kL79ou/images/checkout/advanced/spm-3.png?fit=max&auto=format&n=dM-udzSll5kL79ou&q=85&s=ab14396cc8485f895bf2bfa319b41e6f" alt="schema" width="960" height="538" data-path="images/checkout/advanced/spm-3.png" />
    </Frame>

    ### 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`.

    ```swift SWIFT  theme={"dark"}
    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.
        }
    }
    ```

    <Note>
      Check the SDK API [here](https://www.notion.so/primerio/API-Reference-f62b4be8f24642989e63c25a8fb5f0ba) to customize your SDK settings.
    </Note>

    ### Step 3. Generate a client token

    <Tip>
      Check our guide on how to set up the client session [here](#generate-a-client-token).
    </Tip>

    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.

    ```swift SWIFT theme={"dark"}
    extension MyViewController {

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

    ### 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.

    ```swift SWIFT theme={"dark"}
    class MyViewController: UIViewController {

        func startUniversalCheckout() {
            Primer.shared.showUniversalCheckout(clientToken: self.clientToken)
        }
    }
    ```

    ### 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](/api-reference/v2.4/api-reference/payments-api/create-a-payment) 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))`.

    ```swift SWIFT theme={"dark"}
    // 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))
            }
        }
    }
    ```

    Once 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](/api-reference/v2.4/api-reference/payments-api/resume-a-payment) 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))`.

    ```swift SWIFT theme={"dark"}
    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())
            }
        }
    }
    ```

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

  <Tab title="React Native">
    ### Step 1. Turn off automatic payment creation

    The Universal Checkout option `paymentHandling` defines how the SDK should handle payment creation.

    Set `paymentHandling` to `MANUAL` to turn off automatic payment handling. This will allow you to create the payment yourself (via your backend).

    This disables the callback `onCheckoutComplete`.

    ```typescript Typescript theme={"dark"}
    import {
    	Primer,
    	PrimerSettings,
    	PrimerCheckoutData,
    	PrimerTokenizationHandler,
    	PrimerResumeHandler,
    	PrimerPaymentMethodTokenData,
    } from '@primer-io/react-native'

    const CheckoutScreen = (props: any) => {
    	const onUniversalCheckoutButtonTapped = async () => {
    		try {
    			const settings: PrimerSettings = {
    				paymentHandling: 'MANUAL',
    				/* Other options and callbacks */
    			}

    			await Primer.configure(settings)
    		} catch (err) {
    			// Handle error
    		}
    	}
    }
    ```

    <Note>
      See our [SDK API Reference](https://www.notion.so/1ce697cf1de6425794df9feeb831f3c3) for more info on available settings.
    </Note>

    ### Step 2. Handle callbacks for creating and resuming payments

    There are two required callbacks:

    * `onTokenizeSuccess` to create payments with `paymentMethodToken`
    * `onResumeSuccess` to resume payments with `resumeToken`

    ```typescript Typescript theme={"dark"}
    const CheckoutScreen = (props: any) => {

      const onTokenizeSuccess: async (
        paymentMethodTokenData: PrimerPaymentMethodTokenData,
        handler: PrimerTokenizationHandler
      ) => {
        // Will be fired when the payment method has been tokenized.

        // See implementation details below
      }

      const onResumeSuccess: async (
        resumeToken: string,
        handler: PrimerResumeHandler
      ) => {
        // Will be fired if the SDK is resumed after handling an action
        // like 3DS or a redirect-based payment method.

        // See implementation details below
      }

      const onError = (
        error: PrimerError,
        checkoutData: PrimerCheckoutData | null,
        handler: PrimerErrorHandler | undefined
      ) => {
        // Handle any errors.
        // checkoutData might be null if the payment creation flow hasn't reached the payment step.
      }

      const onUniversalCheckoutButtonTapped = async () => {
        try {
          const settings: PrimerSettings = {
            paymentHandling: "MANUAL",
            /* Other options... */
            onTokenizeSuccess,
            onResumeSuccess,
            onError,
          }

          await Primer.configure(settings);

        } catch (err) {
          // Handle error
        }
      };
    }
    ```

    <Note>
      See our [SDK API Reference](https://www.notion.so/1ce697cf1de6425794df9feeb831f3c3) for the full list of callbacks.
    </Note>

    #### Handle `onTokenizeSuccess()` callback

    * When a customer submits their payment data, the payment details are tokenized and you'll receive a `PrimerPaymentMethodTokenData` object in `onTokenizeSuccess()`
    * [Create a payment request](/api-reference/v2.4/api-reference/payments-api/create-a-payment) with the `token` of the `PrimerPaymentMethodTokenData` received.
    * If the payment is successful, call `handler.handleSuccess()` in order to display a success screen.
    * If the payment is unsuccessful, call `handler.handleFailure(errorMessage)` to display an error / failed message.
    * Payments API may return a new `clientToken` for additional steps (in the `requiredActions` on the response). In this case, call `handler.continueWithNewClientToken(clientToken)` to the checkout.

    See below for an example integration:

    ```typescript Typescript theme={"dark"}
    const onTokenizeSuccess: async (
    		paymentMethodTokenData: PrimerPaymentMethodTokenData,
    	  handler: PrimerTokenizationHandler
    	) => {
    			// Make an API call to your backend to create a payment.
    			try {
    				const paymentResponse = await createPayment(paymentMethodTokenData.token);

    				// If the request failed you can cancel the flow and display an error message
            if (!paymentResponse) {
                handler.handleFailure('The payment failed. Please try with another payment method.');
    						return;
            }

    				// If the payment has any actions for the SDK to complete
    				if (paymentResponse.requiredAction?.clientToken) {
    					this.paymentId = paymentResponse.id;  // This will be used for resuming later
    					handler.continueWithNewClientToken(paymentResponse.requiredAction.clientToken);
    					return;
    				}

    				// Display the success screen
            handler.handleSuccess();
    				return;

    			} catch (error) {
    				// Handle error
    			}
    	}
    ```

    #### Handle `onResumeSuccess()` callback

    <Warning>
      Handling `onResumeSuccess()` is required to fully support 3DS and the majority of payment methods.
    </Warning>

    * You will receive a `resumeToken` via the `onResumeSuccess()` callback if applicable
    * [Send a resume payment request](/api-reference/v2.4/api-reference/payments-api/resume-a-payment) with the `resumeToken`
    * If the payment is successful, call `handler.handleSuccess()` in order to display a success screen.
    * If the payment is unsuccessful, call `handler.handleFailure(errorMessage)` to display an error / failed message.
    * Payments API may *again* return a new `clientToken` for additional steps. In this case, call `handler.continueWithNewClientToken(clientToken)` to the checkout.

    ```typescript Typescript theme={"dark"}
    const onResumeSuccess: async (
    	  resumeToken: string,
    	  handler: PrimerResumeHandler
    	) => {
    			// Make an API call to resume the payment.
    			try {
    				const resumePaymentResponse = await resumePayment(this.paymentId, resumeToken);

    				// If the request failed you can cancel the flow and display an error message
            if (!resumePaymentResponse) {
                handler.handleFailure('The payment failed. Please try with another payment method.');
    						return;
            }

            // The checkout will automatically perform the action required by the Workflow
            if (resumePaymentResponse.requiredAction?.clientToken) {
                handler.continueWithNewClientToken(resumePaymentResponse.requiredAction.clientToken);
    						return;
            }

            // Display the success screen
            handler.handleSuccess();
    				return;

    			} catch (error) {
    				// Handle error
    			}
    	}
    ```

    ### Step 3. Generate a client token

    Make an API call to your backend to generate a [Client Session](/checkout/client-session). The Client Session request returns a client token, which you will need to initialize the checkout.

    Here is an example of how it can be done from your component. Once successful, store your client token for future use.

    ```typescript Typescript theme={"dark"}
    const CheckoutScreen = (props: any) => {
    	// ...

    	const onUniversalCheckoutButtonTapped = async () => {
    		try {
    			// ...
    			// Ask your backend to create a client session
    			const clientToken = await createClientSession()
    		} catch (err) {
    			// Handle error
    		}
    	}
    }
    ```

    ### Step 4. Show Universal Checkout

    At this point, you should have a client token available. To present Universal Checkout, call `showUniversalCheckout(clientToken)` as shown below.

    ```typescript Typescript theme={"dark"}
    const CheckoutScreen = async (props: any) => {
    	// ...

    	const onUniversalCheckoutButtonTapped = async () => {
    		// ...

    		try {
    			// Present Universal Checkout
    			await Primer.showUniversalCheckout(clientToken)
    		} catch (err) {
    			// Handle error
    		}
    	}
    }
    ```

    <Note>
      You should now be able to see Universal Checkout, and the user can now interact with it. When the user tries to pay, we will tokenize the payment method and invoke `onTokenizeSuccess`. You can then create the payment as mentioned in Step 4.
    </Note>

    ### Full Snippet

    ```typescript Typescript theme={"dark"}
    import {
    		Primer,
    		PrimerSettings,
    		PrimerCheckoutData,
    		PrimerTokenizationHandler,
    		PrimerResumeHandler,
    		PrimerPaymentMethodTokenData
    } from '@primer-io/react-native'

    const CheckoutScreen = (props: any) => {

    	const onTokenizeSuccess: async (
    		paymentMethodTokenData: PrimerPaymentMethodTokenData,
    	  handler: PrimerTokenizationHandler
    	) => {
    			try {
    				const paymentResponse = await createPayment(paymentMethodTokenData.token);

            if (!paymentResponse) {
                handler.handleFailure('The payment failed. Please try with another payment method.');
    						return;
            }

    				if (paymentResponse.requiredAction?.clientToken) {
    					this.paymentId = paymentResponse.id;  // This will be used for resuming later
    					handler.continueWithNewClientToken(paymentResponse.requiredAction.clientToken);
    					return;
    				}

            handler.handleSuccess();
    				return;

    			} catch (error) {
    				// Handle error
    			}
    	}

    	const onResumeSuccess: async (
    	  resumeToken: string,
    	  handler: PrimerResumeHandler
    	) => {
    			try {
    				const resumePaymentResponse = await resumePayment(this.paymentId, resumeToken);

            if (!resumePaymentResponse) {
                handler.handleFailure('The payment failed. Please try with another payment method.');
    						return;
            }

            if (resumePaymentResponse.requiredAction?.clientToken) {
                handler.continueWithNewClientToken(resumePaymentResponse.requiredAction.clientToken);
    						return;
            }

            handler.handleSuccess();
    				return;

    			} catch (error) {
    				// Handle error
    			}
    	}

    	const onError = (
        error: PrimerError,
        checkoutData: PrimerCheckoutData | null,
        handler: PrimerErrorHandler | undefined
      ) => {
    			// Handle the error
    	}

      const onUniversalCheckoutButtonTapped = async () => {
        try {
          const settings: PrimerSettings = {
            paymentHandling: "MANUAL",
            /* Other options... */
            onTokenizeSuccess,
            onResumeSuccess,
            onError
          }

          await Primer.configure(settings);

          const clientToken = await createClientSession();

          await Primer.showUniversalCheckout(clientToken)
        } catch (err) {
          // Handle error
        }
      };
    }
    ```
  </Tab>
</Tabs>
