""" Step 1: Register a Stripe webhook endpoint via ngrok and update .env. Run BEFORE starting the dev server: 1. Start ngrok: ngrok http 5000 2. Run this script: uv run python scripts/stripe_e2e_setup.py 3. Start dev server: make dev 4. Run E2E tests: uv run python scripts/stripe_e2e_test.py To tear down afterward: uv run python scripts/stripe_e2e_setup.py --teardown """ import json import os import re import sys import urllib.request from dotenv import load_dotenv load_dotenv() import stripe STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY", "") or os.getenv("STRIPE_API_PRIVATE_KEY", "") if not STRIPE_SECRET_KEY: print("ERROR: Set STRIPE_SECRET_KEY or STRIPE_API_PRIVATE_KEY in .env") sys.exit(1) stripe.api_key = STRIPE_SECRET_KEY stripe.max_network_retries = 2 ENV_PATH = os.path.join(os.path.dirname(__file__), "..", ".env") ENV_PATH = os.path.abspath(ENV_PATH) WEBHOOK_PATH = "/billing/webhook/stripe" NGROK_API = "http://localhost:4040/api/tunnels" def _update_env(key, value): """Update a key in .env file.""" text = open(ENV_PATH).read() pattern = rf"^{key}=.*$" replacement = f"{key}={value}" if re.search(pattern, text, re.MULTILINE): text = re.sub(pattern, replacement, text, flags=re.MULTILINE) else: text = text.rstrip("\n") + f"\n{replacement}\n" open(ENV_PATH, "w").write(text) def setup(): # Get ngrok tunnel URL try: resp = urllib.request.urlopen(NGROK_API, timeout=5) tunnels = json.loads(resp.read()) tunnel_url = tunnels["tunnels"][0]["public_url"] except Exception as e: print(f"ERROR: ngrok not running: {e}") print("Start ngrok first: ngrok http 5000") sys.exit(1) webhook_url = f"{tunnel_url}{WEBHOOK_PATH}" print(f"ngrok tunnel: {tunnel_url}") print(f"Webhook URL: {webhook_url}") # Check for existing E2E webhook endpoint existing_id = os.getenv("STRIPE_WEBHOOK_ENDPOINT_ID", "") if existing_id: try: ep = stripe.WebhookEndpoint.retrieve(existing_id) if ep.url == webhook_url and ep.status == "enabled": print(f"\nEndpoint already exists and matches: {existing_id}") print("Ready to test. Run: uv run python scripts/stripe_e2e_test.py") return # URL changed (new ngrok session), delete and recreate print(f"Existing endpoint URL mismatch, recreating...") stripe.WebhookEndpoint.delete(existing_id) except stripe.InvalidRequestError: pass # Already deleted # Create webhook endpoint endpoint = stripe.WebhookEndpoint.create( url=webhook_url, enabled_events=[ "checkout.session.completed", "customer.subscription.created", "customer.subscription.updated", "customer.subscription.deleted", "invoice.payment_failed", ], ) print(f"\nCreated endpoint: {endpoint.id}") print(f"Webhook secret: {endpoint.secret[:25]}...") # Update .env _update_env("STRIPE_WEBHOOK_SECRET", endpoint.secret) _update_env("STRIPE_WEBHOOK_ENDPOINT_ID", endpoint.id) print("\nUpdated .env with STRIPE_WEBHOOK_SECRET and STRIPE_WEBHOOK_ENDPOINT_ID") print("\nNext steps:") print(" 1. Restart dev server: make dev") print(" 2. Run E2E tests: uv run python scripts/stripe_e2e_test.py") def teardown(): endpoint_id = os.getenv("STRIPE_WEBHOOK_ENDPOINT_ID", "") if endpoint_id: try: stripe.WebhookEndpoint.delete(endpoint_id) print(f"Deleted webhook endpoint: {endpoint_id}") except stripe.InvalidRequestError: print(f"Endpoint {endpoint_id} already deleted") _update_env("STRIPE_WEBHOOK_SECRET", "") _update_env("STRIPE_WEBHOOK_ENDPOINT_ID", "") print("Cleared .env webhook config") if __name__ == "__main__": if "--teardown" in sys.argv: teardown() else: setup()