diff --git a/backend/services/subscription/stripe_service.py b/backend/services/subscription/stripe_service.py index 0a67a99e..2e20e7c2 100644 --- a/backend/services/subscription/stripe_service.py +++ b/backend/services/subscription/stripe_service.py @@ -16,6 +16,10 @@ REQUIRED_STRIPE_PLAN_KEYS = { } +def _is_truthy_env(var_name: str) -> bool: + return os.getenv(var_name, "").strip().lower() in {"1", "true", "yes", "on"} + + def _detect_stripe_mode() -> str: configured_mode = os.getenv("STRIPE_MODE", "").strip().lower() if configured_mode in {"test", "live"}: @@ -98,7 +102,16 @@ class StripeService: self.db = db self.api_key = os.getenv("STRIPE_SECRET_KEY") self.webhook_secret = os.getenv("STRIPE_WEBHOOK_SECRET") + self.require_stripe_checkout = _is_truthy_env("REQUIRE_STRIPE_CHECKOUT") if not self.api_key: + if self.require_stripe_checkout: + raise HTTPException( + status_code=500, + detail=( + "REQUIRE_STRIPE_CHECKOUT=true but STRIPE_SECRET_KEY is missing. " + "Configure STRIPE_SECRET_KEY to enable Stripe checkout." + ), + ) logger.warning("STRIPE_SECRET_KEY is not set. Stripe integration will not work.") else: stripe.api_key = self.api_key diff --git a/frontend/src/components/Pricing/PricingPage.tsx b/frontend/src/components/Pricing/PricingPage.tsx index d7f01d7e..6e334af4 100644 --- a/frontend/src/components/Pricing/PricingPage.tsx +++ b/frontend/src/components/Pricing/PricingPage.tsx @@ -73,6 +73,10 @@ const PricingPage: React.FC = () => { }; const activeTierPolicy = tierPolicyByMode[pricingMode] || tierPolicyByMode.alpha; + const requireStripeCheckout = ['1', 'true', 'yes', 'on'].includes( + (process.env.REACT_APP_REQUIRE_STRIPE_CHECKOUT || '').toLowerCase() + ); + const stripePublishableKey = process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY; const navigate = useNavigate(); const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(true); @@ -225,7 +229,7 @@ const PricingPage: React.FC = () => { const userId = localStorage.getItem('user_id') || 'anonymous'; // Check if Stripe is configured - if (process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY) { + if (stripePublishableKey) { console.log('[PricingPage] Initiating Stripe Checkout'); const response = await apiClient.post('/api/subscription/create-checkout-session', { @@ -239,11 +243,18 @@ const PricingPage: React.FC = () => { window.location.href = response.data.url; return; } + + if (requireStripeCheckout) { + throw new Error('Stripe checkout is required but checkout URL was not returned.'); + } + } else if (requireStripeCheckout) { + throw new Error( + 'Stripe checkout is required but REACT_APP_STRIPE_PUBLISHABLE_KEY is not configured.' + ); } else { // Stripe not configured - warn in demo mode if (isPodcastOnlyDemoMode()) { console.warn('[PricingPage] ⚠️ DEMO MODE WARNING: Stripe is not configured. Using legacy subscription API.'); - // In demo mode without Stripe, we may want to skip actual subscription for testing } } @@ -333,7 +344,8 @@ const PricingPage: React.FC = () => { }, 3000); } catch (err) { console.error('Error subscribing:', err); - setError('Failed to process subscription'); + const errorMessage = err instanceof Error ? err.message : 'Failed to process subscription'; + setError(errorMessage); setSuccessSnackbar({ open: false, message: '', countdown: 0 }); } finally { setSubscribing(false);