76 lines · 3.2 KB
/**
 * Creates (or finds) the Stripe test-mode objects gitfor uses: a Pro monthly price and the prepaid packs, at the
 * amounts in src/lib/pricing.ts, then writes STRIPE_PRICE_PRO and STRIPE_TOPUP_PRICES into .dev.vars. Idempotent via price lookup keys.
 *
 *   STRIPE_SETUP_KEY=sk_test_... bun run scripts/stripe-test-setup.ts [--write .dev.vars]
 *
 * Refuses anything but a test-mode key (sk_test_ / rk_test_) and never prints the key.
 */
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import Stripe from "stripe";
import { PACKS, PRO_PRICE_CENTS } from "../src/lib/pricing";

const key = process.env.STRIPE_SETUP_KEY ?? "";
if (!/^(sk|rk)_test_/.test(key)) {
  console.error("STRIPE_SETUP_KEY must be a Stripe test-mode key (sk_test_ or rk_test_); refusing to continue.");
  process.exit(1);
}
const stripe = new Stripe(key);

/**
 * The active price for `lookupKey` at exactly `cents`. Stripe prices are immutable, so when pricing.ts changes
 * the amount a new price takes over the lookup key and the old one is archived (existing subscriptions keep it).
 */
async function ensurePrice(
  lookupKey: string,
  product: string,
  cents: number,
  recurring: boolean,
  metadata: Record<string, string>,
): Promise<string> {
  const existing = (await stripe.prices.list({ lookup_keys: [lookupKey], limit: 1, expand: ["data.product"] })).data[0];
  if (existing && existing.unit_amount === cents && existing.active) return existing.id;
  const productId = typeof existing?.product === "object" ? existing.product.id : (existing?.product ?? null);
  const price = await stripe.prices.create({
    currency: "usd",
    unit_amount: cents,
    lookup_key: lookupKey,
    transfer_lookup_key: true,
    metadata,
    ...(recurring ? { recurring: { interval: "month" } } : {}),