واجهة واحدة، أربع طرق للدفع

بطاقة مع 3DS، محفظة، دفع في المتجر، وخصم مباشر خلف خدمة داخلية واحدة — كيف يُبقي تجريدُ بوابة الدفع غرائبَ المزوّد خارج تدفق الطلبات.

وسيلة الدفع الأولى سهلة التكامل أينما وضعتها. الثالثة هي التي تعلّمك الندم. عندها تكون مفردات نداءات المزوّد وتوقيتات إعادة محاولاته وتصنيفه للأخطاء قد تشرّبت في خدمة الطلبات، وكل وسيلة جديدة تضاعف الفوضى. على منصة التجارة الإلكترونية التي عملت عليها — خدمات Go مصغّرة على Cloud Run تنسّق الشراء عبر عدة خدمات لاحقة — دعمنا أربع وسائل: بطاقة ائتمان مع 3DS، محفظة، دفع في المتجر، وخصم مباشر. ما أبقى ذلك قابلًا للإدارة قرارٌ اتُّخذ مبكرًا: مفردات المزوّد تتوقف عند حدود مجال الدفع. خدمة بوابة مخصصة ملكت الترجمة، وخدمات التجارة — الطلبات، الهوية، المجتمع — لم ترَ حمولة مزوّد قط.

شكل الحدود

تملك خدمة البوابة ترجمتين:

  • الصادر: نوايا دفع داخلية (authorize، capture، refund مع معاملات كل وسيلة) تتحول إلى استدعاءات API المزوّد.
  • الوارد: نداءات رجوع المزوّد — نجاح، فشل، معلّق، وكل حالة وسيطة خاصة به — تتحول إلى نفس الأحداث الداخلية النظيفة التي تستهلكها بقية الخدمات أصلًا.

خارج مجال الدفع، الجميع يتكلم لغة واحدة. تعرف خدمة الطلبات أن دفعة ما فُوِّضت؛ لكنها لا تعرف ما هي إعادة توجيه تحدي 3DS، ولا شكل رقم قسيمة الدفع في المتجر، ولا أن الخصم المباشر يُسوّى على تقويم مختلف عن البطاقات.

// ما تراه بقية المنصة — مفردات واحدة، أربع وسائل.
type PaymentEvent struct {
    PaymentID string
    OrderID   string
    Method    Method // CARD | WALLET | CVS | DEBIT
    Status    Status // AUTHORIZED | CAPTURED | FAILED | EXPIRED | REFUNDED
    OccurredAt time.Time
}

الوسائل الأربع تختلف حقًا

يستحق التجريد كلفته لأن الوسائل الأربع ليست تنويعات على تدفق واحد — إنها أربع آلات حالات مختلفة:

  • البطاقة + 3DS شبه متزامنة، مع إعادة توجيه متصفح في منتصفها. يمكن للمستخدم هجر التحدي، وذلك ليس نجاحًا ولا فشلًا حتى تقول المهلة كلمتها.
  • المحفظة تسلّم التحكم لتطبيق آخر وتعود عبر نداء رجوع. الكمون عادةً ثوانٍ، لكن النداء قد يصل بعدما غادر المستخدم أصلًا.
  • الدفع في المتجر الحالة القصوى: «الدفعة» وعدٌ لا أكثر. يستلم المستخدم رقم قسيمة ويدفع عند الصندوق — بعد ساعات أو أيام، أو لا يدفع أبدًا. على تدفق الطلب أن يركن في حالة معلّقة لها أجل انتهاء.
  • الخصم المباشر يُسوّى على جداول البنوك، وقد يفشل بعد نجاح كل شيء آخر.

نمذجة كل ذلك داخل خدمة الطلبات كانت ستعني أن آلة حالات الطلب تستورد أربع آلات حالات أخرى. بدلًا من ذلك تضغط البوابة كلًا منها في المفردات المشتركة: كل شيء يصير في النهاية CAPTURED أو FAILED أو EXPIRED، والبوابة وحدها تعرف كم يطول «في النهاية» لكل وسيلة.

نداءات الرجوع هي الجزء الصعب

تصل نداءات المزوّد بدلالاته هو: مكررة أحيانًا، خارج الترتيب أحيانًا، وعلى جدوله دائمًا. ثلاث قواعد منعت الضرر:

  1. نداء الرجوع مدخل غير موثوق. يُتحقق منه، يُحلَّل، ويُحوَّل فورًا إلى حدث داخلي — الحمولة الخام تتوقف عند البوابة وتُخزَّن للتدقيق، لا تُمرَّر.
  2. كل معالج نداء رجوع عديم التأثر بالتكرار، بمفتاح معرّف معاملة المزوّد. الإشعارات المكررة ثلاثاءٌ عادي، لا حادثة.
  3. الأحداث الداخلية تغادر عبر الـ Transactional Outbox. النداء الذي يحدّث حالة الدفع والحدث الذي يعلنها يثبَّتان ذرّيًا — النمط نفسه الذي يحمي تدفق الطلبات يحمي تدفق الدفع.

البديل في القاعدة 3 — النشر مباشرة من معالج النداء — هو بالضبط خطأ الكتابة المزدوجة مرتديًا زيّ الدفع، وسجلات المدفوعات آخر مكان تريد فيه انحرافًا صامتًا.

ما أقوله لمن يبني هذا

  • ضع الحدود من الوسيلة الأولى، حتى لو بدت مراسم. كانت البوابة صغيرة حين لفّت البطاقات وحدها؛ وبقيت صغيرة مفهوميًا مع وصول ثلاث وسائل أخرى، لأن كل وسيلة لم تلمس سوى مجال الدفع.
  • صمّم المفردات الداخلية حول تدفق طلباتك، لا حول API المزوّد. حقل المزوّد الذي لا يغيّر سلوك منصتك لا مكان له في الحدث الداخلي.
  • أعطِ التعليق أجلًا. علّمتنا تدفقات الدفع في المتجر أن كل حالة تتطلب فعلًا بشريًا تحتاج أجل انتهاء وكنسًا مجدولًا — «الانتظار» حالة تُدار، لا حالة تعلق فيها.
  • احتفظ بالحمولات الخام. حين يصل سؤال تسوية بعد أسابيع، تجيب عنه حمولة المزوّد المخزنة؛ حدثك المطبَّع لم يُصنع لذلك قط.
  • توقّع أن تُجادَل الحدود. تحت ضغط التسليم ستنبت لخدمة ثانية، عاجلًا أو آجلًا، عميلها الخاص للمزوّد — وكل بيانات اعتماد تُنسخ هي موضع لم يعد تجريدك يحميه. تصمد الحدود ما دامت المراجعة تفرضها، وهذا سبب لكتابتها لا لإسقاطها.

تكامل مزوّد الدفع تبعية ستعيش معها سنوات. التجريد لا يجعل المزوّد أبسط — بل يضمن أن خدمة واحدة فقط عليها أن تعرف كم هو معقّد.