Skip to main content

نظرة عامة

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

المتطلبات الأساسية

  • حساب تاجر Dodo Payments ومفتاح API
  • إعداد سر webhook ونقطة نهاية لاستقبال الأحداث
  • منتج اشتراك في الكتالوج الخاص بك
ينشئ هذا الدليل الاشتراك عند الطلب من خلال جلسة checkout (POST /checkouts)، والتي تعرض دائماً checkout_url مستضافاً. أعد توجيه العميل إليها للموافقة على mandate، واضبط return_url على المكان الذي يجب أن ينتقل إليه بعد ذلك.

آلية عمل الاشتراكات عند الطلب

  1. تنشئ اشتراكاً باستخدام الكائن on_demand لتفويض طريقة الدفع، مع إمكانية تحصيل رسم ابتدائي.
  2. لاحقاً، تنشئ رسوماً مقابل ذلك الاشتراك بمبالغ مخصصة باستخدام نقطة النهاية المخصصة للرسوم.
  3. تستمع إلى webhooks (مثل payment.succeeded وpayment.failed) لتحديث نظامك.

إنشاء اشتراك عند الطلب

نقطة النهاية: POST /checkouts حقول الطلب الأساسية (body):
يمكنك العثور عليها في إنشاء جلسة Checkout

إنشاء اشتراك عند الطلب

Success

تحصيل رسم من اشتراك عند الطلب

بعد تفويض mandate، أنشئ الرسوم حسب الحاجة. نقطة النهاية: POST /subscriptions/{subscription_id}/charge حقول الطلب الأساسية (body):
integer
مطلوب
المبلغ المطلوب تحصيله (بأصغر وحدة للعملة). مثال: لتحصيل 25.00$، مرّر 2500.
string
تجاوز اختياري للعملة المستخدمة في الرسم.
string
تجاوز اختياري لوصف هذا الرسم.
boolean
إذا كانت القيمة true، فسيشمل رسوم Adaptive Currency ضمن product_price. وإذا كانت false، فستُضاف الرسوم فوق السعر.
object
حدّد كيفية استخدام رصيد محفظة العميل لتسوية هذه الرسوم.
object
بيانات وصفية إضافية للدفع. في حال عدم توفيرها، تُستخدم البيانات الوصفية للاشتراك.
Success
قد يفشل تحصيل رسوم اشتراك غير عند الطلب. تأكد من أن تفاصيل الاشتراك تتضمن on_demand: true قبل تحصيل الرسوم.

التعامل مع الرسوم الفاشلة

عند فشل تحصيل رسوم من اشتراك عند الطلب، يمكنك تحديد الإجراء التالي. بخلاف الاشتراكات المجدولة — حيث يؤدي فشل التجديد إلى إيقاف الفوترة التلقائية اللاحقة — تظل الاشتراكات عند الطلب قابلة لتحصيل الرسوم بعد الفشل. يمكنك استدعاء endpoint تحصيل الرسوم مرة أخرى ضمن منطق إعادة المحاولة الخاص بك.

ما يحدث عند الفشل

1

Charge attempt fails

يعيد الطلب POST /subscriptions/{subscription_id}/charge إما استجابة خطأ أو يكتمل بشكل غير متزامن ويصدر webhook من النوع payment.failed يتضمن سبب الرفض.
2

Subscription may transition to on_hold

قد ينتقل الاشتراك إلى حالة on_hold ويصدر webhook من النوع subscription.on_hold (راجع حالات الاشتراك → On Hold). هذه إشارة وليست قفلًا. بالنسبة إلى الاشتراكات عند الطلب، لا تمنع on_hold من تحصيل الرسوم مرة أخرى.
3

Retry the charge (your call)

في التدفقات عند الطلب، لا ينفّذ Dodo إعادة المحاولة تلقائيًا. يمكنك استدعاء POST /subscriptions/{subscription_id}/charge مرة أخرى في أي وقت لإعادة المحاولة. طبّق سياسة إعادة المحاولة الآمنة أدناه — استخدم exponential backoff، وتجاوز حالات الرفض الصريح، وتجنب الأنماط المتكررة بكثافة — حتى لا ترصد أنظمة مكافحة الاحتيال والمخاطر لدينا عمليات إعادة المحاولة.
4

Optionally, ask the customer for a new payment method

إذا استمر الفشل لأن طريقة الدفع نفسها معطلة (بطاقة منتهية الصلاحية، حساب مغلق، وغير ذلك)، فاستخدم POST /subscriptions/{subscription_id}/update-payment-method لتحصيل طريقة دفع جديدة من العميل. عند النجاح، يعود الاشتراك إلى active وتُصدر webhooks من النوع payment.succeeded ثم subscription.active.
عند الطلب مقابل المجدول: بالنسبة إلى الاشتراكات المجدولة، ينفّذ Dodo عمليات إعادة محاولة التجديد وإدارة المتأخرات الخاصة به. أما بالنسبة إلى الاشتراكات عند الطلب، فأنت مسؤول عن سياسة إعادة المحاولة لأنك وحدك تعرف موعد تحصيل الرسوم التالي (فهو يعتمد على أحداث الاستخدام، وليس على تقويم).

تسلسل webhook عند فشل تحصيل رسوم عند الطلب

لا يُصدر الحدثان 3 و4 إلا بعد نجاح عملية تحصيل رسوم لاحقة.

مسؤولية إعادة المحاولة

لا ينفّذ Dodo Payments إعادة المحاولة تلقائيًا لتحصيل الرسوم عند الطلب الفاشل. أنت مسؤول عن سياسة إعادة المحاولة. اتبع إرشادات إعادة المحاولة الآمنة أدناه لتجنب تصنيف أنظمة اكتشاف الاحتيال لدينا لعملياتك على أنها اختبار للبطاقات.
يقتصر نطاق إدارة متأخرات الاشتراك — وهي تسلسلة استرداد مدمجة عبر البريد الإلكتروني — على مدفوعات التجديد الفاشلة في الاشتراكات المجدولة وعمليات الإلغاء التي يبدأها العميل. ولم تُصمّم للتعامل مع حالات فشل تحصيل الرسوم عند الطلب. تواصل مباشرةً مع العميل (مثلًا عبر بريد إلكتروني للمعاملات أو مطالبة داخل التطبيق) عندما تقرر أن طريقة الدفع تحتاج إلى تحديث.

إعادة محاولة الدفع

قد يحظر نظام اكتشاف الاحتيال لدينا أنماط إعادة المحاولة المكثفة (وقد يصنفها على أنها اختبار محتمل للبطاقات). اتبع سياسة آمنة لإعادة المحاولة.
قد تصنّف أنظمة المخاطر والمعالجات لدينا أنماط إعادة المحاولة المتكررة بكثافة على أنها احتيالية أو اختبارًا مشتبهًا للبطاقات. تجنب عمليات إعادة المحاولة المتقاربة؛ واتبع جدول backoff وإرشادات ضبط التوقيت أدناه.

مبادئ سياسات إعادة المحاولة الآمنة

  • آلية Backoff: استخدم exponential backoff بين عمليات إعادة المحاولة.
  • حدود إعادة المحاولة: حدّد إجمالي عمليات إعادة المحاولة (3–4 محاولات كحد أقصى).
  • التصفية الذكية: أعد المحاولة فقط عند حالات الفشل القابلة لإعادة المحاولة (مثل أخطاء الشبكة أو الجهة المُصدرة أو عدم كفاية الرصيد)؛ ولا تعاود المحاولة مطلقًا عند الرفض الصريح.
  • منع اختبار البطاقات: لا تعاود المحاولة عند حالات الفشل مثل DO_NOT_HONOR وSTOLEN_CARD وLOST_CARD وPICKUP_CARD وFRAUDULENT وAUTHENTICATION_FAILURE.
  • تنويع البيانات الوصفية (اختياري): إذا كنت تدير نظام إعادة المحاولة الخاص بك، فميّز بين عمليات إعادة المحاولة باستخدام البيانات الوصفية (مثل retry_attempt).

جدول إعادة المحاولة المقترح (الاشتراكات)

  • المحاولة الأولى: فور إنشاء الرسوم
  • المحاولة الثانية: بعد 3 أيام
  • المحاولة الثالثة: بعد 7 أيام إضافية (10 أيام إجمالًا)
  • المحاولة الرابعة (الأخيرة): بعد 7 أيام أخرى (17 يومًا إجمالًا)
الخطوة الأخيرة: إذا ظل المبلغ غير مدفوع، فضع علامة على الاشتراك باعتباره غير مدفوع أو ألغِه، وفقًا لسياستك. أخطر العميل خلال هذه الفترة لتحديث طريقة الدفع الخاصة به.

تجنب إعادة المحاولة بكثافة؛ ونسّقها مع وقت التفويض

  • اربط عمليات إعادة المحاولة بالطابع الزمني للتفويض الأصلي لتجنب السلوك «المتكرر بكثافة» عبر محفظتك.
  • مثال: إذا بدأ العميل فترة تجريبية أو تفويضًا في الساعة 1:10 مساءً اليوم، فجدول عمليات إعادة المحاولة اللاحقة في الساعة 1:10 مساءً في الأيام التالية وفقًا لـ backoff (مثلًا: +3 أيام → 1:10 مساءً، +7 أيام → 1:10 مساءً).
  • بديلًا عن ذلك، إذا خزّنت وقت آخر دفعة ناجحة T، فجدول المحاولة التالية في T + X days للحفاظ على التوافق مع وقت اليوم.
المنطقة الزمنية والتوقيت الصيفي (DST): استخدم معيارًا زمنيًا ثابتًا للجدولة، وحوّله للعرض فقط للحفاظ على الفواصل الزمنية.

رموز الرفض التي يجب ألا تعاود المحاولة عند ظهورها

  • STOLEN_CARD
  • DO_NOT_HONOR
  • FRAUDULENT
  • PICKUP_CARD
  • AUTHENTICATION_FAILURE
  • LOST_CARD
للحصول على قائمة شاملة بأسباب الرفض وما إذا كان بإمكان المستخدم تصحيحها، راجع وثائق فشل المعاملة.
أعد المحاولة فقط عند المشكلات المؤقتة أو اللينة (مثل insufficient_funds وissuer_unavailable وprocessing_error ومهلات الشبكة). إذا تكرر الرفض نفسه، فأوقف عمليات إعادة المحاولة الإضافية.

إرشادات التنفيذ (من دون تعليمات برمجية)

  • استخدم scheduler أو queue يحفظ الطوابع الزمنية الدقيقة؛ واحسب المحاولة التالية في الإزاحة الزمنية الدقيقة نفسها (مثل T + 3 days في التوقيت HH:MM نفسه).
  • احتفظ بالطابع الزمني لآخر دفعة ناجحة T وارجع إليه لحساب المحاولة التالية؛ ولا تجمع اشتراكات متعددة في اللحظة نفسها.
  • قيّم دائمًا سبب الرفض الأخير؛ وأوقف إعادة المحاولة عند حالات الرفض الصريح الواردة في قائمة التجاوز أعلاه.
  • حدّد عمليات إعادة المحاولة المتزامنة لكل عميل ولكل حساب لمنع الارتفاعات المفاجئة غير المقصودة.
  • تواصل بشكل استباقي: أرسل بريدًا إلكترونيًا أو رسالة SMS إلى العميل لتحديث طريقة الدفع قبل المحاولة المجدولة التالية.
  • استخدم البيانات الوصفية للمراقبة فقط (مثل retry_attempt)؛ ولا تحاول مطلقًا «التحايل» على أنظمة الاحتيال والمخاطر عبر تدوير الحقول غير المؤثرة.

الإلغاء

تتبع الاشتراكات عند الطلب مسار إلغاء مختلفًا عن الاشتراكات المجدولة، إذ لا توجد دورة فوترة ثابتة يمكن الاستناد إليها لتحديد تاريخ انتهاء فوري.

سلوك Customer Portal

عندما يلغي العميل اشتراكًا عند الطلب من Customer Portal، تتم جدولة الإلغاء لتاريخ الفوترة التالي افتراضيًا. ولا يظهر خيار الإلغاء الآن عمدًا للاشتراكات عند الطلب. السبب هو أن الاشتراكات عند الطلب لا تملك تواريخ تجديد متكررة يمكن التنبؤ بها — إذ يتحدد وقت تحصيل الرسوم التالي بالكامل وفق أحداث الاستخدام الخاصة بك. وتُبقي جدولة الإلغاء في تاريخ الفوترة التالي التفويض نشطًا حتى نهاية الفترة، بحيث يمكن تحصيل رسوم أي استخدام قيد التنفيذ، ثم ينتهي الاشتراك بشكل سليم. بعد تأكيد العميل للإلغاء:
  • يظل الاشتراك في حالة active وقابلًا لتحصيل الرسوم عبر POST /subscriptions/{id}/charge حتى تاريخ الإلغاء المجدول.
  • يتم تعيين cancel_at_next_billing_date إلى true في الاشتراك.
  • يُصدر webhook من النوع subscription.cancelled عند سريان الإلغاء.
إذا كنت بحاجة إلى إنهاء الاشتراك فورًا (مثلًا استجابةً لعملية استرداد أو طلب دعم)، فألغِه برمجيًا عبر API بدلًا من الاعتماد على تدفق Customer Portal.

الإلغاء برمجيًا

يمكنك إلغاء اشتراك عند الطلب عبر API في أي وقت. ويمكنك تحديد ما إذا كان الإلغاء فوريًا أو مجدولًا. Endpoint: PATCH /subscriptions/{subscription_id}
عيّن status للاشتراك إلى cancelled لإنهائه فورًا. يُلغى التفويض ولا يمكن إنشاء أي رسوم إضافية.
cURL

Webhooks عند الإلغاء

للتمييز بين عمليات إلغاء الاشتراكات عند الطلب وعمليات إلغاء الاشتراكات المجدولة في handler الخاص بك، تحقق من flag ‏on_demand للاشتراك عند معالجة webhook.

تتبّع النتائج باستخدام webhooks

نفّذ معالجة webhooks لتتبّع رحلة العميل. راجع تنفيذ Webhooks.
  • subscription.active: تم تفويض التفويض وتفعيل الاشتراك
  • subscription.failed: فشل الإنشاء (مثل فشل التفويض)
  • subscription.on_hold: وُضع الاشتراك قيد التعليق (مثل حالة عدم الدفع)
  • subscription.cancelled: أُلغي الاشتراك بالكامل (راجع الإلغاء)
  • payment.succeeded: نجح تحصيل الرسوم
  • payment.failed: فشل تحصيل الرسوم
في التدفقات عند الطلب، ركّز على payment.succeeded وpayment.failed لتسوية الرسوم المستندة إلى الاستخدام. عندما يتبع payment.failedsubscription.on_hold، راجع التعامل مع الرسوم الفاشلة لاسترداد الاشتراك.

الاختبار والخطوات التالية

1

Create in test mode

استخدم مفتاح API للاختبار لإنشاء الاشتراك، ثم افتح checkout_url المُعاد وأكمل التفويض.
2

Trigger a charge

استدعِ endpoint تحصيل الرسوم مع product_price صغير (مثل 100) وتحقق من استلامك payment.succeeded.
3

Go live

انتقل إلى مفتاح API المباشر بعد التحقق من الأحداث وتحديثات الحالة الداخلية.

استكشاف الأخطاء وإصلاحها

  • 422 Invalid Request: تأكد من توفير on_demand.mandate_only عند الإنشاء وتوفير product_price للرسوم.
  • أخطاء العملة: إذا تجاوزت product_currency، فتأكد من أنها مدعومة لحسابك وللعميل.
  • عدم استلام webhooks: تحقق من إعداد عنوان URL الخاص بـ webhook وسر التوقيع.
آخر تعديل في ٦ أغسطس ٢٠٢٦