# Installment Purchase

Flexible payment plans with 7+ frequencies, custom intervals, and 2-4 plans per product. Features transparent amortization schedules, 75% early payoff interest discount, and two fulfillment options (immediate or after payment). Includes configurable 0-60 day grace periods, automated payment processing via JobRunr, up to 5 retry attempts for failed payments, real-time tracking of payment history and agreement status, and full admin control for plan management.

# Installment Purchase - Customer Endpoints

**Author**: Josh S. Sakweli, Backend Lead Team  
**Last Updated**: 2025-10-18  
**Version**: v1.0

**Base URL**: `https://api.nextgate.com/api/v1/installments`

**Short Description**: This API provides endpoints for customers to manage their installment purchase agreements. Customers can view their agreements, track payment schedules, make manual payments, retry failed payments, calculate and process early payoffs, and cancel agreements. All endpoints require authentication and operate on the principle that customers can only access their own agreement data.

**Hints**: 
- All customer endpoints require authentication via Bearer token
- Customers can only access their own agreements (ownership validation applied)
- Payment processing uses wallet-based transactions
- Early payoff provides 75% discount on remaining interest
- Agreements can only be cancelled before the first payment is completed
- Failed payments can be retried up to 5 times
- Grace periods apply before first payment due date
- All amounts are in TZS (Tanzanian Shillings)

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    // Actual response data goes here
  }
}
```

### Error Response Structure
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-10-18T10:30:45",
  "data": "Error description"
}
```

### Standard Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` for successful operations, `false` for errors |
| `httpStatus` | string | HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.) |
| `message` | string | Human-readable message describing the operation result |
| `action_time` | string | ISO 8601 timestamp of when the response was generated |
| `data` | object/string | Response payload for success, error details for failures |

---

## HTTP Method Badge Standards

For better visual clarity, all endpoints use colored badges for HTTP methods with the following standard colors:

- **GET** - <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> - Green (Safe, read-only operations)
- **POST** - <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> - Blue (Create new resources)
- **DELETE** - <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> - Red (Remove resources)

---

## Endpoints

## 1. Get My Agreements
**Purpose**: Retrieve all installment agreements for the authenticated customer, optionally filtered by status

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/my-agreements`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Query Parameters**:
| Parameter | Type | Required | Description | Validation | Default |
|-----------|------|----------|-------------|------------|---------|
| status | string | No | Filter by agreement status | enum: PENDING_FIRST_PAYMENT, ACTIVE, COMPLETED, DEFAULTED, CANCELLED | null (all statuses) |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Agreements retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "agreementNumber": "INST-2025-12345",
      "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "productName": "Samsung Galaxy S24 Ultra",
      "productImage": "https://cdn.example.com/products/samsung-s24.jpg",
      "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
      "shopName": "Tech World Store",
      "totalAmount": 2500000.00,
      "amountPaid": 500000.00,
      "amountRemaining": 2000000.00,
      "currency": "TZS",
      "paymentsCompleted": 1,
      "paymentsRemaining": 11,
      "totalPayments": 12,
      "progressPercentage": 20.0,
      "nextPaymentDate": "2025-11-18T00:00:00",
      "nextPaymentAmount": 166666.67,
      "agreementStatus": "ACTIVE",
      "agreementStatusDisplay": "ACTIVE",
      "createdAt": "2025-10-18T09:00:00",
      "completedAt": null,
      "canMakeEarlyPayment": true,
      "canCancel": false
    }
  ]
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| agreementId | Unique identifier for the agreement |
| agreementNumber | Human-readable agreement number (format: INST-YYYY-NNNNN) |
| productId | ID of the product being purchased |
| productName | Name of the product |
| productImage | URL to product image |
| shopId | ID of the shop selling the product |
| shopName | Name of the shop |
| totalAmount | Total amount to be paid including interest |
| amountPaid | Amount paid so far |
| amountRemaining | Amount still owed |
| currency | Currency code (TZS) |
| paymentsCompleted | Number of payments made |
| paymentsRemaining | Number of payments left |
| totalPayments | Total number of scheduled payments |
| progressPercentage | Percentage of payments completed |
| nextPaymentDate | Date when next payment is due |
| nextPaymentAmount | Amount of next payment |
| agreementStatus | Current status of the agreement |
| agreementStatusDisplay | Human-readable status |
| createdAt | When agreement was created |
| completedAt | When agreement was completed (null if not completed) |
| canMakeEarlyPayment | Whether early payoff is allowed |
| canCancel | Whether agreement can be cancelled |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2025-10-18T10:30:45",
  "data": "Token has expired"
}
```

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `401 UNAUTHORIZED`: Authentication issues (empty, invalid, expired, or malformed tokens)
- `404 NOT_FOUND`: User not found or not authenticated

**Error Response Examples**:

*Unauthorized - Token Issues (401):*
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token has expired",
  "action_time": "2025-10-18T10:30:45",
  "data": "Token has expired"
}
```

*Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "User not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "User not found"
}
```

---

## 2. Get My Active Agreements
**Purpose**: Retrieve only active installment agreements for the authenticated customer

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/my-agreements/active`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Active agreements retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "agreementNumber": "INST-2025-12345",
      "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "productName": "Samsung Galaxy S24 Ultra",
      "productImage": "https://cdn.example.com/products/samsung-s24.jpg",
      "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
      "shopName": "Tech World Store",
      "totalAmount": 2500000.00,
      "amountPaid": 500000.00,
      "amountRemaining": 2000000.00,
      "currency": "TZS",
      "paymentsCompleted": 1,
      "paymentsRemaining": 11,
      "totalPayments": 12,
      "progressPercentage": 20.0,
      "nextPaymentDate": "2025-11-18T00:00:00",
      "nextPaymentAmount": 166666.67,
      "agreementStatus": "ACTIVE",
      "agreementStatusDisplay": "ACTIVE",
      "createdAt": "2025-10-18T09:00:00",
      "completedAt": null,
      "canMakeEarlyPayment": true,
      "canCancel": false
    }
  ]
}
```

**Success Response Fields**:
Same as Get My Agreements endpoint

**Error Response Examples**:
Same error types as Get My Agreements endpoint

---

## 3. Get Agreement By ID
**Purpose**: Retrieve detailed information about a specific installment agreement by its ID

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/agreements/{agreementId}`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Agreement details retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "agreementNumber": "INST-2025-12345",
    "customerId": "9b2e4d56-7c8a-4f9b-a3d1-5e6f7a8b9c0d",
    "customerName": "John Doe",
    "customerEmail": "john.doe@example.com",
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "productImage": "https://cdn.example.com/products/samsung-s24.jpg",
    "productPrice": 2000000.00,
    "quantity": 1,
    "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
    "shopName": "Tech World Store",
    "selectedPlanId": "4b5c6d7e-8f9a-4b1c-9d2e-3f4a5b6c7d8e",
    "planName": "12 Month Standard Plan",
    "paymentFrequency": "MONTHLY",
    "paymentFrequencyDisplay": "Monthly",
    "customFrequencyDays": null,
    "numberOfPayments": 12,
    "duration": "12 months",
    "apr": 15.00,
    "gracePeriodDays": 30,
    "downPaymentAmount": 400000.00,
    "financedAmount": 1600000.00,
    "monthlyPaymentAmount": 166666.67,
    "totalInterestAmount": 400000.00,
    "totalAmount": 2400000.00,
    "currency": "TZS",
    "paymentsCompleted": 1,
    "paymentsRemaining": 11,
    "amountPaid": 566666.67,
    "amountRemaining": 1833333.33,
    "progressPercentage": 8.33,
    "nextPaymentDate": "2025-11-18T00:00:00",
    "nextPaymentAmount": 166666.67,
    "agreementStatus": "ACTIVE",
    "defaultCount": 0,
    "createdAt": "2025-10-18T09:00:00",
    "firstPaymentDate": "2025-11-18T00:00:00",
    "lastPaymentDate": "2026-10-18T00:00:00",
    "completedAt": null,
    "fulfillmentTiming": "IMMEDIATE",
    "shippedAt": "2025-10-18T14:00:00",
    "deliveredAt": null,
    "orderId": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "shippingAddress": {
      "fullName": "John Doe",
      "phoneNumber": "+255712345678",
      "street": "123 Main Street",
      "city": "Dar es Salaam",
      "state": "Dar es Salaam",
      "postalCode": "12345",
      "country": "Tanzania"
    },
    "billingAddress": {
      "fullName": "John Doe",
      "phoneNumber": "+255712345678",
      "street": "123 Main Street",
      "city": "Dar es Salaam",
      "state": "Dar es Salaam",
      "postalCode": "12345",
      "country": "Tanzania"
    },
    "payments": [
      {
        "paymentId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
        "paymentNumber": 1,
        "scheduledAmount": 166666.67,
        "paidAmount": 166666.67,
        "principalPortion": 146666.67,
        "interestPortion": 20000.00,
        "remainingBalance": 1453333.33,
        "lateFee": null,
        "currency": "TZS",
        "paymentStatus": "COMPLETED",
        "paymentStatusDisplay": "COMPLETED",
        "dueDate": "2025-11-18T00:00:00",
        "paidAt": "2025-11-18T10:30:00",
        "attemptedAt": "2025-11-18T10:29:45",
        "paymentMethod": "WALLET",
        "transactionId": "TXN-2025-67890",
        "failureReason": null,
        "retryCount": 0,
        "daysUntilDue": null,
        "daysOverdue": null,
        "canPay": false,
        "canRetry": false
      }
    ],
    "canMakeEarlyPayment": true,
    "canCancel": false,
    "canUpdatePaymentMethod": false
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| agreementId | Unique identifier for the agreement |
| agreementNumber | Human-readable agreement number |
| customerId | ID of the customer |
| customerName | Customer's full name |
| customerEmail | Customer's email address |
| productId | ID of the product |
| productName | Name of the product |
| productImage | URL to product image |
| productPrice | Original product price |
| quantity | Quantity purchased |
| shopId | ID of the shop |
| shopName | Name of the shop |
| selectedPlanId | ID of the selected installment plan |
| planName | Name of the installment plan |
| paymentFrequency | Payment frequency enum value |
| paymentFrequencyDisplay | Human-readable payment frequency |
| customFrequencyDays | Custom frequency in days (if applicable) |
| numberOfPayments | Total number of payments |
| duration | Human-readable duration |
| apr | Annual Percentage Rate |
| gracePeriodDays | Grace period before first payment |
| downPaymentAmount | Down payment amount |
| financedAmount | Amount being financed |
| monthlyPaymentAmount | Amount per payment |
| totalInterestAmount | Total interest to be paid |
| totalAmount | Grand total (product + interest) |
| currency | Currency code |
| paymentsCompleted | Number of completed payments |
| paymentsRemaining | Number of remaining payments |
| amountPaid | Total amount paid so far |
| amountRemaining | Total amount remaining |
| progressPercentage | Percentage of completion |
| nextPaymentDate | Next payment due date |
| nextPaymentAmount | Next payment amount |
| agreementStatus | Current agreement status |
| defaultCount | Number of missed payments |
| createdAt | Agreement creation timestamp |
| firstPaymentDate | First payment due date |
| lastPaymentDate | Last payment due date |
| completedAt | Completion timestamp (null if not completed) |
| fulfillmentTiming | When product is shipped (IMMEDIATE or AFTER_PAYMENT) |
| shippedAt | Shipment timestamp |
| deliveredAt | Delivery timestamp |
| orderId | Associated order ID |
| shippingAddress | Shipping address object |
| billingAddress | Billing address object |
| payments | Array of payment objects with full details |
| canMakeEarlyPayment | Whether early payoff is allowed |
| canCancel | Whether agreement can be cancelled |
| canUpdatePaymentMethod | Whether payment method can be updated |

**Error Response Examples**:

*Bad Request (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "You do not have access to this agreement",
  "action_time": "2025-10-18T10:30:45",
  "data": "You do not have access to this agreement"
}
```

*Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Agreement not found with ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "action_time": "2025-10-18T10:30:45",
  "data": "Agreement not found with ID: 3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
```

---

## 4. Get Agreement By Number
**Purpose**: Retrieve detailed information about a specific installment agreement by its agreement number

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/agreements/number/{agreementNumber}`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementNumber | string | Yes | Agreement number (format: INST-YYYY-NNNNN) | Pattern: INST-\d{4}-\d{5} |

**Success Response JSON Sample**:
Same as Get Agreement By ID endpoint

**Success Response Fields**:
Same as Get Agreement By ID endpoint

**Error Response Examples**:
Same as Get Agreement By ID endpoint, with agreement number in error messages instead of ID

---

## 5. Get Agreement Payments
**Purpose**: Retrieve all payment records for a specific installment agreement

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/agreements/{agreementId}/payments`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Payment history retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "paymentId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
      "paymentNumber": 1,
      "scheduledAmount": 166666.67,
      "paidAmount": 166666.67,
      "principalPortion": 146666.67,
      "interestPortion": 20000.00,
      "remainingBalance": 1453333.33,
      "lateFee": null,
      "currency": "TZS",
      "paymentStatus": "COMPLETED",
      "paymentStatusDisplay": "COMPLETED",
      "dueDate": "2025-11-18T00:00:00",
      "paidAt": "2025-11-18T10:30:00",
      "attemptedAt": "2025-11-18T10:29:45",
      "paymentMethod": "WALLET",
      "transactionId": "TXN-2025-67890",
      "failureReason": null,
      "retryCount": 0,
      "daysUntilDue": null,
      "daysOverdue": null,
      "canPay": false,
      "canRetry": false
    },
    {
      "paymentId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
      "paymentNumber": 2,
      "scheduledAmount": 166666.67,
      "paidAmount": null,
      "principalPortion": 148533.33,
      "interestPortion": 18133.34,
      "remainingBalance": 1304800.00,
      "lateFee": null,
      "currency": "TZS",
      "paymentStatus": "SCHEDULED",
      "paymentStatusDisplay": "SCHEDULED",
      "dueDate": "2025-12-18T00:00:00",
      "paidAt": null,
      "attemptedAt": null,
      "paymentMethod": null,
      "transactionId": null,
      "failureReason": null,
      "retryCount": 0,
      "daysUntilDue": 61,
      "daysOverdue": null,
      "canPay": false,
      "canRetry": false
    }
  ]
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| paymentId | Unique identifier for the payment |
| paymentNumber | Sequential payment number (1, 2, 3...) |
| scheduledAmount | Amount scheduled to be paid |
| paidAmount | Amount actually paid (null if not paid) |
| principalPortion | Amount going toward principal |
| interestPortion | Amount going toward interest |
| remainingBalance | Balance after this payment |
| lateFee | Late fee if applicable |
| currency | Currency code |
| paymentStatus | Current payment status |
| paymentStatusDisplay | Human-readable status |
| dueDate | When payment is due |
| paidAt | When payment was made (null if not paid) |
| attemptedAt | Last payment attempt timestamp |
| paymentMethod | Payment method used |
| transactionId | Transaction reference ID |
| status | Payment status after processing |
| processedAt | When payment was processed |
| message | Success message |
| agreementUpdate | Updated agreement information |
| agreementUpdate.paymentsCompleted | Updated number of completed payments |
| agreementUpdate.paymentsRemaining | Updated number of remaining payments |
| agreementUpdate.amountPaid | Updated total amount paid |
| agreementUpdate.amountRemaining | Updated remaining amount |
| agreementUpdate.nextPaymentDate | Next payment due date |
| agreementUpdate.nextPaymentAmount | Next payment amount |
| agreementUpdate.agreementStatus | Updated agreement status |
| agreementUpdate.isCompleted | Whether agreement is now completed |

**Error Response Examples**:

*Bad Request - Insufficient Balance (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Insufficient wallet balance. Required: 166666.67 TZS, Available: 50000.00 TZS. Please top up your wallet before the next payment attempt.",
  "action_time": "2025-10-18T10:30:45",
  "data": "Insufficient wallet balance. Required: 166666.67 TZS, Available: 50000.00 TZS. Please top up your wallet before the next payment attempt."
}
```

*Bad Request - Payment Already Completed (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Payment is already completed",
  "action_time": "2025-10-18T10:30:45",
  "data": "Payment is already completed"
}
```

*Bad Request - Payment Not Due (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Payment is not due yet. Due date: 2025-12-18T00:00:00",
  "action_time": "2025-10-18T10:30:45",
  "data": "Payment is not due yet. Due date: 2025-12-18T00:00:00"
}
```

---


## 6. Get Upcoming Payments
**Purpose**: Retrieve all upcoming payments across all active agreements for the authenticated customer

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/upcoming-payments`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Upcoming payments retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "paymentId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
      "paymentNumber": 2,
      "scheduledAmount": 166666.67,
      "paidAmount": null,
      "principalPortion": 148533.33,
      "interestPortion": 18133.34,
      "remainingBalance": 1304800.00,
      "lateFee": null,
      "currency": "TZS",
      "paymentStatus": "SCHEDULED",
      "paymentStatusDisplay": "SCHEDULED",
      "dueDate": "2025-12-18T00:00:00",
      "paidAt": null,
      "attemptedAt": null,
      "paymentMethod": null,
      "transactionId": null,
      "failureReason": null,
      "retryCount": 0,
      "daysUntilDue": 61,
      "daysOverdue": null,
      "canPay": false,
      "canRetry": false
    }
  ]
}
```

**Success Response Fields**:
Same as Get Agreement Payments endpoint

**Error Response Examples**:
Standard authentication errors only

---

## 7. Make Manual Payment
**Purpose**: Process a manual payment for a specific scheduled installment

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/agreements/{agreementId}/payments/{paymentId}/pay`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |
| paymentId | UUID | Yes | Unique identifier of the payment | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Payment processed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "paymentId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
    "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "agreementNumber": "INST-2025-12345",
    "amount": 166666.67,
    "currency": "TZS",
    "paymentMethod": "WALLET",
    "transactionId": "TXN-2025-67891",
    "status": "COMPLETED",
    "processedAt": "2025-10-18T10:30:45",
    "message": "Payment processed successfully",
    "agreementUpdate": {
      "paymentsCompleted": 2,
      "paymentsRemaining": 10,
      "amountPaid": 733333.34,
      "amountRemaining": 1666666.66,
      "nextPaymentDate": "2026-01-18T00:00:00",
      "nextPaymentAmount": 166666.67,
      "agreementStatus": "ACTIVE",
      "isCompleted": false
    }
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| paymentId | ID of the processed payment |
| agreementId | ID of the agreement |
| agreementNumber | Agreement number |
| amount | Amount paid |
| currency | Currency code |
| paymentMethod | Payment method used |
| transaction

## 8. Retry Failed Payment
**Purpose**: Retry a failed payment that has not exceeded maximum retry attempts

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/payments/{paymentId}/retry`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| paymentId | UUID | Yes | Unique identifier of the payment to retry | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Payment retry processed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "paymentId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
    "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "agreementNumber": "INST-2025-12345",
    "amount": 166666.67,
    "currency": "TZS",
    "paymentMethod": "WALLET",
    "transactionId": "TXN-2025-67892",
    "status": "COMPLETED",
    "processedAt": "2025-10-18T10:30:45",
    "message": "Payment retry successful",
    "agreementUpdate": {
      "paymentsCompleted": 2,
      "paymentsRemaining": 10,
      "amountPaid": 733333.34,
      "amountRemaining": 1666666.66,
      "nextPaymentDate": "2026-01-18T00:00:00",
      "nextPaymentAmount": 166666.67,
      "agreementStatus": "ACTIVE",
      "isCompleted": false
    }
  }
}
```

**Success Response Fields**:
Same as Make Manual Payment endpoint

**Error Response Examples**:

*Bad Request - Cannot Retry (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Payment cannot be retried",
  "action_time": "2025-10-18T10:30:45",
  "data": "Payment cannot be retried"
}
```

*Bad Request - Max Retries Exceeded (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Maximum retry attempts (5) exceeded",
  "action_time": "2025-10-18T10:30:45",
  "data": "Maximum retry attempts (5) exceeded"
}
```

---
## 9. Preview Flexible Payment
**Purpose**: Calculate and preview how a flexible payment amount will be distributed across installment payments before processing

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/installments/agreements/{agreementId}/early-flexible-payment/preview`

**Access Level**: 🔒 Protected (Requires Authentication)

**Authentication**: Bearer Token (JWT)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | The ID of the installment agreement | Must be a valid UUID, agreement must belong to authenticated user |

