Processing at creation and a second notification carries the final result.
Register an endpoint
1
Expose an HTTPS endpoint
It must accept POST requests and return a
2xx status on receipt. Respond quickly and do the work asynchronously if it’s slow.2
Register it
3
Store the secret
webhook_secret signs every notification and is only returned by this call. Keep it in a secrets manager; never in source control or logs.PATCH /api/v4/merchantwebhooks/{id} with merchant_guid and the new webhook_url. Your secret is unchanged. GET /api/v4/merchantwebhooks/{merchantGUID} lists a merchant’s registrations but requires agency-level credentials.
Register separate endpoints for sandbox and live.
The payload
The body is JSON. Fields with anull value are omitted.
refund object and object_type: "refund".
uuid
The charge ID. Use it to retrieve the charge, or to re-send the notification.
string
Your reference — the
reference you sent on the charge, or the Widget’s MerchantReference. Match the notification to your order on this.string
string
Decimal with two places.
Bank charges arrive twice
A bank charge isProcessing ("9") when first notified; a later notification with the same id carries the final status. Treat id as the key and the latest notification as current. Make your handler idempotent — receiving the same status twice must be harmless.
Verify the signature
Every notification is signed so you can confirm it came from PayChoice and wasn’t altered in transit. Reject anything that fails.1
Take the raw body
Compute over the exact bytes of the request body as received — before any parsing or re-serialisation.
2
Compute HMAC-SHA256
Key: the UTF-8 bytes of your
webhook_secret string, used as-is. Do not Base64-decode it.3
Compare to the header
X-Webhook-Signature carries the digest as uppercase hexadecimal byte pairs separated by hyphens, e.g. 3A-F2-9C-…. Strip the hyphens, lowercase, and compare to your own hex digest with a constant-time comparison.id / status pairs if you need replay protection.

