Documentation

Webhook Authentication

To ensure the integrity and authenticity of notifications, each webhook sent by Ecart Pay includes security headers that you can use to verify its origin.

Security Headers

Each webhook includes headers to verify the authenticity of the notification:

Content-Type: application/json
x-pay-timestamp: 1642234567890
x-pay-signature: SHA256=abc123def456...
x-pay-webhook-id: hook_12345678-1234-1234-1234-123456789abc

Header Description

  • Content-Type: Always application/json
  • x-pay-timestamp: Request timestamp (in milliseconds, UTC) of when the webhook was sent.
  • x-pay-signature: HMAC SHA256 signature to validate the integrity and authenticity of the notification.
  • x-pay-webhook-id: Unique webhook identifier

Verify Signatures

To verify that the webhook comes from Ecart Pay, you must follow these steps:

1. Get the Secret

Each account has a unique secret that you can obtain from your administration panel. This secret is unique and must be stored securely.

2. Build the Base String

Concatenate the timestamp, webhook_id and the data body with the following format:

{timestamp}.{webhook_id}.{JSON.stringify(data)}

3. Generate the Signature

Use your secret key and the HMAC-SHA256 algorithm:

const crypto = require('crypto');

function verifyWebhookSignature(timestamp, webhookId, body, secret) {
  const signatureString = `${timestamp}.${webhookId}.${JSON.stringify(body)}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signatureString)
    .digest('hex');
  
  return `SHA256=${expectedSignature}`;
}

4. Compare Signatures

Compare the generated value with the one received in the x-pay-signature header. Ignore the SHA256= prefix when comparing.

Implementation Examples

Node.js

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = 'your_webhook_secret';

app.post('/webhook', (req, res) => {
  try {
    // 1. Get headers
    const timestamp = req.headers['x-pay-timestamp'];
    const signature = req.headers['x-pay-signature'];
    const webhookId = req.headers['x-pay-webhook-id'];
    
    if (!timestamp || !signature || !webhookId) {
      return res.status(400).json({ error: 'Missing headers' });
    }
    
    // 2. Verify signature
    const signatureString = `${timestamp}.${webhookId}.${JSON.stringify(req.body)}`;
    const expectedSignature = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(signatureString)
      .digest('hex');
    
    if (signature !== `SHA256=${expectedSignature}`) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // 3. Process webhook
    console.log('Verified webhook:', req.body);
    
    // 4. Respond successfully
    res.status(200).json({ success: true });
    
  } catch (error) {
    console.error('Error verifying signature:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Security Best Practices

  1. Always verify the signature before processing the webhook.
  2. Use HTTPS to receive notifications.
  3. Keep the secret secure (use environment variables, don't share it).
  4. Validate critical headers (x-pay-timestamp, x-pay-signature, x-pay-webhook-id).
  5. Handle errors correctly (respond with 401 if the signature is invalid).

Troubleshooting

Invalid signature

  1. Verify that you are using the correct secret.
  2. Make sure you are building the base string correctly.
  3. Use UTF-8 as encoding.
  4. Verify that you are using HMAC-SHA256 and not another algorithm.

Missing headers

  1. Check that your server is correctly receiving all headers.
  2. If you use a proxy or load balancer, make sure the headers are not being removed.
  3. Verify how your framework exposes headers (some change the format or hide them).

This documentation is updated regularly. For the latest version, consult our documentation repository.