# الدفع عبر إي كاش في التطبيق — ملف لمبرمج التطبيق

**التاريخ:** 2026-11-01 · **السيرفر:** فرع `dev` (سيرفر التيست أولاً) · **النسخة الإنكليزية المختصرة:** `docs/api/ecash.md`

صار عنا بوابة دفع إلكتروني تانية جنب بيميرا: **إي كاش**. هالملف فيه كل شي بيلزم التطبيق حتى يضيفها. **ما في ولا endpoint قديم تغيّر**: كل شي هون نقاط جديدة أو مفاتيح جديدة بتنضاف للجواب. النسخ القديمة من التطبيق بتضل شغّالة متل ما هي. شو تغيّر حول **بيميرا** ملخّص بالقسم 8.

---

## 0. الفكرة باختصار

إي كاش بتشتغل **بنفس نموذج بيميرا الحالي بالتطبيق**:

1. التطبيق بيطلب «ابدأ الدفع». السيرفر بيجمّد السلة، **وما بيعمل طلب لسا**.
2. التطبيق بيفتح صفحة إي كاش بـ WebView. الزبون بيختار البنك، ثم بطاقة أو حساب، وبيأكّد برمز SMS.
3. إي كاش بتبلّغ **سيرفرنا مباشرة** إنو الدفع تم. السيرفر وقتها بيعمل الطلب وبيعلّمه مدفوع، **حتى لو الزبون سكّر التطبيق أو انقطع النت**.
4. التطبيق بيسأل السيرفر «اندفع؟» وبيعرض النتيجة.

> **القاعدة الذهبية:** التطبيق **ما بيقرر أبداً** إنو الدفع نجح من عنوان الـ WebView. بس جواب `ecash/status` بيقول.

كل الطلبات تحت `/api/v2`، مع الترويسات المعتادة: `System-Key` و `App-Language` (`ar` أو `en`). يلي عليها 🔒 بدها توكن الزبون (`Authorization: Bearer ...`).

---

## 1. شاشة اختيار طريقة الدفع

`GET prepaid-gateways?amount=<المجموع بالليرة السورية>`

`amount` اختياري، بس ابعته دايماً: هو المجموع يلي عم تعرضه للزبون (بعد الكوبون والشحن).

```json
{
  "result": true,
  "data": [
    {"gateway": "paymera", "name": "Paymera", "min_amount": null, "max_amount": null,
     "note": null, "available": true, "unavailable_reason": null},
    {"gateway": "ecash", "name": "إي كاش", "min_amount": 15, "max_amount": 250000,
     "note": "الدفع الإلكتروني حتى 250,000 ل.س للطلب الواحد",
     "available": false,
     "unavailable_reason": "أقصى مبلغ يمكن دفعه عبر إي كاش هو 250,000 ل.س. اختر الدفع عند الاستلام أو احذف بعض المنتجات."}
  ]
}
```

| الحالة | شو بيعمل التطبيق |
|---|---|
| البوابة **مو موجودة** بالقائمة | مطفية من الإدارة: لا تعرضها |
| `note` فيها نص | اعرضه تحت اسم الطريقة |
| `available: false` | اعرضها **رمادية وما بتنكبس**، واكتب تحتها `unavailable_reason` |
| `available: true` | عادي |

- **هالقائمة بتشمل بيميرا كمان.** الإدارة صار فيها تطفي بيميرا أو تحطلها حد أعلى، فاقرأ `available` للتنتين، وإذا بيميرا مو بالقائمة لا تعرضها.
- **الدفع عند الاستلام** مو بهالقائمة: بيضل متل ما هو.
- **اطلبها من جديد** كل ما تغيّر المجموع (كوبون، شحن، كمية)، لأنو `available` بيعتمد على المبلغ.

---

## 2. الدفع بإي كاش

### 2.1 ابدأ الدفع

🔒 `POST ecash/create` — **بلا body**. بياخد السلة متل ما بياخدها `paymera/create` (المنتجات المحددة، العنوان، الكوبون، الشحن).

جواب النجاح:

```json
{
  "result": true,
  "paid": false,
  "combined_order_id": 812,
  "payment_ref": "KP812A1",
  "payment_url": "https://checkout.ecash-pay.com/Checkout/CardCheckout?...",
  "return_url": "https://kapitano.shop/api/v2/ecash/return/KP812A1",
  "amount": 14500
}
```

