# Borrower self-service API contract (repayments, statements, wallet history)

This document is the client contract for the borrower/mobile app. The mobile app is
**not part of this workspace**, so no mobile client code was changed — the API is
documented here instead.

All endpoints require a borrower Sanctum token: `Authorization: Bearer <token>`.
Base path: `/api/v1`.

---

## 1. Canonical loan status model

Every loan object (borrower, admin, affiliate, mobile) now carries the same resolved
fields, in addition to the stored `status`:

| Field | Type | Meaning |
|-------|------|---------|
| `status` | string | Canonical stored lifecycle status (unchanged vocabulary). |
| `status_label` | string | Human label, e.g. `Pending review`, `Fully repaid`. |
| `application_progress` | `complete` \| `in_progress` | Whether the **application** has been submitted. |
| `application_progress_label` | string | e.g. `Application complete`. |
| `is_application_complete` | bool | True once the application is submitted. |

Stored statuses and mapping:

| `status` | `status_label` | `application_progress` |
|----------|----------------|------------------------|
| `draft` | Draft | in_progress |
| `collection_pending` | Setting up collection | in_progress |
| `ready_to_apply` | Ready to apply | in_progress |
| `submitted` | Submitted | complete |
| `pending` | Pending review | complete |
| `approved` | Approved | complete |
| `disbursed` | Disbursed | complete |
| `paid` | Fully repaid | complete |
| `defaulting` | Defaulting | complete |
| `managed` | Managed (grace) | complete |
| `declined` | Declined | complete |

**Key point:** a borrower who finished all onboarding stages and whose application is
awaiting review is `application_progress: "complete"` **and** `status: "pending"`
(`status_label: "Pending review"`). It is not "application incomplete". Clients must
render these two facts separately rather than treating `pending` as incomplete.

---

## 2. Pay one of my installments

```
POST /api/v1/repayments/{schedule}/pay
```

- `{schedule}` is a `repayment_schedules.id` the authenticated borrower owns.
- Body (optional): `{ "rail": "default" | "paystack" | "mono_mandate" | "wallet" }`.
  `default` (or omitted) uses the canonical collection waterfall
  (Paystack → Mono → Wallet). `mono` is accepted as an alias of `mono_mandate`.
- The endpoint delegates to the single canonical `RepaymentCollectionService`, so
  collection locks, partial-payment, and reconciliation semantics are unchanged.

Ownership: a schedule that does not belong to the authenticated borrower returns
`404` (the resource is never confirmed to exist).

Responses:

```json
{
  "success": true,
  "status": "success|partial|pending_reconciliation|failed|not_eligible",
  "message": "Full payment collected successfully via Borrower Wallet.",
  "amount_requested": 11000,
  "amount_collected": 11000,
  "outstanding": 22000,
  "loan_reference": "LN-XXXX",
  "schedule_id": 42,
  "installment_number": 2
}
```

- `200` — attempt completed (success / partial / pending_reconciliation / failed).
- `404` — schedule not found or not owned by the caller.
- `422` — not chargeable (already paid, another run in progress, or awaiting
  reconciliation) or invalid `rail`.

No provider secrets (authorization codes, mandate ids, tokens) are ever returned.

---

## 3. Email my loan statement

```
POST /api/v1/loans/{reference}/statement/email
```

- `{reference}` is the loan reference (`loan_requests.reference`) owned by the caller.
- Builds a PDF from the canonical statement generator (same loan + repayment-schedule
  data used by the admin statement/audit report; no admin/internal sections) and emails
  it to the registered address.

Responses:

```json
{ "success": true, "message": "Your loan statement has been sent to your email." }
```

- `200` — dispatched.
- `404` — loan not found or not owned by the caller.
- `429` — a statement was requested in the last 60 seconds (idempotency/rate limit).
- `500` — mail/PDF dispatch failed; safe to retry later.

The statement contains: loan reference, principal, interest/fees, total repayable,
disbursement details, full repayment schedule, paid and outstanding installments with
payment dates/references, remaining balance and the canonical loan status.

---

## 4. Wallet transaction history

Two borrower endpoints exist and now return the **same canonical ledger** shape:

- `GET /api/v1/wallet` → `{ "balance": number, "transactions": { "data": [...], "current_page", "last_page", ... } }`
- `GET /api/v1/borrowers/transactions` → `{ "data": [...], "current_page", "last_page", "per_page", "total" }`

Each transaction:

```json
{
  "id": 123,
  "type": "LOAN_REPAYMENT",
  "direction": "DEBIT",
  "amount": 3312.50,
  "reference": "EMI-WALLET-42-1",
  "description": "Loan Repayment: LN-XXXX (Inst. #2)",
  "category": "loan_repayment",
  "code": "019",
  "created_at": "2026-09-22T10:15:00+00:00"
}
```

`type` values (derived from the ledger `category`):

| category | type |
|----------|------|
| `bank_transfer` | `VIRTUAL_ACCOUNT_CREDIT` |
| `paystack_topup` | `WALLET_TOPUP` |
| `loan_repayment` | `LOAN_REPAYMENT` |
| `loan_settlement` | `LOAN_SETTLEMENT` |
| `withdrawal` | `WITHDRAWAL` |
| `withdrawal_refund` | `REFUND` |
| `commission` | `COMMISSION` |

`direction` is `CREDIT` or `DEBIT`. Ordering is newest first. The source of truth is
the `wallet_transactions` ledger only; unrelated activity logs are never merged in.

> Note: automatic Paystack/Mono repayments debit the borrower's bank/card, not the
> wallet, so they do not appear in wallet history (the wallet balance does not move).
> Wallet-funded repayments (`type: LOAN_REPAYMENT`) are returned exactly once.

---

## 5. Affiliate + admin endpoints added for this pass

- `GET /api/v1/affiliate/borrowers/{id}` — affiliate borrower + loan detail. Scope:
  own referrals, or all referrals with `affiliate.view_borrowers` /
  `affiliate.manage_referrals` (VIEW_ALL_REFERRALS). Otherwise `404`.
- `GET /api/v1/admin/affiliates/{id}/borrowers` — paginated referred borrowers with
  loan/status summary for the admin affiliate detail screen.
