Worked allocation example

Take a synthetic loan with ₦1,000 overdue fee, ₦2,000 interest, and ₦10,000 principal. If the contract says fee first, then interest, then principal, a ₦2,500 repayment covers ₦1,000 fee and ₦1,500 interest, with no principal reduction. A replay of the same provider payment ID must keep those three balances unchanged. A second payment of ₦2,500 has a new ID and should cover the remaining ₦500 interest plus ₦2,000 principal. Use the actual signed loan terms when creating expected results for a real product.

Compare math and authority

Write the payment allocation rule from the loan contract into an expected-results table. Include fee, interest, principal, arrears, and overpayment. Run every case against a fixed loan snapshot, then try the same payment twice and at the same time from two clients.

Checks to run

  1. Reject client-supplied fee waivers or principal allocations without an authorized policy change.
  2. Run partial, exact, overpayment, and duplicate-payment cases against the same contract version.
  3. Lock or version the loan balance during concurrent repayments so both cannot spend the same outstanding amount.

Partial payment checks

Use one fixed contract example and calculate the expected fee, interest, and principal portions by hand before testing the code. Pay less than the fee, exactly the fee, enough to cover fee and interest, and more than the whole balance. Confirm the output sums to the input in every case.

Reject a negative amount, currency mismatch, or payment linked to another loan. The result must not change if the same provider event arrives again.

One more boundary test

Add a policy-change case. A loan opened under allocation rule V1 may still receive a payment after rule V2 is deployed. The payment should use the rule tied to the contract unless an agreed change says otherwise. Create two loans with the same balances but different rule versions to prove that the code does not take one global shortcut. Also test an overpayment: the extra amount needs a stated treatment, such as a held balance or refund, rather than an unexplained negative principal.

Check the repayment arithmetic

Use a synthetic ₦10,000 installment with ₦500 fee, ₦1,500 interest, and ₦8,000 principal under a contract that applies fees first, then interest, then principal. For a ₦2,000 payment, the expected split is ₦500 fee and ₦1,500 interest; principal stays ₦8,000. Send the same request with a client field that claims all ₦2,000 is principal. The server must ignore that field and return the contract-based split. Next, post the payment twice with the same operation ID. Total allocated across both attempts must still be ₦2,000. Keep the contract version with the entry so a later rule change cannot rewrite history.

Include the overpayment line in the total

Under the first synthetic contract, outstanding fee, interest, and principal total 13,000 NGN. Pay 14,000. The expected rows are 1,000 fee, 2,000 interest, 10,000 principal, and 1,000 overpayment held or refunded under the stated contract. The three debt components alone do not total the receipt. Use integer minor units or fixed decimals and state the rounding rule. Post the receipt, allocations, and remaining balances atomically. Inject a crash between receipt storage and allocation: recovery must complete one allocation or leave a visible unresolved receipt. Replay the provider payment ID after recovery and confirm the four allocation lines do not appear twice.

Primary sources

Next step

Calculate one partial repayment by hand, then compare each fee, interest, and principal row with the API output. Read the related guide. For a review of your own system, request a security review.