webhook
Webhooks
Receive real-time HTTP notifications when payment events occur.
schedule
Near-instant
Seconds after confirmation
replay
Auto-retry
3 attempts, exponential backoff
verified_user
HMAC signed
SHA-256 signature header
Events
| Event | Description |
|---|---|
| payment.created | Payment was created |
| payment.confirmed | Deposit received and confirmed |
| payment.expired | Payment expired (15 min timeout) |
HTTP Headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| X-PayCrypt-Event | Event name (e.g. payment.confirmed) |
| X-PayCrypt-Signature | HMAC SHA-256 signature (if webhook secret is set) |
Payload Examples
jsonpayment.created
{
"event": "payment.created",
"payment_id": "9515b51e-0279-4294-805d-91f7762914c3",
"order_id": "123",
"status": "pending",
"amount": 50,
"coin": "USDT",
"network": "ethereum",
"currency": "USD",
"deposit_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68",
"expires_at": "2026-02-20T10:10:46.000Z",
"created_at": "2026-02-20T09:55:46.541Z"
}jsonpayment.confirmed
{
"event": "payment.confirmed",
"payment_id": "9515b51e-0279-4294-805d-91f7762914c3",
"order_id": "123",
"status": "confirmed",
"amount": 50,
"amount_received": 50.02,
"amount_received_usd": 50.02,
"commission_rate_percent": 0.2,
"commission_amount_usd": 0.10,
"amount_net_usd": 49.92,
"coin": "USDT",
"network": "ethereum",
"currency": "USD",
"deposit_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68",
"tx_hash": "0x8a2f7b3c9d1e5f6a...",
"confirmations": 12,
"confirmed_at": "2026-02-20T10:02:15.844Z",
"customer_email": "customer@example.com",
"metadata": { "plan": "pro" }
}jsonpayment.expired
{
"event": "payment.expired",
"payment_id": "9515b51e-0279-4294-805d-91f7762914c3",
"order_id": "123",
"status": "expired",
"amount": 50,
"coin": "USDT",
"network": "ethereum",
"currency": "USD",
"deposit_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68",
"expires_at": "2026-02-20T10:10:46.000Z",
"created_at": "2026-02-20T09:55:46.541Z"
}Signature Verification
If you set a webhook secret in Dashboard → Integrations, every webhook includes an X-PayCrypt-Signature header. Verify it with HMAC SHA-256.
javascriptNode.js
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const sig = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(sig, 'hex'),
Buffer.from(expected, 'hex')
);
}
// Express handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-paycrypt-signature'];
const isValid = verifyWebhookSignature(req.body, signature, 'your_secret');
if (!isValid) return res.status(401).send('Invalid signature');
const event = JSON.parse(req.body.toString());
console.log('Event:', event.event, event.payment_id);
res.status(200).send('OK');
});phpPHP
<?php
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYCRYPT_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';
$expected = hash_hmac('sha256', $rawBody, $secret);
$sig = str_replace('sha256=', '', $signature);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
die('Invalid signature');
}
$event = json_decode($rawBody, true);
// Handle $event['event'], $event['payment_id'], etc.
http_response_code(200);
echo 'OK';Retry Policy
If your endpoint returns a non-2xx status code or times out, PayCrypt retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st attempt | Immediate |
| 2nd attempt | ~30 seconds |
| 3rd attempt | ~2 minutes |
info
Each delivery attempt is recorded and visible in Dashboard → Integrations → Deliveries.