
# إعادة الشحن التلقائي للحسابات الفرعية (مزود دفع مخصص)

إذا كنت تفضل معالجة مدفوعات رصيد الحسابات الفرعية من خلال مزود خاص بك بدلاً من Stripe (مثل التحويلات المصرفية، أو بوابات الدفع المحلية، أو أنظمة الفوترة المخصصة)، فإن المنصة توفر زوجاً من الـ webhook و API حتى تتمكن من تشغيل دورة الشحن ومنح الرصيد بالكامل بنفسك. تتوفر نظرة عامة غير تقنية على صفحة [حسابات الوكالة](agency-accounts.md#option-2-custom-payment-provider)؛ تغطي هذه الصفحة حمولة الـ webhook الدقيقة واستدعاء الـ API الذي تقوم به لمنح الأرصدة بعد ذلك.

تعد خطافات الويب (webhook) مجرد وسيلة واحدة من وسائل دفع عمليات التعبئة التلقائية. إذا قمت بربط [Stripe](agency-accounts.md#option-1-connect-stripe) أو [PayPal](agency-accounts.md#option-3-connect-paypal) في وضع SaaS، فإن المنصة تقوم بخصم المبلغ من بطاقة العميل المحفوظة أو حساب PayPal الخاص به تلقائياً وتمنحه الأرصدة، لذا لا يوجد شيء على هذه الصفحة يتعين عليك بناؤه. تابع القراءة فقط إذا كنت ترغب في معالجة الدفع بنفسك.

***

## نظرة سريعة على سير العمل

1. ينخفض رصيد الحساب الفرعي إلى ما دون حد إعادة الشحن التلقائي الخاص به.
2. تقوم المنصة باستدعاء **رابط الـ webhook** الخاص بك مع تفاصيل الحساب الفرعي وعدد الأرصدة التي يحتاجها.
3. يقوم خادمك بخصم المبلغ من العميل من خلال أي مزود تستخدمه.
4. يقوم خادمك باستدعاء **API منح الأرصدة** لإضافة الأرصدة إلى ذلك الحساب الفرعي.
5. يستجيب خادمك بـ `200` لتأكيد استلام الـ webhook.

***

## 1. الـ Webhook: `agency_sub_account_auto_recharge`

قم بتهيئة عنوان URL الخاص بـ webhook من خلال النقر على **SaaS Mode** في الشريط الجانبي الرئيسي، واختيار **Use a Custom Payment Provider Instead** (أو فتح بطاقة **Custom Payment Provider** بمجرد تهيئتها)، ثم ملء حقل **Auto-Recharge Webhook**.

### متى يتم إطلاقها

عندما ينخفض رصيد الحساب الفرعي إلى ما دون حد إعادة الشحن التلقائي الذي قمت بضبطه.

### الحمولة (Payload)

```json
{
  "event": "agency_sub_account_auto_recharge",
  "sub_account_id": "<sub-account-id>",
  "sub_account_email": "customer@example.com",
  "sub_account_name": "John Doe",
  "agency_id": "<agency-id>",
  "credits_requested": 500,
  "current_balance": 42,
  "threshold": 100,
  "price_per_credit_cents": 10,
  "price_per_credit_currency": "usd",
  "total_amount_cents": 5000,
  "timestamp": "2026-04-13T12:00:00.000Z",
  "idempotency_key": "auto_recharge_abc123_1681387200000"
}
```

| الحقل | الوصف |
|---|---|
| `event` | دائماً `agency_sub_account_auto_recharge` لهذا الـ webhook. |
| `sub_account_id` | المعرف الفريد للحساب الفرعي الذي يحتاج إلى أرصدة. |
| `sub_account_email` | عنوان البريد الإلكتروني للحساب الفرعي. |
| `sub_account_name` | الاسم المعروض للحساب الفرعي. |
| `agency_id` | المعرف الفريد لحساب وكالتك. |
| `credits_requested` | عدد الأرصدة المراد منحها. هذا هو مبلغ إعادة الشحن الذي قمت بضبطه، ولكن إذا انخفض الرصيد إلى ما دون الحد بأكثر مما يغطيه هذا المبلغ، فسيتم رفعه تلقائياً إلى أي قدر يلزم لإعادة الرصيد فوق الحد في عملية واحدة. قم دائماً بالخصم مقابل (ومنح) قيمة `credits_requested` من الحمولة — لا تقم ببرمجة مبلغ إعادة الشحن الخاص بك بشكل ثابت (hard-code). |
| `current_balance` | رصيد الحساب الفرعي في وقت إرسال الـ webhook. |
| `threshold` | حد الرصيد الذي أدى إلى تشغيل إعادة الشحن. |
| `price_per_credit_cents` | السعر الذي قمت بضبطه لكل رصيد، بالسنت. |
| `price_per_credit_currency` | عملة السعر (على سبيل المثال، `usd`). |
| `total_amount_cents` | إجمالي المبلغ المراد خصمه، بالسنت (`credits_requested` × `price_per_credit_cents`). |
| `timestamp` | وقت إرسال الـ webhook (بتنسيق ISO 8601). |
| `idempotency_key` | مفتاح فريد لطلب إعادة الشحن هذا. استخدمه لمنع منح الأرصدة مرتين إذا تلقى خادمك نفس الـ webhook أكثر من مرة. |

### ملاحظات هامة

- **فترة تهدئة لمدة 5 دقائق** — بعد محاولة إعادة شحن لحساب فرعي، لن ترسل المنصة webhook آخر لهذا الحساب لمدة 5 دقائق على الأقل، حتى لو انخفض رصيده أكثر. هذا يمنع الخصومات المكررة أثناء المعالجة.
- **استخدم مفتاح التكرار (idempotency key)** — تحقق دائماً من `idempotency_key` قبل منح الأرصدة. إذا تعطل خادمك بعد منح الأرصدة ولكن قبل إرسال الاستجابة، فقد تعيد المنصة إرسال الـ webhook عند انخفاض الرصيد التالي.
- **الفشل آمن وذاتي الإصلاح** — إذا كان رابط الـ webhook الخاص بك غير قابل للوصول أو أرجع خطأ، فلن يتم منح أي أرصدة. تستمر المنصة في إعادة إرسال الـ webhook (مرة واحدة كل 5 دقائق كفترة تهدئة) طالما ظل الحساب الفرعي تحت الحد — ولا يتطلب الأمر أن يرتفع الرصيد فوق الحد ثم ينخفض مرة أخرى أولاً. لم يعد بإمكان عملية خصم واحدة فائتة أن تترك الحساب الفرعي بدون إعادة شحن بشكل دائم.
- **اضبط مبلغ إعادة الشحن عند أو فوق الحد** — يجب أن يكون مبلغ إعادة الشحن الذي قمت بضبطه أكبر من أو يساوي حد إعادة الشحن التلقائي، بحيث تؤدي عملية إعادة شحن واحدة دائماً إلى استعادة الرصيد فوق الحد. (إذا احتجت يوماً إلى إصلاح حساب فرعي انخفض كثيراً تحت الحد، تقوم المنصة بتكملته بكامل العجز تلقائياً — انظر `credits_requested` أعلاه.)

***

## 2. API منح الأرصدة

بعد أن يقوم خادمك بمعالجة الدفع، استدعِ نقطة النهاية هذه لإضافة الأرصدة إلى الحساب الفرعي.

### الطلب

```http
POST https://api.dmchamp.com/v1/subaccounts/credits
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "email": "customer@example.com",
  "amount": 500,
  "description": "Auto-recharge via webhook"
}
```

| الحقل | مطلوب | الوصف |
|---|---|---|
| `email` | نعم | عنوان البريد الإلكتروني للحساب الفرعي (يجب أن يطابق حساباً فرعياً موجوداً تحت وكالتك). |
| `amount` | نعم | عدد الأرصدة المراد منحها. |
| `description` | لا | ملاحظة تصف سبب إضافة الأرصدة (تظهر في سجل معاملات الرصيد). |

**المصادقة:** أرسل مفتاح واجهة برمجة التطبيقات (API key) الخاص بك في ترويسة الطلب — إما `X-API-Key: YOUR_API_KEY` أو `Authorization: Bearer YOUR_API_KEY`. يُعد استخدام الترويسات هو الطريقة الموصى بها، لأن وجود المفتاح في عنوان URL يؤدي إلى حفظه في سجل المتصفح، وسجلات الوكيل (proxy logs)، وسجلات وصول الخادم. لا يزال بإمكانك استخدام معامل الاستعلام `?apiKey=YOUR_API_KEY` وحقل `apiKey` في نص JSON، لذا ستستمر عمليات التكامل القديمة في العمل دون تغيير.

### الاستجابة

```json
{
  "success": true,
  "sub_account_id": "abc123xyz",
  "credits_added": 500,
  "new_balance": 542
}
```

::: tip
**تلميح:** قم بتخزين `idempotency_key` من حمولة الـ webhook وتحقق منه قبل استدعاء نقطة النهاية هذه. يمنع هذا منح الأرصدة مرتين عن طريق الخطأ إذا تلقى خادمك نفس الـ webhook أكثر من مرة.
:::


### ماذا يحدث للرصيد الممنوح عند إعادة التعيين الشهرية

يعتمد هذا على كيفية إعداد الحساب الفرعي:

- **إعادة البيع** (يدفع لك الحساب الفرعي مقابل الرصيد): يتم تسجيل الرصيد الذي تمنحه هنا كرصيد تم شراؤه و**يتم ترحيله** كل شهر. أي رصيد لم ينفقه الحساب الفرعي يبقى في رصيده.
- **التخصيص** (تمنح الحساب الفرعي مخصصاً شهرياً): يتم إعادة تعبئة الرصيد ليصل إلى المخصص الشهري في تاريخ إعادة التعيين، لذا فإن أي رصيد غير مستخدم لا يتم ترحيله. هذا مقصود - حيث يتم منح المخصص بشكل جديد كل شهر.

إذا كنت ترغب في إضافة الرصيد المتبقي للحساب الفرعي إلى مخصصه الشهري الجديد بدلاً من استبداله، فقم بتفعيل خاصية ترحيل الرصيد:

```
PUT https://api.dmchamp.com/v1/subaccounts/{subAccountUid}/limits
X-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "usageLimits": { "roll_over_to_next_month": true }
}
```

إذا كان للحساب الفرعي - أو الخطة التي يتبعها - حد أقصى للترحيل أو تاريخ انتهاء صلاحية محدد، فإن إعادة التعيين تحدث في اللحظة التي يتم فيها تطبيق هذه القيود: يتم تقليص الرصيد المُرحّل إلى الأشهر التي تسمح بها، ويتم إسقاط الرصيد غير المستخدم لفترة أطول من نافذة انتهاء الصلاحية، وذلك قبل إضافة الرصيد الجديد. الأرصدة الممنوحة من خلال نقطة النهاية هذه، وعمليات إعادة الشحن التلقائية، وعمليات التعبئة الخاصة بالعميل هي أرصدة لمرة واحدة ولا يتم تقليصها أبداً. راجع [تحديد ما يتم ترحيله](sub-accounts.md#capping-what-rolls-over).

***

## مثال شامل (End-to-end)

يبدو المعالج النموذجي كما يلي:

```pseudo
on POST /your-recharge-webhook:
  payload = request.body

  if seen(payload.idempotency_key):
    return 200  // already processed, ack and exit

  charge_result = your_payment_provider.charge(
    email = payload.sub_account_email,
    amount_cents = payload.total_amount_cents,
    currency = payload.price_per_credit_currency,
  )

  if not charge_result.ok:
    return 500  // platform will retry on next balance drop

  api.post("/v1/subaccounts/credits", {
    email = payload.sub_account_email,
    amount = payload.credits_requested,
    description = "Auto-recharge via " + your_provider_name,
  })

  mark_seen(payload.idempotency_key)
  return 200
```
