a tool for shared writing and social publishing
1import Stripe from "stripe";
2import { PRODUCT_DEF_ID, PRODUCT_DEFINITION, PRICE_DEFINITIONS } from "./products";
3
4const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
5 apiVersion: "2026-02-25.clover",
6});
7
8async function sync() {
9 console.log("Syncing Stripe products and prices...");
10
11 // Find or create product
12 let product: Stripe.Product | undefined;
13 const existing = await stripe.products.search({
14 query: `metadata["product_def_id"]:"${PRODUCT_DEF_ID}"`,
15 });
16
17 if (existing.data.length > 0) {
18 product = existing.data[0];
19 console.log(`Found existing product: ${product.id}`);
20 // Update if name or metadata changed
21 product = await stripe.products.update(product.id, {
22 name: PRODUCT_DEFINITION.name,
23 metadata: PRODUCT_DEFINITION.metadata,
24 });
25 console.log(`Updated product: ${product.id}`);
26 } else {
27 product = await stripe.products.create({
28 name: PRODUCT_DEFINITION.name,
29 metadata: PRODUCT_DEFINITION.metadata,
30 });
31 console.log(`Created product: ${product.id}`);
32 }
33
34 // Sync prices by lookup_key
35 for (const [cadence, def] of Object.entries(PRICE_DEFINITIONS)) {
36 const existingPrices = await stripe.prices.list({
37 lookup_keys: [def.lookup_key],
38 });
39
40 if (existingPrices.data.length > 0) {
41 console.log(
42 `Price "${def.lookup_key}" already exists: ${existingPrices.data[0].id}`,
43 );
44 } else {
45 const price = await stripe.prices.create({
46 product: product.id,
47 unit_amount: def.unit_amount,
48 currency: def.currency,
49 recurring: def.recurring,
50 lookup_key: def.lookup_key,
51 });
52 console.log(`Created price "${def.lookup_key}": ${price.id}`);
53 }
54 }
55
56 console.log("Sync complete.");
57}
58
59sync().catch((err) => {
60 console.error("Sync failed:", err);
61 process.exit(1);
62});