تكامل خطافات الويب
إعداد خطافات الويب الخاصة بمزوّد الدفع والتعامل معها لأحداث الاشتراك.
ملاحظة: هذا محتوى تجريبي/مؤقت لأغراض العرض التوضيحي.
تُخطِر خطافات الويب تطبيقك عند وقوع أحداث الفوترة، مما يضمن بقاء تطبيقك متزامنًا مع مزوّد الدفع.
لماذا خطافات الويب؟
تُعدّ خطافات الويب ضرورية لما يلي:
- التحديثات الفورية - إشعار لحظي بأحداث الدفع
- الموثوقية - تعالج الأحداث حتى إذا أغلق المستخدمون متصفحاتهم
- الأمان - اتصال مباشر من خادم إلى خادم
- الأتمتة - تحديثات تلقائية لحالة الاشتراك
نقطة نهاية خطاف الويب
تستقبل نقطة نهاية خطاف الويب الخاصة بك الأحداث من مزوّد الدفع:
// app/api/billing/webhook/route.ts
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('stripe-signature');
// Verify webhook signature
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
// Handle the event
await handleBillingEvent(event);
return new Response('OK', { status: 200 });
}
الأحداث الشائعة
إنشاء الاشتراك
case 'customer.subscription.created':
await prisma.subscription.create({
data: {
id: event.data.object.id,
accountId: event.data.object.metadata.accountId,
status: 'active',
planId: event.data.object.items.data[0].price.id,
currentPeriodEnd: new Date(event.data.object.current_period_end * 1000),
},
});
break;
تحديث الاشتراك
case 'customer.subscription.updated':
await prisma.subscription.update({
where: { id: event.data.object.id },
data: {
status: event.data.object.status,
planId: event.data.object.items.data[0].price.id,
currentPeriodEnd: new Date(event.data.object.current_period_end * 1000),
},
});
break;
حذف الاشتراك
case 'customer.subscription.deleted':
await prisma.subscription.update({
where: { id: event.data.object.id },
data: {
status: 'canceled',
canceledAt: new Date(),
},
});
break;
فشل الدفع
case 'invoice.payment_failed':
const subscription = await prisma.subscription.findUnique({
where: { id: event.data.object.subscription },
});
// Send payment failure notification
await sendPaymentFailureEmail(subscription.accountId);
break;
إعداد خطافات الويب
Stripe
- التطوير المحلي (باستخدام Stripe CLI):
stripe listen --forward-to localhost:3000/api/billing/webhook
- بيئة الإنتاج:
- انتقل إلى لوحة تحكم Stripe ← Developers ← Webhooks
- أضف نقطة النهاية:
https://yourdomain.com/api/billing/webhook - حدّد الأحداث التي تريد الاستماع إليها
- انسخ سرّ توقيع خطاف الويب إلى ملف
.envالخاص بك
Paddle
- اضبط عنوان URL لخطاف الويب في لوحة تحكم Paddle
- أضف سرّ خطاف الويب إلى متغيرات البيئة
- تحقّق من توقيع خطاف الويب:
const signature = request.headers.get('paddle-signature');
const verified = paddle.webhooks.verify(body, signature);
if (!verified) {
return new Response('Invalid signature', { status: 401 });
}
أفضل ممارسات الأمان
- تحقّق دائمًا من التواقيع - يمنع ذلك الطلبات غير المصرّح بها
- استخدم HTTPS - يشفّر بيانات خطاف الويب أثناء النقل
- تحقّق من صحة بيانات الحدث - تأكّد من وجود الحقول المطلوبة
- اجعل المعالجة متساوية القوى (Idempotent) - عالج الأحداث المكرّرة بأمان
- أعِد الرمز 200 بسرعة - أكّد الاستلام، ثم عالج الحدث بشكل غير متزامن
معالجة الأخطاء
async function handleBillingEvent(event: Event) {
try {
await processEvent(event);
} catch (error) {
// Log error for debugging
console.error('Webhook error:', error);
// Store failed event for retry
await prisma.failedWebhook.create({
data: {
eventId: event.id,
type: event.type,
payload: event,
error: error.message,
},
});
// Throw to trigger provider retry
throw error;
}
}
اختبار خطافات الويب
استخدام أدوات CLI الخاصة بالمزوّد
# Stripe stripe trigger customer.subscription.created # Test specific scenarios stripe trigger payment_intent.payment_failed
الاختبار اليدوي
curl -X POST https://your-app.com/api/billing/webhook \ -H "Content-Type: application/json" \ -H "stripe-signature: test_signature" \ -d @test-event.json
المراقبة
تتبّع تسليم خطافات الويب:
- أوقات الاستجابة
- معدلات النجاح/الفشل
- مدة معالجة الأحداث
- الأحداث الفاشلة التي تتطلّب تدخّلًا يدويًا
يوفّر معظم المزوّدين لوحات تحكم لمراقبة خطافات الويب تعرض محاولات التسليم وحالات الفشل.