paymongo-cli 1.4.4 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/.github/copilot-instructions.md +95 -95
  2. package/CHANGELOG.md +85 -1
  3. package/LICENSE +20 -20
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/commands/config.js +30 -15
  6. package/dist/commands/dev/logs.js +3 -3
  7. package/dist/commands/dev/status.js +2 -2
  8. package/dist/commands/dev/stop.js +3 -3
  9. package/dist/commands/dev.js +9 -8
  10. package/dist/commands/env.js +6 -6
  11. package/dist/commands/generate/templates/checkout-page/index.js +520 -520
  12. package/dist/commands/generate/templates/payment-intent/javascript.js +68 -68
  13. package/dist/commands/generate/templates/payment-intent/typescript.js +92 -92
  14. package/dist/commands/generate/templates/webhook-handler/javascript.js +192 -147
  15. package/dist/commands/generate/templates/webhook-handler/typescript.js +147 -117
  16. package/dist/commands/generate.js +43 -37
  17. package/dist/commands/init.js +25 -8
  18. package/dist/commands/login.js +56 -19
  19. package/dist/commands/payments.js +9 -8
  20. package/dist/commands/team/index.js +11 -9
  21. package/dist/commands/trigger.js +58 -18
  22. package/dist/commands/webhooks.js +8 -7
  23. package/dist/index.js +9 -2
  24. package/dist/services/analytics/service.js +24 -19
  25. package/dist/services/api/client.js +16 -16
  26. package/dist/services/config/manager.js +6 -8
  27. package/dist/services/dev/process-manager.js +30 -32
  28. package/dist/services/dev/server.js +45 -39
  29. package/dist/services/team/service.js +4 -1
  30. package/dist/types/schemas.js +38 -9
  31. package/dist/utils/bulk.js +36 -4
  32. package/dist/utils/constants.js +11 -1
  33. package/dist/utils/errors.js +6 -0
  34. package/dist/utils/validator.js +10 -9
  35. package/dist/utils/webhook-store.js +18 -15
  36. package/eslint.config.ts +70 -70
  37. package/package.json +2 -2
  38. package/coverage/base.css +0 -224
  39. package/coverage/block-navigation.js +0 -87
  40. package/coverage/favicon.png +0 -0
  41. package/coverage/index.html +0 -281
  42. package/coverage/lcov-report/base.css +0 -224
  43. package/coverage/lcov-report/block-navigation.js +0 -87
  44. package/coverage/lcov-report/favicon.png +0 -0
  45. package/coverage/lcov-report/index.html +0 -281
  46. package/coverage/lcov-report/prettify.css +0 -1
  47. package/coverage/lcov-report/prettify.js +0 -2
  48. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  49. package/coverage/lcov-report/sorter.js +0 -210
  50. package/coverage/lcov.info +0 -5053
  51. package/coverage/prettify.css +0 -1
  52. package/coverage/prettify.js +0 -2
  53. package/coverage/sort-arrow-sprite.png +0 -0
  54. package/coverage/sorter.js +0 -210
  55. package/dist/commands/config.d.ts +0 -21
  56. package/dist/commands/config.d.ts.map +0 -1
  57. package/dist/commands/config.js.map +0 -1
  58. package/dist/commands/dev.d.ts +0 -16
  59. package/dist/commands/dev.d.ts.map +0 -1
  60. package/dist/commands/dev.js.map +0 -1
  61. package/dist/commands/env.d.ts +0 -4
  62. package/dist/commands/env.d.ts.map +0 -1
  63. package/dist/commands/env.js.map +0 -1
  64. package/dist/commands/init.d.ts +0 -15
  65. package/dist/commands/init.d.ts.map +0 -1
  66. package/dist/commands/init.js.map +0 -1
  67. package/dist/commands/login.d.ts +0 -20
  68. package/dist/commands/login.d.ts.map +0 -1
  69. package/dist/commands/login.js.map +0 -1
  70. package/dist/commands/payments.d.ts +0 -41
  71. package/dist/commands/payments.d.ts.map +0 -1
  72. package/dist/commands/payments.js.map +0 -1
  73. package/dist/commands/team/index.d.ts +0 -4
  74. package/dist/commands/team/index.d.ts.map +0 -1
  75. package/dist/commands/team/index.js.map +0 -1
  76. package/dist/commands/trigger.d.ts +0 -4
  77. package/dist/commands/trigger.d.ts.map +0 -1
  78. package/dist/commands/trigger.js.map +0 -1
  79. package/dist/commands/webhooks.d.ts +0 -23
  80. package/dist/commands/webhooks.d.ts.map +0 -1
  81. package/dist/commands/webhooks.js.map +0 -1
  82. package/dist/index.d.ts +0 -3
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/index.js.map +0 -1
  85. package/dist/services/analytics/service.d.ts +0 -35
  86. package/dist/services/analytics/service.d.ts.map +0 -1
  87. package/dist/services/analytics/service.js.map +0 -1
  88. package/dist/services/api/client.d.ts +0 -26
  89. package/dist/services/api/client.d.ts.map +0 -1
  90. package/dist/services/api/client.js.map +0 -1
  91. package/dist/services/api/rate-limiter.d.ts +0 -64
  92. package/dist/services/api/rate-limiter.d.ts.map +0 -1
  93. package/dist/services/api/rate-limiter.js.map +0 -1
  94. package/dist/services/api/undici-client.d.ts +0 -39
  95. package/dist/services/api/undici-client.d.ts.map +0 -1
  96. package/dist/services/api/undici-client.js +0 -288
  97. package/dist/services/api/undici-client.js.map +0 -1
  98. package/dist/services/config/manager.d.ts +0 -16
  99. package/dist/services/config/manager.d.ts.map +0 -1
  100. package/dist/services/config/manager.js.map +0 -1
  101. package/dist/services/dev/process-manager.d.ts +0 -50
  102. package/dist/services/dev/process-manager.d.ts.map +0 -1
  103. package/dist/services/dev/process-manager.js.map +0 -1
  104. package/dist/services/github/auth.d.ts +0 -15
  105. package/dist/services/github/auth.d.ts.map +0 -1
  106. package/dist/services/github/auth.js +0 -79
  107. package/dist/services/github/auth.js.map +0 -1
  108. package/dist/services/github/client.d.ts +0 -95
  109. package/dist/services/github/client.d.ts.map +0 -1
  110. package/dist/services/github/client.js +0 -130
  111. package/dist/services/github/client.js.map +0 -1
  112. package/dist/services/github/sync.d.ts +0 -26
  113. package/dist/services/github/sync.d.ts.map +0 -1
  114. package/dist/services/github/sync.js +0 -203
  115. package/dist/services/github/sync.js.map +0 -1
  116. package/dist/services/payments/simulator.d.ts +0 -28
  117. package/dist/services/payments/simulator.d.ts.map +0 -1
  118. package/dist/services/payments/simulator.js.map +0 -1
  119. package/dist/services/team/service.d.ts +0 -44
  120. package/dist/services/team/service.d.ts.map +0 -1
  121. package/dist/services/team/service.js.map +0 -1
  122. package/dist/services/web/server.d.ts +0 -31
  123. package/dist/services/web/server.d.ts.map +0 -1
  124. package/dist/services/web/server.js +0 -206
  125. package/dist/services/web/server.js.map +0 -1
  126. package/dist/types/paymongo.d.ts +0 -204
  127. package/dist/types/paymongo.d.ts.map +0 -1
  128. package/dist/types/paymongo.js.map +0 -1
  129. package/dist/types/schemas.d.ts +0 -80
  130. package/dist/types/schemas.d.ts.map +0 -1
  131. package/dist/types/schemas.js.map +0 -1
  132. package/dist/utils/bulk.d.ts +0 -62
  133. package/dist/utils/bulk.d.ts.map +0 -1
  134. package/dist/utils/bulk.js.map +0 -1
  135. package/dist/utils/cache.d.ts +0 -22
  136. package/dist/utils/cache.d.ts.map +0 -1
  137. package/dist/utils/cache.js.map +0 -1
  138. package/dist/utils/constants.d.ts +0 -32
  139. package/dist/utils/constants.d.ts.map +0 -1
  140. package/dist/utils/constants.js.map +0 -1
  141. package/dist/utils/errors.d.ts +0 -34
  142. package/dist/utils/errors.d.ts.map +0 -1
  143. package/dist/utils/errors.js.map +0 -1
  144. package/dist/utils/logger.d.ts +0 -20
  145. package/dist/utils/logger.d.ts.map +0 -1
  146. package/dist/utils/logger.js.map +0 -1
  147. package/dist/utils/spinner.d.ts +0 -17
  148. package/dist/utils/spinner.d.ts.map +0 -1
  149. package/dist/utils/spinner.js.map +0 -1
  150. package/dist/utils/validator.d.ts +0 -10
  151. package/dist/utils/validator.d.ts.map +0 -1
  152. package/dist/utils/validator.js.map +0 -1
  153. package/dist/utils/webhook-store.d.ts +0 -22
  154. package/dist/utils/webhook-store.d.ts.map +0 -1
  155. package/dist/utils/webhook-store.js.map +0 -1
