Skip to main content

Authorize and Capture Payments

In some scenarios, your checkout process may collect and pre-authorize a purchase before the customer completes checkout. This is useful when you want to run a customer and card against your fraud rules, enable pre-orders, or adjust the final amount at fulfillment.

Both Payment Intents and the direct Payments API support authorize-and-capture. Both approaches result in a Payment object on success.

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

Use capture_method: manual to authorize the card without immediately capturing funds. The intent moves to requires_capture after confirmation and waits up to 7 days for an explicit capture call.

Create a Payment Intent

Call the Create Payment Intent endpoint with capture_method: manual. Including a card at creation time starts the intent in requires_confirmation status:

Create a Payment Intent with Manual Capture
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": "manual",
"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
Create Response
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "requires_confirmation",
"amount": 1000,
"currency": "USD",
"capture_method": "manual",
"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 can be updated once the card is available:

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": "manual"
}'

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 authorize the card. Because capture_method is manual, a successful authorization moves the intent to requires_capture rather than succeeded:

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'
Confirm Response
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "requires_capture",
"amount": 1000,
"currency": "USD",
"capture_method": "manual",
"payment_id": null,
"expires_date": "2026-04-22T14:22:10Z",
"confirmed_date": "2026-04-15T14:22:45Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-15T14:22:45Z"
}
The authorization expires after 7 days. The intent will automatically move to canceled status if not captured before the expires_date.

Capture the Payment

Using the authorized intent, call the Capture Payment Intent endpoint:

Capture a Payment Intent
curl 'https://api.publicsquare.com/payment_intents/pmt_int_2xNjK7abcdefghij/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 7c2e9a41-fd83-4b1e-a5d9-2f6c8b3e1047' \
-H 'Content-Type: application/json' \
-d '{
"amount": 1000
}'

You can capture up to, but not exceeding, the original authorized amount. If no amount is passed, the full authorized amount will be captured.

Capture Response
{
"id": "pmt_int_2xNjK7abcdefghij",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"amount": 1000,
"currency": "USD",
"payment_id": "pmt_5mBpT2ghiklmnopq",
"expires_date": null,
"succeeded_date": "2026-04-17T09:14:22Z",
"created_date": "2026-04-15T14:22:10Z",
"modified_date": "2026-04-17T09:14:22Z"
}

Legacy

The direct Payments API supports authorize-and-capture by passing "capture": false when creating the payment.

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 Authorized Payment

We need to make a call to Create Payment endpoint passing "capture": false in the request body:

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",
"capture": false,
"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.

Authorized Payment Response

You should see a payment response with a requires_capture status similar to:

Authorized Payment Response
{
"id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "requires_capture",
"transaction_id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"amount": 0,
"amount_charged": 0,
"amount_capturable": 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": "requires_capture",
"amount": 1000,
"fee_amount": 0,
"net_amount": 1000,
"currency": "USD",
"type_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"type": "payment",
"processor": "nuvei",
"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"
}

If the payment authorization was declined by the processor or rejected because of fraud decisioning rules, you will see a payment status of rejected or declined. These payments cannot be captured.

Capture the Payment

Using the authorized payment from the previous step, we now need to make a call to Capture Payment endpoint:

Capture a Payment
curl 'https://api.publicsquare.com/payments/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"amount": 1000
}'

You can capture up to, but not exceeding, the original authorized payment amount. If no amount is passed, the full authorized amount of the payment will be captured.

Captured Payment Response

You should see a payment response with a succeeded status similar to:

Captured Payment Response
{
"id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"transaction_id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"amount": 1000,
"amount_charged": 1000,
"amount_capturable": 0,
...
"transaction": {
"id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "succeeded",
"amount": 1000,
"fee_amount": 0,
"net_amount": 1000,
"processor": "nuvei",
"processor_id": "7110000000010554769",
...
},
...
}

Multiple partial captures

Instead of capturing the full authorized amount in a single call, you can capture an authorization in multiple, smaller captures. This is useful when the final amount is not known up front, such as shipment-based fulfillment or usage-based billing.

Not all payment processors support multiple partial captures. If your account's processor does not support it, a second capture on the same authorization is rejected.

Interim captures

Call the capture endpoint with is_final: false and an amount less than the remaining capturable amount. The authorization stays open so you can capture again later.

is_final defaults to true. Pass is_final: false explicitly on every interim capture, or the authorization closes out early.

Using a payment intent:

Capture a Payment Intent (interim)
curl 'https://api.publicsquare.com/payment-intents/pmt_int_2xNjK7abcdefghij/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 7c2e9a41-fd83-4b1e-a5d9-2f6c8b3e1047' \
-H 'Content-Type: application/json' \
-d '{
"amount": 400,
"is_final": false
}'

