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:
@@ -162,6 +162,22 @@ def update_password_hash(user_id: int, password_hash: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
def get_siret(user_id: int) -> str | None:
|
||||
row = (
|
||||
get_conn()
|
||||
.execute("SELECT siret FROM users WHERE id = ?", (user_id,))
|
||||
.fetchone()
|
||||
)
|
||||
return row["siret"] if row else None
|
||||
|
||||
|
||||
def set_siret(user_id: int, siret: str) -> None:
|
||||
get_conn().execute(
|
||||
"UPDATE users SET siret = ?, updated_at = ? WHERE id = ?",
|
||||
(siret, _now(), user_id),
|
||||
)
|
||||
|
||||
|
||||
def set_pending_email(user_id: int, email: str) -> None:
|
||||
get_conn().execute(
|
||||
"UPDATE users SET pending_email = ?, updated_at = ? WHERE id = ?",
|
||||
|
||||
@@ -16,6 +16,10 @@ _MIGRATIONS: list[tuple[str, str]] = [
|
||||
"0001_add_prix_ht_to_subscriptions",
|
||||
"ALTER TABLE subscriptions ADD COLUMN prix_ht REAL",
|
||||
),
|
||||
(
|
||||
"0002_add_siret_to_users",
|
||||
"ALTER TABLE users ADD COLUMN siret TEXT",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ La période d'essai est accordée une seule fois par utilisateur.
|
||||
dcc.Markdown(
|
||||
"""
|
||||
L'abonnement est facturé mensuellement, à la date anniversaire de la souscription.
|
||||
Le paiement est traité par [Frisbii](https://www.frisbii.com), prestataire de paiement en ligne.
|
||||
Le paiement est traité par [Frisbii](https://www.frisbii.com), prestataire européen de paiement en ligne.
|
||||
Les coordonnées bancaires sont conservées exclusivement par Frisbii et ne sont pas transmises à decp.info.
|
||||
"""
|
||||
),
|
||||
@@ -63,28 +63,26 @@ Les coordonnées bancaires sont conservées exclusivement par Frisbii et ne sont
|
||||
"""
|
||||
L'abonnement peut être résilié à tout moment depuis l'espace [Mon compte](/compte/abonnement).
|
||||
La résiliation prend effet à la fin de la période mensuelle en cours : l'accès aux fonctionnalités payantes est maintenu jusqu'à cette date, sans remboursement au prorata.
|
||||
"""
|
||||
),
|
||||
html.H4("Droit de rétractation"),
|
||||
dcc.Markdown(
|
||||
"""
|
||||
Conformément à l'article L221-18 du Code de la consommation, vous disposez d'un délai de 14 jours à compter de la souscription pour exercer votre droit de rétractation, sans motif à fournir.
|
||||
|
||||
Si vous avez demandé l'accès immédiat au service et que vous l'avez effectivement utilisé, une partie proportionnelle à l'utilisation peut être déduite du remboursement.
|
||||
|
||||
Pour exercer ce droit, [contactez-nous](/a-propos/contact).
|
||||
"""
|
||||
),
|
||||
html.H4("Données personnelles"),
|
||||
dcc.Markdown(
|
||||
"""
|
||||
La gestion de l'abonnement nécessite le traitement des données suivantes :
|
||||
La gestion de l'abonnement implique le traitement de données personnelles, réparties entre decp.info et Frisbii (prestataire de paiement).
|
||||
|
||||
- adresse e-mail (identification du compte)
|
||||
- historique de facturation (fourni par Frisbii)
|
||||
**Données stockées par decp.info :**
|
||||
|
||||
- Adresse e-mail (identification du compte)
|
||||
- Numéro SIRET (si renseigné, conservé pour pré-remplir les futures souscriptions)
|
||||
|
||||
**Données stockées par Frisbii :**
|
||||
|
||||
- Informations de facturation : prénom, nom, adresse postale, code postal, ville, pays, nom de l'entreprise
|
||||
- Informations de paiement : coordonnées bancaires (accessibles uniquement par Frisbii, jamais transmises à decp.info)
|
||||
- Historique des factures
|
||||
|
||||
Ces données sont utilisées uniquement pour la gestion de votre abonnement et ne sont pas transmises à des tiers à des fins commerciales.
|
||||
Conformément au RGPD, vous pouvez demander l'accès, la rectification ou la suppression de vos données via la [page de contact](/a-propos/contact).
|
||||
Conformément au RGPD, vous pouvez demander l'accès, la rectification ou la suppression de vos données en [me contactant](/a-propos/contact).
|
||||
"""
|
||||
),
|
||||
html.H4("Contact"),
|
||||
|
||||
@@ -31,7 +31,7 @@ def _plan_card(meta: dict, trial: int | None, trial_used: bool):
|
||||
className="text-muted mb-2",
|
||||
)
|
||||
elif trial:
|
||||
badge = html.Div(f"{trial} jours d'essai gratuit", className="mb-2")
|
||||
badge = html.Div(f"{trial} jours d'essai gratuit", className="mb-3")
|
||||
else:
|
||||
badge = None
|
||||
return dbc.Card(
|
||||
@@ -41,23 +41,18 @@ def _plan_card(meta: dict, trial: int | None, trial_used: bool):
|
||||
html.P(f"{meta['prix_ht']} € HT / mois", className="text-muted mb-3"),
|
||||
html.P(meta["description"], className="mb-3"),
|
||||
badge,
|
||||
html.Form(
|
||||
method="POST",
|
||||
action="/subscriptions/subscribe",
|
||||
children=[
|
||||
_csrf_input(),
|
||||
dcc.Input(type="hidden", name="plan", value=meta["key"]),
|
||||
html.Button(
|
||||
html.A(
|
||||
"S'abonner",
|
||||
type="submit",
|
||||
href=(
|
||||
"#"
|
||||
if TOUS_ABONNES
|
||||
else f"/compte/abonnement/mes-infos?plan={meta['key']}"
|
||||
),
|
||||
className=(
|
||||
"btn btn-secondary disabled"
|
||||
if TOUS_ABONNES
|
||||
else "btn btn-primary"
|
||||
),
|
||||
disabled=TOUS_ABONNES,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
className="p-4",
|
||||
@@ -166,7 +161,10 @@ def _active_view(row):
|
||||
|
||||
def _feedback(query):
|
||||
msgs = {
|
||||
"succes": ("Merci, votre abonnement est en cours d'activation.", "success"),
|
||||
"succes": (
|
||||
"Merci, votre abonnement est activé. Bonne exploration !",
|
||||
"success",
|
||||
),
|
||||
"annule": ("Paiement annulé.", "secondary"),
|
||||
}
|
||||
out = []
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
import dash_bootstrap_components as dbc
|
||||
from dash import Input, Output, State, callback, dcc, html, register_page
|
||||
from flask_login import current_user
|
||||
|
||||
from src.auth import db as auth_db
|
||||
from src.pages._compte_shell import account_guard, account_shell
|
||||
from src.subscriptions import client as frisbii_client
|
||||
from src.utils.data import get_annuaire_data
|
||||
|
||||
register_page(
|
||||
__name__,
|
||||
path="/compte/abonnement/mes-infos",
|
||||
title="Mes informations | Abonnement | decp.info",
|
||||
name="Mes informations de facturation",
|
||||
description="Informations de facturation pour votre abonnement decp.info.",
|
||||
)
|
||||
|
||||
_CGU_MARKDOWN = """
|
||||
L'accès aux fonctionnalités de base de decp.info est gratuit et sans inscription.
|
||||
Un abonnement payant donne accès à des fonctionnalités supplémentaires qui nécessitent la création d'un compte.
|
||||
|
||||
#### Fonctionnalités incluses
|
||||
|
||||
Les fonctionnalités accessibles aux abonnés évoluent au fil du développement du service.
|
||||
La liste à jour est visible depuis la [page d'abonnement](/compte/abonnement).
|
||||
|
||||
#### Tarifs
|
||||
|
||||
Deux formules sont proposées :
|
||||
|
||||
- **Abonnement** — 20 € HT / mois (soit 24 € TTC)
|
||||
- **Abonnement de soutien** — 50 € HT / mois (soit 60 € TTC) — mêmes fonctionnalités, contribution renforcée au projet
|
||||
|
||||
La TVA applicable est de 20 %. Les prix TTC sont affichés lors de la souscription.
|
||||
|
||||
#### Période d'essai
|
||||
|
||||
Une période d'essai gratuite peut être proposée lors de la souscription.
|
||||
Sa durée est indiquée avant validation. Aucun prélèvement n'est effectué pendant cette période.
|
||||
À son terme, l'abonnement est activé et facturé automatiquement.
|
||||
|
||||
La période d'essai est accordée une seule fois par utilisateur.
|
||||
|
||||
#### Facturation et paiement
|
||||
|
||||
L'abonnement est facturé mensuellement, à la date anniversaire de la souscription.
|
||||
Le paiement est traité par [Frisbii](https://www.frisbii.com), prestataire européen de paiement en ligne.
|
||||
Les coordonnées bancaires sont conservées exclusivement par Frisbii et ne sont pas transmises à decp.info.
|
||||
|
||||
#### Résiliation
|
||||
|
||||
L'abonnement peut être résilié à tout moment depuis l'espace [Mon compte](/compte/abonnement).
|
||||
La résiliation prend effet à la fin de la période mensuelle en cours : l'accès aux fonctionnalités payantes est maintenu jusqu'à cette date, sans remboursement au prorata.
|
||||
|
||||
#### Données stockées
|
||||
|
||||
La gestion de l'abonnement implique le traitement de données personnelles, réparties entre decp.info et Frisbii (prestataire de paiement).
|
||||
|
||||
**Données stockées par decp.info :**
|
||||
|
||||
- Adresse e-mail (identification du compte)
|
||||
- Numéro SIRET (si renseigné, conservé pour pré-remplir les futures souscriptions et alimenter des fonctionnalités)
|
||||
|
||||
**Données stockées par Frisbii :**
|
||||
|
||||
- Informations de facturation : prénom, nom, adresse postale, code postal, ville, pays, nom de l'entreprise
|
||||
- Informations de paiement : coordonnées bancaires (accessibles uniquement par Frisbii, jamais transmises à decp.info)
|
||||
- Historique des factures
|
||||
|
||||
Ces données sont utilisées uniquement pour la gestion de votre abonnement et ne sont pas transmises à des tiers à des fins commerciales.
|
||||
Conformément au RGPD, vous pouvez demander l'accès, la rectification ou la suppression de vos données en [me contactant](/a-propos/contact).
|
||||
|
||||
#### Contact
|
||||
|
||||
Pour toute question relative à votre abonnement : [page de contact](/a-propos/contact).
|
||||
"""
|
||||
|
||||
|
||||
def _csrf_input():
|
||||
from flask_wtf.csrf import generate_csrf
|
||||
|
||||
return dcc.Input(type="hidden", name="csrf_token", value=generate_csrf())
|
||||
|
||||
|
||||
def _cgu_modal():
|
||||
return dbc.Modal(
|
||||
[
|
||||
dbc.ModalHeader(dbc.ModalTitle("Conditions générales d'utilisation")),
|
||||
dbc.ModalBody(
|
||||
dcc.Markdown(_CGU_MARKDOWN),
|
||||
style={"maxHeight": "60vh", "overflowY": "auto"},
|
||||
),
|
||||
dbc.ModalFooter(
|
||||
dbc.Button(
|
||||
"Fermer", id="inf-cgu-close", className="ms-auto", color="secondary"
|
||||
)
|
||||
),
|
||||
],
|
||||
id="inf-cgu-modal",
|
||||
size="lg",
|
||||
is_open=False,
|
||||
)
|
||||
|
||||
|
||||
def layout(**query):
|
||||
guard = account_guard("/compte/abonnement/mes-infos", require_subscription=False)
|
||||
if guard is not None:
|
||||
return guard
|
||||
|
||||
plan = query.get("plan", "")
|
||||
if not plan:
|
||||
return dcc.Location(href="/compte/abonnement", id="inf-no-plan-redirect")
|
||||
|
||||
prefill: dict = {}
|
||||
try:
|
||||
prefill = frisbii_client.get_customer(f"decpinfo-{current_user.id}")
|
||||
except frisbii_client.FrisbiiError:
|
||||
prefill = {}
|
||||
stored_siret = auth_db.get_siret(current_user.id) or ""
|
||||
|
||||
col1 = dbc.Col(
|
||||
[
|
||||
dbc.Label("Prénom *"),
|
||||
dbc.Input(
|
||||
id="inf-prenom",
|
||||
name="first_name",
|
||||
type="text",
|
||||
required=True,
|
||||
value=prefill.get("first_name", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("Nom *"),
|
||||
dbc.Input(
|
||||
id="inf-nom",
|
||||
name="last_name",
|
||||
type="text",
|
||||
required=True,
|
||||
value=prefill.get("last_name", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("SIRET"),
|
||||
dbc.InputGroup(
|
||||
[
|
||||
dbc.Input(
|
||||
id="inf-siret",
|
||||
name="siret",
|
||||
type="text",
|
||||
maxLength=14,
|
||||
placeholder="14 chiffres",
|
||||
value=stored_siret,
|
||||
),
|
||||
dbc.Button(
|
||||
"Récupérer les autres infos",
|
||||
id="inf-siret-btn",
|
||||
color="secondary",
|
||||
type="button",
|
||||
n_clicks=0,
|
||||
),
|
||||
],
|
||||
className="mb-1",
|
||||
),
|
||||
html.Div(id="inf-siret-msg", className="small mb-3"),
|
||||
dbc.Label("Nom de l'entreprise"),
|
||||
dbc.Input(
|
||||
id="inf-entreprise",
|
||||
name="company",
|
||||
type="text",
|
||||
value=prefill.get("company", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
],
|
||||
md=6,
|
||||
)
|
||||
|
||||
col2 = dbc.Col(
|
||||
[
|
||||
dbc.Label("Adresse ligne 1 *"),
|
||||
dbc.Input(
|
||||
id="inf-adresse1",
|
||||
name="address",
|
||||
type="text",
|
||||
required=True,
|
||||
value=prefill.get("address", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("Adresse ligne 2"),
|
||||
dbc.Input(
|
||||
id="inf-adresse2",
|
||||
name="address2",
|
||||
type="text",
|
||||
value=prefill.get("address2", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("Code postal *"),
|
||||
dbc.Input(
|
||||
id="inf-cp",
|
||||
name="postal_code",
|
||||
type="text",
|
||||
required=True,
|
||||
value=prefill.get("postal_code", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("Ville *"),
|
||||
dbc.Input(
|
||||
id="inf-ville",
|
||||
name="city",
|
||||
type="text",
|
||||
required=True,
|
||||
value=prefill.get("city", ""),
|
||||
className="mb-3",
|
||||
),
|
||||
dbc.Label("Pays *"),
|
||||
dbc.Select(
|
||||
id="inf-pays",
|
||||
name="country",
|
||||
value=prefill.get("country", "FR"),
|
||||
options=[
|
||||
{"label": "France", "value": "FR"},
|
||||
{"label": "Allemagne", "value": "DE"},
|
||||
{"label": "Autriche", "value": "AT"},
|
||||
{"label": "Belgique", "value": "BE"},
|
||||
{"label": "Chypre", "value": "CY"},
|
||||
{"label": "Croatie", "value": "HR"},
|
||||
{"label": "Espagne", "value": "ES"},
|
||||
{"label": "Estonie", "value": "EE"},
|
||||
{"label": "Finlande", "value": "FI"},
|
||||
{"label": "Grèce", "value": "GR"},
|
||||
{"label": "Irlande", "value": "IE"},
|
||||
{"label": "Italie", "value": "IT"},
|
||||
{"label": "Lettonie", "value": "LV"},
|
||||
{"label": "Lituanie", "value": "LT"},
|
||||
{"label": "Luxembourg", "value": "LU"},
|
||||
{"label": "Malte", "value": "MT"},
|
||||
{"label": "Pays-Bas", "value": "NL"},
|
||||
{"label": "Portugal", "value": "PT"},
|
||||
{"label": "Slovaquie", "value": "SK"},
|
||||
{"label": "Slovénie", "value": "SI"},
|
||||
],
|
||||
className="mb-3",
|
||||
),
|
||||
],
|
||||
md=6,
|
||||
)
|
||||
|
||||
checkboxes = html.Div(
|
||||
[
|
||||
dcc.Checklist(
|
||||
id="inf-cb-retractation",
|
||||
options=[
|
||||
{
|
||||
"label": "Je renonce à mon droit de rétractation légal de 14 jours.",
|
||||
"value": "ok",
|
||||
}
|
||||
],
|
||||
value=[],
|
||||
className="mb-2",
|
||||
),
|
||||
dcc.Checklist(
|
||||
id="inf-cb-cgu",
|
||||
options=[
|
||||
{
|
||||
"label": [
|
||||
"J'ai lu et accepte les ",
|
||||
html.A(
|
||||
"conditions générales d'utilisation du service",
|
||||
href="#",
|
||||
id="inf-cgu-link",
|
||||
style={"cursor": "pointer"},
|
||||
),
|
||||
".",
|
||||
],
|
||||
"value": "ok",
|
||||
}
|
||||
],
|
||||
value=[],
|
||||
className="mb-4",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
form = html.Form(
|
||||
method="POST",
|
||||
action="/subscriptions/subscribe",
|
||||
children=[
|
||||
_csrf_input(),
|
||||
dcc.Input(type="hidden", name="plan", value=plan),
|
||||
dbc.Row([col1, col2], className="g-4 mb-4"),
|
||||
checkboxes,
|
||||
html.Button(
|
||||
"Suivant",
|
||||
id="inf-submit",
|
||||
type="submit",
|
||||
className="btn btn-primary",
|
||||
disabled=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
return account_shell(
|
||||
"abonnement",
|
||||
html.Div(
|
||||
[
|
||||
html.H2("Mes informations de facturation", className="mb-4"),
|
||||
form,
|
||||
_cgu_modal(),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@callback(
|
||||
Output("inf-entreprise", "value"),
|
||||
Output("inf-adresse1", "value"),
|
||||
Output("inf-cp", "value"),
|
||||
Output("inf-ville", "value"),
|
||||
Output("inf-siret-msg", "children"),
|
||||
Output("inf-siret-msg", "className"),
|
||||
Input("inf-siret-btn", "n_clicks"),
|
||||
State("inf-siret", "value"),
|
||||
prevent_initial_call=True,
|
||||
)
|
||||
def _lookup_siret(_, siret):
|
||||
empty = ("", "", "", "")
|
||||
if not siret or not siret.strip():
|
||||
return *empty, "Veuillez saisir un SIRET.", "small text-danger mb-3"
|
||||
data = get_annuaire_data(siret.strip())
|
||||
if data is None:
|
||||
return (
|
||||
*empty,
|
||||
"SIRET introuvable dans l'annuaire des entreprises.",
|
||||
"small text-danger mb-3",
|
||||
)
|
||||
etablissement = data.get("matching_etablissements")
|
||||
if not etablissement:
|
||||
return (
|
||||
*empty,
|
||||
"Aucun établissement trouvé pour ce SIRET.",
|
||||
"small text-danger mb-3",
|
||||
)
|
||||
etab = etablissement[0]
|
||||
return (
|
||||
data.get("nom_raison_sociale", ""),
|
||||
etab.get("adresse", ""),
|
||||
etab.get("code_postal", ""),
|
||||
etab.get("libelle_commune", ""),
|
||||
"Informations récupérées.",
|
||||
"small text-success mb-3",
|
||||
)
|
||||
|
||||
|
||||
@callback(
|
||||
Output("inf-submit", "disabled"),
|
||||
Input("inf-cb-retractation", "value"),
|
||||
Input("inf-cb-cgu", "value"),
|
||||
)
|
||||
def _toggle_submit(retractation, cgu):
|
||||
return not (retractation and cgu)
|
||||
|
||||
|
||||
@callback(
|
||||
Output("inf-cgu-modal", "is_open"),
|
||||
Input("inf-cgu-link", "n_clicks"),
|
||||
Input("inf-cgu-close", "n_clicks"),
|
||||
State("inf-cgu-modal", "is_open"),
|
||||
prevent_initial_call=True,
|
||||
)
|
||||
def _toggle_cgu(_, __, is_open):
|
||||
return not is_open
|
||||
@@ -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:
|
||||
def get_customer(handle: str) -> dict:
|
||||
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 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)
|
||||
|
||||
@@ -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,17 +29,53 @@ 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)
|
||||
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,
|
||||
cust,
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user