diff --git a/routes.py b/routes.py index 8e00956..5ede910 100644 --- a/routes.py +++ b/routes.py @@ -291,12 +291,12 @@ def ai_message(): # --------------------------------------------------------------------------- @api.route("/payment/success", methods=["POST"]) -@_require_auth def payment_success(): """ - Validate a payment webhook and flip user.has_ai_access. + Server-side payment webhook – NOT callable by clients. - Expected body: { "secret": "" } + Validates the webhook secret and unlocks AI access for the user + identified by the 'user_id' field in the JSON body. For Stripe production: replace the secret comparison with stripe.Webhook.construct_event() using the raw request body and @@ -312,7 +312,15 @@ def payment_success(): ): return jsonify({"error": "Invalid or missing payment secret"}), 403 - user = g.current_user + # Identify the user from the webhook payload (NOT from client auth) + user_id = data.get("user_id") + if not user_id: + return jsonify({"error": "Missing user_id in webhook payload"}), 400 + + user = db.session.get(User, user_id) + if not user: + return jsonify({"error": "User not found"}), 404 + if not user.has_ai_access: user.has_ai_access = True db.session.commit() diff --git a/static/chat.js b/static/chat.js index ca81338..d113435 100644 --- a/static/chat.js +++ b/static/chat.js @@ -546,30 +546,10 @@ $("tab-lobby").onclick = () => switchTab("lobby"); $("close-paywall").onclick = () => paywallModal.classList.add("hidden"); $("unlock-btn").onclick = async () => { - // Generate dummy secret for the stub endpoint - // In production, this would redirect to a real payment gateway (Stripe) - const secret = "change-me-payment-webhook-secret"; - const token = localStorage.getItem("sexychat_token"); - - try { - const resp = await fetch("/api/payment/success", { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${token}` - }, - body: JSON.stringify({ secret }) - }); - const res = await resp.json(); - if (res.status === "ok") { - // socket event should handle UI unlock, but we can optimistically update - state.hasAiAccess = true; - updateVioletBadge(); - paywallModal.classList.add("hidden"); - } - } catch (err) { - alert("Payment simulation failed."); - } + // In production, this redirects to a real payment gateway (Stripe Checkout). + // The server-side webhook will unlock AI access after payment confirmation. + // For now, show a placeholder message. + alert("Payment integration coming soon. Contact the administrator to unlock Violet."); }; logoutBtn.onclick = () => {