# Deferred Payment (الدفع الآجل) — Full Cycle

This document describes the deferred payment feature end-to-end: business flow, enums, endpoints, request payloads, and response resources for **Client**, **Vendor**, and **Admin**.

---

## 1. What is deferred payment?

Deferred payment is a **buy-now, pay-later credit** between a **client** and a **specific vendor**.

It is **not** wallet top-up.

| Step | What happens |
|------|----------------|
| 1 | Client requests deferred payment for a vendor |
| 2 | Vendor or Admin accepts/rejects |
| 3 | Vendor adds a credit limit (default expiry: 30 days) |
| 4 | Client shops and selects `deferred` for that vendor |
| 5 | Product total (+ VAT − discount) goes on credit; shipping is usually paid now (online) |
| 6 | Later, client pays the used credit online |
| 7 | Vendor wallet is credited with `used_credit - vat_amount` |

---

## 2. Lifecycle diagram

```
Client applies (pending)
        │
        ▼
Vendor/Admin accept | reject
        │
        ▼ (if accepted)
Vendor adds credit (active, expires in 30 days)
        │
        ▼
Cart / Order: vendor_payments[vendorId] = "deferred"
        │
        ├─ pay_now   = shipping share (often online)
        └─ deferred  = products + VAT − discount
        │
        ▼
Credit used_credit increases
        │
        ▼
Client pays credit (payment_method: online)
        │
        ▼
Vendor wallet += (used_credit − vat)
        │
        ▼ (if unpaid after expires_at)
Daily job marks credit as overdue
```

---

## 3. Base URLs & auth

| Side | Base prefix | Auth |
|------|-------------|------|
| Client | `/api/app/client` | `Authorization: Bearer <token>` + `user-type: client` + `agent-token` |
| Vendor | `/api/dashboard/vendor` | `Authorization: Bearer <token>` + `user-type: vendor` |
| Admin | `/api/dashboard/admin` | `Authorization: Bearer <token>` + `user-type: admin` |

### Standard response envelope

```json
{
  "status": "success",
  "message": "...",
  "data": {}
}
```

On failure: `"status": "fail"` (or similar) with an error message.

### Common filters (where supported)

```
?filters[status]=pending
&filters[keyword]=ahmed
&filters[client]=12
&filters[vendor]=5
&per_page=15
```

Credit filter shortcut: `filters[status]=current` → `pending|active|waiting_paid`.

---

## 4. Enums

### Deferred invoice status (`DeferredInvoiceStatus`)

| Value | Meaning |
|-------|---------|
| `pending` | Waiting for vendor/admin decision |
| `accepted` | Approved — vendor can add credit |
| `rejected` | Rejected — client may re-apply |

### Client credit status (`ClientCreditStatus`)

| Value | Meaning |
|-------|---------|
| `pending` | Pending (can still be usable if active + not expired) |
| `active` | Usable credit line |
| `waiting_paid` | Waiting for payment (blocks adding a new credit) |
| `paid` | Fully paid |
| `overdue` | Expired and unpaid (set by daily job) |

### Payment methods (relevant)

| Value | Meaning |
|-------|---------|
| `online` | Gateway / instant |
| `wallet` | Client wallet |
| `deferred` | دفع آجل |
| `cod` | Cash on delivery |

---

## 5. Amount rules

| Concept | Formula |
|---------|---------|
| Deferred amount (per vendor) | `subtotal − discount + VAT` |
| Pay now (for deferred vendor) | Shipping share (often `0` if receive / free shipping / subscription) |
| Vendor wallet on credit pay | `used_credit − vat_amount` (`vendor_dues`) |
| Available credit | `max(0, credit_limit − used_credit)` |

**Important:** Shipping is **not** put on deferred credit. Products + VAT are.

---

# 6. Client APIs

## 6.1 List deferred invoice requests

`GET /api/app/client/deferred-invoices`

**Auth:** required  
**Body:** none

