Record Loan Repayment
Process loan repayment transactions with automated payment allocation across fees, penalties, interest, and principal following the payment hierarchy.
Overview
The Loan Repayment Commands enable secure processing of loan payments through multiple channels. These operations automatically allocate payments according to the configured payment hierarchy (typically: Fees → Penalties → Interest → Principal), update loan schedules, manage account balances, and maintain compliance with loan product rules.
Available Commands
- InitiateLoanRepaymentCommand - Process loan repayment from external sources (cash, bank transfer, etc.)
- InitiateLoanRepaymentWithDepositCommand - Process loan repayment by debiting a deposit account
Key Capabilities
- Automatic Payment Allocation: Distributes payments across fees, penalties, interest, and principal
- Schedule-Based Processing: Updates loan repayment schedules in chronological order
- Multi-Channel Support: Accepts payments from various transaction channels
- Partial Payment Handling: Processes payments less than total amount due
- External Channel Integration: Tracks payments from external institutions
- Deposit Account Integration: Directly debits customer deposit accounts
- Backdating Support: Allows backdated repayment transactions
- Transaction Tracking: Generates unique transaction keys for audit trails
- State Management: Updates loan and schedule states based on payment application
- Accounting Integration: Creates appropriate GL entries based on loan product rules
Command 1: InitiateLoanRepaymentCommand
Process loan repayment from external payment channels (cash, bank transfer, mobile money, etc.).
API Endpoint
POST /api/bpm/cmd
Headers
Content-Type: application/json
Authorization: Bearer {access_token}
X-Tenant-ID: {tenant_id}
Request Structure
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "string",
"channelEncodedKey": "string",
"amount": "decimal",
"notes": "string",
"serviceId": "string",
"serviceDescription": "string",
"isBackDated": "boolean",
"backDateValueDate": "string (ISO 8601)",
"isBookingDate": "boolean",
"bookingDate": "string (ISO 8601)",
"repaymentChannelDetails": {
"institutionCode": "string",
"institutionAccount": "string",
"narration": "string",
"reference": "string",
"providerCode": "string",
"note": "string"
}
}
}
Request Fields
| Field | Type | Required | Description | Validation |
|---|---|---|---|---|
accountEncodedKey | string | Yes | Loan account number or encoded key | Must exist, must be Active or In_Arrears |
channelEncodedKey | string | Yes | Transaction channel identifier | Must exist in system |
amount | decimal | Yes | Repayment amount | Must be > 0 |
notes | string | No | Payment description/remarks | Free-text notes |
serviceId | string | No | Service identifier | Defaults to "LOAN_REPAYMENT" |
serviceDescription | string | No | Service description | Defaults to "LOAN REPAYMENT" |
isBackDated | boolean | No | Whether transaction is backdated | Defaults to false |
backDateValueDate | string | Conditional | Transaction value date | Required if isBackDated=true (ISO 8601 format) |
isBookingDate | boolean | No | Whether to specify booking date | Defaults to false |
bookingDate | string | Conditional | Transaction booking date | Required if isBookingDate=true (ISO 8601 format) |
repaymentChannelDetails | object | No | External channel payment details | Optional for tracking external payments |
Repayment Channel Details Object
| Field | Type | Required | Description |
|---|---|---|---|
institutionCode | string | No | External institution code |
institutionAccount | string | No | External institution account |
narration | string | No | Payment narration |
reference | string | No | External payment reference |
providerCode | string | No | Payment provider code |
note | string | No | Additional notes |
Response Structure
Success Response
{
"isSuccessful": true,
"message": "Loan repayment has been processed successfully.",
"statusCode": "00",
"data": {
"transactionKey": "8A3F2D1E9B5C4F7A6E8D2C1B3A9F5E7D",
"principalPaid": 50000.00,
"interestPaid": 5000.00,
"feesPaid": 500.00,
"penaltiesPaid": 1000.00,
"totalPaid": 56500.00
}
}
Error Response
{
"isSuccessful": false,
"message": "The loan account has been locked presently and no transaction can be posted until it is unlocked",
"statusCode": "REQUEST_NOT_VALID"
}
Response Fields
| Field | Type | Description |
|---|---|---|
isSuccessful | boolean | Indicates if repayment was successful |
message | string | Descriptive result message |
statusCode | string | Response code (00 = success) |
transactionKey | string | Unique transaction identifier |
principalPaid | decimal | Amount applied to principal |
interestPaid | decimal | Amount applied to interest |
feesPaid | decimal | Amount applied to fees |
penaltiesPaid | decimal | Amount applied to penalties |
totalPaid | decimal | Total amount processed |
Command 2: InitiateLoanRepaymentWithDepositCommand
Process loan repayment by debiting funds from a customer's deposit account.
API Endpoint
POST /api/bpm/cmd
Headers
Content-Type: application/json
Authorization: Bearer {access_token}
X-Tenant-ID: {tenant_id}
Request Structure
{
"commandName": "InitiateLoanRepaymentWithDepositCommand",
"data": {
"accountEncodedKey": "string",
"depositAccountEncodedKey": "string",
"amount": "decimal",
"notes": "string",
"serviceId": "string",
"serviceDescription": "string",
"allowPartial": "boolean",
"isBackDated": "boolean",
"backDateValueDate": "string (ISO 8601)",
"isBookingDate": "boolean",
"bookingDate": "string (ISO 8601)"
}
}
Request Fields
| Field | Type | Required | Description | Validation |
|---|---|---|---|---|
accountEncodedKey | string | Yes | Loan account number or encoded key | Must exist, must be Active or In_Arrears |
depositAccountEncodedKey | string | Yes | Deposit account number or encoded key | Must exist, must have sufficient balance |
amount | decimal | Yes | Repayment amount | Must be > 0 |
notes | string | No | Payment description/remarks | Free-text notes |
serviceId | string | No | Service identifier | Optional service tracking |
serviceDescription | string | No | Service description | Human-readable service info |
allowPartial | boolean | No | Allow partial payment if insufficient balance | Defaults to false |
isBackDated | boolean | No | Whether transaction is backdated | Defaults to false |
backDateValueDate | string | Conditional | Transaction value date | Required if isBackDated=true (ISO 8601 format) |
isBookingDate | boolean | No | Whether to specify booking date | Defaults to false |
bookingDate | string | Conditional | Transaction booking date | Required if isBookingDate=true (ISO 8601 format) |
Response Structure
Same as InitiateLoanRepaymentCommand above.
Sample Requests
1. Basic Cash Loan Repayment
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "LOAN123456789",
"channelEncodedKey": "CHANNEL_CASH",
"amount": 25000.00,
"notes": "Monthly installment payment - December 2024"
}
}
Use Case: Customer makes regular monthly payment at branch with cash.
2. External Bank Transfer Repayment
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "8A3F2D1E9B5C4F7A6E8D2C1B3A9F5E7D",
"channelEncodedKey": "CHANNEL_BANK_TRANSFER",
"amount": 50000.00,
"notes": "Partial loan repayment from external bank",
"serviceId": "BANK_TRANSFER_REPAY",
"serviceDescription": "Bank Transfer Loan Repayment",
"repaymentChannelDetails": {
"institutionCode": "058",
"institutionAccount": "0123456789",
"narration": "Loan repayment for account LOAN123456789",
"reference": "TRF/2024/12/001234",
"providerCode": "GTB",
"note": "Transfer from GTBank"
}
}
}
Use Case: Customer transfers funds from external bank account to repay loan.
3. Mobile Money Repayment
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "LOAN987654321",
"channelEncodedKey": "CHANNEL_MOBILE_MONEY",
"amount": 15000.00,
"notes": "Mobile money payment via MTN",
"serviceId": "MOBILE_MONEY_REPAY",
"serviceDescription": "Mobile Money Loan Repayment",
"repaymentChannelDetails": {
"institutionCode": "MTN",
"institutionAccount": "233240123456",
"reference": "MM2024120112345",
"providerCode": "MTN_GHANA",
"note": "MTN Mobile Money payment"
}
}
}
Use Case: Customer uses mobile money service to repay loan.
4. Loan Repayment from Deposit Account
{
"commandName": "InitiateLoanRepaymentWithDepositCommand",
"data": {
"accountEncodedKey": "LOAN123456789",
"depositAccountEncodedKey": "SAV987654321",
"amount": 30000.00,
"notes": "Automatic monthly repayment from savings",
"serviceId": "AUTO_DEBIT_REPAY",
"serviceDescription": "Automatic Loan Repayment"
}
}
Use Case: Automated monthly deduction from customer's savings account.
5. Backdated Repayment Correction
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "LOAN555666777",
"channelEncodedKey": "CHANNEL_BRANCH",
"amount": 20000.00,
"notes": "Backdated payment correction - received on Dec 15",
"isBackDated": true,
"backDateValueDate": "2024-12-15T00:00:00Z",
"isBookingDate": true,
"bookingDate": "2024-12-18T00:00:00Z"
}
}
Use Case: Recording a payment that was received earlier but not immediately posted.
6. Partial Repayment from Deposit (Allow Partial)
{
"commandName": "InitiateLoanRepaymentWithDepositCommand",
"data": {
"accountEncodedKey": "LOAN888999000",
"depositAccountEncodedKey": "CHK111222333",
"amount": 40000.00,
"notes": "Partial payment from checking account",
"allowPartial": true,
"serviceId": "PARTIAL_REPAY",
"serviceDescription": "Partial Loan Repayment"
}
}
Use Case: Process whatever amount is available in deposit account, even if less than requested amount.
Business Logic
Payment Allocation Hierarchy
Loan repayments are automatically allocated in this order:
Processing Workflow
-
Validation Phase
- Verify loan account exists and is active (Active or In_Arrears state)
- Check loan is not locked
- Validate transaction channel exists
- Verify amount is greater than zero
- For deposit-based: Check deposit account balance
-
Transaction Creation
- Generate unique transaction key
- Create CBS transaction record
- Set transaction type as "Repayment"
- Apply value date and booking date logic
-
Schedule Processing (in chronological order)
- Retrieve all schedules with outstanding balances
- Order schedules by due date (earliest first)
- For each schedule:
- Calculate outstanding amounts (fees, penalties, interest, principal)
- Allocate payment according to hierarchy
- Update schedule paid amounts
- Mark schedule as "Paid" if fully cleared
- Update schedule state (Due, Late, etc.) if partially paid
-
Account Updates
- Recalculate loan balances
- Update loan state if fully paid
- Create GL entries based on loan product accounting rules
- Record external channel details (if provided)
-
Response Generation
- Return transaction key
- Provide breakdown of payment allocation
- Confirm total amount processed
State Transitions
Loan States:
- Active → Remains Active (if balance outstanding)
- Active → Closed (if fully paid)
- In_Arrears → Active (if brought current)
- In_Arrears → Closed (if fully paid)
- Locked → Cannot process payment (error returned)
Schedule States:
- Pending → Paid (if schedule fully cleared)
- Due → Paid (if schedule fully cleared)
- Late → Paid (if schedule fully cleared)
- Late → Remains Late (if partially paid, still past due)
- Upfront → Not affected by repayments
Validation Rules
Common Validations (Both Commands)
| Validation | Error Message | Status Code |
|---|---|---|
| Loan account not found | "The supplied loan account or encoded key is not valid." | CODE_DOES_NOT_EXIST |
| Loan not active | "The loan - [accountNumber] is no longer active. The present state is [state]." | REQUEST_NOT_VALID |
| Loan is locked | "The loan account has been locked presently and no transaction can be posted until it is unlocked" | REQUEST_NOT_VALID |
| Amount less than or equal to 0 | "The repayment amount must be greater than 0." | REQUEST_NOT_VALID |
| Channel not found | "The selected transaction channel cannot be found." | - |
| Backdate without date | "The backdate is required" | DO_NOT_HONOR |
| Booking date without date | "The book date is required" | DO_NOT_HONOR |
| Duplicate transaction | "A transaction already exists with the same transaction reference." | DUPLICATE_RECORD |
InitiateLoanRepaymentWithDepositCommand Specific
| Validation | Error Message |
|---|---|
| Deposit account not found | "The supplied deposit account or encoded key is not valid." |
| Deposit account insufficient balance | "The source account does not have sufficient balance." |
| Deposit account locked/frozen | "The deposit account is currently locked/frozen and cannot be debited." |
| Deposit account closed | "Cannot debit from a closed account." |
| Currency mismatch | "Loan and deposit accounts must have the same currency." |
Error Codes
| Status Code | Description | Resolution |
|---|---|---|
00 | Success | Payment processed successfully |
CODE_DOES_NOT_EXIST | Account not found | Verify account number/encoded key |
REQUEST_NOT_VALID | Invalid request | Check loan state and amount |
DO_NOT_HONOR | Cannot process | Review backdating/booking date requirements |
DUPLICATE_RECORD | Duplicate transaction | Use different transaction reference |
INSUFFICIENT_BALANCE | Insufficient funds | Ensure deposit account has adequate balance |
Integration Examples
Example 1: Automated Loan Repayment from Salary Account
{
"commandName": "InitiateLoanRepaymentWithDepositCommand",
"data": {
"accountEncodedKey": "LOAN456789123",
"depositAccountEncodedKey": "SAL123456789",
"amount": 35000.00,
"notes": "Monthly salary deduction for loan repayment",
"serviceId": "SALARY_DEDUCTION",
"serviceDescription": "Salary-Based Loan Repayment"
}
}
Scenario: On salary payment day, automatically deduct loan installment from employee's salary account.
Example 2: USSD Mobile Banking Repayment
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "LOAN777888999",
"channelEncodedKey": "CHANNEL_USSD",
"amount": 12500.00,
"notes": "USSD payment via *737# code",
"serviceId": "USSD_REPAY",
"serviceDescription": "USSD Loan Repayment"
}
}
Scenario: Customer dials USSD code to make loan payment from any mobile phone.
Example 3: Agent Banking Repayment
{
"commandName": "InitiateLoanRepaymentCommand",
"data": {
"accountEncodedKey": "LOAN321654987",
"channelEncodedKey": "CHANNEL_AGENT",
"amount": 18000.00,
"notes": "Payment collected by agent AG-1234",
"serviceId": "AGENT_REPAY",
"serviceDescription": "Agent Banking Repayment",
"repaymentChannelDetails": {
"institutionCode": "AGENT",
"institutionAccount": "AG-1234",
"reference": "AGT/2024/12/5678",
"note": "Collected by agent John Doe"
}
}
}
Scenario: Agent collects cash payment on behalf of the bank and remits to customer's loan.
Best Practices
For Operations Teams
- Payment Verification: Always verify customer details before processing repayment
- Receipt Generation: Issue payment receipts immediately after successful transaction
- Balance Confirmation: Confirm remaining loan balance with customer
- Documentation: Keep records of external payment references for reconciliation
- Exception Handling: Use backdating only for genuine corrections with proper authorization
For IT Integration
- Idempotency: Check for duplicate transaction keys before retrying failed payments
- Balance Checks: For deposit-based repayments, verify balance before initiating transaction
- Error Handling: Implement proper retry logic with exponential backoff
- Channel Tracking: Always provide channel information for payment source tracking
- Webhook Notifications: Set up webhooks to receive payment status updates
- Reconciliation: Store transaction keys for end-of-day reconciliation
For Customers
- Payment Confirmation: Always request and save payment confirmation receipt
- Account Monitoring: Check loan balance after payment to confirm posting
- Early Payments: Know your loan product's policy on early/advance payments
- Multiple Channels: Use the most convenient payment channel available
- Schedule Awareness: Understand your repayment schedule and due dates
Related APIs
| API | Relationship | Description |
|---|---|---|
| Create Loan | Related | Create the loan account |
| Get Loan Details | Related | Retrieve loan balance and schedule |
| Loan Pay Off | Alternative | Full early repayment |
| Initiate Transfer | Related | Transfer funds between accounts |
| Teller Deposit | Alternative | Process repayment through teller |