Handle Webhook
This guide shows how to receive, validate, and process webhook events using your prefered language
Requirements
An HTTPS endpoint that accepts
POSTPHP, Python, NodeJS, Java...
Secure & Handle Webhook
When your server receives a webhook, you should verify the signature before processing it. This ensures the request really comes from the platform and not from someone else.
Example payload (short)
Here is an example of the data you will receive, it contains all the necessary data related to the payment: user identifiers, basket contents, price, payment ID...
{
"mode": "live",
"event": "payment.success",
"created_at": "2025-09-04T13:45:44-04:00",
"request_id": "51b97ba5891ec220e8b64385a00c3826",
"webhook_id": "458",
"store_id": "7541",
"data": {
"id": 71134,
"transaction_id": "68B9D0471D02A",
"gateway": "paypal",
"amount": { "total_paid": 12, "currency": "EUR" },
"user": { "email": "[email protected]", "discord_id": "123", "username": "Player" },
"basket": [{ "id": 183, "name": "VIP Rank", "price": 10, "quantity": 1 }],
"actions": { "...": "..." }
}
}Create an endpoint
Create a file in your chosen language and start a server that listens for POST requests using the following examples:
How it works
Read the body → the raw JSON payload sent by the platform.
Get headers →
X-Pay-TimestampandX-Pay-Signature(provided in every request).Check freshness → reject if older than 5 minutes.
Recalculate signature →
HMAC-SHA256(timestamp.body, secret).Compare signatures → use
hash_equalsto prevent timing attacks.Process event → decode JSON and handle it (e.g., update DB).
How it works
Raw body is required — don’t use
express.json()directly, otherwise the signature check will fail.Check timestamp (
X-Pay-Timestamp) — reject if older than 5 minutes.Recalculate signature:
HMAC-SHA256(timestamp.body, secret)with the base64-decoded secret.Compare with
X-Pay-Signatureusingcrypto.timingSafeEqualfor safety.Process the event (decode JSON and apply business logic).
How it works
Read the body → the raw JSON payload sent by Tip4Serv.
Get headers →
X-Pay-TimestampandX-Pay-Signature(sent with every request).Check freshness → reject if the webhook is older than 5 minutes.
Recalculate signature →
HMAC-SHA256(timestamp.body, secret)using your Base64-decoded secret.Compare signatures → use
hmac.compare_digestto avoid timing attacks.Process event → decode JSON and handle it (e.g., update your database).
How it works
Read the body →
@RequestBody String bodycontains the raw JSON payload.Get headers →
X-Pay-TimestampandX-Pay-Signatureare extracted with@RequestHeader.Check freshness → reject if the timestamp is more than 5 minutes old.
Recalculate signature → compute
HMAC-SHA256(timestamp.body, secret)with your Base64-decoded secret.Compare signatures → ensure
expected == signaturebefore processing.Process event → parse JSON with Jackson (
ObjectMapper) and act on theevent.
Tips & best practices
Respond fast (under a few seconds). Offload heavy work to a queue/worker.
Treat optional fields defensively: not every
useridentifier orcustom_fieldswill be present.Log the raw payload (with privacy in mind) to help debugging.
Last updated