Aller au contenu

Reference donnees anti-fraude

Tables et colonnes

wallet_transactions

Colonne Type Notes
id uuid PK
customerId uuid FK vers customers
createdAt timestamp Date de creation
kind enum Type de transaction (voir section Kinds)
status enum waiting, confirmed, declined, canceled
value integer Montant en centimes. Negatif pour les debits (retraits, achats)
paymentMethodId uuid nullable Lien vers payment_methods. Renseigne a 100% sur withdrawal, JAMAIS sur topup_card/topup_checkout
context jsonb nullable Contexte specifique au kind. Discriminated union par type
referenceId uuid nullable Lien vers une WT parente (ex: withdrawal_fee -> withdrawal)
balance integer Solde apres la transaction
index bigint Ordre de creation (pour le ranking des debits)

customers

Colonne Type Notes
id uuid PK, sert aussi de accountId Lemonway
createdAt timestamp Date de creation du compte
email text Email du customer
withdrawableBalances jsonb { current, pendingCredit, pendingDebit } en centimes
deletedAt timestamp nullable Non null = compte supprime
transactionRights enum Droits de transaction

payment_methods

Colonne Type Notes
id uuid PK
customerId uuid FK vers customers
kind enum iban_withdraw ou virtual_iban_deposit
createdAt timestamp Date d'ajout
bic text nullable Code BIC de la banque
iban text nullable IBAN
deletedAt timestamp nullable Non null = supprime
lemonwayBankAccountStatus integer nullable Statut chez LW

primary_purchase

Colonne Type Notes
status_view text Statut de l'achat. confirmed = investissement reel
propertyId_view uuid Propriete achetee
json jsonb Contient investorId, brickCount, etc.
createdAt_view timestamp Date d'achat

Pour compter les investissements d'un customer :

SELECT COUNT(*)::int
FROM primary_purchase
WHERE (json->>'investorId')::uuid = $1
  AND "status_view" = 'confirmed'

Kinds de wallet_transactions

Topups carte (money-in CB)

Kind Description Utiliser pour l'analyse fraude
topup_card Paiement carte directe via Lemonway OUI
topup_checkout Paiement carte / Apple Pay via Checkout.com OUI
topup_wire Virement bancaire NON (pas une carte)
topup Legacy generique NON (ancien format)

Pour les analyses fraude, toujours utiliser kind IN ('topup_card', 'topup_checkout').

Retraits (money-out)

Kind Description Utiliser pour l'analyse fraude
withdrawal Retrait standard OUI
withdrawal_legacy Ancien format de retrait NON (ignore)
withdrawal_fee Commission de retrait NON (pas un retrait)

Pour les analyses fraude, toujours utiliser kind = 'withdrawal'.

Autres kinds utiles

Kind Description
primary_purchase Achat de bricks (debit)
revenue_royalty Revenu locatif
revenue_obligation_coupon Coupon obligataire

Statuts

Statut Signification
waiting En attente d'execution
confirmed Execute avec succes
declined Refuse
canceled Annule

Regles de filtrage par statut

Contexte Filtre a appliquer Raison
Topups carte pour analyse fraude Aucun filtre sur le statut Un topup declined reste une tentative suspecte (test de carte volee)
Retraits status = 'confirmed' Seuls les retraits effectivement sortis comptent
Investissements status_view = 'confirmed' sur primary_purchase Seuls les achats finalises

Pieges connus

paymentMethodId

  • Renseigne a 100% sur les withdrawal : lien exact vers l'IBAN utilise pour le retrait
  • JAMAIS renseigne sur les topup_card / topup_checkout (0 sur 848K+ transactions)
  • Impossible de distinguer les cartes depuis cette colonne. Pour le multi-cartes, il faut utiliser le card.id retourne par l'API Lemonway GET /moneyins

