Skip to Content
đź‘‹ Welcome to 100Pay Developers
DocsWebhooksTransaction Session Callbacks

Transaction Session Callbacks

When you create a payment session with a callbackUrl (e.g. a virtual-account funding session), 100Pay POSTs a callback to that URL when the session ends.

EventTrigger
transaction_session.completedThe session received full payment
transaction_session.expiredThe session timed out before full payment

Unlike other webhook families, these are sent to the session’s callbackUrl rather than your registered webhook endpoints. If the callbackUrl matches one of your registered endpoints, delivery goes through the standard webhook pipeline (dedupe, retries, verification-token header); otherwise it is a direct POST with no retries — prefer using your registered webhook URL as the callbackUrl.

Payload

Note the event field is named event (not eventType).

interface TransactionSessionCallback { event: "transaction_session.completed" | "transaction_session.expired"; eventId: string; // UUID timestamp: string; // ISO 8601 sessionId: string; status: "completed" | "expired"; channel: string; // e.g. "virtualAccount" requestedAmount: number; totalAmountPaid: number; currency: string; paymentsCount: number; // Present on expiry reconciliation: paymentStatus?: { isPartial: boolean; isComplete: boolean; percentageComplete: number; }; }

Every request also carries an X-Session-Id header with the session ID.

Example — completed

{ "event": "transaction_session.completed", "eventId": "4a5b6c7d-8e9f-4a1b-9c2d-3e4f5a6b7c8d", "timestamp": "2026-03-12T14:41:00.000Z", "sessionId": "VIR_SESSION_1700000000000_XXXXXXXX", "status": "completed", "channel": "virtualAccount", "requestedAmount": 50000, "totalAmountPaid": 50000, "currency": "NGN", "paymentsCount": 1 }

Example — expired with partial payment

{ "event": "transaction_session.expired", "eventId": "5b6c7d8e-9f0a-4b2c-8d3e-4f5a6b7c8d9e", "timestamp": "2026-03-12T15:10:00.000Z", "sessionId": "VIR_SESSION_1700000000000_YYYYYYYY", "status": "expired", "channel": "virtualAccount", "requestedAmount": 50000, "totalAmountPaid": 20000, "currency": "NGN", "paymentsCount": 1, "paymentStatus": { "isPartial": true, "isComplete": false, "percentageComplete": 40 } }

Handling

async function handleSession(event: TransactionSessionCallback) { if (event.event === "transaction_session.completed") { await markOrderPaid(event.sessionId, event.totalAmountPaid); return; } // Expired — check for partial payment if (event.totalAmountPaid > 0) { await handlePartialPayment(event.sessionId, event.totalAmountPaid, event.requestedAmount); } else { await cancelOrder(event.sessionId); } }

Payments received by a session also emit their own payment charge events. Use the session callback for session lifecycle (paid in full / expired), and the charge events for individual payments.

Last updated on