diff --git a/backend/app/routes/billing.py b/backend/app/routes/billing.py index 5ab53fa2..5764bf41 100644 --- a/backend/app/routes/billing.py +++ b/backend/app/routes/billing.py @@ -139,20 +139,21 @@ async def stripe_webhook(): logger.warning(f"Webhook signature invalid: {e}") return jsonify({"message": "Invalid signature"}), 400 - if event["type"] == "checkout.session.completed": - session = event["data"]["object"] + if event.type == "checkout.session.completed": + session = event.data.object # Only process confirmed payments — never grant credits for unpaid sessions - if session.get("payment_status") != "paid": - logger.info("Skipping unpaid checkout session %s", session.get("id")) + if session.payment_status != "paid": + logger.info("Skipping unpaid checkout session %s", session.id) return jsonify({"status": "ok"}), 200 - meta = session.get("metadata", {}) + # metadata is a plain dict in the Stripe SDK + meta = dict(session.metadata) if session.metadata else {} user_id = meta.get("user_id") credits = int(meta.get("credits", 0)) pack_id = meta.get("pack_id", "") # `or` so explicit null payment_intent falls through to session id - payment_id = session.get("payment_intent") or session.get("id", "") + payment_id = session.payment_intent or session.id if not user_id or credits <= 0 or not payment_id: return jsonify({"status": "ok"}), 200