نظرة عامة
تتيح لك الاشتراكات عند الطلب تفويض طريقة دفع العميل مرة واحدة، ثم تحصيل مبالغ متغيرة كلما احتجت إلى ذلك، بدلاً من اتباع جدول زمني ثابت. هذه الميزة متاحة لجميع الحسابات — ولا تتطلب موافقة. استخدم هذا الدليل من أجل:- إنشاء اشتراك عند الطلب (تفويض 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
بيانات وصفية إضافية للدفع. إذا لم تُحدَّد، فسيُستخدمَت بيانات الاشتراك الوصفية.
- Node.js SDK
- Python SDK
- Go SDK
- cURL
Success
معالجة الرسوم الفاشلة
عندما يفشل رسم مقابل اشتراك عند الطلب، فإنك تحدد الإجراء التالي. بخلاف الاشتراكات المجدولة — حيث يؤدي فشل التجديد إلى إيقاف الفوترة التلقائية اللاحقة — تبقى الاشتراكات عند الطلب قابلة لتحصيل الرسوم بعد الفشل. يمكنك استدعاء نقطة نهاية الرسم مرة أخرى كجزء من منطق retry الخاص بك.ما يحدث عند الفشل
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 من تحصيل رسم مرة أخرى.3
Retry the charge (your call)
في التدفقات عند الطلب، لا تنفذ Dodo إعادة المحاولة تلقائياً. يمكنك استدعاء
POST /subscriptions/{subscription_id}/charge مرة أخرى في أي وقت لإعادة المحاولة. طبّق سياسة retry الآمنة أدناه — استخدم 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 عمليات retry الخاصة بها للتجديد والتحصيل المتعثر. أما في الاشتراكات عند الطلب، فأنت المسؤول عن سياسة retry لأنك وحدك تعرف متى يجب أن يحدث الرسم التالي (إذ تحدده أحداث الاستخدام لديك، وليس التقويم).
تسلسل webhook عند فشل رسم عند الطلب
لا يتم تشغيل الحدثين 3 و4 إلا بعد نجاح رسم لاحق.
مسؤولية retry
إن تحصيل الاشتراكات المتعثر — وهو تسلسل recovery المدمج عبر البريد الإلكتروني — مخصص لدفعات التجديد الفاشلة في الاشتراكات المجدولة، ولعمليات الإلغاء التي يبدأها العميل. ولا صُمم لمعالجة حالات فشل الرسوم عند الطلب. تواصل مباشرةً مع العميل (مثل البريد الإلكتروني للمعاملات أو مطالبة داخل التطبيق) عندما تقرر ضرورة تحديث طريقة الدفع.إعادة محاولة الدفع
قد يحظر نظام اكتشاف الاحتيال لدينا أنماط retry العدوانية (وقد يضع عليها علامة باعتبارها اختبارات محتملة للبطاقات). اتبع سياسة retry آمنة.مبادئ سياسات retry الآمنة
- آلية Backoff: استخدم exponential backoff بين عمليات retry.
- حدود retry: حدّد إجمالي عمليات retry (3–4 محاولات كحد أقصى).
- التصفية الذكية: أعد المحاولة فقط عند حالات الفشل القابلة لإعادة المحاولة (مثل أخطاء الشبكة أو الجهة المُصدرة أو عدم كفاية الرصيد)؛ ولا تعِد المحاولة مطلقاً عند الرفض النهائي.
- منع اختبار البطاقات: لا تعِد محاولة حالات الفشل مثل
DO_NOT_HONORوSTOLEN_CARDوLOST_CARDوPICKUP_CARDوFRAUDULENTوAUTHENTICATION_FAILURE. - تنويع البيانات الوصفية (اختياري): إذا كنت تدير نظام retry الخاص بك، فميّز عمليات retry عبر البيانات الوصفية (مثل
retry_attempt).
جدول retry المقترح (الاشتراكات)
- المحاولة الأولى: فور إنشاء الرسم
- المحاولة الثانية: بعد 3 أيام
- المحاولة الثالثة: بعد 7 أيام إضافية (10 أيام إجمالاً)
- المحاولة الرابعة (النهائية): بعد 7 أيام أخرى (17 يوماً إجمالاً)
تجنب عمليات retry المتكررة بكثافة؛ ووازِنها مع وقت التفويض
- اربط عمليات retry بالطابع الزمني للتفويض الأصلي لتجنب السلوك «المتكرر بكثافة» عبر محفظتك.
- مثال: إذا بدأ العميل فترة تجريبية أو mandate اليوم الساعة 1:10 مساءً، فجدول عمليات retry اللاحقة عند الساعة 1:10 مساءً في الأيام التالية وفقاً لـ backoff (مثل +3 أيام ← 1:10 مساءً، +7 أيام ← 1:10 مساءً).
- بدلاً من ذلك، إذا خزّنت وقت آخر دفعة ناجحة
T، فجدول المحاولة التالية عندT + X daysللحفاظ على مواءمة وقت اليوم.
المنطقة الزمنية والتوقيت الصيفي (DST): استخدم معياراً زمنياً متسقاً للجدولة، وحوّله للعرض فقط للحفاظ على الفواصل الزمنية.
رموز الرفض التي يجب ألا تعيد المحاولة عند ظهورها
STOLEN_CARDDO_NOT_HONORFRAUDULENTPICKUP_CARDAUTHENTICATION_FAILURELOST_CARD
للحصول على قائمة شاملة بأسباب الرفض وما إذا كان بإمكان المستخدم تصحيحها، راجع
وثائق فشل المعاملات.
إرشادات التنفيذ (من دون code)
- استخدم scheduler أو queue يحفظ الطوابع الزمنية الدقيقة؛ واحسب المحاولة التالية عند الإزاحة الزمنية نفسها تماماً (مثل
T + 3 daysفي التوقيت نفسه HH:MM). - احتفظ بالطابع الزمني لآخر دفعة ناجحة
Tوارجع إليه لحساب المحاولة التالية؛ ولا تجمع عدة اشتراكات في اللحظة نفسها. - قيّم دائماً سبب الرفض الأخير؛ وأوقف retry عند حالات الرفض النهائي الواردة في قائمة التجاوز أعلاه.
- حدّد عمليات retry المتزامنة لكل عميل ولكل حساب لمنع الارتفاعات المفاجئة غير المقصودة.
- تواصل بشكل استباقي: أرسل بريداً إلكترونياً أو رسالة SMS إلى العميل لتحديث طريقة الدفع قبل المحاولة المجدولة التالية.
- استخدم البيانات الوصفية لأغراض المراقبة فقط (مثل
retry_attempt)؛ ولا تحاول مطلقاً «التحايل» على أنظمة الاحتيال والمخاطر عبر تدوير حقول غير مؤثرة.
الإلغاء
تتبع الاشتراكات عند الطلب تدفق إلغاء مختلفاً عن الاشتراكات المجدولة، إذ لا توجد دورة فوترة ثابتة يمكن الاستناد إليها لتحديد تاريخ انتهاء فوري.سلوك Customer Portal
عندما يلغي العميل اشتراكاً عند الطلب من Customer Portal، تتم جدولة الإلغاء لتاريخ الفوترة التالي بشكل افتراضي. ولا يظهر خيار الإلغاء الآن عمداً للاشتراكات عند الطلب. السبب هو أن الاشتراكات عند الطلب لا تتضمن تواريخ تجديد متكررة يمكن التنبؤ بها — إذ يحدد وقت الرسم التالي بالكامل أحداث الاستخدام لديك. وتُبقي جدولة الإلغاء في تاريخ الفوترة التالي mandate نشطاً حتى نهاية الفترة، بحيث يمكن تحصيل رسوم أي استخدام جارٍ، ثم ينتهي الاشتراك بشكل سليم. بعد تأكيد العميل للإلغاء:- يبقى الاشتراك في
activeوقابلاً لتحصيل الرسوم عبرPOST /subscriptions/{id}/chargeحتى تاريخ الإلغاء المجدول. - يتم ضبط
cancel_at_next_billing_dateعلىtrueفي الاشتراك. - يصدر webhook من نوع
subscription.cancelledعند سريان الإلغاء.
إذا كنت بحاجة إلى إنهاء الاشتراك فوراً (على سبيل المثال، استجابةً لعملية refund أو طلب دعم)، فألغِه برمجياً عبر API بدلاً من الاعتماد على تدفق Customer Portal.
الإلغاء برمجياً
يمكنك إلغاء اشتراك عند الطلب عبر API في أي وقت. وتتحكم في ما إذا كان الإلغاء فورياً أو مجدولاً. نقطة النهاية: PATCH /subscriptions/{subscription_id}- Cancel immediately
- Cancel at next billing date
اضبط
status للاشتراك على cancelled لإنهائه فوراً. يتم إلغاء mandate ولا يمكن إنشاء رسوم إضافية.cURL
Webhooks عند الإلغاء
تتبّع النتائج باستخدام webhooks
نفّذ معالجة webhooks لتتبّع رحلة العميل. راجع تنفيذ Webhooks.- subscription.active: تم تفويض mandate وتفعيل الاشتراك
- subscription.failed: فشل الإنشاء (مثل فشل mandate)
- subscription.on_hold: وُضع الاشتراك قيد التعليق (مثل حالة عدم الدفع)
- subscription.cancelled: أُلغي الاشتراك بالكامل (راجع الإلغاء)
- payment.succeeded: نجح تحصيل الرسم
- payment.failed: فشل تحصيل الرسم
الاختبار والخطوات التالية
1
Create in test mode
استخدم مفتاح API الاختباري لإنشاء الاشتراك، ثم افتح
checkout_url الذي تم إرجاعه وأكمل mandate.2
Trigger a charge
استدعِ نقطة نهاية الرسم مع
product_price صغير (مثل 100)، وتحقق من استلام payment.succeeded.3
Go live
انتقل إلى مفتاح API المباشر بعد التحقق من الأحداث وتحديثات الحالة الداخلية.
استكشاف الأخطاء وإصلاحها
- 422 Invalid Request: تأكد من توفير
on_demand.mandate_onlyعند الإنشاء، وتوفيرproduct_priceللرسوم. - أخطاء العملة: إذا تجاوزت
product_currency، فتأكد من أنها مدعومة لحسابك وللعميل. - لم يتم استلام webhooks: تحقّق من إعداد عنوان URL الخاص بـ webhook وسر التوقيع.