Webhooks
PayRequest can send a signed HTTP POST to your endpoint when a payment succeeds. Use this to automate fulfillment, update your database, or trigger downstream workflows without polling.
Setup
- Go to Settings → API & MCP in your PayRequest dashboard
- Scroll down to the Webhooks section
- Enter your endpoint URL (must be publicly reachable over HTTPS)
- Click Save — a signing secret is generated automatically
- Copy the Signing Secret and store it securely in your environment
Verifying signatures
Every request includes an X-PayRequest-Signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this before processing the event.
X-PayRequest-Signature: sha256=a1b2c3d4...
$secret = $_ENV['PAYREQUEST_WEBHOOK_SECRET'];
$body = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_PAYREQUEST_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit;
}
$event = json_decode($body, true);
Always use a timing-safe comparison (hash_equals, hmac.compare_digest, timingSafeEqual) to prevent timing attacks.
Event: payment.succeeded
Fired when a transaction status changes to paid. Delivered asynchronously via the queue (typically within seconds).
Payload
{
"event": "payment.succeeded",
"timestamp": "2026-05-30T12:00:00+02:00",
"data": {
"id": 1234,
"amount": 49.00,
"currency": "EUR",
"description": "JMIT-githubuser",
"payment_link_id": 42,
"payment_method": "ideal",
"reference": "tr_abc123",
"paid_at": "2026-05-30T11:59:45+02:00",
"customer": {
"id": 99,
"name": "Jane Doe",
"email": "jane@example.com"
}
}
}
Fields
| Field | Type | Description |
|---|
event | string | Always payment.succeeded |
timestamp | ISO 8601 | When the event was dispatched |
data.id | integer | Transaction ID |
data.amount | decimal | Amount paid (e.g. 49.00) |
data.currency | string | 3-letter ISO currency code |
data.description | string | Your custom reference (e.g. JMIT-githubuser) |
data.payment_link_id | integer|null | ID of the Smart Link used, if any |
data.payment_method | string | Payment method used (e.g. ideal, creditcard) |
data.reference | string | Payment provider transaction reference |
data.paid_at | ISO 8601 | Exact time the payment was confirmed |
data.customer | object|null | Customer name, email, and ID |
Retries
Webhook delivery is handled by the queue with 3 attempts and a 60-second backoff between retries. If all attempts fail, the event is dropped and a warning is logged.
Return any 2xx status code to acknowledge receipt.
Rotating the secret
If your secret is compromised, go to Settings → API & MCP → Webhooks and click Regenerate. Your old secret stops working immediately — update your environment variable before rotating.
Testing locally
Use a tunnel tool to expose your local server during development:
# Using ngrok
ngrok http 8000
# Then set your webhook URL to:
# https://abc123.ngrok.io/webhooks/payrequest