Formulaire d'infos de facturation avant paiement (#abonnement)

- Nouvelle page /compte/abonnement/mes-infos avec formulaire deux
  colonnes (prénom, nom, SIRET, entreprise, adresse, CP, ville, pays)
- Lookup SIRET via l'annuaire des entreprises pour pré-remplissage
- Cases à cocher obligatoires : renonciation rétractation + CGU (modale)
- Le bouton Valider est désactivé tant que les deux cases ne sont pas cochées
- Pays : liste déroulante des 20 pays de la zone euro, France par défaut
- Le SIRET est stocké dans users.siret (migration 0002) pour pré-remplissage
- Pré-remplissage des infos Frisbii (GET customer) si déjà client
- Côté Frisbii : update_customer si client existant, create_customer sinon
- Les URLs accept/cancel sont désormais transmises dans le body Frisbii
- Mise à jour des CGU : distinction données decp.info vs Frisbii

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Colin Maudry
2026-06-29 13:21:10 +02:00
parent 5e5c5170a7
commit b5f529c789
7 changed files with 479 additions and 52 deletions
+15 -9
View File
@@ -36,32 +36,38 @@ def _call(method: str, path: str, json: dict | None = None) -> dict:
except httpx.RequestError as exc:
raise FrisbiiError(0, str(exc)) from exc
if resp.status_code >= 400:
print(resp.text)
raise FrisbiiError(resp.status_code, resp.text)
return resp.json()
def get_or_create_customer(handle: str, email: str) -> dict:
try:
return _call("GET", f"/v1/customer/{handle}")
except FrisbiiError as exc:
if exc.status_code != 404:
raise
return _call("POST", "/v1/customer", json={"handle": handle, "email": email})
def get_customer(handle: str) -> dict:
return _call("GET", f"/v1/customer/{handle}")
def update_customer(handle: str, data: dict) -> dict:
return _call("PUT", f"/v1/customer/{handle}", json=data)
def create_subscription_session(
plan_handle: str,
customer_handle: str,
accept_url: str,
cancel_url: str,
no_trial: bool = False,
customer_handle: str | None = None,
create_customer: dict | None = None,
) -> str:
body: dict = {
"plan": plan_handle,
"customer": customer_handle,
"signup_method": "link",
"generate_handle": True,
"accept_url": accept_url,
"cancel_url": cancel_url,
}
if customer_handle:
body["customer"] = customer_handle
elif create_customer:
body["create_customer"] = create_customer
if no_trial:
body["no_trial"] = True
data = _call("POST", "/v1/subscription", json=body)
+46 -9
View File
@@ -3,6 +3,7 @@ import os
from flask import Blueprint, redirect, request
from flask_login import current_user, login_required
from src.auth import db as auth_db
from src.subscriptions import client, db, plans, webhooks
from src.utils import logger
@@ -28,19 +29,55 @@ def subscribe():
cust = _customer_handle(current_user.id)
try:
meta = plans.plan_meta(plan_key)
client.get_or_create_customer(cust, current_user.email)
db.create_pending(
current_user.id, cust, plan_key, meta["prix_ht"] if meta else None
)
# Anti-abus : pas de nouvel essai si l'utilisateur en a déjà consommé un.
no_trial = db.has_used_trial(current_user.id)
url = client.create_subscription_session(
handle,
cust,
f"{base}/compte/abonnement?paiement=succes",
f"{base}/compte/abonnement?paiement=annule",
no_trial=no_trial,
)
siret = (request.form.get("siret") or "").strip()
billing: dict = {
"email": current_user.email,
"first_name": request.form.get("first_name", ""),
"last_name": request.form.get("last_name", ""),
"address": request.form.get("address", ""),
"city": request.form.get("city", ""),
"postal_code": request.form.get("postal_code", ""),
"country": request.form.get("country", "FR"),
}
if request.form.get("address2"):
billing["address2"] = request.form["address2"]
if request.form.get("company"):
billing["company"] = request.form["company"]
if siret:
auth_db.set_siret(current_user.id, siret)
customer_exists = True
try:
client.update_customer(cust, billing)
except client.FrisbiiError as exc:
if exc.status_code != 404:
raise
customer_exists = False
if customer_exists:
url = client.create_subscription_session(
handle,
f"{base}/compte/abonnement?paiement=succes",
f"{base}/compte/abonnement?paiement=annule",
customer_handle=cust,
no_trial=no_trial,
)
else:
create_customer = {"handle": cust, **billing}
if siret:
create_customer["metadata"] = {"siret": siret}
url = client.create_subscription_session(
handle,
f"{base}/compte/abonnement?paiement=succes",
f"{base}/compte/abonnement?paiement=annule",
create_customer=create_customer,
no_trial=no_trial,
)
except client.FrisbiiError:
logger.exception("Échec de création de session d'abonnement Frisbii")
return redirect("/compte/abonnement?error=frisbii")