HyoNovaالتوثيق

إضافة مزوّد دفع

كيف ينضم مزوّد دفع جديد إلى HyoNova Commerce — وحدة محوّل واحدة مع manifest وأحداث موحّدة واختبارات عقد، تُسجَّل في قائمة واحدة دون المساس بالنواة.

يتطلب إعداداًمبني، لكنه لا يعمل إلا بعد إعداد خدمة داعمة لمساحة عملك.دليللمن: المطورونآخر تحديث: أُضيفت في:
في هذه الصفحة

لا تظهر طريقة الدفع إلا بعد أن يُعدّ HyoNova مزوّد الدفع الخاص بها.

لمهندسي HyoNova. مزوّد الدفع هو وحدة واحدة في مجلدها الخاص مع مدخل واحد في قائمة المزوّدين. فقاعدة البيانات والنواة التجارية ومنطق الاشتراكات والفواتير والواجهة البرمجية والشاشات تعمل كلها من الـmanifest والقدرات، فلا يتغير أي منها.

ما لا تلمسه#

  • عقد المزوّد والسجل والمال وعميل HTTP ومساعدات التوقيع.
  • النواة التجارية: لا تتفرّع أبداً حسب اسم المزوّد.
  • مخطط قاعدة البيانات: لا أعمدة ولا جداول لمزوّد.
  • المفردات المشتركة للقدرات والطرق والحالات.
  • شاشات مركز التحكم والمنتج: تُرسم من الـmanifest.

الخطوات#

  1. أنشئ المجلد. packages/payments/providers/<key>/index.ts، حيث <key> حروف صغيرة وأرقام وشرطات.
  2. اكتب الـmanifest. المفتاح، والاسم المعروض، وexternal أو native، والبيئات، والقدرات، والطرق، والعملات، وكيف تُحصَّل التجديدات (provider أو invoice_per_cycle)، وحقول الإعدادات والأسرار مع تسميات ومساعدة بالعربية والإنجليزية، ومسار الـWebhook وترويسة التوقيع وأنواع الأحداث، ورابط التوثيق، وعنوان الـAPI الأساسي والمضيفات المسموح بها، وهل هو البائع المسجَّل.
  3. نفّذ المحوّل. المطلوب: health وcreateCheckout وverifyWebhook وnormalizeWebhook. ولكل طريقة اختيارية قدرتها: getSubscription (reconciliation)، وchangeSubscription (proration وseat_quantity)، وcancelSubscription (cancel_at_period_end)، وresumeSubscription (resume)، وcreateBillingPortal (customer_portal)، وrefund (refunds وpartial_refunds، أو refund_record مع إعادة استرداد يدوي)، وinvoiceDocument (invoice_documents)، وpaymentInstructions (manual_confirmation).
  4. وحّد الأحداث. حوّل كل حدث موثّق لدى المزوّد إلى أحداث موحّدة؛ وأضف لاحقة إلى المعرّفات حين ينتج الإشعار الواحد عدة أحداث؛ وحوّل المبالغ فقط بـمساعدات المال؛ وحوّل ما عدا ذلك إلى ignored.
  5. تحقق من التواقيع على الجسم الخام بمقارنة ثابتة الزمن؛ وارفض الترويسة أو السر الناقص.
  6. سجّل الوحدة في packages/payments/providers/index.ts. ويتحقق السجل من الـmanifest عند بدء التشغيل.
  7. أضف نسخة في مركز التحكم (أو صفاً أولياً معطّلاً)، وأعِدّها، وشغّل اختبار الاتصال.
  8. وجّه طريقة إليها، أو أعطها دور secondary، وأضف ربط الأسعار حين يحصّل المزوّد التجديدات بنفسه.
  9. اختبر. انسخ مجموعة اختبارات عقد المحوّل tests/payments-contract.test.ts للمحوّل الجديد، مع بيانات اختبار من توثيق المزوّد الرسمي. ويجب أن تنجح اختبارات التكامل التجارية دون تغيير.

قواعد الأخطاء#

ارمِ خطأ الدفع المصنّف برمز موحّد. واستخدم PAYMENT_PROVIDER_UNAVAILABLE فقط حين لم يُصَل إلى المزوّد أو لم يُنشئ شيئاً — فهذا ما يسمح بالبديل الآمن. ويجب أن تكون الرسائل آمنة للعرض: بلا حمولة ولا بيانات اعتماد ولا تتبّع مكدّس.

هيكل مبسّط#

packages/payments/providers/acmepay/index.ts
export const acmepayManifest: PaymentProviderManifest = {
  key: 'acmepay',
  displayName: 'AcmePay',
  kind: 'external',
  environments: ['test', 'live'],
  capabilities: ['checkout', 'one_time_payment', 'invoices', 'webhooks', 'refunds', 'health_check'],
  methods: ['card'],
  currencies: ['USD', 'EUR'],
  recurringCollection: 'invoice_per_cycle',
  settings: [/* non-secret fields with labels and help in English and Arabic */],
  secrets: [/* apiKey, webhookSecret: minLength, maskable, required */],
  webhook: { path: 'acmepay', signatureHeader: 'X-Acme-Signature', eventTypes: ['payment.succeeded', 'payment.failed'] },
  documentation: 'https://docs.acmepay.example/api',
  apiBaseUrl: 'https://api.acmepay.example',
  apiHosts: ['api.acmepay.example'],
  defaultEnabled: false,
  merchantOfRecord: false,
};

function create(ctx: ProviderRuntimeContext): PlatformPaymentProvider {
  return {
    manifest: acmepayManifest,
    async health() { /* one authenticated read-only call → sandbox_connected / live_connected */ },
    async createCheckout(request) { /* hosted checkout; provider metadata carries request.checkoutSessionId */ },
    async refund(request) { /* POST with Idempotency-Key = request.idempotencyKey */ },
    verifyWebhook({ rawBody, headers }) { /* HMAC over rawBody, safeEqual, else WEBHOOK_SIGNATURE_INVALID */ },
    normalizeWebhook(verified) { /* → NormalizedBillingEvent[] */ },
  };
}

export const acmepay: PaymentProviderModule = { manifest: acmepayManifest, create };

والهيكل الكامل القابل للتشغيل ومرجع حقول الـmanifest حقلاً حقلاً موجودان في الدليل الهندسي المرفق بالشيفرة.

↑↓ للتنقل · Enter للفتح · Esc للإغلاق