본문으로 건너뛰기

Webhooks

Receive real-time notifications when checkout events occur. Webhooks are the primary way to know when a payment is confirmed.

Setup

Configure your webhook URL in the Volr Dashboard under Project Settings > Checkout Config, or via CLI:

volr checkout setup

You'll receive a webhook secret (whsec_...) for signature verification.

Handling Webhooks

import { VolrCheckout, type WebhookPayload } from '@volr/checkout-sdk';

app.post('/webhooks/volr', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-volr-signature'] as string;
const event = req.headers['x-volr-event'] as string;
const payload = req.body.toString();

// 1. Verify signature
const isValid = await VolrCheckout.verifySignature(
payload,
signature,
process.env.VOLR_WEBHOOK_SECRET!,
);

if (!isValid) {
return res.status(401).send('Invalid signature');
}

// 2. Parse and handle event
const webhookEvent: WebhookPayload = JSON.parse(payload);

switch (webhookEvent.event) {
case 'checkout.paid':
await handlePaymentConfirmed(webhookEvent.data);
break;
case 'checkout.expired':
await handleCheckoutExpired(webhookEvent.data);
break;
case 'checkout.cancelled':
await handleCheckoutCancelled(webhookEvent.data);
break;
case 'checkout.settled':
await handleSettlement(webhookEvent.data);
break;
case 'checkout.late_paid':
await handleLatePaid(webhookEvent.data);
break;
}

// 3. Return 200 quickly
res.status(200).send('OK');
});
주의

Always verify the webhook signature before processing. Never trust unverified webhook payloads.

Webhook Events

EventDescriptionWhen
checkout.paidPayment confirmed on-chainCustomer payment is verified
checkout.expiredCheckout expiredNo payment received before expiry
checkout.cancelledCheckout was cancelledMerchant cancelled the checkout
checkout.settledFunds settled to merchantPayment fully settled
checkout.late_paidLate payment receivedPayment arrived after expiry

Webhook Payload

interface WebhookPayload {
event: string;
data: {
checkoutId: string;
projectId: string;
status: string;
referenceId: string | null;
chainId: number;
tokenAddress: string;
amount: string;
depositAddress: string;
fiatAmount: string | null;
fiatCurrency: string | null;
itemName: string | null;
customerEmail: string | null;
customerName: string | null;
paymentTxHash: string | null;
paidAmount: string | null;
paidAt: string | null;
createdAt: string;
expiresAt: string;
metadata: Record<string, unknown> | null;
};
timestamp: string;
}

Signature Verification

Webhooks are signed with HMAC-SHA256. The signature is sent in the X-Volr-Signature header.

// Using the SDK (recommended)
const isValid = await VolrCheckout.verifySignature(payload, signature, secret);

// Manual verification (any language)
import { createHmac } from 'crypto';

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret)
.update(payload)
.digest('hex');
return expected === signature;
}

Retry Policy

If your webhook endpoint returns a non-2xx response or times out, Volr retries with exponential backoff:

AttemptDelay
1stImmediate
2nd1 minute
3rd5 minutes
4th15 minutes

Each attempt has a 5-second timeout.

Best Practices

  1. Return 200 quickly — Process the event asynchronously after acknowledging receipt
  2. Handle duplicates — Use checkoutId as an idempotency key; you may receive the same event multiple times
  3. Verify signatures — Always validate the X-Volr-Signature header
  4. Use referenceId — Map webhooks to your internal orders via referenceId
  5. Log everything — Store raw webhook payloads for debugging

Next Steps