Skip to main content

EA FC, Stripe et premium

Cette page documente les parties implémentées dans le code vérifiées pour EA FC et Stripe. Sources principales :
  • convex/matches/ea_stats_sync.ts
  • convex/matches/ea_score_sync.ts
  • convex/admin/cron_runners.ts
  • convex/awards/recipients.ts
  • convex/stripe/customerIndex.ts
  • convex/schema.ts

EA FC

Pipeline score sync (matchs)

La synchronisation de score EA est pilotée par le cron runMatchEaScoreSync (convex/admin/cron_runners.ts) :
  • il lit les matchs éligibles via getEligibleMatchesForEaSync,
  • traite les matchIds par lots avec scoreConcurrency,
  • appelle resolveEaScoreAction pour chaque match,
  • en parallèle, il prépare statsSyncCandidates (matchs confirmés sans match_player_stats et sans lignes unmatched), puis planifie internal.matches.ea_stats_sync.fetchAndPersistEaPlayerStats si nécessaire.
Dans resolveEaScoreAction (convex/matches/ea_score_sync.ts) :
  • récupère les matchs EA des deux clubs,
  • construit une fenêtre de correspondance autour du kickoff (DEFAULT_MATCH_TOLERANCE_MINUTES),
  • si rien : retry avec fenêtre large MATCH_HISTORY_DAYS,
  • sans candidat -> persistUnavailable (ea.score.unavailable),
  • plusieurs candidats -> persistAmbiguous (ea.score.ambiguous),
  • divergence entre score EA et score soumis / score joueur -> ea.potential_draw_review_required,
  • candidate unique valide -> persistConfirmedScore (finalizeEaConfirmedScore).
notifyAdmins émet les événements en sortie de sync score :
  • ea.score.confirmed
  • ea.score.ambiguous
  • ea.score.unavailable
  • ea.score.error

Pipeline stats sync + matching

fetchAndPersistEaPlayerStats (ea_stats_sync.ts) traite les lignes EA d’un match :
  • résolution des profils via :
    • compo soumise,
    • actif de l’effectif,
    • eaPlayerId,
    • eaGamertagNormalized.
  • ligne matchée : insertion dans match_player_stats,
  • ligne non résolue : insertion/mise à jour dans match_ea_unmatched_player_stats avec reviewStatus + reasonCode,
  • recalc MOTM possible via replaceMotmAwardRecipientForMatch quand des stats sont matchées (auditReason: 'EA_STATS_PERSISTED').
reviewComparisons (stocké avec la ligne unmatched) regroupe les comparaisons de résolution lineup / active_roster pour le debug admin.

Unmatched review (vérifié)

Types vérifiés :
  • ACTION_REQUIRED
  • IGNORED
  • FRAUD
Codes de raison vérifiés :
  • AMBIGUOUS_LINEUP_MATCH
  • AMBIGUOUS_ACTIVE_ROSTER_MATCH
  • PLAYER_NOT_IN_SUBMITTED_LINEUP
  • PLAYER_NOT_IN_ACTIVE_ROSTER
  • LEGACY_UNCLASSIFIED
Règles de classification :
  • plusieurs candidats dans la compo => ACTION_REQUIRED + AMBIGUOUS_LINEUP_MATCH
  • plusieurs candidats actifs résolus => ACTION_REQUIRED + AMBIGUOUS_ACTIVE_ROSTER_MATCH
  • un seul actif + absent de compo => IGNORED + PLAYER_NOT_IN_SUBMITTED_LINEUP
  • aucun actif => FRAUD + PLAYER_NOT_IN_ACTIVE_ROSTER
autoMapUnmatchedPlayerStats :
  • relit uniquement les ACTION_REQUIRED,
  • peut basculer en match_player_stats si résolution univoque,
  • supprime la ligne unmatched après mapping,
  • met à jour les compteurs autoMappedCount, unresolvedCount, ambiguousCount,
  • relance le recalcul MOTM (auditReason: 'EA_STATS_AUTO_MAPPED') quand un mapping auto réussit.
backfillMappedEaStats existe en backfill technique (batch de 50 avec pagination auto).

Point à vérifier

  • La portée des règles métier autour du statut du match (par ex. validation finale selon l’état compétition) n’est pas détaillée dans ces modules.

Stripe premium (vérifié dans convex/stripe/customerIndex.ts)

Statuts et éligibilité

Tableau des statuts connus :
  • trialing
  • active
  • past_due
  • canceled
  • unpaid
  • incomplete
  • incomplete_expired
  • paused
Éligibilité premium effective :
  • statut membre de l’ensemble {trialing, active},
  • livemode cohérent entre subscription et customer résolu,
  • filtrage optionnel par STRIPE_PREMIUM_PRICE_IDS si défini.
Règles STRIPE_PREMIUM_PRICE_IDS :
  • variable optionnelle ; parsing en set de chaînes commençant par price_,
  • si variable absente/vides : pas de filtrage par priceId.
livemode :
  • pour un owner, resolveEffectiveLivemode vaut true si au moins un customer Stripe lié est en livemode: true,
  • la souscription doit matcher ce mode.

Jobs / synchronisation

  • reconcilePremiumEntitlements :
    • batch par défaut 25,
    • max 50,
    • pagination + replanification automatique jusqu’à fin.
  • listForAdmin (admin/modérateurs) :
    • filtre options searchEmail, hasSubscription, livemode,
    • scan candidats limité à MAX_ADMIN_SCAN = 500.
  • événements gérés :
    • customer.created
    • customer.updated
    • customer.deleted
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • checkout.session.completed
Last modified on June 24, 2026