feat(cfcfrms): I4C SOP layer chain, outward transfer ledger, bank holds and RBI 2026 liability
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Waiting to run
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Waiting to run
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Waiting to run
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Waiting to run
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Waiting to run
Fineract Documentation build / build (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Waiting to run
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Waiting to run
Fineract Liquibase Only mode - PostgreSQL / test (push) Waiting to run
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Waiting to run
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Waiting to run
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Waiting to run

- Layer-aware hold handling per MHA SOP 02.01.2026: prospective holds only on
  first-layer accounts, onward transfers reported by I4C bank code at layer 2+
- IFSC to I4C bank code map (migration 0617, /v1/i4c/cases/ifsc-map)
- Outward NEFT/IMPS/RTGS booked through Outward Suspense and Settlement GLs;
  MRM restorations to other banks via I4C Restoration Payable (migration 0614)
- Bank holds, exclusions, customer liability and show-cause (0615, 0616)
- Book debits honour every hold and the savings debit block; current account
  debit freeze enforced on all debits; channel block guard on fund transfers
- Drops the restoration pool account setting in favour of GL-based booking

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 11:22:47 +05:30
co-authored by Claude Opus 5.5
parent f016bde385
commit e0cbb80ca4
40 changed files with 3771 additions and 153 deletions
+85 -10
View File
@@ -22,14 +22,28 @@ I4C ──POST /complaints (Bearer)─────► bank checks token + sour
find the account (savings, then current)
layer 0 → find the debit, report where the money went
layer ≥1 → hold up to the disputed amount at once,
trace onward debits for any shortfall,
keep the rest as a prospective hold
report where the rest went (next bank, by I4C bank code),
layer 1 only: keep the rest as a prospective hold
▼
bank ──POST <I4C auth>───────────────► I4C token
bank ──POST <I4C response> (Bearer)──► I4C section 6 message: acknowledgement_no, job_id, transactions[]
(outbox: retried with backoff until delivered)
```
### The chain: Bank A → Bank B → Bank C (MHA SOP 02.01.2026, para 6(g), 6(j), 9.1)
- The victim complains on NCRP / 1930. CFCFRMS sends the complaint to the **victim's bank (A, layer 0)** and to the bank that received the money (**B, layer 1**), each with its own layer number. A reports which debit it was and where it went; B holds what is left.
- If B's customer had already sent part of it to **Bank C**, B reports a *Money Transfer To* entry naming C by its **I4C bank code**. CFCFRMS then sends C its own complaint as **layer 2**, and so on "till the complete money trail is established" (SOP 6(j)).
- This CBS plays every role. It reads the layer in each incident:
- **Layer 0:** it traces the victim's debit.
- **Layer 1** (a first-layer account, SOP definition (x)): it holds the disputed amount, reports the onward transfers for what is gone, and keeps a **prospective hold** for the gap. The prospective hold tops up from later credits (SOP 9.1(i)).
- **Layer 2 and later:** it holds what is there and reports onward. It places **no prospective hold**, because SOP 9.1(i) limits that to first-layer accounts.
- **The next bank must be named by its code.** The seeded master knows only some IFSC prefixes, and co-operative banks often use a sponsor bank's IFSC series. Bank Master > *IFSC to I4C Bank Code map* (migration 0617, endpoints `GET/POST /v1/i4c/cases/ifsc-map`) maps a prefix or a full IFSC to the right code. It also lists the IFSCs already reported with code 0, and the dashboard counts them.
- **Money that moved within this bank** (to another savings / current account here, or to a loan) is raised as a linked alert by the Hold Review job, so the officer can act before CFCFRMS sends the next-layer complaint back to us.
- **Repeat pushes:** the same reported transaction (ack, account, layer, RRN, amount) arriving again in a new request finds the earlier hold. It is never held twice.
- **Freeze and digital suspension** need the LEA's direction (order / notice reference, SOP 9.1(i), S.106 BNSS). Without it, only the disputed amount is held.
- The hold detail shows the customer's **other accounts** here, to report as related accounts (SOP 9.1(v)). They are not held automatically.
### Response types produced
| txn_type_id | txn_type | When |
@@ -51,7 +65,7 @@ The Money Transfer To entry carries the payee reference under **both** `rrn` (th
These follow the MHA SOP for NCRP-CFCFRMS (02.01.2026) and RBI's draft KYC Amendment Directions on debit holds (Sep 2026).
- **Only the disputed amount is held** as an amount hold (`AMOUNT_HOLD`). The rest of the balance stays usable. Every debit path honours the hold: counter, transfer, standing instruction, CTS inward and NACH.
- **Prospective hold:** what the balance cannot cover is recorded as a shortfall. The *I4C Hold Review* job tops it up from later credits.
- **Prospective hold** (first-layer accounts only, not nodal / pool / escrow, SOP 9.1(i)): what the balance cannot cover is recorded as a shortfall. The *I4C Hold Review* job tops it up from later credits. At layer 2+ the gap is reported onward and is not held from future credits.
- **Full debit freeze is never automatic.** An officer asks for it (savings only) and a different officer approves it.
- **Review date** is set on every hold: 90 days by default. The register flags holds 15 days before it.
- **Customer intimation deadline:** 24 hours by default ("by the end of the next day"). It is tracked in the register until an officer records how the customer was told.
@@ -157,7 +171,6 @@ In the UI: **CFCFRMS → I4C Live**. Its tabs are Dashboard, Complaints from I4C
| `…ack.duplicate-mode` / `FINERACT_I4C_ACK_DUPLICATE_MODE` | `SAME_JOB` (00 + original job_id) or `CODE_50003`. Default `SAME_JOB`. |
| `…ack.max-future-days` | A `transaction_date` further ahead than this is refused (40005). Default 1. |
| `…hold.review-days`, `…hold.intimation-hours` | Defaults 90 and 24. |
| `…restoration.pool-account-no` / `FINERACT_I4C_RESTORATION_POOL_ACCOUNT` | Bank-owned savings account (of a bank client) that outward MRM restorations leave from. Blank = only restorations to accounts in this bank. |
| `…restoration.fir-threshold` | MRM: above this amount in one complaint the IO must give an FIR. Default 50000. |
| `…simulator.enabled` | Local stand-in for I4C. **Never `true` in production.** |
@@ -184,11 +197,72 @@ To generate the bcrypt hash: `htpasswd -bnBC 10 "" 'the-password' | tr -d ':\n'`
- `m_i4c_intimation_log` (every notice sent, failed or recorded, per hold);
- column `m_i4c_hold.auto_intimation_attempts`;
- permission `CONFIGURE_I4C`.
- **Migration `0617_i4c_ifsc_bank_code_map.xml`:** `m_i4c_ifsc_map` (the bank's own IFSC prefix / full IFSC -> I4C bank code).
- **Migration `0613_i4c_grm_mrm.xml`:**
- `m_i4c_grievance`, `m_i4c_grievance_event` (GRM), `m_i4c_restoration` (MRM, beneficiary and victim bank), `m_i4c_document` (files, with SHA-256);
- columns `m_i4c_hold.released_amount`, `restored_amount`; `m_i4c_hold_action_request.amount`, `restoration_id`, `grievance_id`;
- permissions `MANAGE_I4CGRIEVANCE`, `MANAGE_I4CRESTORATION`.
### Accounting (Organisation > Clearing GL Configuration)
Map these GL roles (migration 0614 adds them unmapped):
| Role | GL (example from the chart) | Use |
|---|---|---|
| `FUNDTRANSFER_OUTWARD_SUSPENSE` | 200502 NEFT OUTWARD SUSPENSE (liability) | money sent, not yet settled |
| `FUNDTRANSFER_SETTLEMENT` | the sponsor bank / RBI current account (asset), e.g. 100201 | settled outward transfers |
| `I4C_RESTORATION_PAYABLE` | a sundry liability, e.g. under 200900 | restored money owed to victims in other banks |
| `I4C_SHADOW_CREDIT` | a receivable (asset) | shadow credits to our customers on reported fraud |
Entries:
- Hold / release: none (a lien on the account, memo only).
- Restore to a victim here: Dr suspect's Savings / Current Control, Cr victim's control (account transfer).
- Restore to another bank: approval Dr suspect's control, Cr I4C Restoration Payable; sending Dr Payable, Cr Outward Suspense; settlement Dr Outward Suspense, Cr Settlement; failure Dr Outward Suspense, Cr Payable.
- Every outward NEFT / IMPS / RTGS (not only I4C) now books the same way once `FUNDTRANSFER_OUTWARD_SUSPENSE` is mapped: Dr payer's Savings Control, Cr Outward Suspense; settlement on SUCCESS; failure / recall credit the payer back. Unmapped, the earlier savings "suspense account" behaviour continues.
- Shadow credit: Dr I4C Shadow Credit, Cr customer's control; regularisation when restored money arrives: Dr customer's control, Cr I4C Shadow Credit.
Also fixed in the CBS core: a book debit (demand draft, outward transfer) now honours every hold and the debit block on savings accounts, and a block on a current account (debit freeze) is enforced on all its debits.
### MHA SOP duties tracked (migration 0614, permission `MANAGE_I4CRISK`)
- **15-day credit** to the victim after the IO's notice; **15-day challenge** by the suspect after the refund.
- **90-day lapse** counted from the bank's GRM grievance; notice to the LEA due 15 days before; an LEA extension recorded; a release "90-day review" without an order is allowed only after the lapse with no extension.
- **Digital banking suspension** on a repeatedly reported account (hold action SUSPEND_DIGITAL / RESTORE_DIGITAL, maker-checker); lifted with the last hold on the account.
- **Mule register**: an account in 2+ complaints is flagged SUSPECTED automatically; confirm or clear with the basis.
- **Fraud reporting** (RBI (UCB - Fraud Risk Management) Directions, 2026, RBI/DoS/2026-27/439, 31.07.2026): opening the case starts the FIU-IND STR (7 days, PML Rules); the account holder gets a show-cause notice with at least **21 days** to reply and a reasoned order before classification; the RBI FMR is then due within **14 days**.
- **Linked alerts:** after the reported credit, the Hold Review job raises an alert for each transfer from a held account to a loan here (SOP 9.1(v)) or to another savings / current account here.
- **Victim bank, customer protection**: the framework follows the transaction date.
- Before 01.01.2027 - the 2017 framework: zero liability on bank fault or a third-party breach reported within 3 working days, limited liability for 4-7 working days (Rs 5,000 BSBD / 10,000 savings / 25,000 current), shadow reversal within 10 working days, resolution within 90 days.
- From 01.01.2027 - RBI (UCB - Responsible Business Conduct) Third Amendment Directions, 2026 (RBI/2026-27/172, 24.06.2026): zero liability on bank negligence or a third-party breach reported within **5 calendar days**; resolution within **45 calendar days** (60 cross-border); compensation for customer-negligence losses up to Rs 50,000 = 85% of the net loss or Rs 25,000, whichever is less, once in a lifetime, reported to the bank and NCRP / 1930 within 5 calendar days, paid within **5 calendar days** of the application (maker-checker), shared RBI / this bank / beneficiary bank and claimed quarterly (CSV: `compensation-claims.csv`); for frauds up to 31.12.2027.
- A shadow reversal is provisional: it is credited with a lien so the customer cannot use it until zero liability is established ("make final") or it is recovered from restored money.
- GL roles: `I4C_SHADOW_CREDIT`, `I4C_COMPENSATION_RECEIVABLE` (RBI and beneficiary-bank shares), `I4C_COMPENSATION_EXPENSE` (this bank's share).
- **Nodal / pool / escrow accounts** (migration 0615), listed on Risk Registers (SOP 9.1(iii)):
- the **disputed amount is held** like on any other account;
- **no prospective hold** is placed on future credits;
- LEAs normally do not ask for suspension or seizure.
(Corrected 23.10.2026: an earlier build skipped holds on these accounts altogether. That contradicted SOP 9.1(iii).)
- **GRM deadlines** as the GRM manual's flow and SOP 9.1 set them: the bank raises the grievance within **7 days** of the customer approaching it, the IO decides within **15 days** (15 more if investigating), and an IO who does not act in time can be escalated to the District GRO.
### Bank-initiated suspected-mule holds (RBI draft, 11.09.2026)
RBI's draft amendment to the KYC Directions (Annex III, effective 01.04.2027 if finalised; banks may adopt earlier) lets the bank itself hold a suspected money-mule transaction. Risk Registers → *Bank-initiated holds*:
1. Hold a flagged transaction of Rs 1,000 or more at once (its amount), or all debits on the account as a last resort, with the reason. The customer is told on the automatic intimation channels (or the officer records it).
2. The customer has **20 days** to explain; the bank decides within **10 days** of the explanation (**30 days** from the hold without one).
3. Satisfied: lift. Not satisfied: refer to the police through NCRP; the police have **30 days** to instruct, otherwise the hold lifts on day 31.
4. Never more than **60 days** without an LEA instruction. The I4C Hold Review job lifts holds at the end of these periods.
5. Grievances are resolved by the nodal officer within **30 days**.
Not for nodal, pool or escrow accounts.
### Sources and versions this is built on (checked 23.10.2026)
| Source | Version used |
|---|---|
| RBI letter on NCRP-CFCFRMS API integration | CO.DOS.KYC.No.S6675, 05.12.2025 |
| I4C-MHA API Specification (Banks) | 2.0 (no later public version found) |
| MHA SOP on NCRP-CFCFRMS custody, restoration and grievance | 02.01.2026 (I4C CEO letter of 03.07.2026 implements it; no revision found) |
| GRM user manual / MRM user manual | V3 03.06.2026 / V4 30.07.2026 (as supplied) |
| Customer protection in fraudulent EBTs (UCBs) | RBI/2026-27/172, 24.06.2026, from 01.01.2027; 2017 framework before |
| Fraud risk management (UCBs) | RBI/DoS/2026-27/439, 31.07.2026 |
| Money-mule temporary debit holds | RBI draft amendment to KYC Directions, 11.09.2026 (still a draft; final directions to be issued) |
| Supreme Court, digital arrest suo motu | Order of 04.08.2026 (GRM / MRM operationalisation, RBI mule SOP) |
### GRM: grievances against a hold
GRM and MRM are I4C web portals with no API. Officers act there; the CBS keeps the bank's record, the deadlines and the documents, and carries orders out on the account through maker-checker.
1. The customer asks the branch to remove the hold / unfreeze. Open a grievance on the hold (Holds Register → hold → **GRM / MRM**).
@@ -205,7 +279,7 @@ GRM and MRM are I4C web portals with no API. Officers act there; the CBS keeps t
1. **Assess** each complaint (hold → GRM / MRM → *Assess for MRM*): *possible by attribution*, *possible pro-rata* (the calculator splits what is held on an account by disputed amount), or *not possible* (already refunded / no balance / other) with the reason. Enter the same on MRM; the **MRM Balance Report** page gives every held account's balances for MRM's *Update Account Balance* (CSV).
2. When the victim's request comes from the IO, record the MR id, the refund account (IFSC blank = this bank), the IO, and the papers: a **court order**, or the IO's **BNSS 106 notice** with the victim's **indemnity bond**, plus the **FIR** above Rs 50,000. Attach their copies.
3. **Bank action:** *Compliance* checks the papers and raises a RESTORE request; *not complied* and *refer back to LEA* need the bank statement; *refer back to victim bank* and *consensus* are recorded for MRM.
4. A second officer approves the RESTORE on Approvals. In the same transaction the amount is released from hold and leaves the suspect account: straight to the victim's account if it is here (RESTORED at once, transaction id `CBS-<id>`), otherwise to the **restoration pool** (IN_POOL) and on by NEFT / IMPS / RTGS through the fund-transfer module (its limits, time window, screening and approval apply). TRANSFER_PENDING becomes RESTORED when the transfer settles (Hold Review job). A failed or refused outward leaves the money in the pool for **Send again**, possibly to a corrected account; it never goes back to the suspect.
4. A second officer approves the RESTORE on Approvals. In the same transaction the amount is released from hold and leaves the suspect account: straight to the victim's account if it is here (RESTORED at once, transaction id `CBS-<id>`), otherwise book-debited to the **I4C Restoration Payable** GL (IN_POOL) and paid by NEFT / IMPS / RTGS from that GL through the fund-transfer module (its time window and payee screening apply). TRANSFER_PENDING becomes RESTORED when the transfer settles (Hold Review job). A failed or refused outward returns the money to the payable for **Send again**, possibly to a corrected account; it never goes back to the suspect. The bank must credit the victim within **15 days** of the IO's notice (the notice date is recorded with the request), and after the refund the suspect has **15 days** to contest it (recorded on the restoration).
5. Enter amount, transaction id / UTR and time on MRM, then mark **Entered on MRM** to close.
**Victim bank** (our customer lost the money, layer 0): open a victim-bank case with the complaint's ack no., record restored money received (amount, UTR, date, account), or why none is expected (already refunded through bank / police notice / court order, declined, declined to pursue, received through court, not reachable).
@@ -302,7 +376,7 @@ The backend unit tests are under `fineract-provider/src/test/java/org/apache/fin
- customer intimation: check the automatic notice in the hold's intimation history, press **Send now** to resend, or call the customer and record it;
- raise releases on IO / court orders (in full, or the amount the order releases); GRM and MRM orders are raised from their records;
- raise a full debit freeze only on an explicit direction.
- **GRM Grievances / MRM Restorations:** filter *Past deadline only* every morning; carry out allowed orders within 48 hours; send pooled restorations again; mark restorations entered on MRM.
- **GRM Grievances / MRM Restorations:** filter *Past deadline only* every morning; carry out allowed orders within 48 hours; send restorations held in the payable again; mark restorations entered on MRM; record LEA notices 15 days before a hold lapses; review suspected mules; file FMR / STR by their due dates; action loan-repayment alerts (Risk Registers page).
- **Approvals:** a second officer approves or rejects. Approval acts on the account immediately. If the account refuses (closed, hold already released at the counter), the request becomes FAILED with the reason.
- **Responses & API Log:**
- retry FAILED responses;
@@ -330,12 +404,13 @@ The backend unit tests are under `fineract-provider/src/test/java/org/apache/fin
- **Follow-up:** store the UTR/RRN on inward postings.
- **No UPI, ATM, POS or AEPS channels.** The response formats for ATM (5), AEPS (6) and POS (11) are built and tested, but nothing produces them until such a channel exists.
- **Branch cash withdrawals** have no specification type. Above Rs 500 they are reported as type 14 with an empty cheque number and remarks "Cash withdrawal at branch counter (no cheque)". **Confirm with I4C.**
- **Current accounts:** the amount hold works, but the CBS's current-account debit block does not reach the account, so a full debit freeze is offered on savings accounts only.
- **Current-account transfer legs** are not linked transaction-to-transaction, so onward tracing from a current account is limited to counter withdrawals.
- **Payee bank name:** only IFSC prefixes in the seeded list map to an I4C bank code. Others are reported with code 0 and the IFSC in full.
- **Payee bank name:** IFSCs not in the seeded prefixes or the bank's own IFSC map are reported with code 0 and the IFSC in full. Until they are mapped, CFCFRMS may not be able to route the next-layer complaint by code. They are listed on Bank Master.
- **GRM and MRM** are I4C portal modules with no published API. Officers act there and record it here; nothing is sent to the portals automatically.
- **Outward restorations** go only from a savings pool account (the fund-transfer module debits savings accounts only); the pool must be configured, and its client's transfer limits must allow the amounts.
- **Current accounts** cannot be debit-frozen (above); GRM *unfreeze* orders apply to savings accounts only.
- **Outward restorations** need the GLs below mapped; without them only restorations to accounts in this bank can be approved.
- **Digital-channel suspension** is enforced on the CBS's own fund transfers. UPI, ATM, cards and AePS are not run by this CBS; the suspension is listed for the systems that run them.
- **Shadow credits** are booked to savings accounts; working days skip weekends only (bank holidays are not known to the CBS).
- **RBI FMR / FIU-IND STR** are filed on their own portals; the CBS keeps the due dates, references and a CSV of the cases.
---