**Request JSON Sample**:
```json
{
  "amount": 500000.00
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| amount | decimal | Yes | Amount the customer wants to pay | Must be > 0, cannot exceed remaining balance |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Flexible payment preview calculated successfully",
  "action_time": "2025-11-06T10:30:45",
  "data": {
    "requestedAmount": 500000.00,
    "minimumRequired": 150000.00,
    "maximumAllowed": 1200000.00,
    "isValid": true,
    "validationMessage": null,
    "impactedPayments": [
      {
        "paymentNumber": 3,
        "dueDate": "2025-12-01T00:00:00",
        "scheduledAmount": 200000.00,
        "currentPaid": 50000.00,
        "willApply": 150000.00,
        "willRemain": 0.00,
        "resultStatus": "Will be COMPLETED"
      },
      {
        "paymentNumber": 4,
        "dueDate": "2026-01-01T00:00:00",
        "scheduledAmount": 200000.00,
        "currentPaid": 0.00,
        "willApply": 200000.00,
        "willRemain": 0.00,
        "resultStatus": "Will be COMPLETED"
      },
      {
        "paymentNumber": 5,
        "dueDate": "2026-02-01T00:00:00",
        "scheduledAmount": 200000.00,
        "currentPaid": 0.00,
        "willApply": 150000.00,
        "willRemain": 50000.00,
        "resultStatus": "Will be PARTIALLY_PAID"
      }
    ],
    "paymentsWillComplete": 2,
    "paymentsWillBePartial": 1,
    "remainingAfter": 700000.00
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| requestedAmount | The amount customer wants to pay |
| minimumRequired | Minimum payment required (next incomplete payment amount) |
| maximumAllowed | Maximum payment allowed (total remaining balance) |
| isValid | Whether the requested amount is valid |
| validationMessage | Error message if amount is invalid, null if valid |
| impactedPayments | Array of payments that will be affected by this payment |
| impactedPayments[].paymentNumber | Sequential payment number |
| impactedPayments[].dueDate | When this payment is due |
| impactedPayments[].scheduledAmount | Original scheduled amount for this payment |
| impactedPayments[].currentPaid | Amount already paid towards this payment |
| impactedPayments[].willApply | How much of the flexible payment will apply here |
| impactedPayments[].willRemain | How much will remain unpaid after applying |
| impactedPayments[].resultStatus | Final status: "Will be COMPLETED" or "Will be PARTIALLY_PAID" |
| paymentsWillComplete | Number of payments that will be fully completed |
| paymentsWillBePartial | Number of payments that will be partially paid |
| remainingAfter | Total remaining balance after this payment |

**Error Response Examples**:

*Bad Request - Amount Too Low (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Minimum payment required: 150000.00 TZS",
  "action_time": "2025-11-06T10:30:45",
  "data": "Minimum payment required: 150000.00 TZS"
}
```

*Bad Request - Amount Too High (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Payment amount exceeds remaining balance. Use early payoff endpoint if paying off completely.",
  "action_time": "2025-11-06T10:30:45",
  "data": "Payment amount exceeds remaining balance. Use early payoff endpoint if paying off completely."
}
```

*Forbidden - Not Agreement Owner (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not have access to this agreement",
  "action_time": "2025-11-06T10:30:45",
  "data": "You do not have access to this agreement"
}
```

*Not Found - Agreement Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Agreement not found",
  "action_time": "2025-11-06T10:30:45",
  "data": "Agreement not found"
}
```

*Validation Error - Invalid Amount (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-11-06T10:30:45",
  "data": {
    "amount": "must be greater than 0"
  }
}
```

---

## 10. Process Flexible Payment
**Purpose**: Process a flexible payment that can pay multiple installments or partially pay upcoming installments

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/installments/agreements/{agreementId}/early-flexible-payment`

**Access Level**: 🔒 Protected (Requires Authentication, Sufficient Wallet Balance)

**Authentication**: Bearer Token (JWT)

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | Must be `application/json` |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | The ID of the installment agreement | Must be a valid UUID, agreement must belong to authenticated user |

**Request JSON Sample**:
```json
{
  "amount": 500000.00,
  "note": "Paying 3 months ahead"
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| amount | decimal | Yes | Amount to pay | Must be > 0, between minimumRequired and maximumAllowed |
| note | string | No | Optional note about the payment | Max 500 characters |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Successfully paid 2 installments and partially paid 1 more",
  "action_time": "2025-11-06T10:30:45",
  "data": {
    "agreementId": "a8b3c4d5-e6f7-8901-2345-6789abcdef01",
    "agreementNumber": "INST-2025-12345",
    "totalAmountPaid": 500000.00,
    "currency": "TZS",
    "transactionId": "TXN-2025-67890",
    "processedAt": "2025-11-06T10:30:45",
    "paymentsAffected": [
      {
        "paymentId": "p1a2b3c4-d5e6-f789-0123-456789abcdef",
        "paymentNumber": 3,
        "dueDate": "2025-12-01T00:00:00",
        "scheduledAmount": 200000.00,
        "amountApplied": 150000.00,
        "previouslyPaid": 50000.00,
        "newPaidAmount": 200000.00,
        "remaining": 0.00,
        "status": "COMPLETED",
        "wasCompleted": true
      },
      {
        "paymentId": "p2a3b4c5-d6e7-f890-1234-56789abcdef0",
        "paymentNumber": 4,
        "dueDate": "2026-01-01T00:00:00",
        "scheduledAmount": 200000.00,
        "amountApplied": 200000.00,
        "previouslyPaid": 0.00,
        "newPaidAmount": 200000.00,
        "remaining": 0.00,
        "status": "COMPLETED",
        "wasCompleted": true
      },
      {
        "paymentId": "p3a4b5c6-d7e8-f901-2345-6789abcdef01",
        "paymentNumber": 5,
        "dueDate": "2026-02-01T00:00:00",
        "scheduledAmount": 200000.00,
        "amountApplied": 150000.00,
        "previouslyPaid": 0.00,
        "newPaidAmount": 150000.00,
        "remaining": 50000.00,
        "status": "PARTIALLY_PAID",
        "wasCompleted": false
      }
    ],
    "agreementUpdate": {
      "paymentsCompleted": 4,
      "paymentsPartial": 1,
      "paymentsRemaining": 8,
      "amountPaid": 1300000.00,
      "amountRemaining": 700000.00,
      "nextPaymentDate": "2026-02-01T00:00:00",
      "nextPaymentAmount": 50000.00,
      "agreementStatus": "ACTIVE",
      "isCompleted": false
    },
    "message": "Successfully paid 2 installments and partially paid 1 more"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| agreementId | ID of the installment agreement |
| agreementNumber | Human-readable agreement number |
| totalAmountPaid | Total amount paid in this transaction |
| currency | Currency code (TZS) |
| transactionId | Unique transaction identifier from ledger system |
| processedAt | Timestamp when payment was processed |
| paymentsAffected | Array of payments that were affected |
| paymentsAffected[].paymentId | ID of the affected payment |
| paymentsAffected[].paymentNumber | Sequential payment number |
| paymentsAffected[].dueDate | When this payment is due |
| paymentsAffected[].scheduledAmount | Original scheduled amount |
| paymentsAffected[].amountApplied | How much of flexible payment was applied here |
| paymentsAffected[].previouslyPaid | Amount that was previously paid |
| paymentsAffected[].newPaidAmount | Total amount now paid for this payment |
| paymentsAffected[].remaining | Amount still remaining for this payment |
| paymentsAffected[].status | New payment status (COMPLETED or PARTIALLY_PAID) |
| paymentsAffected[].wasCompleted | Whether this payment became fully completed |
| agreementUpdate | Updated agreement summary |
| agreementUpdate.paymentsCompleted | Total number of completed payments |
| agreementUpdate.paymentsPartial | Number of partially paid payments |
| agreementUpdate.paymentsRemaining | Number of payments still remaining |
| agreementUpdate.amountPaid | Total amount paid on agreement |
| agreementUpdate.amountRemaining | Total amount still remaining |
| agreementUpdate.nextPaymentDate | When next payment is due |
| agreementUpdate.nextPaymentAmount | Amount of next payment (remaining amount) |
| agreementUpdate.agreementStatus | Current agreement status |
| agreementUpdate.isCompleted | Whether agreement is fully completed |
| message | Human-readable success message |

**Error Response Examples**:

*Bad Request - Insufficient Balance (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Insufficient wallet balance. Required: 500000.00 TZS, Available: 300000.00 TZS",
  "action_time": "2025-11-06T10:30:45",
  "data": "Insufficient wallet balance. Required: 500000.00 TZS, Available: 300000.00 TZS"
}
```

*Bad Request - Agreement Not Active (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot make payment on inactive agreement. Status: COMPLETED",
  "action_time": "2025-11-06T10:30:45",
  "data": "Cannot make payment on inactive agreement. Status: COMPLETED"
}
```

*Bad Request - Wallet Not Active (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Wallet is not active",
  "action_time": "2025-11-06T10:30:45",
  "data": "Wallet is not active"
}
```

*Validation Error (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-11-06T10:30:45",
  "data": {
    "amount": "must be greater than 0",
    "note": "size must be between 0 and 500"
  }
}
```

---

## 11. Calculate Early Payoff
**Purpose**: Calculate the amount required to pay off the entire agreement early with interest discount

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/agreements/{agreementId}/early-payoff`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Early payoff calculation completed",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "paymentsCompleted": 2,
    "paymentsRemaining": 10,
    "amountPaid": 733333.34,
    "remainingPrincipal": 1320000.00,
    "unaccruedInterest": 346666.66,
    "interestRebate": 260000.00,
    "payoffWithRebate": 1406666.66,
    "payoffWithoutRebate": 1666666.66,
    "savingsVsScheduled": 260000.00,
    "rebatePolicy": "75% discount on remaining interest for early payoff",
    "calculatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| agreementId | ID of the agreement |
| paymentsCompleted | Number of payments already made |
| paymentsRemaining | Number of payments left |
| amountPaid | Total amount paid so far |
| remainingPrincipal | Principal amount remaining |
| unaccruedInterest | Interest not yet charged |
| interestRebate | Interest discount amount (75% of remaining interest) |
| payoffWithRebate | Early payoff amount with discount applied |
| payoffWithoutRebate | Full remaining amount if paid on schedule |
| savingsVsScheduled | Amount saved by paying off early |
| rebatePolicy | Description of rebate policy |
| calculatedAt | When calculation was performed |

**Error Response Examples**:

*Bad Request - Not Eligible (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Early payoff not available for this agreement",
  "action_time": "2025-10-18T10:30:45",
  "data": "Early payoff not available for this agreement"
}
```

*Bad Request - Agreement Not Active (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Agreement is not active. Status: COMPLETED",
  "action_time": "2025-10-18T10:30:45",
  "data": "Agreement is not active. Status: COMPLETED"
}
```

---

## 12. Process Early Payoff
**Purpose**: Process full early payment of the agreement with interest discount applied

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/agreements/{agreementId}/early-payoff`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Early payoff processed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "paymentId": null,
    "agreementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "agreementNumber": "INST-2025-12345",
    "amount": 1406666.66,
    "currency": "TZS",
    "paymentMethod": "WALLET",
    "transactionId": null,
    "status": "COMPLETED",
    "processedAt": "2025-10-18T10:30:45",
    "message": "Early payoff processed successfully",
    "agreementUpdate": {
      "paymentsCompleted": 12,
      "paymentsRemaining": 0,
      "amountPaid": 2400000.00,
      "amountRemaining": 0.00,
      "nextPaymentDate": null,
      "nextPaymentAmount": null,
      "agreementStatus": "COMPLETED",
      "isCompleted": true
    }
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| paymentId | Payment ID (null for early payoff) |
| agreementId | ID of the agreement |
| agreementNumber | Agreement number |
| amount | Total early payoff amount paid |
| currency | Currency code |
| paymentMethod | Payment method used |
| transactionId | Transaction reference (null for early payoff) |
| status | Payment status (COMPLETED) |
| processedAt | When payoff was processed |
| message | Success message |
| agreementUpdate | Updated agreement information |
| agreementUpdate.paymentsCompleted | Updated to total number of payments |
| agreementUpdate.paymentsRemaining | Set to 0 |
| agreementUpdate.amountPaid | Updated to total amount |
| agreementUpdate.amountRemaining | Set to 0 |
| agreementUpdate.nextPaymentDate | Set to null |
| agreementUpdate.nextPaymentAmount | Set to null |
| agreementUpdate.agreementStatus | Set to COMPLETED |
| agreementUpdate.isCompleted | Set to true |

**Error Response Examples**:

*Bad Request - Insufficient Balance (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Insufficient wallet balance for early payoff. Required: 1406666.66 TZS, Available: 500000.00 TZS",
  "action_time": "2025-10-18T10:30:45",
  "data": "Insufficient wallet balance for early payoff. Required: 1406666.66 TZS, Available: 500000.00 TZS"
}
```

*Bad Request - Not Eligible (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Agreement is not active. Status: DEFAULTED",
  "action_time": "2025-10-18T10:30:45",
  "data": "Agreement is not active. Status: DEFAULTED"
}
```

---

## 13. Cancel Agreement
**Purpose**: Cancel an installment agreement before any payments have been completed

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/agreements/{agreementId}/cancel`

**Access Level**: 🔒 Protected (Requires Authentication, Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | application/json |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| agreementId | UUID | Yes | Unique identifier of the agreement | Valid UUID format |

**Request JSON Sample**:
```json
{
  "reason": "Changed my mind about the purchase. Found a better deal elsewhere."
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| reason | string | Yes | Reason for cancellation | Min: 1, Max: 500 characters |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Agreement cancelled successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": null
}
```

**Success Response Fields**:
No data returned on successful cancellation

**Error Response Examples**:

*Bad Request - Cannot Cancel (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Agreement cannot be cancelled. Only agreements with no completed payments can be cancelled.",
  "action_time": "2025-10-18T10:30:45",
  "data": "Agreement cannot be cancelled. Only agreements with no completed payments can be cancelled."
}
```

*Validation Error (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "reason": "Cancellation reason is required"
  }
}
```

---

## Quick Reference Guide

### HTTP Method Badge Code Templates

**GET Badge:**
```html
<span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span>
```

**POST Badge:**
```html
<span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span>
```

### Common HTTP Status Codes
- `200 OK`: Successful GET/POST request
- `400 Bad Request`: Invalid request data or business logic violation
- `401 Unauthorized`: Authentication required/failed
- `403 Forbidden`: Insufficient permissions
- `404 Not Found`: Resource not found
- `422 Unprocessable Entity`: Validation errors
- `500 Internal Server Error`: Server error

### Authentication
- **Bearer Token**: Include `Authorization: Bearer your_token` in headers
- All customer endpoints require valid authentication
- Token must belong to the customer who owns the agreement

### Agreement Status Flow
1. **PENDING_FIRST_PAYMENT**: Down payment made, waiting for grace period
2. **ACTIVE**: Currently paying installments
3. **COMPLETED**: Fully paid off
4. **DEFAULTED**: Missed 2+ payments, in collections
5. **CANCELLED**: User cancelled before first payment

### Payment Status Types
- **SCHEDULED**: Not due yet
- **PENDING**: Due today, awaiting payment
- **PROCESSING**: Payment in progress
- **COMPLETED**: Successfully paid
- **FAILED**: Payment attempt failed
- **LATE**: Past due date
- **SKIPPED**: Missed completely
- **WAIVED**: Forgiven (special cases)

### Business Rules
- **Early Payoff Discount**: 75% off remaining interest
- **Maximum Retries**: 5 attempts per payment
- **Default Threshold**: 2 missed payments triggers DEFAULTED status
- **Cancellation Window**: Only before first payment completed
- **Payment Method**: Wallet payments only
- **Currency**: All amounts in TZS (Tanzanian Shillings)

### Common Error Scenarios
1. **Insufficient Wallet Balance**: Top up wallet before payment
2. **Payment Not Due**: Wait until due date or make early payoff
3. **Agreement Defaulted**: Contact support for resolution
4. **Max Retries Exceeded**: Contact support
5. **Ownership Validation Failed**: Can only access own agreements

---

## Notes for Developers

### Idempotency
- Payment processing endpoints are idempotent
- Duplicate payment requests will return existing payment status
- Use transaction IDs to track payment processing

### Rate Limiting
- Standard rate limits apply: 100 requests per hour per user
- Payment processing has additional throttling for security

### Webhooks
- Payment success/failure events trigger notifications
- Agreement completion triggers order creation (for AFTER_PAYMENT fulfillment)
- Integration with notification service for email/SMS alerts

### Testing
- Use sandbox environment for testing: `https://sandbox-api.nextgate.com`
- Test user accounts available with pre-loaded wallets
- Mock payment processing for integration testing

### Support
- For API issues: api-support@nextgate.com
- For business logic questions: product@nextgate.com
- Emergency contact: +255-XXX-XXX-XXXId | Transaction reference ID |
| failureReason | Reason if payment failed |
| retryCount | Number of retry attempts |
| daysUntilDue | Days until payment is due (negative if overdue) |
| daysOverdue | Days payment is overdue (null if not overdue) |
| canPay | Whether payment can be made now |
| canRetry | Whether payment can be retried |
---

# Installment Purchase  - Public & Plan Endpoints

**Author**: Josh S. Sakweli, Backend Lead Team  
**Last Updated**: 2025-10-18  
**Version**: v1.0

**Base URL**: `https://api.nextgate.com/api/v1/installments`

**Short Description**: This API provides public endpoints for browsing installment plans and calculating installment previews before checkout. These endpoints do not require authentication and are designed to help customers explore installment options, understand payment breakdowns, and make informed purchase decisions. The endpoints return detailed financial calculations including amortization schedules, interest breakdowns, and payment timelines.

**Hints**: 
- Public endpoints do not require authentication
- Plans are filtered to show only active plans to customers
- Preview calculations use amortization formula for accurate payment schedules
- Early payoff discount (75% off remaining interest) is standard across all plans
- Down payment range: minimum set by plan (typically 10-20%), maximum 50% (platform limit)
- All financial calculations rounded to 2 decimal places
- Grace periods determine when first payment is due
- Fulfillment timing (IMMEDIATE vs AFTER_PAYMENT) affects when product ships
- APR (Annual Percentage Rate) converted to period rate based on payment frequency
- All amounts are in TZS (Tanzanian Shillings)

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    // Actual response data goes here
  }
}
```

### Error Response Structure
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-10-18T10:30:45",
  "data": "Error description"
}
```

