Skip to content

RapidCents

Webhooks

RapidCents sends server-to-server POST notifications when checkout sessions and payments change. Treat verified webhooks as the source of truth for settlement, voids, and refunds—return URLs and iframe messages are UX signals only.

Complements checkout guides

Session creation and payment UI are covered in the checkout overview. This page covers receiving and processing outbound events.

Endpoint & headers

Configure RapidCents to POST JSON to your HTTPS endpoint. In this demo app:

POST {APP_URL}/webhooks/payment

Replace {APP_URL} with your public base URL (must match the active gateway environment).

Content-Type Yes
Signature or X-Signature When secret set
X-Gateway-Environment-Id Optional

Respond with HTTP 2xx and a small JSON body (for example {"ok": true}) promptly. Defer slow work to a queue if needed.

Signature validation

When webhook_secret is stored on the gateway environment, the demo app requires a valid signature. Algorithm: hash_hmac('sha256', raw_body, webhook_secret) compared with constant-time equality to the header value.

Verify signature (pseudocode)
expected = HMAC_SHA256(raw_request_body, webhook_secret)
provided = request.header("Signature") ?? request.header("X-Signature")
if webhook_secret is set and not secure_compare(expected, provided):
    return 401

Envelope structure

Current RapidCents outbound webhooks use an envelope with eventType, identifiers, and a nested payload object. Event types are prefixed with rapidcents. (for example rapidcents.payment.succeeded); handlers typically strip that prefix.

eventType string
webhookId string
notificationId string
eventDate string
payload object
Envelope (illustrative)
{
  "eventType": "rapidcents.payment.succeeded",
  "webhookId": "wh_01HXABCDEF",
  "notificationId": "ntf_01HXABCDEF",
  "eventDate": "2026-05-22T14:30:00Z",
  "payload": {
    "sourceName": "checkout_session",
    "sourceId": "550e8400-e29b-41d4-a716-446655440000",
    "sessionToken": "cs_live_a1b2c3",
    "sessionStatus": "completed",
    "paymentStatus": "paid",
    "amountTotal": 49.99,
    "currency": "USD",
    "metadata": {
      "local_checkout_session_id": "164",
      "order_id": "42"
    },
    "transaction": {
      "id": "txn_987654",
      "originalTransactionId": "txn_987654",
      "authAmount": 49.99
    }
  }
}

Event types

Checkout-related events handled by this demo app (suffix after rapidcents.):

checkout.session.created Session opened at gateway
checkout.session.expired Session timed out
checkout.session.cancelled Shopper or merchant cancelled
payment.succeeded Charge approved
payment.failed Decline or error
payment.voided Authorization voided
payment.refunded Full refund
payment.partially_refunded Partial refund

Payment events with sourceName other than checkout_session are ignored by this demo. Legacy suffixes such as checkout.payment.succeeded are normalized to payment.succeeded.

Inner payload fields

Use these fields to correlate a webhook with your local checkout session:

sourceName Should be checkout_session for checkout integrations.
sourceId Gateway checkout session UUID — primary correlation key.
sessionToken Public session token (same as create/pay response token).
sessionStatus Gateway session lifecycle: pending, completed, expired, cancelled, failed.
paymentStatus Gateway payment state: unpaid, paid, failed, etc.
metadata.local_checkout_session_id Optional id you sent at session create — fastest lookup in this app.
transaction.id Charge / transaction id for void, refund, and reconciliation.
transaction.authAmount Amount for refund webhooks (preferred over amountTotal).
isFinalFailure On payment.failed, when true marks terminal failure.

Sample payloads

payment.succeeded

Inner payload (abbreviated)
{
  "sourceName": "checkout_session",
  "sourceId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionToken": "cs_live_a1b2c3",
  "sessionStatus": "completed",
  "paymentStatus": "paid",
  "amountTotal": 29.99,
  "currency": "USD",
  "metadata": { "local_checkout_session_id": "164" },
  "transaction": {
    "id": "txn_987654",
    "originalTransactionId": "txn_987654",
    "authAmount": 29.99
  }
}

payment.voided

Inner payload (abbreviated)
{
  "sourceName": "checkout_session",
  "sourceId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionToken": "cs_live_a1b2c3",
  "sessionStatus": "cancelled",
  "paymentStatus": "voided",
  "transaction": {
    "id": "txn_987654",
    "originalTransactionId": "txn_987654"
  }
}

payment.partially_refunded

Inner payload (abbreviated)
{
  "sourceName": "checkout_session",
  "sourceId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionToken": "cs_live_a1b2c3",
  "sessionStatus": "completed",
  "paymentStatus": "partially_refunded",
  "amountTotal": 29.99,
  "transaction": {
    "id": "txn_987654",
    "originalTransactionId": "txn_987654",
    "authAmount": 5.00
  }
}

authAmount on the transaction object is the refunded amount for this event. Full refunds use payment.refunded with the same shape.

payment.failed

Inner payload (abbreviated)
{
  "sourceName": "checkout_session",
  "sessionToken": "cs_live_a1b2c3",
  "sessionStatus": "pending",
  "paymentStatus": "failed",
  "isFinalFailure": false
}

Void & refund

Void and refund are initiated through RapidCents transaction APIs (from your server or this app’s admin UI). Status changes are applied when the matching webhook arrives, not when the API returns.

  • VoidPOST {API origin}/api/{business_id}/transactions/{transaction_id}/void → await payment.voided.
  • RefundPOST {API origin}/api/{business_id}/transactions/{transaction_id}/refund with amount → await payment.refunded or payment.partially_refunded.

Admin UI in this demo

Admin → Checkout sessions → Void / Refund submits the API call and shows a pending state until the webhook updates payment_status and refund history.

This demo app

  • Entry pointWebhookControllerWebhookProcessingService.
  • Deduppayment_webhooks.event_id stores webhookId (fallback notificationId).
  • Lookup ordermetadata.local_checkout_session_id, stored gateway_source_id, external_session_id / token.
  • Columnscheckout_sessions.status (session lifecycle) and payment_status (paid, voided, refunded, etc.) are synced from webhook payloads.

Handler checklist

Recommended processing flow
1. Read raw body; verify HMAC when webhook_secret is configured.
2. Parse JSON envelope; dedupe by webhookId.
3. Branch on eventType (strip rapidcents. prefix).
4. Resolve local session using sourceId, sessionToken, metadata.
5. Update order / payment state idempotently.
6. Return 2xx quickly; log unmatched events for investigation.