diff options
Diffstat (limited to 'Biz/PodcastItLater/Web.py')
| -rw-r--r-- | Biz/PodcastItLater/Web.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py index 2f86b16..c469874 100644 --- a/Biz/PodcastItLater/Web.py +++ b/Biz/PodcastItLater/Web.py @@ -15,8 +15,10 @@ Provides ludic + htmx interface and RSS feed generation. # : dep pytest-asyncio # : dep pytest-mock # : dep starlette +# : dep stripe import Biz.EmailAgent import Biz.PodcastItLater.Admin as Admin +import Biz.PodcastItLater.Billing as Billing import Biz.PodcastItLater.Core as Core import html as html_module import httpx @@ -1123,6 +1125,76 @@ app.get("/admin")(Admin.admin_queue_status) app.post("/queue/{job_id}/retry")(Admin.retry_queue_item) +@app.get("/billing") +def billing_page(request: Request) -> Response: + """Display billing page with current plan and upgrade options.""" + user_id = request.session.get("user_id") + if not user_id: + return RedirectResponse(url="/?error=login_required") + + user = Core.Database.get_user_by_id(user_id) + if not user: + return RedirectResponse(url="/?error=user_not_found") + + tier = user.get("plan_tier", "free") + tier_info = Billing.get_tier_info(tier) + + # Get current usage + period_start, period_end = Billing.get_period_boundaries(user) + Billing.get_usage(user_id, period_start, period_end) + + # Billing page component to be implemented + return Response(f"<h1>Billing - Current plan: {tier_info['name']}</h1>") + + +@app.post("/billing/checkout") +def billing_checkout(request: Request, data: FormData) -> Response: + """Create Stripe Checkout session.""" + user_id = request.session.get("user_id") + if not user_id: + return Response("Unauthorized", status_code=401) + + tier = data.get("tier", "personal") + if tier not in {"personal", "pro"}: + return Response("Invalid tier", status_code=400) + + try: + checkout_url = Billing.create_checkout_session(user_id, tier, BASE_URL) + return RedirectResponse(url=checkout_url) + except ValueError as e: + logger.exception("Checkout error") + return Response(f"Error: {e!s}", status_code=400) + + +@app.post("/billing/portal") +def billing_portal(request: Request) -> Response: + """Create Stripe Billing Portal session.""" + user_id = request.session.get("user_id") + if not user_id: + return Response("Unauthorized", status_code=401) + + try: + portal_url = Billing.create_portal_session(user_id, BASE_URL) + return RedirectResponse(url=portal_url) + except ValueError as e: + logger.exception("Portal error") + return Response(f"Error: {e!s}", status_code=400) + + +@app.post("/stripe/webhook") +async def stripe_webhook(request: Request) -> Response: + """Handle Stripe webhook events.""" + payload = await request.body() + sig_header = request.headers.get("stripe-signature", "") + + try: + result = Billing.handle_webhook_event(payload, sig_header) + return Response(f"OK: {result['status']}", status_code=200) + except Exception as e: + logger.exception("Webhook error") + return Response(f"Error: {e!s}", status_code=400) + + @app.post("/queue/{job_id}/cancel") def cancel_queue_item(request: Request, job_id: int) -> Response: """Cancel a pending queue item.""" |
