ADORSYS-GIS / ADORSYS-GIS/fineract-aml
Afterpay clearance — AML veto for merchant-funded BNPL (reuse graph_service + scoring_service)
- 主要言語
- 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 はまだ評価されていません。