- **احفظ `payment_ref`.** بتحتاجه لتسأل عن النتيجة.
- `amount` هو يلي رح تسحبه إي كاش بالليرة، وهو نفس المجموع يلي بتعرضه الشاشة (مقرّب لأقرب 5 ل.س).

أجوبة تانية:

| الجواب | شو بيعمل التطبيق |
|---|---|
| `result: false` + `message` | اعرض `message` كما هي. أمثلة: السلة فاضية، الكوبون ما عاد صالح، ما في عنوان، المبلغ فوق الحد، إي كاش مطفية |
| `result: true, paid: true, payment_url: null` | الكوبون غطّى كل المبلغ: الطلب انعمل ومدفوع. روح لشاشة النجاح وافتح الطلب `combined_order_id` |
| HTTP 500 | «صار خطأ، جرّب مرة تانية». **ما في شي انسحب** |

### 2.2 الـ WebView

1. افتح `payment_url` بـ WebView. لغة صفحة إي كاش بتمشي مع ترويسة `App-Language`.
2. راقب كل تنقّل (`onNavigationRequest` / `shouldOverrideUrlLoading`):
   - إذا العنوان **بيبلّش بـ `return_url`**: سكّر الـ WebView وروح على §2.3.
   - إذا الـ WebView **طلع من موقع إي كاش لأي عنوان تاني** (زر «إلغاء» عندهم بيرجع للصفحة يلي فتحتهم): سكّره وروح على §2.3.
3. إذا الزبون سكّر الـ WebView بنفسه (رجوع / X): روح على §2.3 كمان.

- **ما في داعي تلغي شي.** المحاولة يلي ما كملت بتنتهي لحالها بعد ساعة، وما بيضل طلب معلّق، والسلة بتضل كاملة.
- `return_url` بيرجّع JSON (نفس معلومات `status`). ما في داعي تعرضه للزبون، بس لا تعتمد عليه لتقرر.

### 2.3 اندفع؟

🔒 `POST ecash/status` — body: `{"payment_ref": "KP812A1"}`

```json
{
  "result": true,
  "payment_ref": "KP812A1",
  "state": "pending",
  "paid": false,
  "message": "نتأكد من دفعتك مع إي كاش. يستغرق ذلك بضع ثوانٍ.",
  "combined_order_id": 812
}
```

| `state` | شو بتعرض | الأزرار |
|---|---|---|
| `pending` | شاشة انتظار مع `message`. **اسأل كل 3 ثواني لحد دقيقتين.** إذا ضل `pending` بعدها: «إذا انسحب المبلغ رح يطلع طلبك بس تأكّد إي كاش، **لا تدفع مرة تانية**» | «طلباتي» |
| `paid` | شاشة نجاح. الطلب صار موجود: افتحه بـ `combined_order_id` | «عرض الطلب» |
| `review` | «وصلت دفعتك، فريقنا عم يتأكد منها وبيتواصل معك» (مبلغ مختلف أو دفعة مكررة) | **بلا** «جرّب مرة تانية» |
| `failed` | `message` (فيه سبب إي كاش إذا في) | «جرّب مرة تانية» ← `ecash/create` من جديد، و«الدفع عند الاستلام» |
| `expired` / `superseded` | «لم يكتمل الدفع» | «جرّب مرة تانية» |

- اعرض `message` يلي جاية من السيرفر: هي بلغة `App-Language` وجاهزة.
- **إذا الزبون رجع من الـ WebView بلا ما يدفع**، رح تجيك `pending`. اعرضله شاشة الانتظار بس لثواني، وخلّي زر «رجوع للدفع» ظاهر، لأنو عملياً هو ما دفع. السلة بتضل كاملة.

---

## 3. طلب قديم غير مدفوع: «ادفع الآن»

`GET purchase-history/combined/{order_id}` و`purchase-history/grouped` صار فيهن مفتاح جديد جنب `can_pay_online` و`pay_order_id`:

| المفتاح | المعنى |
|---|---|
| `pay_gateway` | `"ecash"` أو `"paymera"` أو `null` |

- `pay_gateway = "ecash"` ← 🔒 `POST ecash/re-pay` مع `{"order_id": <pay_order_id>}`. الجواب نفس §2.1، وبعده نفس الـ WebView ونفس §2.3.
- `pay_gateway = "paymera"` ← `paymera/re-pay` متل قبل.

---

## 4. الاسترجاع

