Apabila anda mengesahkan beribu-ribu atau berjuta-juta alamat e-mel, menunggu secara sinkronus untuk setiap keputusan tidaklah praktikal. Webhook pengesahan e-mel menyediakan penyelesaian yang elegan dengan memberitahu aplikasi anda apabila tugas pengesahan selesai, menghapuskan keperluan untuk pengundian berterusan dan membolehkan aliran kerja asinkronus yang cekap. Panduan komprehensif ini meneroka segala yang pembangun perlu tahu tentang pelaksanaan webhook pengesahan e-mel, daripada persediaan asas hingga corak lanjutan untuk mengendalikan operasi pengesahan berskala besar.
Memahami Webhook Pengesahan E-mel
Webhook adalah panggilan balik HTTP yang menghantar data ke aplikasi anda apabila peristiwa tertentu berlaku. Dalam konteks pengesahan e-mel, webhook memberitahu sistem anda apabila tugas pengesahan pukal selesai, apabila pengesahan e-mel individu selesai dalam mod asinkronus, atau apabila peristiwa penting lain berlaku semasa proses pengesahan.
Mengapa Menggunakan Webhook untuk Pengesahan E-mel?
Corak permintaan-respons tradisional berfungsi dengan baik untuk pengesahan e-mel tunggal, tetapi operasi pukal menimbulkan cabaran. Mengesahkan 100,000 e-mel mungkin mengambil masa berjam-jam, dan mengekalkan sambungan HTTP yang lama tidaklah praktikal. Pengundian untuk kemas kini status membazir sumber dan mewujudkan beban API yang tidak perlu.
Menghapuskan Overhead Pengundian
Tanpa webhook, anda perlu berulang kali menanyakan API untuk memeriksa sama ada tugas pukal telah selesai. Ini mewujudkan trafik rangkaian yang tidak perlu, menggunakan had kadar API, dan menambah kerumitan kepada aplikasi anda. Webhook menolak notifikasi kepada anda tepat pada masa ia diperlukan.
Pemprosesan Masa Nyata
Webhook membolehkan tindakan segera apabila pengesahan selesai. Aplikasi anda boleh memproses keputusan, mengemas kini pangkalan data, dan mencetuskan tindakan susulan tanpa kelewatan yang diperkenalkan oleh selang pengundian.
Seni Bina Berskala
Seni bina berasaskan webhook berskala secara semula jadi. Sama ada anda memproses satu tugas pukal atau beratus-ratus secara serentak, titik akhir webhook anda menerima notifikasi apabila ia tiba, dan anda boleh memprosesnya secara asinkronus menggunakan baris gilir atau pekerja.
Kecekapan Sumber
Daripada mengekalkan sambungan atau menjalankan gelung pengundian, aplikasi anda kekal terbiar sehingga webhook tiba. Ini mengurangkan kos pengiraan dan memudahkan keperluan infrastruktur.
Peristiwa Webhook dalam Pengesahan E-mel
Perkhidmatan pengesahan e-mel biasanya mencetuskan webhook untuk beberapa jenis peristiwa:
Penyiapan Tugas Pukal
Peristiwa webhook yang paling biasa berlaku apabila tugas pengesahan pukal selesai diproses. Muatan termasuk status tugas, statistik ringkasan, dan maklumat tentang memuat turun keputusan.
Kemajuan Tugas Pukal
Sesetengah perkhidmatan menghantar webhook kemajuan pada selang masa semasa pemprosesan pukal, membolehkan anda menjejak kemajuan pengesahan dan menganggarkan masa penyiapan.
Kegagalan Tugas Pukal
Apabila tugas pukal menghadapi ralat yang menghalang penyiapan, webhook kegagalan memberikan butiran tentang apa yang salah dan sama ada keputusan separa tersedia.
Pengesahan E-mel Tunggal (Mod Asinkronus)
Untuk senario pengesahan masa nyata bervolum tinggi, pengesahan e-mel tunggal asinkronus menghantar keputusan melalui webhook dan bukannya menunggu respons sinkronus.
Menyediakan Titik Akhir Webhook
Melaksanakan webhook memerlukan penciptaan titik akhir dalam aplikasi anda yang boleh menerima dan memproses muatan webhook.
Struktur Titik Akhir Asas
Titik akhir webhook hanyalah titik akhir HTTP POST yang menerima muatan JSON:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/email-verification', async (req, res) => {
const { event_type, job_id, status, data } = req.body;
console.log(`Received webhook: ${event_type} for job ${job_id}`);
// Process the webhook
try {
await handleWebhookEvent(req.body);
// Always respond quickly to acknowledge receipt
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
// Still acknowledge receipt to prevent retries
res.status(200).json({ received: true, error: error.message });
}
});
async function handleWebhookEvent(payload) {
switch (payload.event_type) {
case 'bulk.completed':
await handleBulkCompleted(payload);
break;
case 'bulk.failed':
await handleBulkFailed(payload);
break;
case 'bulk.progress':
await handleBulkProgress(payload);
break;
default:
console.log(`Unknown event type: ${payload.event_type}`);
}
}
Amalan Terbaik Respons Webhook
Perkhidmatan pengesahan e-mel mengharapkan respons pantas daripada titik akhir webhook. Jika titik akhir anda mengambil masa terlalu lama untuk bertindak balas, perkhidmatan mungkin menganggap penghantaran gagal dan mencuba semula.
Bertindak Balas Serta-merta
Akui penerimaan webhook dengan segera, kemudian proses muatan secara asinkronus:
app.post('/webhooks/email-verification', async (req, res) => {
// Immediately acknowledge receipt
res.status(200).json({ received: true });
// Process asynchronously
setImmediate(async () => {
try {
await handleWebhookEvent(req.body);
} catch (error) {
console.error('Async webhook processing error:', error);
// Log for retry or manual processing
await logFailedWebhook(req.body, error);
}
});
});
Gunakan Baris Gilir Mesej untuk Pemprosesan Berat
Untuk sistem pengeluaran, letakkan muatan webhook dalam barisan gilir untuk diproses oleh proses pekerja:
const Queue = require('bull');
const webhookQueue = new Queue('email-verification-webhooks');
app.post('/webhooks/email-verification', async (req, res) => {
// Queue the webhook for processing
await webhookQueue.add('process-webhook', req.body, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
}
});
res.status(200).json({ received: true });
});
// Worker process
webhookQueue.process('process-webhook', async (job) => {
const payload = job.data;
await handleWebhookEvent(payload);
});
Mengkonfigurasi Webhook dengan API
Daftarkan titik akhir webhook anda dengan perkhidmatan pengesahan e-mel:
async function registerWebhook(webhookUrl, events, secret) {
const response = await fetch('https://api.billionverify.com/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: webhookUrl,
events: events,
secret: secret
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(`Failed to register webhook: ${result.error}`);
}
console.log(`Webhook registered: ${result.webhook_id}`);
return result;
}
// Register for bulk job events
await registerWebhook(
'https://yourapp.com/webhooks/email-verification',
['bulk.completed', 'bulk.failed', 'bulk.progress'],
process.env.WEBHOOK_SECRET
);
Mengamankan Titik Akhir Webhook
Titik akhir webhook boleh diakses secara awam, menjadikan keselamatan penting. Tanpa pengesahan yang betul, penyerang boleh menghantar muatan webhook palsu untuk memanipulasi aplikasi anda.
Pengesahan Tandatangan
Kebanyakan perkhidmatan pengesahan e-mel menandatangani muatan webhook menggunakan HMAC-SHA256 dengan rahsia yang dikongsi. Sahkan tandatangan sebelum memproses:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/email-verification', async (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
const isValid = verifyWebhookSignature(
req.body,
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
console.warn('Invalid webhook signature received');
return res.status(401).json({ error: 'Invalid signature' });
}
// Signature valid, process webhook
await handleWebhookEvent(req.body);
res.status(200).json({ received: true });
});
Pengesahan Cap Masa
Cegah serangan main semula dengan mengesahkan cap masa webhook:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const webhookTime = new Date(timestamp).getTime();
const currentTime = Date.now();
const difference = Math.abs(currentTime - webhookTime);
return difference <= toleranceSeconds * 1000;
}
app.post('/webhooks/email-verification', async (req, res) => {
const { timestamp } = req.body;
if (!isTimestampValid(timestamp)) {
console.warn('Webhook timestamp outside acceptable range');
return res.status(400).json({ error: 'Invalid timestamp' });
}
// Continue with signature verification and processing
});
Senarai Putih IP
Untuk keselamatan tambahan, hadkan akses webhook kepada alamat IP yang diketahui:
const allowedIPs = [
'203.0.113.0/24', // BillionVerify webhook servers
'198.51.100.0/24'
];
function isIPAllowed(clientIP) {
// Implement CIDR range checking
return allowedIPs.some(range => isIPInRange(clientIP, range));
}
app.post('/webhooks/email-verification', async (req, res) => {
const clientIP = req.ip || req.connection.remoteAddress;
if (!isIPAllowed(clientIP)) {
console.warn(`Webhook from unauthorized IP: ${clientIP}`);
return res.status(403).json({ error: 'Forbidden' });
}
// Continue with processing
});
Pengendalian Idempoten
Webhook mungkin dihantar beberapa kali disebabkan oleh isu rangkaian atau percubaan semula. Laksanakan idempoten untuk mengendalikan pendua dengan selamat:
const processedWebhooks = new Set(); // Use Redis in production
async function handleWebhookIdempotent(payload) {
const webhookId = payload.webhook_id || payload.event_id;
// Check if already processed
if (processedWebhooks.has(webhookId)) {
console.log(`Duplicate webhook ignored: ${webhookId}`);
return;
}
// Mark as processing
processedWebhooks.add(webhookId);
try {
await handleWebhookEvent(payload);
} catch (error) {
// Remove from processed set to allow retry
processedWebhooks.delete(webhookId);
throw error;
}
}
Untuk sistem pengeluaran, gunakan Redis untuk idempoten yang diedarkan:
const Redis = require('ioredis');
const redis = new Redis();
async function isWebhookProcessed(webhookId) {
const key = `webhook:processed:${webhookId}`;
const result = await redis.set(key, '1', 'NX', 'EX', 86400); // 24 hour expiry
return result === null; // Already exists
}
app.post('/webhooks/email-verification', async (req, res) => {
const webhookId = req.body.webhook_id;
if (await isWebhookProcessed(webhookId)) {
console.log(`Duplicate webhook: ${webhookId}`);
return res.status(200).json({ received: true, duplicate: true });
}
await handleWebhookEvent(req.body);
res.status(200).json({ received: true });
});
Memproses Muatan Webhook
Peristiwa webhook yang berbeza memerlukan logik pengendalian yang berbeza. Mari kita terokai corak biasa untuk memproses webhook pengesahan e-mel.
Mengendalikan Penyiapan Tugas Pukal
Apabila tugas pengesahan pukal selesai, muat turun dan proses keputusan:
async function handleBulkCompleted(payload) {
const { job_id, status, summary, download_url } = payload;
console.log(`Bulk job ${job_id} completed with status: ${status}`);
console.log(`Summary: ${summary.valid} valid, ${summary.invalid} invalid`);
// Download results
const results = await downloadResults(download_url);
// Process results
await processVerificationResults(job_id, results);
// Update job status in database
await updateJobStatus(job_id, 'completed', summary);
// Notify relevant parties
await sendCompletionNotification(job_id, summary);
}
async function downloadResults(url) {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`
}
});
if (!response.ok) {
throw new Error(`Failed to download results: ${response.status}`);
}
return await response.json();
}
async function processVerificationResults(jobId, results) {
// Batch update contacts in database
const validEmails = results.filter(r => r.is_valid);
const invalidEmails = results.filter(r => !r.is_valid);
await db.transaction(async (trx) => {
// Update valid emails
for (const batch of chunkArray(validEmails, 1000)) {
await trx('contacts')
.whereIn('email', batch.map(r => r.email))
.update({
email_verified: true,
verification_date: new Date(),
verification_job_id: jobId
});
}
// Handle invalid emails
for (const batch of chunkArray(invalidEmails, 1000)) {
await trx('contacts')
.whereIn('email', batch.map(r => r.email))
.update({
email_verified: false,
email_invalid_reason: trx.raw('CASE email ' +
batch.map(r => `WHEN '${r.email}' THEN '${r.reason}'`).join(' ') +
' END'),
verification_date: new Date(),
verification_job_id: jobId
});
}
});
}
Mengendalikan Kegagalan Tugas Pukal
Apabila tugas gagal, tangkap maklumat ralat dan tentukan sama ada pemulihan mungkin:
async function handleBulkFailed(payload) {
const { job_id, error_code, error_message, partial_results_available } = payload;
console.error(`Bulk job ${job_id} failed: ${error_message}`);
// Update job status
await updateJobStatus(job_id, 'failed', {
error_code,
error_message
});
// Try to retrieve partial results if available
if (partial_results_available) {
console.log('Attempting to retrieve partial results...');
try {
const partialResults = await downloadPartialResults(job_id);
await processVerificationResults(job_id, partialResults);
// Identify unprocessed emails for retry
const processedEmails = new Set(partialResults.map(r => r.email));
const originalEmails = await getOriginalJobEmails(job_id);
const unprocessedEmails = originalEmails.filter(e => !processedEmails.has(e));
if (unprocessedEmails.length > 0) {
// Schedule retry for unprocessed emails
await scheduleRetryJob(job_id, unprocessedEmails);
}
} catch (error) {
console.error('Failed to retrieve partial results:', error);
}
}
// Notify about failure
await sendFailureNotification(job_id, error_message);
}
async function scheduleRetryJob(originalJobId, emails) {
// Create new job for remaining emails
const response = await fetch('https://api.billionverify.com/v1/bulk/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BILLIONVERIFY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
emails,
metadata: {
retry_of: originalJobId
}
})
});
const { job_id: newJobId } = await response.json();
console.log(`Scheduled retry job ${newJobId} for ${emails.length} emails`);
}
Mengendalikan Kemas Kini Kemajuan
Webhook kemajuan membantu menjejak tugas yang berjalan lama:
async function handleBulkProgress(payload) {
const { job_id, processed_count, total_count, estimated_completion } = payload;
const percentComplete = Math.round((processed_count / total_count) * 100);
console.log(`Job ${job_id}: ${percentComplete}% complete (${processed_count}/${total_count})`);
// Update progress in database
await updateJobProgress(job_id, {
processed_count,
total_count,
percent_complete: percentComplete,
estimated_completion: new Date(estimated_completion)
});
// Optionally notify users of progress
if (percentComplete % 25 === 0) {
await sendProgressNotification(job_id, percentComplete);
}
}
Corak Webhook Lanjutan
Sistem pengeluaran mendapat manfaat daripada corak lanjutan yang meningkatkan kebolehpercayaan dan kebolehselenggaraan.
Baris Gilir Surat Mati untuk Webhook Gagal
Apabila pemprosesan webhook gagal berulang kali, pindahkan muatan ke baris gilir surat mati untuk semakan manual:
const webhookQueue = new Queue('email-verification-webhooks');
const deadLetterQueue = new Queue('webhook-dead-letters');
webhookQueue.process('process-webhook', async (job) => {
try {
await handleWebhookEvent(job.data);
} catch (error) {
// Check if this is the final retry
if (job.attemptsMade >= job.opts.attempts - 1) {
// Move to dead letter queue
await deadLetterQueue.add('failed-webhook', {
original_payload: job.data,
error: error.message,
failed_at: new Date().toISOString(),
attempts: job.attemptsMade + 1
});
}
throw error; // Re-throw to trigger retry
}
});
// Process dead letters manually or with alerts
deadLetterQueue.on('completed', async (job) => {
await sendAlert({
type: 'webhook_dead_letter',
job_id: job.data.original_payload.job_id,
error: job.data.error
});
});
Penyumberan Peristiwa Webhook
Simpan semua peristiwa webhook untuk jejak audit dan keupayaan main semula:
async function handleWebhookWithEventSourcing(payload) {
// Store raw event
const eventId = await storeWebhookEvent(payload);
try {
// Process event
await handleWebhookEvent(payload);
// Mark as processed
await markEventProcessed(eventId);
} catch (error) {
// Mark as failed
await markEventFailed(eventId, error);
throw error;
}
}
async function storeWebhookEvent(payload) {
const result = await db('webhook_events').insert({
event_type: payload.event_type,
job_id: payload.job_id,
payload: JSON.stringify(payload),
received_at: new Date(),
status: 'pending'
});
return result[0];
}
// Replay failed events
async function replayFailedEvents() {
const failedEvents = await db('webhook_events')
.where('status', 'failed')
.where('retry_count', '<', 3);
for (const event of failedEvents) {
try {
await handleWebhookEvent(JSON.parse(event.payload));
await markEventProcessed(event.id);
} catch (error) {
await incrementRetryCount(event.id);
}
}
}
Penghalaan Webhook Berbilang Penyewa
Untuk aplikasi SaaS, halakan webhook kepada pengendali khusus penyewa:
async function handleMultiTenantWebhook(payload) {
const { tenant_id, event_type, data } = payload;
// Get tenant configuration
const tenant = await getTenantConfig(tenant_id);
if (!tenant) {
console.error(`Unknown tenant: ${tenant_id}`);
return;
}
// Route to tenant-specific handler
switch (event_type) {
case 'bulk.completed':
await handleTenantBulkCompleted(tenant, data);
break;
case 'bulk.failed':
await handleTenantBulkFailed(tenant, data);
break;
}
// Forward to tenant webhook if configured
if (tenant.webhook_url) {
await forwardToTenant(tenant.webhook_url, tenant.webhook_secret, payload);
}
}
async function forwardToTenant(url, secret, payload) {
const signature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature
},
body: JSON.stringify(payload)
});
}
Pengendalian Ralat dan Kebolehpercayaan
Pelaksanaan webhook yang teguh mengendalikan kegagalan dengan anggun dan memastikan tiada data hilang.
Strategi Percubaan Semula
Laksanakan backoff eksponen untuk kegagalan sementara:
async function processWebhookWithRetry(payload, maxRetries = 5) {
const delays = [1000, 5000, 30000, 120000, 300000]; // 1s, 5s, 30s, 2m, 5m
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
await handleWebhookEvent(payload);
return; // Success
} catch (error) {
const isRetryable = isRetryableError(error);
if (!isRetryable || attempt === maxRetries - 1) {
// Log to dead letter queue
await logFailedWebhook(payload, error, attempt + 1);
throw error;
}
console.log(`Retry ${attempt + 1}/${maxRetries} after ${delays[attempt]}ms`);
await sleep(delays[attempt]);
}
}
}
function isRetryableError(error) {
// Network errors, timeouts, and 5xx responses are retryable
const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
return retryableCodes.includes(error.code) ||
(error.status && error.status >= 500);
}
Corak Pemutus Litar
Cegah kegagalan berturut-turut apabila perkhidmatan hiliran tidak tersedia:
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000;
this.state = 'CLOSED';
this.failures = 0;
this.lastFailure = null;
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailure > this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failures++;
this.lastFailure = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
console.warn('Circuit breaker opened due to failures');
}
}
}
const databaseCircuitBreaker = new CircuitBreaker();
async function handleBulkCompletedSafely(payload) {
await databaseCircuitBreaker.execute(async () => {
await processVerificationResults(payload.job_id, payload.results);
});
}
Pemantauan dan Amaran
Jejak metrik kesihatan webhook:
const metrics = {
received: 0,
processed: 0,
failed: 0,
latency: []
};
app.post('/webhooks/email-verification', async (req, res) => {
const startTime = Date.now();
metrics.received++;
try {
await handleWebhookEvent(req.body);
metrics.processed++;
} catch (error) {
metrics.failed++;
throw error;
} finally {
metrics.latency.push(Date.now() - startTime);
// Keep only last 1000 measurements
if (metrics.latency.length > 1000) {
metrics.latency.shift();
}
}
res.status(200).json({ received: true });
});
// Expose metrics endpoint
app.get('/metrics/webhooks', (req, res) => {
const avgLatency = metrics.latency.reduce((a, b) => a + b, 0) / metrics.latency.length;
res.json({
received: metrics.received,
processed: metrics.processed,
failed: metrics.failed,
success_rate: (metrics.processed / metrics.received * 100).toFixed(2) + '%',
avg_latency_ms: Math.round(avgLatency)
});
});
// Alert on high failure rate
setInterval(() => {
const failureRate = metrics.failed / metrics.received;
if (failureRate > 0.1) { // More than 10% failures
sendAlert({
type: 'high_webhook_failure_rate',
failure_rate: failureRate,
total_received: metrics.received,
total_failed: metrics.failed
});
}
}, 60000);
Menguji Pelaksanaan Webhook
Pengujian menyeluruh memastikan pengendali webhook berfungsi dengan betul dalam pengeluaran.
Ujian Tempatan dengan ngrok
Gunakan ngrok untuk mendedahkan titik akhir tempatan untuk ujian webhook:
# Start your local server node server.js # In another terminal, expose it via ngrok ngrok http 3000
Daftarkan URL ngrok sebagai titik akhir webhook anda semasa pembangunan.
Muatan Webhook Mock
Cipta fixture ujian untuk jenis peristiwa yang berbeza:
const mockPayloads = {
bulkCompleted: {
event_type: 'bulk.completed',
job_id: 'job_123456',
status: 'completed',
timestamp: new Date().toISOString(),
summary: {
total: 1000,
valid: 850,
invalid: 120,
risky: 30
},
download_url: 'https://api.billionverify.com/v1/bulk/download/job_123456'
},
bulkFailed: {
event_type: 'bulk.failed',
job_id: 'job_789012',
error_code: 'PROCESSING_ERROR',
error_message: 'Internal processing error',
partial_results_available: true
},
bulkProgress: {
event_type: 'bulk.progress',
job_id: 'job_345678',
processed_count: 5000,
total_count: 10000,
estimated_completion: new Date(Date.now() + 3600000).toISOString()
}
};
// Test endpoint
describe('Webhook Handler', () => {
it('should process bulk.completed event', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', generateSignature(mockPayloads.bulkCompleted))
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(200);
expect(response.body.received).toBe(true);
// Verify side effects
const job = await db('verification_jobs').where('job_id', 'job_123456').first();
expect(job.status).toBe('completed');
});
});
Ujian Integrasi
Uji aliran webhook yang lengkap termasuk pengesahan tandatangan:
describe('Webhook Security', () => {
it('should reject requests without signature', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(401);
});
it('should reject requests with invalid signature', async () => {
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', 'invalid_signature')
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(401);
});
it('should accept requests with valid signature', async () => {
const signature = generateSignature(mockPayloads.bulkCompleted);
const response = await request(app)
.post('/webhooks/email-verification')
.set('X-Webhook-Signature', signature)
.send(mockPayloads.bulkCompleted);
expect(response.status).toBe(200);
});
});
Integrasi Webhook BillionVerify
BillionVerify menyediakan sokongan webhook yang komprehensif untuk peristiwa pengesahan e-mel, memudahkan pembinaan aliran kerja pengesahan asinkronus.
Mengkonfigurasi Webhook
Sediakan webhook melalui papan pemuka atau API BillionVerify:
// Register webhook via API
async function setupBillionVerifyWebhooks() {
const webhook = await registerWebhook(
'https://yourapp.com/webhooks/billionverify',
['bulk.completed', 'bulk.failed', 'bulk.progress'],
process.env.BILLIONVERIFY_WEBHOOK_SECRET
);
console.log('Webhook configured:', webhook);
}
Format Muatan Webhook
Webhook BillionVerify termasuk maklumat komprehensif tentang peristiwa pengesahan:
{
"event_type": "bulk.completed",
"webhook_id": "wh_abc123",
"job_id": "job_xyz789",
"timestamp": "2025-01-15T10:30:00Z",
"status": "completed",
"summary": {
"total": 10000,
"valid": 8500,
"invalid": 1200,
"risky": 300,
"disposable": 150,
"catch_all": 200
},
"processing_time_ms": 45000,
"download_url": "https://api.billionverify.com/v1/bulk/download/job_xyz789"
}
Contoh Integrasi Lengkap
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook endpoint for BillionVerify
app.post('/webhooks/billionverify', async (req, res) => {
// Verify signature
const signature = req.headers['x-billionverify-signature'];
const isValid = verifySignature(req.body, signature);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Acknowledge immediately
res.status(200).json({ received: true });
// Process asynchronously
processWebhookAsync(req.body);
});
async function processWebhookAsync(payload) {
try {
switch (payload.event_type) {
case 'bulk.completed':
await handleBulkCompleted(payload);
break;
case 'bulk.failed':
await handleBulkFailed(payload);
break;
case 'bulk.progress':
await handleBulkProgress(payload);
break;
}
} catch (error) {
console.error('Webhook processing error:', error);
await logFailedWebhook(payload, error);
}
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
Kesimpulan
Webhook pengesahan e-mel mengubah cara aplikasi mengendalikan pengesahan pukal dengan membolehkan pemprosesan asinkronus yang cekap, berskala, dan boleh dipercayai. Dengan melaksanakan pengendalian webhook yang betul dengan langkah keselamatan, pengendalian ralat, dan pemantauan, anda boleh membina aliran kerja pengesahan e-mel yang teguh yang berskala dengan keperluan aplikasi anda.
Intipati utama untuk melaksanakan webhook pengesahan e-mel:
- Bertindak balas dengan cepat kepada permintaan webhook dan proses muatan secara asinkronus
- Sahkan tandatangan untuk memastikan webhook datang daripada sumber yang sah
- Laksanakan idempoten untuk mengendalikan penghantaran pendua dengan selamat
- Gunakan baris gilir mesej untuk pemprosesan yang boleh dipercayai pada skala
- Pantau kesihatan webhook dengan metrik dan amaran
Sama ada anda memproses ribuan atau berjuta-juta pengesahan e-mel, webhook menyediakan asas untuk pemprosesan asinkronus yang cekap. Mulakan melaksanakan webhook hari ini dengan sokongan webhook komprehensif BillionVerify dan tingkatkan aliran kerja pengesahan e-mel anda ke tahap seterusnya.