Learn payments once

All Primer payments conform to a unified payment lifeycle, so you only have to figure out payments once.

A payment always has a payment status. Each payment will progress through some combination of the statuses listed below on their way through your Workflow.


The payment is awaiting a subsequent action or callback. This usually occurs with asynchronous processors. Prepare for this contingency by setting up Webhooks.

The processor has authorized the payment and no further status-altering actions have been defined in your Workflow.

The processor has declined the payment and no further status-altering actions have been defined in your Workflow. You may want to consider retrying certain declined payments.

This usually occurs when there is a processor gateway issue, such as a service disruption. Set up retries and fallbacks in Workflows to mitigate failures.

The processor has settled funds to your account.

You have successfuly cancelled the payment.


A payment may go through multiple processors in the event of retries and fallbacks. Workflows streamline all processor responses to make sure you only see a single instance of each relevant payment status. Individual processor responses can be inspected in the Timeline.


Available for every payment in the Dashboard, the Timeline is the best representation of a payment on Primer. It displays all attributes of the payment, as well as the entire series of events that have taken place throughout its lifecycle.

Here, you can see all the Connections the payment went through as it progressed through your Workflow, and the various stages at which its status has been updated.



Primer is not a black box. It's your payments infrastructure, we're just hosting it.
You can inspect any event on the Timeline to see all of our requests and responses to your Connections. (But, we do hide sensitive customer data for security and compliance reasons.)

Create payments with the Payments API

1. Create a payment request

Once you've received a paymentMethodToken from Universal Checkout, use it to create a payment.

payment creation

Payment request POST/payments

curl --location --request \    POST '' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "orderId": "<YOUR_ORDER_ID>",        "amount": 800,        "currencyCode": "GBP",        "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>",    }'
Required fields
  1. orderId
your unique order ID
  1. amount

payment amount
example in currency (USD) with minor units: 700 for $7.00
example in currency (JPY) without minor units: 100 for ¥100

  1. currencyCode

ISO 4217 three-letter currency code: USD for US dollars

  1. paymentMethodToken

paymentMethodToken obtained from Universal Checkout or the Payment Methods API


Data passed in the payment request overwrites any data passed in the clientToken.

See more request options in the API Reference.

Payment response

The payment request is synchronous. It will trigger a Workflow and return a payment object containing, among other things, the payment status. In circumstances where the payment flow is asynchronous, you may receive a status of PENDING.

// 2XX response{    "id": "slklmjh4",    "status": "AUTHORIZED",    "orderId": "<YOUR_ORDER_ID>",    "currencyCode": "GBP",    "amount": 800,  "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>"    // ... additional attributes for the Payment object    // ... see API ref}

2. Resuming payment flows

Payment response with requiredAction 👇

Some Connections, such as 3D Secure, can pause the Workflow pending further action. When this happens, the requiredAction object returned in the response to your payment request will include a new clientToken. Return this to Universal Checkout to render next steps and continue.

// 2XX response{  "id": "slklmjh4",  "status": "PENDING",  "orderId": "<YOUR_ORDER_ID>",  "currencyCode": "GBP",  "amount": 800,  "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>",    "requiredAction": {        "name": "3DS_AUTHENTICATION",        "description": "A 3DS Authentication is required. Return clientToken.",        "clientToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."    }    // ... additional attributes for the Payment object    // ... see API ref}

Resume payment request POST/payments/<YOUR-PAYMENT-ID>/resume

Once the required action is completed, Universal Checkout will return a resumeToken from onResumeSuccess(). Use this to resume the Workflow for the payment. You will receive an updated payment status in response.

curl --location --request \    POST '<YOUR-PAYMENT-ID>/resume' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "resumeToken": "9QpvyT3qNFh6CbJSa0HwxNjE4MjI0NTYw"    }'
See the Universal Checkout docs for more on handling callbacks and resuming payments.

When resuming a payment, another requiredAction may be returned in the response, which restarts the cycle until all required actions in the Workflow are complete.

3. Handle pending payments with Webhooks

When creating payments, you may receive a payment status of . This means that the payment is awaiting another step (such as a required action in Workflows, or a callback for asynchronous processors) in order to advance to the next step or Connection. Set up Webhooks to be notified when the payment status changes.


