fix(billing): handle Stripe auth/config errors distinctly; document Stripe env
The /billing Checkout/Portal failed opaquely when the live Stripe secret key was revoked/expired: the generic rescue showed "Could not start checkout. Please try again", which blames the user for a server-side misconfig. Now rescue Stripe::AuthenticationError / PermissionError separately, log it loudly for ops, and show an honest "Billing is temporarily unavailable" message. Also document STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET in .env.example (they were undocumented, which is how a rotated/expired key went unnoticed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seto Elkahfi committed
Jun 30, 2026 at 21:37 UTC
1ff18bde5e19c2e6768aeeea416e344447307fc5
2 files changed
+23
-2
.env.example
+13
@@ -37,6 +37,19 @@ ONDE_CLOUD_APP_SECRET=your-onde-app-secret-here
37
# ONDE_CLOUD_BASE_URL=https://cloud.ondeinference.com/v1
38
39
40
+# -----------------------------------------------------------------------------
41
+# Stripe — siGit Code Pro/Team billing (the /billing page + Checkout/Portal).
42
+# Separate from Onde Cloud billing. Billing is a no-op unless STRIPE_SECRET_KEY
43
+# is set. Use sk_test_… in dev; sk_live_… in production. Rotating the key in the
44
+# Stripe dashboard revokes the old one, so update this value too or Checkout
45
+# fails with "Expired API Key".
46
+# -----------------------------------------------------------------------------
47
+
48
+STRIPE_SECRET_KEY=sk_test_your-stripe-secret-key
49
+# Signing secret for the /stripe/webhooks endpoint (Stripe CLI or dashboard).
50
+STRIPE_WEBHOOK_SECRET=whsec_your-webhook-signing-secret
51
+
52
+
53
# -----------------------------------------------------------------------------
54
# Database (PostgreSQL)
55
# The defaults below work with a local Postgres.app installation.
app/controllers/billing_controller.rb
+10
-2
@@ -28,8 +28,13 @@ class BillingController < ApplicationController
28
cancel_url: billing_url(billing: "cancel")
29
)
30
redirect_to url, allow_other_host: true
31
+ rescue Stripe::AuthenticationError, Stripe::PermissionError => e
32
+ # A revoked/expired key or wrong-account key — a server-side misconfig, not a
33
+ # user error. Don't tell the user to "try again"; flag it loudly for ops.
34
+ Rails.logger.error("Stripe is misconfigured (checkout): #{e.class}: #{e.message}")
35
+ redirect_to billing_path, alert: "Billing is temporarily unavailable. Please try again later."
36
rescue StandardError => e
32
- Rails.logger.error("Stripe checkout failed: #{e.message}")
37
+ Rails.logger.error("Stripe checkout failed: #{e.class}: #{e.message}")
38
redirect_to billing_path, alert: "Could not start checkout. Please try again."
39
end
40
@@ -40,8 +45,11 @@ class BillingController < ApplicationController
45
return redirect_to billing_path, alert: "No subscription to manage yet." unless url
46
47
redirect_to url, allow_other_host: true
48
+ rescue Stripe::AuthenticationError, Stripe::PermissionError => e
49
+ Rails.logger.error("Stripe is misconfigured (portal): #{e.class}: #{e.message}")
50
+ redirect_to billing_path, alert: "Billing is temporarily unavailable. Please try again later."
51
rescue StandardError => e
44
- Rails.logger.error("Stripe portal failed: #{e.message}")
52
+ Rails.logger.error("Stripe portal failed: #{e.class}: #{e.message}")
53
redirect_to billing_path, alert: "Could not open the billing portal."
54
end
55
end