ابتدائی رسائی کی درخواست کریں

اپنا پہلا نام اور ای میل یا فون نمبر درج کریں۔ ہم Tajo تک رسائی کی تفصیلات کے ساتھ آپ سے رابطہ کریں گے۔

ویب ہک سیٹ اپ گائیڈ

ویب ہکس Brevo اور آپ کے Tajo لائلٹی پلیٹ فارم کے درمیان حقیقی وقت میں رابطہ ممکن بناتے ہیں۔ یہ گائیڈ آپ کو سیٹ اپ کے پورے عمل سے گزارتی ہے۔

جائزہ

ویب ہکس Brevo کو اس قابل بناتے ہیں کہ مخصوص ایونٹس پیش آنے پر وہ آپ کی Tajo ایپلیکیشن کو خودکار طور پر مطلع کرے، مثلاً:

  • ای میل ایونٹس: ڈیلیور ہوئی، کھولی گئی، کلک کی گئی، باؤنس ہوئی
  • ایس ایم ایس ایونٹس: بھیجا گیا، ڈیلیور ہوا، ناکام رہا، جواب آیا
  • رابطہ ایونٹس: بنایا گیا، اپ ڈیٹ ہوا، ان سبسکرائب ہوا
  • مہم کے ایونٹس: شروع ہوئی، مکمل ہوئی، روکی گئی

پیشگی شرائط

ویب ہکس سیٹ اپ کرنے سے پہلے یقینی بنائیں کہ آپ کے پاس یہ موجود ہیں:

  • HTTPS اینڈ پوائنٹ ویب ہکس وصول کرنے کے لیے (SSL لازمی ہے)
  • ویب ہک سیکریٹ دستخط کی تصدیق کے لیے
  • سرور ماحول جو HTTP POST درخواستیں سنبھال سکے
  • Brevo اکاؤنٹ جس میں ویب ہک تک رسائی کی اجازت ہو

مرحلہ 1: اپنا ویب ہک اینڈ پوائنٹ تیار کریں

ویب ہک ہینڈلر بنائیں

