ADORSYS-GIS / ADORSYS-GIS/fineract-aml

Afterpay clearance — AML veto for merchant-funded BNPL (reuse graph_service + scoring_service)

オープン
#3 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
0
フォーク
0
PR マージ指標
30日以内にマージされた PR はありません

説明

> **Status:** Design/spec ticket. This is the **AML-side dependency** of the afterpay (merchant-funded BNPL) design in [webank-mobile#103](https://github.com/ADORSYS-GIS/webank-mobile/issues/103). It scopes only what AML must expose; the product flow lives in #103.

## Summary

Afterpay lets a customer split an in-app merchant payment into installments while the **merchant is paid in full upfront** — Webank fronts the cash over the Fineract credit rail. Because real money leaves before the customer repays, **collusion** (fake merchant + fake buyer to extract the upfront cash) is the dominant fraud. Per the agreed module boundaries (#103 §8), **AML is the risk authority / veto**; the commercial "who is a good merchant to invite" scorecard stays out of AML.

This ticket adds an **afterpay clearance** capability that reuses existing AML services — it does **not** build new risk infrastructure.

## What AML must expose

A **clearance verdict** at two gating points:

| Gate | When | Latency |
|------|------|---------|
| **(a) Entity clearance** — is this merchant safe to *invite* to afterpay? | Periodic / batch, before the app shows the offer | Async OK |
| **(b) Transaction clearance** — is *this* afterpay disbursement safe? (merchant + buyer + amount) | Real-time, at checkout, **before fronting cash** | Synchronous, low-latency |

Gate (b) is where collusion crystallises (the cash-out moment), so it must be in — or pre-cached for — the synchronous checkout path.

### Proposed endpoints (mirror the existing `api/credit.py` pattern)

```
POST /afterpay/clearance/merchant # entity-level (gate a)
body: { user_id }
returns: ClearanceVerdict

POST /afterpay/clearance/transaction # per-disbursement (gate b)
body: { merchant_user_id, buyer_user_id, amount, currency }
returns: ClearanceVerdict
```

### `ClearanceVerdict` schema (new `schemas/afterpay.py`)
```
{
"decision": "clear" | "hold" | "deny",
"risk_score": float,
"reasons": [string], // human-readable, audit-safe
"linkage_findings": [ // from graph_service
{ "type": "shared_device|shared_sim|cni_family|recovery|referral|funding|cycle",
"from": user_id, "to": user_id, "confidence": float }
],
"expires_at": timestamp // entity verdicts are cacheable until expiry
}
```

## Reuse — what this builds on (no new engines)

| Need | Existing AML service | Use |
|------|----------------------|-----|
| Collusion cycles / clusters; **buyer ⇄ merchant linkage** (device/SIM/CNI/recovery/referral/funding edges) | `graph_service.py` (`api/graph.py`, `schemas/graph.py`) | Counterparty-independence check + closed-loop detection |
| Entity & transaction **risk score**, velocity / structuring anomaly | `scoring_service.py` (`api/scoring.py`) | Drives `risk_score` + `decision` |
| Transaction history / monitoring | `transaction_service.py` | Inputs for scoring & velocity |
| Sanctions / adverse media | `sanctions_service.py`, `adverse_media_service.py` | Hard-deny conditions |
| **Hold/deny follow-up** | `alert_service.py`, `case_service.py` | A `hold`/`deny` raises an alert / opens a case |
| **Audit trail** | `audit_service.py` | Every verdict is logged, regulator-facing |
| Precedent | `credit_service.py` / `api/credit.py` | Afterpay *is* merchant-funded credit — same clearance shape |

New code is thin: `api/afterpay.py` + `services/afterpay_service.py` + `schemas/afterpay.py` that **orchestrate** the above into a verdict.

## Boundary (mandate purity — #103 §8)

- AML returns **risk only**. It must **not** compute the merchant-quality / commercial scorecard (fan-in breadth, counterparty entropy, conversion fit) — that lives in webank-mobile's behaviour-profile layer. Mixing growth-targeting into a module that files SARs/CTRs would pressure risk thresholds toward conversion.
- The caller (BFF) composes: `invite = commercial_score ≥ T AND AML.clearance(entity)==clear`; `disburse = AML.clearance(txn)==clear AND credit_eligible AND within caps/reserve`.

## Collusion signals AML should weigh (gate b)

- Closed-loop / cyclic flow among a tight cluster (`graph_service`).
- Merchant's "customers" are also its **funders**.
- Buyer ⇄ merchant share **device / SIM / CNI-family / recovery contacts / referral chain / funding source** → `counterparty_independent = false` → `hold`/`deny`.
- Buyer or merchant under an open AML case/alert.
- Velocity / behaviour cliff right after a merchant's afterpay opt-in.

## Non-functional

- **Latency:** gate (b) target p95 well under the checkout budget; pre-cache entity-level risk, compute only the incremental buyer⇄merchant linkage + amount check inline.
- **Idempotency & audit:** every verdict persisted via `audit_service` with inputs + reasons.
- **Fail-closed:** if AML is unavailable at gate (b), default to **deny/hold** (do not front cash uncleared).
- **Verdict caching:** entity verdicts cacheable until `expires_at`; transaction verdicts are single-use.

## Open questions

1. Push model vs. pull — should AML also emit a `webhook` (`api/webhook.py`) when a previously-cleared merchant becomes high-risk, so BFF can revoke afterpay proactively?
2. Risk-score thresholds for `clear` / `hold` / `deny` — reuse the credit risk bands or define afterpay-specific bands?
3. Does `hold` auto-open a case, or only alert until an operator triages?
4. Linkage-edge sources available today in `graph_service` (which of device/SIM/CNI/recovery/referral/funding are already modelled vs. to be added)?

## Phasing

- **MVP:** synchronous `POST /afterpay/clearance/transaction` (gate b) reusing `graph_service` + `scoring_service`; fail-closed; audit; `hold`/`deny` raises an alert. Entity clearance (gate a) can start as a batch query over the same logic.
- **V2:** webhook-driven proactive revocation; afterpay-specific scoring band; case auto-open with `escalation_service`.

## Links

- Product design & flow: [webank-mobile#103](https://github.com/ADORSYS-GIS/webank-mobile/issues/103)
- KYC model & behaviour profile: webank-mobile#87
- COBAC-safe fee framing: webank-mobile#95

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。