CDL Copilot · en production

Annuler un paiement saisi à tort

La sortie de secours qui manquait. Elle ne supprime rien : elle défait le paiement, après quoi la suppression redevient possible — et si c'est justifié, vous la faites.

migration 020 en base déployé en prod 14/14 contrôles prod 29/29 Postgres local 344 tests unitaires

C'est fait, et vérifié en production. Les deux résidus de mes propres tests (marqués payés) ont été nettoyés par l'enchaînement réel : annulation du paiement, puis suppression. Votre base est propre : Pionix, polydruck, HEPPNER, Michael BOUTON EURL — vos quatre vraies inscriptions, intactes.

Ouvrir CDL Copilot ↗

Pourquoi une porte de sortie était nécessaire

La migration 019 refuse de supprimer une inscription payée — et elle a raison : derrière, il y a une vente, une facture, un client. Mais un paiement peut être faux : un événement Stripe de test, une saisie manuelle, un encaissement annulé par le client.

Sans porte de sortie, le seul recours serait d'aller modifier la base à la main — exactement ce qu'on veut éviter. D'où la migration 020.

Ce qu'elle fait — et ce qu'elle refuse de faire

public.annuler_paiement(uuid, motif) → 'ok' | 'refuse' | 'introuvable'
                                        | 'non_payee' | 'motif_manquant'
                                        | 'motif_trop_long'

Elle repasse l'inscription à prospect, efface la date de paiement, et journalise le motif obligatoire. Une fois revenue prospect, la suppression de 019 redevient utilisable. Les deux gestes restent séparés, et chacun est tracé.

Elle ne touche pas :

Elle n'annule PAS le paiement chez Stripe. Si l'argent a réellement été encaissé, un remboursement est nécessaire — et il se fait dans Stripe, pas ici. L'écran le dit en clair, en orange, avant de valider.

À l'écran

Le bloc sur la fiche
Le bloc n'apparaît que sur une inscription payée.
La modale d'annulation
Motif obligatoire, et l'avertissement Stripe écrit noir sur blanc.

Vérifications

Sur un Postgres reconstruit à l'identique (toutes les migrations rejouées, rôles réels, auth.uid() jouable) : 29 contrôles.

ContrôleAttenduRésultat
Formateur, compte sans profil, anonymerefusésrefusés
Motif vide, trop court, trop longrefusésrefusés
Inscription non payéenon_payeenon_payee
Aucun refus n'a rien modifiéétat intactintact
Payée par etat seulokok
Payée par date_paiement seuleokok
Lien Stripe, dernier_appel_le, relances, stagiairesintactsintacts
Trace : motif + auteur + état d'avant, sans doublon1 ligne1
Annuler, puis supprimerokok
Le verrou : authenticated n'écrit toujours que opcoopco seulopco

Et sur la production, avec une session membre réelle : 14 contrôles, dont l'enchaînement complet annulation → suppression, vérifié en base.

Comment vous vous en servez