# Payment Allocation System - Implementation Guide

## Overview
The LendCore payment allocation system intelligently distributes payments across loan installments with automatic rollover to guarantee deposits when applicable.

## Core Business Logic

### Payment Allocation Rules
1. **Amount < Installment Balance**: Pay partial, leave balance on current installment
2. **Amount = Installment Balance**: Pay in full, mark installment as paid
3. **Amount > Installment Balance**: 
   - Pay current installment in full
   - Roll excess to next installment of same loan
   - Continue rollover to subsequent installments as needed
4. **All Installments Paid + Remaining Balance**: Add remainder to user's guarantee deposit

### Entry Points & Flows

#### 1. C2B Payment (M-Pesa Customer-to-Business)
**File**: `app/Http/Controllers/Api/MpesaCallbackController.php::c2bConfirmation()`

```
C2B Payment Received
    ↓
Match BillRefNumber to User National ID
    ├─ NO MATCH → Save to Suspense Account (manual review)
    └─ MATCH → recordPaymentForUserActiveLoans()
                  ├─ Allocate across active loans (by creation order)
                  ├─ Roll excess to next loan's installments
                  └─ Add remainder to guarantee_deposit
```

**Suspense Reassignment** (`app/Services/SuspenseService.php::reassignAndApply()`):
```
Suspense Transaction Reassigned
    ↓
recordLoanPayment()
    ├─ Allocate to installments (by installment_number)
    ├─ Roll excess to next installments
    └─ Add remainder to guarantee_deposit
```

#### 2. Manual Repayment Payment (User/Admin)
**Files**: 
- `app/Http/Controllers/Web/UserDashboardController.php::processPayment()`
- `app/Http/Controllers/Admin/RepaymentController.php::storePayment()`

```
Manual Payment Submitted for Repayment #3
    ↓
recordPayment($repayment, $amount, allowRollover=true)
    ↓
recordLoanPayment() [via internal routing]
    ├─ Start with Repayment #3
    ├─ Apply amount to Repayment #3
    ├─ Roll excess to Repayment #4, #5, etc.
    └─ Add remainder to guarantee_deposit
```

#### 3. STK Push Callback (Mobile Usable App)
**File**: `app/Http/Controllers/Api/MpesaCallbackController.php::handle()`

```
STK Payment Callback
    ↓
processMpesaPaymentCallback()
    ↓
applyPaymentFromTransaction($transaction, $amount, allowRollover=true)
    ↓
recordLoanPayment()
    ├─ Allocate across installments
    ├─ Roll excess to next installments
    └─ Add remainder to guarantee_deposit
```

## Implementation Details

### Key Service Methods

#### `RepaymentService::recordPayment()`
- **Parameters**: `$repayment, $amount, ..., $allowRollover=true`
- **Behavior**: 
  - If `allowRollover=true` (default): Uses `recordLoanPayment()` for intelligent allocation
  - If `allowRollover=false`: Only updates the target repayment
- **Returns**: Updated `Repayment` object

#### `RepaymentService::recordLoanPayment()`
- **Parameters**: `$loan, $amount, ...`
- **Behavior**:
  1. Gets unpaid repayments (pending/overdue) ordered by installment_number
  2. Allocates payment to each repayment sequentially
  3. Rolls over any excess to next installment
  4. Returns remaining amount and list of updated repayments
- **Returns**: Array with `['paid_amount', 'remaining_amount', 'updates' => Collection]`

#### `RepaymentService::recordPaymentForUserActiveLoans()`
- **Parameters**: `$user, $amount, ...`
- **Behavior**:
  1. Gets active loans ordered by creation date
  2. Calls `recordLoanPayment()` for each loan
  3. Rolls over remaining amount to next loan
  4. Adds final remainder to user's `guarantee_deposit_balance`
- **Returns**: Array with `['paid_amount', 'remaining_amount', 'guarantee_deposit_added', 'loan_updates']`

#### `SuspenseService::reassignAndApply()`
- **Parameters**: `$suspense, $user, $loan=null, $reason=null`
- **Behavior**:
  1. If loan provided: Calls `recordLoanPayment($loan, $suspense->amount)`
  2. Rolls excess to guarantee_deposit
  3. Updates suspense transaction status to 'applied'
- **Returns**: Array with `['suspense', 'applied_amount', 'guarantee_deposit_added']`

## Usage Examples

### Example 1: User pays more than current installment
```
Installments: #1 (due: 2000, paid: 0), #2 (due: 2000, paid: 0), #3 (due: 2000, paid: 0)
Payment: KSh 5,500

Flow:
- Installment #1: pays 2000 (balance: 0) → PAID
- Installment #2: pays 2000 (balance: 0) → PAID  
- Installment #3: pays 1500 (balance: 500)
- Remaining: 0
```