بنفس الـ endpoints صار في مفتاح `refund_gateway` جنب `can_refund` و`refund_order_id`:

| المفتاح | المعنى |
|---|---|
| `refund_gateway` | `"ecash"` أو `"paymera"` أو `null` |

- `refund_gateway = "ecash"` ← 🔒 `POST ecash/refund` مع `{"order_id": <refund_order_id>}`.
- `refund_gateway = "paymera"` ← `paymera/refund` متل قبل. (إذا انبعت `paymera/refund` لطلب إي كاش، السيرفر بيتعامل معه صح كمان.)

```json
{"result": true, "refund_status": "reversed", "message": "تم استرجاع المبلغ بنجاح"}
```

| `refund_status` | المعنى |
|---|---|
| `reversed` | المبلغ رجع للزبون فوراً |
| `requested` | الطلب انلغى، والمبلغ رح يرجع خلال ساعات (فريقنا بيرجّعه من لوحة إي كاش) |
| `result: false` | اعرض `message` |

- **إمتى بيطلع زر الاسترجاع؟** بس إذا `can_refund: true`، يعني أول 5 ساعات بعد الدفع وما في تاجر أكّد الطلب. اعتمد على `can_refund` وما تحسبها بالتطبيق.
- الاسترجاع بإي كاش **للمبلغ كامل** وبيلغي الطلب كله.
- اعمل تأكيد قبل الإرسال: «رح ينلغى طلبك ويرجع المبلغ. متأكد؟»

---

## 5. ممنوعات

1. **لا تبعت طلب إي كاش على `order/store`.** السيرفر بيرفض `payment_type = ecash` هونيك برسالة «حدّث التطبيق».
2. **لا تقرر «اندفع» من عنوان الـ WebView.** بس `ecash/status`.
3. **لا تنادي `ecash/webhook`.** هاد لإي كاش بس.
4. **لا تحسب حدود المبلغ بالتطبيق.** خدها من `prepaid-gateways`.
5. **لا تفرّغ السلة بالتطبيق** وقت تبدأ الدفع. السيرفر بيشيل المنتجات يلي انطلبت بس لما ينجح الدفع.

---

## 6. التجربة على سيرفر التيست

- إي كاش على بيئة الاختبار (`checkout.ecash-pay.co`). بيانات بطاقة المحاكي موجودة بملف «Merchant Info» يلي عطتنا ياه إي كاش (اطلبه من الإدارة)، **ورمز الـ SMS بيقبل أي رقم**.
- جرّب هالحالات:

| # | الحالة | النتيجة المتوقعة |
|---|---|---|
| 1 | دفع ناجح | `state: paid`، الطلب ظاهر بطلباتي، السلة فضيت |
| 2 | دفع ناجح وسكّر التطبيق فوراً قبل ما يرجع | لما تفتح التطبيق: الطلب موجود ومدفوع |
| 3 | «إلغاء» بصفحة إي كاش | ما في طلب، السلة كاملة |
| 4 | سكّر الـ WebView قبل الدفع | `pending` ثم ما في طلب، السلة كاملة |
| 5 | مجموع فوق 250,000 ل.س | إي كاش رمادية مع السبب |
| 6 | كوبون بيغطي كل المبلغ | `paid: true` بلا صفحة دفع |
| 7 | استرجاع خلال 5 ساعات | `reversed` أو `requested`، والطلب صار ملغى |
| 8 | استرجاع تاني لنفس الطلب | مرفوض برسالة |
| 9 | طلب قديم غير مدفوع بـ `pay_gateway: ecash` | `ecash/re-pay` بيفتح الدفع |
| 10 | الإدارة طفّت إي كاش | ما بتطلع بالقائمة، و`ecash/create` بيرجّع `result: false` |
| 11 | التطبيق بالإنكليزي | صفحة إي كاش والرسائل بالإنكليزي |

---

## 7. ملخص الـ endpoints الجديدة

| الطريقة | المسار | توكن | لشو |
|---|---|---|---|
| GET | `prepaid-gateways?amount=` | لا | الطرق الإلكترونية المتاحة وحدودها (بيميرا + إي كاش) |
| POST | `ecash/create` | 🔒 | ابدأ دفع السلة |
| POST | `ecash/re-pay` | 🔒 | ادفع طلب قديم غير مدفوع |
| POST | `ecash/status` | 🔒 | اندفع؟ |
| POST | `ecash/refund` | 🔒 | استرجاع |
| GET | `ecash/return/{ref}` | لا | عنوان الرجوع من صفحة إي كاش (بس لتعرف إنو الـ WebView خلص) |

