Lemon Squeezy Failover Runbook
Lemon Squeezy Failover Runbook
Section titled “Lemon Squeezy Failover Runbook”Switch nSelf billing from Stripe to Lemon Squeezy in under 15 minutes. Existing Stripe subscriptions keep running — only new checkouts route through Lemon Squeezy.
When to use this
Section titled “When to use this”- Stripe experiences a regional outage or checkout degradation
- Stripe terminates the nSelf merchant account
- EU VAT or US sales-tax compliance requires a Merchant of Record
Prerequisites
Section titled “Prerequisites”The following must be configured in ping_api environment variables before starting:
| Variable | Where to find it |
|---|---|
LEMONSQUEEZY_API_KEY | Lemon Squeezy dashboard → Settings → API Keys |
LEMONSQUEEZY_STORE_ID | Dashboard URL: app.lemonsqueezy.com/stores/{id} |
LEMONSQUEEZY_WEBHOOK_SECRET | Dashboard → Webhooks → your endpoint secret |
LEMONSQUEEZY_VARIANT_ID_* | Dashboard → Products → each product → Variants |
Verify the webhook endpoint is registered at:
https://ping.nself.org/webhooks/lemonsqueezywith all subscription events enabled (subscription_created, subscription_updated,
subscription_cancelled, order_created).
Step-by-step activation (~10 min)
Section titled “Step-by-step activation (~10 min)”1. Verify LS credentials (2 min)
Section titled “1. Verify LS credentials (2 min)”source ~/.claude/vault.envcurl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $LEMONSQUEEZY_API_KEY" \ https://api.lemonsqueezy.com/v1/stores/$LEMONSQUEEZY_STORE_ID# Expected: 2002. Health-check the admin endpoint (1 min)
Section titled “2. Health-check the admin endpoint (1 min)”curl -s -H "X-Admin-Key: $PING_API_ADMIN_KEY" \ https://ping.nself.org/admin/payment-provider | jq .Expected output:
{ "active": "stripe", "persisted": "stripe", "stripe_configured": true, "lemonsqueezy_configured": true}If lemonsqueezy_configured is false, stop here — set the missing env vars and redeploy
ping_api before continuing.
3. Switch the active provider (1 min)
Section titled “3. Switch the active provider (1 min)”curl -s -X POST \ -H "X-Admin-Key: $PING_API_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{"provider":"lemonsqueezy"}' \ https://ping.nself.org/admin/payment-provider | jq .Expected:
{ "previous": "stripe", "active": "lemonsqueezy", "message": "Payment provider switched to lemonsqueezy. New checkouts will use lemonsqueezy. Existing subscriptions continue unaffected."}4. Verify the switch took effect (1 min)
Section titled “4. Verify the switch took effect (1 min)”curl -s -H "X-Admin-Key: $PING_API_ADMIN_KEY" \ https://ping.nself.org/admin/payment-provider | jq .active# Expected: "lemonsqueezy"5. Test a checkout (5 min)
Section titled “5. Test a checkout (5 min)”Open https://cloud.nself.org/pricing in a private window and start a checkout.
Confirm the checkout page is hosted at checkout.lemonsqueezy.com rather than
checkout.stripe.com.
If Stripe Elements appear instead of the LS checkout page, the cloud frontend is
caching old provider state. Add a query param to bust the cache:
?_bust=$(date +%s).
6. Update status page (1 min)
Section titled “6. Update status page (1 min)”Post to status.nself.org (or nSentry status page if live):
Billing: switched to backup payment provider. Existing subscriptions unaffected.Coexistence during switchover
Section titled “Coexistence during switchover”While PAYMENT_PROVIDER=lemonsqueezy:
- New checkouts go through Lemon Squeezy
- Existing Stripe subscriptions keep renewing — Stripe webhooks (
/webhooks/stripe) continue processing renewals, cancellations, and payment failures - The
/webhooks/lemonsqueezyendpoint handles new LS events in parallel - License activation works identically for both providers
There is no need to migrate existing Stripe subscribers to Lemon Squeezy. They stay on Stripe until they cancel; new sign-ups go through LS.
Reverting to Stripe
Section titled “Reverting to Stripe”curl -s -X POST \ -H "X-Admin-Key: $PING_API_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{"provider":"stripe"}' \ https://ping.nself.org/admin/payment-provider | jq .Environment variable alias
Section titled “Environment variable alias”The internal switch uses PAYMENT_PROVIDER. The admin panel and cloud frontend
also read BILLING_PROVIDER as an alias for display purposes — set either one.
The factory in payments/factory.ts reads PAYMENT_PROVIDER as the authoritative
source; BILLING_PROVIDER is surfaced in health checks only.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check |
|---|---|
lemonsqueezy_configured: false | Missing one of the three required LS env vars |
| Checkout still goes to Stripe | Vercel may be caching a CDN response — check PAYMENT_PROVIDER in Vercel env |
| Webhook signature failures | LEMONSQUEEZY_WEBHOOK_SECRET mismatch — regenerate in LS dashboard and redeploy |
| License not activated after purchase | Check np_payment_events for the LS event ID; check ping_api logs |
503 Lemon Squeezy not fully configured | One of the three required vars is missing from the running ping_api process |