### Example 2: User pays more than entire loan
```
Loan has 3 installments × 2000 each = 6000 total
User's active loans: Loan1 (5500 remaining), Loan2 (3000 remaining)
Payment: KSh 8,700 (C2B)

Flow:
- Loan1: Apply 5500 → Loan1 PAID
- Loan2: Apply 3000 → Loan2 PAID
- Remaining: 200 → Added to guarantee_deposit_balance
```

### Example 3: Suspense payment with partial allocation
```
Suspense Amount: KSh 7,200 (previously unmatched)
Reassigned to: User123, Loan #5
Loan #5 has 4 unpaid installments of 2000 each

Flow:
- Installment #1: pays 2000 → PAID
- Installment #2: pays 2000 → PAID
- Installment #3: pays 2000 → PAID
- Installment #4: pays 1200 (balance: 800)
- Remaining: 0
- Status: 'applied'
```

## Validation Rules

### Payment Amount Validation
- **User Dashboard**: `min:0.01` (no upper limit)
- **Admin Controller**: 
  - `min:0.01`
  - `max: {total_loan_balance}` (dynamically calculated)
  - Allows full loan overpayment which rolls to guarantee_deposit

### Key Validations in Code
1. Payment amount > 0 (enforced in service)
2. Repayment exists and belongs to user (controller authorization)
3. Loan exists and is active (checked in various places)

## Error Handling

### Common Exceptions
- "Payment amount must be greater than 0" → Amount validation failed
- "Payment exceeds total due amount" → Overpayment (only when allowRollover=false)
- "Payment transaction is not linked to a repayment" → Callback routing issue
- "Bill reference number matches no user" → Sent to suspense (not an error)

## Database Updates

### Fields Modified by Payment System
1. **Repayments Table**:
   - `amount_paid`: Incremented
   - `balance`: Recalculated (amount_due - amount_paid)
   - `status`: Updated (paid/pending)
   - `paid_at`: Set when status becomes 'paid'
   - `is_active`: Set to false when paid

2. **Fines Table**:
   - `amount`: Decremented if fine is paid

3. **Users Table**:
   - `guarantee_deposit_balance`: Incremented with remainder

4. **Transactions Table**:
   - New record created for each payment
   - Links repayment, loan, and user
   - Stores M-Pesa metadata

5. **SuspenseTransactions Table**:
   - `status`: Updated to 'applied'
   - `matched_at`: Set
   - `applied_at`: Set
   - `user_id`: Populated
   - `loan_id`: Populated (if assigned)

## Testing Scenarios

### Test Case 1: C2B Complete Loan Payment
```gherkin
Given a user with national ID 1234567890
And the user has a Loan with KSh 6000 total due
When a C2B payment of KSh 6000 is received with billRef=1234567890
Then Loan should be fully paid
And guarantee_deposit_balance should not change
```

### Test Case 2: C2B Overpayment to Guarantee Deposit
```gherkin
Given a user with national ID 1234567890
And the user has a Loan with KSh 5000 due
When a C2B payment of KSh 5800 is received
Then Loan should be fully paid
And guarantee_deposit_balance should increase by 800
```

### Test Case 3: Suspense Reassignment with Rollover
```gherkin
Given a suspense transaction with amount 4500
And a loan with 3 installments of 1500 each
When the suspense is reassigned to the loan
Then Installments 1-3 should be paid
And no guarantee_deposit increase (exact match)
```

### Test Case 4: Manual Admin Payment with Rollover
```gherkin
Given Installments #2-#4 of a loan are pending
And admin records payment of 4000 for Installment #2
Then Installment #2 should be paid (1500)
And Installment #3 should be paid (1500)
And Installment #4 should have 1000 paid (balance: 500)
```

## Deployment Checklist

- [ ] Verify all tests pass (especially payment allocation)
- [ ] Test C2B → Suspense → Reassign flow end-to-end
- [ ] Verify guarantee_deposit updates correctly
- [ ] Check admin payment form allows overpayment input
- [ ] Review audit logs for payment allocations
- [ ] Confirm no regression in existing payment flows
- [ ] Test with multiple concurrent payments
- [ ] Verify rollover works across loan boundaries

## Configuration

No additional configuration needed. The system uses:
- Loan `status = 'active'` to identify active loans
- Repayment `status IN ('pending', 'overdue')` to identify unpaid installments
- Installment ordering by `installment_number` ASC
- Loan ordering by `created_at` ASC

## Performance Considerations

- **Payment Processing**: O(n) where n = number of remaining installments
- **Guarantee Deposit**: Single increment operation (atomic)
- **Database Calls**: Approximately 5-10 per payment (depends on rollover depth)
- **Optimization**: Consider caching user active loans if N > 5

## Future Enhancements

1. **Partial Fine Payment**: Current logic pays fine after repayment balance
2. **Payment Scheduling**: Schedule future payments with pre-allocated rollover
3. **Payment Plans**: Create custom payment plans with different rollover rules
4. **Batch Processing**: Handle batch payments more efficiently
5. **Analytics**: Track rollover patterns and frequency
