1. Guides
Casharc
Merchant
  • Merchant
  • Getting Started
    • Introduction
    • Authentication
    • Quickstart
  • Guides
    • Idempotency
    • Verifying webhooks
    • Metadata
    • Statuses
    • Reason Codes
  • Payments
    • Create Payment
      POST
    • List Payments
      GET
    • Retrieve Payment
      GET
    • Confirm Payment
      POST
    • Cancel Payment
      POST
    • Refund Payment
      POST
  • Payouts
    • Create Payout
      POST
    • List Payouts
      GET
    • Retrieve Payout
      GET
    • Cancel Payout
      POST
  • Appeals
    • Create Appeal
      POST
    • List Appeals
      GET
    • Retrieve Appeal
      GET
    • Cancel Appeal
      POST
  • Balance
    • Retrieve Balance
      GET
  • Webhook
    • On Status Change
  1. Guides

Verifying webhooks

Anyone can send an HTTP POST to your webhook URL. To confirm that a request genuinely came from Casharc — and not an attacker — every webhook is signed. You verify the signature before trusting the payload.
Casharc signs each delivery with a secret unique to your webhook, so only you and Casharc can produce a valid signature.

The signing secret#

Each webhook has its own signing secret. Treat it like a password: store it server-side, never expose it in client code, and rotate it if it leaks.

How the signature is built#

Two headers are relevant:
HeaderDescription
X-TimestampUnix timestamp, in seconds, at which Casharc signed the event.
X-SignatureHex-encoded HMAC-SHA256 of the signed payload, using your secret.
The signed payload is the timestamp and the raw request body joined by a dot:
signed_payload = X-Timestamp + "." + raw_request_body
X-Signature    = hex( HMAC_SHA256(secret, signed_payload) )
Including the timestamp lets you reject old requests, which defeats replay attacks — an attacker who captures a valid webhook can't resend it later.

Verifying a request#

1.
Read the raw request body — the exact bytes received, before any JSON parsing or re-serialization. Reformatting the JSON changes the bytes and breaks the signature.
2.
Read the X-Timestamp and X-Signature headers.
3.
Reject the request if X-Timestamp is outside your tolerance window (5 minutes is a good default), to block replays.
4.
Compute HMAC-SHA256 over {X-Timestamp}.{raw_body} with your signing secret.
5.
Compare your result to X-Signature using a constant-time comparison.
6.
If they don't match, respond 400 and do not process the event.
PHP
Python
JavaScript

Common mistakes#

Signing the parsed body. Verify against the raw bytes, not a re-encoded object — key order and whitespace differ and the hash won't match.
Non-constant-time comparison. Use hash_equals / timingSafeEqual, not ==, to avoid timing attacks.
No timestamp check. Without the tolerance window, a captured request can be replayed indefinitely.
Trusting before verifying. Verify first, then parse and act on the event.
Previous
Idempotency
Next
Metadata
Built with