Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

61 linesCodeBlameRaw
1// Read-only live configuration check. Never installs secrets or creates charges.
2import assert from "node:assert/strict";
3import { readFileSync } from "node:fs";
4
5const key = readFileSync(".secrets/stripe-live-key.txt", "utf8").trim();
6const signingSecret = readFileSync(".secrets/stripe-live-webhook.txt", "utf8").trim();
7assert.ok(/^(sk|rk)_live_/.test(key), "Expected a live API key");
8assert.ok(/^whsec_\S+$/.test(signingSecret), "Expected a webhook signing secret");
9const source = readFileSync("services/billing/src/webhooks.rs", "utf8");
10const url = source.match(/WEBHOOK_URL: &str = "([^"]+)"/)[1];
11const required = [...source.match(/const EVENTS: &\[&str\] = &\[([\s\S]*?)\];/)[1].matchAll(/"([a-z_.]+)"/g)].map((m) => m[1]);
12const version = readFileSync("services/billing/src/stripe.rs", "utf8").match(/STRIPE_VERSION: &str = "([^"]+)"/)[1];
13async 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}
20const [account, tax] = await Promise.all([get("/account"), get("/tax/settings")]);
21const endpoints = [];
22let after;
23do {
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);
29const 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}));
37const 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);
41console.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);
61if (!ready) process.exitCode = 1;