import express from 'express';
import crypto from 'crypto';
import { TajoLoyaltyService } from './loyalty-service.js';
const app = express();
const loyaltyService = new TajoLoyaltyService();
// Middleware to capture raw body for signature verification
app.use('/webhooks/brevo', express.raw({
type: 'application/json',
limit: '10mb'
}));
// Main webhook handler
app.post('/webhooks/brevo', async (req, res) => {
try {
// Verify webhook signature
const signature = req.headers['x-brevo-signature'];
if (!verifyWebhookSignature(req.body, signature)) {
console.warn('Invalid webhook signature received');
return res.status(401).json({ error: 'Unauthorized: Invalid signature' });
}
// Parse webhook payload
const event = JSON.parse(req.body.toString());
console.log('Received webhook event:', event.event, event.email);
// Route to appropriate handler
await handleWebhookEvent(event);
// Respond quickly (Brevo expects response within 5 seconds)
res.status(200).json({
success: true,
eventId: event['message-id'],
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({
error: 'Internal server error',
message: error.message
});
}
});
// Signature verification function
function verifyWebhookSignature(payload, signature) {
if (!process.env.BREVO_WEBHOOK_SECRET || !signature) {
return false;
}
const expectedSignature = crypto
.createHmac('sha256', process.env.BREVO_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature.replace('sha256=', ''), 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// Event router
async function handleWebhookEvent(event) {
switch (event.event) {
// Email events
case 'delivered':
await handleEmailDelivered(event);
break;
case 'opened':
await handleEmailOpened(event);
break;
case 'clicked':
await handleEmailClicked(event);
break;
case 'bounced':
case 'hard_bounced':
await handleEmailBounced(event);
break;
case 'spam':
await handleEmailSpam(event);
break;
case 'unsubscribed':
await handleEmailUnsubscribed(event);
break;
// SMS events
case 'sms_delivered':
await handleSMSDelivered(event);
break;
case 'sms_failed':
await handleSMSFailed(event);
break;
case 'sms_reply':
await handleSMSReply(event);
break;
// Contact events
case 'contact_created':
await handleContactCreated(event);
break;
case 'contact_updated':
await handleContactUpdated(event);
break;
case 'list_addition':
await handleListAddition(event);
break;
default:
console.warn('Unhandled webhook event:', event.event);
}
}

ای میل ایونٹ ہینڈلرز

// Handle email delivery confirmation
async function handleEmailDelivered(event) {
const customerEmail = event.email;
const messageId = event['message-id'];
await loyaltyService.updateCustomerEngagement(customerEmail, {
lastEmailDelivered: new Date(),
emailDeliveryRate: 'increment'
});
// Log for analytics
console.log(`Email delivered to ${customerEmail}: ${messageId}`);
}
// Handle email opens (key engagement metric)
async function handleEmailOpened(event) {
const customerEmail = event.email;
const subject = event.subject;
const timestamp = new Date(event.ts * 1000);
// Update customer engagement score
await loyaltyService.updateCustomerEngagement(customerEmail, {
lastEmailOpened: timestamp,
emailOpenRate: 'increment',
engagementScore: 'increase'
});
// Track loyalty campaign engagement
if (event.tag?.includes('loyalty')) {
await loyaltyService.trackLoyaltyEngagement(customerEmail, {
event: 'email_opened',
campaign: extractCampaignFromSubject(subject),
timestamp: timestamp
});
}
console.log(`Email opened by ${customerEmail}: "${subject}"`);
}
// Handle email clicks (high-value engagement)
async function handleEmailClicked(event) {
const customerEmail = event.email;
const clickedUrl = event.link;
const timestamp = new Date(event.ts * 1000);
// High-value engagement - boost customer score
await loyaltyService.updateCustomerEngagement(customerEmail, {
lastEmailClicked: timestamp,
emailClickRate: 'increment',
engagementScore: 'boost'
});
// Track reward page visits
if (clickedUrl.includes('/rewards') || clickedUrl.includes('/loyalty')) {
await loyaltyService.trackEvent(customerEmail, 'Rewards Page Visited', {
source: 'email',
referrer_url: clickedUrl,
timestamp: timestamp
});
}
console.log(`Email link clicked by ${customerEmail}: ${clickedUrl}`);
}
// Handle email bounces (delivery issues)
async function handleEmailBounced(event) {
const customerEmail = event.email;
const bounceReason = event.reason;
const bounceType = event.event; // 'bounced' or 'hard_bounced'
if (bounceType === 'hard_bounced') {
// Hard bounce - email address invalid
await loyaltyService.updateCustomerStatus(customerEmail, {
emailStatus: 'invalid',
emailBounced: true,
bounceReason: bounceReason,
lastBounce: new Date()
});
// Consider switching to SMS for critical notifications
await loyaltyService.suggestAlternativeChannel(customerEmail, 'sms');
} else {
// Soft bounce - temporary issue
await loyaltyService.updateCustomerEngagement(customerEmail, {
emailBounceCount: 'increment',
lastBounce: new Date()
});
}
console.warn(`Email bounced for ${customerEmail}: ${bounceReason}`);
}
// Handle spam reports (reputation management)
async function handleEmailSpam(event) {
const customerEmail = event.email;
// Mark customer as unengaged to prevent future spam reports
await loyaltyService.updateCustomerStatus(customerEmail, {
emailStatus: 'spam_reported',
marketingEnabled: false,
lastSpamReport: new Date()
});
// Alert marketing team for review
await loyaltyService.alertMarketing('spam_report', {
email: customerEmail,
campaign: event.tag
});
console.warn(`Spam reported by ${customerEmail}`);
}

ایس ایم ایس ایونٹ ہینڈلرز

// Handle SMS delivery confirmation
async function handleSMSDelivered(event) {
const customerPhone = event.phone;
const messageId = event['message-id'];
await loyaltyService.updateCustomerEngagement(customerPhone, {
lastSMSDelivered: new Date(),
smsDeliveryRate: 'increment'
});
console.log(`SMS delivered to ${customerPhone}: ${messageId}`);
}
// Handle SMS failures
async function handleSMSFailed(event) {
const customerPhone = event.phone;
const failureReason = event.reason;
await loyaltyService.updateCustomerStatus(customerPhone, {
smsStatus: 'failed',
smsFailureReason: failureReason,
lastSMSFailure: new Date()
});
// If SMS fails, consider email as alternative
const customer = await loyaltyService.getCustomerByPhone(customerPhone);
if (customer?.email) {
await loyaltyService.suggestAlternativeChannel(customer.email, 'email');
}
console.warn(`SMS failed for ${customerPhone}: ${failureReason}`);
}
// Handle SMS replies (two-way communication)
async function handleSMSReply(event) {
const customerPhone = event.phone;
const replyText = event.text.toLowerCase().trim();
// Process common replies
if (replyText === 'stop' || replyText === 'unsubscribe') {
await loyaltyService.unsubscribeFromSMS(customerPhone);
} else if (replyText === 'help' || replyText === 'info') {
await loyaltyService.sendSMSHelp(customerPhone);
} else {
// Forward to customer service
await loyaltyService.forwardSMSToSupport(customerPhone, replyText);
}
console.log(`SMS reply from ${customerPhone}: "${replyText}"`);
}

مرحلہ 2: Brevo میں ویب ہک کنفیگر کریں

Brevo ڈیش بورڈ کے ذریعے

  1. Brevo اکاؤنٹ میں لاگ ان کریں
  2. Developers > Webhooks پر جائیں
  3. “Add a new webhook” پر کلک کریں
  4. ویب ہک کی سیٹنگز کنفیگر کریں:
{
"url": "https://your-tajo-domain.com/webhooks/brevo",
"description": "Tajo Loyalty Platform Integration",
"events": [
"delivered",
"opened",
"clicked",
"bounced",
"hard_bounced",
"spam",
"unsubscribed",
"sms_delivered",
"sms_failed",
"sms_reply",
"contact_created",
"contact_updated"
]
}

API کے ذریعے

import { WebhooksApi } from '@getbrevo/brevo';
async function createWebhook() {
const webhooksApi = new WebhooksApi();
const createWebhook = {
url: 'https://your-tajo-domain.com/webhooks/brevo',
description: 'Tajo Loyalty Platform Integration',
events: [
'delivered',
'opened',
'clicked',
'bounced',
'hard_bounced',
'spam',
'unsubscribed',
'sms_delivered',
'sms_failed',
'sms_reply',
'contact_created',
'contact_updated'
]
};
try {
const response = await webhooksApi.createWebhook(createWebhook);
console.log('Webhook created successfully:', response.id);
return response;
} catch (error) {
console.error('Error creating webhook:', error);
throw error;
}
}

مرحلہ 3: سیکیورٹی کا نفاذ

ماحولیاتی متغیرات

Terminal window
# .env file
BREVO_WEBHOOK_SECRET=your-super-secure-webhook-secret-here
BREVO_API_KEY=xkeysib-your-api-key-here
WEBHOOK_RATE_LIMIT=1000
WEBHOOK_TIMEOUT=5000

ریٹ لمٹنگ

import rateLimit from 'express-rate-limit';
const webhookLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000, // Limit each IP to 1000 requests per windowMs
message: 'Too many webhook requests from this IP',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/webhooks/brevo', webhookLimiter);

IP وائٹ لسٹ (اختیاری)

const brevoIPs = [
'185.41.28.0/24',
'185.41.29.0/24',
'217.182.196.0/24'
];
function isBrevoIP(ip) {
// Implement IP range checking
return brevoIPs.some(range => ipInRange(ip, range));
}
app.use('/webhooks/brevo', (req, res, next) => {
const clientIP = req.ip || req.connection.remoteAddress;
if (process.env.NODE_ENV === 'production' && !isBrevoIP(clientIP)) {
return res.status(403).json({ error: 'Forbidden: Invalid source IP' });
}
next();
});

مرحلہ 4: ویب ہکس کی جانچ

ویب ہک ہینڈلر کی جانچ

// Test endpoint for webhook verification
app.post('/webhooks/brevo/test', (req, res) => {
const testEvent = {
event: 'test',
'message-id': 'test-message-id',
timestamp: Date.now() / 1000,
tags: ['test', 'loyalty']
};
console.log('Test webhook received:', testEvent);
res.status(200).json({
success: true,
message: 'Test webhook processed successfully',
receivedAt: new Date().toISOString()
});
});

دستی جانچ

Terminal window
# Test webhook endpoint with curl
curl -X POST https://your-domain.com/webhooks/brevo/test \
-H "Content-Type: application/json" \
-H "X-Brevo-Signature: sha256=test-signature" \
-d '{
"event": "delivered",
"email": "[email protected]",
"message-id": "test-123",
"ts": 1640995200
}'

ویب ہک کی توثیق

class WebhookValidator {
static validateEvent(event) {
const required = ['event', 'email', 'message-id'];
const missing = required.filter(field => !event[field]);
if (missing.length > 0) {
throw new Error(`Missing required fields: ${missing.join(', ')}`);
}
// Validate email format
if (!this.isValidEmail(event.email)) {
throw new Error('Invalid email format');
}
// Validate event type
const validEvents = [
'delivered', 'opened', 'clicked', 'bounced', 'hard_bounced',
'spam', 'unsubscribed', 'sms_delivered', 'sms_failed'
];
if (!validEvents.includes(event.event)) {
throw new Error(`Invalid event type: ${event.event}`);
}
return true;
}
static isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
}

مرحلہ 5: نگرانی اور لاگنگ

ویب ہک کی نگرانی

import winston from 'winston';
const webhookLogger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'webhook-error.log', level: 'error' }),
new winston.transports.File({ filename: 'webhook-combined.log' })
]
});
// Add monitoring middleware
app.use('/webhooks/brevo', (req, res, next) => {
const startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - startTime;
webhookLogger.info('Webhook processed', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: duration,
userAgent: req.headers['user-agent'],
contentLength: req.headers['content-length']
});
});
next();
});