**Response `data[]` — DeferredInvoiceResource**

```json
{
  "id": 1,
  "vendor": {
    "id": 10,
    "name": "Store Name",
    "description": "...",
    "logo": "...",
    "banner": "...",
    "rate": 4.5,
    "is_deferred_payment_available": true,
    "is_deferred_registered": true,
    "location": {}
  },
  "phone_code": "966",
  "phone": "501234567",
  "status": "pending",
  "status_translation": "Pending",
  "notes": null,
  "identity": null,
  "created_at": "2026-05-13",
  "last_updated_at": "2026-05-13"
}
```

---

## 6.2 Create / re-submit deferred invoice request

`POST /api/app/client/deferred-invoices`

### Request payload

```json
{
  "vendor_id": 10,
  "phone_code": "966",
  "phone": "501234567",
  "identity": "123"
}
```

| Field | Type | Rules |
|-------|------|-------|
| `vendor_id` | integer | Required. Must be a vendor user. No existing `pending`/`accepted` request for this client+vendor |
| `phone_code` | string | Required, max 10 |
| `phone` | string | Required, max = country phone length (or 20) |
| `identity` | string/id | Required, must exist in `media` table |
| `notes` | string | Optional (accepted via input, not strictly validated) |

### Business rules

- One record per client + vendor.
- If previous status is `rejected` → same row is reset to `pending`.
- If `pending` or `accepted` already exists → error.
- Supervisors are notified on create/update.

### Success response

`data` = DeferredInvoiceResource (see above).

---

## 6.3 Upload identity document

`POST /api/app/client/deferred-invoices/{id}/identify`

### Request (multipart/form-data)

| Field | Type | Rules |
|-------|------|-------|
| `identify` | file | Required, image/pdf, max 5MB (`jpeg,png,jpg,pdf`) |

> **Note:** FormRequest validates `identify`, while controller reads `identity`. Prefer confirming which field the live API expects before release.

### Success response

`data` = DeferredInvoiceResource (with identity media).

---

## 6.4 List credits

`GET /api/app/client/deferred-invoices/credits`

### Query

```
?per_page=15
&filters[status]=active
&filters[keyword]=store
```

### Response `data.credits[]` — CreditResource

```json
{
  "id": 1,
  "vendor": { "...VendorResource..." },
  "deferred_invoice_id": 1,
  "credit_limit": 1000.0,
  "used_credit": 250.0,
  "available_credit": 750.0,
  "is_active": true,
  "paid_at": null,
  "notes": null,
  "status": "active",
  "status_translation": "Active",
  "is_expired": false,
  "created_at": "2026-05-13",
  "expires_at": "2026-06-12",
  "orders_count": null,
  "products_count": null,
  "products": null
}
```

Plus standard Laravel pagination meta.

---

## 6.5 Show credit details + related orders

`GET /api/app/client/deferred-invoices/credits/{creditId}`

### Response

```json
{
  "credit": { "...CreditResource..." },
  "orders": {
    "data": [
      {
        "id": 99,
        "order_group_id": 50,
        "total": 120.5,
        "created_at": "2026-05-20",
        "products": [
          {
            "id": 1,
            "name": "Product",
            "main": "https://..."
          }
        ]
      }
    ],
    "links": {},
    "meta": {}
  }
}
```

---

## 6.6 Pay used credit (MyFatoorah initiation)

`POST /api/app/client/deferred-invoices/credits/{creditId}/pay`

### Request payload

```json
{
  "payment_method": "online"
}
```

| Field | Rules |
|-------|--------|
| `payment_method` | Required. Only `online` is allowed |

### Business rules

- Credit must belong to authenticated client → otherwise 403
- Already `paid` → error
- `used_credit <= 0` → error
- Creates a `CheckoutSession` (`type = client_credit`) and returns MyFatoorah `payment_url`
- Credit is **not** marked paid until successful callback

### Success response (payment initiation)