@@ -1,5 +1,4 @@
1
1
  import { Command } from 'commander';
2
- import { input } from '@inquirer/prompts';
3
2
  import chalk from 'chalk';
4
3
  import fs from 'fs/promises';
5
4
  import ConfigManager from '../services/config/manager.js';
@@ -8,12 +7,12 @@ import { getJavaScriptWebhookHandler, getTypeScriptWebhookHandler, getJavaScript
8
7
  const command = new Command('generate');
9
8
  command
10
9
  .description('Generate boilerplate code for PayMongo integrations')
11
- .addHelpText('after', `
12
- EXAMPLES
13
- $ paymongo generate webhook-handler --events payment.paid,payment.failed
14
- $ paymongo generate webhook-handler --language typescript --framework express
15
- $ paymongo generate payment-intent --methods card,gcash --language typescript
16
- $ paymongo generate checkout-page --framework react --output Checkout.jsx
10
+ .addHelpText('after', `
11
+ EXAMPLES
12
+ $ paymongo generate webhook-handler --events payment.paid,payment.failed
13
+ $ paymongo generate webhook-handler --language typescript --framework express
14
+ $ paymongo generate payment-intent --methods card,gcash --language typescript
15
+ $ paymongo generate checkout-page --language react --output Checkout.jsx
17
16
  `)
18
17
  .addCommand(new Command('webhook-handler')
19
18
  .description('Generate a webhook handler for specific events')
@@ -21,17 +20,18 @@ EXAMPLES
21
20
  .option('-l, --language <language>', 'Programming language (javascript, typescript)', 'javascript')
22
21
  .option('-f, --framework <framework>', 'Framework (express, fastify, hapi)', 'express')
23
22
  .option('-o, --output <file>', 'Output file path')
24
- .addHelpText('after', `
25
- SUPPORTED EVENTS:
26
- payment.paid, payment.failed, payment.refunded, payment.expired
27
- source.chargeable, source.failed, source.cancelled
28
- checkout.session.succeeded, checkout.session.cancelled
29
-
30
- EXAMPLES:
31
- $ paymongo generate webhook-handler
32
- $ paymongo generate webhook-handler --events payment.paid,payment.failed
33
- $ paymongo generate webhook-handler --language typescript --framework fastify
34
- $ paymongo generate webhook-handler --output my-webhook.js
23
+ .addHelpText('after', `
24
+ SUPPORTED EVENTS:
25
+ payment.paid, payment.failed, payment.refunded
26
+ source.chargeable
27
+ checkout_session.payment.paid
28
+ qrph.expired
29
+
30
+ EXAMPLES:
31
+ $ paymongo generate webhook-handler
32
+ $ paymongo generate webhook-handler --events payment.paid,payment.failed
33
+ $ paymongo generate webhook-handler --language typescript --framework fastify
34
+ $ paymongo generate webhook-handler --output my-webhook.js
35
35
  `)
36
36
  .action(async (options) => {
37
37
  await generateWebhookHandler(options);
@@ -41,14 +41,14 @@ EXAMPLES:
41
41
  .option('-l, --language <language>', 'Programming language (javascript, typescript)', 'javascript')
42
42
  .option('-m, --methods <methods>', 'Payment methods (card,gcash,paymaya,grab_pay,qrph)')
43
43
  .option('-o, --output <file>', 'Output file path')
44
- .addHelpText('after', `
45
- PAYMENT METHODS:
46
- card, gcash, paymaya, grab_pay, qrph
47
-
48
- EXAMPLES:
49
- $ paymongo generate payment-intent
50
- $ paymongo generate payment-intent --methods card,gcash
51
- $ paymongo generate payment-intent --language typescript --output create-payment.js
44
+ .addHelpText('after', `
45
+ PAYMENT METHODS:
46
+ card, gcash, paymaya, grab_pay, qrph
47
+
48
+ EXAMPLES:
49
+ $ paymongo generate payment-intent
50
+ $ paymongo generate payment-intent --methods card,gcash
51
+ $ paymongo generate payment-intent --language typescript --output create-payment.js
52
52
  `)
53
53
  .action(async (options) => {
54
54
  await generatePaymentIntent(options);
@@ -57,14 +57,14 @@ EXAMPLES:
57
57
  .description('Generate a basic checkout page with PayMongo integration')
58
58
  .option('-l, --language <language>', 'Frontend language/framework (html, react, vue)', 'html')
59
59
  .option('-o, --output <file>', 'Output file path')
60
- .addHelpText('after', `
61
- FRAMEWORKS:
62
- html (vanilla HTML/JS), react, vue
63
-
64
- EXAMPLES:
65
- $ paymongo generate checkout-page
66
- $ paymongo generate checkout-page --framework react
67
- $ paymongo generate checkout-page --language vue --output Checkout.vue
60
+ .addHelpText('after', `
61
+ FRAMEWORKS:
62
+ html (vanilla HTML/JS), react, vue
63
+
64
+ EXAMPLES:
65
+ $ paymongo generate checkout-page
66
+ $ paymongo generate checkout-page --framework react
67
+ $ paymongo generate checkout-page --language vue --output Checkout.vue
68
68
  `)
69
69
  .action(async (options) => {
70
70
  await generateCheckoutPage(options);
@@ -83,6 +83,7 @@ async function generateWebhookHandler(options) {
83
83
  }
84
84
  spinner.succeed('Configuration loaded');
85
85
  let events = [];
86
+ const { input } = await import('@inquirer/prompts');
86
87
  if (options.events) {
87
88
  events = options.events.split(',').map(e => e.trim());
88
89
  }
@@ -94,9 +95,12 @@ async function generateWebhookHandler(options) {
94
95
  events = eventInput.split(',').map(e => e.trim());
95
96
  }
96
97
  const validEvents = [
97
- 'payment.paid', 'payment.failed', 'payment.refunded', 'payment.expired',
98
- 'source.chargeable', 'source.failed', 'source.cancelled',
99
- 'checkout.session.succeeded', 'checkout.session.cancelled'
98
+ 'payment.paid',
99
+ 'payment.failed',
100
+ 'payment.refunded',
101
+ 'source.chargeable',
102
+ 'checkout_session.payment.paid',
103
+ 'qrph.expired',
100
104
  ];
101
105
  const invalidEvents = events.filter(e => !validEvents.includes(e));
102
106
  if (invalidEvents.length > 0) {
@@ -146,6 +150,7 @@ async function generatePaymentIntent(options) {
146
150
  else {
147
151
  code = getJavaScriptPaymentIntent(methods);
148
152
  }
153
+ const { input } = await import('@inquirer/prompts');
149
154
  let outputFile = options.output;
150
155
  if (!outputFile) {
151
156
  const defaultName = `create-payment-intent.${options.language === 'typescript' ? 'ts' : 'js'}`;
@@ -170,6 +175,7 @@ async function generateCheckoutPage(options) {
170
175
  const spinner = new Spinner();
171
176
  try {
172
177
  const { code, extension } = getCheckoutPageTemplate(options.language);
178
+ const { input } = await import('@inquirer/prompts');
173
179
  let outputFile = options.output;
174
180
  if (!outputFile) {
175
181
  const defaultName = `checkout.${extension}`;
@@ -6,7 +6,7 @@ import ConfigManager from '../services/config/manager.js';
6
6
  import ApiClient from '../services/api/client.js';
7
7
  import { validateApiKey } from '../utils/validator.js';
8
8
  import Spinner from '../utils/spinner.js';
9
- import { ApiKeyError, NetworkError, PayMongoError } from '../utils/errors.js';
9
+ import { ApiKeyError, NetworkError, PayMongoError, CommandError } from '../utils/errors.js';
10
10
  export async function initAction(options) {
11
11
  const spinner = new Spinner();
12
12
  const configManager = new ConfigManager();
@@ -156,7 +156,7 @@ export async function initAction(options) {
156
156
  else {
157
157
  console.error(chalk.red('❌ Unexpected error during validation. Please try again.'));
158
158
  }
159
- process.exit(1);
159
+ throw new CommandError();
160
160
  }
161
161
  spinner.start('Saving configuration...');
162
162
  await configManager.save(tempConfig);
@@ -170,13 +170,30 @@ PAYMONGO_ENVIRONMENT=${answers.environment}
170
170
  fs.writeFileSync(envPath, envContent);
171
171
  spinner.succeed('.env file created');
172
172
  const gitignorePath = path.join(process.cwd(), '.gitignore');
173
+ const ignoreHeader = '# PayMongo';
174
+ let gitignoreContent = '';
173
175
  if (fs.existsSync(gitignorePath)) {
174
- let gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
175
- if (!gitignoreContent.includes('.env')) {
176
- gitignoreContent += '\n# PayMongo\n.env\n.paymongo\n';
177
- fs.writeFileSync(gitignorePath, gitignoreContent);
178
- console.log(chalk.green('✓ Added .env and .paymongo to .gitignore'));
176
+ gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
177
+ }
178
+ const needsEnv = !gitignoreContent.includes('.env');
179
+ const needsPaymongo = !gitignoreContent.includes('.paymongo');
180
+ if (needsEnv || needsPaymongo) {
181
+ const lines = [];
182
+ if (!gitignoreContent.trim()) {
183
+ lines.push(ignoreHeader);
184
+ }
185
+ else {
186
+ lines.push('', ignoreHeader);
187
+ }
188
+ if (needsEnv) {
189
+ lines.push('.env');
190
+ }
191
+ if (needsPaymongo) {
192
+ lines.push('.paymongo');
179
193
  }
194
+ gitignoreContent += lines.join('\n') + '\n';
195
+ fs.writeFileSync(gitignorePath, gitignoreContent);
196
+ console.log(chalk.green('✓ Added .env and .paymongo to .gitignore'));
180
197
  }
181
198
  console.log('\n' + chalk.green('🎉 PayMongo project initialized!'));
182
199
  console.log('\n' + chalk.bold('Configuration saved to .paymongo'));
@@ -222,7 +239,7 @@ PAYMONGO_ENVIRONMENT=${answers.environment}
222
239
  console.log('');
223
240
  console.log(chalk.yellow('💡 For help, visit: https://developers.paymongo.com/docs'));
224
241
  }
225
- process.exit(1);
242
+ throw new CommandError();
226
243
  }
227
244
  }
228
245
  const command = new Command('init');
@@ -1,5 +1,4 @@
1
1
  import { Command } from 'commander';
2
- import { select, password } from '@inquirer/prompts';
3
2
  import * as fs from 'fs';
4
3
  import * as path from 'path';
5
4
  import * as crypto from 'crypto';
@@ -8,7 +7,7 @@ import chalk from 'chalk';
8
7
  import ConfigManager from '../services/config/manager.js';
9
8
  import ApiClient from '../services/api/client.js';
10
9
  import { validateApiKey } from '../utils/validator.js';
11
- import { ApiKeyError, NetworkError, PayMongoError } from '../utils/errors.js';
10
+ import { ApiKeyError, NetworkError, PayMongoError, CommandError } from '../utils/errors.js';
12
11
  import Spinner from '../utils/spinner.js';
13
12
  class CredentialManager {
14
13
  credentialsPath;
@@ -20,22 +19,38 @@ class CredentialManager {
20
19
  fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
21
20
  }
22
21
  this.credentialsPath = path.join(configDir, 'credentials.enc');
22
+ this.encryptionKey = this.deriveEncryptionKey();
23
+ }
24
+ deriveEncryptionKey() {
23
25
  const machineId = os.hostname() + os.userInfo().username;
24
- this.encryptionKey = crypto
25
- .createHash('sha256')
26
- .update(machineId)
27
- .digest('hex')
28
- .substring(0, 32);
26
+ const saltPath = path.join(path.dirname(this.credentialsPath), 'credentials.salt');
27
+ let salt;
28
+ try {
29
+ if (fs.existsSync(saltPath)) {
30
+ const saltHex = fs.readFileSync(saltPath, 'utf8').trim();
31
+ salt = Buffer.from(saltHex, 'hex');
32
+ }
33
+ else {
34
+ salt = crypto.randomBytes(16);
35
+ fs.writeFileSync(saltPath, salt.toString('hex'), { mode: 0o600 });
36
+ }
37
+ }
38
+ catch {
39
+ salt = crypto.randomBytes(16);
40
+ }
41
+ return crypto.scryptSync(machineId, salt, 32);
29
42
  }
30
43
  async saveCredentials(credentials) {
31
44
  const data = JSON.stringify(credentials);
32
- const iv = crypto.randomBytes(16);
33
- const cipher = crypto.createCipheriv('aes-256-cbc', this.encryptionKey, iv);
34
- let encrypted = cipher.update(data, 'utf8', 'hex');
35
- encrypted += cipher.final('hex');
45
+ const iv = crypto.randomBytes(12);
46
+ const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
47
+ const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]);
48
+ const tag = cipher.getAuthTag();
36
49
  const payload = {
50
+ v: 2,
37
51
  iv: iv.toString('hex'),
38
- data: encrypted,
52
+ tag: tag.toString('hex'),
53
+ data: encrypted.toString('hex'),
39
54
  };
40
55
  fs.writeFileSync(this.credentialsPath, JSON.stringify(payload), { mode: 0o600 });
41
56
  }
@@ -45,11 +60,32 @@ class CredentialManager {
45
60
  return null;
46
61
  }
47
62
  const payload = JSON.parse(fs.readFileSync(this.credentialsPath, 'utf8'));
48
- const iv = Buffer.from(payload.iv, 'hex');
49
- const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
50
- let decrypted = decipher.update(payload.data, 'hex', 'utf8');
51
- decrypted += decipher.final('utf8');
52
- return JSON.parse(decrypted);
63
+ if (payload.v === 2 && payload.iv && payload.tag && payload.data) {
64
+ const iv = Buffer.from(payload.iv, 'hex');
65
+ const tag = Buffer.from(payload.tag, 'hex');
66
+ const decipher = crypto.createDecipheriv('aes-256-gcm', this.encryptionKey, iv);
67
+ decipher.setAuthTag(tag);
68
+ const decrypted = Buffer.concat([
69
+ decipher.update(Buffer.from(payload.data, 'hex')),
70
+ decipher.final(),
71
+ ]).toString('utf8');
72
+ return JSON.parse(decrypted);
73
+ }
74
+ if (payload.iv && payload.data) {
75
+ const legacyKey = crypto
76
+ .createHash('sha256')
77
+ .update(os.hostname() + os.userInfo().username)
78
+ .digest('hex')
79
+ .substring(0, 32);
80
+ const iv = Buffer.from(payload.iv, 'hex');
81
+ const decipher = crypto.createDecipheriv('aes-256-cbc', legacyKey, iv);
82
+ let decrypted = decipher.update(payload.data, 'hex', 'utf8');
83
+ decrypted += decipher.final('utf8');
84
+ const credentials = JSON.parse(decrypted);
85
+ await this.saveCredentials(credentials);
86
+ return credentials;
87
+ }
88
+ return null;
53
89
  }
54
90
  catch (_error) {
55
91
  return null;
@@ -95,6 +131,7 @@ command
95
131
  }
96
132
  else {
97
133
  const storedCredentials = await credentialManager.loadCredentials();
134
+ const { select, password } = await import('@inquirer/prompts');
98
135
  const environment = await select({
99
136
  message: 'Environment:',
100
137
  choices: [
@@ -176,7 +213,7 @@ command
176
213
  else {
177
214
  console.error(chalk.red('❌ Unexpected error during validation. Please try again.'));
178
215
  }
179
- process.exit(1);
216
+ throw new CommandError();
180
217
  }
181
218
  spinner.start('Storing credentials securely...');
182
219
  const credentials = {
@@ -248,7 +285,7 @@ command
248
285
  console.log('');
249
286
  console.log(chalk.yellow('💡 For help, visit: https://developers.paymongo.com'));
250
287
  }
251
- process.exit(1);
288
+ throw new CommandError();
252
289
  }
253
290
  });
254
291
  export { command, CredentialManager };
@@ -6,6 +6,7 @@ import ApiClient from '../services/api/client.js';
6
6
  import { PaymentSimulator } from '../services/payments/simulator.js';
7
7
  import { BulkOperations } from '../utils/bulk.js';
8
8
  import Spinner from '../utils/spinner.js';
9
+ import { CommandError } from '../utils/errors.js';
9
10
  export async function exportAction(options) {
10
11
  const spinner = new Spinner();
11
12
  const configManager = new ConfigManager();
@@ -52,7 +53,7 @@ export async function exportAction(options) {
52
53
  spinner.stop();
53
54
  const err = error;
54
55
  console.error(chalk.red('❌ Failed to export payments:'), err.message);
55
- process.exit(1);
56
+ throw new CommandError();
56
57
  }
57
58
  }
58
59
  export async function importAction(filename, options) {
@@ -90,7 +91,7 @@ export async function importAction(filename, options) {
90
91
  spinner.stop();
91
92
  const err = error;
92
93
  console.error(chalk.red('❌ Failed to import payments:'), err.message);
93
- process.exit(1);
94
+ throw new CommandError();
94
95
  }
95
96
  }
96
97
  export async function listAction(options) {
@@ -155,7 +156,7 @@ export async function listAction(options) {
155
156
  spinner.stop();
156
157
  const err = error;
157
158
  console.error(chalk.red('❌ Failed to fetch payments:'), err.message);
158
- process.exit(1);
159
+ throw new CommandError();
159
160
  }
160
161
  }
161
162
  export async function showAction(id, options) {
@@ -208,7 +209,7 @@ export async function showAction(id, options) {
208
209
  spinner.stop();
209
210
  const err = error;
210
211
  console.error(chalk.red('❌ Failed to fetch payment:'), err.message);
211
- process.exit(1);
212
+ throw new CommandError();
212
213
  }
213
214
  }
214
215
  export async function createIntentAction(options) {
@@ -252,7 +253,7 @@ export async function createIntentAction(options) {
252
253
  spinner.stop();
253
254
  const err = error;
254
255
  console.error(chalk.red('❌ Failed to create payment intent:'), err.message);
255
- process.exit(1);
256
+ throw new CommandError();
256
257
  }
257
258
  }
258
259
  export async function confirmAction(intentId, options) {
@@ -349,7 +350,7 @@ export async function confirmAction(intentId, options) {
349
350
  spinner.stop();
350
351
  const err = error;
351
352
  console.error(chalk.red('❌ Failed to confirm payment intent:'), err.message);
352
- process.exit(1);
353
+ throw new CommandError();
353
354
  }
354
355
  }
355
356
  export async function captureAction(intentId, options) {
@@ -389,7 +390,7 @@ export async function captureAction(intentId, options) {
389
390
  spinner.stop();
390
391
  const err = error;
391
392
  console.error(chalk.red('❌ Failed to capture payment intent:'), err.message);
392
- process.exit(1);
393
+ throw new CommandError();
393
394
  }
394
395
  }
395
396
  export async function refundAction(paymentId, options) {
@@ -435,7 +436,7 @@ export async function refundAction(paymentId, options) {
435
436
  spinner.stop();
436
437
  const err = error;
437
438
  console.error(chalk.red('❌ Failed to create refund:'), err.message);
438
- process.exit(1);
439
+ throw new CommandError();
439
440
  }
440
441
  }
441
442
  const command = new Command('payments');
@@ -4,7 +4,7 @@ import Spinner from '../../utils/spinner.js';
4
4
  import chalk from 'chalk';
5
5
  import { ConfigManager } from '../../services/config/manager.js';
6
6
  import { TeamService } from '../../services/team/service.js';
7
- import { input, confirm } from '@inquirer/prompts';
7
+ import { CommandError } from '../../utils/errors.js';
8
8
  const command = new Command('team')
9
9
  .description('Team collaboration with API key sharing')
10
10
  .showHelpAfterError();
@@ -24,7 +24,7 @@ command
24
24
  if (invalidEnvs.length > 0) {
25
25
  console.error(chalk.red(`❌ Invalid environments: ${invalidEnvs.join(', ')}`));
26
26
  console.log(`Valid environments: ${validEnvs.join(', ')}`);
27
- process.exit(1);
27
+ throw new CommandError();
28
28
  }
29
29
  spinner.start(`Creating key bundle for ${environments.join(', ')}...`);
30
30
  const bundle = await teamService.createKeyBundle(environments);
@@ -71,16 +71,17 @@ command
71
71
  spinner.stop();
72
72
  const err = error;
73
73
  console.error(chalk.red('❌ Failed to create key bundle:'), err.message);
74
- process.exit(1);
74
+ throw new CommandError();
75
75
  }
76
76
  });
77
77
  command
78
78
  .command('import-keys')
79
79
  .description('Import shared API keys from team member')
80
80
  .option('-f, --force', 'Overwrite existing keys without confirmation')
81
- .action(async (_options) => {
81
+ .action(async (options) => {
82
82
  const spinner = new Spinner();
83
83
  try {
84
+ const { input } = await import('@inquirer/prompts');
84
85
  const config = new ConfigManager();
85
86
  const teamService = new TeamService({ config });
86
87
  const bundleJson = await input({
@@ -109,7 +110,7 @@ command
109
110
  });
110
111
  spinner.start('Importing keys...');
111
112
  const bundle = teamService.deserializeBundle(bundleJson);
112
- await teamService.importKeyBundle(bundle, memberName);
113
+ await teamService.importKeyBundle(bundle, memberName, { force: options.force });
113
114
  spinner.succeed('Keys imported successfully!');
114
115
  console.log(chalk.green('✅ API keys imported'));
115
116
  console.log('');
@@ -127,7 +128,7 @@ command
127
128
  spinner.stop();
128
129
  const err = error;
129
130
  console.error(chalk.red('❌ Failed to import keys:'), err.message);
130
- process.exit(1);
131
+ throw new CommandError();
131
132
  }
132
133
  });
133
134
  command
@@ -190,7 +191,7 @@ command
190
191
  spinner.stop();
191
192
  const err = error;
192
193
  console.error(chalk.red('❌ Failed to load team members:'), err.message);
193
- process.exit(1);
194
+ throw new CommandError();
194
195
  }
195
196
  });
196
197
  command
@@ -211,7 +212,7 @@ command
211
212
  spinner.stop();
212
213
  const err = error;
213
214
  console.error(chalk.red('❌ Failed to rename team:'), err.message);
214
- process.exit(1);
215
+ throw new CommandError();
215
216
  }
216
217
  });
217
218
  command
@@ -221,6 +222,7 @@ command
221
222
  .action(async (memberName) => {
222
223
  const spinner = new Spinner();
223
224
  try {
225
+ const { confirm } = await import('@inquirer/prompts');
224
226
  const confirmed = await confirm({
225
227
  message: `Are you sure you want to remove "${memberName}" from the team?`,
226
228
  default: false,
@@ -241,7 +243,7 @@ command
241
243
  spinner.stop();
242
244
  const err = error;
243
245
  console.error(chalk.red('❌ Failed to remove team member:'), err.message);
244
- process.exit(1);
246
+ throw new CommandError();
245
247
  }
246
248
  });
247
249
  export default command;