From 8f0a56079f5032e8638bc239d8acc20448da7ef0 Mon Sep 17 00:00:00 2001 From: Deeman Date: Tue, 3 Mar 2026 15:31:52 +0100 Subject: [PATCH] =?UTF-8?q?feat(billing):=20A5=20=E2=80=94=20dual-path=20J?= =?UTF-8?q?S=20templates=20for=20Paddle=20overlay=20/=20Stripe=20redirect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New _payment_js.html: conditionally loads Paddle.js or nothing (Stripe uses server-side Checkout Session). Provides startCheckout() helper. - All checkout templates use _payment_js.html instead of _paddle.html - export.html, signup_step_4.html: Paddle.Checkout.open() → startCheckout() - dashboard_boosts.html: inline onclick → buyItem() with server round-trip - New /billing/checkout/item endpoint for single-item purchases (boosts, credits) Co-Authored-By: Claude Opus 4.6 --- web/src/padelnomics/billing/routes.py | 26 ++++++++++ .../padelnomics/planner/templates/export.html | 8 +-- .../templates/suppliers/dashboard.html | 2 +- .../suppliers/partials/dashboard_boosts.html | 28 +++++++++- .../suppliers/partials/signup_step_4.html | 6 +-- .../suppliers/templates/suppliers/signup.html | 2 +- .../padelnomics/templates/_payment_js.html | 51 +++++++++++++++++++ 7 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 web/src/padelnomics/templates/_payment_js.html diff --git a/web/src/padelnomics/billing/routes.py b/web/src/padelnomics/billing/routes.py index c06708a..84feaba 100644 --- a/web/src/padelnomics/billing/routes.py +++ b/web/src/padelnomics/billing/routes.py @@ -175,6 +175,32 @@ async def checkout(plan: str): return jsonify(payload) +@bp.route("/checkout/item", methods=["POST"]) +@login_required +async def checkout_item(): + """Return checkout JSON for a single item (boost, credit pack, etc.). + + Used by dashboard boost/credit buttons that need a server round-trip + for Stripe (Checkout Session creation) and work with Paddle overlay too. + Expects JSON body: {price_key, custom_data, success_url?} + """ + body = await request.get_json(silent=True) or {} + price_key = body.get("price_key", "") + custom_data = body.get("custom_data", {}) + success_url = body.get("success_url", f"{config.BASE_URL}/suppliers/dashboard?tab=boosts") + + price_id = await get_price_id(price_key) + if not price_id: + return jsonify({"error": "Product not configured."}), 400 + + payload = _provider().build_checkout_payload( + price_id=price_id, + custom_data=custom_data, + success_url=success_url, + ) + return jsonify(payload) + + @bp.route("/manage", methods=["POST"]) @login_required async def manage(): diff --git a/web/src/padelnomics/planner/templates/export.html b/web/src/padelnomics/planner/templates/export.html index 70f99fa..f5c2327 100644 --- a/web/src/padelnomics/planner/templates/export.html +++ b/web/src/padelnomics/planner/templates/export.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block title %}{{ t.export_title }} - {{ config.APP_NAME }}{% endblock %} -{% block paddle %}{% include "_paddle.html" %}{% endblock %} +{% block paddle %}{% include "_payment_js.html" %}{% endblock %} {% block head %}