Skip to main content

Authorization & Capture

An authorization reserves funds on a customer's card without capturing (charging) them immediately. You capture — or "settle" — the funds later, once you are ready to fulfill. You can also release the hold entirely by voiding the authorization.

When to use auth & capture

ScenarioWhy auth & capture
Hotel or rental holdsReserve the full amount at booking; settle the actual charge at checkout
Marketplace escrowHold buyer funds until the seller confirms delivery
Delayed fulfillmentAuthorize at order time; capture only when the item ships
Partial captureAuthorize for an estimated total; settle the exact amount actually used

Path A: Authorization via card UI

Use this path when the customer is present and will enter their card details in the hosted checkout form. The flow is identical to a regular card checkout — only the SDK method names differ.

Step 1: Start an authorization session (server)

import AccruPay, { CURRENCY, COUNTRY_ISO_2 } from '@accrupay/node';

const sdk = new AccruPay({
apiSecret: process.env.ACCRUPAY_API_SECRET!,
});

const session = await sdk.transactions.clientSessions.authorizations.start({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
data: {
merchantInternalTransactionCode: 'auth-txn-001',
amount: 15000n, // $150.00 hold
currency: CURRENCY.USD,
storePaymentMethod: false,
customer: {
merchantInternalCustomerCode: 'customer-123',
},
billing: {
billingFirstName: 'Jane',
billingLastName: 'Smith',
billingEmail: 'jane@example.com',
billingAddressCountry: COUNTRY_ISO_2.US,
},
},
});

// Send only session.id to the frontend
return session.id;

Step 2: Render the checkout form (React)

The React SDK form is identical to the card checkout form — use the same components:

import {
AccruPay,
CardholderName,
CardNumber,
CardExpiry,
CardCVC,
SubmitButton,
} from '@accrupay/react';

function AuthorizationForm({ sessionId }: { sessionId: string }) {
return (
<AccruPay
merchantPublicId={process.env.NEXT_PUBLIC_ACCRUPAY_MERCHANT_ID!}
transactionSessionId={sessionId}
>
<CardholderName placeholder="Full name on card" />
<CardNumber />
<CardExpiry />
<CardCVC />
<SubmitButton
onSuccess={(result) => console.log('Authorized', result)}
onError={(err) => console.error('Authorization failed', err)}
>
Authorize $150.00
</SubmitButton>
</AccruPay>
);
}

Step 3: Verify the authorization (server)

Use any one of these identifiers to look up the result:

const transaction =
await sdk.transactions.clientSessions.authorizations.verify({
merchantInternalTransactionCode: 'auth-txn-001',
// Alternatively: id, providerCode, or token
});

console.log(transaction.id, transaction.status);

Path B: Authorization via stored card (server-only)

Use this path when the customer's card is already stored and no frontend interaction is needed.

Step 1: Start the authorization (server)

const session =
await sdk.transactions.serverSessions.authorizations.paymentMethod.start({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
data: {
merchantInternalTransactionCode: 'auth-stored-001',
merchantCustomerPaymentMethodId: 'pm_abc123',
amount: 15000n, // $150.00 hold
currency: CURRENCY.USD,
billing: {
billingFirstName: 'Jane',
billingLastName: 'Smith',
billingEmail: 'jane@example.com',
billingAddressCountry: COUNTRY_ISO_2.US,
},
},
});

Step 2: Verify the authorization (server)

For server-session authorizations, you can identify the transaction by id, merchantInternalTransactionCode, or providerCode. There is no token option for server-side flows.

const transaction =
await sdk.transactions.serverSessions.authorizations.paymentMethod.verify({
merchantInternalTransactionCode: 'auth-stored-001',
// Alternatively: id or providerCode
});

Capture (settle)

When you are ready to charge the customer, call settle(). You must always supply the amount — even if you want to capture the full authorized amount.

const settled = await sdk.transactions.authorizations.settle({
merchantInternalTransactionCode: 'auth-txn-001',
amount: 12000n, // $120.00 — partial capture
});
amount is required

There is no shorthand to capture "the full authorized amount." You must always pass amount explicitly to settle(). Omitting it will result in an error.

Partial capture: pass an amount less than or equal to the original authorized amount. The remaining hold is released automatically — behavior confirmed for current supported providers; confirm with your provider for others.

You can identify the authorization using any of:

IdentifierType
idstring
merchantInternalTransactionCodestring
providerCodestring

Release hold (void)

To release the authorization without charging the customer, call void():

await sdk.transactions.authorizations.void({
merchantInternalTransactionCode: 'auth-txn-001',
// Alternatively: id or providerCode
});

Voiding immediately cancels the hold. The reserved funds are returned to the customer's available balance, subject to the card network's release timing.


Provider capture windows

After the capture window closes, the authorization expires and the hold is released automatically. A settle() call after expiry will fail — you must re-authorize the customer.

Capture window by provider
Nuvei

7 days from authorization.

Stripe

7 days from authorization. Stripe cancels uncaptured PaymentIntents automatically after this window.

Other providers

Capture windows range from 1 day to 30 days depending on the PSP and card network. Check your provider's documentation for the exact limit.

warning

Treat the capture window as a hard deadline. Build a scheduled job or alerting on any authorization older than 80% of your provider's window to catch and settle or void before expiry.


Next steps