Skip to main content

Opérations de paris

Cette page documente le cycle de vie des paris et la formule de points confirmée côté Convex. Sources principales :
  • convex/competition/betting.ts
  • convex/competition/betting_rules.ts
  • convex/competition/bet_settlement.ts
  • convex/competition/betting_reasons.ts
  • convex/competition/betting_waves.ts
  • convex/admin/bets.ts
  • convex/admin/cron_runners.ts

États de pari

bets.status observé :
  • PENDING
  • WON
  • LOST
  • CANCELLED

Cycle de vie du pari

Création / update

createOrUpdateBet positionne/actualise en PENDING.

Settlement d’un match

settleBetsForMatch traite les statuts PENDING/WON/LOST, écrit WON/LOST + settledAt, et nettoie cancelReason. Le traitement parcourt toutes les pages de paris du match par lots bornés. Il ne s’arrête pas à 500 lignes : seuls les paris de la vague courante et du match concerné sont modifiés, les autres matchs, autres vagues et statuts non éligibles restent inchangés.

Réouverture

reopenBetsForMatch repasse PENDING/WON/LOST vers PENDING et remet pointsWon = 0. La réouverture utilise le même parcours paginé complet que le settlement.

Annulation

  • cancelBetsForMatch : annule PENDING/WON/LOST, écrit CANCELLED, remet pointsWon = 0, avec option advanceWave.
  • cancelPendingBetsForMatch : annule PENDING seulement.
Les deux chemins annulent toutes les pages de paris éligibles de la vague courante. advanceWave n’avance pas une deuxième fois quand un retry rejoue la même annulation horodatée. La valeur de reason utilisée par les flux de match dépend de la cause (ex. MATCH_FORFEITED, MATCH_CANCELLED, MATCH_RESCHEDULED, BETTING_DISABLED, ADMIN_INVALIDATION, ADMIN_ERROR).

Éligibilité paris / listing

Un match listable doit être en statut paris :
  • scheduled (minuscule) ou SCHEDULED (legacy),
  • bettable === true,
  • fenêtre de coupe à BETTING_CUTOFF_MINUTES = 60,
  • pas de signal de progression de résultat (hasMatchProgressSignal) déjà posé.
hasMatchProgressSignal couvre notamment scores finalisés partiels/finaux et timestamps associés.

Formule de scoring

settleSingleBet applique :
  • EXACT_SCORE : 3 points en cas de score identique exact.
  • autres types (HOME/DRAW/AWAY) : 1 point si l’issue est correcte.

Résultat match → paris (points de couplage)

Depuis le flux Convex, les événements impactant les paris sont :
  • résultat validé : settlement de la vague courante (settleBetsForMatch),
  • contestation / réouverture (contestResult, adminResetMatchResult, adminSetMatchResult en retour pending_validation) : reopenBetsForMatch,
  • annulations/indisponibilités match (ex. cancellation, reschedule, forfeit) : cancel... selon contexte.

Forfaits et paris

applyClubForfeit (forfait club-compétition) et declareMatchForfeit (forfait ponctuel de match) annulent les paris via cancelPendingBetsForMatch avec raison MATCH_FORFEITED :
  • seuls les paris PENDING de la vague courante sont affectés,
  • les paris déjà gagnants/perdants ne sont pas modifiés,
  • si l’annulation est paginée, le forfait ponctuel garde un état betCancellationInProgress puis synchronise pendingBetsCancelled avec le total final du job,
  • les paris annulés par forfait ne sont pas réouverts par une réversion ou une annulation de forfait.

Admin bet bulk settlement

requestBulkSettleBets (admin / modérateur) crée une exécution Cron admin-bets-bulk-settlement. La demande refuse les listes vides, les IDs introuvables et les conflits de job en cours avec des erreurs typées côté client. runBetBulkSettlement :
  • filtre et dédoublonne les IDs,
  • applique patch par patch,
  • écrit un audit par pari,
  • envoie notifyBetStatusChange,
  • maintient requested/updated/skipped/failed, requestedStatus et actorUserId dans cron_executions.details,
  • notifie en in-app l’admin/modérateur demandeur à la fin ou en cas d’échec.
En cas d’échec partiel, admin.bet_bulk_job.partial_failure_alerted envoie un email au groupe ADMINS, dédupliqué par exécution.

Export et notifications

  • exportBets n’est pas paginé : il lit au maximum 5 000 paris, applique les filtres, puis retourne { exportedAt, total, filters, columns, rows }.
  • bet.status_changed crée toujours une notification in-app ciblée vers /betting, dédupliquée par eventKey.
  • Les statuts exceptionnels CANCELLED et PENDING déclenchent aussi un email au parieur ; WON et LOST restent in-app uniquement.
  • Le suivi opérationnel du bulk repose sur cron_executions, les notifications ADMIN_BET_BULK_JOB et le reporting d’erreurs PostHog.
Last modified on July 18, 2026