Webhooks Setup
Webhooks allow you to receive real-time notifications about payment events from Bloque.
What are Webhooks?
Webhooks are HTTP callbacks that notify your server when specific events occur, such as successful payments, failed transactions, or status updates.
Setting Up Webhooks
1. Create Webhook Endpoint
Create an endpoint in your backend to receive webhook events:
import { Bloque } from '@bloque/payments';
import express from 'express';
const app = express();
// Initialize SDK with webhook secret
const bloque = new Bloque({
apiKey: process.env.BLOQUE_API_KEY!,
mode: 'production',
webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET,
});
// Webhook endpoint
app.post('/webhooks/bloque', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-bloque-signature'] as string;
try {
// Verify webhook signature
const isValid = bloque.webhooks.verify(req.body, signature);
if (!isValid) {
return res.status(400).send('Invalid signature');
}
// Parse event
const event = JSON.parse(req.body.toString());
// Handle event
handleWebhookEvent(event);
res.json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(400).send('Webhook error');
}
});
app.listen(3000);
2. Verify Webhook Signatures
Always verify webhook signatures to ensure they come from Bloque:
const isValid = bloque.webhooks.verify(
requestBody,
signatureHeader,
{ secret: process.env.BLOQUE_WEBHOOK_SECRET }
);
if (!isValid) {
// Reject webhook
return res.status(400).send('Invalid signature');
}
3. Handle Webhook Events
Process different event types:
function handleWebhookEvent(event) {
switch (event.type) {
case 'payment.succeeded':
handlePaymentSucceeded(event.data);
break;
case 'payment.failed':
handlePaymentFailed(event.data);
break;
case 'checkout.completed':
handleCheckoutCompleted(event.data);
break;
case 'checkout.expired':
handleCheckoutExpired(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
}
Webhook Events
payment.succeeded
Fired when a payment is successfully processed.
{
type: 'payment.succeeded',
data: {
id: 'pay_123',
status: 'succeeded',
amount: 2999,
currency: 'USD',
payment_method: 'card',
created_at: '2024-01-15T10:30:00Z'
}
}
payment.failed
Fired when a payment fails.
{
type: 'payment.failed',
data: {
id: 'pay_123',
status: 'failed',
error: 'insufficient_funds',
error_message: 'Insufficient funds'
}
}
checkout.completed
Fired when a checkout session is completed.
{
type: 'checkout.completed',
data: {
id: 'checkout_123',
status: 'completed',
payment_id: 'pay_123'
}
}
checkout.expired
Fired when a checkout session expires.
{
type: 'checkout.expired',
data: {
id: 'checkout_123',
status: 'expired'
}
}
Complete Example
import { Bloque } from '@bloque/payments';
import express from 'express';
const app = express();
const bloque = new Bloque({
apiKey: process.env.BLOQUE_API_KEY!,
mode: 'production',
webhookSecret: process.env.BLOQUE_WEBHOOK_SECRET,
});
app.post('/webhooks/bloque', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-bloque-signature'] as string;
try {
// Verify signature
const isValid = bloque.webhooks.verify(req.body, signature);
if (!isValid) {
return res.status(400).send('Invalid signature');
}
// Parse event
const event = JSON.parse(req.body.toString());
console.log('Received webhook:', event.type);
// Handle events
switch (event.type) {
case 'payment.succeeded':
// Update order status
await updateOrderStatus(event.data.id, 'paid');
// Send confirmation email
await sendConfirmationEmail(event.data);
break;
case 'payment.failed':
// Log failure
console.error('Payment failed:', event.data);
// Notify customer
await sendFailureNotification(event.data);
break;
case 'checkout.completed':
// Mark checkout as complete
await markCheckoutComplete(event.data.id);
break;
}
res.json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(400).send('Webhook error');
}
});
app.listen(3000);
Best Practices
1. Respond Quickly
Always respond with 200 OK quickly. Process events asynchronously:
app.post('/webhooks/bloque', async (req, res) => {
const signature = req.headers['x-bloque-signature'] as string;
// Verify
const isValid = bloque.webhooks.verify(req.body, signature);
if (!isValid) {
return res.status(400).send('Invalid signature');
}
// Respond immediately
res.json({ received: true });
// Process asynchronously
const event = JSON.parse(req.body.toString());
processWebhookAsync(event).catch(console.error);
});
2. Handle Idempotency
Webhooks may be sent multiple times. Store processed event IDs:
const processedEvents = new Set();
function handleWebhookEvent(event) {
if (processedEvents.has(event.id)) {
console.log('Event already processed:', event.id);
return;
}
// Process event
processEvent(event);
// Mark as processed
processedEvents.add(event.id);
}
3. Retry Logic
Bloque will retry webhooks if your endpoint fails. Ensure your endpoint is idempotent.
4. Secure Your Endpoint
- Always verify webhook signatures
- Use HTTPS in production
- Keep webhook secrets secure
- Rate limit webhook endpoints
Testing Webhooks
Test webhooks locally using tools like ngrok:
# Start ngrok
ngrok http 3000
# Use the ngrok URL in Bloque dashboard
https://your-ngrok-url.ngrok.io/webhooks/bloque
Next Steps