| 1 | // Read-only live configuration check. Never installs secrets or creates charges. |
| 2 | import assert from "node:assert/strict"; |
| 3 | import { readFileSync } from "node:fs"; |
| 4 | |
| 5 | const key = readFileSync(".secrets/stripe-live-key.txt", "utf8").trim(); |
| 6 | const signingSecret = readFileSync(".secrets/stripe-live-webhook.txt", "utf8").trim(); |
| 7 | assert.ok(/^(sk|rk)_live_/.test(key), "Expected a live API key"); |
| 8 | assert.ok(/^whsec_\S+$/.test(signingSecret), "Expected a webhook signing secret"); |
| 9 | const source = readFileSync("services/billing/src/webhooks.rs", "utf8"); |
| 10 | const url = source.match(/WEBHOOK_URL: &str = "([^"]+)"/)[1]; |
| 11 | const required = [...source.match(/const EVENTS: &\[&str\] = &\[([\s\S]*?)\];/)[1].matchAll(/"([a-z_.]+)"/g)].map((m) => m[1]); |
| 12 | const version = readFileSync("services/billing/src/stripe.rs", "utf8").match(/STRIPE_VERSION: &str = "([^"]+)"/)[1]; |
| 13 | async function get(path) { |
| 14 | const response = await fetch(`https://api.stripe.com/v1${path}`, { |
| 15 | headers: { Authorization: `Bearer ${key}`, "Stripe-Version": version }, |
| 16 | }); |
| 17 | if (!response.ok) throw Error(`Stripe read failed: HTTP ${response.status}`); |
| 18 | return response.json(); |
| 19 | } |
| 20 | const [account, tax] = await Promise.all([get("/account"), get("/tax/settings")]); |
| 21 | const endpoints = []; |
| 22 | let after; |
| 23 | do { |
| 24 | const page = await get(`/webhook_endpoints?limit=100${after ? `&starting_after=${encodeURIComponent(after)}` : ""}`); |
| 25 | endpoints.push(...page.data.filter((e) => e.url === url)); |
| 26 | after = page.has_more ? page.data.at(-1)?.id : undefined; |
| 27 | assert.ok(!page.has_more || after, "Malformed endpoint pagination"); |
| 28 | } while (after); |
| 29 | const matching = endpoints.map((e) => ({ |
| 30 | id: e.id, |
| 31 | url: e.url, |
| 32 | status: e.status, |
| 33 | live: e.livemode, |
| 34 | apiVersion: e.api_version, |
| 35 | missingEvents: e.enabled_events.includes("*") ? [] : required.filter((t) => !e.enabled_events.includes(t)), |
| 36 | })); |
| 37 | const ready = |
| 38 | account.charges_enabled && |
| 39 | tax.status === "active" && |
| 40 | matching.some((e) => e.live && e.status === "enabled" && e.apiVersion === "2026-08-26.dahlia" && !e.missingEvents.length); |
| 41 | console.log( |
| 42 | JSON.stringify( |
| 43 | { |
| 44 | checkedAt: new Date().toISOString(), |
| 45 | account: account.id, |
| 46 | chargesEnabled: account.charges_enabled, |
| 47 | payoutsEnabled: account.payouts_enabled, |
| 48 | taxStatus: tax.status, |
| 49 | endpoints: matching, |
| 50 | configurationReady: ready, |
| 51 | signingSecretFilePresent: true, |
| 52 | limitations: [ |
| 53 | "Stored signing secret cannot be matched to an existing endpoint through the read API", |
| 54 | "Does not verify deployed secrets, delivery, application readiness, or settlement", |
| 55 | ], |
| 56 | }, |
| 57 | null, |
| 58 | 2, |
| 59 | ), |
| 60 | ); |
| 61 | if (!ready) process.exitCode = 1; |