Parrainage
Chaque utilisateur reçoit, au moment de son inscription, un code de parrainage unique qu’il pourra communiquer à de futurs inscrits. Lorsqu’un nouvel inscrit fournit ce code dans son POST /api/auth/register (champ optionnel sponsorshipCode), une relation parrain → filleul est enregistrée. Deux compteurs sont maintenus côté parrain :
numUserTotal— nombre total de filleuls associés. Incrémenté à l’inscription du filleul (au moment où le code est validé et l’edgesponsorship_userinsérée).numUserValid— nombre de filleuls ayant confirmé leur adresse e-mail. Incrémenté au moment du clic sur le lien de confirmation.
L’invariant côté base est numUserValid <= numUserTotal. L’écart entre les deux représente les filleuls inscrits mais qui n’ont pas (encore) activé leur compte.
Notification au parrain
Section titled “Notification au parrain”À chaque association d’un filleul, un e-mail est envoyé au parrain pour l’informer de l’inscription (template templates/mails/sponsorship_referred_signup.twig, traductions dans resources/lang/<locale>/mails.php sous la clé sponsorshipReferredSignup). Le mail est rendu dans la locale du parrain (et non celle de la requête HTTP du filleul). L’échec d’envoi est silencieusement absorbé côté RegistrationService : la notif est informationnelle, elle ne doit jamais faire échouer l’inscription du filleul.
Format du code
Section titled “Format du code”- Longueur actuelle : 6 caractères.
- Alphabet : Crockford Base32 —
0-9+A-Zsans les lettres ambiguësI,L,O,U. Cela rend le code facile à dicter oralement et illisible-de-travers (pas de confusion0/Oou1/I/L). - Casse : le serveur normalise en majuscules ; côté client la saisie est case-insensitive.
- Codespace : 32⁶ ≈ 1,07 × 10⁹ combinaisons — largement suffisant pour la base actuelle, avec possibilité d’allonger la longueur plus tard sans casser les codes existants (la longueur n’est imposée qu’à la génération, la validation accepte 6 caractères pour le moment et sera assouplie si besoin).
- Génération : tirages CSPRNG via
random_intcaractère par caractère ; en cas de collision sur l’index unique (très improbable mais possible), la transaction d’inscription retente jusqu’à 5 fois avant d’échouer.
Voir SponsorshipCode pour les détails d’implémentation.
Validation à l’inscription
Section titled “Validation à l’inscription”Lorsque sponsorshipCode est fourni dans POST /api/auth/register :
- Si le format ne correspond pas à l’alphabet/longueur attendus → erreur
422avec le codesponsorship.codeInvalid(pointeur/data/attributes/sponsorshipCode). - Si le format est valide mais qu’aucun utilisateur ne porte ce code → erreur
422avec le codesponsorship.codeNotFound. - Dans les deux cas, aucun utilisateur n’est créé : la résolution du code se fait avant l’insertion pour éviter les comptes fantômes.
- Si le code résout vers l’utilisateur en train de s’inscrire (cas théorique impossible puisque le compte n’existe pas encore, mais protégé en interne), l’association est silencieusement ignorée — on ne s’auto-parraine pas.
Quand la résolution réussit, l’inscription provisionne dans une seule transaction logique :
- La ligne
user. - La ligne
sponsorship(code unique du nouvel inscrit). - La ligne
sponsorship_statsinitialisée à zéro. - La ligne
sponsorship_userreliant le filleul au parrain.
Incrémentation du compteur numUserValid
Section titled “Incrémentation du compteur numUserValid”Le compteur est incrémenté atomiquement au moment de la confirmation d’e-mail du filleul (EmailConfirmationService). L’idempotence repose sur le fait que les tokens de confirmation sont à usage unique : un même filleul ne peut donc déclencher l’incrément qu’une fois. Si le filleul n’a pas de parrain associé, l’opération est un no-op.
GET /api/users/me/sponsorship
Section titled “GET /api/users/me/sponsorship”Retourne le code de parrainage du viewer authentifié et ses statistiques courantes.
- Auth : requise (Bearer token).
- Réponse :
200 OK— l’utilisateur dispose bien d’une lignesponsorship(cas standard).404avec le code interneSponsorship not provisioned— l’utilisateur a été créé avant l’introduction du parrainage et n’a jamais été provisionné. Le client doit afficher un message “fonctionnalité indisponible pour ce compte” plutôt que tenter de générer un code à la volée (unGETne doit pas écrire en base).
Exemple de réponse
Section titled “Exemple de réponse”{ "data": { "type": "sponsorships", "id": "5f6e2d4a-1b8c-4d2e-9a3b-7c1d8e9f0a2b", "attributes": { "code": "A7K3M9", "numUserTotal": 7, "numUserValid": 4, "createdAt": "2026-06-10T14:23:11+00:00" } }}