Primer supports merchant-initiated payments (MITs) for repeat customers, recurring and subscription business models. You can use Primer’s Payments API to create these payments.

🎯

This guide explains how to create and process a payment using our Payments API

Before you start

Before you start, make sure you are ready to process a payment

Generate an API key

Requests to our API are authenticated using an API key in the X-Api-Key header. Create an API key by visiting the developer page of the Primer Dashboard.

Make sure to set the following scopes for your API Key:

  • client_tokens:write
  • transactions:authorize

Get a payment method token

The Payments API requires a payment method token to create a payment.

A payment method token represents the sensitive data to process a payment: the card data, or the banking details for instance.

You can get a payment method token from:

Payment Method Token

Send a payment request

See our API Reference for more details

Below is a minimal payment request example

1234567891011
curl --location --request \    POST 'https://api.sandbox.primer.io/payments' \    --header 'X-Api-Key: <YOUR_API_KEY>' \    --header 'X-Api-Version: 2.4' \    --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>' \    --data '{        "orderId": "<YOUR_ORDER_ID>",        "amount": 800,        "currencyCode": "GBP",        "paymentMethodToken": "<PAYMENT_METHOD_TOKEN>",    }'
bash
copy

See "Optimize for recurring payments" below for more information

When you create a payment, it will start your workflow with a "Payment created" Trigger.

Remember to add the "Continue payment flow" Action to your workflow. This tells Primer when to respond to your payment request. Without this action, your payment request will time out.

Learn more about processing payments here.

Any change in the payment’s status (e.g. once it is AUTHORIZED) will trigger a PAYMENT.STATUS webhook notification event. Learn more about webhooks here.

Handle the Payment’s Lifecycle

All Primer payments conform to a unified payment lifecycle.

Each payment will progress through some combination of the Payment Statuses on their way through your Workflow.

Recover 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 unexpire.)
  • 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 service disruption at the processor's gateway are usually the cause of FAILED payments, but these can also occur when a configuration error prevents 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.

Handle payment 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.

Optimize for recurring payments

Payments are tied together through the Network Transaction ID, the ID assigned by card networks to an initial payment in a series of payments. Subsequent payments can then be routed to any processor, rather than being tied to the processor used for the initial authorization.

While the Network Transaction ID is a universal value, every processor has additional, unique requirements around what data is sent for payments made using a stored payment method. Normally you would need to calculate these requirements yourself on a processor by processor basis, but at Primer we handle these fields for you.

Let’s take a common example - processing subscriptions, using three different processors. For various reasons, including fee and authorization rate optimization, a customer’s card could be against any one of those processors throughout the life of their subscription. But each processor has different requirements:

Code RequirementContext
Processor 1- merchant_initiated: recurring
- first_stored: 01/01/1970
- The type of merchant initiated transaction
- Where the card was first stored.
Processor 2- payment_type: subscription
- customer_present: false
- externally_stored: true
- The type of merchant initiated transaction
- If the customer is present
- Whether the card was stored with the processor or elsewhere
Processor 3- charge_intent: subscription- The type of merchant initiated transaction

Managing these differing needs adds considerable complexity to the already elaborate task of running a subscription business; and that’s without considering all of the other intricacies managing multiple processors can bring.

Primer therefore manages this complexity for you. We store the mapping for each processor and calculate the correct fields to send for each transaction. Our internal logic reviews multiple inputs, including if the customer is present at the point of making the payment, whether the payment method has been stored before and what type of payment method is being used.

Importantly, we also take into account the payment type you pass in your payment request. While the parameter isn’t mandatory, we recommend including it - both for your own reporting and to ensure your intent for the payment is aligned with the fields we calculate.

Using this wealth of information, Primer can automatically calculate the correct fields to send to any processor. During this process we also store and handle the related acquirer and scheme data in order to increase approvals.

Note: this logic is present in API v2.4 onwards. We recommend upgrading to API v2.4 as soon as possible and in the interim, always passing the paymentType parameter when processing recurring payments with API ≤v2.3.

Payment Types

Additional parameters
Description
  1. paymentMethod
  2. paymentType
  • FIRST_PAYMENT: a customer-initiated payment which is the first in a series of recurring payments for subscriptions, unscheduled transactions, or "card-on-file" scenarios.
  • 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.

If you are storing a payment method for the first time, it is also recommended you pass the relevant paymentMethod.firstPaymentReason to indicate why the payment method is being stored.

Additional parameters
Description
  1. paymentMethod
  2. firstPaymentReason
  • CardOnFile: storage for future "card-on-file" scenarios, where the customer will be returning to use their payment method on your website or within an app. This correlates with the ECOMMERCE paymentType.
  • Recurring: storage for future subscription payments, with a fixed amount or fixed schedule. This correlates with the SUBSCRIPTION paymentType.
  • Unscheduled: storage for future unscheduled payments with no fixed schedule or amount. This correlates with the UNSCHEDULED paymentType.

This field is particularly relevant for processors that rely on precise card storage logic. We recommend setting the correct value when storing cards for a specific purpose, as this ensures compliance with processor requirements and optimizes performance.

Note: Many processors benefit from the firstPaymentReason field for enhanced payment flows and compliance. While the implementation is complete for several key processors (e.g., Adyen, Cybersource, MPGS, Checkout), we continue to expand support to provide a seamless experience across additional platforms.

Avoid duplicated payments

Primer supports a request idempotency mechanism for our Payments API. This optional feature enables you to safely retry a request without risking the user being charged or refunded multiple times.

To make an idempotent request, generate an idempotency key and pass it to the header X-Idempotency-Key.

12
curl -X POST 'https://api.primer.io/<ENDPOINT>' \  --header 'X-Idempotency-Key: <idempotency-key>'
bash
copy

Example idempotency error response:

12345678910
{    "error": {        "errorId": "TransactionRequestIdempotencyKeyAlreadyExists",        "description": "Transaction request already exists with idempotency key 'IDEMPOTENCY_KEY'.",        "diagnosticsId": "3233041960986039254",        "validationErrors": [],        "paymentId": "6gHJtooK6",        "paymentStatus": "SETTLED"    }}
json
copy

paymentId and paymentStatus are only available from API version 2.2 onwards

If an API call fails due to the request being invalid, due to a network issue, or if Primer is momentarily unavailable, make another request with the same idempotency key:

  • If a request with the same idempotency key has already been successfully processed by Primer, the new request will be ignored. A 400 error will be returned with an errorId set to TransactionRequestIdempotencyKeyAlreadyExists.
  • On API version 2.2 and later, we will include the paymentId and paymentStatus in the error response to provide more insights into the related payment. This data can be used to validate which payment was already created in case of a duplicate request.
  • Otherwise, Primer will attempt to process the new request.

The way you generate the idempotency key is totally up to you, as long as it is unique per request attempt.