Skip to main content

Process Card Payments

You can process card payments using either Payment Intents or the direct Payments API. Both approaches result in a Payment object. Payment Intents are recommended for new integrations — they provide a durable, stateful lifecycle with built-in support for async outcomes and safe retries.

Now that you have completed the Collect Cards guide, let's process a card payment.

Getting Started

To get started, you will need a Credova Account.

Get your Secret Key

Next you will need your Secret Key. Go to your Developers section and click Reveal for your Secret Key and copy the value.

Save the Secret Key as it will be used in the next steps of this guide.

Payment Intents

A Payment Intent represents your intent to collect funds from a customer. It tracks the full lifecycle of a charge — creation, confirmation, processing, and the final succeeded state — and creates a Payment record on success.

Create a Payment Intent

Call the Create Payment Intent endpoint. Including a card at creation time starts the intent in requires_confirmation status and it is ready to confirm immediately:

Create a Payment Intent
curl 'https://api.publicsquare.com/payment_intents' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 09ec2c87-7fb8-44ca-bb18-5c71a76974da' \
-H 'Content-Type: application/json' \
-d '{
"amount": 1000,
"currency": "USD",
"capture_method": "automatic",
"payment_method": {
"card": "card_AjkCFKAYiTsjghXWMzoXFPMxj"
},
"customer": {
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com"
},
"billing_details": {
"address_line_1": "111 Test St.",
"city": "Des Moines",
"state": "IA",
"postal_code": "51111",
"country": "US"
}
}'
The amount is provided in cents. 1000 is the equivalent of $10.00
The IDEMPOTENCY-KEY header can be passed to protect against duplicate payment intents being created.
Create Response
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "requires_confirmation",
"amount": 1000,
"currency": "USD",
"capture_method": "automatic",
"payment_method": {
"card": {
"id": "card_AjkCFKAYiTsjghXWMzoXFPMxj",
"last_four": "4242",
"brand": "visa",
"exp_month": "12",
"exp_year": "2025"
}
},
"payment_id": null,
"expires_date": "2026-04-16T14:22:10Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:10Z"
}

Create Without a Payment Method

For multi-step checkout flows where the payment method is collected separately, omit the payment_method field. The intent starts in requires_payment_method status and the amount and other details can still be updated before a card is attached:

Create a Payment Intent without a Payment Method
curl 'https://api.publicsquare.com/payment_intents' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 09ec2c87-7fb8-44ca-bb18-5c71a76974da' \
-H 'Content-Type: application/json' \
-d '{
"amount": 1000,
"currency": "USD",
"capture_method": "automatic"
}'
Create Response
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "requires_payment_method",
"amount": 1000,
"currency": "USD",
"capture_method": "automatic",
"payment_method": null,
"payment_id": null,
"expires_date": "2026-04-16T14:22:10Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:10Z"
}

Once you have a card, call the Update Payment Intent endpoint to attach it. The intent will transition to requires_confirmation:

Attach a Payment Method
curl 'https://api.publicsquare.com/payment_intents/pmt_int_2xNjK7abcdefghij' \
-X 'PUT' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"payment_method": {
"card": "card_AjkCFKAYiTsjghXWMzoXFPMxj"
}
}'

Confirm the Payment Intent

With the intent in requires_confirmation, call the Confirm Payment Intent endpoint to submit the charge to the processor:

Confirm a Payment Intent
curl 'https://api.publicsquare.com/payment_intents/pmt_int_2xNjK7abcdefghij/confirm' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 3a9f1b72-cc4e-4f92-b7d3-1e5a9c8d2041' \
-H 'Content-Type: application/json'

When the processor responds quickly, you will receive a 200 with status: succeeded and a populated payment_id:

Confirm Response — Succeeded (200)
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"amount": 1000,
"currency": "USD",
"payment_id": "pmt_5mBpT2ghiklmnopq",
"next_action": null,
"expires_date": null,
"succeeded_date": "2026-04-15T14:22:45Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:45Z"
}

When the processor takes longer to respond, you will receive a 202 with status: processing:

