Webhook Basics

Ching POSTs events to URLs you register. Each delivery is signed with your endpoint's secret so you can trust its origin.

Event Shape

{
  "id": "evt_m2n3o4p5q6r7",
  "type": "charge.succeeded",
  "data": {
    "id": "ch_9mTPfRSDmEOU",
    "amount": 9900,
    "currency": "ils",
    "customer": "cus_V8ltq1pK_MWH"
  },
  "livemode": false,
  "created": "2026-04-19T09:15:22.000Z"
}

Verify the Signature

Every delivery includes a Ching-Signature header whose value is HMAC-SHA256(raw_body, endpoint_secret) as a lowercase hex digest. Compute the same on your side and compare with a timing-safe check:

Node.js
import crypto from "node:crypto";

export function verifyChingSignature(
  rawBody: string,
  signature: string,
  secret: string,
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(signature, "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Use the raw body: Verify against the exact bytes Ching sent, not the JSON-parsed object. Parse after verifying.

Event Types

The most common events to subscribe to:

TypeFires when
charge.succeededA charge completed successfully
charge.failedA charge was declined
refund.succeededA refund landed at the provider
setup_session.succeededA customer finished adding a card
subscription.createdA new subscription started (including trial)
subscription.updatedPlan change, cancel-at-period-end toggle, renewal
subscription.canceledA subscription ended
checkout_session.completedA hosted checkout was paid or applied
payment_method.detachedA customer removed a card

Subscribe to ["*"] to receive every event.

Retries

Deliveries time out after 10 seconds. If your endpoint returns a non-2xx or times out, Ching retries up to 3 total attempts via a background cron. To be a good webhook consumer:

  • Return 200 as soon as you have persisted the event id.
  • Do heavy work asynchronously - don't make Ching wait for your database, email provider, or third-party API.
  • Deduplicate by event.id - a retry after a 500 will re-send the same id.