Skip to main content

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

1

Event Occurs

A payment status changes (e.g., completed, failed)
2

LuxCore Sends Notification

We send an HTTP POST request to your configured endpoint
3

You Process the Event

Your server processes the event and returns a 2xx response
4

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

EventDescription
payment.createdPayment was initialized
payment.processingPayment is being processed
payment.completedPayment completed successfully
payment.failedPayment failed
payment.cancelledPayment was cancelled
payment.refundedPayment was refunded
payout.createdPayout was initiated
payout.completedPayout completed successfully
payout.failedPayout failed
webhook.testTest 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
}

Webhook Headers

Each webhook request includes these headers:
HeaderDescription
X-Webhook-EventEvent type (e.g., payment.completed)
X-Webhook-TimestampUnix timestamp when signature was created
X-Webhook-SignatureHMAC-SHA256 signature for verification
X-Webhook-Retrytrue if this is a retry attempt
Content-TypeAlways application/json

Signature Verification

Always verify webhook signatures to ensure requests are from LuxCore.

Signature Format

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:
AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 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.