feat(abonnement): route /subscriptions/update (maj formule + facturation) (#109)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,49 @@ def change_payment_method():
|
|||||||
return redirect(url, code=303)
|
return redirect(url, code=303)
|
||||||
|
|
||||||
|
|
||||||
|
@subscriptions_bp.route("/subscriptions/update", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def update():
|
||||||
|
new_plan_key = request.form.get("plan") or ""
|
||||||
|
new_handle = plans.resolve_handle(new_plan_key)
|
||||||
|
if new_handle is None:
|
||||||
|
return "Plan inconnu", 400
|
||||||
|
|
||||||
|
row = db.get_current(current_user.id)
|
||||||
|
if row is None or not row["frisbii_subscription_handle"]:
|
||||||
|
return "Aucun abonnement à mettre à jour", 400
|
||||||
|
|
||||||
|
cust = _customer_handle(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"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if siret:
|
||||||
|
auth_db.set_siret(current_user.id, siret)
|
||||||
|
client.update_customer(cust, billing)
|
||||||
|
if new_handle != plans.resolve_handle(row["plan"]):
|
||||||
|
timing = "immediate" if row["status"] == "pending" else "renewal"
|
||||||
|
client.change_subscription(
|
||||||
|
row["frisbii_subscription_handle"], new_handle, timing
|
||||||
|
)
|
||||||
|
except client.FrisbiiError:
|
||||||
|
logger.exception("Échec de mise à jour de l'abonnement Frisbii")
|
||||||
|
return redirect("/compte/abonnement?error=frisbii")
|
||||||
|
return redirect("/compte/abonnement?maj=succes", code=303)
|
||||||
|
|
||||||
|
|
||||||
@subscriptions_bp.route("/subscriptions/cancel", methods=["POST"])
|
@subscriptions_bp.route("/subscriptions/cancel", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def cancel():
|
def cancel():
|
||||||
|
|||||||
@@ -208,3 +208,92 @@ def test_change_payment_method_api_error_redirects(logged_in_client, monkeypatch
|
|||||||
def test_change_payment_method_requires_login(sub_app):
|
def test_change_payment_method_requires_login(sub_app):
|
||||||
resp = sub_app.test_client().post("/subscriptions/change-payment-method")
|
resp = sub_app.test_client().post("/subscriptions/change-payment-method")
|
||||||
assert resp.status_code in (302, 401)
|
assert resp.status_code in (302, 401)
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_changes_plan_and_redirects(logged_in_client, monkeypatch):
|
||||||
|
client, uid = logged_in_client
|
||||||
|
handle, _ = db.create_pending(uid, "colibre-%d" % uid, "simple")
|
||||||
|
db.update_from_webhook(handle, "active", "2099-01-01T00:00:00+00:00")
|
||||||
|
monkeypatch.setattr(frisbii_client, "update_customer", lambda h, d: {})
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
def fake_change(sub_handle, plan_handle, timing="renewal"):
|
||||||
|
captured.update(sub_handle=sub_handle, plan_handle=plan_handle, timing=timing)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
monkeypatch.setattr(frisbii_client, "change_subscription", fake_change)
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "soutien"})
|
||||||
|
assert resp.status_code == 303
|
||||||
|
assert "maj=succes" in resp.headers["Location"]
|
||||||
|
assert captured["sub_handle"] == handle
|
||||||
|
assert captured["plan_handle"] == "plan_soutien"
|
||||||
|
assert captured["timing"] == "renewal"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_pending_uses_immediate_timing(logged_in_client, monkeypatch):
|
||||||
|
client, uid = logged_in_client
|
||||||
|
handle, _ = db.create_pending(uid, "colibre-%d" % uid, "simple")
|
||||||
|
monkeypatch.setattr(frisbii_client, "update_customer", lambda h, d: {})
|
||||||
|
captured = {}
|
||||||
|
monkeypatch.setattr(
|
||||||
|
frisbii_client,
|
||||||
|
"change_subscription",
|
||||||
|
lambda sub_handle, plan_handle, timing="renewal": captured.update(
|
||||||
|
timing=timing
|
||||||
|
),
|
||||||
|
)
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "soutien"})
|
||||||
|
assert resp.status_code == 303
|
||||||
|
assert captured["timing"] == "immediate"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_same_plan_skips_change_subscription(logged_in_client, monkeypatch):
|
||||||
|
client, uid = logged_in_client
|
||||||
|
handle, _ = db.create_pending(uid, "colibre-%d" % uid, "simple")
|
||||||
|
db.update_from_webhook(handle, "active", "2099-01-01T00:00:00+00:00")
|
||||||
|
called = []
|
||||||
|
monkeypatch.setattr(frisbii_client, "update_customer", lambda h, d: {})
|
||||||
|
monkeypatch.setattr(
|
||||||
|
frisbii_client,
|
||||||
|
"change_subscription",
|
||||||
|
lambda *a, **k: called.append(a),
|
||||||
|
)
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "simple"})
|
||||||
|
assert resp.status_code == 303
|
||||||
|
assert "maj=succes" in resp.headers["Location"]
|
||||||
|
assert called == [], (
|
||||||
|
"formule inchangée : change_subscription ne doit pas être appelé"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_without_subscription_returns_400(logged_in_client):
|
||||||
|
client, _ = logged_in_client
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "simple"})
|
||||||
|
assert resp.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_unknown_plan_returns_400(logged_in_client, monkeypatch):
|
||||||
|
client, uid = logged_in_client
|
||||||
|
handle, _ = db.create_pending(uid, "colibre-%d" % uid, "simple")
|
||||||
|
db.update_from_webhook(handle, "active", "2099-01-01T00:00:00+00:00")
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "bidon"})
|
||||||
|
assert resp.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_api_error_redirects(logged_in_client, monkeypatch):
|
||||||
|
client, uid = logged_in_client
|
||||||
|
handle, _ = db.create_pending(uid, "colibre-%d" % uid, "simple")
|
||||||
|
db.update_from_webhook(handle, "active", "2099-01-01T00:00:00+00:00")
|
||||||
|
|
||||||
|
def boom(h, d):
|
||||||
|
raise frisbii_client.FrisbiiError(500, "boom")
|
||||||
|
|
||||||
|
monkeypatch.setattr(frisbii_client, "update_customer", boom)
|
||||||
|
resp = client.post("/subscriptions/update", data={"plan": "soutien"})
|
||||||
|
assert resp.status_code == 302
|
||||||
|
assert "error=frisbii" in resp.headers["Location"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_requires_login(sub_app):
|
||||||
|
resp = sub_app.test_client().post("/subscriptions/update", data={"plan": "simple"})
|
||||||
|
assert resp.status_code in (302, 401)
|
||||||
|
|||||||
Reference in New Issue
Block a user