create_coupons.mjs 3.7 KB

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