```json
{
  "status": "pending_payment",
  "session_id": 1,
  "reference": "CHK-XXXXXXXXXXXX",
  "invoice_id": 12345,
  "payment_url": "https://...",
  "credit_id": 1,
  "amount": 250.0,
  "currency": "SAR"
}
```

### Callback completion (`/api/general/payments/myfatoorah/callback`)

```
MyFatoorah verifies payment
  → ClientCreditPaymentService::completePayment()
  → Payment record created (paid)
  → ClientCredit status = paid, paid_at = now
  → CreditPaidEvent
  → AddVendorWalletOnCreditPaid (queued)
  → vendor.wallet += (used_credit − vat_amount)
  → Vendor notified
```

This is **not** triggered by wallet top-up.

---

## 6.7 Client routes declared but NOT implemented

Do **not** rely on these yet (methods missing on controller):

| Method | Path |
|--------|------|
| GET | `/api/app/client/deferred-invoices/summary` |
| GET | `/api/app/client/deferred-invoices/upcoming` |
| GET | `/api/app/client/deferred-invoices/overdue` |
| GET | `/api/app/client/deferred-invoices/{id}` |
| POST | `/api/app/client/deferred-invoices/{id}/request-extension` |

---

# 7. Cart & Order (Client) — using deferred

## 7.1 Cart preview with deferred selection

`GET /api/app/client/cart`

### Query / params

```
?delivery_method=delivery
&vendor_payments[10]=deferred
&vendor_payments[12]=online
&location_id=3
&distance=12.5
&code=SAVE10
```

| Field | Rules |
|-------|--------|
| `delivery_method` | Optional enum (`delivery`, `receive`, ...) — default `delivery` |
| `vendor_payments` | Optional map: `{ vendor_id: payment_method }` |
| `vendor_payments.*` | `online` \| `wallet` \| `deferred` \| `cod` |
| `location_id` | Optional |
| `distance` | Optional |
| `code` | Optional coupon |

### Response extras (authenticated user)

```json
{
  "total_pay_now": 15.0,
  "total_deferred": 230.0,
  "vendor_payment_methods": [
    {
      "vendor_id": 10,
      "selected_method": "deferred",
      "pay_now_amount": 15.0,
      "deferred_amount": 230.0,
      "payment_methods": [
        {
          "method": "online",
          "label": "Instant",
          "available": true,
          "selected": false,
          "pay_now_amount": 245.0,
          "deferred_amount": 0
        },
        {
          "method": "wallet",
          "label": "Wallet",
          "available": true,
          "selected": false,
          "pay_now_amount": 245.0,
          "deferred_amount": 0,
          "wallet_balance": 500.0
        },
        {
          "method": "deferred",
          "label": "Deferred payment",
          "available": true,
          "selected": true,
          "pay_now_amount": 15.0,
          "deferred_amount": 230.0
        }
      ]
    }
  ],
  "deferred_payment_available": true,
  "deferred_payment": {
    "total_deferred_amount": 230.0,
    "online_payment_amount": 15.0,
    "vendors": [
      {
        "vendor_id": 10,
        "vendor_name": "Store",
        "deferred_amount": 230.0,
        "remaining_credit_after": 770.0
      }
    ]
  }
}
```

### When deferred is unavailable

```json
{
  "deferred_payment_available": false,
  "deferred_payment_message": "Deferred payment is not available for all selected vendors",
  "deferred_payment": {
    "total_deferred_amount": 230.0,
    "online_payment_amount": 15.0,
    "vendors": [],
    "unavailable_vendors": [
      {
        "vendor_id": 10,
        "vendor_name": "Store",
        "deferred_amount": 230.0,
        "reason": "Insufficient credit balance",
        "available_credit": 100.0
      }
    ]
  }
}
```

### Deferred availability conditions

For a vendor, deferred is available only if:

1. Client has an **accepted** deferred invoice with that vendor
2. There is an **active** credit (`is_active`, status `active` or `pending`, not expired)
3. Remaining credit covers the vendor deferred amount

If client selects deferred but it is unavailable, cart falls back to `online`.

---

## 7.2 Create order with deferred

`POST /api/app/client/orders`

### Request payload

```json
{
  "location_id": 3,
  "distance": 12.5,
  "delivery_method": "delivery",
  "coupon_code": "FREE_SHIP",
  "vendor_payments": {
    "10": "deferred",
    "12": "online"
  }
}
```

| Field | Rules |
|-------|--------|
| `location_id` | Required, belongs to auth user |
| `distance` | Optional numeric ≥ 0 |
| `delivery_method` | Required DeliveryMethod enum |
| `coupon_code` | Optional, must exist |
| `vendor_payments` | **Required** object, min 1 entry |
| `vendor_payments.*` | Required payment method value |

### Order behavior

1. Validates deferred availability per vendor
2. Deferred vendors: shipping → gateway/pay-now; products+VAT → credit
3. Creates credit debit transaction and increases `used_credit`
4. Stamps order with `deferred_invoice_id` and `client_credit_id`
5. If gateway amount > 0 → returns MyFatoorah `payment_url` (pending payment)
6. If gateway amount = 0 (fully deferred with no shipping due) → creates order immediately

### Possible errors

- No accepted deferred invoice for vendor
- No active credit
- Insufficient credit balance
- Online payment failed
- Insufficient wallet balance

---

# 8. Vendor APIs

Auth: vendor token. All invoices/credits scoped to authenticated vendor.

## 8.1 List requests

`GET /api/dashboard/vendor/deferred-invoices`

### Query

```
?per_page=15
&filters[status]=pending
&filters[keyword]=ahmed
&filters[client]=2
```

### Response item — DeferredInvoiceResource

```json
{
  "id": 1,
  "client": {
    "id": 2,
    "name": "Client Name",
    "phone_code": "966",
    "phone": "501234567",
    "avatar": "https://..."
  },
  "phone_code": "966",
  "phone": "501234567",
  "status": "pending",
  "status_translation": "Pending",
  "notes": null,
  "rejection_note": null,
  "identity": null,
  "created_at": "2026-05-13"
}
```

Wrapped as `deferred_invoices` + pagination.

---

## 8.2 Show request

`GET /api/dashboard/vendor/deferred-invoices/{id}`

Must belong to current vendor.  
Response: DeferredInvoiceResource.

---

## 8.3 Accept request

`POST /api/dashboard/vendor/deferred-invoices/{id}/accept`

**Body:** none

**Rules**

- Already accepted → error
- Sets status = `accepted`
- Notifies client

---

## 8.4 Reject request

`POST /api/dashboard/vendor/deferred-invoices/{id}/reject`

### Request payload

```json
{
  "notes": "Incomplete documents"
}
```

| Field | Rules |
|-------|--------|
| `notes` | Required, string, min 3, max 500 |

Sets status = `rejected`, stores `rejection_note`, notifies client.

---

## 8.5 List credits for an accepted invoice

`GET /api/dashboard/vendor/deferred-invoices/{id}/credits`

Invoice must be **accepted**.

### Response item — CreditResource (vendor)

```json
{
  "id": 1,
  "credit_limit": 1000.0,
  "used_credit": 250.0,
  "vat_amount": 32.61,
  "vendor_dues": 217.39,
  "available_credit": 750.0,
  "is_active": true,
  "paid_at": null,
  "notes": null,
  "status": "active",
  "status_translation": "Active",
  "is_expired": false,
  "created_at": "2026-05-13",
  "expires_at": "2026-06-12"
}
```

`vendor_dues = used_credit − vat_amount`

---

## 8.6 Add credit

`POST /api/dashboard/vendor/deferred-invoices/{id}/add-credit`

Invoice must be accepted and belong to vendor.

### Request payload

```json
{
  "credit_limit": 5000,
  "notes": "Monthly limit"
}
```

| Field | Rules |
|-------|--------|
| `credit_limit` | Required, numeric, min 1, max 999999 |
| `notes` | Optional, string, max 500 |

### Business rules

- Blocked if client already has credit from this vendor in: `waiting_paid | overdue | active | pending`
- Creates credit with:
  - `used_credit = 0`
  - `is_active = true`
  - `status = active`
  - `expires_at = now() + 30 days`
- Notifies client

---

## 8.7 Toggle credit active/inactive

`PATCH /api/dashboard/vendor/deferred-invoices/credits/{creditId}/toggle-active`

**Body:** none

Toggles `is_active`. Must belong to vendor.  
Returns CreditResource + activated/deactivated message.

---

# 9. Admin APIs

Auth: admin token. Not vendor-scoped.

| Method | Path | Body | Notes |
|--------|------|------|-------|
| GET | `/api/dashboard/admin/deferred-invoices` | — | List + filters + pagination |
| GET | `/api/dashboard/admin/deferred-invoices/{id}` | — | Show |
| POST | `/api/dashboard/admin/deferred-invoices/{id}/accept` | none | Same as vendor |
| POST | `/api/dashboard/admin/deferred-invoices/{id}/reject` | `{ "notes": "..." }` | notes required min 3 |
| GET | `/api/dashboard/admin/deferred-invoices/{id}/credits` | — | Credits for that invoice |

Admin CreditResource = vendor credit fields **+ nested `vendor`**.

Admin does **not** add credit or toggle active (vendor only).

---

# 10. Mobile recommended screens & calls

| Screen | Endpoints |
|--------|-----------|
| Request deferred from vendor page | `POST /deferred-invoices` (+ upload identity) |
| My deferred requests | `GET /deferred-invoices` |
| My credits / bills | `GET /deferred-invoices/credits` |
| Credit details & related orders | `GET /deferred-invoices/credits/{id}` |
| Cart payment method per vendor | `GET /cart?vendor_payments[...]` |
| Place order | `POST /orders` with `vendor_payments` |
| Pay outstanding credit | `POST /deferred-invoices/credits/{id}/pay` |

---

# 11. Overdue job

- Command: `php artisan inventory:check-client-credit`
- Schedule: daily at `00:00`
- Marks unpaid expired credits as `overdue`
- Notifies client

---

# 12. Quick FAQ

### Does wallet top-up go to the vendor automatically?

**No.** Vendor gets money only when the client **pays the used credit** (`POST .../credits/{id}/pay`).

### Can one client have deferred with many vendors?

**Yes.** Each request/credit is per client + vendor.

### What is paid now vs later on checkout?

- **Later (deferred):** products + VAT − discount  
- **Now:** usually shipping (online/wallet depending on selection)

### Who can approve?

Vendor **or** Admin can accept/reject. Only Vendor can **add credit**.

---

# 13. Key source files

| Area | Path |
|------|------|
| Client controller | `app/Http/Controllers/Api/App/Client/DeferredInvoice/DeferredInvoiceController.php` |
| Vendor controller | `app/Http/Controllers/Api/Dashboard/Vendor/DeferredInvoice/DeferredInvoiceController.php` |
| Admin controller | `app/Http/Controllers/Api/Dashboard/Admin/DeferredInvoice/DeferredInvoiceController.php` |
| Deferred payment service | `app/Services/General/DeferredPaymentService.php` |
| Cart deferred logic | `app/Services/App/CartService.php` |
| Vendor wallet on pay | `app/Listeners/AddVendorWalletOnCreditPaid.php` |
| Models | `DeferredInvoice`, `ClientCredit`, `CreditTransaction` |
| Client routes | `routes/api/app/client.php` |
| Vendor routes | `routes/api/dashboard/vendor.php` |
| Admin routes | `routes/api/dashboard/admin.php` |

---

*Generated from the current codebase implementation of deferred payment (دفع الآجل).*
