RapidCents Developer Documentation
Webhooks Integration Guide
v1.0 · Webhooks

Webhooks

Server-to-server event notifications · Signature verification · Checkout & payment lifecycle · Voids & refunds

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).

HeaderRequiredNotes
Content-TypeYesMust be application/json
Signature or X-SignatureWhen secret setHMAC-SHA256 of the raw body
X-Gateway-Environment-IdOptionalIdentifies the originating gateway environment

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

Always compute the HMAC over the raw request body bytes, before any JSON parsing or re-serialization. Re-encoding the payload will change the signature and cause valid webhooks to be rejected.

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.

FieldTypeDescription
eventTypestringEvent name, prefixed with rapidcents.
webhookIdstringUnique webhook delivery id (primary dedup key)
notificationIdstringNotification id (dedup fallback)
eventDatestringISO-8601 timestamp of the event
payloadobjectNested event data (see inner payload fields)
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:

FieldDescription
sourceNameShould be checkout_session for checkout integrations.
sourceIdGateway checkout session UUID — primary correlation key.
sessionTokenPublic session token (same as create/pay response token).
sessionStatusGateway session lifecycle: pending, completed, expired, cancelled, failed.
paymentStatusGateway payment state: unpaid, paid, failed, etc.
metadata.local_checkout_session_idOptional id you sent at session create — fastest lookup in this app.
transaction.idCharge / transaction id for void, refund, and reconciliation.
transaction.authAmountAmount for refund webhooks (preferred over amountTotal).
isFinalFailureOn 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.

ActionRequestAwaited Webhook
Void POST {API origin}/api/{business_id}/transactions/{transaction_id}/void payment.voided
Refund POST {API origin}/api/{business_id}/transactions/{transaction_id}/refund with amount 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 (https://rapidcheckout.rapidcents.net/)

ConcernImplementation
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

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

Idempotency matters. Webhooks may be retried or delivered out of order. Always make state transitions safe to apply more than once and never downgrade a terminal state (e.g., do not move a refunded payment back to paid).