Webhooks

Receive real-time notifications about payment events

Webhooks

Webhooks allow your application to receive real-time notifications when events happen in your ZEUSXPAY account. This is the recommended way to handle payment confirmations.

Why Use Webhooks?

  • Real-time updates: Get notified immediately when events occur
  • Reliable: We retry failed deliveries automatically
  • Secure: Cryptographically signed for verification
  • Asynchronous: Don’t depend on customers returning to your site

Setting Up Webhooks

1. Create an Endpoint

Create an endpoint on your server to receive webhook events:

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

app.post('/webhooks/zeusxpay', express.raw({ type: 'application/json' }), (req, res) => {
	const event = JSON.parse(req.body);

	// Handle the event
	console.log('Received event:', event.type);

	// Return 200 to acknowledge receipt
	res.status(200).send('OK');
});

2. Register Your Webhook URL

Register your endpoint in the Dashboard:

  1. Go to SettingsWebhooks
  2. Click Add Endpoint
  3. Enter your URL: https://yoursite.com/webhooks/zeusxpay
  4. Select events to receive
  5. Save and copy your webhook secret

Event Types

Order Events

  • order.created - Order was created
  • order.confirming - Payment detected, waiting for confirmations
  • order.completed - Payment confirmed and completed
  • order.expired - Order expired without payment
  • order.failed - Payment failed

Refund Events

  • refund.created - Refund initiated
  • refund.completed - Refund completed
  • refund.failed - Refund failed

Example Event Payload

{
	"id": "evt_1234567890",
	"type": "order.completed",
	"created_at": "2024-01-01T12:00:00Z",
	"data": {
		"id": "ord_1234567890",
		"status": "completed",
		"amount": 100.0,
		"currency": "USD",
		"crypto_currency": "BTC",
		"crypto_amount": 0.00234567,
		"transaction_hash": "abc123...",
		"customer_email": "customer@example.com",
		"metadata": {
			"order_id": "12345"
		}
	}
}

Verifying Webhook Signatures

Always verify webhook signatures to ensure requests are from ZEUSXPAY:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
	const expectedSignature = crypto.createHmac('sha256', secret).update(payload).digest('hex');

	return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

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

	if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
		return res.status(401).send('Invalid signature');
	}

	const event = JSON.parse(payload);
	// Handle event...

	res.status(200).send('OK');
});

Handling Events

Process different event types:

app.post('/webhooks/zeusxpay', async (req, res) => {
	const event = JSON.parse(req.body);

	try {
		switch (event.type) {
			case 'order.created':
				await handleOrderCreated(event.data);
				break;

			case 'order.confirming':
				await handleOrderConfirming(event.data);
				break;

			case 'order.completed':
				await handleOrderCompleted(event.data);
				break;

			case 'order.failed':
				await handleOrderFailed(event.data);
				break;

			case 'refund.completed':
				await handleRefundCompleted(event.data);
				break;

			default:
				console.log(`Unhandled event type: ${event.type}`);
		}

		res.status(200).send('OK');
	} catch (error) {
		console.error('Webhook error:', error);
		res.status(500).send('Error processing webhook');
	}
});

async function handleOrderCompleted(order) {
	// Fulfill the order
	console.log(`Order ${order.id} completed`);

	// Update database
	await db.orders.update({
		where: { id: order.metadata.order_id },
		data: { status: 'paid', payment_id: order.id }
	});

	// Send confirmation email
	await sendEmail(order.customer_email, {
		subject: 'Payment Received',
		template: 'payment-confirmation',
		data: order
	});
}

Best Practices

1. Return 200 Quickly

Return a 200 status immediately and process asynchronously:

app.post('/webhooks/zeusxpay', async (req, res) => {
	const event = JSON.parse(req.body);

	// Acknowledge receipt immediately
	res.status(200).send('OK');

	// Process asynchronously
	processWebhookAsync(event).catch(console.error);
});

2. Idempotency

Handle duplicate events gracefully:

async function handleOrderCompleted(order) {
	// Check if already processed
	const existing = await db.payments.findUnique({
		where: { zeusxpay_order_id: order.id }
	});

	if (existing) {
		console.log('Event already processed');
		return;
	}

	// Process the payment
	await fulfillOrder(order);
}

3. Error Handling

Handle errors properly to trigger retries:

app.post('/webhooks/zeusxpay', async (req, res) => {
	try {
		const event = JSON.parse(req.body);
		await processEvent(event);
		res.status(200).send('OK');
	} catch (error) {
		console.error('Webhook processing failed:', error);
		// Return 500 to trigger retry
		res.status(500).send('Processing failed');
	}
});

4. Logging

Log all webhook events for debugging:

app.post('/webhooks/zeusxpay', async (req, res) => {
	const event = JSON.parse(req.body);

	// Log to database
	await db.webhookLogs.create({
		data: {
			event_id: event.id,
			event_type: event.type,
			payload: event,
			processed_at: new Date()
		}
	});

	// Process event...
});

Retry Logic

ZEUSXPAY automatically retries failed webhook deliveries:

  • Immediate retry: If first attempt fails
  • Exponential backoff: 1min, 5min, 30min, 2hr, 6hr, 12hr, 24hr
  • Maximum attempts: 10 attempts over 3 days
  • Manual retry: Retry failed webhooks from Dashboard

Testing Webhooks

Local Development

Use tools like ngrok to expose your local server:

# Start ngrok
ngrok http 3000

# Use the ngrok URL in your webhook settings
https://abc123.ngrok.io/webhooks/zeusxpay

Test Events

Send test webhooks from the Dashboard:

  1. Go to SettingsWebhooks
  2. Select your endpoint
  3. Click Send Test Event
  4. Choose event type
  5. View response

CLI Testing

Use cURL to simulate webhooks:

curl -X POST http://localhost:3000/webhooks/zeusxpay 
  -H "Content-Type: application/json" 
  -H "X-ZeusXPay-Signature: test_signature" 
  -d '{
    "id": "evt_test_123",
    "type": "order.completed",
    "data": {
      "id": "ord_test_123",
      "status": "completed"
    }
  }'

Monitoring

Monitor webhook delivery in the Dashboard:

  • Success rate: View delivery success rate
  • Recent deliveries: See recent webhook attempts
  • Failed deliveries: Review and retry failed webhooks
  • Logs: View full request/response logs

Security Considerations

  • ✅ Always verify signatures
  • ✅ Use HTTPS endpoints only
  • ✅ Validate event data
  • ✅ Implement idempotency
  • ✅ Rate limit your endpoint
  • ❌ Don’t trust unverified webhooks
  • ❌ Don’t process duplicate events
  • ❌ Don’t expose sensitive data in logs

Troubleshooting

Webhooks Not Received

  1. Check your firewall allows incoming requests
  2. Verify webhook URL is publicly accessible
  3. Check Dashboard for delivery logs
  4. Ensure endpoint returns 200 status

Signature Verification Fails

  1. Use raw request body (don’t parse before verifying)
  2. Check you’re using the correct webhook secret
  3. Verify signature header name: x-zeusxpay-signature

Duplicate Events

  1. Implement idempotency checks
  2. Store processed event IDs
  3. Check timestamp to detect replays

Examples

See our GitHub repository for complete webhook examples in multiple languages:

  • Node.js/Express
  • Python/Flask
  • PHP/Laravel
  • Ruby/Rails
  • Go

Need Help?