create_coupons.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env node
  2. import minimist from 'minimist'
  3. import { scriptRunner } from '../lib/ScriptRunner.mjs'
  4. import { getRegionClient } from '../../modules/subscriptions/app/src/StripeClient.mjs'
  5. // eslint-disable-next-line import/no-unresolved
  6. import * as csv from 'csv/sync'
  7. import { readFile } from 'node:fs/promises'
  8. /**
  9. * This script creates Stripe coupons and promotion codes from a CSV file.
  10. *
  11. * Usage:
  12. * node scripts/stripe/create_coupons.mjs --region=us INPUT.CSV
  13. *
  14. * Options:
  15. * --region=us|uk Required. Stripe region to process (us or uk)
  16. *
  17. * CSV Format:
  18. * id,name,percent_off,duration,code,max_redemptions
  19. */
  20. async function main(trackProgress) {
  21. const args = minimist(process.argv.slice(2), {
  22. string: ['region'],
  23. })
  24. const inputCSV = args._[0]
  25. const region = args.region
  26. await trackProgress(
  27. `Starting script for Stripe ${region.toUpperCase()} region`
  28. )
  29. const file = await readFile(inputCSV, { encoding: 'utf8' })
  30. const couponsPlannedToCreate = csv.parse(file, { columns: true })
  31. await trackProgress(
  32. `Successfully parsed "${inputCSV}" CSV file with ${couponsPlannedToCreate.length} coupons to create`
  33. )
  34. const client = getRegionClient(region)
  35. const existingCoupons = await client.stripe.coupons.list({ limit: 100 })
  36. const existingIdsAndNames = existingCoupons.data.map(ec => ({
  37. id: ec.id,
  38. name: ec.name,
  39. }))
  40. await trackProgress(
  41. `Successfully parsed ${existingIdsAndNames.length} existing coupons for verification`
  42. )
  43. const couponsToCreate = couponsPlannedToCreate.filter(
  44. c => !existingIdsAndNames.some(e => e.id === c.id || e.name === c.name)
  45. )
  46. if (couponsToCreate.length === 0) {
  47. await trackProgress(`There are no coupons to create`)
  48. } else if (couponsToCreate.length < couponsPlannedToCreate.length) {
  49. const filteredOut = couponsPlannedToCreate
  50. .filter(c =>
  51. existingIdsAndNames.some(e => e.id === c.id || e.name === c.name)
  52. )
  53. .map(c => c.name)
  54. await trackProgress(
  55. `Successfully filtered out: ${filteredOut.join(', ')} existing coupons from the ones to create`
  56. )
  57. }
  58. const errors = []
  59. for (const toCreate of couponsToCreate) {
  60. try {
  61. const createdCoupon = await client.stripe.coupons.create({
  62. id: toCreate.id,
  63. name: toCreate.name,
  64. percent_off: parseFloat(toCreate.percent_off),
  65. duration: toCreate.duration,
  66. })
  67. const promotionPayload = {
  68. coupon: createdCoupon.id,
  69. code: toCreate.code,
  70. }
  71. const maxRedemptions = parseInt(toCreate.max_redemptions, 10)
  72. if (maxRedemptions > 0) {
  73. promotionPayload.max_redemptions = maxRedemptions
  74. }
  75. await client.stripe.promotionCodes.create(promotionPayload)
  76. } catch (error) {
  77. await trackProgress(
  78. `Failed to create coupon "${toCreate.name}" (${toCreate.id})`,
  79. error.message
  80. )
  81. errors.push(toCreate.name)
  82. }
  83. }
  84. if (errors.length > 0) {
  85. await trackProgress(
  86. `Could not create the following coupons: ${errors.join(', ')}`
  87. )
  88. } else {
  89. await trackProgress(
  90. `Successfully created ${couponsToCreate.length} coupon(s) and promotion code(s).`
  91. )
  92. }
  93. }
  94. // Execute the script using the runner
  95. try {
  96. await scriptRunner(main)
  97. process.exit(0)
  98. } catch (error) {
  99. console.error('Script failed:', error.message)
  100. process.exit(1)
  101. }