Bank rail
Loanly talks to the bank through two ports — abstractions the domain depends on, so the core never knows which bank it is:
- Disbursement gateway (money out) — pays the borrower at activation.
- Collection gateway (money in) — registers collectibles as bank claims the borrower's bank presents, and returns the payments back.
Real rail vs. stubs
AddBankRail wires the ports based on the Iobs configuration section:
- Configured → the real rail: a SOAP integration with an Icelandic online-banking service (the
Iobsadapter inLoanly.Adapters), with claim and payment adapters behind the two gateways. - Not configured (default) → in-memory stubs, so dev, test, and demo run with no bank.
The domain and every servicing sweep only ever see the ports, so switching between the stub and the real rail is a configuration change, not a code change.
Collection flow
- Charge — the daily charging sweep turns each due installment (on the active calculation) into a
collectible and registers it as a claim on the collection rail. It's idempotent: an installment
moves
Scheduled → Dueonce, so a re-run never double-charges. - Reconcile — a sweep pulls settled bank-claim payments back and records each through the normal payment path, mapped to its loan by the claim reference. A deterministic idempotency key per payment makes re-syncing the same window a safe no-op.
- Recall — when a loan is re-termed, prepaid, or paid up, its open claims are recalled so the borrower's bank stops presenting them; the charging sweep performs the rail cancel. A straggler payment on a recalled claim still lands correctly on the current schedule (payments always target the current calculation), so the balance nets out either way.
Disbursement flow
Money out is the mirror image: activation records a pending payout, and a background sweep dispatches it to the disbursement gateway, recording the outcome. Failed payouts are retried idempotently. See Disbursement for the full model.
Configuration
The Iobs section (unset by default) holds the endpoints, credentials, signing certificate, and the
claimant / payout-account identity:
{
"Iobs": {
"ClaimsEndpoint": "https://…",
"PaymentEndpoint": "https://…",
"Username": "…",
"Password": "…",
"CertificateBase64": "…",
"CertificatePassword": "…",
"ClaimantId": "…",
"PayoutAccount": "…",
"PayoutAccountOwnerId": "…"
}
}
With the section empty, the stubs are registered and no bank is contacted — which is why the console demo and the test suite run end-to-end with nothing to configure.