Fraud

The fraud module (schema fraud) owns three things no other module wants to: an append-only order-attempt log for sliding-window velocity checks, an operator-managed blocklist (bans on ip / email / card_fingerprint / person), and the pure AVS/CVV + velocity decisioning that checkout runs before money moves. It is its own module (not a table inside checkout or payments) because it owns a schema and is a shared decision surface — checkout composes it through the published createFraudRepo seam and calls it inside the order-placement path.

Enrolment and enforcement of velocity + AVS/CVV happen in checkout; this module supplies the durable state and the pure policy. The AVS/CVV code->disposition mapping itself lives in payments (evaluateAvsCvv), because that is where the gateway codes surface.

Privacy / no enumeration oracle: every subject is stored only as a tenant-salted fingerprint (domain/fingerprint.ts), never in the clear — no raw email, IP, or PAN is persisted. And checkout refuses a banned entity with the same generic decline a normal card decline produces, so probing “is this email banned?” through checkout is impossible.

Operator routes

x-operator-token, rate-limited. Each ban write is audited.

MethodPathPurpose
POST/v1/tenants/:tenant/fraud/bansAdd a ban on an ip/email/card_fingerprint/person subject (stored as a tenant-salted fingerprint).
GET/v1/tenants/:tenant/fraud/bansList the tenant’s active bans (fingerprint previews only).
DELETE/v1/tenants/:tenant/fraud/bans/:idLift a ban.
GET/v1/tenants/:tenant/fraud/velocity-eventsRead the tenant’s recent order-attempt velocity events (the append-only sliding-window log).
GET/v1/fraud/auditOperator audit-log read (makeAuditReadRoute).

Merchant routes

fdk_ credential, tenant from the credential per credential-carries-ownership, scope manage:own_fraud. Same repo methods as the operator ban routes — mounted at /v1/merchant/fraud.

MethodPathScopePurpose
GET/v1/merchant/fraud/bansmanage:own_fraudList the caller’s bans.
POST/v1/merchant/fraud/bansmanage:own_fraudAdd a ban.
DELETE/v1/merchant/fraud/bans/:idmanage:own_fraudLift a ban.

Events

DirectionEvent
Publishes(none)
Consumes(none)

Decisioning is synchronous and in-process (checkout calls the repo on the order-placement path); there is no bus traffic.

Manifest gates

None. Fraud decisioning is a server-side gate inside checkout; it has no storefront manifest toggle.

Seams (honest notes)

  • Velocity + AVS/CVV enforcement lives in checkout, not here — this module is the durable state (the attempt log + blocklist) and the pure policy the checkout path consults.
  • Bans are keyed to a fingerprint an operator copied from the fraud audit trail, never a raw identifier — there is no reverse lookup from a ban row back to a plaintext email/IP/PAN.