Aller au contenu

Recertif KYC Lemonway

Lemonway peut rouvrir un KYC update sur un investisseur déjà accepté (requirements[] type KYC_UPDATE, éventuellement restrictions KYC_OUTDATED). Bricks expose ça sur investorIdentityStatus — pas via un nouveau champ /me, et pas via le formulaire Excel admin (KYC Update Lemonway Form).

Le profile LENDER reste ACCEPTED. L'identité des déjà-onboardés se calcule avec computeAcceptedInvestorIdentity, pas avec profiles[].status.

Contrat : investorIdentityStatus.ts.

const updateStatus = union('requested', 'underAnalysis', 'declined')
const update = object({ status: updateStatus, dueDate: isoDate })

verified: { status: 'verified', update?: update }
outdated: { status: 'outdated', update: update }
  • verified reste le gate transactionnel (assertTransactionRights). Avant dueDate, l'investisseur peut encore investir.
  • outdated bloque — restriction KYC_OUTDATED ou requirement OVERDUE. HTTP investisseur et création admin de withdraw : Customer.assertTransactionRights (['all']). Play Lemonway : le worker P2P et getWaitingWithdrawToPlay excluent outdated (débit et crédit). Les WT waiting déjà créées restent en file, Lemonway n'est pas appelé. verified + update n'est pas exclu.
  • dueDate = ISO datetime du GET /accounts (isoDate), pas le DueDate YYYY-MM-DD du webhook 53.
  • update.status est recertif-only. On ne réutilise pas waitingForVerifier / incorrect (funnel initial, interdit de transacter).

Correspondances Lemonway → Bricks

Funnel initial — profiles[].status LENDER

Lemonway Bricks investorIdentityStatus
UNDER_ANALYSIS { status: 'waitingForVerifier' }
REJECTED / DENIED { status: 'incorrect', documents }
ACCEPTED (1re fois) { status: 'verified' }

Recertif — requirements[] (KYC_UPDATE) + restrictions (KYC_OUTDATED)

Lemonway Bricks
Pas de KYC_UPDATE ouvert (vide / undefined / ACCEPTED) { status: 'verified' }
REQUESTED / UNDER_ANALYSIS / REJECTED, sans KYC_OUTDATED { status: 'verified', update } — peut encore transacter
Restriction KYC_OUTDATED ou requirement OVERDUE { status: 'outdated', update } — bloqué

update.status : REQUESTED / OVERDUE → requested · UNDER_ANALYSIS → underAnalysis · REJECTED → declined.

Deux ACCEPTED distincts : profile LENDER (funnel, ne bouge pas pendant la recertif) vs requirement KYC_UPDATE (recertif validée → plus d'update ouvert).

stateDiagram-v2
  verifiedIdle: verified
  verifiedRefresh: verified + update
  outdated: outdated bloqué
  verifiedIdle --> verifiedRefresh: notif 53 REQUESTED
  verifiedRefresh --> verifiedRefresh: UNDER_ANALYSIS ou REJECTED avant dueDate
  verifiedRefresh --> verifiedIdle: requirement ACCEPTED
  verifiedRefresh --> outdated: notif 54 KYC_OUTDATED ou OVERDUE
  outdated --> outdated: resubmit UNDER_ANALYSIS ou REJECTED
  outdated --> verifiedIdle: ACCEPTED, restriction levée

Le submit du widget ne clôture pas l'update. C'est ACCEPTED (ou la disparition de la requirement) qui clôture.

Sync

  • Webhooks 53/54 → /lemonway/account/requirement-change et restriction-change. Chaque webhook enqueue le job migrate-lemonway-onboarding-v1-to-v2 (idempotent si déjà v2, rattrape un v1 restant). Compte déjà v2 (lemonway.account.id) : aussi syncInvestorKycStatus (tous les customers matchés). Compte encore v1 : le job écrit le JSONB customers.lemonway (dual-write tables lemonway SPV seulement) puis syncInvestorKycStatus pour chaque customer migré. Plusieurs customers / wallets LENDER distincts sur le même account : chacun passe v2. customerType vs account.type : mismatch → pas de migration investor (Slack, sans retry). L'URL hosted recertif (POST /onboarding/v2/start-onboarding) reste bloquée par investor-already-has-bricks-account — traitement OPS (HelpCenter). Recertif SPV : alerte Slack OPS (pas d'UI), y compris après une migration v1→v2. Les hooks onboarding v2 (account-status-changed / profile-status-changed) n'enqueuent pas ce job.
  • POST /administration/customers/:customerId/sync-kyc-status resync le même chemin (compte, profil, wallet, requirements/restrictions).
  • GET /onboarding/v2/profile-status resync si le profile ou le JSONB requirements/restrictions a bougé. Le poll ne renvoie que le kind (investorIdentityStatusKind, y compris outdated) — update / dueDate restent sur GET /customers/me.
  • GET /customers/me ne sync pas.

onboardingId recertif = root GET /accounts/{id}, distinct du funnel. Persisté tant que KYC_UPDATE est ouvert, vidé à la clôture — sinon le cycle suivant reprendrait l'ancienne session hosted.

Relances Customer.io

Cron kyc-update-reminder-notifications (07:15 UTC). Un run fetch les investisseurs avec un investorIdentityStatus.update (deletedAt null) dont la dueDate (jour calendaire Europe/Paris de l'isoDate persisté) tombe aujourd'hui à J-60, J-30, J-14, J-7, J-3, J0, J+7 ou J+14. Recertif ouverte ≠ à relancer : on notifie requested et declined (verified+update ou outdated), pas underAnalysis (dossier déjà soumis, Lemonway en cours). Chaque offset déclenche son broadcast API-triggered ; la seule variable template est dueDate au format DD/MM/YYYY.

Widget

Le front rejoue POST /onboarding/v2/start-onboarding. L'API ne rappelle pas startOnboarding : elle resume le nouvel onboardingId recertif. verified sans update → onboarding.investor-onboarding-already-completed.

Pas d'UI recertif dédiée : funnel initial et recertif partagent le même écran widget.

App investisseur (mobile + web)

Élément Emplacement
Composant LemonwayKYCScreen — projects/front-mobile-app/src/modules/accountStatus/screens/LemonwayKYCScreen/ (hors legacy/OnboardingScreen)
Route Expo /(authenticated)/onboarding/lemonway
Hook API useStartLemonwayOnboarding → POST /onboarding/v2/start-onboarding
Fin de parcours postMessage Lemonway → ReactNativeWebView (natif) ou parent (iframe web) ; poll GET /onboarding/v2/profile-status en parallèle
Entrées recertif Modales account status (RecertNeededModal, LockedModal, ErrorModal) → router.navigate('/(authenticated)/onboarding/lemonway')
Compte doublon (investor-already-has-bricks-account) openModal(HelpCenterModal) — pas Zendesk

Le formulaire profil investisseur (étape avant Lemonway) reste sur /onboarding ; seul le widget hébergé Lemonway vit sur /onboarding/lemonway.

Liens