Managing Payments

Vaulting after a successful payment

Primer's centralized Vault is a PCI-DSS Level 1 service you can use to securely store tokenized customer payment method data after it has been authorized.

Sensitive payment data is stored as a secure multi-use token that enables recurring merchant-initiated payments and can be used with Universal checkout to provide a seamless one-click checkout experience for your customers.

Payment request (vaultOnSuccess) POST/payments

Pass paymentMethod.vaultOnSuccess in the payment request. A customerId must also be passed here unless you have already passed it in the clientToken. This will store the payment method data against the customer ID.


You can also use the Payment Methods API to store paymentMethodToken manually.

curl --location --request \    POST '' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "amount": 700,        "currencyCode": "EUR",        "orderId": "order-123",        "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>",        "paymentMethod": {            "vaultOnSuccess": true        },        # Alternatively, pass this in the client token request        "customerId": "<CUSTOMER_ID>",    }'

Not all payment methods support this option.

Recovering declined or failed payments

A declined transaction doesn't necessarily have to mean the end of the road for your payment.

When authorization is declined by the card issuer or processor gateway, a statusReason object is returned in response to your payment request, providing a decline code and processor message (if available) with further information that can help you recover the payment. You can also see this information on the payment's Timeline in the Dashboard.

  • Hard declines include reasons like invalid information, a closed account, or because the card owner reported the card lost or stolen. These cannot be retried with the existing paymentMethodtoken because the payment can never be successfully authorized unless the payment method data is changed. (Your customer's expired card, for example, will never un-expire.)

  • Soft declines indicate that authorization wasn't possible due to conditions that in some cases may only be temporary, like when the card limit has been exceeded or the issuer is unavailable. It may be possible to complete these payments successfully at a later time with the same information captured in the initial attempt. In these cases, you can use the same paymentMethodToken to attempt authorization again.

Issues like latency or a service disruption at the processor's gateway are usually the cause of payments, but these can also occur when a configuration error prevented the request from being processed. These transactions may also be successful on a later attempt in the event that there was a technical issue, but you should make sure everything is configured correctly before you retry the payment.

Handling errors

Set up Webhooks to receive information about declined and failed payments and consume the data contained in the statusReason object to handle next steps, or consider setting up a fallback or retry scenario in Workflows.

Information about HTTP error responses (4XX) when making an API call to Primer can be found in the Errors section of the API Reference.

Handling recurring payments

To optimize authorization rates for recurring payments, pass paymentMethod.recurring.type in the payment request. Primer automatically stores and handles processor, acquirer and scheme data in order to increase approvals.

Additional parameters
  1. paymentMethod
  2. recurringType
  • FIRST_PAYMENT: a customer-initiated payment which is the first in a series of recurring payments for subscriptions, or "card-on-file" scenarios. This field is automatically set when vaulting cards for the first time.
  • ECOMMERCE: a customer-initiated payment using a stored payment method where the cardholder is present
  • SUBSCRIPTION: a merchant-initiated payment as part of a series of payments on a fixed schedule and for a set amount
  • UNSCHEDULED: a merchant-initiated payment using stored payment details with no fixed schedule or amount

Payment request (recurring options) POST/payments

Pass paymentMethod.recurringType in the Payment Request.

curl --location --request \    POST '' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "amount": 700,        "currencyCode": "EUR",        "orderId": "order-123",        "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>",        "paymentMethod": {            "recurringType": "FIRST_PAYMENT"        },    }'

See all the request options in the API Reference.


Cancel request POST/payments/<PRIMER_ID>/cancel

Provided the payment has not reached status, Primer will send a void request to the processor, thereby cancelling the payment and releasing the hold on customer funds. Upon success, the payment status will be updated to .

curl --location --request \    POST '<YOUR-PAYMENT-ID>/cancel' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>'


Refund request POST/payments/<PRIMER_ID>/refund

By default, this will refund the full amount against the processor. Optionally, pass in a lesser amount for partial refunds, if supported.

curl --location --request \    POST '<PRIMER_ID>/refund' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "amount": 200, # supply this parameter for a partial refund, otherwise defaults to full amount        "orderId": "order-123",    }'
© Primer 2021