Card Checkout
This guide walks through the complete session-based card checkout flow: create a payment session on your server, render the hosted card fields on your frontend, and verify the result on your server.
Flow overview
- Server — start a payment session
- Server → Client — pass only
session.idto the frontend - Client — render the checkout form and let the customer submit
- Server — verify the transaction and act on the status
Step 1: Start a session (server)
import AccruPay, { TRANSACTION_PROVIDER, CURRENCY, COUNTRY_ISO_2 } from '@accrupay/node';
const sdk = new AccruPay({
apiSecret: process.env.ACCRUPAY_API_SECRET!,
});
export async function startPaymentSession(
customerCode: string,
transactionCode: string,
) {
const session = await sdk.transactions.clientSessions.payments.start({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
data: {
merchantInternalTransactionCode: transactionCode,
amount: 10000n, // $100.00
currency: CURRENCY.USD,
storePaymentMethod: false,
customer: {
merchantInternalCustomerCode: customerCode,
},
billing: {
billingFirstName: 'Jane',
billingLastName: 'Smith',
billingEmail: 'jane@example.com',
billingAddressCountry: COUNTRY_ISO_2.US,
billingPhone: '+15551234567',
billingAddressState: 'CA',
billingAddressCity: 'San Francisco',
billingAddressLine1: '123 Market St',
billingAddressPostalCode: '94105',
},
},
});
return session;
}
AccruPay amounts are bigint in the smallest currency unit (cents for USD). 10000n = $100.00.
Step 2: Send session.id to the frontend
Only send the session id to the client — never expose the full session object or your API secret.
// Example: Express route
app.post('/api/checkout/start', async (req, res) => {
const session = await startPaymentSession(
req.user.id,
`txn_${Date.now()}`,
);
res.json({ sessionId: session.id });
});
Step 3: Render the checkout form (React)
import {
AccruPay,
CardholderName,
CardNumber,
CardExpiry,
CardCVC,
SubmitButton,
} from '@accrupay/react';
function CheckoutForm({
sessionId,
onSuccess,
}: {
sessionId: string;
onSuccess: (transactionCode: string) => void;
}) {
return (
<AccruPay
merchantPublicId={process.env.NEXT_PUBLIC_ACCRUPAY_MERCHANT_ID!}
transactionSessionId={sessionId}
>
{/* Standard input — accepts all HTML input attributes */}
<CardholderName
placeholder="Full name on card"
className="checkout-input"
/>
{/* Embedded secure fields — only className and style are supported */}
<CardNumber className="checkout-input" />
<CardExpiry className="checkout-input" />
<CardCVC className="checkout-input" />
<SubmitButton
className="checkout-btn"
onSuccess={(result) => onSuccess(result.merchantInternalTransactionCode)}
onError={(err) => console.error('Payment failed', err)}
>
Pay $100.00
</SubmitButton>
</AccruPay>
);
}
CardNumber, CardExpiry, and CardCVC are embedded secure iframes. Do not attach onChange or onBlur handlers to them — card data never touches your JavaScript context. CardholderName is a standard text input and accepts all standard HTML input attributes.
Step 4: Verify the payment (server)
Call verify() after the customer submits. The onSuccess callback is a convenience signal only — always verify server-side.
import { TRANSACTION_STATUS } from '@accrupay/node';
export async function verifyPayment(merchantInternalTransactionCode: string) {
const transaction =
await sdk.transactions.clientSessions.payments.verify({
merchantInternalTransactionCode,
});
switch (transaction.status) {
case TRANSACTION_STATUS.SUCCEEDED:
// Fulfill the order
await fulfillOrder(transaction);
break;
case TRANSACTION_STATUS.PENDING:
case TRANSACTION_STATUS.STARTED:
// Payment is still processing — poll or wait for a webhook
await markOrderPending(transaction);
break;
case TRANSACTION_STATUS.DECLINED:
// Card was declined — prompt the customer to use a different card
throw new Error('Card declined');
case TRANSACTION_STATUS.FAILED:
case TRANSACTION_STATUS.ERROR:
// Technical failure — safe to retry with a new session
throw new Error('Payment failed — please try again');
case TRANSACTION_STATUS.CANCELED:
throw new Error('Payment was canceled');
case TRANSACTION_STATUS.EXPIRED:
// Session timed out before the customer completed checkout
throw new Error('Checkout session expired');
default:
throw new Error(`Unexpected status: ${transaction.status}`);
}
return transaction;
}
The onSuccess callback in the React SDK confirms the form was submitted — it does not confirm the payment succeeded. verify() is the authoritative source of truth. Always verify on your server before fulfilling any order.
Status reference
| Status | Meaning | Action |
|---|---|---|
SUCCEEDED | Payment captured | Fulfill the order |
PENDING | Processing, final status pending | Wait / poll / webhook |
STARTED | Session opened, not yet submitted | Same as PENDING |
DECLINED | Card declined by issuer | Prompt for a new card |
FAILED | Terminal failure | Retry with a new session |
ERROR | Technical error | Retry with a new session |
CANCELED | Explicitly canceled | Allow customer to restart |
EXPIRED | Session timed out | Start a new session |
UNKNOWN | Status could not be determined | Contact support |