Before you start
Before you start, make sure you are ready to process a payment.Generate an API key
Ensure you have an API key configured. 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, so make sure you save the payment method on the first payment so you have a vaulted token for MITs.Send a payment request
See our API Reference for more details
See Optimize for recurring payments below for more information
PAYMENT.STATUS
webhook notification event. Learn more about webhooks here.
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, astatusReason
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.
Handle payment errors
Set up Webhooks to receive information about declined and failed payments and consume the data contained in thestatusReason
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 theNetwork 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 Requirement | Context | |
---|---|---|
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 |
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
paymentMethod -> paymentType
FIRST_PAYMENT
: a customer-initiated payment for subscriptions, unscheduled transactions, or “card-on-file” scenarios.ECOMMERCE
: Customer-initiated payment using a stored payment method where the cardholder is present.SUBSCRIPTION
: Merchant-initiated payment as part of a series of payments on a fixed schedule and for a set amount.UNSCHEDULED
: Merchant-initiated payment using stored payment details with no fixed schedule or amount.
paymentMethod.firstPaymentReason
to indicate why the payment method is being stored.
paymentMethod -> firstPaymentReason
CardOnFile
: For “card-on-file” scenarios, correlating with the ECOMMERCE paymentType.Recurring
: For future subscription payments, correlating with the SUBSCRIPTION paymentType.Unscheduled
: For future unscheduled payments, correlating with the UNSCHEDULED paymentType.
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 headerX-Idempotency-Key
.
JSON
paymentId
and paymentStatus
are only available from API version 2.2 onwards- 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 anerrorId
set toTransactionRequestIdempotencyKeyAlreadyExists
. - On API version
2.2
and later, we will include thepaymentId
andpaymentStatus
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.