ہیلتھ چیک اینڈ پوائنٹ

app.get('/webhooks/brevo/health', async (req, res) => {
const health = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: process.env.npm_package_version,
environment: process.env.NODE_ENV,
checks: {
database: await checkDatabaseConnection(),
redis: await checkRedisConnection(),
brevoAPI: await checkBrevoAPIConnection()
}
};
const allHealthy = Object.values(health.checks).every(check => check.status === 'ok');
res.status(allHealthy ? 200 : 503).json(health);
});

مرحلہ 6: غلطیوں کا انتظام اور بحالی

دوبارہ کوشش کا طریقہ کار

class WebhookProcessor {
constructor() {
this.maxRetries = 3;
this.retryDelay = 1000; // 1 second
}
async processEvent(event, retries = 0) {
try {
await this.handleEvent(event);
} catch (error) {
if (retries < this.maxRetries && this.isRetryableError(error)) {
console.warn(`Webhook processing failed, retrying... (${retries + 1}/${this.maxRetries})`);
await this.delay(this.retryDelay * Math.pow(2, retries)); // Exponential backoff
return this.processEvent(event, retries + 1);
}
// Max retries reached or non-retryable error
await this.handleFailedEvent(event, error);
throw error;
}
}
isRetryableError(error) {
// Retry on temporary failures
return error.code === 'ECONNRESET' ||
error.code === 'ETIMEDOUT' ||
(error.status >= 500 && error.status < 600);
}
async handleFailedEvent(event, error) {
// Store failed event for manual review
await loyaltyService.storeFailedEvent(event, error.message);
// Alert operations team for critical events
if (this.isCriticalEvent(event)) {
await loyaltyService.alertOps('webhook_failure', {
event: event.event,
email: event.email,
error: error.message
});
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

ڈیڈ لیٹر کیو

import Bull from 'bull';
const webhookQueue = new Bull('webhook processing', process.env.REDIS_URL);
const deadLetterQueue = new Bull('webhook failed', process.env.REDIS_URL);
webhookQueue.process(async (job) => {
const { event } = job.data;
await handleWebhookEvent(event);
});
webhookQueue.on('failed', async (job, err) => {
console.error(`Webhook job failed: ${err.message}`);
// Add to dead letter queue for manual processing
await deadLetterQueue.add('failed webhook', {
originalEvent: job.data.event,
error: err.message,
failedAt: new Date(),
attempts: job.attemptsMade
});
});

عام مسائل کا حل

1. دستخط کی تصدیق ناکام ہو جائے

// Debug signature verification
function debugSignature(payload, receivedSignature) {
const expectedSignature = crypto
.createHmac('sha256', process.env.BREVO_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
console.log('Received signature:', receivedSignature);
console.log('Expected signature:', expectedSignature);
console.log('Payload length:', payload.length);
console.log('First 100 chars:', payload.slice(0, 100));
return expectedSignature === receivedSignature.replace('sha256=', '');
}

2. ایونٹس غائب ہوں

ویب ہک کی کنفیگریشن جانچیں:

async function auditWebhookConfig() {
const webhooksApi = new WebhooksApi();
try {
const webhooks = await webhooksApi.getWebhooks();
webhooks.webhooks.forEach(webhook => {
console.log('Webhook ID:', webhook.id);
console.log('URL:', webhook.url);
console.log('Events:', webhook.events);
console.log('Status:', webhook.is_enabled ? 'enabled' : 'disabled');
});
} catch (error) {
console.error('Error auditing webhooks:', error);
}
}

3. زیادہ تاخیر

ویب ہک پروسیسنگ بہتر بنائیں:

// Process webhooks asynchronously
app.post('/webhooks/brevo', async (req, res) => {
// Verify signature quickly
if (!verifyWebhookSignature(req.body, req.headers['x-brevo-signature'])) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Respond immediately
res.status(200).json({ success: true });
// Process event asynchronously
const event = JSON.parse(req.body.toString());
webhookQueue.add('process event', { event }, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
});
});

اگلے اقدامات

  • ویب ہک سیکیورٹی گائیڈ - اعلیٰ درجے کے سیکیورٹی طریقے
  • ایونٹ اقسام کا حوالہ - ایونٹس کی مکمل دستاویزات
  • ویب ہکس کی جانچ - جانچ اور ڈیبگنگ گائیڈ
  • ویب ہک اینالیٹکس - ویب ہک کی کارکردگی کی نگرانی

ابتدائی رسائی کی درخواست کریں

اپنا پہلا نام اور ای میل یا فون نمبر درج کریں۔ ہم Tajo تک رسائی کی تفصیلات کے ساتھ آپ سے رابطہ کریں گے۔

خودکار شناخت
AI معاون

السلام علیکم! دستاویزات کے بارے میں کچھ بھی پوچھیں۔