Remboursement (refund)¶
Le remboursement d'un achat primaire est porté par refundPrimaryPurchase dans primary-purchase.service.ts, avec une finalisation asynchrone côté Lemonway via le worker PlayedLemonwayP2P et PrimaryPurchaseRefundService.
Statuts remboursables¶
| Remboursable | Bloqué (WALLET_TRANSACTION_STATE_NOT_CONFIRMED_OR_WAITING) |
|---|---|
waiting_for_assignation |
declined |
waiting_for_auto_invest_investor_confirmation |
refunded |
waiting_for_p2p_creation |
waiting_for_reservation_confirmation |
waiting_for_contract_creation |
|
confirmed |
Un achat est remboursable dès qu'une WT d'achat existe — c'est elle qui matérialise l'argent à rendre. waiting_for_reservation_confirmation n'en a pas encore (le paiement carte n'a pas abouti), declined et refunded sont terminaux.
Éligibilité¶
| Initiateur | Conditions |
|---|---|
Investor (performedByCustomer) |
Funding non clôturé et now < refundableUntil |
Admin (performedByAdmin) |
Funding non clôturé |
Script funding-failure (propertyNotFinanced) |
Toujours |
refundableUntil est stocké dans le contexte de la WT d'achat à sa création : date d'achat + businessRules.primaryMarket.retractDelayDays (4 jours), arrondie à la fin de journée Europe/Paris. La confirmation d'un achat auto-invest le recalcule à partir de la date de confirmation.
Sinon : Err('primary-purchase.refund.not-allowed').
Déclencheurs (5)¶
| Déclencheur | Reason | Qui |
|---|---|---|
POST /wallet-transactions/:walletTransactionId/refund |
performedByCustomer |
Investor (rétractation ≤ J+4) |
POST /investor/investment-plan/purchase/cancel |
performedByCustomer |
Investor (annulation d'un achat auto-invest non confirmé) |
POST /administration/transactions/:transactionId/refund |
performedByAdmin |
Admin (uniquement kind PRIMARY_PURCHASE) |
Worker PlayedLemonwayP2P |
p2pFailed |
Automatique, quand le P2P d'achat échoue chez Lemonway |
refund-investors.script.ts |
propertyNotFinanced |
Remboursement de masse quand le funding échoue |
Mécanique¶
Tout s'exécute dans un safePgTransaction (verrou sur le purchase et sur la WT d'achat) :
- WT d'achat →
CANCELED, aveccontext.refund = { walletTransactionId, reason, at }. - WT de refund créée — kind
PRIMARY_PURCHASE_REFUND(ouPRIMARY_REFUND_PROPERTY_NOT_FINANCED), qui re-crédite la balance classique et la part de gift balance utilisée. Son statut initial dépend du P2P d'achat :- P2P d'achat exécuté avec succès chez Lemonway → l'argent est sur le wallet de la SPV : la WT de refund naît
WAITINGavec un P2P inverse (SPV → investor). Il est joué parPendingLemonwayP2P, puisPlayedLemonwayP2Pfinalise viahandleSucceededLemonwayP2PRefund(WT de refund →CONFIRMED,refund.atstampé). - Sinon (P2P jamais joué ou échoué) → rien à rapatrier : la WT de refund naît directement
CONFIRMED(lemonwayTransactionId: -1).
- P2P d'achat exécuté avec succès chez Lemonway → l'argent est sur le wallet de la SPV : la WT de refund naît
- Bricks libérées — sauf en
waiting_for_assignation(pas encore assignées). EnpropertyNotFinanced, variantecloseFundingAndReleaseBricksForBricksIdsqui clôture aussi le funding. - Si l'achat était
confirmed: le contrat d'obligation est marquérefundedet les referral rewards encore pending sont annulés. - Compteurs Redis décrémentés (bricks vendues de la propriété, + compteur auto-invest le cas échéant), avec compensation
setOnRollback. - Purchase →
refundedavecrefund: { wtId, reason, at }.
flowchart TD
Trigger["refundPrimaryPurchase"]:::action
CheckStatus{Statut remboursable<br/>et éligible ?}
Cancel["WT d'achat → CANCELED"]:::action
P2PCheck{P2P d'achat réussi<br/>chez Lemonway ?}
RefundWaiting["WT refund WAITING<br/>+ P2P inverse SPV → investor"]:::status
RefundConfirmed["WT refund CONFIRMED<br/>immédiatement"]:::success
Played["PendingLemonwayP2P joue le P2P<br/>PlayedLemonwayP2P → WT CONFIRMED"]:::action
Refunded([purchase refunded]):::success
Rejected["Err — not-allowed /<br/>NOT_CONFIRMED_OR_WAITING"]:::failure
Trigger --> CheckStatus
CheckStatus -- "Non" --> Rejected
CheckStatus -- "Oui" --> Cancel
Cancel --> P2PCheck
P2PCheck -- "Oui" --> RefundWaiting
P2PCheck -- "Non" --> RefundConfirmed
RefundWaiting --> Played
Played --> Refunded
RefundConfirmed --> Refunded
classDef action fill:#0ea5e9,color:#fff,stroke:#0369a1
classDef status fill:#fef3c7,color:#78350f,stroke:#d97706
classDef success fill:#10b981,color:#fff,stroke:#047857
classDef failure fill:#ef4444,color:#fff,stroke:#b91c1c
declined vs refunded¶
declined |
refunded |
|
|---|---|---|
| Quand | L'achat n'a jamais abouti (expiration, sur-vente, échec WT) | L'achat avait une WT d'achat débitée |
| Argent | La WT d'achat, si elle existe, passe DECLINED — aucune WT supplémentaire |
WT de refund dédiée + WT d'achat CANCELED |
| Lemonway | Jamais sollicité | P2P inverse si l'argent avait atteint la SPV |