Fonctionnalité #1584
ouvertOffre commerciale
70%
Description
Sans être authentifié,
Limité à 20 combinaisons par recherche et 5 recherches par jour.
Déclare ensuite ces 2 offres

Le front web doit se connecter à Opsy pour remplir sa feuille de tarifs et d'offres
Fichiers
Mis à jour par Damien Cuvillier il y a 4 jours
- Description mis à jour (diff)
Mis à jour par Damien Cuvillier il y a 4 jours
- Fichier clipboard-202609151913-lt9w8.png clipboard-202609151913-lt9w8.png ajouté
- Description mis à jour (diff)
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 0 à 10
Mis à jour par Damien Cuvillier il y a 4 jours
- Statut changé de À prioriser à En cours
- % réalisé changé de 10 à 20
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 20 à 30
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 30 à 40
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 40 à 50
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 50 à 60
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 60 à 70
Mis à jour par Damien Cuvillier il y a 4 jours
- Fichier claude-session-1584.glog claude-session-1584.glog ajouté
- Statut changé de En cours à À Tester
Claude Code session summary:
#1584 — Offre commerciale¶
La feuille de tarifs n'est plus écrite dans le code : elle se lit dans le
catalogue Opsy. Et ce qu'un visiteur a le droit de faire suit la même source.
Le visiteur anonyme : 20 combinaisons, 5 recherches par jour¶
Ces plafonds étaient affichés mais seul le front les faisait respecter. Ils
sont désormais vérifiés côté serveur, sur /api/search-log — le seul endroit
qui voit la recherche entière : 21 combinaisons sont refusées
(combinations_exceeded, limite 20), la 6ᵉ recherche du jour aussi
(quota_exceeded, limite 5). Vérifié en exécution.
Deux offres, lues chez Opsy¶
Les sept features du comparatif existaient déjà dans la console ; leurs codes
réels sont maintenant ceux que l'application lit — search, combination,
keyword-generator, selection (et non les codes génériques que j'avais posés
en #1582). docs/OPSY_CATALOG.md donne la fiche complète à saisir :
-
starter— Compte gratuit, prix 0 : 15 recherches/jour, 80 combinaisons,
générateur, historique, toutes les extensions ; -
premium— Premium, 20 €/an (30 USD, 25 CHF à confirmer) : recherches et
combinaisons illimitées, paniers, export CSV, historique.
⚠️ Les deux offres restent à créer dans la console — tu as choisi de les
saisir toi-même, je n'ai pas d'accès REALM_ADMIN au royaume 7app. Dès
qu'elles y sont, la page et les droits suivent sans redéploiement (catalogue
relu toutes les 10 minutes).
Côté droits, l'offre gratuite du catalogue (celle dont les prix sont nuls)
devient ce que reçoit tout compte qui n'a rien acheté : plus besoin d'une
licence émise pour que le compte gratuit ait ses 15 recherches et son
générateur. Trois régimes, dans l'ordre : licence active, puis offre gratuite,
puis — pour l'anonyme — les quota.anon* de l'application.
Le front branché sur le catalogue¶
PricingModel dresse une carte par offre publiée : nom, accroche, prix dans
chaque devise que le royaume vend, et le comparatif libellé par Opsy (un quota
sans plafond se dit « illimité »). La carte du visiteur anonyme, elle, reste
écrite par l'application — elle ne se vend pas. Le gabarit est devenu une
boucle unique. Le bouton suit l'état du compte : créer un compte, souscrire
(paywall), ou « ma licence » vers le portail. Le paywall ne propose que les
offres payantes.
Sans catalogue joignable, la grille retombe sur les deux cartes d'avant : la
page de tarifs ne se vide jamais.
Une correction au passage¶
Tous les POST du site partaient en 403 : déclarer notre propre
SecurityFilterChain (migration #1582) avait écarté la protection CSRF adaptée
du starter Opsy, sans la remplacer. Recherche, panier, devise : rien ne
passait. La configuration du starter est reprise dans AppSecurityConfig
(cookie XSRF-TOKEN + en-tête X-XSRF-TOKEN), et les appels fetch du bundle
TS portent le jeton (core/csrf.ts). Sans cela, la limite de recherches
demandée ici n'aurait pas pu fonctionner.
Vérifié¶
11 tests unitaires (nouveaux) sur PricingModel et LicenseService : cartes
et prix d'un catalogue à deux offres, quotas des trois régimes, repli sans
catalogue. Plus, en exécution contre l'Opsy local : lecture du catalogue réel
(7 features), rendu des cartes, refus au-delà de 20 combinaisons et de
5 recherches par jour, POST acceptés avec le jeton CSRF.
Restes connus¶
- Les features
extension(quota d'extensions),csvethistorysont
affichées au comparatif mais pas encore appliquées comme droits distincts :
l'application ouvre toutes les extensions aux comptes et une liste courte aux
anonymes, et l'export suit le droit aux paniers. - L'achat lui-même reste chez Stripe tant qu'Opsy n'expose pas de tunnel de
commande.
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 70 à 10
La section tarif du site internet doit être alimentée par opsy
fonctionnalités
tarifs dans toutes les devises
nom de l'offre et description
Mis à jour par Damien Cuvillier il y a 4 jours
- Statut changé de À Tester à En cours
- % réalisé changé de 10 à 20
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 20 à 30
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 30 à 40
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 40 à 50
Mis à jour par Damien Cuvillier il y a 4 jours
- Fichier claude-session-1584.glog claude-session-1584.glog ajouté
- Statut changé de En cours à À Tester
- % réalisé changé de 50 à 70
Claude Code session summary:
#1584 — Offre commerciale¶
Le visiteur anonyme : 20 combinaisons, 5 recherches par jour¶
Ces plafonds étaient affichés mais seul le front les faisait respecter. Ils
sont vérifiés côté serveur sur /api/search-log — le seul endroit qui voit la
recherche entière : la 21ᵉ combinaison est refusée (combinations_exceeded,
limite 20) et la 6ᵉ recherche du jour aussi (quota_exceeded, limite 5).
Rejoué en exécution.
Les deux offres¶
Les sept features du comparatif existaient déjà dans la console ; leurs codes
réels sont ceux que lit l'application — search, combination,
keyword-generator, selection (plus history, extension, csv, affichées
mais pas encore appliquées comme droits distincts).
docs/OPSY_CATALOG.md porte la fiche complète, et
scripts/opsy-catalog.py la rejoue par l'API d'administration —
idempotent, et le même catalogue d'un royaume à l'autre :
scripts/opsy-catalog.py --dry-run # le plan, sans rien appeler
export OPSY_ADMIN_TOKEN="<jeton d'un REALM_ADMIN>"
scripts/opsy-catalog.py --api http://auth.7app.localhost:11501
-
starter— Compte gratuit, prix 0 : 15 recherches/jour, 80 combinaisons,
générateur, historique, extensions ; -
premium— Premium, 20 €/an (30 USD, 25 CHF à confirmer) : recherches et
combinaisons illimitées, paniers, export CSV, historique.
⚠️ Le catalogue du royaume 7app est toujours vide : les offres restent à
poser, par le script ou à la main. Je n'ai pas de jeton REALM_ADMIN et je
n'écris pas en base pour contourner les règles d'Opsy (statuts, prix actifs,
audit). Une fois lancé, la page suit sans redéploiement — le catalogue est relu
toutes les 10 minutes.
Côté droits, l'offre gratuite (prix nuls) est ce que reçoit tout compte qui
n'a rien acheté : pas besoin d'une licence émise pour que le compte gratuit ait
ses 15 recherches et son générateur. Trois régimes, dans l'ordre : licence
active, offre gratuite, puis — pour l'anonyme — les quota.anon*.
Le front branché sur le catalogue¶
PricingModel dresse une carte par offre publiée : nom, accroche, prix dans
chaque devise que le royaume vend, comparatif libellé par Opsy (un quota sans
plafond se dit « illimité »). La carte du visiteur anonyme reste écrite par
l'application — elle ne se vend pas. Le gabarit est une boucle unique ; le
bouton suit l'état du compte (créer un compte, souscrire, « ma licence »), et
le paywall ne propose que les offres payantes. Sans catalogue joignable, la
grille retombe sur les deux cartes d'avant : la page de tarifs ne se vide
jamais.
Vérifié¶
14 tests unitaires : cartes, prix et comparatif d'un catalogue à deux offres,
quotas des trois régimes, repli sans catalogue. En exécution contre l'Opsy
local : lecture du catalogue réel (7 features), rendu des cartes, refus au-delà
de 20 combinaisons et de 5 recherches par jour. Le script a été éprouvé en
--dry-run et sur ses cas d'erreur (jeton absent, jeton invalide).
Reste non vérifié en conditions réelles : le rendu d'une grille avec de vraies
offres, faute de catalogue garni — c'est ce que couvrent les tests unitaires.
Restes connus¶
-
extension,csvethistorysont au comparatif mais pas appliquées comme
droits distincts : l'application ouvre toutes les extensions aux comptes et
une liste courte aux anonymes, et l'export suit le droit aux paniers. - L'achat reste chez Stripe tant qu'Opsy n'expose pas de tunnel de commande.
Mis à jour par Damien Cuvillier il y a 4 jours
tu dois maintenant brancher la rubrique commerciale aux fonctionnalités et tarifs enregistrés dans Opsy
Mis à jour par Damien Cuvillier il y a 4 jours
- Statut changé de À Tester à À Faire
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 70 à 10
Mis à jour par Damien Cuvillier il y a 4 jours
- Statut changé de À Faire à En cours
- % réalisé changé de 10 à 20
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 20 à 30
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 30 à 40
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 40 à 50
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 50 à 60
Mis à jour par Damien Cuvillier il y a 4 jours
- % réalisé changé de 60 à 70
Mis à jour par Damien Cuvillier il y a 4 jours
- Fichier claude-session-1584.glog claude-session-1584.glog ajouté
- Statut changé de En cours à À Tester
Claude Code session summary:
#1584 — Offre commerciale¶
Le visiteur anonyme¶
Ses deux plafonds sont ceux demandés, et configurables sans redéploiement
(quota.* dans application.yml) :
| Limite | Valeur | Variable |
|---|---|---|
| Combinaisons par recherche | 20 | ANON_COMBINATIONS_LIMIT |
| Recherches par jour | 5 | ANON_DAILY_SEARCHES |
Ils sont appliqués côté serveur, pas seulement affichés : /api/check et
/api/search-log refusent en 429 (quota du jour) et 403
(combinations_exceeded) — /api/search-log est le seul point qui voit la
recherche entière, donc le seul qui peut compter les combinaisons.
Le visiteur garde sa carte « Démo » en tête de la feuille de tarifs : elle
décrit ce qu'on fait sans compte et ne se vend pas.
Les deux offres¶
Elles vivent dans la console Opsy, pas dans le code. La fiche complète
(codes, libellés, prix, comparatif) est dans
docs/OPSY_CATALOG.md et se pose d'un coup par
scripts/opsy-catalog.py, qui rejoue le même
catalogue d'un royaume à l'autre par l'API d'administration :
scripts/opsy-catalog.py --dry-run # le plan, sans rien appeler
export OPSY_ADMIN_TOKEN="<jeton d'un REALM_ADMIN>"
scripts/opsy-catalog.py --api http://auth.7app.localhost:11501
starter — Compte gratuit |
premium — Premium |
|
|---|---|---|
| Prix | 0 | 20 EUR / an (30 USD, 25 CHF) |
| Recherches / jour | 15 | illimité |
| Combinaisons / recherche | 80 | illimité |
| Générateur de mots-clés | oui | oui |
| Historique | oui | oui |
| Paniers / sélections | non | oui |
| Export CSV | non | oui |
Le front branché sur Opsy¶
-
OpsyCatalogServicelitGET {issuer}/api/public/catalog/domain-seeket le
garde 10 minutes : une offre créée ou repricée dans la console apparaît sans
redéploiement. Opsy injoignable rend un catalogue vide, pas une erreur. -
PricingModeldresse la feuille de tarifs depuis ce catalogue — une carte par
offre publiée, dans l'ordresortOrder, avec les prix de chaque devise que le
royaume vend et le comparatif libellé par Opsy, dans la langue du visiteur.
Tant que le royaume ne publie rien, la grille retombe sur les deux cartes que
l'application portait : la page ne se vide jamais. -
GET /api/licensesert au front ce que le compte détient et ce que
l'application vend ; le paywall ne propose que les offres payantes et renvoie
l'achat au portail Opsy. -
LicenseServicearbitre les droits dans cet ordre : licence active → offre
gratuite du catalogue → plafonds anonymes de l'application.
Corrections apportées au passage¶
-
Les recherches d'un compte étaient comptées sur son navigateur.
/api/checket/api/search-logpassaient toujoursuserId = null: le
plafond de l'offrestarter(15/jour) se remettait à zéro d'un navigateur à
l'autre, et l'écran de quota d'un compte affichait toujours 0. Elles sont
désormais rattachées auuser_profiledu compte connecté (anonyme :
toujours lebrowserId). Couvert parSearchLogControllerTest. -
Devise vendue vs devise détectée. Depuis que la liste des devises vient
du catalogue, un visiteur dont la devise détectée n'est pas vendue par le
royaume (GBP, si le royaume ne vend qu'EUR/USD/CHF) voyait des cartes sans
aucun prix : toutes les étiquettes[data-currency]restaient masquées. La
page retombe maintenant sur la première devise réellement chiffrée, et la
modale de souscription sur le premier prix de l'offre. -
scripts/opsy-catalog.pyn'était pas tout à fait idempotent. Il reposait
les prix à chaque exécution : Opsy archivait l'ancien (OPSY-TAR-003) et
relançait la synchronisation Stripe. Un prix actif identique est maintenant
laissé tel quel. -
Doc.
SUBSCRIPTION_PRICE_{EUR,USD,CHF,GBP}était encore annoncé dans le
README alors que les prix viennent d'Opsy — la ligne renvoie désormais vers
docs/OPSY_CATALOG.md.
Vérifications¶
-
mvn test: 19 tests, tous verts (dont 5 nouveaux sur les deux plafonds et
leur rattachement au compte). -
vite build: OK. - Les textes sont traduits dans les 5 langues (en, fr, es, de, it), pluriels en
{count}.
Deux points restés ouverts¶
-
Les offres n'ont pas été posées sur un royaume. L'Opsy local
(domain-seek.auth.7app.localhost:11501) ne répondait pas pendant ce
travail : le script a été validé en--dry-runseulement. Il reste à le
lancer une fois avec un jetonREALM_ADMIN, sur dev puis sur les autres
royaumes. -
Les montants sont à confirmer. La capture jointe à la demande n'était
pas accessible depuis l'environnement de travail (redmine.gotan.devne
résout pas ici). Les prix repris — 20 EUR / 30 USD / 25 CHF par an — et les
plafonds dustarter(15 recherches, 80 combinaisons) sont ceux de
docs/OPSY_CATALOG.md. Merci de confirmer qu'ils correspondent bien à la
capture ; ils se corrigent dans la console sans toucher au code.