Skip to content

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’edge sponsorship_user insé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.

À 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.

  • Longueur actuelle : 6 caractères.
  • Alphabet : Crockford Base32 — 0-9 + A-Z sans les lettres ambiguës I, L, O, U. Cela rend le code facile à dicter oralement et illisible-de-travers (pas de confusion 0/O ou 1/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_int caractè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.

Lorsque sponsorshipCode est fourni dans POST /api/auth/register :

  • Si le format ne correspond pas à l’alphabet/longueur attendus → erreur 422 avec le code sponsorship.codeInvalid (pointeur /data/attributes/sponsorshipCode).
  • Si le format est valide mais qu’aucun utilisateur ne porte ce code → erreur 422 avec le code sponsorship.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 :

  1. La ligne user.
  2. La ligne sponsorship (code unique du nouvel inscrit).
  3. La ligne sponsorship_stats initialisée à zéro.
  4. La ligne sponsorship_user reliant le filleul au parrain.

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.


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 ligne sponsorship (cas standard).
    • 404 avec le code interne Sponsorship 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 (un GET ne doit pas écrire en base).
{
"data": {
"type": "sponsorships",
"id": "5f6e2d4a-1b8c-4d2e-9a3b-7c1d8e9f0a2b",
"attributes": {
"code": "A7K3M9",
"numUserTotal": 7,
"numUserValid": 4,
"createdAt": "2026-06-10T14:23:11+00:00"
}
}
}

GetMySponsorshipAction.