Agent skill
HitPay Webhooks Skill
Receive and verify HitPay event webhooks. Use when setting up HitPay webhook handlers, validating the Hitpay-Signature header (HMAC-SHA256 with the per-webhook salt), dispatching on Hitpay-Event-Object and Hitpay-Event-Type, or handling charge.created, order.created, invoice.created and transfer.paid events.
Install this skill
npx skills add hookdeck/webhook-skills --skill hitpay-webhooks
HitPay is a payments platform (Singapore and Southeast Asia). Its event webhooks, registered under Developers > Webhooks, are HTTP POSTs sent after events such as a completed payment, a new order or a transfer. Each request carries Hitpay-Signature: the hex HMAC-SHA256 of the raw JSON body, keyed with that webhook endpoint's salt.
Not covered: the legacy payment-request and charge callbacks (form-encoded, with an hmac field in the payload, keyed with the API-key salt from the main Developers page). They use a different scheme: sorted, concatenated parameters rather than the raw body.
When to Use This Skill
- How do I receive HitPay webhooks?
- How do I verify the
Hitpay-Signatureheader? - Which salt do I use: the API-key salt or the webhook salt?
- How do I tell
charge.createdfromorder.created? (The body has no event name.) - Why is my HitPay webhook signature verification failing?
Verification (core)
const crypto = require('crypto');
function verifyHitpaySignature(rawBody, signature, salt = process.env.HITPAY_WEBHOOK_SALT) {
if (!salt) return false; // never accept when unconfigured
// Check shape first: Buffer.from(x, 'hex') silently truncates invalid hex.
if (typeof signature !== 'string' || !/^[0-9a-f]{64}$/i.test(signature)) return false;
const expected = crypto.createHmac('sha256', salt).update(rawBody).digest('hex');
// Equal-length (64 char) buffers, so timingSafeEqual cannot throw.
return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase()), Buffer.from(expected));
}
Python: hmac.compare_ on bytes, so a non-ASCII header gives 401 rather than a TypeError and a 500. Use the raw body bytes, never re-serialised JSON. Express: express.raw({ type: '*/*' }) so any content type verifies. Details in references/verification.md.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
The body is the object itself and carries no event name. The event arrives in two headers: Hitpay-Event-Object (charge, payout, invoice, order, transfer) and Hitpay-Event-Type (created, updated, ...). Dispatch on {Hitpay-Event-Object}.{Hitpay-Event-Type}.
| Event | Triggered When |
|---|---|
charge.created | A payment is successfully completed |
charge.updated | A payment is refunded or partially refunded |
payout.created | A payout is successfully completed |
order.created | An order is created |
order.updated | An order status is updated |
invoice.created | An invoice is created |
transfer.created, transfer.updated | A transfer is created or updated |
transfer.processing, transfer.scheduled | A transfer is processed or scheduled |
transfer.paid, transfer.failed, transfer.canceled | A transfer is paid, fails or is canceled |
HitPay's list also names payment_request.* and recurring_billing.* events; see references/overview.md.
Environment Variables
HITPAY_WEBHOOK_SALT=your_webhook_salt_from_dashboard # Developers > Webhooks > the webhook's detail view
Setup
Register the endpoint under Developers > Webhooks in the HitPay dashboard and copy the salt from that webhook's detail view (not the API-key salt on the main Developers page). Details in references/setup.md.
With Hookdeck: only HitPay App webhooks (Developers > Webhooks), which send a Hitpay-Signature header, are supported. Paste that webhook's salt as the source's webhook secret. Legacy payment-request callbacks aren't supported.
Local Development
For local webhook testing, run the Hookdeck CLI via npx (no account required):
npx hookdeck-cli listen 3000 hitpay --path /webhooks/hitpay
Paste the printed URL into HitPay Dashboard > Developers > Webhooks.
Resources
- references/overview.md - What HitPay webhooks are, event types, headers, payloads
- references/setup.md - Register a webhook, find the salt, use Hookdeck
- references/verification.md - Signature details, the two salts, gotchas, debugging
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests