نظرة عامة
تتيح لك الاشتراكات عند الطلب تفويض طريقة دفع العميل مرة واحدة، ثم تحصيل مبالغ متغيرة كلما احتجت إلى ذلك، بدلاً من اتباع جدول زمني ثابت. هذه الميزة متاحة لجميع الحسابات — ولا تتطلب موافقة. استخدم هذا الدليل من أجل:- إنشاء اشتراك عند الطلب (تفويض mandate مع سعر ابتدائي اختياري)
- تشغيل رسوم لاحقة بمبالغ مخصصة
- تتبّع النتائج باستخدام webhooks
المتطلبات الأساسية
- حساب تاجر Dodo Payments ومفتاح API
- إعداد سر webhook ونقطة نهاية لاستقبال الأحداث
- منتج اشتراك في الكتالوج الخاص بك
آلية عمل الاشتراكات عند الطلب
- تنشئ اشتراكاً باستخدام الكائن
on_demandلتفويض طريقة الدفع، مع إمكانية تحصيل رسم ابتدائي. - لاحقاً، تنشئ رسوماً مقابل ذلك الاشتراك بمبالغ مخصصة باستخدام نقطة النهاية المخصصة للرسوم.
- تستمع إلى webhooks (مثل
payment.succeededوpayment.failed) لتحديث نظامك.
إنشاء اشتراك عند الطلب
نقطة النهاية: POST /checkouts حقول الطلب الأساسية (body):يمكنك العثور عليها في إنشاء جلسة Checkout
إنشاء اشتراك عند الطلب
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
تحصيل رسم من اشتراك عند الطلب
بعد تفويض mandate، أنشئ الرسوم حسب الحاجة. نقطة النهاية: POST /subscriptions/{subscription_id}/charge حقول الطلب الأساسية (body):Charge request body parameters
Charge request body parameters
integer
مطلوب
المبلغ المطلوب تحصيله (بأصغر وحدة للعملة). مثال: لتحصيل 25.00$، مرّر
2500.string
تجاوز اختياري للعملة المستخدمة في الرسم.
string
تجاوز اختياري لوصف هذا الرسم.
boolean
إذا كانت القيمة true، فسيشمل رسوم Adaptive Currency ضمن
product_price. وإذا كانت false، فستُضاف الرسوم فوق السعر.object
حدّد كيفية استخدام رصيد محفظة العميل لتسوية هذه الرسوم.
object
بيانات وصفية إضافية للدفع. في حال عدم توفيرها، تُستخدم البيانات الوصفية للاشتراك.
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
التعامل مع الرسوم الفاشلة
عند فشل تحصيل رسوم من اشتراك عند الطلب، يمكنك تحديد الإجراء التالي. بخلاف الاشتراكات المجدولة — حيث يؤدي فشل التجديد إلى إيقاف الفوترة التلقائية اللاحقة — تظل الاشتراكات عند الطلب قابلة لتحصيل الرسوم بعد الفشل. يمكنك استدعاء 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 إلا بعد نجاح عملية تحصيل رسوم لاحقة.
مسؤولية إعادة المحاولة
يقتصر نطاق إدارة متأخرات الاشتراك — وهي تسلسلة استرداد مدمجة عبر البريد الإلكتروني — على مدفوعات التجديد الفاشلة في الاشتراكات المجدولة وعمليات الإلغاء التي يبدأها العميل. ولم تُصمّم للتعامل مع حالات فشل تحصيل الرسوم عند الطلب. تواصل مباشرةً مع العميل (مثلًا عبر بريد إلكتروني للمعاملات أو مطالبة داخل التطبيق) عندما تقرر أن طريقة الدفع تحتاج إلى تحديث.إعادة محاولة الدفع
قد يحظر نظام اكتشاف الاحتيال لدينا أنماط إعادة المحاولة المكثفة (وقد يصنفها على أنها اختبار محتمل للبطاقات). اتبع سياسة آمنة لإعادة المحاولة.مبادئ سياسات إعادة المحاولة الآمنة
- آلية 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_CARDDO_NOT_HONORFRAUDULENTPICKUP_CARDAUTHENTICATION_FAILURELOST_CARD
للحصول على قائمة شاملة بأسباب الرفض وما إذا كان بإمكان المستخدم تصحيحها، راجع وثائق
فشل المعاملة.
إرشادات التنفيذ (من دون تعليمات برمجية)
- استخدم 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}- Cancel immediately
- Cancel at next billing date
عيّن
status للاشتراك إلى cancelled لإنهائه فورًا. يُلغى التفويض ولا يمكن إنشاء أي رسوم إضافية.cURL
Webhooks عند الإلغاء
تتبّع النتائج باستخدام webhooks
نفّذ معالجة webhooks لتتبّع رحلة العميل. راجع تنفيذ Webhooks.- subscription.active: تم تفويض التفويض وتفعيل الاشتراك
- subscription.failed: فشل الإنشاء (مثل فشل التفويض)
- subscription.on_hold: وُضع الاشتراك قيد التعليق (مثل حالة عدم الدفع)
- subscription.cancelled: أُلغي الاشتراك بالكامل (راجع الإلغاء)
- payment.succeeded: نجح تحصيل الرسوم
- payment.failed: فشل تحصيل الرسوم
الاختبار والخطوات التالية
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 وسر التوقيع.