### Standard Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` for successful operations, `false` for errors |
| `httpStatus` | string | HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.) |
| `message` | string | Human-readable message describing the operation result |
| `action_time` | string | ISO 8601 timestamp of when the response was generated |
| `data` | object/string | Response payload for success, error details for failures |

---

## HTTP Method Badge Standards

For better visual clarity, all endpoints use colored badges for HTTP methods with the following standard colors:

- **GET** - <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> - Green (Safe, read-only operations)
- **POST** - <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> - Blue (Create new resources)

---

## Endpoints

## 1. Get Available Plans for Product
**Purpose**: Retrieve all active installment plans available for a specific product

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/products/{productId}/plans`

**Access Level**: 🌐 Public (No Authentication Required)

**Authentication**: None

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Available installment plans retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "planId": "4b5c6d7e-8f9a-4b1c-9d2e-3f4a5b6c7d8e",
      "planName": "Quick Payment Plan",
      "paymentFrequency": "WEEKLY",
      "paymentFrequencyDisplay": "Weekly",
      "customFrequencyDays": null,
      "numberOfPayments": 8,
      "duration": "8 weeks",
      "apr": 10.00,
      "minDownPaymentPercent": 20,
      "gracePeriodDays": 7,
      "fulfillmentTiming": "IMMEDIATE",
      "isActive": true,
      "isFeatured": false,
      "displayOrder": 1,
      "preview": {
        "productPrice": 2000000.00,
        "minDownPaymentAmount": 400000.00,
        "maxDownPaymentAmount": 1000000.00,
        "financedAmountExample": 1600000.00,
        "paymentAmountExample": 210000.00,
        "totalInterestExample": 80000.00,
        "totalCostExample": 2080000.00,
        "firstPaymentDateExample": "2025-10-25T00:00:00",
        "lastPaymentDateExample": "2025-12-13T00:00:00"
      }
    },
    {
      "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
      "planName": "Standard Monthly Plan",
      "paymentFrequency": "MONTHLY",
      "paymentFrequencyDisplay": "Monthly",
      "customFrequencyDays": null,
      "numberOfPayments": 12,
      "duration": "12 months",
      "apr": 15.00,
      "minDownPaymentPercent": 15,
      "gracePeriodDays": 30,
      "fulfillmentTiming": "IMMEDIATE",
      "isActive": true,
      "isFeatured": true,
      "displayOrder": 2,
      "preview": {
        "productPrice": 2000000.00,
        "minDownPaymentAmount": 300000.00,
        "maxDownPaymentAmount": 1000000.00,
        "financedAmountExample": 1700000.00,
        "paymentAmountExample": 156250.00,
        "totalInterestExample": 175000.00,
        "totalCostExample": 2175000.00,
        "firstPaymentDateExample": "2025-11-17T00:00:00",
        "lastPaymentDateExample": "2026-10-17T00:00:00"
      }
    },
    {
      "planId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
      "planName": "Budget Friendly Plan",
      "paymentFrequency": "MONTHLY",
      "paymentFrequencyDisplay": "Monthly",
      "customFrequencyDays": null,
      "numberOfPayments": 24,
      "duration": "24 months",
      "apr": 18.00,
      "minDownPaymentPercent": 10,
      "gracePeriodDays": 30,
      "fulfillmentTiming": "AFTER_PAYMENT",
      "isActive": true,
      "isFeatured": false,
      "displayOrder": 3,
      "preview": {
        "productPrice": 2000000.00,
        "minDownPaymentAmount": 200000.00,
        "maxDownPaymentAmount": 1000000.00,
        "financedAmountExample": 1800000.00,
        "paymentAmountExample": 87500.00,
        "totalInterestExample": 300000.00,
        "totalCostExample": 2300000.00,
        "firstPaymentDateExample": "2025-11-17T00:00:00",
        "lastPaymentDateExample": "2027-10-17T00:00:00"
      }
    }
  ]
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | Unique identifier for the plan |
| planName | Display name of the plan |
| paymentFrequency | Payment frequency enum (DAILY, WEEKLY, BI_WEEKLY, SEMI_MONTHLY, MONTHLY, QUARTERLY, CUSTOM_DAYS) |
| paymentFrequencyDisplay | Human-readable payment frequency |
| customFrequencyDays | Custom frequency in days (only for CUSTOM_DAYS type) |
| numberOfPayments | Total number of payments in the plan |
| duration | Human-readable duration (e.g., "12 months", "8 weeks") |
| apr | Annual Percentage Rate |
| minDownPaymentPercent | Minimum down payment percentage required by this plan |
| gracePeriodDays | Days before first payment is due |
| fulfillmentTiming | When product ships (IMMEDIATE = after down payment, AFTER_PAYMENT = after final payment) |
| isActive | Whether plan is currently active |
| isFeatured | Whether plan is featured/recommended (shows "Most Popular" badge) |
| displayOrder | Order for display on product page |
| preview | Preview calculations object |
| preview.productPrice | Product price used for calculations |
| preview.minDownPaymentAmount | Minimum down payment amount |
| preview.maxDownPaymentAmount | Maximum down payment amount (platform limit: 50%) |
| preview.financedAmountExample | Amount being financed (at minimum down payment) |
| preview.paymentAmountExample | Each installment amount (at minimum down payment) |
| preview.totalInterestExample | Total interest to be paid (at minimum down payment) |
| preview.totalCostExample | Grand total cost including interest (at minimum down payment) |
| preview.firstPaymentDateExample | When first payment would be due |
| preview.lastPaymentDateExample | When last payment would be due |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "action_time": "2025-10-18T10:30:45",
  "data": "Product not found with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
```

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `404 NOT_FOUND`: Product not found or product does not have installment enabled

