Skip to main content

Overview

Your subscription platform sends real-time webhooks for all subscription events. This allows you to automate workflows, update your database, send notifications, and more.

Setting Up Webhooks

1

Add an Endpoint

Navigate to the Webhooks tab of your merchant dashboard
2

Copy your signing secret

Save the secret shown during creation (displayed only once)
3

Verify webhook signatures

Implement signature verification in your endpoint handler

Available Events

subscription.created

Triggered when a user subscribes to one of your plans

subscription.payment_succeeded

Triggered when a recurring payment is successfully processed

subscription.payment_failed

Triggered when a recurring payment fails

subscription.cancelled

Triggered when a user cancels their subscription

subscription.created

Triggered when a user subscribes to one of your plans.
{
  "event": "subscription.created",
  "timestamp": 1701234567890,
  "data": {
    "subscription_id": "Abc123...",
    "user_wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "plan_id": "premium-monthly-1701234567890-abc123",
    "amount_prepaid": "29000000"
  }
}

subscription.payment_succeeded

Triggered when a recurring payment is successfully processed.
{
  "event": "subscription.payment_succeeded",
  "timestamp": 1701234567890,
  "data": {
    "subscription_id": "Abc123...",
    "user_wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "amount": "29000000",
    "payment_number": 2
  }
}

subscription.payment_failed

Triggered when a recurring payment fails (insufficient balance, etc).
{
  "event": "subscription.payment_failed",
  "timestamp": 1701234567890,
  "data": {
    "subscription_id": "Abc123...",
    "user_wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "amount_required": "29000000",
    "balance_available": "5000000",
    "failure_count": 1
  }
}

subscription.cancelled

Triggered when a user cancels their subscription.
{
  "event": "subscription.cancelled",
  "timestamp": 1701234567890,
  "data": {
    "subscription_id": "Abc123...",
    "user_wallet": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "refund_amount": "15000000",
    "user_email": "abc@..."
    "payments_made": 3
  }
}

Verifying Webhook Signatures

Always verify webhook signatures to ensure the webhook came from your subscription platform.
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook endpoint
app.post('/webhooks/subscriptions', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];
  const webhookId = req.headers['x-webhook-id'];
  
  // Verify signature
  if (!verifyWebhookSignature(req.body, signature, YOUR_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Prevent replay attacks (optional)
  const now = Date.now();
  if (Math.abs(now - parseInt(timestamp)) > 300000) { // 5 minutes
    return res.status(401).json({ error: 'Webhook too old' });
  }
  
  // Process the webhook
  const { event, data } = req.body;
  
  switch(event) {
    case 'subscription.created':
      await grantAccess(data.user_wallet, data.plan_id);
      break;
      
    case 'subscription.payment_succeeded':
      await extendAccess(data.user_wallet);
      break;
      
    case 'subscription.payment_failed':
      await handlePaymentFailure(data.user_wallet, data.failure_count);
      break;
      
    case 'subscription.cancelled':
      await revokeAccess(data.user_wallet);
      break;
  }
  
  res.json({ received: true });
});

Best Practices

Never trust unverified webhooks. Always implement signature verification.
Respond quickly (< 10s) and process in background to avoid timeouts.
Use x-webhook-id header to deduplicate events.
Reject old webhooks to prevent replay attacks.
We’ll retry on timeouts or 5xx errors.
Check the Webhooks tab for delivery failures.

Retry Policy

Failed webhooks are automatically retried with exponential backoff:
1

1st retry

After 1 minute
2

2nd retry

After 5 minutes
3

3rd retry

After 30 minutes
After 3 failed attempts, we stop retrying. Check your webhook logs in the dashboard to debug failures.

Testing

Use tools like webhook.site or ngrok to test your webhook endpoint locally:
# Expose your local server
ngrok http 3000

# Add the ngrok URL to your webhook endpoints
https://abc123.ngrok.io/webhooks/subscriptions

Rate Limits

  • Maximum 10 endpoints per merchant
  • Webhooks must respond within 10 seconds
  • Automatic retry on timeouts or 5xx errors

Getting Help

If you’re having trouble with webhooks:
  1. Check the Webhook Logs tab for detailed error messages
  2. Verify your endpoint is publicly accessible
  3. Ensure you’re verifying signatures correctly
  4. Check that your endpoint returns a 2xx status code