Webhooks Guide
Webhooks allow you to receive real-time HTTP notifications when events occur in your LuxCore account, such as when a payment completes or fails.
How Webhooks Work
Event Occurs
A payment status changes (e.g., completed, failed)
LuxCore Sends Notification
We send an HTTP POST request to your configured endpoint
You Process the Event
Your server processes the event and returns a 2xx response
Retry if Needed
If delivery fails, we retry with exponential backoff
Setting Up Webhooks
Create a Webhook Endpoint
curl -X POST "https://api.lux-core.io/api/v1/webhooks" \
-H "X-API-Key: qp_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"merchant_id": "123",
"url": "https://your-server.com/webhooks/luxcore",
"events": ["payment.completed", "payment.failed"],
"signature_algorithm": "hmac_sha256"
}'
Response
{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://your-server.com/webhooks/luxcore",
"events": ["payment.completed", "payment.failed"],
"status": "active",
"signature_algorithm": "hmac_sha256",
"created_at": "2025-01-21T10:30:00Z"
}
}
The webhook secret is only shown once when creating the webhook. Store it securely!
Webhook Events
| Event | Description |
|---|
payment.created | Payment was initialized |
payment.processing | Payment is being processed |
payment.completed | Payment completed successfully |
payment.failed | Payment failed |
payment.cancelled | Payment was cancelled |
payment.refunded | Payment was refunded |
payout.created | Payout was initiated |
payout.completed | Payout completed successfully |
payout.failed | Payout failed |
webhook.test | Test event for verification |
Webhook Payload
All webhook payloads follow this structure:
{
"event": "payment.completed",
"payment": {
"id": "pay_1234567890_abcdefgh",
"status": "completed",
"amount": 100050,
"currency": "ARS",
"merchant_reference": "order_123456",
"method": "bank_transfer",
"type": "deposit",
"fee": 250,
"net_amount": 99800,
"completed_at": "2025-01-21T10:35:00Z",
"created_at": "2025-01-21T10:30:00Z",
"updated_at": "2025-01-21T10:35:00Z"
},
"timestamp": 1737452100
}
Each webhook request includes these headers:
| Header | Description |
|---|
X-Webhook-Event | Event type (e.g., payment.completed) |
X-Webhook-Timestamp | Unix timestamp when signature was created |
X-Webhook-Signature | HMAC-SHA256 signature for verification |
X-Webhook-Retry | true if this is a retry attempt |
Content-Type | Always application/json |
Signature Verification
Always verify webhook signatures to ensure requests are from LuxCore.
X-Webhook-Signature: sha256=<hmac_signature>
Verification Algorithm
payload_to_sign = `${timestamp}.${JSON.stringify(body)}`
expected_signature = HMAC_SHA256(webhook_secret, payload_to_sign)
Implementation Examples
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, timestamp, secret) {
// Reject old timestamps (> 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - timestamp) > 300) {
throw new Error('Timestamp too old');
}
// Calculate expected signature
const payloadToSign = `${timestamp}.${JSON.stringify(payload)}`;
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payloadToSign)
.digest('hex');
// Compare signatures
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)) {
throw new Error('Invalid signature');
}
return true;
}
// Express.js example
app.post('/webhooks/luxcore', express.json(), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = parseInt(req.headers['x-webhook-timestamp']);
try {
verifyWebhookSignature(req.body, signature, timestamp, WEBHOOK_SECRET);
// Process the event
const { event, payment } = req.body;
console.log(`Received ${event} for payment ${payment.id}`);
// Handle specific events
switch (event) {
case 'payment.completed':
// Update order status, send confirmation email, etc.
break;
case 'payment.failed':
// Handle failed payment
break;
}
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook error:', error.message);
res.status(400).json({ error: error.message });
}
});
Retry Policy
If webhook delivery fails (non-2xx response or timeout), we retry with exponential backoff:
| Attempt | Delay |
|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 8 hours |
After 6 failed attempts, the webhook is marked as failed. You can manually retry failed events via the API.
Testing Webhooks
Send a test event to verify your endpoint:
curl -X POST "https://api.lux-core.io/api/v1/webhooks/wh_abc123/test" \
-H "X-API-Key: qp_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"event_type": "payment.completed"
}'
Managing Webhooks
List Webhooks
curl -X GET "https://api.lux-core.io/api/v1/webhooks" \
-H "X-API-Key: qp_test_your_key"
Update Webhook
curl -X PUT "https://api.lux-core.io/api/v1/webhooks/wh_abc123" \
-H "X-API-Key: qp_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"events": ["payment.completed", "payment.failed", "payout.completed"]
}'
Delete Webhook
curl -X DELETE "https://api.lux-core.io/api/v1/webhooks/wh_abc123" \
-H "X-API-Key: qp_test_your_key"
Best Practices
Always Verify Signatures
Never process webhooks without verifying the signature first
Respond Quickly
Return 200 immediately, process events asynchronously
Handle Duplicates
Use payment ID for idempotency - you may receive the same event twice
Log Everything
Log webhook payloads for debugging and audit trails
Webhook endpoints must be publicly accessible HTTPS URLs. Self-signed certificates are not supported.