Просмотр исходного кода

Merge pull request #29826 from overleaf/oa-coupon-scripts

[web] Coupon scripts

GitOrigin-RevId: 9d98b65649326ebad7a41c55ebce009327c9c634
Olzhas Askar 8 месяцев назад
Родитель
Сommit
9ebab12049
1 измененных файлов с 115 добавлено и 0 удалено
  1. 115 0
      services/web/scripts/stripe/create_coupons.mjs

+ 115 - 0
services/web/scripts/stripe/create_coupons.mjs

@@ -0,0 +1,115 @@
+#!/usr/bin/env node
+
+import minimist from 'minimist'
+import { scriptRunner } from '../lib/ScriptRunner.mjs'
+import { getRegionClient } from '../../modules/subscriptions/app/src/StripeClient.mjs'
+// eslint-disable-next-line import/no-unresolved
+import * as csv from 'csv/sync'
+import { readFile } from 'node:fs/promises'
+
+/**
+ * This script creates Stripe coupons and promotion codes from a CSV file.
+ *
+ * Usage:
+ *   node scripts/stripe/create_coupons.mjs --region=us INPUT.CSV
+ *
+ * Options:
+ *   --region=us|uk     Required. Stripe region to process (us or uk)
+ *
+ * CSV Format:
+ * id,name,percent_off,duration,code,max_redemptions
+ */
+
+async function main(trackProgress) {
+  const args = minimist(process.argv.slice(2), {
+    string: ['region'],
+  })
+
+  const inputCSV = args._[0]
+  const region = args.region
+
+  await trackProgress(
+    `Starting script for Stripe ${region.toUpperCase()} region`
+  )
+
+  const file = await readFile(inputCSV, { encoding: 'utf8' })
+  const couponsPlannedToCreate = csv.parse(file, { columns: true })
+  await trackProgress(
+    `Successfully parsed "${inputCSV}" CSV file with ${couponsPlannedToCreate.length} coupons to create`
+  )
+
+  const client = getRegionClient(region)
+
+  const existingCoupons = await client.stripe.coupons.list({ limit: 100 })
+  const existingIdsAndNames = existingCoupons.data.map(ec => ({
+    id: ec.id,
+    name: ec.name,
+  }))
+  await trackProgress(
+    `Successfully parsed ${existingIdsAndNames.length} existing coupons for verification`
+  )
+
+  const couponsToCreate = couponsPlannedToCreate.filter(
+    c => !existingIdsAndNames.some(e => e.id === c.id || e.name === c.name)
+  )
+  if (couponsToCreate.length === 0) {
+    await trackProgress(`There are no coupons to create`)
+  } else if (couponsToCreate.length < couponsPlannedToCreate.length) {
+    const filteredOut = couponsPlannedToCreate
+      .filter(c =>
+        existingIdsAndNames.some(e => e.id === c.id || e.name === c.name)
+      )
+      .map(c => c.name)
+    await trackProgress(
+      `Successfully filtered out: ${filteredOut.join(', ')} existing coupons from the ones to create`
+    )
+  }
+
+  const errors = []
+  for (const toCreate of couponsToCreate) {
+    try {
+      const createdCoupon = await client.stripe.coupons.create({
+        id: toCreate.id,
+        name: toCreate.name,
+        percent_off: parseFloat(toCreate.percent_off),
+        duration: toCreate.duration,
+      })
+
+      const promotionPayload = {
+        coupon: createdCoupon.id,
+        code: toCreate.code,
+      }
+      const maxRedemptions = parseInt(toCreate.max_redemptions, 10)
+      if (maxRedemptions > 0) {
+        promotionPayload.max_redemptions = maxRedemptions
+      }
+
+      await client.stripe.promotionCodes.create(promotionPayload)
+    } catch (error) {
+      await trackProgress(
+        `Failed to create coupon "${toCreate.name}" (${toCreate.id})`,
+        error.message
+      )
+      errors.push(toCreate.name)
+    }
+  }
+
+  if (errors.length > 0) {
+    await trackProgress(
+      `Could not create the following coupons: ${errors.join(', ')}`
+    )
+  } else {
+    await trackProgress(
+      `Successfully created ${couponsToCreate.length} coupon(s) and promotion code(s).`
+    )
+  }
+}
+
+// Execute the script using the runner
+try {
+  await scriptRunner(main)
+  process.exit(0)
+} catch (error) {
+  console.error('Script failed:', error.message)
+  process.exit(1)
+}