# Stripe billing setup walkthrough This closes production blocker 2. Complete the test-mode checklist first, prove the full lifecycle, then repeat it in live mode. Stripe test and live objects are separate: keys, prices, webhook secrets, customers and subscriptions cannot be mixed. The current production misconfiguration is specific: `STRIPE_PRICE_PREMIUM` contains a Product ID (`prod_...`). JobTracker needs the recurring Price ID attached to that product (`price_...`). The API deliberately treats the Product ID as disabled instead of sending an invalid Checkout request. ## What you need - Access to the Stripe account that will receive JobTracker subscriptions. - Access to `/opt/job-tracker/shared/.env` on the production host. - One disposable JobTracker user for the test purchase. - A decision on the monthly Pro price and currency. Start with one monthly price; annual billing can be added after the monthly lifecycle is proven. Never paste Stripe keys or webhook signing secrets into Git, chat, screenshots, tickets or CI logs. ## Part 1 — create the test subscription price 1. Sign in to the Stripe Dashboard and enable **Test mode**. 2. Open **Product catalog** and either open the existing JobTracker Pro product or create one named `JobTracker Pro`. 3. Add a price with: - pricing model: standard/flat rate; - billing: recurring; - interval: monthly; - currency and amount: your chosen commercial terms. 4. Save the price. 5. Open the new price and copy its identifier. It must start with `price_`, not `prod_`. The product describes what is sold. The price describes how it is billed. Checkout needs the latter. ## Part 2 — configure the customer portal 1. In Stripe, open the customer-portal settings. 2. Enable the portal in test mode. 3. Allow customers to update payment details and cancel subscriptions. 4. Decide whether cancellation is immediate or at the end of the billing period. The application follows Stripe's resulting subscription status; Stripe remains authoritative. 5. Save the portal configuration. ## Part 3 — create the test webhook 1. Open Stripe's webhook/event-destination area in test mode. 2. Add this public HTTPS endpoint: ```text https://jobs.cesnimda.uk/api/billing/webhook ``` 3. Subscribe it to exactly these events: ```text customer.subscription.created customer.subscription.updated customer.subscription.deleted ``` 4. Save the endpoint. 5. Reveal and copy that endpoint's signing secret. It starts with `whsec_`. The endpoint signing secret belongs to this specific webhook endpoint and mode. It is not the API secret key shown on Stripe's API keys page. ## Part 4 — install the three test values On the production host, edit `/opt/job-tracker/shared/.env` and set: ```text STRIPE_SECRET_KEY= STRIPE_PRICE_PREMIUM= STRIPE_WEBHOOK_SECRET= ``` Before saving, verify all three values came from test mode. Do not mix a live key with a test price or test webhook secret. Redeploy after saving so the backend receives the new environment. ## Part 5 — prove the test lifecycle Use a disposable authenticated JobTracker account: 1. Open account/settings and confirm billing is available. 2. Select **Upgrade**. Stripe-hosted Checkout must open for the expected monthly amount and currency. 3. Complete Checkout using a Stripe test card. 4. Confirm Stripe redirects to `/settings?billing=success`. 5. In Stripe's webhook delivery log, confirm the subscription event received a successful `2xx` response. 6. Refresh JobTracker and confirm the user has Pro/Premium entitlements. 7. Open **Manage billing** and confirm the Stripe customer portal opens. 8. Cancel the test subscription using the portal. 9. Confirm the updated/deleted subscription webhook succeeds and JobTracker removes Premium after the subscription is no longer active or trialling. 10. Replay one already-successful event from Stripe. JobTracker's stored state and role must remain correct; replay must not duplicate or corrupt billing state. Do not continue to live mode if any webhook delivery is red, Checkout shows the wrong amount, or the role does not follow Stripe's subscription state. ## Part 6 — live-mode cutover 1. Disable Test mode in Stripe. 2. Create or open the live JobTracker Pro product and create a live recurring monthly price. 3. Configure the live customer portal. 4. Create the live webhook endpoint with the same URL and three subscription events. 5. Replace all three production environment values together: - live `sk_live_...` secret key; - live `price_...` recurring Price ID; - live endpoint-specific `whsec_...` signing secret. 6. Redeploy. 7. Complete one low-risk real subscription, portal visit and cancellation. 8. Confirm every live webhook delivery is successful and the JobTracker role follows the live subscription state. ## Troubleshooting | Symptom | Check | | --- | --- | | Billing appears unavailable | `STRIPE_PRICE_PREMIUM` must begin `price_`; all three settings must be present. | | Checkout says the price does not exist | The API key and Price ID probably come from different Stripe modes or accounts. | | Webhook signature fails | Use the endpoint's `whsec_...` secret, not an API key or a Stripe CLI temporary secret. | | Checkout succeeds but Premium is absent | Inspect the webhook delivery response; JobTracker provisions from signed subscription events. | | Portal does not open | Enable and save the customer portal in the same Stripe mode as the customer. | | Cancellation does not remove Premium | Check the resulting Stripe status and replay the authoritative subscription event. | ## Safe rollback Removing all three Stripe environment values and redeploying disables new Checkout and portal sessions without deleting billing history. Do not manually remove Premium while Stripe still reports an active subscription. Repair webhook delivery and replay the authoritative event instead.