دليل واجهة برمجة تطبيقات نظام الدفع OmniCRM
نظرة عامة
يوفر نظام الدفع OmniCRM بنية تحتية شاملة لمعالجة المدفوعات، غير مرتبطة بالبائع. يدعم حاليًا Stripe وPayPal، لكن الهيكلية المودولارية تسمح بالدمج مع أي مزود دفع (Square، Adyen، Braintree، إلخ) دون تغيير كود التطبيق.
يغطي هذا المستند جميع واجهات برمجة التطبيقات الخاصة بالدفع وسير العمل المتاحة في النظام.
🔧 دمج Ansible Playbook: لتنفيذ هذه واجهات برمجة التطبيقات للدفع في playbooks الخاصة بالت provisioning، راجع التحصيل والمدفوعات من Playbooks
جدول المحتويات
- الهيكلية المودولارية
- المفاهيم الأساسية
- المستندات المالية
- واجهات برمجة التطبيقات لطرق الدفع
- واجهات برمجة التطبيقات لتدفقات الدفع
- واجهات برمجة التطبيقات للمحفظة
- ملخص مرجع واجهة برمجة التطبيقات
- حالات الاستخدام الشائعة
الهيكلية المودولارية
لماذا غير مرتبطة بالبائع؟
يستخدم النظام طبقات التجريد لفصل منطق الأعمال عن تفاصيل مزود الدفع. وهذا يعني:
- ✅ إضافة مزودي الدفع الجدد دون لمس كود التطبيق
- ✅ تبديل البائعين دون الحاجة إلى ترحيل قواعد البيانات
- ✅ دعم عدة بائعين في نفس الوقت
- ✅ واجهة برمجة تطبيقات متسقة بغض النظر عن مزود الخلفية
طبقات الهيكلية
إضافة مزود دفع جديد
لإضافة مزود جديد (مثل Square، Adyen، Braintree)، اتصل بفريق OmniCRM الفني الخاص بك.
للحصول على تفاصيل التكوين حول البائعين الحاليين (Stripe، PayPal)، راجع تكوين البائع.
نظرة عامة على العملية:
الخطوات:
- تنفيذ فئة المعالج مع
PaymentVendorInterfaceالقياسية (تفويض، استحواذ، شحن، استرداد، تحرير) - تسجيل المعالج في VendorFactory مع اسم المزود
- اختبار التكامل مع بيئة صندوق الرمل الخاصة بالمزود
- نشر - جميع واجهات برمجة التطبيقات الحالية تدعم تلقائيًا المزود الجديد
النتيجة: بمجرد النشر، يعمل المزود الجديد بسلاسة:
# إضافة طريقة الدفع Square
curl -X POST /api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "square",
"payment_token": "sq_xxxxx"
}'
تدعم جميع واجهات برمجة التطبيقات للدفع المزود الجديد تلقائيًا دون الحاجة إلى تغييرات في كود التطبيق.
وقت التكامل: عادةً ما يستغرق من 1-2 يومًا لمعالج مزود جدي��.
تكوين البائع
تمكين بائعي الدفع
تتحكم واجهة المستخدم في بائعي الدفع المتاحين للعملاء. يتم تعيين ذلك عبر المتغير VITE_PAYMENT_VENDORS في ملف .env الخاص بواجهة المستخدم:
# تمكين Stripe فقط (افتراضي)
VITE_PAYMENT_VENDORS="stripe"
# تمكين كل من Stripe وPayPal
VITE_PAYMENT_VENDORS="stripe,paypal"
# تمكين PayPal فقط
VITE_PAYMENT_VENDORS="paypal"
مهم: VITE_PAYMENT_VENDORS هو متغير وقت البناء. يتطلب تغييره إعادة بناء وإعادة تشغيل واجهة المستخدم.
تكوين Stripe
Stripe هو مزود الدفع الأساسي، حيث يوفر معالجة بطاقات مع دعم 3D Secure.
1. الحصول على مفاتيح API الخاصة بـ Stripe
- سجل في https://stripe.com
- انتقل إلى المطورين → مفاتيح API
- انسخ المفتاح القابل للنشر الخاص بك (pk_live_... أو pk_test_...)
- انسخ المفتاح السري الخاص بك (sk_live_... أو sk_test_...)
مهم:
- استخدم مفاتيح الاختبار (pk_test_/sk_test_) للتطوير
- استخدم المفاتيح الحية (pk_live_/sk_live_) للإنتاج فقط
- لا تقم أبدًا بالتزام مفاتيح API في التحكم في الإصدارات
2. تكوين الخلفية
أضف إلى crm_config.yaml (مستوى الجذر، وليس متداخلًا تحت payment_vendors):
stripe:
secret_key: 'sk_live_YOUR_SECRET_KEY_HERE'
publishable_key: 'pk_live_YOUR_PUBLISHABLE_KEY_HERE'
currency: 'aud'
statement_descriptor_suffix: 'YOUR_COMPANY'
تقرأ واجهة برمجة التطبيقات بيانات اعتماد Stripe من crm_config.yaml عند بدء التشغيل. يتطلب تغيير هذه القيم إعادة تشغيل واجهة برمجة التطبيقات.
3. تكوين الواجهة الأمامية
أضف إلى ملف .env الخاص بواجهة المستخدم:
VITE_STRIPE_PUBLISHABLE_KEY=pk_live_YOUR_PUBLISHABLE_KEY_HERE
ملاحظة أمان: يجب أن يكون المفتاح القابل للنشر فقط في الواجهة الأمامية. يجب ألا يتم الكشف عن المفتاح السري في المتصفح.
تكوين PayPal
يوفر PayPal تخزين بطاقات المدفوعات ومدفوعات حساب PayPal.
1. الحصول على بيانات اعتماد API الخاصة بـ PayPal
- أنشئ ��طبيق REST API في https://developer.paypal.com/dashboard/
- انسخ معرف العميل والمفتاح السري
- للاختبار، استخدم بيانات الاعتماد Sandbox؛ للإنتاج، استخدم بيانات الاعتماد الحية
2. تكوين الخلفية
أضف إلى crm_config.yaml (مستوى الجذر):
paypal:
client_id: 'YOUR_PAYPAL_CLIENT_ID'
client_secret: 'YOUR_PAYPAL_CLIENT_SECRET'
mode: 'sandbox' # 'sandbox' للاختبار، 'live' للإنتاج
تقرأ واجهة برمجة التطبيقات بيانات اعتماد PayPal من crm_config.yaml عند بدء التشغيل. يتطلب تغيير هذه القيم إعادة تشغيل واجهة برمجة التطبيقات.
3. تكوين الواجهة الأمامية
أضف إلى ملف .env الخاص بواجهة المستخدم:
VITE_PAYMENT_VENDORS="stripe,paypal"
VITE_PAYPAL_CLIENT_ID=YOUR_PAYPAL_CLIENT_ID
يجب أن يتطابق VITE_PAYPAL_CLIENT_ID مع قيمة paypal.client_id في crm_config.yaml.
Sandbox مقابل Live
| Sandbox | Live | |
|---|---|---|
| Stripe API | sk_test_ / pk_test_ | sk_live_ / pk_live_ |
| PayPal API | api-m.sandbox.paypal.com | api-m.paypal.com |
| وضع PayPal | sandbox | live |
| المال | وهمي | حقيقي |
عند الانتقال من sandbox إلى live، قم بتحديث كلا crm_config.yaml (بيانات الاعتماد والوضع) وملف .env (المفتاح القابل للنشر / معرف العميل)، ثم أعد بناء واجهة المستخدم وأعد تشغيل واجهة برمجة التطبيقات.
الامتثال لمعايير PCI
كيف يحافظ OmniCRM على الامتثال لمعايير PCI:
- يتم إدخال بيانات البطاقة مباشرة في iframes المستضافة بواسطة البائع (Stripe Elements، حقول بطاقة PayPal)
- OmniCRM لا يرى أو يخزن أرقام البطاقة الكاملة
- يتم تخزين طرق الدفع المرمزة فقط في قاعدة البيانات
- نطاق الامتثال لمعايير PCI مخفض لعملك
مقاييس معالجة الدفع
يوفر OmniCRM مقاييس شاملة لمراقبة عمليات معالجة الدفع. للحصول على تفاصيل كاملة حول مقاييس دفع Stripe بما في ذلك تتبع استدعاءات API، وأحجام الدفع، ومعدلات الفشل، وأوقات الاستجابة، راجع المراقبة والمقاييس.
مخطط قاعدة البيانات غير مرتبط بالبائع
يدعم مخطط قاعدة البيانات أي مزود دفع دون الحاجة إلى ترحيلات:
مثال - حفظ بطاقة Square:
{
"vendor": "square",
"vendor_payment_method_id": "sq_card_abc123",
"payment_type": "card"
}
لا تغييرات في الكود. لا ترحيلات. فقط يعمل.
المفاهيم الأساسية
نماذج البيانات
PaymentMethod
تخزين غير مرتبط بالبائع لطرق دفع العملاء.
{
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe", // 'stripe'، 'paypal'، أو أي بائع مضاف
"vendor_payment_method_id": "pm_xxx", // معرف داخلي للبائع
"payment_type": "card", // 'card'، 'paypal'، 'ach'، إلخ.
"is_default": true,
"card_brand": "visa",
"card_last4": "4242",
"card_exp_month": 12,
"card_exp_year": 2025,
"card_nickname": "My Visa Card",
"status": "active"
}
PaymentAuthorization
سجلات تفويض بتسليم مزدوج (احتجاز الأموال).
{
"authorization_id": 301,
"customer_id": 123,
"payment_method_id": 789,
"vendor": "stripe", // أي بائع قام بالتفويض
"vendor_authorization_id": "auth_xxx", // معرف تفويض البائع
"amount": 200.00,
"currency": "USD",
"status": "authorized", // 'authorized'، 'captured'، 'released'
"authorized_at": "2025-12-27T10:00:00Z",
"expires_at": "2026-01-03T10:00:00Z",
"meta": {}
}
PaymentCapture
المدفوعات الملتقطة/المكتملة.
{
"capture_id": 103,
"authorization_id": 301,
"customer_id": 123,
"payment_method_id": 789,
"vendor": "stripe",
"vendor_transaction_id": "ch_xxx", // معرف المعاملة الخاصة بالبائع
"amount": 200.00,
"currency": "USD",
"status": "succeeded", // 'succeeded'، 'failed'، 'refunded'
"captured_at": "2025-12-27T10:30:00Z",
"vendor_response": {}, // استجابة كاملة من البائع
"meta": {}
}
WalletAccount
محفظة العميل مع تتبع الرصيد (1 إلى 1 مع العميل).
{
"wallet_account_id": 456,
"customer_id": 123,
"balance": 150.50,
"currency": "USD",
"auto_recharge_enabled": true,
"auto_recharge_amount": 100.00,
"auto_recharge_threshold": 10.00,
"low_balance_warning_threshold": 10.00
}
WalletLedger
سجل تدقيق كامل لجميع معاملات المحفظة.
{
"ledger_id": 501,
"customer_id": 123,
"wallet_account_id": 456,
"transaction_type": "credit", // 'credit'، 'debit'، 'refund'، 'adjustment'
"amount": 100.00,
"balance_before": 150.50,
"balance_after": 250.50,
"currency": "USD",
"description": "Card top-up",
"reference_type": "payment_capture", // روابط إلى الكائن ذي الصلة
"reference_id": 103,
"meta": {},
"created_at": "2025-12-27T10:35:00Z"
}
المستندات المالية
لإنشاء قوالب الفواتير والتخصيص، راجع فواتير العملاء.
الفواتير
التعريف: الفاتورة هي مستند يحتوي على قائمة من معاملات الخصم (الرسوم). عند استدعاء واجهة برمجة التطبيقات لإنشاء فاتورة، يجب تقديم مصفوفة من معرفات معاملات الخصم. يتم "ربط" تلك المعاملات بالفاتورة عن طريق تعيين حقل invoice_id الخاص بها.
للحصول على تفاصيل إدارة المعاملات، راجع المعاملات.
الحقول الرئيسية:
{
"invoice_id": 12345,
"invoice_number": "INV-2025-000001", // تم إنشاؤه تلقائيًا: INV-YYYY-NNNNNN
"customer_id": 123,
"title": "فاتورة خدمات شهرية",
"paid": true, // حالة الدفع
"void": false, // حالة الإلغاء
"payment_reference": "ch_xxxxx", // آخر/معرف الدفع الأساسي
"payment_type": "stripe_capture", // آخر نوع دفع
"payment_time": "2025-12-27T10:30:00",
"start_date": "2025-12-01",
"end_date": "2025-12-31",
"due_date": "2026-01-15",
"retail_cost": 500.00, // إجمالي مبلغ الفاتورة
"wholesale_cost": 250.00
}
إنشاء الفاتورة: يتم إنشاء أرقام الفواتير تلقائيًا بتنسيق INV-YYYY-NNNNNN، بالتسلسل ضمن السنة التقويمية، وإعادة تعيينها في 1 يناير من كل عام.
المدفوعات على الفواتير
كيف تعمل المدفوعات: بمجرد إنشاء فاتورة من معاملات الخصم، يمكن تطبيق الدفع عن طريق إنشاء معاملة ائتمانية (تكلفة تجزئة سالبة) مرتبطة بالفاتورة.
يجب أن:
- يتم إدراجها في الفاتورة مع وضع علامة واضحة كـ "مدفوعات"
- تظهر تاريخ الدفع ذي الصلة (والذي يمكن أن يختلف عن تاريخ إنشاء الفاتورة)
- تدعم عدة مدفوعات لكل فاتورة
- يتم صافيها ضد الخصومات لحساب رصيد الفاتورة
حالة الفاتورة:
- مدفوعة: إجمالي المدفوعات (الائتمانات) يساوي أو يتجاوز إجمالي الخصومات
- مدفوعة جزئيًا: تم تطبيق بعض المدفوعات ولكن لا يزال هناك رصيد
- مدفوعة بشكل زائد: لا يتم التعامل معها حاليًا - تتطلب تقسيم الائتمان (ميزة مستقبلية)
المحاسبة ذات القيد المزدوج: ينفذ النظام محاسبة صحيحة حيث يكون لكل رسوم دفع مقابل:
// 1. معاملة خصم (رسوم)
{
"transaction_id": 7001,
"invoice_id": 12345,
"retail_cost": 100.00, // إيجابي = العميل مدين
"title": "رسوم الخدمة"
}
// 2. معاملة ائتمانية (دفع)
{
"transaction_id": 7002,
"invoice_id": 12345,
"retail_cost": -100.00, // سالب = تم استلام الدفع
"title": "دفع للفاتورة: رسوم الخدمة (12345)",
"payment_type": "stripe_capture",
"payment_reference": "ch_xxxxx"
}
// النتيجة الصافية: رصيد 0$ → الفاتورة مميزة كمدفوعة
بيانات التعريف الإضافية للفاتورة: بالإضافة إلى المعاملات الائتمانية، ت��زن الفاتورة أيضًا حقول ملخص (payment_reference، payment_type، payment_time) للبحث السريع. ومع ذلك:
- الطريقة الأساسية: المعاملات الائتمانية مرتبطة عبر
invoice_id(مواصفات هايدن) - بيانات التعريف الثانوية: تخزن حقول الفاتورة ملخصًا لآخر/أساسي الدفع
- لعدة مدفوعات: استعلام عن المعاملات الائتمانية للحصول على التاريخ الكامل
البيانات المالية
التعريف: تظهر البيانات المالية جميع الخصومات والائتمانات من معاملات العميل على مدى فترة محددة. هذا هو نوع المستند الوحيد حيث تظهر كل من الخصومات والائتمانات كعناصر سطر معًا، مثل بيان البنك.
ملاحظات الائتمان
الغرض: يتم تطبيقها فقط على الفواتير التي تم تطبيق المدفوعات عليها بالفعل. يجب أن يتم ذلك كجزء من عملية إلغاء الفاتورة.
العملية:
- يتم إلغاء فاتورة (مع معاملاتها الخصمية المرتبطة بها)
- يتم ربط أي مدفوعات تم تطبيقها بالفعل بملاحظة الائتمان التي تم إنشاؤها
- يدخل رصيد العميل في الائتمان بالمبلغ المعادل
- يمكن بعد ذلك أن يكون رصيد ملاحظة الائتمان:
- مطبق على فاتورة أخرى كدفع، أو
- مسترد إلى العميل
منطق الاسترداد: إذا تم اختيار الاسترداد، يتم استدعاؤه بناءً على معاملات الائتمان المرتبطة وكيف تم دفعها في الأصل (مثل استرداد Stripe إذا كان نوع معاملة الائتمان هو Stripe).
واجهات برمجة التطبيقات لطرق الدفع
تستخدم جميع نقاط النهاية عنوان URL الأساسي: /api/payments/
لإدارة طرق الدفع بالتفصيل ومعالجة البطاقات، راجع طرق الدفع.
إضافة طريقة دفع
احفظ طريقة دفع جديدة لعميل.
نقطة النهاية: POST /api/payments/methods
الطلب:
curl -X POST https://your-domain.com/api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"is_default": false,
"card_nickname": "My Visa Card"
}'
جسم الطلب:
{
"customer_id": 123,
"vendor": "stripe", // 'stripe'، 'paypal'، أو أي بائع مضاف
"payment_token": "pm_xxxxx", // رمز لمرة واحدة من واجهة برمجة التطبيقات الأمامية
"is_default": false, // تعيين كطريقة الدفع الافتراضية؟
"card_nickname": "My Visa Card" // اسم ودي اختياري
}
الاستجابة (201 تم الإنشاء):
{
"success": true,
"message": "تمت إضافة طريقة الدفع بنجاح",
"data": {
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"card_exp_month": 12,
"card_exp_year": 2025,
"card_nickname": "My Visa Card",
"is_default": false,
"status": "active"
}
}
الحصول على طرق الدفع
استرجع جميع طرق الدفع لعميل.
نقطة النهاية: GET /api/payments/methods?customer_id={id}
الطلب:
curl -X GET "https://your-domain.com/api/payments/methods?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"success": true,
"data": [
{
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"is_default": true
},
{
"payment_method_id": 790,
"customer_id": 123,
"vendor": "paypal",
"payment_type": "paypal",
"paypal_email": "user@example.com",
"is_default": false
}
]
}
الحصول على طريقة الدفع الافتراضية
احصل على طريقة الدفع الافتراضية لعميل.
نقطة النهاية: GET /api/payments/methods/default?customer_id={id}
الطلب:
curl -X GET "https://your-domain.com/api/payments/methods/default?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"success": true,
"data": {
"payment_method_id": 789,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"is_default": true
}
}
تعيين طريقة الدفع الافتراضية
قم بتعيين طريقة دفع كافتراضية.
نقطة النهاية: PUT /api/payments/methods/set-default
الطلب:
curl -X PUT https://your-domain.com/api/payments/methods/set-default \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"payment_method_id": 790
}'
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم تحديث طريقة الدفع الافتراضية",
"data": {
"payment_method_id": 790,
"is_default": true
}
}
حذف طريقة الدفع
احذف طريقة دفع محفوظة.
نقطة النهاية: DELETE /api/payments/methods/{payment_method_id}?customer_id={id}
الطلب:
curl -X DELETE "https://your-domain.com/api/payments/methods/789?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم حذف طريقة الدفع بنجاح"
}
واجهات برمجة التطبيقات لتدفقات الدفع
يدعم النظام عدة تدفقات دفع اعتمادًا على حالة الاستخدام الخاصة بك.
1. الدفع المباشر (شحن بسيط)
حالة الاستخدام: دفع بسيط من خطوة واحدة دون توفير الخدمة.
نقطة النهاية: POST /api/payments/charge
الطلب:
curl -X POST https://your-domain.com/api/payments/charge \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 50.00,
"currency": "USD",
"payment_method_id": 789,
"metadata": {
"order_id": "12345"
}
}'
جسم الطلب:
{
"customer_id": 123,
"amount": 50.00,
"currency": "USD", // الافتراضي: USD
"payment_method_id": 789, // اختياري - يستخدم الافتراضي إذا تم تجاهله
"vendor": "stripe", // مطلوب إذا كنت تستخدم payment_token
"payment_token": "pm_xxxxx", // اختياري - رمز لمرة واحدة
"save_method": false, // حفظ طريقة الدفع للاستخدام في المستقبل؟
"metadata": {
"order_id": "12345"
}
}
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم الدفع بنجاح",
"data": {
"transaction_id": "ch_xxxxx",
"capture_id": 101,
"amount": 50.00,
"currency": "USD",
"status": "succeeded"
}
}
2. دفع الفاتورة (توجيه أول للمحفظة)
حالة الاستخدام: دفع فاتورة باستخدام رصيد المحفظة أولاً، ثم بطاقة للدفع المتبقي.
نقطة النهاية: POST /api/payments/invoice
التدفق:
- تحقق من رصيد المحفظة
- استخدم أموال المحفظة أولاً
- شحن البطاقة للقصور (إن وجد)
- ائتمان المحفظة بمبلغ البطاقة
- خصم المحفظة للمبلغ الكامل للفاتورة
الطلب:
curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 200.00,
"payment_method_id": 789,
"metadata": {
"invoice_id": 12345,
"description": "دفع الفاتورة"
}
}'
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم معالجة دفع الفاتورة",
"data": {
"customer_id": 123,
"service_amount": 200.00,
"routing_mode": "hybrid",
"initial_balance": 150.00,
"wallet_portion_used": 150.00, // غطت المحفظة هذا المبلغ
"card_portion_used": 50.00, // تم شحن البطاقة للمبلغ المتبقي
"charged_amount": 50.00,
"wallet_credited": 50.00,
"wallet_debited": 200.00,
"final_balance": 0.00,
"payment_method_used": true
}
}
3. احتجاز التفويض (احتياطي الأموال)
حالة الاستخدام: احتجاز الأموال للاستخدام لاحقًا (مثل حجوزات الفنادق، الإيجارات).
🔧 تنفيذ Playbook: للحصول على أمثلة playbook الخاصة بتدفق الدفع ذو المرحلتين، راجع نمط الالتزام ذو المرحلتين
الخطوة 1: إنشاء احتجاز التفويض
نقطة النهاية: POST /api/payments/authorize/hold
تدفق أول للمحفظة:
- تحقق من رصيد المحفظة
- احسب القصور (المبلغ - wallet_balance)
- تفويض البطاقة للقصور فقط
- خصم المحفظة يحدث عند وقت الالتقاط، وليس هنا
الطلب:
curl -X POST https://your-domain.com/api/payments/authorize/hold \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 200.00,
"payment_method_id": 789,
"use_wallet": true,
"metadata": {
"reservation_id": "RES-001"
}
}'
جسم الطلب:
{
"customer_id": 123,
"amount": 200.00,
"currency": "USD",
"payment_method_id": 789, // اختياري - يستخدم الافتراضي إذا تم تجاهله
"vendor": "stripe", // مطلوب إذا كنت تستخدم payment_token
"payment_token": "pm_xxxxx", // اختياري - رمز لمرة واحدة
"save_method": false,
"use_wallet": true, // تمكين توجيه أول للمحفظة (الافتراضي: true)
"metadata": {
"reservation_id": "RES-001"
}
}
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم تفويض الدفع (تم إنشاء الاحتجاز)",
"data": {
"authorization_id": 301,
"vendor_authorization_id": "auth_xxxxx",
"amount": 200.00,
"currency": "USD",
"status": "authorized",
"wallet_balance": 150.00,
"wallet_to_use": 150.00, // ستغطي المحفظة هذا المبلغ
"card_amount": 50.00, // تم تفويض البطاقة لهذا المبلغ
"message": "تم تفويض البطاقة بمبلغ 50$ (تعبئة المحفظة). سيتم خصم المحفظة بمبلغ 200$ عند الالتقاط."
}
}
الخطوة 2: التقاط التفويض
نقطة النهاية: POST /api/payments/capture/{authorization_id}
تدفق الالتقاط الأول للمحفظة:
- التقاط البطاقة (إذا تم تفويضها) - تعبئة المحفظة
- ائتمان المحفظة بمبلغ البطاقة الملتقطة
- خصم المحفظة للمبلغ الكامل للخدمة
- إنشاء الفواتير/المعاملات (إذا تم الطلب)
الطلب:
curl -X POST https://your-domain.com/api/payments/capture/301 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"amount": 200.00,
"metadata": {
"invoice": true,
"title": "حجز الفندق",
"description": "إقامة لمدة 3 ليال",
"wholesale_cost": 100.00,
"contract_days": 3,
"send_email": true
}
}'
جسم الطلب:
{
"amount": 200.00, // اختياري - يتم التقاط المبلغ الكامل إذا تم تجاهله
"metadata": {
"invoice": true, // إنشاء فاتورة ومعاملات؟
"create_transaction": true, // إنشاء سجل المعاملة؟
"title": "حجز الفندق",
"description": "إقامة لمدة 3 ليال",
"wholesale_cost": 100.00,
"contract_days": 3,
"send_email": true // إرسال بريد إلكتروني للفاتورة؟
}
}
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم التقاط التفويض",
"data": {
"capture_id": 103,
"transaction_id": "ch_xxxxx",
"authorization_id": 301,
"amount": 200.00,
"currency": "USD",
"status": "succeeded",
"wallet_credit": { // تم تعبئة المحفظة بالبطاقة
"ledger_id": 401,
"amount": 50.00
},
"wallet_debit": { // تم شحن الخدمة من المحفظة
"ledger_id": 402,
"amount": 200.00
},
"transaction": { // تم إنشاؤه إذا كانت الفاتورة=true
"transaction_id": 7001
},
"invoice": { // تم إنشاؤه إذا كانت الفاتورة=true
"invoice_id": 12345,
"invoice_number": "INV-2025-000001"
}
}
}
الخطوة 3: تحرير التفويض (إلغاء)
نقطة النهاية: POST /api/payments/release/{authorization_id}
حالة الاستخدام: إلغاء الحجز، فشل التوفير، أو تغيير رأي العميل.
الطلب:
curl -X POST https://your-domain.com/api/payments/release/301 \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تم تحرير التفويض",
"data": {
"authorization_id": 301,
"vendor_authorization_id": "auth_xxxxx",
"status": "released",
"released_at": "2025-12-27T10:45:00Z"
}
}
ملاحظة: مع تدفق أول للمحفظة، لا حاجة لاسترداد المحفظة حيث لا يتم خصم المحفظة حتى وقت الالتقاط.
4. دفع التعبئة (ذو مرحلتين مع التوفير)
حالة الاستخدام: معالجة الدفع لتعبئة ا��خدمة التي تتطلب التوفير (مثل تنشيط نقطة اتصال/دونغل). إذا فشل التوفير، يتم تحرير التفويض.
نقطة النهاية: POST /api/payments/topup
التدفق: تفويض → توفير الخدمة → التقاط
الطلب:
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 30.00,
"payment_method_id": 789,
"service_uuid": "svc-uuid-123",
"imsi": "123456789012345",
"days": 30,
"metadata": {
"is_rental": false
}
}'
لإيجار/نقطة اتصال مجهولة (طريقة الدفع غير محفوظة):
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 1,
"amount": 5.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_uuid": "hotspot-uuid",
"imsi": "123456789012345",
"days": 1,
"metadata": {
"is_rental": true,
"billing_email": "user@example.com"
}
}'
الاستجابة (200 حسن��ا):
{
"success": true,
"message": "تمت معالجة دفع التعبئة بنجاح",
"data": {
"transaction_id": "ch_xxxxx",
"authorization_id": 302,
"capture_id": 104,
"amount": 30.00,
"status": "succeeded",
"provision_result": {
"success": true,
"topup_result": {...},
"service_uuid": "svc-uuid-123",
"imsi": "123456789012345",
"days": 30
},
"payment_method_saved": false // خطأ للإيجار/المجهول
}
}
5. دفع الإيجار (طرف ثالث)
حالة الاستخدام: شحن بطاقة عميل واحد لدفع خدمة عميل آخر.
نقطة النهاية: POST /api/payments/rental
الطلب:
curl -X POST https://your-domain.com/api/payments/rental \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"beneficiary_customer_id": 456,
"charge_customer_id": 123,
"amount": 75.00,
"payment_method_id": 789,
"service_description": "دفع خدمة الإيجار",
"metadata": {
"rental_agreement_id": "RA-001"
}
}'
لإيجار مجهول (بطاقة المستأجر غير محفوظة):
curl -X POST https://your-domain.com/api/payments/rental \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"beneficiary_customer_id": 456,
"amount": 75.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_description": "دفع إيجار مجهول",
"metadata": {
"billing_email": "renter@example.com"
}
}'
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تمت معالجة دفع الإيجار",
"data": {
"transaction_id": "ch_xxxxx",
"amount": 75.00,
"payment_method_saved": false // مجهول: لا توجد طريقة محفوظة
}
}
6. استرداد الدفع
نقطة النهاية: POST /api/payments/refund
الطلب:
curl -X POST https://your-domain.com/api/payments/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"transaction_id": "ch_xxxxx",
"vendor": "stripe",
"amount": 50.00,
"reason": "customer_request"
}'
جسم الطلب:
{
"transaction_id": "ch_xxxxx", // معرف المعاملة الخاصة بالبائع
"vendor": "stripe", // 'stripe'، 'paypal'، إلخ.
"amount": 50.00, // اختياري - استرداد كامل إذا تم تجاهله
"reason": "customer_request" // سبب الاسترداد الاختياري
}
الاستجابة (200 حسنًا):
{
"success": true,
"message": "تمت معالجة الاسترداد",
"data": {
"refund_id": "re_xxxxx",
"amount": 50.00,
"status": "succeeded"
}
}
واجهات برمجة التطبيقات للمحفظة
تستخدم جميع نقاط النهاية للمحفظة عنوان URL الأساسي: /api/wallet/
الحصول على رصيد المحفظة
نقطة النهاية: GET /api/wallet/balance?customer_id={id}
الطلب:
curl -X GET "https://your-domain.com/api/wallet/balance?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"customer_id": 123,
"balance": 150.50,
"currency": "USD"
}
الحصول على معلومات المحفظة
احصل على معلومات كاملة عن المحفظة والائتمان بما في ذلك إعدادات إعادة التعبئة التلقائية.
نقطة النهاية: GET /api/wallet/info?customer_id={id}
الطلب:
curl -X GET "https://your-domain.com/api/wallet/info?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"
الاستجابة (200 حسنًا):
{
"customer_id": 123,
"wallet": {
"wallet_account_id": 456,
"balance": 150.50,
"currency": "USD",
"auto_recharge_enabled": true,
"auto_recharge_amount": 100.00,
"auto_recharge_threshold": 10.00,
"low_balance_warning_threshold": 10.00
}
}
تعبئة المحفظة
قم بتعبئة المحفظة عن طريق شحن طريقة الدفع.
نقطة النهاية: POST /api/wallet/topup
التدفق: شحن بطاقة/PayPal → ائتمان المحفظة
الطلب:
curl -X POST https://your-domain.com/api/wallet/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00,
"payment_method_id": 789
}'
جسم الطلب:
{
"customer_id": 123,
"amount": 100.00,
"currency": "USD", // الافتراضي: USD
"payment_method_id": 789 // اختياري - يستخدم الافتراضي إذا تم تجاهله
}
الاستجابة (200 حسنًا):
{
"success": true,
"payment": {
"transaction_id": "ch_xxxxx",
"amount": 100.00
},
"wallet": {
"ledger_id": 503,
"balance_after": 250.50,
"customer_transaction_id": 7001
},
"message": "تم تعبئة المحفظة بمبلغ 100.00 دولار أمريكي"
}
ينشئ خط ائتمان الفواتير (customer_transaction مع payment_type=wallet_topup_card، تكلفة تجزئة سالبة، wallet_ledger_id محدد). هذا متميز عن توفير الهجين، حيث يتم ائتمان دفتر المحفظة كـ card_capture_topup دون خط تعبئة.
إلغاء تعبئة الأموال: DELETE /crm/transaction/transaction_id/{transaction_id} يقوم بخصم المحفظة واسترداد البطاقة. راجع إلغاء المعاملة أدناه.
الحصول على معاملات المحفظة
احصل على تاريخ معاملات المحفظة (دفتر الأستاذ).
نقطة النهاية: GET /api/wallet/transactions?customer_id={id}&limit={n}&offset={n}&type={type}
الطلب:
curl -X GET "https://your-domain.com/api/wallet/transactions?customer_id=123&limit=50&offset=0&type=credit" \
-H "Authorization: Bearer YOUR_API_KEY"
معلمات الاستعلام:
customer_id(مطلوب): معرف العميلlimit(اختياري): عدد السجلات (الافتراضي: 50)offset(اختياري): إزاحة الصفحات (الافتراضي: 0)type(اختياري): تصفية حسب نوع المعاملة ('credit'، 'debit'، 'refund'، 'adjustment')
الاستجابة (200 حسنًا):
{
"customer_id": 123,
"count": 2,
"transactions": [
{
"ledger_id": 501,
"transaction_type": "credit",
"amount": 100.00,
"balance_before": 150.50,
"balance_after": 250.50,
"description": "تعبئة البطاقة",
"reference_type": "payment_capture",
"reference_id": 103,
"created_at": "2025-12-27T10:35:00Z"
},
{
"ledger_id": 502,
"transaction_type": "debit",
"amount": 50.00,
"balance_before": 250.50,
"balance_after": 200.50,
"description": "رسوم الخدمة",
"reference_type": "service_charge",
"reference_id": 789,
"created_at": "2025-12-27T11:00:00Z"
}
]
}
إلغاء المعاملة
قم بإلغاء معاملة عميل غير مفوترة. يتطلب delete_customer_transaction.
نقطة النهاية: DELETE /crm/transaction/transaction_id/{transaction_id}
الطلب:
curl -X DELETE "https://your-domain.com/api/transaction/transaction_id/7001" \
-H "Authorization: Bearer YOUR_API_KEY"
النجاح (200 حسنًا) — رسوم يدوية بسيطة (لا رابط للمحفظة):
{
"result": "نجاح"
}
النجاح (200 حسنًا) — خط مرتبط بالمحفظة (عكس فقط):
{
"result": "نجاح",
"wallet_reversal": {
"wallet_reversed": true,
"wallet_amount": 50.00,
"wallet_ledger_id": 503
}
}
النجاح (200 حسنًا) — تعبئة أموال صريحة (payment_type=wallet_topup_card):
{
"result": "نجاح",
"wallet_reversal": {
"wallet_reversed": true,
"wallet_amount": 100.00,
"wallet_ledger_id": 503,
"refund": {
"capture_id": 88,
"vendor": "stripe",
"vendor_transaction_id": "ch_xxxxx",
"refund_id": "re_xxxxx",
"status": "succeeded",
"amount": 100.00
}
}
}
سياسة الإلغاء / الاسترداد
| مصدر المعاملة | wallet_ledger_id | المحفظة عند الإلغاء | استرداد البطاقة عند الإلغاء |
|---|---|---|---|
| رسوم يدوية/ائتمانية | لا | لا | لا |
تعبئة الأموال (POST /wallet/topup) | نعم | خصم المحفظة | نعم (wallet_topup_card) |
| نقد / ائتمان يدوي للمحفظة | نعم | خصم المحفظة | لا |
| فاتورة مدفوعة من المحفظة | نعم | ائتمان المحفظة | لا |
| خدمة / توفير (التقاط هجين) | عادةً لا على رسوم CT | لا* | لا |
*توجه التدفقات الهجينة البطاقة → المحفظة → خصم الخدمة داخليًا (card_capture_topup + service_charge صفوف دفتر الأستاذ). الخط المرئي للفواتير هو رسوم الخدمة، وليس خط تعبئة. يتم التخطيط لعكس أغنى لتلك الرسوم في فرع المستندات المالية.
الأخطاء (500):
{
"result": "فشل",
"Reason": "رصيد المحفظة غير كاف. المتاح: 0.00، المطلوب: 20.00"
}
- معاملة مفوترة:
"لا يمكن حذف معاملة مرتبطة بفاتورة" - رصيد المحفظة غير كاف: أنفق العميل بالفعل الأموال المعبأة؛ يتم رفض الإلغاء (لا إلغاء جزئي)
- فشل استرداد البطاقة: يتم التراجع عن عكس المحفظة؛ تبقى المعاملة غير ملغاة
ملاحظة: الإلغاء يميز void=true (مستبعد من الفواتير/الإحصائيات). ليس هو نفسه استرداد الفاتورة.
ملخص مرجع واجهة برمجة التطبيقات
نقاط نهاية طريقة الدفع
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /api/payments/methods | إضافة طريقة دفع |
| GET | /api/payments/methods?customer_id={id} | الحصول على جميع طرق الدفع |
| GET | /api/payments/methods/default?customer_id={id} | الحصول على طريقة الدفع الافتراضية |
| PUT | /api/payments/methods/set-default | تعيين طريقة الدفع الافتراضية |
| DELETE | /api/payments/methods/{id}?customer_id={id} | حذف طريقة الدفع |
نقاط نهاية تدفق الدفع
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /api/payments/charge | دفع ��باشر (خطوة واحدة) |
| POST | /api/payments/invoice | دفع فاتورة (أولاً للمحفظة) |
| POST | /api/payments/topup | تعبئة مع توفير |
| POST | /api/payments/authorize/hold | إنشاء احتجاز التفويض |
| POST | /api/payments/capture/{id} | التقاط التفويض |
| POST | /api/payments/release/{id} | تحرير التفويض |
| POST | /api/payments/rental | دفع إيجار/طرف ثالث |
| POST | /api/payments/refund | استرداد الدفع |
نقاط نهاية المحفظة
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| GET | /api/wallet/balance?customer_id={id} | الحصول على رصيد المحفظة |
| GET | /api/wallet/info?customer_id={id} | الحصول على معلومات المحفظة + الإعدادات |
| POST | /api/wallet/topup | تعبئة المحفظة عبر طريقة الدفع (ينشئ خط فواتير wallet_topup_card) |
| GET | /api/wallet/transactions?customer_id={id} | الحصول على تاريخ المعاملات |
نقاط نهاية المعاملات
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| PUT | /api/transaction/ | إنشاء معاملة عميل |
| GET | /api/transaction/ | قائمة المعاملات (مقيدة) |
| DELETE | /api/transaction/transaction_id/{id} | إلغاء المعاملة؛ عكس المحفظة؛ استرداد البطاقة فقط لتعبئة الأموال |
نقاط نهاية خاصة بـ PayPal
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /api/payments/paypal/vault/setup-token | إنشاء رمز إعداد PayPal لواجهة برمجة تطبيقات Card Fields |
| POST | /api/payments/paypal/vault/finalize | إنهاء خزانة PayPal وحفظ طريقة الدفع |
| POST | /api/payments/paypal/vault/update-setup-token | تحديث رمز الإعداد لمعالجة 3DS/SCA |
حالات الاستخدام الشائعة
حالة الاستخدام 1: شحن العميل مقابل الخدمة (مدفوعة مسبقًا)
السيناريو: عميل يدفع مقابل 30 يومًا من الخدمة باستخدام توجيه أول للمحفظة.
# دفع الفاتورة (أولاً للمحفظة، بطاقة للقصور)
curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 99.99,
"metadata": {
"service_id": 456,
"contract_days": 30,
"description": "خدمة متميزة لمدة 30 يومًا"
}
}'
الاستجابة:
{
"success": true,
"message": "تمت معالجة دفع الفاتورة",
"data": {
"service_amount": 99.99,
"wallet_portion_used": 50.00, // كانت المحفظة تحتوي على 50$
"card_portion_used": 49.99, // تم شحن البطاقة بمبلغ 49.99$
"final_balance": 0.00
}
}
حالة الاستخدام 2: دفع ذو مرحلتين مع توفير الخدمة
السيناريو: توفير خدمة فقط بعد تفويض الدفع. إذا فشل التوفير، لا يحدث أي شحن.
# دفع تعبئة مع توفير تلقائي
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 149.99,
"payment_method_id": 789,
"service_uuid": "fiber-svc-uuid",
"imsi": "123456789012345",
"days": 30,
"metadata": {
"service_type": "fiber_internet"
}
}'
التدفق:
- تفويض 149.99$ على البطاقة
- توفير خدمة الإنترنت الألياف
- إذا نجح التوفير → التقاط الدفع
- إذا فشل التوفير → تحرير التفويض (لا شحن)
حالة الاستخدام 3: دفع مجهول لنقطة اتصال
السيناريو: مستخدم مجهول يدفع للوصول إلى نقطة اتصال WiFi. لا سجل للعميل، لا طريقة دفع محفوظة.
# دفع نقطة اتصال مجهولة
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 1,
"amount": 5.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_uuid": "hotspot-downtown",
"imsi": "999999999999999",
"days": 1,
"metadata": {
"is_rental": true,
"billing_email": "user@example.com",
"hotspot_location": "Cafe Downtown"
}
}'
النتيجة:
- تمت معالجة الدفع ✅
- تم تنشيط نقطة الاتصال ✅
- لم يتم حفظ طريقة الدفع ✅
- لم يتم إنشاء سجل للعميل ✅
حالة الاستخدام 4: حجز فندق (احتجاز + التقاط)
السيناريو: احتجاز الأموال لحجز الفندق، التقاط عند تسجيل الوصول، تحرير إذا تم الإلغاء.
# الخطوة 1: تسجيل الوصول - احتجاز الأموال
curl -X POST https://your-domain.com/api/payments/authorize/hold \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 500.00,
"use_wallet": true,
"metadata": {
"reservation_id": "RES-2025-001"
}
}'
# الاستجابة: {"authorization_id": 301, "status": "authorized"}
# الخطوة 2a: تسجيل دخول العميل - التقاط
curl -X POST https://your-domain.com/api/payments/capture/301 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"metadata": {
"invoice": true,
"title": "إقامة الفندق - 3 ليال",
"description": "غرفة 302، 27-30 ديسمبر",
"send_email": true
}
}'
# الخطوة 2b: إلغاء العميل - تحرير الاحتجاز
curl -X POST https://your-domain.com/api/payments/release/301 \
-H "Authorization: Bearer YOUR_API_KEY"
حالة الاستخدام 5: إضافة طريقة دفع (Stripe)
السيناريو: عميل يضيف بطاقة Visa جديدة إلى حسابه.
# الخطوة 1: الواجهة الأمامية تنشئ رمز Stripe عبر Stripe.js
# const {token} = await stripe.createToken(cardElement);
# الخطوة 2: الواجهة الخلفية تحفظ طريقة الدفع
curl -X POST https://your-domain.com/api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"is_default": true,
"card_nickname": "بطاقة Visa للعمل"
}'
حالة الاستخدام 6: تعبئة المحفظة
السيناريو: عميل يقوم بتعبئة محفظته بمبلغ 100$ باستخدام طريقة الدفع المحفوظة.
curl -X POST https://your-domain.com/api/wallet/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00
}'
الاستجابة:
{
"success": true,
"payment": {
"transaction_id": "ch_xxxxx",
"amount": 100.00
},
"wallet": {
"balance_after": 250.50
},
"message": "تم تعبئة المحفظة بمبلغ 100.00 دولار أمريكي"
}
معالجة الأخطاء
استجابات الأخطاء الشائعة
الأموال غير الكافية (400 طلب غير صحيح):
{
"error": "رصيد المحفظة غير كاف. المتاح: 50.00، المطلوب: 200.00"
}
فشل الدفع (400 طلب غير صحيح):
{
"error": "فشل الدفع. يرجى المحاولة مرة أخرى."
}
خطأ في التحقق (400 طلب غير صحيح):
{
"error": "customer_id و amount مطلوبان"
}
غير موجود (404 غير موجود):
{
"error": "التفويض 999 غير موجود"
}
خطأ في الخادم (500 خطأ داخلي في الخادم):
{
"error": "حدث خطأ أثناء معالجة الدفع الخاص بك"
}
الاستردادات وتوجيه أول للمحفظة
خيارات الاسترداد
يدعم النظام نوعين من الاستردادات اعتمادًا على احتياجات عملك:
1. استرداد إلى مصدر الدفع (Stripe/PayPal)
حالة الاستخدام: يطلب العميل استرداد كامل للخدمة الملغاة أو المنتج المعيب.
التدفق: تعود الأموال إلى طريقة الدفع الأصلية (البطاقة، حساب PayPal، إلخ.)
نقطة النهاية: POST /api/payments/refund
مثال:
curl -X POST https://your-domain.com/api/payments/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"transaction_id": "ch_xxxxx",
"vendor": "stripe",
"amount": 100.00,
"reason": "customer_request"
}'
النتيجة:
- يقوم Stripe/PayPal بمعالجة الاسترداد إلى طريقة الدفع الأصلية
- يرى العميل الائتمان على بطاقته/حساب PayPal في غضون 5-10 أيام عمل
- لا تذهب الأموال إلى المحفظة
- يتم الحفاظ على سجل تدقيق كامل في جدول
PaymentCapture
متى تستخدم:
- ✅ ألغى العميل الطلب
- ✅ لم يتم تسليم الخدمة
- ✅ خطأ في الفوترة
- ✅ يطلب العميل صراحة الاسترداد إلى البطاقة
2. ائتمان إلى المحفظة
حالة الاستخدام: استرداد جزئي، ائتمان الخدمة، أو الاحتفاظ بالأموال للشراء في المستقبل.
التدفق: يتم ائتمان الأموال إلى رصيد المحفظة للعميل للاستخدام الفوري.
ملاحظة: عادةً ما يتم التعامل مع ائتمانات المحفظة داخليًا بواسطة النظام خلال سيناريوهات الخطأ. للحصول على ائتمانات المحفظة اليدوية، اتصل بالدعم أو استخدم أدوات الإدارة.
النتيجة:
- الأموال متاحة على الفور في المحفظة
- لا فترة انتظار
- يمكن استخدامها للشراء في المستقبل
- يتم الحفاظ على سجل تدقيق كامل في جدول
WalletLedger
متى تستخدم:
- ✅ ائتمانات الخدمة (مثل التعويض عن التوقف)
- ✅ استردادات جزئية حيث سيعيد العميل الشراء
- ✅ تعويض عن الخطأ (فشل التوفير)
- ✅ ائتمانات ترويجية
استراتيجية الاسترداد الهجينة
أفضل ممارسة: في سيناريوهات الخطأ أثناء تدفق الدفع، يقوم النظام تلقائيًا بائتمان المحفظة بدلاً من استرداد البطاقة.
استرداد الخطأ التلقائي:
سيناريو المثال:
- يدفع العميل 100$ مقابل الخدمة
- يتم شحن البطاقة بنجاح
- يفشل التوفير
- بدلاً من استرداد 100$ إلى البطاقة (5-10 أيام + رسوم الاسترداد):
- ائتمان 100$ إلى المحفظة على الفور
- يمكن للعميل إعادة المحاولة للشراء على الفور
- لا رسوم استرداد
- تجربة مستخدم أفضل
توجيه أول للمحفظة: شحنات البطاقة المحسنة
يتم شحن بطاقتك فقط للقصور، وليس المبلغ الكامل، عندما يكون لديك رصيد في المحفظة.
مثال 1: رصيد محفظة 1$ + شراء 10$
السيناريو: لديك 1$ في محفظتك وتريد شراء إضافة بقيمة 10$.
تدفق الدفع التقليدي (ليس كيف يعمل هذا النظام):
تدفق OmniCRM أول للمحفظة:
طلب واجهة برمجة التطبيقات:
curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 10.00,
"metadata": {
"addon_id": 456
}
}'
استجابة واجهة برمجة التطبيقات:
{
"success": true,
"message": "تمت معالجة دفع الفاتورة",
"data": {
"customer_id": 123,
"service_amount": 10.00,
"routing_mode": "hybrid",
"initial_balance": 1.00,
"wallet_portion_used": 1.00, // استخدم 1$ الموجودة
"card_portion_used": 9.00, // تم شحن 9$ فقط
"charged_amount": 9.00, // ← شحن البطاقة
"wallet_credited": 9.00, // ← تعبئة المحفظة
"wallet_debited": 10.00, // ← رسوم الخدمة
"final_balance": 0.00
}
}
مثال 2: رصيد محفظة 50$ + شراء 30$
السيناريو: لديك 50$ في المحفظة وشراء يكلف 30$.
تدفق أول للمحفظة:
استجابة واجهة برمجة التطبيقات:
{
"success": true,
"data": {
"service_amount": 30.00,
"initial_balance": 50.00,
"charged_amount": 0, // ← لا شحن للبطاقة
"wallet_debited": 30.00,
"final_balance": 20.00,
"payment_method_used": false // ← لم يتم استخدام البطاقة
}
}
مثال 3: احتجاز التفويض مع رصيد المحفظة
السيناريو: احتجاز 500$ للتفويض مع رصيد محفظة 150$.
الخطوة 1: التفويض (POST /api/payments/authorize/hold):
استجابة واجهة برمجة التطبيقات:
{
"authorization_id": 301,
"amount": 500.00,
"status": "authorized",
"wallet_balance": 150.00,
"wallet_to_use": 150.00,
"card_amount": 350.00,
"message": "تم تفويض البطاقة بمبلغ 350$ (تعبئة المحفظة). سيتم خصم المحفظة بمبلغ 500$ عند الالتقاط."
}
الخطوة 2: الالتقاط (POST /api/payments/capture/301):
لماذا يهم هذا:
- ✅ يتم احتجاز 350$ فقط على البطاقة، وليس 500$
- ✅ يقلل من تأثير الائتمان المتاح للعميل
- ✅ كميات التفويض أكثر دقة
- ✅ تجربة مستخدم أفضل
تفاصيل التنفيذ
منطق توجيه أول للمحفظة:
كيف يعمل:
- يتحقق النظام من الرصيد الحالي في المحفظة
- يحسب القصور:
المبلغ - wallet_balance - إذا كان القصور > 0، يتم شحن البطاقة للقصور فقط
- يتم ائتمان المحفظة بمبلغ الدفع من البطاقة
- يتم خصم المحفظة للمبلغ الكامل للخدمة
- النتيجة: يتم شحن العميل فقط لما لم تتمكن المحفظة من تغطيته
تجاوز وضع التوجيه
يمكنك تجاوز سلوك أول للمحفظة إذا لزم الأمر:
وضع التجاوز - شحن البطاقة دائمًا حتى لو كان لدى المحفظة أموال:
curl -X POST https://your-domain.com/api/payments/charge \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00,
"metadata": {
"routing_mode": "bypass"
}
}'
النتيجة: تم شحن البطاقة بمبلغ 100$، وتم ائتمان المحفظة بمبلغ 100$، وتم خصم المحفظة بمبلغ 100$ (صافي: 0)
حالة الاستخدام: يريد العميل استخدام البطاقة للحصول على مكافآت/نقاط حتى مع وجود رصيد في المحفظة.
أفضل الممارسات
- استخدم دائمًا توجيه أول للمحفظة لمدفوعات العملاء لتمكين الوظائف المدفوعة مسبقًا
- استخدم المدفوعات ذات المرحلتين (نقطة نهاية التعبئة) عند توفير الخدمات لتجنب الشحن إذا فشل التوفير
- قم بتعيين بيانات التعريف لجميع المدفوعات للحفاظ على سجل التدقيق
- استخدم المدفوعات المجهولة فقط للمستخدمين المجهولين حقًا (نقاط الاتصال، الإيجارات)
- تعامل مع الأخطاء بشكل لطيف وقدم ملاحظات واضحة للمستخدم
- اختبر تدفقات الدفع في كل من سيناريوهات النجاح والفشل
- راقب انتهاء صلاحية التفويضات (عادةً 7 أيام للبطاقات)
- قم بتنفيذ إعادة التعبئة التلقائية للخدمات المتكررة لتحسين تجربة العميل