**Error Response Examples**:

*Not Found - Product (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "action_time": "2025-10-18T10:30:45",
  "data": "Product not found with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
```

**Special Cases**:
- If product has installment disabled, returns empty array
- If product has no active plans, returns empty array
- Plans are automatically sorted by displayOrder

---

## 2. Calculate Installment Preview
**Purpose**: Calculate detailed payment breakdown and schedule for a specific plan with customer's chosen down payment percentage

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}/calculate-preview`

**Access Level**: 🌐 Public (No Authentication Required)

**Authentication**: None

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Content-Type | string | Yes | application/json |

**Request JSON Sample**:
```json
{
  "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
  "productPrice": 2000000.00,
  "quantity": 1,
  "downPaymentPercent": 20
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| planId | UUID | Yes | ID of the installment plan to calculate | Valid UUID format, plan must exist and be active |
| productPrice | decimal | Yes | Price of the product | Min: 0.01, Max: 999999999.99 |
| quantity | integer | Yes | Quantity of product | Min: 1, Max: 1 (installment limited to 1 item) |
| downPaymentPercent | integer | Yes | Down payment percentage customer chooses | Min: 10, Max: 50, must be >= plan's minDownPaymentPercent |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment preview calculated successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "planName": "Standard Monthly Plan",
    "planDescription": "Pay in 12 monthly installments at 15.0% APR",
    "paymentFrequency": "Monthly",
    "numberOfPayments": 12,
    "durationDisplay": "12 months",
    "apr": 15.00,
    "gracePeriodDays": 30,
    "productPrice": 2000000.00,
    "quantity": 1,
    "totalProductCost": 2000000.00,
    "downPaymentPercent": 20,
    "downPaymentAmount": 400000.00,
    "minDownPaymentPercent": 15,
    "maxDownPaymentPercent": 50,
    "minDownPaymentAmount": 300000.00,
    "maxDownPaymentAmount": 1000000.00,
    "financedAmount": 1600000.00,
    "monthlyPaymentAmount": 146666.67,
    "totalInterestAmount": 160000.00,
    "totalAmount": 2160000.00,
    "currency": "TZS",
    "firstPaymentDate": "2025-11-17T00:00:00",
    "lastPaymentDate": "2026-10-17T00:00:00",
    "schedule": [
      {
        "paymentNumber": 1,
        "dueDate": "2025-11-17T00:00:00",
        "amount": 146666.67,
        "principalPortion": 126666.67,
        "interestPortion": 20000.00,
        "remainingBalance": 1473333.33,
        "description": "Month 1 payment"
      },
      {
        "paymentNumber": 2,
        "dueDate": "2025-12-17T00:00:00",
        "amount": 146666.67,
        "principalPortion": 128250.00,
        "interestPortion": 18416.67,
        "remainingBalance": 1345083.33,
        "description": "Month 2 payment"
      },
      {
        "paymentNumber": 3,
        "dueDate": "2026-01-17T00:00:00",
        "amount": 146666.67,
        "principalPortion": 129853.13,
        "interestPortion": 16813.54,
        "remainingBalance": 1215230.20,
        "description": "Month 3 payment"
      },
      {
        "paymentNumber": 12,
        "dueDate": "2026-10-17T00:00:00",
        "amount": 146666.67,
        "principalPortion": 144833.33,
        "interestPortion": 1833.34,
        "remainingBalance": 0.00,
        "description": "Final payment"
      }
    ],
    "comparison": {
      "payingUpfront": 2000000.00,
      "payingWithInstallment": 2160000.00,
      "additionalCost": 160000.00,
      "additionalCostPercent": 8.00
    },
    "fulfillmentTiming": "IMMEDIATE",
    "fulfillmentDescription": "Product ships immediately after down payment"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | ID of the plan used |
| planName | Name of the plan |
| planDescription | Description of the plan |
| paymentFrequency | Human-readable payment frequency |
| numberOfPayments | Total number of payments |
| durationDisplay | Human-readable duration |
| apr | Annual Percentage Rate |
| gracePeriodDays | Grace period before first payment |
| productPrice | Per-unit product price |
| quantity | Quantity purchased |
| totalProductCost | Total product cost (price × quantity) |
| downPaymentPercent | Chosen down payment percentage |
| downPaymentAmount | Calculated down payment amount |
| minDownPaymentPercent | Minimum allowed down payment % |
| maxDownPaymentPercent | Maximum allowed down payment % (50%) |
| minDownPaymentAmount | Minimum down payment amount |
| maxDownPaymentAmount | Maximum down payment amount |
| financedAmount | Amount being financed (after down payment) |
| monthlyPaymentAmount | Each installment payment amount |
| totalInterestAmount | Total interest to be paid |
| totalAmount | Grand total (product + interest) |
| currency | Currency code (TZS) |
| firstPaymentDate | When first payment is due |
| lastPaymentDate | When last payment is due |
| schedule | Array of payment schedule items |
| schedule[].paymentNumber | Payment number (1, 2, 3...) |
| schedule[].dueDate | When payment is due |
| schedule[].amount | Payment amount |
| schedule[].principalPortion | Amount going to principal |
| schedule[].interestPortion | Amount going to interest |
| schedule[].remainingBalance | Balance after this payment |
| schedule[].description | Payment description |
| comparison | Comparison information object |
| comparison.payingUpfront | Cost if paying full price now |
| comparison.payingWithInstallment | Total cost with installment |
| comparison.additionalCost | Extra cost (interest) |
| comparison.additionalCostPercent | Interest as % of product price |
| fulfillmentTiming | When product ships |
| fulfillmentDescription | Description of fulfillment timing |

**Error Response JSON Sample**:
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Down payment must be at least 15% for this plan",
  "action_time": "2025-10-18T10:30:45",
  "data": "Down payment must be at least 15% for this plan"
}
```

**Standard Error Types**:

### Application-Level Exceptions (400-499)
- `400 BAD_REQUEST`: Invalid down payment percentage, inactive plan, or business rule violation
- `404 NOT_FOUND`: Plan not found
- `422 UNPROCESSABLE_ENTITY`: Validation errors on request parameters

**Error Response Examples**:

*Bad Request - Down Payment Too Low (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Down payment must be at least 15% for this plan",
  "action_time": "2025-10-18T10:30:45",
  "data": "Down payment must be at least 15% for this plan"
}
```

*Bad Request - Down Payment Too High (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Down payment cannot exceed 50%",
  "action_time": "2025-10-18T10:30:45",
  "data": "Down payment cannot exceed 50%"
}
```

*Bad Request - Plan Not Active (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "This installment plan is not currently available",
  "action_time": "2025-10-18T10:30:45",
  "data": "This installment plan is not currently available"
}
```

*Not Found - Plan (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Installment plan not found with ID: 5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
  "action_time": "2025-10-18T10:30:45",
  "data": "Installment plan not found with ID: 5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f"
}
```

*Validation Error (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "Plan ID is required",
    "productPrice": "Product price must be greater than 0",
    "downPaymentPercent": "must be between 10 and 50"
  }
}
```

---

## Quick Reference Guide

### HTTP Method Badge Code Templates

**GET Badge:**
```html
<span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span>
```

**POST Badge:**
```html
<span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span>
```

### Common HTTP Status Codes
- `200 OK`: Successful GET/POST request
- `400 Bad Request`: Invalid request data or business logic violation
- `404 Not Found`: Resource not found
- `422 Unprocessable Entity`: Validation errors
- `500 Internal Server Error`: Server error

### Payment Frequency Types
- **DAILY**: Payment every day
- **WEEKLY**: Payment every week (7 days)
- **BI_WEEKLY**: Payment every 2 weeks (14 days)
- **SEMI_MONTHLY**: Payment twice per month (1st and 15th)
- **MONTHLY**: Payment every month (30 days)
- **QUARTERLY**: Payment every 3 months (90 days)
- **CUSTOM_DAYS**: Payment at custom interval (specified in customFrequencyDays)

### Fulfillment Timing Options
- **IMMEDIATE**: Product ships immediately after down payment is made. Customer gets product while paying installments.
- **AFTER_PAYMENT**: Product ships only after final payment is completed (layaway model). Inventory is held during payment period.

### Down Payment Rules
- **Minimum**: Set by individual plan (typically 10-20%)
- **Maximum**: 50% (platform-wide limit)
- **Validation**: Customer's choice must be within min-max range
- **Purpose**: Reduces financed amount and total interest paid

### APR (Annual Percentage Rate)
- Expressed as percentage (e.g., 15.00 = 15%)
- Range: 0% to 36% (platform limits)
- Converted to period rate based on payment frequency
- Used in amortization calculation for each payment

### Financial Calculations

#### Amortization Formula
Monthly Payment = P × [r(1+r)^n] / [(1+r)^n - 1]

Where:
- P = Principal (financed amount)
- r = Period rate (APR / periods per year)
- n = Number of payments

#### Period Rate Calculation
- **Daily**: APR / 365
- **Weekly**: APR / 52
- **Bi-weekly**: APR / 26
- **Semi-monthly**: APR / 24
- **Monthly**: APR / 12
- **Quarterly**: APR / 4
- **Custom**: APR / (365 / customDays)

#### Payment Breakdown
Each payment consists of:
1. **Interest Portion**: Remaining balance × period rate
2. **Principal Portion**: Payment amount - interest portion
3. **Remaining Balance**: Previous balance - principal portion

### Schedule Generation
- First payment due after grace period
- Subsequent payments calculated based on frequency
- Last payment adjusted for rounding differences
- All dates in ISO 8601 format

### Comparison Information
Shows cost difference between:
- **Paying upfront**: Original product price
- **Paying with installment**: Product price + total interest
- **Additional cost**: Total interest amount
- **Additional cost %**: Interest as percentage of product price

### Common Use Cases

#### 1. Display Plans on Product Page
```
GET /api/v1/installments/products/{productId}/plans
```
- Shows all active plans for product
- Displays preview calculations at minimum down payment
- Highlights featured plans
- Sorted by displayOrder

#### 2. Calculate Custom Preview
```
POST /api/v1/installments/calculate-preview
{
  "planId": "...",
  "productPrice": 2000000.00,
  "quantity": 1,
  "downPaymentPercent": 25
}
```
- Customer adjusts down payment slider
- Real-time calculation of payments
- Shows full payment schedule
- Displays savings comparison

#### 3. Pre-Checkout Validation
```
POST /api/v1/installments/calculate-preview
```
- Validates customer's choices before checkout
- Ensures plan is still active
- Confirms down payment within range
- Generates schedule for agreement creation

### Integration Examples

#### Frontend Flow
1. Product page loads → Call GET /products/{id}/plans
2. Display plan options with previews
3. User selects plan and adjusts down payment
4. Call POST /calculate-preview on down payment change
5. Show detailed breakdown and schedule
6. User proceeds to checkout with selected configuration

#### Mobile App Flow
1. Fetch plans when user taps "Installment Options"
2. Show plan cards with key metrics
3. Tapped plan shows full preview
4. Slider for down payment percentage
5. Real-time preview updates
6. "Continue to Checkout" with configuration

### Error Handling Best Practices

#### Client-Side Validation
- Validate down payment range before API call
- Check quantity = 1 for installment
- Ensure product price > 0
- Validate UUID formats

#### Server-Side Errors
- 400: Show user-friendly message, allow retry
- 404: Product/plan not found, redirect or show alternatives
- 422: Display field-specific validation errors
- 500: Generic error message, log for investigation

### Performance Considerations
- Cache plan data (TTL: 1 hour)
- Debounce preview calculations (300ms)
- Lazy load full schedules
- Optimize for mobile networks

### Testing Scenarios

#### Happy Path
1. Get plans for valid product with installment enabled
2. Calculate preview with valid parameters
3. Verify calculations match expected values

#### Edge Cases
1. Product with no active plans → empty array
2. Product with installment disabled → empty array
3. Down payment at minimum boundary
4. Down payment at maximum boundary (50%)
5. Inactive plan in preview request → error

#### Error Cases
1. Invalid product ID → 404
2. Invalid plan ID → 404
3. Down payment too low → 400
4. Down payment too high → 400
5. Missing required fields → 422
6. Invalid data types → 422

### Data Format Standards
- **Dates**: ISO 8601 format (2025-10-18T14:30:00Z)
- **Currency**: TZS (Tanzanian Shillings), no currency symbol in API
- **Decimals**: 2 decimal places for all monetary values
- **Percentages**: Whole numbers (15 = 15%, not 0.15)
- **UUIDs**: Standard UUID v4 format with hyphens

### Business Rules Summary
1. **One Item Per Agreement**: Installment limited to single product
2. **Active Plans Only**: Only active plans returned in public endpoints
3. **Down Payment Range**: 10-50% (plan minimum to platform maximum)
4. **APR Limits**: 0-36% (platform enforced)
5. **Grace Period**: 0-60 days (plan-specific)
6. **Payment Frequency**: All standard frequencies supported
7. **Fulfillment Options**: IMMEDIATE or AFTER_PAYMENT
8. **Currency**: TZS only (Tanzania market)
9. **Rounding**: All calculations rounded to 2 decimal places
10. **Early Payoff**: 75% discount on remaining interest (not shown in preview)

### Notes for Developers

#### Calculation Accuracy
- Use decimal/numeric types for currency calculations
- Avoid floating-point arithmetic
- Round to 2 decimals at final step only
- Last payment absorbs rounding differences

#### Caching Strategy
- Cache plan data per product (1 hour TTL)
- Invalidate cache when plans updated
- Preview calculations should not be cached
- Consider CDN for plan endpoints

#### Mobile Optimization
- Minimize payload size (exclude schedule if not needed)
- Compress responses (gzip/brotli)
- Use pagination for long schedules
- Implement request debouncing

#### Security Considerations
- No authentication required for public endpoints
- Rate limiting: 1000 requests/hour per IP
- Input validation on all parameters
- SQL injection prevention (parameterized queries)
- XSS prevention (sanitize all inputs)

### Support & Resources
- **API Documentation**: https://docs.nextgate.com/api/installments
- **Developer Portal**: https://developers.nextgate.com
- **Support Email**: api-support@nextgate.com
- **Status Page**: https://status.nextgate.com
- **Changelog**: https://docs.nextgate.com/changelog

## Documentation Checklist

Before using this API, ensure you understand:

- [x] **Public Access**: No authentication required
- [x] **Plan Filtering**: Only active plans returned
- [x] **Down Payment Rules**: Min (plan-specific) to Max (50%)
- [x] **APR Calculation**: Converted to period rate automatically
- [x] **Amortization**: Principal + interest breakdown per payment
- [x] **Schedule Generation**: All payment dates calculated
- [x] **Fulfillment Options**: IMMEDIATE vs AFTER_PAYMENT
- [x] **Currency**: TZS only
- [x] **Rounding**: 2 decimal places
- [x] **Error Handling**: Proper validation and error messages
- [x] **Rate Limiting**: 1000 requests/hour per IP
- [x] **Caching**: Recommended for plan data
- [x] **Testing**: Sandbox environment available

---

# Installment Plan Management - Admin/Shop Endpoints

**Author**: Josh S. Sakweli, Backend Lead Team  
**Last Updated**: 2025-10-18  
**Version**: v1.0

**Base URL**: `https://api.nextgate.com/api/v1/products/{shopId}/{productId}/installment-plans`

