Webhooks

كيف تؤمّن webhook مدفوعات العملات الرقمية

ما الذي يحمي نقطة نهاية webhook للمدفوعات: التحقق من توقيع webhook، والمعالجة idempotent، وأكواد الحالة الدقيقة، وإعادة المحاولة، والتسجيل. استنادًا إلى كيفية عمل webhook في Qvard فعليًا.

الـ webhook هو اللحظة التي يقرر فيها متجرك أن الطلب مدفوع. أي شخص يستطيع استدعاء نقطة النهاية هذه بالجسم الصحيح قد يحاول الحصول على بضاعة مجانًا، لذا تستحق عناية أكبر من معظم المسارات. يصف هذا الدليل ما يحمي webhook المدفوعات، استنادًا إلى كيفية عمل webhook في Qvard اليوم، بما في ذلك ترويسة X-Qvard-Signature، التي تتيح التحقق من أن الإشعار صادر عن Qvard.

ما هو webhook في Qvard وما ليس هو

عند اكتمال الدفعة يرسل Qvard طلب POST إلى callback URL المحفوظ في مشروعك. الجسم صغير: الـ id الذي أعطيته عند إنشاء الدفعة وstatus الدفعة. يُرسل الـ webhook عندما تكون الدفعة مكتملة، وهي الإشارة لوضع علامة «مدفوع» على الطلب.

يحمل الطلب ترويسة X-Qvard-Signature بالصيغة t=<unix>,v1=<hex>: HMAC-SHA256 للطابع الزمني والجسم الخام، محسوبًا بمفتاحك السري. تُرسَل قيمتان v1 لمدة 24 ساعة بعد تدوير المفتاح، وأيّهما صالح. التجار الذين لديهم مفتاح قديم (بلا بادئة qvard_sk_) يتلقّون أيضًا ترويسة x-api-key في الـ webhook؛ تحقّق من التوقيع على أي حال. ويُتحقق منه على الجسم الخام قبل تحليل JSON. هذا الدليل يصف التحقق من التوقيع كما ترسله Qvard اليوم.

خاصيتان في التصميم تحددان كل شيء آخر: نقطة نهايتك عنوان عام، والسر الوحيد الذي يحميها هو مفتاحك السري الذي تُتحقق به من التوقيع.

التهديدات المهمة فعلًا

قائمة قصيرة أفيد من قائمة طويلة:

  • طلب مزوَّر. شخص يجد نقطة نهايتك فيرسل { "id": "1234", "status": "completed" } ليُعلَّم طلب بأنه مدفوع. التحقق من التوقيع هو ما يوقف ذلك.
  • مفتاح مسرَّب. المفتاح نفسه يوقّع الـ webhook ويوثّق استدعاءاتك للـ API. إن ظهر في مستودع أو سجل أو شيفرة واجهة أمامية انكشف كلاهما.
  • تسليم مكرر. ليس هجومًا لكنه السبب الأشيع للضرر الفعلي: يصل الحدث نفسه مرتين فيُنفَّذ الطلب مرتين.
  • نقطة نهاية بطيئة أو فاشلة. عمليات التسليم الفائتة تترك طلبات مدفوعة دون تنفيذ.
  • تسجيل مهمل. تسجيل الطلب كاملًا يضع المفتاح في ملفات السجل.

كل قسم من الأقسام التالية يسدّ أحد هذه التهديدات.

افحص المفتاح وقارنه بأمان

اقرأ الترويسة X-Qvard-Signature في كل طلب وتحقّق منها على الجسم الخام قبل فعل أي شيء آخر. تفصيلان مهمان:

  • مقارنة بزمن ثابت. قد يسرّب === العادي معلومات عن عدد الأحرف الأولى المتطابقة. استخدم دالة الزمن الثابت في لغتك (crypto.timingSafeEqual في Node.js، وhash_equals في PHP، وhmac.compare_digest في Python). افحص الأطوال أولًا إن كانت الدالة تشترط أحجامًا متساوية.
  • ارفض بصمت. أجب بـ 400 دون شرح. لا تقل هل التوقيع مفقود أم خاطئ.

يعرض المثال أدناه المعالج كاملًا: فحص التوقيع، وحدًّا صغيرًا للجسم، والتحقق من الحقول، وتحديثًا لا يغيّر إلا طلبًا غير مدفوع بعد، وردًّا بـ 200 بالضبط. احتفظ بالمفتاح في متغير بيئة أو مدير أسرار، وليس في المستودع أو شيفرة الواجهة الأمامية أبدًا.

Node.jswebhook.js
import express from 'express';
import { Qvard } from '@qvard/node';

const secret = process.env.QVARD_SECRET_KEY;
const qvard = new Qvard(secret);
const app = express();

app.post('/webhooks/qvard', express.raw({ type: 'application/json', limit: '10kb' }), async (req, res) => {
  let event;
  try {
    event = qvard.webhooks.constructEvent(req.body, req.get('X-Qvard-Signature'), secret);
  } catch {
    return res.sendStatus(400);
  }

  const { id, status } = event;
  if (typeof id !== 'string' || status !== 'completed') return res.sendStatus(200);

  // Idempotent: the update only matches an order that is not paid yet.
  const changed = await db.orders.markPaidIfNot(id);
  if (changed) queue.push({ type: 'fulfil', orderId: id }); // slow work happens after the answer

  return res.sendStatus(200); // exactly 200
});

اجعل المعالج idempotent

