Accept Card Payments

See below for a sample end-to-end flow covering the steps involved in accepting a card payment using the Circle Payments API.

Prepare Encryption of Sensitive Data

Card details are encrypted in the browser so sensitive card details are never accessible to you server.

The Circle Payments API exposes a public key endpoint that is used to encrypt data on the client-side.

On page load you will load the OpenPgP.js JavaScript library used for encryption.

The request to get your public key to be used for encryption can also be done on page load so that it is already available when the user wants to make a payment. Your public keys change infrequently, so we further recommend that you cache their values for a duration of 24 hours or more.

Encrypt End-user's Card Details

Before card details are saved using the API, the sensitive data elements first need to be encrypted on the client-side. Below is a sample code that describes the data-types and functions involved in performing the encryption of card details.

import { createMessage, encrypt, readKey } from 'openpgp'

// Object to be encrypted
interface CardDetails {
 number?: string,    // required when storing card details
 cvv?: string        // required when cardVerification is set to cvv

// Encrypted result
interface EncryptedValue {
 encryptedData: string,
 keyId: string
const pciEncryptionKey = await getPCIPublicKey()
* Encrypt card data function
return async function(dataToEncrypt: CardDetails): Promise<EncryptedValue> {
 const decodedPublicKey = await readKey({ armoredKey: atob(publicKey) })
  const message = await createMessage({ text: JSON.stringify(dataToEncrypt) })
  return encrypt({
    encryptionKeys: decodedPublicKey,
  }).then((ciphertext) => {
    return {
      encryptedMessage: btoa(ciphertext),

The returned encryptedData and keyId values will then be included in the request made to save card details.

Save End-user's Card Details

The next step is to save the end-user's card details by using the create card endpoint.

As well as passing the encrypted card details some extra fields need to be added to the request before it can be sent to the create card endpoint. You need to collect billing details for your end user, as well as provide a unique ID for the active session (sessionId) and the IP address of the end-user (ipAddress).

Creating a card will respond with an id value that can be stored on your side to refer to this end-user's card in future payment requests.
The card creation response also contains the result of the different verification checks performed under the verification property. See Verifying Card Details for details on what checks are performed and the meaning of the returned values.
The card will also be checked by the Circle Risk Service. If the card is denied by Risk then an errorCode and riskEvaluation property will be set on the response see Risk Evaluation for details on the evaluation responses.
Both the card verification and Risk Service checks are performed asynchronously so the results will not be set in the initial response. To get the results you can either poll the GET /cards/{id} endpoint or use a subscription notification.

Create a Card Payment

You then need to make a payment request. You will use the id of the card created in the previous step as the "source" for this payment.

When making a payment request you can choose to verify the CVV value again by setting the verification property in the request to cvv. The code to encrypt the CVV value when making a payment is the same as shown above except this time the CardDetails object passed to the encryption function needs only to contain the cvv property. If the verification value in the request is set to none then the encryptedData property does not need to be provided.

You also need to provide an amount and currency to be charged against this end-user's card. Amounts are expressed in units of the currency, with decimals prefixed with a . separator. Currency is expressed in ISO 4217 currency codes - note that for the time being, only USD is supported. You will receive settlement for this payment in USDC equivalent (minus processing fees).

Make sure to include a refId property to allow for idempotent requests . The end-user's sessionId and ipAddress fields referenced above need to be included once more in the request when making a payment (those values are used for preventing fraud).

Auth and Capture (Beta)

When creating a payment POST /v1/payments you have the option to either authorize a payment only (thus delaying capture), or auth and capture a payment at the same time.

By default, POST /v1/payments will auth and capture a payment at the same time. For scenarios where you want to auth a payment only and delay capture until a later time, send autoCapture: false in the request.

For a payment that is authorized only, it is your responsibility to capture the payment using POST /v1/payments/id/capture. Auths that are not captured will expire 6 days + 18 hours after creation. If you do not intend to capture an auth, use POST /v1/payments/id/cancel to release the cardholder's funds.

Authorization (Auth): Check the cardholder has sufficient funds to cover the cost of the transaction.
Capture: Request the transfer of funds from the cardholder's issuing bank.


Partial Capture (Beta)

The ability to partially capture a payment is not yet supported for all accounts. Please reach out to your Account Manager to confirm if partial capture is enabled for your account.

Check for Payment Status

The Circle Payments API processes payments asynchronously. In order to determine the status of a payment you initiated, you have to poll the API periodically until the payment status has changed from pending to either confirmed or failed. To poll for payment status, use the retrieve payment endpoint with the id of the payment you initiated.


🎉 You have accepted your first card payment and your USDC settlement is on its way!

Are you ready for the next step?

If you are in advanced stages of experimenting with our APIs and want to plan moving to production, please start by applying for a Circle Account and subsequently reach out to sales. We'll be happy to walk you through to the next steps.

We can't wait to see what you are going to build!