From 8872d851ec70902d936a8bd33c5312ffcd1d2ad9 Mon Sep 17 00:00:00 2001 From: Kristina <7614497+khjrtbrg@users.noreply.github.com> Date: Wed, 18 Feb 2026 15:47:30 +0100 Subject: [PATCH] [web] rm unnecessary scripts (#31631) GitOrigin-RevId: c6388685bd9cd43d8d22e38f25db4ee579def808 --- .../stripe/add_user_id_to_stripe_customer.mjs | 255 ------------- .../stripe/bulk-update-migration-status.mjs | 358 ------------------ .../stripe/register-analytics-mapping.mjs | 244 ------------ ...admin_user_id_from_stripe_subscription.mjs | 227 ----------- 4 files changed, 1084 deletions(-) delete mode 100644 services/web/scripts/stripe/add_user_id_to_stripe_customer.mjs delete mode 100644 services/web/scripts/stripe/bulk-update-migration-status.mjs delete mode 100644 services/web/scripts/stripe/register-analytics-mapping.mjs delete mode 100644 services/web/scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs diff --git a/services/web/scripts/stripe/add_user_id_to_stripe_customer.mjs b/services/web/scripts/stripe/add_user_id_to_stripe_customer.mjs deleted file mode 100644 index f2327be9e2..0000000000 --- a/services/web/scripts/stripe/add_user_id_to_stripe_customer.mjs +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env node - -/** - * This script iterates through all Stripe subscriptions, checks if they have metadata adminUserId, - * and sets it to customer metadata "userId" if present. - * - * Usage: - * node scripts/stripe/add_user_id_to_stripe_customer.mjs --region=us [options] - * node scripts/stripe/add_user_id_to_stripe_customer.mjs --region=uk [options] - * - * Options: - * --region=us|uk Required. Stripe region to process (us or uk) - * --commit Actually perform the updates (default: dry-run mode) - * --verbose Enable verbose logging - * --limit=N Limit processing to N subscriptions (for testing) - * - * Examples: - * # Dry run for US region with verbose output - * node scripts/stripe/add_user_id_to_stripe_customer.mjs --region=us --verbose - * - * # Commit changes for UK region - * node scripts/stripe/add_user_id_to_stripe_customer.mjs --region=uk --commit - * - * # Test with limited subscriptions - * node scripts/stripe/add_user_id_to_stripe_customer.mjs --region=us --limit=10 --verbose - */ - -import minimist from 'minimist' -import { z } from '../../app/src/infrastructure/Validation.mjs' -import { scriptRunner } from '../lib/ScriptRunner.mjs' -import { - getRegionClient, - CUSTOMER_SEGMENT_MAPPING, -} from '../../modules/subscriptions/app/src/StripeClient.mjs' - -const paramsSchema = z.object({ - region: z.enum(['us', 'uk']), - commit: z.boolean().default(false), - verbose: z.boolean().default(false), - limit: z.number().int().min(1).optional(), -}) - -let processedCount = 0 -let updatedCount = 0 -let errorCount = 0 - -/** - * Sleep function to respect Stripe rate limits (100 requests per second) - * We'll be conservative and sleep for 50ms between requests to stay well under the limit - */ -async function rateLimitSleep() { - return new Promise(resolve => setTimeout(resolve, 50)) -} - -/** - * Process a single subscription and update customer metadata if needed - */ -async function processSubscription( - subscription, - stripeClient, - commit, - verbose -) { - try { - processedCount++ - - // Check if subscription has adminUserId metadata - const adminUserId = subscription.metadata?.adminUserId - - if (verbose) { - console.info( - `Processing subscription ${subscription.id} (customer: ${subscription.customer.id}) - adminUserId: ${adminUserId || 'none'}` - ) - } - - if (!adminUserId) { - // No adminUserId to migrate - return - } - - // Get customer details to check current metadata - const customer = subscription.customer - if (customer.deleted) { - if (verbose) { - console.info(`Customer ${customer.id} is deleted, skipping`) - } - return - } - - if (customer.metadata?.userId === adminUserId) { - if (verbose) { - console.info( - `Customer ${customer.id} already has userId=${adminUserId}, skipping` - ) - } - return - } - - if (customer.metadata?.userId && customer.metadata.userId !== adminUserId) { - console.warn( - `Customer ${customer.id} has existing userId=${customer.metadata.userId} which differs from adminUserId=${adminUserId}, skipping to avoid overwrite` - ) - return - } - - if (commit) { - // Update customer metadata using the StripeClient method - await stripeClient.updateCustomerMetadata(customer.id, { - ...customer.metadata, - userId: adminUserId, - segment: CUSTOMER_SEGMENT_MAPPING.B2C, - }) - - console.info( - `Updated customer ${customer.id} metadata: userId=${adminUserId}` - ) - } else { - console.info( - `DRY RUN: Would update customer ${customer.id} metadata: userId=${adminUserId}` - ) - } - - updatedCount++ - } catch (error) { - errorCount++ - console.log(error) - } - - // Respect rate limits - await rateLimitSleep() -} - -/** - * Main script function - */ -async function main(trackProgress) { - const parseResult = paramsSchema.safeParse( - minimist(process.argv.slice(2), { - boolean: ['commit', 'verbose'], - string: ['region'], - number: ['limit'], - }) - ) - - if (!parseResult.success) { - throw new Error(`Invalid parameters: ${parseResult.error.message}`) - } - - const { region, commit, verbose, limit } = parseResult.data - - const mode = commit ? 'COMMIT MODE' : 'DRY RUN MODE' - await trackProgress( - `Starting script in ${mode} for Stripe ${region.toUpperCase()} region` - ) - - if (limit) { - await trackProgress(`Processing limited to ${limit} subscriptions`) - } - - // Get Stripe client for the specified region - const stripeClient = getRegionClient(region) - - // Reset counters - processedCount = 0 - updatedCount = 0 - errorCount = 0 - - await trackProgress('Starting to iterate through Stripe subscriptions...') - - const listParams = { - limit: 100, // Stripe's maximum limit per request - expand: ['data.customer'], // Expand customer data to reduce additional API calls - status: 'all', // Include subscriptions in all statuses (active, past_due, unpaid, canceled, etc.) - } - - let hasMore = true - let startingAfter = null - let totalProcessed = 0 - - while (hasMore) { - const params = { ...listParams } - if (startingAfter) { - params.starting_after = startingAfter - } - - // Get batch of subscriptions - const subscriptions = await stripeClient.stripe.subscriptions.list(params) - - await trackProgress( - `Retrieved ${subscriptions.data.length} subscriptions (total processed so far: ${totalProcessed})` - ) - - // Process each subscription in the batch - for (const subscription of subscriptions.data) { - await processSubscription(subscription, stripeClient, commit, verbose) - - totalProcessed++ - - // Check if we've hit the limit - if (limit && totalProcessed >= limit) { - await trackProgress(`Reached limit of ${limit} subscriptions, stopping`) - hasMore = false - break - } - - // Progress update every 50 subscriptions - if (totalProcessed % 50 === 0) { - await trackProgress( - `Progress: ${totalProcessed} processed, ${updatedCount} customers updated, ${errorCount} errors` - ) - } - } - - // Check if there are more subscriptions to process - hasMore = hasMore && subscriptions.has_more - if (hasMore && subscriptions.data.length > 0) { - startingAfter = subscriptions.data[subscriptions.data.length - 1].id - } - - // Rate limit between batch requests - await rateLimitSleep() - } - - // Final summary - await trackProgress('FINAL SUMMARY:') - await trackProgress(` Total subscriptions processed: ${processedCount}`) - await trackProgress( - ` Customers ${commit ? 'updated' : 'would be updated'}: ${updatedCount}` - ) - await trackProgress(` Errors encountered: ${errorCount}`) - - if (!commit && updatedCount > 0) { - await trackProgress('') - await trackProgress( - 'To actually perform the updates, run the script with --commit flag' - ) - } - - if (errorCount > 0) { - await trackProgress( - 'Some errors were encountered. Check the logs above for details.' - ) - } - - await trackProgress(`Script completed successfully in ${mode}`) -} - -// Execute the script using the runner -try { - await scriptRunner(main) - process.exit(0) -} catch (error) { - console.error('Script failed:', error.message) - process.exit(1) -} diff --git a/services/web/scripts/stripe/bulk-update-migration-status.mjs b/services/web/scripts/stripe/bulk-update-migration-status.mjs deleted file mode 100644 index 08ed3c4c9e..0000000000 --- a/services/web/scripts/stripe/bulk-update-migration-status.mjs +++ /dev/null @@ -1,358 +0,0 @@ -#!/usr/bin/env node - -/** - * This script validates subscriptions that are canceled or expired and have migration metadata set to "in_progress", - * then updates the metadata to "cancelled" if validation passes. - * - * TODO: This script can be deleted after being run in production. - * - * Usage: - * node scripts/stripe/bulk-update-migration-status.mjs [OPTS] [INPUT-FILE] - * - * Options: - * --output PATH Output file path (default: /tmp/bulk_update_output_.csv) - * Use '-' to write to stdout - * --commit Apply changes (without this flag, runs in dry-run mode) - * --concurrency N Number of subscriptions to process concurrently (default: 10) - * --stripe-rate-limit N Requests per second for Stripe (default: 50) - * --stripe-api-retries N Number of retries on Stripe 429s (default: 5) - * --stripe-retry-delay-ms N Delay between Stripe retries in ms (default: 1000) - * --help Show a help message - * - * CSV Input Format: - * The CSV must have the following columns: - * - subscription_id: Stripe subscription id - * - target_stripe_account: Either 'stripe-uk' or 'stripe-us' - * - * Output: - * Writes a CSV with columns: - * - subscription_id: The subscription id processed - * - target_stripe_account: The Stripe account - * - status: Result status (validated, updated, invalid-status, invalid-metadata, or error) - * - note: Additional information about the status - */ - -import fs from 'node:fs' -import path from 'node:path' -import * as csv from 'csv' -import minimist from 'minimist' -import PQueue from 'p-queue' -import { z } from '../../app/src/infrastructure/Validation.mjs' -import { scriptRunner } from '../lib/ScriptRunner.mjs' -import { getRegionClient } from '../../modules/subscriptions/app/src/StripeClient.mjs' -import { ReportError } from './helpers.mjs' -import { - createRateLimitedApiWrappers, - DEFAULT_STRIPE_RATE_LIMIT, - DEFAULT_STRIPE_API_RETRIES, - DEFAULT_STRIPE_RETRY_DELAY_MS, -} from './RateLimiter.mjs' - -const DEFAULT_CONCURRENCY = 10 - -// rate limiters - initialized in main() -let rateLimiters - -function usage() { - console.error(`Usage: node scripts/stripe/bulk-update-migration-status.mjs [OPTS] [INPUT-FILE] - -Options: - --output PATH Output file path (default: /tmp/bulk_update_output_.csv) - Use '-' to write to stdout - --commit Apply changes (without this, runs in dry-run mode) - --concurrency N Number of subscriptions to process concurrently (default: ${DEFAULT_CONCURRENCY}) - --stripe-rate-limit N Requests per second for Stripe (default: ${DEFAULT_STRIPE_RATE_LIMIT}) - --stripe-api-retries N Number of retries on Stripe 429s (default: ${DEFAULT_STRIPE_API_RETRIES}) - --stripe-retry-delay-ms N Delay between Stripe retries in ms (default: ${DEFAULT_STRIPE_RETRY_DELAY_MS}) - --help Show this help message -`) -} - -async function main(trackProgress) { - const opts = parseArgs() - const timestamp = new Date().toISOString().replace(/[:.]/g, '-') - const outputFile = opts.output ?? `/tmp/bulk_update_output_${timestamp}.csv` - - // initialize rate limiters - rateLimiters = createRateLimitedApiWrappers({ - stripeRateLimit: opts.stripeRateLimit, - stripeApiRetries: opts.stripeApiRetries, - stripeRetryDelayMs: opts.stripeRetryDelayMs, - }) - - await trackProgress( - 'Starting bulk validation and update of subscription metadata' - ) - await trackProgress(`Run mode: ${opts.commit ? 'COMMIT' : 'DRY RUN'}`) - await trackProgress(`Rate limit: Stripe ${opts.stripeRateLimit}/s`) - await trackProgress(`Concurrency: ${opts.concurrency}`) - - const inputStream = opts.inputFile - ? fs.createReadStream(opts.inputFile) - : process.stdin - const csvReader = getCsvReader(inputStream) - const csvWriter = getCsvWriter(outputFile) - - await trackProgress(`Output: ${outputFile === '-' ? 'stdout' : outputFile}`) - - let processedCount = 0 - let successCount = 0 - let errorCount = 0 - - const queue = new PQueue({ concurrency: opts.concurrency }) - const maxQueueSize = opts.concurrency - - try { - for await (const input of csvReader) { - if (queue.size >= maxQueueSize) { - await queue.onSizeLessThan(maxQueueSize) - } - - queue.add(async () => { - try { - const result = await processValidation(input, opts.commit) - - csvWriter.write({ - subscription_id: input.subscription_id, - target_stripe_account: input.target_stripe_account, - status: result.status, - note: - result.note || - (opts.commit ? '' : 'dry run - no changes applied'), - }) - - if (result.status === 'updated' || result.status === 'validated') { - successCount++ - } else { - errorCount++ - } - } catch (err) { - errorCount++ - if (err instanceof ReportError) { - csvWriter.write({ - subscription_id: input.subscription_id, - target_stripe_account: input.target_stripe_account, - status: err.status, - note: err.message, - }) - } else { - csvWriter.write({ - subscription_id: input.subscription_id, - target_stripe_account: input.target_stripe_account, - status: 'error', - note: err.message, - }) - await trackProgress( - `Error processing ${input.subscription_id}: ${err.message}` - ) - } - } - - processedCount++ - if (processedCount % 10 === 0) { - await trackProgress( - `Processed ${processedCount} subscriptions (${successCount} ${opts.commit ? 'updated' : 'validated'}, ${errorCount} errors)` - ) - } - }) - } - } finally { - await queue.onIdle() - } - - await trackProgress(`✅ Total processed: ${processedCount}`) - if (opts.commit) { - await trackProgress(`✅ Successfully updated: ${successCount}`) - } else { - await trackProgress(`✅ Successfully validated: ${successCount}`) - await trackProgress('ℹ️ DRY RUN: No changes were applied') - } - await trackProgress(`❌ Errors: ${errorCount}`) - await trackProgress('🎉 Script completed!') - - csvWriter.end() -} - -function parseArgs() { - const args = minimist(process.argv.slice(2), { - string: [ - 'output', - 'concurrency', - 'stripe-rate-limit', - 'stripe-api-retries', - 'stripe-retry-delay-ms', - ], - boolean: ['commit', 'help'], - default: { - commit: false, - concurrency: DEFAULT_CONCURRENCY, - 'stripe-rate-limit': DEFAULT_STRIPE_RATE_LIMIT, - 'stripe-api-retries': DEFAULT_STRIPE_API_RETRIES, - 'stripe-retry-delay-ms': DEFAULT_STRIPE_RETRY_DELAY_MS, - }, - unknown: arg => { - if (arg.startsWith('-')) { - console.error(`Unknown option: ${arg}`) - usage() - process.exit(1) - } - return true - }, - }) - - if (args.help) { - usage() - process.exit(0) - } - - const inputFile = args._[0] - const paramsSchema = z.object({ - output: z.string().optional(), - commit: z.boolean(), - concurrency: z.number().int().positive(), - stripeRateLimit: z.number().positive(), - stripeApiRetries: z.number().int().nonnegative(), - stripeRetryDelayMs: z.number().int().nonnegative(), - inputFile: z.string().optional(), - }) - - try { - return paramsSchema.parse({ - output: args.output, - commit: args.commit, - concurrency: Number(args.concurrency), - stripeRateLimit: Number(args['stripe-rate-limit']), - stripeApiRetries: Number(args['stripe-api-retries']), - stripeRetryDelayMs: Number(args['stripe-retry-delay-ms']), - inputFile, - }) - } catch (err) { - console.error('Invalid arguments:', err.message) - usage() - process.exit(1) - } -} - -function getCsvReader(inputStream) { - const parser = csv.parse({ columns: true }) - inputStream.pipe(parser) - return parser -} - -function getCsvWriter(outputFile) { - if (outputFile === '-') { - const writer = csv.stringify({ - columns: ['subscription_id', 'target_stripe_account', 'status', 'note'], - header: true, - }) - writer.on('error', err => { - console.error(err) - process.exit(1) - }) - writer.pipe(process.stdout) - return writer - } - - fs.mkdirSync(path.dirname(outputFile), { recursive: true }) - const outputStream = fs.createWriteStream(outputFile) - - const writer = csv.stringify({ - columns: ['subscription_id', 'target_stripe_account', 'status', 'note'], - header: true, - }) - - writer.on('error', err => { - console.error(err) - process.exit(1) - }) - - writer.pipe(outputStream) - return writer -} - -async function processValidation(input, commit) { - const { - subscription_id: subscriptionId, - target_stripe_account: targetStripeAccount, - } = input - - // get Stripe client for the target account (strip 'stripe-' prefix if present) - const region = targetStripeAccount.replace(/^stripe-/, '') - const stripeClient = getRegionClient(region) - - // fetch subscription - let subscription - try { - subscription = await rateLimiters.requestWithRetries( - stripeClient.serviceName, - () => stripeClient.stripe.subscriptions.retrieve(subscriptionId), - { - operation: 'subscriptions.retrieve', - subscriptionId, - region: stripeClient.serviceName, - } - ) - } catch (err) { - throw new ReportError( - 'subscription-not-found', - `Subscription not found: ${err.message}` - ) - } - - const validStatuses = ['canceled'] - if (!validStatuses.includes(subscription.status)) { - throw new ReportError( - 'invalid-status', - `Subscription status is ${subscription.status}, expected canceled` - ) - } - - if ( - subscription.metadata?.recurly_to_stripe_migration_status !== 'in_progress' - ) { - throw new ReportError( - 'invalid-metadata', - `Migration status is ${subscription.metadata?.recurly_to_stripe_migration_status}, expected in_progress` - ) - } - - if (!commit) { - return { - status: 'validated', - note: 'Subscription is valid for update', - } - } - - try { - await rateLimiters.requestWithRetries( - stripeClient.serviceName, - () => - stripeClient.updateSubscriptionMetadata(subscriptionId, { - recurly_to_stripe_migration_status: 'cancelled', - }), - { - operation: 'updateSubscriptionMetadata', - subscriptionId, - region: stripeClient.serviceName, - } - ) - - return { - status: 'updated', - note: `Updated metadata for subscription ${subscriptionId}`, - } - } catch (err) { - throw new ReportError( - 'update-failed', - `Failed to update metadata: ${err.message}` - ) - } -} - -try { - await scriptRunner(main) - process.exit(0) -} catch (error) { - console.error(error) - process.exit(1) -} diff --git a/services/web/scripts/stripe/register-analytics-mapping.mjs b/services/web/scripts/stripe/register-analytics-mapping.mjs deleted file mode 100644 index 81a15c4343..0000000000 --- a/services/web/scripts/stripe/register-analytics-mapping.mjs +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env node - -/** - * This script registers analytics account mapping for subscriptions migrated to Stripe. - * - * // TODO: delete this when the migration is complete - * - * Usage: - * node scripts/stripe/register-analytics-mapping.mjs [OPTS] [INPUT-FILE] - * - * Options: - * --output PATH Output file path (default: /tmp/register_output_.csv) - * --commit Apply changes (without this, runs in dry-run mode) - * --help Show help message - * - * CSV Input Format: - * recurly_account_code,target_stripe_account,stripe_customer_id - * 507f1f77bcf86cd799439011,stripe-uk,cus_1234567890abcdef - * - * CSV Output Format: - * recurly_account_code,target_stripe_account,stripe_customer_id,status,note - */ - -import fs from 'node:fs' -import path from 'node:path' -import * as csv from 'csv' -import minimist from 'minimist' -import { z } from '../../app/src/infrastructure/Validation.mjs' -import { scriptRunner } from '../lib/ScriptRunner.mjs' -import { Subscription } from '../../app/src/models/Subscription.mjs' -import AnalyticsManager from '../../app/src/Features/Analytics/AnalyticsManager.mjs' -import AccountMappingHelper from '../../app/src/Features/Analytics/AccountMappingHelper.mjs' -import { ReportError } from './helpers.mjs' - -function usage() { - console.error(`Usage: node scripts/stripe/register-analytics-mapping.mjs [OPTS] [INPUT-FILE] - -Options: - --output PATH Output file path (default: /tmp/register_output_.csv) - --commit Apply changes (without this, runs in dry-run mode) - --help Show this help message -`) -} - -async function main(trackProgress) { - const opts = parseArgs() - const timestamp = new Date().toISOString().replace(/[:.]/g, '-') - const outputFile = opts.output ?? `/tmp/register_output_${timestamp}.csv` - - await trackProgress('Starting analytics mapping registration') - await trackProgress(`Run mode: ${opts.commit ? 'COMMIT' : 'DRY RUN'}`) - - const inputStream = opts.inputFile - ? fs.createReadStream(opts.inputFile) - : process.stdin - const csvReader = getCsvReader(inputStream) - const csvWriter = getCsvWriter(outputFile) - - await trackProgress(`Output: ${outputFile}`) - - let processedCount = 0 - let successCount = 0 - let errorCount = 0 - - for await (const input of csvReader) { - processedCount++ - - try { - const result = await processRow(input, opts.commit) - - csvWriter.write({ - recurly_account_code: input.recurly_account_code, - target_stripe_account: input.target_stripe_account, - stripe_customer_id: input.stripe_customer_id, - status: result.status, - note: result.note, - }) - - if (result.status === 'registered' || result.status === 'dry-run') { - successCount++ - } else { - errorCount++ - } - - if (processedCount % 25 === 0) { - await trackProgress( - `Progress: ${processedCount} processed, ${successCount} successful, ${errorCount} errors` - ) - } - } catch (err) { - errorCount++ - if (err instanceof ReportError) { - csvWriter.write({ - recurly_account_code: input.recurly_account_code, - target_stripe_account: input.target_stripe_account, - stripe_customer_id: input.stripe_customer_id, - status: err.status, - note: err.message, - }) - } else { - csvWriter.write({ - recurly_account_code: input.recurly_account_code, - target_stripe_account: input.target_stripe_account, - stripe_customer_id: input.stripe_customer_id, - status: 'error', - note: err.message, - }) - } - } - } - - await trackProgress(`✅ Total processed: ${processedCount}`) - if (opts.commit) { - await trackProgress(`✅ Successfully registered: ${successCount}`) - } else { - await trackProgress(`✅ Successfully validated: ${successCount}`) - await trackProgress('ℹ️ DRY RUN: No changes were applied') - } - await trackProgress(`❌ Errors: ${errorCount}`) - await trackProgress('🎉 Script completed!') - - csvWriter.end() -} - -function getCsvReader(inputStream) { - const parser = csv.parse({ columns: true }) - inputStream.pipe(parser) - return parser -} - -function getCsvWriter(outputFile) { - fs.mkdirSync(path.dirname(outputFile), { recursive: true }) - const outputStream = fs.createWriteStream(outputFile) - - const writer = csv.stringify({ - columns: [ - 'recurly_account_code', - 'target_stripe_account', - 'stripe_customer_id', - 'status', - 'note', - ], - header: true, - }) - - writer.on('error', err => { - console.error(err) - process.exit(1) - }) - - writer.pipe(outputStream) - return writer -} - -async function processRow(input, commit) { - const { - recurly_account_code: accountCode, - target_stripe_account: targetStripeAccount, - } = input - - // 1. Fetch Mongo subscription - const mongoSubscription = await Subscription.findOne({ - admin_id: accountCode, - }).exec() - if (!mongoSubscription) { - throw new ReportError( - 'no-mongo-subscription', - 'No subscription found in Mongo' - ) - } - - // 2. Check if migrated to Stripe - if (!mongoSubscription.paymentProvider?.service?.includes('stripe')) { - throw new ReportError('not-stripe', 'Subscription not using Stripe') - } - - const subscriptionId = mongoSubscription.paymentProvider.subscriptionId - if (!subscriptionId) { - throw new ReportError( - 'no-subscription-id', - 'No Stripe subscription ID in Mongo' - ) - } - - // 3. Register analytics mapping - if (commit) { - AnalyticsManager.registerAccountMapping( - AccountMappingHelper.generateSubscriptionToStripeMapping( - mongoSubscription._id, - subscriptionId, - targetStripeAccount - ) - ) - return { - status: 'registered', - note: 'Analytics mapping registered', - } - } else { - return { - status: 'dry-run', - note: 'DRY RUN: Would register analytics mapping', - } - } -} - -function parseArgs() { - const args = minimist(process.argv.slice(2), { - string: ['output'], - boolean: ['commit', 'help'], - default: { commit: false }, - }) - - if (args.help) { - usage() - process.exit(0) - } - - const inputFile = args._[0] - const paramsSchema = z.object({ - output: z.string().optional(), - commit: z.boolean(), - inputFile: z.string().optional(), - }) - - try { - return paramsSchema.parse({ - output: args.output, - commit: args.commit, - inputFile, - }) - } catch (err) { - console.error('Invalid arguments:', err.message) - usage() - process.exit(1) - } -} - -try { - await scriptRunner(main) - process.exit(0) -} catch (error) { - console.error(error) - process.exit(1) -} diff --git a/services/web/scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs b/services/web/scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs deleted file mode 100644 index 4e0cbf54d8..0000000000 --- a/services/web/scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env node - -/** - * This script iterates through all Stripe subscriptions and removes the adminUserId metadata - * from subscription objects that have it. - * - * Usage: - * node scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs --region=us [options] - * node scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs --region=uk [options] - * - * Options: - * --region=us|uk Required. Stripe region to process (us or uk) - * --commit Actually perform the updates (default: dry-run mode) - * --verbose Enable verbose logging - * --limit=N Limit processing to N subscriptions (for testing) - * - * Examples: - * # Dry run for US region with verbose output - * node scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs --region=us --verbose - * - * # Commit changes for UK region - * node scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs --region=uk --commit - * - * # Test with limited subscriptions - * node scripts/stripe/remove_admin_user_id_from_stripe_subscription.mjs --region=us --limit=10 --verbose - */ - -import minimist from 'minimist' -import { z } from '../../app/src/infrastructure/Validation.mjs' -import { scriptRunner } from '../lib/ScriptRunner.mjs' -import { getRegionClient } from '../../modules/subscriptions/app/src/StripeClient.mjs' - -const paramsSchema = z.object({ - region: z.enum(['us', 'uk']), - commit: z.boolean().default(false), - verbose: z.boolean().default(false), - limit: z.number().int().min(1).optional(), -}) - -let processedCount = 0 -let updatedCount = 0 -let errorCount = 0 - -/** - * Sleep function to respect Stripe rate limits (100 requests per second) - * We'll be conservative and sleep for 50ms between requests to stay well under the limit - */ -async function rateLimitSleep() { - return new Promise(resolve => setTimeout(resolve, 50)) -} - -/** - * Process a single subscription and remove adminUserId metadata if present - */ -async function processSubscription( - subscription, - stripeClient, - commit, - verbose -) { - try { - processedCount++ - - // Check if subscription has adminUserId metadata - const adminUserId = subscription.metadata?.adminUserId - - if (verbose) { - console.info( - `Processing subscription ${subscription.id} - adminUserId: ${adminUserId || 'none'}` - ) - } - - if (!adminUserId) { - // No adminUserId to remove - return - } - - if (commit) { - // Create a new metadata object that will remove adminUserId - const updatedMetadata = { ...subscription.metadata } - updatedMetadata.adminUserId = '' - - // Update subscription metadata using Stripe API directly - await stripeClient.stripe.subscriptions.update(subscription.id, { - metadata: updatedMetadata, - }) - - console.info( - `Removed adminUserId metadata from subscription ${subscription.id}` - ) - } else { - console.info( - `DRY RUN: Would remove adminUserId metadata from subscription ${subscription.id}` - ) - } - - updatedCount++ - } catch (error) { - errorCount++ - console.log(error) - } - - // Respect rate limits - await rateLimitSleep() -} - -/** - * Main script function - */ -async function main(trackProgress) { - const parseResult = paramsSchema.safeParse( - minimist(process.argv.slice(2), { - boolean: ['commit', 'verbose'], - string: ['region'], - number: ['limit'], - }) - ) - - if (!parseResult.success) { - throw new Error(`Invalid parameters: ${parseResult.error.message}`) - } - - const { region, commit, verbose, limit } = parseResult.data - - const mode = commit ? 'COMMIT MODE' : 'DRY RUN MODE' - await trackProgress( - `Starting script in ${mode} for Stripe ${region.toUpperCase()} region` - ) - - if (limit) { - await trackProgress(`Processing limited to ${limit} subscriptions`) - } - - // Get Stripe client for the specified region - const stripeClient = getRegionClient(region) - - // Reset counters - processedCount = 0 - updatedCount = 0 - errorCount = 0 - - await trackProgress('Starting to iterate through Stripe subscriptions...') - - const listParams = { - limit: 100, // Stripe's maximum limit per request - } - - let hasMore = true - let startingAfter = null - let totalProcessed = 0 - - while (hasMore) { - const params = { ...listParams } - if (startingAfter) { - params.starting_after = startingAfter - } - - // Get batch of subscriptions - const subscriptions = await stripeClient.stripe.subscriptions.list(params) - - await trackProgress( - `Retrieved ${subscriptions.data.length} subscriptions (total processed so far: ${totalProcessed})` - ) - - // Process each subscription in the batch - for (const subscription of subscriptions.data) { - await processSubscription(subscription, stripeClient, commit, verbose) - - totalProcessed++ - - // Check if we've hit the limit - if (limit && totalProcessed >= limit) { - await trackProgress(`Reached limit of ${limit} subscriptions, stopping`) - hasMore = false - break - } - - // Progress update every 50 subscriptions - if (totalProcessed % 50 === 0) { - await trackProgress( - `Progress: ${totalProcessed} processed, ${updatedCount} subscriptions updated, ${errorCount} errors` - ) - } - } - - // Check if there are more subscriptions to process - hasMore = hasMore && subscriptions.has_more - if (hasMore && subscriptions.data.length > 0) { - startingAfter = subscriptions.data[subscriptions.data.length - 1].id - } - - // Rate limit between batch requests - await rateLimitSleep() - } - - // Final summary - await trackProgress('FINAL SUMMARY:') - await trackProgress(` Total subscriptions processed: ${processedCount}`) - await trackProgress( - ` Subscriptions ${commit ? 'updated' : 'would be updated'}: ${updatedCount}` - ) - await trackProgress(` Errors encountered: ${errorCount}`) - - if (!commit && updatedCount > 0) { - await trackProgress('') - await trackProgress( - 'To actually perform the updates, run the script with --commit flag' - ) - } - - if (errorCount > 0) { - await trackProgress( - 'Some errors were encountered. Check the logs above for details.' - ) - } - - await trackProgress(`Script completed successfully in ${mode}`) -} - -// Execute the script using the runner -try { - await scriptRunner(main) - process.exit(0) -} catch (error) { - console.error('Script failed:', error.message) - process.exit(1) -}