feat: Stripe integration complete - products created, DB migration, payment_failed handler, credit buttons wired
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m5s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m5s
- Create Stripe products/prices (Starter/Pro/Business monthly+yearly) - Fix CRITICAL bug: add subscription_ends_at + cancel_at_period_end columns to users table - Alembic migration: f6a7b8c9d0e1_add_subscription_ends_at_cancel_at_period_end - Fix: implement handle_payment_failed() to set subscription_status=PAST_DUE - Fix: harmonize .env.production Stripe variable names to match pricing_config.py - Fix: add missing FRONTEND_URL and STRIPE_PUBLISHABLE_KEY to .env.production - Add all Stripe Price IDs (test mode) to .env.production - Wire credit purchase buttons to /api/v1/auth/create-credits-checkout - Dashboard sync post-checkout was already implemented (no change needed) Stripe test keys: configured in .env.production Webhook: must be configured on server via stripe CLI or Stripe Dashboard Webhook URL: https://wordly.art/api/v1/auth/webhook/stripe
This commit is contained in:
@@ -2,9 +2,12 @@
|
||||
Stripe payment integration for subscriptions and credits
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Try to import stripe
|
||||
try:
|
||||
import stripe
|
||||
@@ -347,13 +350,33 @@ async def handle_subscription_deleted(subscription: Dict):
|
||||
|
||||
|
||||
async def handle_payment_failed(invoice: Dict):
|
||||
"""Handle failed payment"""
|
||||
"""Handle failed payment — set subscription status to PAST_DUE"""
|
||||
customer_id = invoice.get("customer")
|
||||
if not customer_id:
|
||||
return
|
||||
|
||||
# Find user by customer ID and update status
|
||||
# In production, query database by stripe_customer_id
|
||||
|
||||
# Find user by stripe_customer_id
|
||||
user = None
|
||||
try:
|
||||
from database.connection import get_sync_session
|
||||
from database.models import User as DBUser
|
||||
|
||||
with get_sync_session() as session:
|
||||
db_user = (
|
||||
session.query(DBUser)
|
||||
.filter(DBUser.stripe_customer_id == customer_id)
|
||||
.first()
|
||||
)
|
||||
if db_user:
|
||||
user_id = str(db_user.id)
|
||||
db_user.subscription_status = SubscriptionStatus.PAST_DUE
|
||||
session.commit()
|
||||
logger.warning(
|
||||
"Payment failed for customer %s (user %s) — status set to past_due",
|
||||
customer_id, user_id,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error("handle_payment_failed DB error: %s", exc)
|
||||
|
||||
|
||||
async def cancel_subscription(user_id: str) -> Dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user