From 081811bea70fa3ac07afb3820ed52e30a1b04ff4 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 5 Jul 2026 16:52:16 +0200 Subject: [PATCH] feat(abonnement): route /subscriptions/change-payment-method --- src/subscriptions/routes.py | 19 ++++++++++++ tests/subscriptions/test_routes.py | 49 ++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/subscriptions/routes.py b/src/subscriptions/routes.py index 4061f7e..4b0a9fe 100644 --- a/src/subscriptions/routes.py +++ b/src/subscriptions/routes.py @@ -129,6 +129,25 @@ def add_payment_callback(): return redirect("/compte/abonnement?paiement=succes") +@subscriptions_bp.route("/subscriptions/change-payment-method", methods=["POST"]) +@login_required +def change_payment_method(): + base = os.getenv("APP_BASE_URL", "") + row = db.get_current(current_user.id) + if row is None or not row["frisbii_subscription_handle"]: + return "Aucun abonnement actif", 400 + try: + url = client.get_payment_info_url( + row["frisbii_subscription_handle"], + f"{base}/compte/abonnement?carte=succes", + f"{base}/compte/abonnement?carte=annule", + ) + except client.FrisbiiError: + logger.exception("Échec de récupération du lien de paiement Frisbii") + return redirect("/compte/abonnement?error=frisbii") + return redirect(url, code=303) + + @subscriptions_bp.route("/subscriptions/cancel", methods=["POST"]) @login_required def cancel(): diff --git a/tests/subscriptions/test_routes.py b/tests/subscriptions/test_routes.py index 2259e82..dca30f8 100644 --- a/tests/subscriptions/test_routes.py +++ b/tests/subscriptions/test_routes.py @@ -159,3 +159,52 @@ def test_subscribe_skips_if_trial_active(logged_in_client, monkeypatch): assert resp.status_code == 302 assert "compte/abonnement" in resp.headers["Location"] assert db.get_current(uid)["status"] == "trial" + + +def test_change_payment_method_redirects_to_hosted_url(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, + "get_payment_info_url", + lambda h, + accept_url, + cancel_url: f"https://pay.test/{h}?a={accept_url}&c={cancel_url}", + ) + resp = client.post("/subscriptions/change-payment-method") + assert resp.status_code == 303 + assert resp.headers["Location"].startswith(f"https://pay.test/{handle}") + assert ( + "carte%3Dsucces" in resp.headers["Location"] + or "carte=succes" in resp.headers["Location"] + ) + assert ( + "carte%3Dannule" in resp.headers["Location"] + or "carte=annule" in resp.headers["Location"] + ) + + +def test_change_payment_method_without_subscription(logged_in_client): + client, _ = logged_in_client + resp = client.post("/subscriptions/change-payment-method") + assert resp.status_code == 400 + + +def test_change_payment_method_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, accept_url, cancel_url): + raise frisbii_client.FrisbiiError(500, "boom") + + monkeypatch.setattr(frisbii_client, "get_payment_info_url", boom) + resp = client.post("/subscriptions/change-payment-method") + assert resp.status_code == 302 + assert "error=frisbii" in resp.headers["Location"] + + +def test_change_payment_method_requires_login(sub_app): + resp = sub_app.test_client().post("/subscriptions/change-payment-method") + assert resp.status_code in (302, 401)