Confirm Response — Processing (202)
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "processing",
"amount": 1000,
"currency": "USD",
"payment_id": null,
"expires_date": "2026-04-15T14:52:10Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:35Z"
}
When you receive a 202, subscribe to the payment-intents:update webhook event. The final outcome is in entity.status on the Payment Intent in the notification payload. See the Webhooks guide for setup instructions.
Webhook event: payment-intents:update
{
"id": "evnt_5jxWRFNLCAWeegrkCAG3a9DGc",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "production",
"event_type": "payment-intents:update",
"entity_type": "PaymentIntent",
"entity_id": "pmt_int_2xNjK7abcdefghij",
"entity": {
"id": "pmt_int_2xNjK7abcdefghij",
"status": "succeeded",
"amount": 1000,
"currency": "USD",
"payment_id": "pmt_5mBpT2ghiklmnopq",
"succeeded_date": "2026-04-15T14:22:45Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:45Z"
},
"created_at": "2026-04-15T14:22:45Z"
}

Handling Declines

If the card is hard-declined, the intent returns to requires_confirmation and the last_payment_error field is populated with the decline reason. You can attach a new payment method and re-confirm the same intent — no need to create a new one:

Declined Payment Intent
{
"id": "pmt_int_2xNjK7abcdefghij",
"status": "requires_confirmation",
"last_payment_error": {
"code": "card_declined",
"message": "Your card was declined."
},
...
}

Legacy

The direct Payments API provides a synchronous, single-request charge. The response contains the final payment result.

For example, given you have a previously collected card:

card.json
{
"id": "card_AjkCFKAYiTsjghXWMzoXFPMxj",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"cardholder_name": "John Smith",
"last4": "4242",
"exp_month": "12",
"exp_year": "2025",
"fingerprint": "CC2XvyoohnqecEq4r3FtXv6MdCx4TbaW1UUTdCCN5MNL",
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
}

Create a Payment

We need to make a call to Create Payment endpoint:

Create a Payment
curl 'https://api.publicsquare.com/payments' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPONTENCY-KEY: 09ec2c87-7fb8-44ca-bb18-5c71a76974da' \
-H 'Content-Type: application/json' \
-d '{
"amount": 1000,
"currency": "USD",
"payment_method": {
"card": "card_AjkCFKAYiTsjghXWMzoXFPMxj"
},
"customer": {
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com"
},
"billing_details": {
"address_line_1": "111 Test St.",
"city": "Des Moines",
"state": "IA",
"postal_code": "51111",
"country": "US"
}
}'
The amount is provided in cents. 1000 is the equivalent of $10.00
The IDEMPONTENCY-KEY header can be passed to protect against duplicate payments being processed.

Payment Response

Given the above example, you should see a payment result similar to:

Payment Response
{
"id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"transaction_id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"amount": 1000,
"amount_capturable": 0,
"amount_charged": 1000,
"amount_refunded": 0,
"refunded": false,
"currency": "USD",
"payment_method": {
"card": {
"id": "card_AjkCFKAYiTsjghXWMzoXFPMxj",
"cardholder_name": "John Smith",
"last4": "4242",
"exp_month": "12",
"exp_year": "2025",
"brand": "visa",
"avs_code": "Y",
"cvv2_reply": "M",
"fingerprint": "CC2XvyoohnqecEq4r3FtXv6MdCx4TbaW1UUTdCCN5MNL"
}
},
"customer": {
"id": "cus_7Ay5mcUXAxwrN6wQEQUVEHBCJ",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@email.com"
},
"billing_details": {
"address_line_1": "111 Test St.",
"city": "Des Moines",
"state": "IA",
"postal_code": "51111",
"country": "US"
},
"transaction": {
"id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"amount": 1000,
"fee_amount": 0,
"net_amount": 1000,
"currency": "USD",
"type_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"type": "payment",
"processor": "nuvei",
"processor_id": "7110000000010554769",
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
},
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
}

Conclusion

Now that we have seen how to process payments with a collected card, we may need to manage the payment or view information about the payment. Follow these guides to learn more: