# Borrower Book a Loan Flow (End-to-End Mobile App Guide)

This document provides the complete end-to-end API specification for the **Book a Loan** journey on the borrower mobile application.

It begins from **Employer Selection & Submission**, covers both **Bank Statement Verification paths (My Bank Statement / MBS SMS Ticket vs. Manual PDF Upload)**, **AI Financial Analysis & Credit Offer**, **Mono Mandate Setup**, **Affiliate Booking Fee Payment**, and ends at **Final Loan Booking & Disbursal**.

---

## High-Level Flow Overview

```mermaid
flowchart TD
    A[Step 1: Select & Submit Employer] --> B{Step 2: Bank Statement Option}
    B -->|Option A: MBS Ticket| C[Initiate MBS Request via SMS]
    C --> D[Submit Ticket & Password]
    D --> E[Poll / Await AI Analysis]
    B -->|Option B: Manual Upload| F[Upload PDF Statement Directly]
    E --> G[Step 3: Check Eligibility & Recommended Offer]
    F --> G
    G --> H[Step 4: Review Loan Terms & EMI Calculation]
    H --> I[Step 5: Authorize Mono Direct Debit Mandate]
    I --> J{Step 6: Referral Booking Fee?}
    J -->|Yes| K[Pay Booking Fee via Paystack]
    J -->|No| L[Step 7: Finalize & Book Loan]
    K --> L
    L --> M[Auto-Approval & Wallet Disbursal / Admin Review]
```

---

## Step 1: Employer Selection & Employment Details

The borrower must be linked to an active employer on the platform.

### 1.1 Fetch List of Active Employers
Use this to populate the employer dropdown in the mobile UI.

- **URL:** `GET /api/v1/loans/offer` (or `GET /api/v1/profile/employment-options`)
- **Headers:** `Authorization: Bearer <token>`
- **Response:** `200 OK`
```json
{
  "employers": [
    {
      "id": 12,
      "name": "Federal Ministry of Health",
      "state": "Abuja"
    },
    {
      "id": 15,
      "name": "Lagos State Civil Service",
      "state": "Lagos"
    }
  ]
}
```

### 1.2 Submit Employment Verification
The borrower submits their employer selection and payroll details.

- **URL:** `POST /api/v1/profile/verify/employment`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:**
```json
{
  "employer_id": 12,
  "payroll_number": "IPPIS982341",
  "state_of_employment": "Abuja"
}
```
- **Response:** `200 OK`
```json
{
  "message": "Employment verified successfully",
  "verificationSteps": [
    { "key": "bvn_verified", "label": "BVN Verification", "completed": true },
    { "key": "employment_verified", "label": "Employment Verification", "completed": true }
  ]
}
```

---

## Step 2: Bank Statement Verification (MBS vs. Manual Upload)

NextPayday requires a 6-month bank statement to evaluate the borrower's monthly salary, cash flow, and loan repayment capacity.

The mobile app provides **two options** for borrowers:
1. **Option A: Automated Bank Statement (My Bank Statement / MBS)** — The bank dispatches a secure ticket SMS to the borrower's phone.
2. **Option B: Manual PDF Upload** — The borrower uploads their 6-month PDF bank statement directly.

---

### Option A: My Bank Statement (MBS SMS Ticket Flow)

This is the automated retrieval method. The borrower does not need to download or handle PDF files.

#### Step 2A.1: Initiate Statement Request
Triggers NextPayday to call the My Bank Statement provider. The provider instructs the user's bank to generate an official 6-month statement and sends a **Ticket Number** and **Ticket Password** to the borrower's phone via SMS.

- **URL:** `POST /api/v1/bank-statements/requests`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:** `{}` (empty JSON object; server uses the linked 10-digit NUBAN and profile phone number).
- **Response:** `201 Created`
```json
{
  "request_id": "REQ_MBS_9812401",
  "status": "pending",
  "message": "Ticket will be sent to the phone on file; use request_id to complete retrieval.",
  "expires_at": "2026-09-11T20:30:00.000000Z",
  "statement_period": {
    "start_date": "2026-03-10",
    "end_date": "2026-09-10"
  }
}
```
*Note on Cooldown:* There is a 60-second cooldown between requests. A 409 response indicates a pending request already exists or the cooldown is still active.

#### Step 2A.2: Submit Ticket Received via SMS
Once the borrower receives the SMS from their bank / My Bank Statement, they enter the ticket number and password into the app.

- **URL:** `POST /api/v1/bank-statements/requests/{request_id}/complete`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:**
```json
{
  "ticket_number": "TCK-892341",
  "ticket_password": "UserPassword123",
  "defer_mandate": false
}
```
- **Response:** `202 Accepted`
```json
{
  "request_id": "REQ_MBS_9812401",
  "status": "processing",
  "analysis_status": "pending",
  "message": "SMS ticket confirmed. Statement retrieval and analysis are processing.",
  "statement_period": {
    "start_date": "2026-03-10",
    "end_date": "2026-09-10"
  }
}
```

#### Step 2A.3: Poll Bank Statement Request Status
The statement retrieval and AI analysis run as asynchronous queue jobs. The mobile app polls this endpoint every 3–5 seconds until status is `completed` (or listens for push notification `BankStatementProcessed`).

- **URL:** `GET /api/v1/bank-statements/requests/{request_id}`
- **Headers:** `Authorization: Bearer <token>`
- **Response (When Finished):** `200 OK`
```json
{
  "request_id": "REQ_MBS_9812401",
  "status": "completed",
  "analysis_status": "completed",
  "result": {
    "average_net_pay": 185000,
    "minimum_net_pay": 170000,
    "recommended_loan_amount": 120000,
    "safe_recommended_loan_amount": 95000,
    "months_analyzed": 6
  },
  "completed_at": "2026-09-10T20:31:15.000000Z"
}
```

---

### Option B: Manual PDF Upload Flow

Use this if the borrower's bank does not support My Bank Statement SMS tickets, or if the user already has a 6-month PDF bank statement downloaded from their mobile banking app.

#### Step 2B.1: Upload and Analyze PDF
Uploads the PDF directly to NextPayday's AI Credit Scoring Engine. The engine extracts transactions, verifies salary credits, computes net pay, determines the maximum recommended loan amount, and initializes the Mono direct debit mandate.

- **URL:** `POST /api/v1/bank-statements/analyze-upload`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: multipart/form-data`
- **Form Data Fields:**
  - `file`: `[binary PDF file]` *(Required, max 20MB)*
  - `pdf_password`: `"1234"` *(Optional, if the PDF is password-protected)*
  - `defer_mandate`: `false` *(Optional, defaults to false)*
- **Response:** `200 OK`
```json
{
  "status": "success",
  "message": "Analysis complete.",
  "report": {
    "status": "success",
    "average_net_pay": 185000,
    "minimum_net_pay": 170000,
    "recommended_loan_amount": 120000,
    "safe_recommended_loan_amount": 95000,
    "months_analyzed": 6
  },
  "mandate_url": "https://connect.mono.co/?token=sec_mono_token_123456",
  "mandate_id": "md_981240124",
  "local_mandate_id": 45,
  "mandate_error": null,
  "verificationSteps": [
    { "key": "bvn_verified", "label": "BVN Verification", "completed": true },
    { "key": "bank_connected", "label": "Link Salary Account", "completed": true },
    { "key": "bank_statement_analyzed", "label": "Bank Statement Analysis", "completed": true },
    { "key": "employment_verified", "label": "Employment Verification", "completed": true }
  ]
}
```

---

## Step 3: Check Eligibility Status & Recommended Loan Limit

After the bank statement is analyzed (via either MBS or Manual Upload), the app queries the borrower's overall eligibility status. This returns their maximum borrowing power and the Mono mandate setup link.

- **URL:** `GET /api/v1/profile/eligibility-status`
- **Headers:** `Authorization: Bearer <token>`
- **Response:** `200 OK`
```json
{
  "all_complete": true,
  "mandate_url": "https://connect.mono.co/?token=sec_mono_token_123456",
  "loan_mandate_activation_id": 45,
  "recommended_loan_amount": 120000,
  "verificationSteps": [
    { "key": "identity_verified", "label": "Identity Verified", "completed": true },
    { "key": "employment_verified", "label": "Employment Verified", "completed": true },
    { "key": "eligibility_check_complete", "label": "Credit Assessment", "completed": true }
  ]
}
```

### UI Guidance for the App:
1. Display the maximum available loan to the borrower (e.g. *"You qualify for up to ₦120,000"*).
2. Set the loan slider maximum to `recommended_loan_amount`.
3. Save `loan_mandate_activation_id` for the final submission.

---

## Step 4: Calculate Loan Terms & Repayment Breakdown

When the borrower adjusts the amount and duration slider, call this endpoint to calculate interest, fees, monthly repayment (EMI), and net payout.

- **URL:** `POST /api/v1/loans/offer/details`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:**
```json
{
  "amount": 100000,
  "duration_months": 6,
  "loan_type": "setoff",
  "employer_id": 12
}
```
*`loan_type` can be `setoff` (interest deducted upfront) or `capitalize` (interest spread over monthly installments).*

- **Response:** `200 OK`
```json
{
  "monthly_repayment": 21666.67,
  "total_repayment": 130000,
  "insurance_fee": 2500,
  "management_fee": 1000,
  "disbursal_amount": 96500,
  "loan_charged": 5.0
}
```

---

## Step 5: Authorize Mono Direct Debit Mandate

Before the loan can be booked, the borrower must authorize automated salary debit via Mono.

1. Retrieve `mandate_url` from Step 2B or Step 3 (`GET /api/v1/profile/eligibility-status`).
2. Open `mandate_url` inside an in-app WebView or using the Mono Mobile SDK.
3. The borrower logs into their bank account and approves the recurring mandate.
4. Mono sends a webhook to NextPayday, activating the mandate in the background (`status: active`).

---

## Step 6: Pay Affiliate Booking Fee (If Applicable)

If the borrower was registered through an affiliate partner whose scheme mandates an upfront booking fee (e.g. ₦5,000):

> **Payment Channels**: The booking fee can be paid via **card or bank transfer** (`channels: ["card", "bank_transfer"]`). When paid via card, the card authorization is automatically saved for future repayment recovery in case the borrower defaults on their direct debit.

### 6.1 Initiate Payment
- **URL:** `POST /api/v1/affiliate/booking-payment/initiate`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:**
```json
{
  "loan_reference": "DRAFT-REF-123",
  "card_authorization_consent": true
}
```
> `card_authorization_consent` is **required** and must be `true`. This records the borrower's explicit consent to save the card for future default recovery charges if card payment is used.

- **Response:** `200 OK`
```json
{
  "authorization_url": "https://checkout.paystack.com/3x9abc...",
  "reference": "BOOKING-892341-XYZ",
  "amount": 5000,
  "paystack_public_key": "pk_live_..."
}
```

### 6.2 Complete Payment
- Open `authorization_url` in Paystack Checkout WebView (card or transfer checkout).
- When paid with card, the card authorization is automatically saved to `billing_cards` (via webhook) with `purpose: "default_recovery"`. When paid via bank transfer, the fee is marked paid.
- Save the `reference` (e.g., `BOOKING-892341-XYZ`) to submit in Step 7.

### 6.3 Card Saved for Default Recovery
After successful payment, the system automatically:
1. Marks the booking fee as paid
2. Extracts the Paystack card authorization from the webhook
3. Saves it to `billing_cards` with `purpose = 'default_recovery'` and the associated `loan_request_id`
4. This card can be charged in the future using `chargeAuthorization()` if the borrower's Mono/Paystack DD fails

---

## Step 7: Finalize & Book the Loan

Once employment is verified, bank statement analyzed, Mono mandate authorized, and booking fee paid (if required), submit the final booking request.

- **URL:** `POST /api/v1/loans/requests`
- **Headers:** `Authorization: Bearer <token>`, `Content-Type: application/json`
- **Request Body:**
```json
{
  "amount": 100000,
  "duration_months": 6,
  "loan_type": "setoff",
  "employer_id": 12,
  "purpose": "Home renovation & supplies",
  "loan_mandate_activation_id": 45,
  "referral_code": "NPD-4492",
  "booking_payment_reference": "BOOKING-892341-XYZ"
}
```
*(Note: `referral_code` and `booking_payment_reference` are optional unless applying under an affiliate requiring a fee).*

- **Response:** `200 OK`
```json
{
  "message": "Loan approved and disbursed to wallet.",
  "loan": {
    "id": 182,
    "reference": "LND-20260910-891",
    "amount": 100000,
    "duration_months": 6,
    "status": "disbursed",
    "repayment_schedules": [
      {
        "installment_number": 1,
        "due_date": "2026-10-28",
        "expected_amount": 21666.67
      }
    ]
  }
}
```
*(If the borrower requires manual credit review, `status` will be `pending` with message `"Loan application submitted and pending approval."`)*

---

## Error Handling & Edge Cases

| HTTP Status | Error Code / Reason | Resolution |
| :--- | :--- | :--- |
| `409 Conflict` | Statement request cooldown / duplicate pending | Wait 60 seconds before re-initiating or submit the existing `request_id`. |
| `410 Gone` | `This statement request has expired.` | Re-initiate MBS ticket request (`POST /bank-statements/requests`). |
| `422 Unprocessable` | `VERIFICATION_INCOMPLETE` | Ensure BVN, bank statement analysis, and Mono mandate are completed. |
| `422 Unprocessable` | `EMPLOYER_MISMATCH` | `employer_id` in loan request must match verified employment. |
| `422 Unprocessable` | `MONO_MANDATE_INVALID` / `MONO_MANDATE_EXPIRED` | The borrower needs to authorize the Mono direct debit mandate URL. |
| `422 Unprocessable` | `The maximum loan tenor allowed by your employer is X months.` | Reduce `duration_months` in calculation/submission. |