يعدّ Qvard التسليم ناجحًا فقط عندما تردّ نقطتك بـ HTTP 200 بالضبط. أي حالة أخرى أو انتهاء مهلة أو إعادة توجيه تُحسب فشلًا وتُعاد المحاولة: خمس محاولات في المجموع خلال نحو ست دقائق. والنتيجة أن معالجك سيرى أحيانًا الحدث نفسه أكثر من مرة، مثلًا حين أنجز خادمك العمل لكن الرد ضاع.

صمّم مع أخذ ذلك في الحسبان:

  • اجعل تغيير الحالة مشروطًا: «ضع علامة مدفوع على هذا الطلب إن لم يكن مدفوعًا بعد»، ويفضّل بعبارة واحدة في قاعدة البيانات، حتى لا ينجح تسليمان متوازيان معًا.
  • ابدأ التنفيذ (رسائل بريد، مفاتيح، وصول) فقط عندما يكون التحديث قد غيّر صفًّا فعلًا.
  • تجاهل أحداث الطلبات التي لا تعرفها لكن أجب بـ 200 مع ذلك، وإلا واصل Qvard إعادة المحاولة لشيء لن تعالجه أبدًا.
  • تصرّف عند completed فقط. لا يجوز لأي حالة أخرى أن تضع علامة «مدفوع» على طلب.

وهذا أيضًا دفاع ضد الطلب المُعاد: حدث صالح مكرر لا يغيّر شيئًا في المرة الثانية.

أجب بسرعة واعمل لاحقًا

يجب أن تردّ نقطتك قبل أن يستسلم Qvard، لذا اجعلها قصيرة: افحص، حدّث، ضع في الطابور، أجب. الأجزاء البطيئة مثل إرسال البريد واستدعاء أنظمة أخرى وتوليد الملفات نفّذها من طابور بعد الرد.

فكّر أيضًا فيما لا تعرفه نقطتك. يحمل الجسم id وstatus فقط، لذا يأتي المبلغ المتوقع من طلبك أنت لا من الـ webhook. لا تأخذ مبلغًا أو عملة أو منتجًا من جسم الطلب أبدًا.

قدّم النقطة عبر HTTPS فقط وتأكد ألا يقف أمامها وكيل أو إعادة توجيه: إعادات التوجيه لا تُتبع، لذا فإن 301 من http إلى https تسليم فاشل.

السجلات والمراقبة والاستعادة

عامل الـ webhook كحركة مال واحتفظ بأثر.

  • سجّل ما قررته لا السر. احفظ id وstatus والوقت وقرارك (طُبّق، مكرر، طلب مجهول، مفتاح مرفوض). لا تسجّل مفتاحك السري أبدًا.
  • نبّه عند الرفض. سلسلة مفاجئة من ردود 400 تعني إما أن أحدًا يختبر نقطتك أو أنك غيّرت مفتاحك في مكان دون الجميع.
  • طابق. مرة يوميًا قارن الطلبات التي تعدّها مدفوعة برصيدك ومدفوعاتك في لوحة Qvard.
  • استعد. إن توقف خادمك أطول من نافذة إعادة المحاولة فأعد إرسال إشعار تلك الدفعة من اللوحة ودع معالجك idempotent يتولى الأمر.

إن شككت في تسرّب المفتاح فاستبدله وراجع أي أنظمتك استخدمته.

قائمة تحقق قبل الإطلاق

  • نقطة النهاية HTTPS وعامة ويمكن الوصول إليها من خارج شبكتك.
  • تتحقق من ترويسة X-Qvard-Signature على الجسم الخام وتردّ بـ 400 خلاف ذلك.
  • تحدّ من حجم الجسم وتتحقق من id وstatus.
  • تضع علامة «مدفوع» على الطلب عند completed فقط وفقط إن لم يكن مدفوعًا.
  • تردّ بـ 200 بالضبط وبسرعة وتنجز العمل البطيء لاحقًا.
  • لا تثق أبدًا بمبالغ أو منتجات من الجسم.
  • لا تسجّل المفتاح أبدًا.
  • اختبرت ردًّا فاشلًا وتسليمًا مكررًا وطلبًا مجهولًا.

للمسار الكامل حول الـ webhook انظر كيف تقبل مدفوعات العملات الرقمية على موقع ويب. صيغ الطلب والاستجابة في وثائق المطورين.

FAQ

هل يوقّع Qvard الـ webhook بـ HMAC؟

نعم. يحمل كل webhook ترويسة X-Qvard-Signature (t=<unix>,v1=<hex>): HMAC-SHA256 للطابع الزمني والجسم الخام، محسوبًا بمفتاحك السري. تحقّق منها على الجسم الخام بزمن ثابت، وعامل مفتاحك كسرّ.

أي رمز حالة يجب أن تعيده نقطة نهايتي؟

HTTP 200 بالضبط. أي حالة أخرى أو انتهاء مهلة أو إعادة توجيه تُحسب تسليمًا فاشلًا وتُعاد المحاولة، خمس محاولات في المجموع خلال نحو ست دقائق.

ماذا لو كان خادمي متوقفًا عند اكتمال الدفعة؟

تغطي إعادة المحاولة نحو ست دقائق. بعد ذلك أعد إرسال إشعار الدفعة من اللوحة. المعالج idempotent يجعل ذلك آمنًا.

هل يمكنني الوثوق بالمبلغ في الـ webhook؟

يحمل الـ webhook معرّف الدفعة وحالتها فقط. استخدم المبلغ من طلبك أنت وضع علامة «مدفوع» عند الحالة completed فقط.

مقالات ذات صلة

مقالات ذات صلة