مفاتيح جديدة بالـ endpoints القديمة (`purchase-history/grouped` و`purchase-history/combined/{id}`): `pay_gateway` و`refund_gateway`.

---

## 8. بيميرا: شو تغيّر وشو لازم يعرف التطبيق

**الخلاصة:** نسخة التطبيق الحالية بتضل تدفع ببيميرا بلا أي تعديل. الـ endpoints نفسها (`paymera/create`، `paymera/re-pay`، `paymera/check-payment`، `paymera/cancel-payment`، `paymera/refund`، والـ callback والـ trigger)، بنفس الحقول ونفس شكل الجواب. السيرفر عنده اختبار بيقارن أجوبتها مع الكود القديم حرف بحرف.

### 8.1 شو تغيّر بالسيرفر

| الموضوع | قبل | هلأ | أثره على التطبيق |
|---|---|---|---|
| المبلغ يلي بينطلب من بيميرا | كان ينحذف الكسر (2499.8 ← 2499) | نفس المجموع يلي بتعرضه الشاشة، مقرّب لأقرب 5 ل.س (2499.8 ← 2500) | ما في. بس إذا التطبيق بيقارن المبلغ بصفحة بيميرا مع رقم عنده، صار متطابق |
| إطفاء بيميرا من الإدارة | ما كان ممكن | `paymera/create` و`paymera/re-pay` بيرجّعوا `{"result": false, "message": "طريقة الدفع هذه غير متاحة حالياً. اختر طريقة أخرى."}` | اعرض `message`. والأحسن تخفيها من الأساس (§8.2) |
| حد أدنى أو أعلى لبيميرا (إذا حطّته الإدارة) | ما كان في | نفس الطلبين بيرجّعوا `result: false` ورسالة «أقصى مبلغ يمكن دفعه عبر بيميرا هو ...»، **وما بيتعمل أي طلب** | اعرض `message`. والأحسن تعطّلها من الأساس (§8.2) |
| الزبون دفع على رابط دفع قديم (فتح الدفع مرتين) | المصاري توصل لبيميرا والطلب يضل غير مدفوع | السيرفر بيلقط الدفعة خلال 5 دقايق وبيطبّقها على الطلب، أو بينبّه الموظفين إذا كانت مكررة | ما في |
| مخزون الطلب المدفوع من التطبيق | كان ينخصم عند الدفع ومرة تانية عند التأكيد | بينخصم مرة وحدة عند تأكيد التاجر | ما في |
| زر الإلغاء بالتطبيق (`paymera/cancel-payment`) | بيلغي المحاولة | نفس الشي، وصار ينسجّل عند الموظفين إنو المحاولة انلغت | ما في |
| صفحة الطلب | `can_pay_online` و`can_refund` | نفسهن، ومعهن مفاتيح جديدة `pay_gateway` و`refund_gateway`، قيمتهن `"paymera"` لطلبات بيميرا | اختياري (§3 و§4) |

### 8.2 التعديل المقترح بالتطبيق لبيميرا

شاشة الدفع تقرأ `GET prepaid-gateways?amount=` (§1) وتتعامل مع بيميرا **متل إي كاش بالضبط**:

- `paymera` مو موجودة بالقائمة ← لا تعرضها.
- `available: false` ← رمادية مع `unavailable_reason`.
- `note` ← تحت الاسم.

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

### 8.3 حالات تجربة بيميرا على سيرفر التيست

| # | الحالة | النتيجة المتوقعة |
|---|---|---|
| 1 | دفع ناجح بالتطبيق | الطلب ظاهر ومدفوع، والسلة فضيت (متل قبل) |
| 2 | إلغاء من التطبيق | ما في طلب، والسلة كاملة (متل قبل) |
| 3 | المبلغ المطلوب بصفحة بيميرا | نفس المجموع المعروض بالتطبيق |
| 4 | الإدارة طفّت بيميرا | ما بتطلع بـ `prepaid-gateways`، و`paymera/create` بيرجّع `result: false` |
| 5 | الإدارة حطّت حد أعلى، والسلة فوقه | `available: false` مع السبب، و`paymera/create` بيرجّع `result: false` بلا ما يعمل طلب |
| 6 | استرجاع طلب بيميرا | نفس قبل، و`refund_gateway: "paymera"` |
