دليل واجهة برمجة تطبيقات نظام الدفع OmniCRM
نظرة عامة
يوفر نظام الدفع OmniCRM بنية تحتية شاملة لمعالجة المدفوعات غير المرتبطة بالبائع. يدعم اليوم كل من Stripe وPayPal، لكن الهيكلية المودولارية تسمح بالتكامل مع أي مزود دفع (Square، Adyen، Braintree، إلخ) دون تغيير كود التطبيق.
يغطي هذا المستند جميع واجهات برمجة التطبيقات الخاصة بالدفع وسير العمل المتاحة في النظام.
🔧 تكامل Playbook Ansible: لتنفيذ هذه واجهات برمجة التطبيقات للدفع في Playbooks التزويد، راجع التحصيل والمدفوعات من 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. الحصول على مفاتيح واجهة برمجة تطبيقات Stripe
- قم بالتسجيل في https://stripe.com
- انتقل إلى المطورين → مفاتيح واجهة برمجة التطبيقات
- انسخ المفتاح القابل للنشر الخاص بك (pk_live_... أو pk_test_...)
- انسخ المفتاح السري الخاص بك (sk_live_... أو sk_test_...)
مهم:
- استخدم المفاتيح التجريبية (pk_test_/sk_test_) للتطوير
- استخدم المفاتيح الحية (pk_live_/sk_live_) للإ��تاج فقط
- لا تقم بتسجيل مفاتيح واجهة برمجة التطبيقات في التحكم في الإصدارات
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. الحصول على بيانات اعتماد واجهة برمجة تطبيقات PayPal
- أنشئ تطبيق واجهة برمجة تطبيقات REST في https://developer.paypal.com/dashboard/
- انسخ معرف العميل والمفتاح السري
- للاختبار، استخدم بيانات اعتماد Sandbox؛ للإنتاج، استخدم بيانات اعتماد Live
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 | sk_test_ / pk_test_ | sk_live_ / pk_live_ |
| واجهة برمجة تطبيقات PayPal | 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 بما في ذلك تتبع استدعاءات واجهة برمجة التطبيقات، وأحجام الدفع، ومعدلات الفشل، وأوقات الاستجابة، راجع المراقبة والمقاييس.
مخطط قاعدة البيانات غير مرتبط بالبائع
يدعم مخطط قاعدة البيانات أي مزود دفع دون الحاجة إلى ترحيلات:
مثال - حفظ بطاقة 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", // رمز لمرة واحدة من SDK الواجهة الأمامية
"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 Ansible لتدفقات الدفع ذات المرحلتين، راجع نمط الالتزام ذو المرحلتين
الخطوة 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": { // تم إنشاؤه إذا كان invoice=true
"transaction_id": 7001
},
"invoice": { // تم إنشاؤه إذا كان 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) |
| نقد / ائتمان يدوي للمحفظة | نعم | خصم المحفظة | لا |
| فاتورة مدفوعة من المحفظة | نعم | ائتمان المحفظة | لا |
| خدمة / توفير (التقاط هجين) | عادةً لا على رسوم الشحن | لا* | لا |
*توجه التدفقات الهجينة البطاقة → المحفظة → خصم الخدمة داخليًا (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 SDK |
| 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": "Work 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 دولار)
حالة الاستخدام: يرغب العميل في استخدام البطاقة للحصول على مكافآت/نقاط حتى مع وجود رصيد في المحفظة.
أفضل الممارسات
- استخدم دائمًا توجيه المحفظة أولاً لمدفوعات العملاء لتمكين الوظائف المدفوعة مسبقًا
- استخدم المدفوعات ذات المرحلتين (نقطة النهاية للتعبئة) عند توفير الخدمات لتجنب الشحن إذا فشل التوفير
- قم بتعيين بيانات التعريف لجميع المدفوعات للحفاظ على سجل التدقيق
- استخدم المدفوعات المجهولة فقط للمستخدمين المجهولين حقًا (نقاط الاتصال، الإ