Using the legacy endpoint:

Capture a Payment (interim)
curl 'https://api.publicsquare.com/payments/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 09ec2c87-7fb8-44ca-bb18-5c71a76974da' \
-H 'Content-Type: application/json' \
-d '{
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"amount": 400,
"is_final": false
}'
Send a unique IDEMPOTENCY-KEY header on every capture request, including interim ones, so a retried request cannot create a duplicate capture.

While an authorization has open interim captures, the payment status is partially_captured. The payment intent's own status stays requires_capture — only the nested payment reports partially_captured.

Final capture

A capture is final when any of the following is true:

  • You omit is_final, or pass is_final: true. is_final defaults to true.
  • You send amount: 0. This always releases the remaining authorization, regardless of is_final.
  • The amount you send equals the full remaining capturable amount.

A final capture completes the payment: status moves to succeeded and the remaining authorization is released.

Final Capture Payment Intent
curl 'https://api.publicsquare.com/payment-intents/pmt_int_2xNjK7abcdefghij/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 3a9f1b72-cc4e-4f92-b7d3-1e5a9c8d2041' \
-H 'Content-Type: application/json' \
-d '{
"amount": 600,
"is_final": true
}'

Releasing the remaining authorization

To release the remaining authorization without capturing more funds, send amount: 0. This always releases the remainder, whether or not you also pass is_final. This is supported where the account/processor supports it.

Release Remaining Authorization
curl 'https://api.publicsquare.com/payments/capture' \
-X 'POST' \
-H 'X-API-KEY: <SECRET_API_KEY>' \
-H 'IDEMPOTENCY-KEY: 5b6e1c93-2a7d-4f8e-9c1b-3d7a8f2e6b10' \
-H 'Content-Type: application/json' \
-d '{
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"amount": 0
}'

Capturing more than the remaining capturable amount is also rejected: 'amount' exceeds capturable payment amount.

The payment_captures array

Every payment response, including the payment nested in a payment-intent response, includes a payment_captures array. Entries are ordered oldest first. Payments with no captures yet have an empty array, and payments captured before this feature shipped show a single entry.

Payment with Multiple Captures
{
"id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"status": "partially_captured",
"transaction_id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"amount_charged": 400,
"amount_capturable": 600,
"amount_refunded": 0,
"refunded": false,
"currency": "USD",
"payment_captures": [
{
"id": "cpt_3fRkT8bcdefghijklmnop",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"external_id": null,
"amount": 400,
"status": "succeeded",
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"transaction_id": "trx_95rvMJvAVeG68W4NtLdfkN3LG",
"currency": "USD",
"is_final": false,
"declined_reason": null,
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
},
{
"id": "cpt_7hQmV1defghijklmnopqr",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"external_id": null,
"amount": 200,
"status": "pending",
"payment_id": "pmt_2YKewBonG4tgk12MheY3PiHDy",
"transaction_id": "trx_51xzQKvBWfH79Y5OuMegfP4MH",
"currency": "USD",
"is_final": false,
"declined_reason": null,
"created_at": "2024-06-30T01:04:11.500Z",
"modified_at": "2024-06-30T01:04:11.500Z"
}
],
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:04:11.500Z"
}

Each payment_captures entry has the following fields:

FieldDescription
idUnique identifier for the capture. Prefix: cpt_.
account_idThe merchant account that owns the capture.
environmenttest or production.
external_idOptional external identifier you passed on the capture request.
amountAmount captured, in cents.
statusOne of pending, succeeded, declined, error, cancelled.
payment_idID of the parent payment.
transaction_idID of the parent transaction.
currencyThree-character ISO currency code.
is_finalWhether this capture was final.
declined_reasonPopulated when status is declined.
created_atISO 8601 timestamp.
modified_atISO 8601 timestamp.

An optional external_id and IDEMPOTENCY-KEY header can both be passed on each capture request.

Maximum captures per authorization

Each authorization allows a limited number of captures, controlled by the account setting max_captures_per_authorization (default 5). A capture beyond the limit is rejected with a 400 error: payment has reached the maximum of {n} captures. Contact PublicSquare support if you need this limit adjusted for your account.

American Express does not support partial capture: 'american_express' does not support partial payment capture. This error is returned unless the capture amount equals the full capturable amount.

Conclusion

Authorize and capturing payments also works with ACH payments using a bank account payment method. See our Process ACH payments guide to see how to collect bank accounts and process ACH payments following similar steps in this guide.

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