**Short Description**: This API provides comprehensive endpoints for shop owners and administrators to create, manage, and configure installment plans for their products. Shop owners can define flexible payment terms including payment frequencies, interest rates, down payment requirements, grace periods, and fulfillment options. The API supports full CRUD operations, plan activation/deactivation, featured plan selection, and product-level installment enablement. All endpoints require authentication and validate shop ownership.

**Hints**: 
- All endpoints require authentication and shop ownership validation
- Shop owners can only manage plans for their own products
- Multiple plans can be created per product for different customer segments
- Only one plan per product can be featured at a time
- Plans can be deactivated without deletion to preserve historical data
- Active plans are automatically available to customers on product pages
- Inactive plans are hidden from customers but preserved for existing agreements
- Payment frequencies support daily, weekly, bi-weekly, semi-monthly, monthly, quarterly, and custom intervals
- APR (Annual Percentage Rate) must be between 0% and 36%
- Down payment requirements: 10-50% range
- Grace periods: 0-60 days before first payment
- Fulfillment options: IMMEDIATE (ship after down payment) or AFTER_PAYMENT (ship after completion)
- Display order determines plan sorting on product pages
- Featured plans get "Most Popular" or "Recommended" badge
- Enabling/disabling installments at product level affects all plans

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    // Actual response data goes here
  }
}
```

### Error Response Structure
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-10-18T10:30:45",
  "data": "Error description"
}
```

### Standard Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` for successful operations, `false` for errors |
| `httpStatus` | string | HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.) |
| `message` | string | Human-readable message describing the operation result |
| `action_time` | string | ISO 8601 timestamp of when the response was generated |
| `data` | object/string | Response payload for success, error details for failures |

---

## HTTP Method Badge Standards

For better visual clarity, all endpoints use colored badges for HTTP methods with the following standard colors:

- **GET** - <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> - Green (Safe, read-only operations)
- **POST** - <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> - Blue (Create new resources)
- **PUT** - <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> - Yellow (Update/replace entire resource)
- **PATCH** - <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> - Orange (Partial updates)
- **DELETE** - <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> - Red (Remove resources)

---

## Endpoints

## 1. Create Installment Plan
**Purpose**: Create a new installment plan for a specific product

**Endpoint**: <span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span> `{base_url}`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan to deactivate | Valid UUID format, must belong to specified product |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan deactivated successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
    "planName": "Budget Friendly Plan",
    "isActive": false,
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | ID of the deactivated plan |
| planName | Name of the plan |
| isActive | Active status (false) |
| updatedAt | Update timestamp |

**Error Response Examples**:
Same as Activate Installment Plan endpoint

**Important Notes**:
- Deactivated plans are hidden from customers on product pages
- Existing agreements continue to use deactivated plans
- Plan can be reactivated at any time
- Deactivation is preferred over deletion for plans with existing agreements

---

## 2. Get Product Installment Plans
**Purpose**: Retrieve all installment plans for a specific product (including inactive plans for admin view)

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plans retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": [
    {
      "planId": "4b5c6d7e-8f9a-4b1c-9d2e-3f4a5b6c7d8e",
      "planName": "Quick Payment Plan",
      "paymentFrequency": "WEEKLY",
      "paymentFrequencyDisplay": "Weekly",
      "customFrequencyDays": null,
      "numberOfPayments": 8,
      "calculatedDurationDays": 56,
      "calculatedDurationDisplay": "8 weeks",
      "apr": 10.00,
      "minDownPaymentPercent": 20,
      "gracePeriodDays": 7,
      "fulfillmentTiming": "IMMEDIATE",
      "isActive": true,
      "isFeatured": false,
      "displayOrder": 1,
      "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "productName": "Samsung Galaxy S24 Ultra",
      "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
      "shopName": "Tech World Store",
      "createdAt": "2025-10-15T09:00:00",
      "updatedAt": "2025-10-15T09:00:00"
    },
    {
      "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
      "planName": "12 Month Standard Plan",
      "paymentFrequency": "MONTHLY",
      "paymentFrequencyDisplay": "Monthly",
      "customFrequencyDays": null,
      "numberOfPayments": 12,
      "calculatedDurationDays": 360,
      "calculatedDurationDisplay": "12 months",
      "apr": 15.00,
      "minDownPaymentPercent": 20,
      "gracePeriodDays": 30,
      "fulfillmentTiming": "IMMEDIATE",
      "isActive": true,
      "isFeatured": true,
      "displayOrder": 2,
      "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "productName": "Samsung Galaxy S24 Ultra",
      "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
      "shopName": "Tech World Store",
      "createdAt": "2025-10-16T10:30:00",
      "updatedAt": "2025-10-16T10:30:00"
    },
    {
      "planId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
      "planName": "Budget Friendly Plan",
      "paymentFrequency": "MONTHLY",
      "paymentFrequencyDisplay": "Monthly",
      "customFrequencyDays": null,
      "numberOfPayments": 24,
      "calculatedDurationDays": 720,
      "calculatedDurationDisplay": "24 months",
      "apr": 18.00,
      "minDownPaymentPercent": 10,
      "gracePeriodDays": 30,
      "fulfillmentTiming": "AFTER_PAYMENT",
      "isActive": false,
      "isFeatured": false,
      "displayOrder": 3,
      "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "productName": "Samsung Galaxy S24 Ultra",
      "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
      "shopName": "Tech World Store",
      "createdAt": "2025-10-17T14:00:00",
      "updatedAt": "2025-10-18T08:00:00"
    }
  ]
}
```

**Success Response Fields**:
Same as Create Installment Plan endpoint, returned as array

**Error Response Examples**:

*Forbidden (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not have permission to view this shop's products",
  "action_time": "2025-10-18T10:30:45",
  "data": "You do not have permission to view this shop's products"
}
```

*Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "Product not found"
}
```

---

## 3. Get Installment Plan By ID
**Purpose**: Retrieve detailed information about a specific installment plan

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/{planId}`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan | Valid UUID format, must belong to specified product |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan retrieved successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "planName": "12 Month Standard Plan",
    "paymentFrequency": "MONTHLY",
    "paymentFrequencyDisplay": "Monthly",
    "customFrequencyDays": null,
    "numberOfPayments": 12,
    "calculatedDurationDays": 360,
    "calculatedDurationDisplay": "12 months",
    "apr": 15.00,
    "minDownPaymentPercent": 20,
    "gracePeriodDays": 30,
    "fulfillmentTiming": "IMMEDIATE",
    "isActive": true,
    "isFeatured": true,
    "displayOrder": 2,
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
    "shopName": "Tech World Store",
    "createdAt": "2025-10-16T10:30:00",
    "updatedAt": "2025-10-16T10:30:00",
    "metadata": {}
  }
}
```

**Success Response Fields**:
Same as Create Installment Plan endpoint

**Error Response Examples**:
Same as Get Product Installment Plans endpoint, plus:

*Not Found - Plan (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Installment plan not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "Installment plan not found"
}
```

---

## 4. Update Installment Plan
**Purpose**: Update an existing installment plan's configuration

**Endpoint**: <span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span> `{base_url}/{planId}`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |
| Content-Type | string | Yes | application/json |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan to update | Valid UUID format, must belong to specified product |

**Request JSON Sample**:
```json
{
  "planName": "12 Month Premium Plan",
  "paymentFrequency": "MONTHLY",
  "customFrequencyDays": null,
  "numberOfPayments": 12,
  "apr": 12.00,
  "minDownPaymentPercent": 25,
  "gracePeriodDays": 45,
  "fulfillmentTiming": "IMMEDIATE",
  "displayOrder": 1
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| planName | string | No | Updated display name | Min: 3, Max: 100 characters |
| paymentFrequency | string | No | Updated payment frequency | enum: DAILY, WEEKLY, BI_WEEKLY, SEMI_MONTHLY, MONTHLY, QUARTERLY, CUSTOM_DAYS |
| customFrequencyDays | integer | Conditional | Updated custom frequency | Required if paymentFrequency is CUSTOM_DAYS, Min: 1, Max: 365 |
| numberOfPayments | integer | No | Updated number of payments | Min: 2, Max: 120 |
| apr | decimal | No | Updated APR | Min: 0.00, Max: 36.00 |
| minDownPaymentPercent | integer | No | Updated minimum down payment | Min: 10, Max: 50 |
| gracePeriodDays | integer | No | Updated grace period | Min: 0, Max: 60 |
| fulfillmentTiming | string | No | Updated fulfillment option | enum: IMMEDIATE, AFTER_PAYMENT |
| displayOrder | integer | No | Updated display order | Min: 0 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan updated successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "planName": "12 Month Premium Plan",
    "paymentFrequency": "MONTHLY",
    "paymentFrequencyDisplay": "Monthly",
    "customFrequencyDays": null,
    "numberOfPayments": 12,
    "calculatedDurationDays": 360,
    "calculatedDurationDisplay": "12 months",
    "apr": 12.00,
    "minDownPaymentPercent": 25,
    "gracePeriodDays": 45,
    "fulfillmentTiming": "IMMEDIATE",
    "isActive": true,
    "isFeatured": true,
    "displayOrder": 1,
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
    "shopName": "Tech World Store",
    "createdAt": "2025-10-16T10:30:00",
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
Same as Create Installment Plan endpoint

**Error Response Examples**:
Same as Create Installment Plan endpoint

**Important Notes**:
- Updating a plan only affects new agreements created after the update
- Existing agreements continue using the original terms
- Cannot update isActive or isFeatured via this endpoint (use dedicated endpoints)

---

## 5. Delete Installment Plan
**Purpose**: Permanently delete an installment plan (only if no active agreements exist)

**Endpoint**: <span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span> `{base_url}/{planId}`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan to delete | Valid UUID format, must belong to specified product |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan deleted successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": null
}
```

**Success Response Fields**:
No data returned on successful deletion

**Error Response Examples**:

*Bad Request - Has Active Agreements (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot delete plan with active agreements. Deactivate the plan instead.",
  "action_time": "2025-10-18T10:30:45",
  "data": "Cannot delete plan with active agreements. Deactivate the plan instead."
}
```

*Forbidden (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not have permission to delete this plan",
  "action_time": "2025-10-18T10:30:45",
  "data": "You do not have permission to delete this plan"
}
```

*Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Installment plan not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "Installment plan not found"
}
```

---

## 6. Activate Installment Plan
**Purpose**: Activate an installment plan to make it available to customers

**Endpoint**: <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/{planId}/activate`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan to activate | Valid UUID format, must belong to specified product |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan activated successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "6d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f9a",
    "planName": "Budget Friendly Plan",
    "isActive": true,
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | ID of the activated plan |
| planName | Name of the plan |
| isActive | Active status (true) |
| updatedAt | Update timestamp |

**Error Response Examples**:
Same as Delete Installment Plan endpoint

---

## 7. Deactivate Installment Plan
**Purpose**: Deactivate an installment plan to hide it from customers (preserves data for existing agreements)

**Endpoint**: <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/{planId}/deactivate`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token

## 8. Set Featured Plan
**Purpose**: Mark a plan as featured (recommended/most popular) - only one plan per product can be featured

**Endpoint**: <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/{planId}/set-featured`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |
| planId | UUID | Yes | Unique identifier of the plan to feature | Valid UUID format, must belong to specified product |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Featured plan set successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "planName": "12 Month Premium Plan",
    "isFeatured": true,
    "previousFeaturedPlanId": "4b5c6d7e-8f9a-4b1c-9d2e-3f4a5b6c7d8e",
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | ID of the newly featured plan |
| planName | Name of the plan |
| isFeatured | Featured status (true) |
| previousFeaturedPlanId | ID of previously featured plan (null if none) |
| updatedAt | Update timestamp |

**Error Response Examples**:
Same as Activate Installment Plan endpoint

**Important Notes**:
- Setting a plan as featured automatically un-features any previously featured plan
- Only active plans should be featured
- Featured plans typically display "Most Popular" or "Recommended" badge
- Featured plans often appear first in plan listings

---

## 9. Enable Installments for Product
**Purpose**: Enable installment payment option for a product (makes all active plans available)

**Endpoint**: <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/enable-installments`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installments enabled successfully for product",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "installmentAvailable": true,
    "activePlansCount": 2,
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| productId | ID of the product |
| productName | Name of the product |
| installmentAvailable | Installment availability status (true) |
| activePlansCount | Number of active plans for this product |
| updatedAt | Update timestamp |

**Error Response Examples**:

*Bad Request - No Plans (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot enable installments: No installment plans created for this product",
  "action_time": "2025-10-18T10:30:45",
  "data": "Cannot enable installments: No installment plans created for this product"
}
```

*Forbidden (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not have permission to modify this product",
  "action_time": "2025-10-18T10:30:45",
  "data": "You do not have permission to modify this product"
}
```

*Not Found (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "Product not found"
}
```

---

## 10. Disable Installments for Product
**Purpose**: Disable installment payment option for a product (hides all plans from customers)

**Endpoint**: <span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span> `{base_url}/disable-installments`

**Access Level**: 🔒 Protected (Requires Authentication, Shop Owner Only)

**Authentication**: Bearer Token

**Request Headers**:
| Header | Type | Required | Description |
|--------|------|----------|-------------|
| Authorization | string | Yes | Bearer token for authentication |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installments disabled successfully for product",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "installmentAvailable": false,
    "activePlansCount": 2,
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
Same as Enable Installments endpoint

**Error Response Examples**:
Same as Enable Installments endpoint

**Important Notes**:
- Disabling installments hides the option from product page
- Existing agreements continue to function normally
- Plans are preserved and can be reactivated
- Customers with active agreements can still make payments

---

### Common HTTP Status Codes
- `200 OK`: Successful request
- `400 Bad Request`: Invalid request data or business logic violation
- `401 Unauthorized`: Authentication required/failed
- `403 Forbidden`: Insufficient permissions or not shop owner
- `404 Not Found`: Resource not found
- `422 Unprocessable Entity`: Validation errors
- `500 Internal Server Error`: Server error

### Payment Frequency Options
- **DAILY**: Payment every day (1 day)
- **WEEKLY**: Payment every week (7 days)
- **BI_WEEKLY**: Payment every 2 weeks (14 days)
- **SEMI_MONTHLY**: Payment twice per month (~15 days)
- **MONTHLY**: Payment every month (~30 days)
- **QUARTERLY**: Payment every 3 months (~90 days)
- **CUSTOM_DAYS**: Custom interval (1-365 days)

### Fulfillment Timing Options
- **IMMEDIATE**: Product ships after down payment, customer receives product while paying installments
- **AFTER_PAYMENT**: Product ships after final payment (layaway model), inventory held during payment period

### Plan Configuration Constraints
| Parameter | Minimum | Maximum | Notes |
|-----------|---------|---------|-------|
| APR | 0% | 36% | Platform-enforced limit |
| Down Payment | 10% | 50% | Minimum by plan, maximum platform-wide |
| Number of Payments | 2 | 120 | Typical range: 4-24 payments |
| Grace Period | 0 days | 60 days | Days before first payment |
| Custom Frequency | 1 day | 365 days | Only for CUSTOM_DAYS type |
| Plan Name | 3 chars | 100 chars | Must be unique per product |

### Plan States and Lifecycle

#### Active Plan
- Visible to customers on product page
- Can be selected during checkout
- Creates new agreements
- Appears in public API responses

#### Inactive Plan
- Hidden from customers
- Cannot be selected for new purchases
- Existing agreements continue
- Visible only to shop owner

#### Featured Plan
- Displays "Most Popular" or "Recommended" badge
- Only one plan per product can be featured
- Typically listed first in plan options
- Used for promoting preferred payment terms

#### Deleted Plan
- Permanently removed from system
- Only possible if no agreements exist
- Cannot be recovered
- Use deactivation instead for preservation

### Business Rules Summary

