Webhooks

استراتيجية إعادة محاولة الـ webhook في أنظمة الدفع

كيف يعيد Qvard محاولة webhook الدفع، وما الذي يُعدّ تسليمًا فاشلًا، وكيف تبني معالجًا يصمد أمام إعادة المحاولة والتكرار والتوقف دون تنفيذ الطلب مرتين.

webhook الدفع رسالة يجب ألا تضيع وألا يُتصرف بناءً عليها مرتين. الشبكات تفشل والخوادم تعيد التشغيل، لذا يعيد المرسل المحاولة؛ ولهذا يجب أن يُكتب معالجك مع احتساب التكرار. يصف هذا الدليل كيف يعيد Qvard المحاولة، وما الذي يعدّه فشلًا، وكيف تبني معالجًا وروتين استعادة حول هذا السلوك.

متى يُرسل الـ webhook

يرسل Qvard الـ webhook عندما تكون الدفعة مكتملة. وهو طلب POST إلى callback URL المحفوظ في مشروعك، بجسم JSON صغير يحوي الـ id الخاص بك (المعرّف الذي أعطيته عند إنشاء الدفعة) وstatus، موقّعًا في الترويسة X-Qvard-Signature.

الدفعة الناقصة أو المرفوضة أو التي ما زالت تنتظر لا تنتج webhook اكتمال. إن احتجت إلى معرفة تلك الدفعات فانظر إليها في اللوحة؛ ولا تنتظر رسالة لا تُرسل.

ما الذي يُعدّ مسلَّمًا

يعدّ Qvard التسليم ناجحًا فقط عندما تردّ نقطتك بـ HTTP 200 بالضبط. كل ما عدا ذلك فشل:

  • أي رمز حالة آخر، بما فيها 201 و202 و204 وكل 4xx و5xx؛
  • انتهاء المهلة، لأن خادمك كان بطيئًا أو غير قابل للوصول؛
  • إعادة التوجيه: لا يتبع Qvard الرمزين 301 و302، لذا فقاعدة تعيد توجيه http إلى https أو تضيف شرطة مائلة في النهاية تجعل التسليم يفشل.

هذه الدقة مقصودة. يبدو 204 No Content ردًّا جيدًا تمامًا ومع ذلك هو تسليم فاشل. أعد 200، بجسم فارغ إن شئت.

كيف تعمل إعادة المحاولة

بعد تسليم فاشل يحاول Qvard مجددًا. المجموع خمس محاولات خلال نحو ست دقائق. ويترتب على ذلك أمران:

  • التوقف القصير مغطّى. إن أعاد خادمك التشغيل أو استغرق نشر دقيقة أو دقيقتين فإن محاولة لاحقة تنجح ولا تخسر شيئًا.
  • التوقف الأطول لا. بعد المحاولة الأخيرة يتوقف Qvard. إن كان خادمك متوقفًا أكثر من تلك الدقائق الست فلن يصل الإشعار من تلقاء نفسه؛ أعد إرساله لتلك الدفعة من اللوحة.

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

Pythonwebhook.py
import os
from fastapi import FastAPI, Request, Response
from qvard import Qvard

app = FastAPI()
SECRET = os.environ["QVARD_SECRET_KEY"]
qvard = Qvard(SECRET)

@app.post("/webhooks/qvard")
async def qvard_webhook(request: Request):
    try:
        body = qvard.webhooks.construct_event(await request.body(), request.headers.get("X-Qvard-Signature", ""), SECRET)
    except ValueError:
        return Response(status_code=400)

    order_id, status = body.get("id"), body.get("status")

    # Idempotent: the UPDATE changes a row only for an order that is not paid yet.
    if status == "completed":
        changed = await db.execute(
            "UPDATE orders SET state = 'paid' WHERE id = :id AND state <> 'paid'", {"id": order_id}
        )
        if changed:
            await queue.enqueue("fulfil", order_id)   # slow work after the answer

    return Response(status_code=200)   # exactly 200, also for repeats and unknown orders

ما الذي يجب أن يفعله معالجك

أربع قواعد تغطي تقريبًا كل المشكلات:

  1. تحقّق من التوقيع. تحقّق من ترويسة X-Qvard-Signature (HMAC-SHA256 بمفتاحك السري على الجسم الخام) بزمن ثابت وردّ بـ 400 إن لم يتطابق. تحقّق منها قبل التصرف في الدفعة. انظر كيف تؤمّن webhook مدفوعات العملات الرقمية.
  2. كن idempotent. اجعل تغيير الحالة مشروطًا، «ضع علامة مدفوع إن لم يكن مدفوعًا بعد»، ويفضّل في عبارة واحدة بقاعدة البيانات، حتى لا ينجح تسليمان متوازيان معًا.
  3. أجب بسرعة. افحص، حدّث، ضع في الطابور، وردّ بـ 200 بالضبط. نفّذ البريد وتوليد الملفات والاستدعاءات لأنظمة أخرى من طابور، حتى لا تتحول خدمة بطيئة إلى انتهاء مهلة للـ webhook.
  4. ردّ بـ 200 على ما لن تعالجه أبدًا. الطلب المجهول أو التكرار يجب أن يعيدا 200 أيضًا؛ وإلا واصل Qvard إعادة رسالة عالجتها بالفعل أو لن تعالجها أبدًا.

أعطال تبدو كأخطاء في Qvard

معظم بلاغات «لم يصل الـ webhook» لها سبب في جانب الاستقبال:

  • جدار حماية أو WAF يحجب الطلبات من عناوين مجهولة، أو صفحة «قريبًا» أو صيانة.
  • إعادة توجيه من http إلى https، أو من www إلى النطاق المجرد أو إلى شرطة مائلة نهائية.
  • خطأ في الإطار بعد أن حُسم الرد، مثل استثناء في middleware يحوّل الـ 200 لديك إلى 500.
  • معالج بطيء ينجز العمل قبل الرد فيصطدم بانتهاء المهلة.
  • ذاكرة تخزين مؤقت أو CDN أمام المسار.
  • عنوان خاطئ محفوظ في المشروع، أو خطأ مطبعي في المسار.

اختبر نقطة النهاية من خارج شبكتك بطلب يحمل ترويسة X-Qvard-Signature صالحة، وانظر في سجل الوصول لخادمك إلى رمز الرد، لا إلى وصول الطلب فقط.

روتين استعادة للإشعارات الفائتة

تغطي إعادة المحاولة التوقفات القصيرة؛ وللباقي أضف شبكة أمان.

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

باقي التكامل، بما فيه إنشاء الدفعة، في كيف تقبل مدفوعات العملات الرقمية على موقع ويب وواجهة API لمدفوعات العملات الرقمية للمطورين.

FAQ

كم مرة يعيد Qvard محاولة الـ webhook؟

خمس محاولات في المجموع خلال نحو ست دقائق. بعد ذلك يتوقف Qvard، ويمكنك إعادة إرسال إشعار الدفعة من اللوحة.

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

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

هل يمكن أن يصل الـ webhook نفسه مرتين؟

نعم، مثلًا عندما عالج خادمك التسليم الأول لكن الرد ضاع. اجعل المعالج idempotent وردّ بـ 200 على التكرارات مع ذلك.

هل أتلقى webhook لدفعة ناقصة أو مرفوضة؟

لا. يُرسل الـ webhook عند اكتمال الدفعة. افحص بقية الدفعات في اللوحة.

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

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