Colonnes legacy

  • customers.lemonwayBalance : alias legacy de availableWithdrawableBalance_view. Ne PAS utiliser pour le solde reel
  • withdrawal_legacy : ancien kind de retrait. Ne pas inclure dans les analyses recentes
  • topup (sans suffixe) : kind legacy. Ne PAS confondre avec topup_card

Montants

  • Tous les montants sont en centimes (integer)
  • value est negatif pour les debits (retraits : ex -50000 = 500 EUR retires)
  • Pour les montants en euros dans les tableaux : diviser par 100

Context jsonb

  • Nullable sur les withdrawals
  • Pour les withdrawals, le context contient exactement deux champs :
{
  "type": "withdrawal",
  "withdrawRequestId": "..."
}

Le context de la wallet_transaction ne porte aucune information anti-fraude. La decision anti-fraude est synchrone (block/allow) et stockee sur l'entite WithdrawRequest (champs outcome = allowed | blocked et fraudCheck = { version, description, config, metrics, decision: { blocked, fraudChecks } }).

Pour analyser les decisions anti-fraude, requeter withdraw_requests (champs outcome / fraud_check), pas wallet_transactions.context.

BICs de reference

Neobanques exotiques (signal fort de fraude)

BIC Banque Pays Nb fraudeurs (sur 53)
DBLKFR22XXX Deblock France 10
BZENLT22XXX Zen.com Lituanie 9
INTFBGSFXXX iCard Bulgarie 2
CFTEMTM1XXX (fintech) Malte 2
EVIULT2VXXX kevin. Lituanie 2
SUMUIE22XXX SumUp Irlande 2
SUPULT22XXX (fintech) Lituanie 2

Banques mainstream (attention faux positifs)

BIC Banque Nb fraudeurs
REVOFRP2XXX Revolut 10
BFBKFRP1XXX BforBank/BRED 9
TRWIBEB1XXX Wise 4
BOUSFRPPXXX Boursorama 4

Signal : IBANs hors-FR

Un client francais avec un IBAN non-FR (prefixes LT, BG, MT, IE, BE) est un signal d'alerte. A combiner avec d'autres signaux, pas en standalone.

Regles de blocage runtime (FraudCheckV2)

Les regles ci-dessous sont evaluees a chaque tentative de retrait investisseur via WithdrawFraudService.buildAndCheckWithdrawRequest.

Inputs (WithdrawContext)

Champ Source SQL
accountAgeDays (NOW()::date - customers.createdAt::date)::int
confirmedInvestmentCount Count de wallet_transactions avec kind = 'primary_purchase', status = 'confirmed', et (context->>'refundableUntil')::timestamptz < NOW()
cardTopupCount Count de wallet_transactions avec kind IN ('topup_card', 'topup_checkout') — aucun filtre sur status
bic payment_methods.bic du moyen de paiement selectionne (trimme avant evaluation)

Regle A — Pattern fraude (AND composite)

Condition Seuil V2 Seuil V1 (historique)
Compte recent accountAgeDays < 60 idem
Pas d'investissement confirme non remboursable confirmedInvestmentCount = 0 idem
Topups carte cardTopupCount >= 1 >= 3

Regle B — BIC blackliste (OR independant)

Le BIC (trimme) est teste en prefixe (pas egalite stricte) contre la liste : DBLKFR22, BZENLT22, INTFBGSF, CFTEMTM1, EVIULT2V, SUMUIE22, SUPULT22.

Decision

blocked = matchesFraudPattern || isBlacklistedBic

Bypasses

  • Admin (executedBy === 'admin') : jamais bloque
  • Whitelist (investor_withdraw_whitelist row exists) : jamais bloque

Le fraudCheck complet (version, config snapshot, metriques, decision avec flags individuels) est persiste dans investor_withdraw_requests quel que soit l'outcome.

Difference V1 vs V2

Seul changement : minCardTopupCount passe de 3 (V1) a 1 (V2). V1 reste dans le code pour la validation des records historiques (discriminated union sur version), mais seul V2 est evalue a runtime.