1. **One Featured Plan**: Only one plan per product can be featured at a time
2. **Shop Ownership**: Only shop owners can manage their product plans
3. **Active Plans Only**: Customers only see active plans for products with installments enabled
4. **No Deletion with Agreements**: Plans with existing agreements cannot be deleted, only deactivated
5. **Update Impact**: Plan updates only affect new agreements, not existing ones
6. **Product-Level Control**: Disabling installments at product level hides all plans
7. **Duration Calculation**: System automatically calculates duration based on frequency and payment count
8. **Validation**: All parameters validated against platform constraints
9. **Plan Minimum**: At least one active plan required to enable installments on product
10. **Historical Preservation**: Deactivated plans preserved for agreement history

### Common Workflows

#### Creating a New Installment Plan
1. Ensure product exists and you own the shop
2. POST /installment-plans with plan configuration
3. Verify plan appears in GET /installment-plans
4. Optionally set as featured via PATCH /set-featured
5. Enable installments on product if not already enabled

#### Managing Multiple Plans
1. Create multiple plans for different customer segments
   - Quick payment plan (8 weeks, 10% APR)
   - Standard plan (12 months, 15% APR)
   - Budget plan (24 months, 18% APR)
2. Set most balanced plan as featured
3. Order by display priority (displayOrder field)
4. Monitor customer preferences and adjust

#### Temporarily Disabling Plans
1. PATCH /deactivate for specific plan
2. OR PATCH /disable-installments for all plans
3. Existing agreements continue normally
4. Reactivate when ready with PATCH /activate or /enable-installments

#### Updating Plan Terms
1. Analyze current agreement performance
2. PUT /installment-plans/{planId} with new terms
3. New terms apply only to future agreements
4. Consider creating new plan for major changes

#### Deleting Unused Plans
1. Check if plan has any agreements (attempt delete will fail if so)
2. DELETE /installment-plans/{planId}
3. Plan permanently removed
4. If has agreements, use deactivate instead

#### Client-Side Validation
- Validate shop/product ownership before API call
- Check APR within 0-36% range
- Verify down payment within 10-50% range
- Validate payment count between 2-120
- Ensure customFrequencyDays present for CUSTOM_DAYS
- Check plan name length (3-100 characters)

#### Server-Side Error Handling
- 400: Display user-friendly message, allow retry
- 403: Show "Insufficient permissions" message
- 404: Redirect to product list or show error
- 422: Display field-specific validation errors
- 500: Generic error message, log for investigation

### Testing Scenarios

#### Happy Path
1. Create plan with valid parameters
2. Retrieve plan and verify all fields
3. Update plan configuration
4. Set as featured plan
5. Activate/deactivate plan
6. Enable/disable product installments

#### Edge Cases
1. Create multiple plans with same parameters
2. Set featured when no featured plan exists
3. Set featured when another plan is featured
4. Update plan to use custom frequency
5. Deactivate only active plan
6. Enable installments with no active plans

#### Error Cases
1. Invalid shop/product IDs → 404
2. Non-owner attempting management → 403
3. APR outside 0-36% range → 422
4. Down payment outside 10-50% → 422
5. Delete plan with agreements → 400
6. Missing required fields → 422
7. Invalid payment frequency → 422
8. Custom days without CUSTOM_DAYS → 422

### Performance Considerations
- Cache plan data at product level (TTL: 30 minutes)
- Invalidate cache on plan create/update/delete
- Batch operations when managing multiple plans
- Use pagination for products with many plans
- Index on productId for fast retrieval

### Security Considerations
- Always validate shop ownership
- Verify product belongs to shop
- Rate limiting: 100 requests/hour per user
- Input validation on all parameters
- SQL injection prevention (parameterized queries)
- XSS prevention (sanitize all inputs)
- Audit log all plan changes

### Monitoring and Analytics
Track the following metrics:
- Plans created per shop
- Plan activation/deactivation frequency
- Featured plan changes
- Plans by payment frequency
- Average APR across plans
- Popular plan configurations
- Plans with most agreements

### Best Practices for Shop Owners

#### Plan Design Strategy
1. **Offer 2-3 plans** for different budgets
   - Quick plan: Short term, low interest
   - Standard plan: Medium term, moderate interest
   - Budget plan: Long term, higher interest
2. **Use clear naming**: "8-Week Quick Pay", "12-Month Standard"
3. **Set appropriate featured plan**: Balance of affordability and profit
4. **Adjust based on data**: Monitor which plans customers choose

#### APR Configuration
- **Premium products**: Lower APR (8-12%)
- **Standard products**: Moderate APR (12-18%)
- **High-risk categories**: Higher APR (18-24%)
- **Promotional periods**: Reduced APR (0-10%)

#### Down Payment Strategy
- **High-value items**: Higher down payment (25-40%)
- **Impulse purchases**: Lower down payment (10-20%)
- **Luxury items**: Flexible range (15-50%)
- **Clearance items**: Fixed minimum (10-15%)

#### Grace Period Guidelines
- **Weekly payments**: 7-14 days grace
- **Monthly payments**: 30-45 days grace
- **Seasonal products**: Align with customer cash flow
- **New customers**: Shorter grace (7-15 days)

### Common Mistakes to Avoid
1. Too many plans (confuses customers) - stick to 2-4
2. Too high APR (customers avoid) - keep competitive
3. Too low down payment (higher default risk) - minimum 15-20%
4. Deleting active plans - deactivate instead
5. Not setting featured plan - customers want guidance
6. Inconsistent plan naming - use clear conventions
7. Not updating based on performance - review quarterly
8. Enabling without active plans - create plans first

### Support & Resources
- **API Documentation**: https://docs.nextgate.com/api/installment-plans
- **Shop Owner Guide**: https://help.nextgate.com/shop/installments
- **Developer Portal**: https://developers.nextgate.com
- **Support Email**: shop-support@nextgate.com
- **Status Page**: https://status.nextgate.com
- **Video Tutorials**: https://academy.nextgate.com/installments

### Version 1.0 (2025-10-18)
- Initial release
- Full CRUD operations for installment plans
- Plan activation/deactivation
- Featured plan management
- Product-level installment control
- Shop ownership validation
- Comprehensive error handling
- Audit logging support for authentication |
| Content-Type | string | Yes | application/json |

**Path Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| shopId | UUID | Yes | Unique identifier of the shop | Valid UUID format, must be owned by authenticated user |
| productId | UUID | Yes | Unique identifier of the product | Valid UUID format, must belong to specified shop |

**Request JSON Sample**:
```json
{
  "planName": "12 Month Standard Plan",
  "paymentFrequency": "MONTHLY",
  "customFrequencyDays": null,
  "numberOfPayments": 12,
  "apr": 15.00,
  "minDownPaymentPercent": 20,
  "gracePeriodDays": 30,
  "fulfillmentTiming": "IMMEDIATE",
  "isActive": true,
  "isFeatured": false,
  "displayOrder": 1
}
```

**Request Body Parameters**:
| Parameter | Type | Required | Description | Validation |
|-----------|------|----------|-------------|------------|
| planName | string | Yes | Display name for the plan | Min: 3, Max: 100 characters |
| paymentFrequency | string | Yes | Payment frequency type | enum: DAILY, WEEKLY, BI_WEEKLY, SEMI_MONTHLY, MONTHLY, QUARTERLY, CUSTOM_DAYS |
| customFrequencyDays | integer | Conditional | Custom frequency in days | Required if paymentFrequency is CUSTOM_DAYS, Min: 1, Max: 365 |
| numberOfPayments | integer | Yes | Total number of payments | Min: 2, Max: 120 |
| apr | decimal | Yes | Annual Percentage Rate | Min: 0.00, Max: 36.00 |
| minDownPaymentPercent | integer | Yes | Minimum down payment percentage | Min: 10, Max: 50 |
| gracePeriodDays | integer | Yes | Days before first payment due | Min: 0, Max: 60 |
| fulfillmentTiming | string | Yes | When to ship product | enum: IMMEDIATE, AFTER_PAYMENT |
| isActive | boolean | No | Whether plan is active | Default: true |
| isFeatured | boolean | No | Whether plan is featured | Default: false |
| displayOrder | integer | No | Display order on product page | Min: 0, Default: 0 |

**Success Response JSON Sample**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Installment plan created successfully",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "planName": "12 Month Standard Plan",
    "paymentFrequency": "MONTHLY",
    "paymentFrequencyDisplay": "Monthly",
    "customFrequencyDays": null,
    "numberOfPayments": 12,
    "calculatedDurationDays": 360,
    "calculatedDurationDisplay": "12 months",
    "apr": 15.00,
    "minDownPaymentPercent": 20,
    "gracePeriodDays": 30,
    "fulfillmentTiming": "IMMEDIATE",
    "isActive": true,
    "isFeatured": false,
    "displayOrder": 1,
    "productId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "productName": "Samsung Galaxy S24 Ultra",
    "shopId": "8d3a7b12-9c4e-4f8a-b5d2-3e6f7a8b9c0d",
    "shopName": "Tech World Store",
    "createdAt": "2025-10-18T10:30:45",
    "updatedAt": "2025-10-18T10:30:45"
  }
}
```

**Success Response Fields**:
| Field | Description |
|-------|-------------|
| planId | Unique identifier for the created plan |
| planName | Name of the plan |
| paymentFrequency | Payment frequency enum value |
| paymentFrequencyDisplay | Human-readable frequency |
| customFrequencyDays | Custom days (if CUSTOM_DAYS type) |
| numberOfPayments | Total number of payments |
| calculatedDurationDays | Auto-calculated total duration in days |
| calculatedDurationDisplay | Human-readable duration |
| apr | Annual Percentage Rate |
| minDownPaymentPercent | Minimum down payment percentage |
| gracePeriodDays | Grace period in days |
| fulfillmentTiming | Fulfillment option |
| isActive | Active status |
| isFeatured | Featured status |
| displayOrder | Display order |
| productId | Associated product ID |
| productName | Associated product name |
| shopId | Associated shop ID |
| shopName | Associated shop name |
| createdAt | Creation timestamp |
| updatedAt | Last update timestamp |

**Error Response Examples**:

*Bad Request - Invalid APR (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "APR must be between 0% and 36%",
  "action_time": "2025-10-18T10:30:45",
  "data": "APR must be between 0% and 36%"
}
```

*Bad Request - Invalid Payment Count (400):*
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Number of payments must be between 2 and 120",
  "action_time": "2025-10-18T10:30:45",
  "data": "Number of payments must be between 2 and 120"
}
```

*Validation Error (422):*
```json
{
  "success": false,
  "httpStatus": "UNPROCESSABLE_ENTITY",
  "message": "Validation failed",
  "action_time": "2025-10-18T10:30:45",
  "data": {
    "planName": "Plan name is required",
    "apr": "must be between 0 and 36",
    "minDownPaymentPercent": "must be between 10 and 50"
  }
}
```

*Forbidden - Not Shop Owner (403):*
```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "You do not have permission to manage this shop's products",
  "action_time": "2025-10-18T10:30:45",
  "data": "You do not have permission to manage this shop's products"
}
```

*Not Found - Product (404):*
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found",
  "action_time": "2025-10-18T10:30:45",
  "data": "Product not found"
}
```

---