Skip to main content

Charge a Stored Card

Once a card is saved, you can charge it entirely server-side — no frontend session or checkout form required.

When to use this flow:

  • Recurring billing and subscription renewals
  • Scheduled charges (e.g. usage-based billing run overnight)
  • Re-order or one-click purchase flows where the customer does not re-enter card details

For flows where the customer needs to enter card details, use Card Checkout instead.

Prerequisite

You need a merchantCustomerPaymentMethodId — the ID of a stored payment method obtained from a previous payment or add-card session. See Save a Card for how to obtain one.


Charge the stored card

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

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

export async function chargeStoredCard(
paymentMethodId: string,
transactionCode: string,
) {
const transaction = await sdk.transactions.payments.paymentMethod.charge({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
data: {
merchantCustomerPaymentMethodId: paymentMethodId,
merchantInternalTransactionCode: transactionCode,
amount: 10000n, // $100.00
currency: CURRENCY.USD,
billing: {
billingFirstName: 'Jane',
billingLastName: 'Smith',
billingEmail: 'jane@example.com',
billingAddressCountry: COUNTRY_ISO_2.US,
},
},
});

return transaction;
}
BigInt amounts

Amounts are bigint in the smallest currency unit. 10000n = $100.00.


Handle the response

charge() returns a MerchantTransaction directly — there is no session to verify separately. Check transaction.status before fulfilling.

import { TRANSACTION_STATUS } from '@accrupay/node';

const transaction = await chargeStoredCard('pm_abc123', `charge_${Date.now()}`);

switch (transaction.status) {
case TRANSACTION_STATUS.SUCCEEDED:
// Charge captured — fulfill the order
await fulfillOrder(transaction);
break;

case TRANSACTION_STATUS.DECLINED:
// Card declined — notify the customer to update their payment method
await notifyPaymentDeclined(transaction);
break;

case TRANSACTION_STATUS.PENDING:
// Still processing — wait for a webhook or poll
await markChargePending(transaction);
break;

case TRANSACTION_STATUS.FAILED:
case TRANSACTION_STATUS.ERROR:
// Technical failure — retry with exponential backoff or alert your team
throw new Error(`Charge failed with status: ${transaction.status}`);

default:
throw new Error(`Unexpected charge status: ${transaction.status}`);
}
Always check the status

Server-side charges can still fail or be declined. Never assume a charge succeeded because the API call returned without throwing. transaction.status is the authoritative result.


Status reference

StatusMeaningAction
SUCCEEDEDCharge capturedFulfill the order
DECLINEDDeclined by issuerNotify customer to update payment method
PENDINGStill processingWait for webhook or poll
FAILEDTerminal failureRetry or alert
ERRORTechnical errorRetry or alert
CANCELEDCharge was canceledInvestigate and restart if needed
UNKNOWNStatus undeterminedContact support