paymongo-cli 1.4.12 → 1.4.14

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 (51) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/copilot-instructions.md +8 -6
  3. package/AGENTS.md +8 -9
  4. package/CHANGELOG.md +21 -0
  5. package/README.md +3 -3
  6. package/biome.json +72 -0
  7. package/dist/.tsbuildinfo +1 -1
  8. package/dist/commands/config/actions.js +13 -5
  9. package/dist/commands/config/analytics.js +75 -0
  10. package/dist/commands/config/helpers.js +14 -25
  11. package/dist/commands/config/rate-limit.js +3 -3
  12. package/dist/commands/config.js +7 -1
  13. package/dist/commands/dev/logs.js +13 -4
  14. package/dist/commands/dev/status.js +1 -1
  15. package/dist/commands/dev/stop.js +2 -2
  16. package/dist/commands/dev.js +10 -258
  17. package/dist/commands/doctor.js +7 -8
  18. package/dist/commands/env.js +10 -19
  19. package/dist/commands/generate/templates/index.js +3 -3
  20. package/dist/commands/generate.js +7 -7
  21. package/dist/commands/init.js +22 -36
  22. package/dist/commands/login.js +18 -29
  23. package/dist/commands/payments/actions.js +15 -15
  24. package/dist/commands/payments/helpers.js +6 -24
  25. package/dist/commands/payments.js +1 -1
  26. package/dist/commands/shared/auth.js +23 -0
  27. package/dist/commands/shared/runtime.js +35 -0
  28. package/dist/commands/team/index.js +4 -4
  29. package/dist/commands/trigger/actions.js +2 -2
  30. package/dist/commands/trigger/helpers.js +13 -9
  31. package/dist/commands/trigger.js +2 -2
  32. package/dist/commands/webhooks/actions.js +11 -11
  33. package/dist/commands/webhooks/helpers.js +5 -23
  34. package/dist/commands/webhooks.js +1 -1
  35. package/dist/index.js +37 -19
  36. package/dist/services/analytics/service.js +3 -3
  37. package/dist/services/api/client.js +8 -4
  38. package/dist/services/api/rate-limiter.js +3 -2
  39. package/dist/services/config/manager.js +13 -3
  40. package/dist/services/dev/process-manager.js +4 -4
  41. package/dist/services/dev/server.js +7 -8
  42. package/dist/services/dev/session.js +354 -0
  43. package/dist/services/team/service.js +1 -1
  44. package/dist/types/schemas.js +3 -2
  45. package/dist/utils/bulk.js +11 -11
  46. package/dist/utils/cache.js +5 -5
  47. package/dist/utils/constants.js +1 -1
  48. package/dist/utils/webhook-store.js +3 -3
  49. package/package.json +11 -25
  50. package/vitest.config.ts +18 -0
  51. package/eslint.config.ts +0 -70
@@ -1,9 +1,7 @@
1
- import { Command } from 'commander';
2
1
  import chalk from 'chalk';
3
- import ConfigManager from '../services/config/manager.js';
4
- import ApiClient from '../services/api/client.js';
5
- import Spinner from '../utils/spinner.js';
6
- import { ApiKeyError, NetworkError, PayMongoError, CommandError } from '../utils/errors.js';
2
+ import { Command } from 'commander';
3
+ import { ApiKeyError, CommandError, NetworkError, PayMongoError } from '../utils/errors.js';
4
+ import { createApiClient, createCommandContext, loadCommandConfig, showNoConfigMessage, } from './shared/runtime.js';
7
5
  const command = new Command('env');
8
6
  command
9
7
  .description('Manage PayMongo environments')
@@ -12,22 +10,16 @@ command
12
10
  .arguments('<environment>')
13
11
  .option('-f, --force', 'Skip API key validation')
14
12
  .action(async (environment, options) => {
15
- const spinner = new Spinner();
16
- const configManager = new ConfigManager();
13
+ const { spinner, configManager } = createCommandContext();
17
14
  try {
18
15
  if (!['test', 'live'].includes(environment)) {
19
16
  console.error(chalk.red('❌ Invalid environment. Must be "test" or "live"'));
20
17
  throw new CommandError();
21
18
  }
22
- spinner.start('Loading configuration...');
23
- const config = await configManager.load();
19
+ const config = await loadCommandConfig(spinner, configManager);
24
20
  if (!config) {
25
- spinner.fail('No configuration found');
26
- console.log(chalk.yellow('No PayMongo configuration found.'));
27
- console.log(chalk.gray("Run 'paymongo init' to set up your project first."));
28
21
  return;
29
22
  }
30
- spinner.succeed('Configuration loaded');
31
23
  const envConfig = config.apiKeys[environment];
32
24
  if (!envConfig?.secret || !envConfig?.public) {
33
25
  spinner.fail(`Missing API keys for ${environment} environment`);
@@ -41,7 +33,7 @@ command
41
33
  if (!options.force) {
42
34
  spinner.start('Validating API keys...');
43
35
  const testConfig = { ...config, environment: environment };
44
- const apiClient = new ApiClient({ config: testConfig });
36
+ const apiClient = createApiClient(testConfig);
45
37
  try {
46
38
  await apiClient.validateApiKey();
47
39
  spinner.succeed('API keys validated');
@@ -102,20 +94,19 @@ command
102
94
  }
103
95
  }))
104
96
  .addCommand(new Command('current').description('Show current environment').action(async () => {
105
- const configManager = new ConfigManager();
97
+ const { configManager } = createCommandContext();
106
98
  try {
107
99
  const config = await configManager.load();
108
100
  if (!config) {
109
- console.log(chalk.yellow('No PayMongo configuration found.'));
110
- console.log(chalk.gray("Run 'paymongo init' to set up your project first."));
101
+ showNoConfigMessage();
111
102
  return;
112
103
  }
113
104
  const env = config.environment;
114
105
  const envConfig = config.apiKeys[env];
115
106
  console.log(chalk.bold('Current Environment:'));
116
107
  console.log(`Environment: ${chalk.cyan(env.toUpperCase())}`);
117
- console.log(`Public Key: ${envConfig?.public ? chalk.gray(envConfig.public.substring(0, 10) + '...') : chalk.red('Not set')}`);
118
- console.log(`Secret Key: ${envConfig?.secret ? chalk.gray(envConfig.secret.substring(0, 10) + '...') : chalk.red('Not set')}`);
108
+ console.log(`Public Key: ${envConfig?.public ? chalk.gray(`${envConfig.public.substring(0, 10)}...`) : chalk.red('Not set')}`);
109
+ console.log(`Secret Key: ${envConfig?.secret ? chalk.gray(`${envConfig.secret.substring(0, 10)}...`) : chalk.red('Not set')}`);
119
110
  if (env === 'live') {
120
111
  console.log('');
121
112
  console.log(chalk.yellow('⚠️ You are using LIVE environment!'));
@@ -1,5 +1,5 @@
1
- export { getWebhookHandlerTemplate as getJavaScriptWebhookHandler } from './webhook-handler/javascript.js';
2
- export { getWebhookHandlerTemplate as getTypeScriptWebhookHandler } from './webhook-handler/typescript.js';
1
+ export { getCheckoutPageTemplate, getHtmlTemplate, getReactTemplate, getVueTemplate, } from './checkout-page/index.js';
3
2
  export { getPaymentIntentTemplate as getJavaScriptPaymentIntent } from './payment-intent/javascript.js';
4
3
  export { getPaymentIntentTemplate as getTypeScriptPaymentIntent } from './payment-intent/typescript.js';
5
- export { getCheckoutPageTemplate, getHtmlTemplate, getReactTemplate, getVueTemplate, } from './checkout-page/index.js';
4
+ export { getWebhookHandlerTemplate as getJavaScriptWebhookHandler } from './webhook-handler/javascript.js';
5
+ export { getWebhookHandlerTemplate as getTypeScriptWebhookHandler } from './webhook-handler/typescript.js';
@@ -1,9 +1,9 @@
1
- import { Command } from 'commander';
1
+ import fs from 'node:fs/promises';
2
2
  import chalk from 'chalk';
3
- import fs from 'fs/promises';
3
+ import { Command } from 'commander';
4
4
  import ConfigManager from '../services/config/manager.js';
5
5
  import Spinner from '../utils/spinner.js';
6
- import { getJavaScriptWebhookHandler, getTypeScriptWebhookHandler, getJavaScriptPaymentIntent, getTypeScriptPaymentIntent, getCheckoutPageTemplate, } from './generate/templates/index.js';
6
+ import { getCheckoutPageTemplate, getJavaScriptPaymentIntent, getJavaScriptWebhookHandler, getTypeScriptPaymentIntent, getTypeScriptWebhookHandler, } from './generate/templates/index.js';
7
7
  const command = new Command('generate');
8
8
  command
9
9
  .description('Generate boilerplate code for PayMongo integrations')
@@ -63,7 +63,7 @@ FRAMEWORKS:
63
63
 
64
64
  EXAMPLES:
65
65
  $ paymongo generate checkout-page
66
- $ paymongo generate checkout-page --framework react
66
+ $ paymongo generate checkout-page --language react
67
67
  $ paymongo generate checkout-page --language vue --output Checkout.vue
68
68
  `)
69
69
  .action(async (options) => {
@@ -126,7 +126,7 @@ async function generateWebhookHandler(options) {
126
126
  spinner.start(`Generating webhook handler...`);
127
127
  await fs.writeFile(outputFile, code, 'utf-8');
128
128
  spinner.succeed(`Webhook handler generated: ${outputFile}`);
129
- console.log('\n' + chalk.green('✅ Webhook handler generated successfully!'));
129
+ console.log(`\n${chalk.green('✅ Webhook handler generated successfully!')}`);
130
130
  console.log(chalk.gray(`Events handled: ${events.join(', ')}`));
131
131
  console.log(chalk.gray(`Language: ${options.language}`));
132
132
  console.log(chalk.gray(`Framework: ${options.framework}`));
@@ -162,7 +162,7 @@ async function generatePaymentIntent(options) {
162
162
  spinner.start(`Generating payment intent code...`);
163
163
  await fs.writeFile(outputFile, code, 'utf-8');
164
164
  spinner.succeed(`Payment intent code generated: ${outputFile}`);
165
- console.log('\n' + chalk.green('✅ Payment intent code generated successfully!'));
165
+ console.log(`\n${chalk.green('✅ Payment intent code generated successfully!')}`);
166
166
  console.log(chalk.gray(`Payment methods: ${methods.join(', ')}`));
167
167
  console.log(chalk.gray(`Language: ${options.language}`));
168
168
  }
@@ -187,7 +187,7 @@ async function generateCheckoutPage(options) {
187
187
  spinner.start(`Generating checkout page...`);
188
188
  await fs.writeFile(outputFile, code, 'utf-8');
189
189
  spinner.succeed(`Checkout page generated: ${outputFile}`);
190
- console.log('\n' + chalk.green('✅ Checkout page generated successfully!'));
190
+ console.log(`\n${chalk.green('✅ Checkout page generated successfully!')}`);
191
191
  console.log(chalk.gray(`Framework: ${options.language}`));
192
192
  }
193
193
  catch (error) {
@@ -1,15 +1,13 @@
1
- import { Command } from 'commander';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
4
3
  import chalk from 'chalk';
5
- import ConfigManager from '../services/config/manager.js';
6
- import ApiClient from '../services/api/client.js';
4
+ import { Command } from 'commander';
5
+ import { ApiKeyError, CommandError, NetworkError, PayMongoError } from '../utils/errors.js';
7
6
  import { validateApiKey } from '../utils/validator.js';
8
- import Spinner from '../utils/spinner.js';
9
- import { ApiKeyError, NetworkError, PayMongoError, CommandError } from '../utils/errors.js';
7
+ import { createCredentialValidationConfig } from './shared/auth.js';
8
+ import { createApiClient, createCommandContext } from './shared/runtime.js';
10
9
  export async function initAction(options) {
11
- const spinner = new Spinner();
12
- const configManager = new ConfigManager();
10
+ const { spinner, configManager } = createCommandContext();
13
11
  try {
14
12
  if (await configManager.exists()) {
15
13
  const { confirm } = await import('@inquirer/prompts');
@@ -107,28 +105,16 @@ export async function initAction(options) {
107
105
  };
108
106
  }
109
107
  spinner.start('Validating API keys...');
110
- const tempConfig = {
111
- version: '1.0',
108
+ const tempConfig = createCredentialValidationConfig({
112
109
  projectName: answers.projectName,
113
110
  environment: answers.environment,
114
- apiKeys: {
115
- [answers.environment]: {
116
- public: answers.publicKey,
117
- secret: answers.secretKey,
118
- },
119
- },
120
- webhooks: {
121
- url: answers.webhookUrl || `http://localhost:${answers.port}/webhook`,
122
- events: answers.events,
123
- },
124
- webhookSecrets: {},
125
- dev: {
126
- port: answers.port,
127
- autoRegisterWebhook: true,
128
- verifyWebhookSignatures: true,
129
- },
130
- };
131
- const apiClient = new ApiClient({ config: tempConfig });
111
+ publicKey: answers.publicKey,
112
+ secretKey: answers.secretKey,
113
+ webhookUrl: answers.webhookUrl || `http://localhost:${answers.port}/webhook`,
114
+ events: answers.events,
115
+ port: answers.port,
116
+ });
117
+ const apiClient = createApiClient(tempConfig);
132
118
  try {
133
119
  await apiClient.validateApiKey();
134
120
  spinner.succeed('API keys validated');
@@ -191,19 +177,19 @@ PAYMONGO_ENVIRONMENT=${answers.environment}
191
177
  if (needsPaymongo) {
192
178
  lines.push('.paymongo');
193
179
  }
194
- gitignoreContent += lines.join('\n') + '\n';
180
+ gitignoreContent += `${lines.join('\n')}\n`;
195
181
  fs.writeFileSync(gitignorePath, gitignoreContent);
196
182
  console.log(chalk.green('✓ Added .env and .paymongo to .gitignore'));
197
183
  }
198
- console.log('\n' + chalk.green('🎉 PayMongo project initialized!'));
199
- console.log('\n' + chalk.bold('Configuration saved to .paymongo'));
184
+ console.log(`\n${chalk.green('🎉 PayMongo project initialized!')}`);
185
+ console.log(`\n${chalk.bold('Configuration saved to .paymongo')}`);
200
186
  console.log(chalk.bold('Environment variables saved to .env'));
201
- console.log('\n' + chalk.bold('Next steps:'));
202
- console.log(' 1. Run ' + chalk.cyan('paymongo dev') + ' to start development server');
187
+ console.log(`\n${chalk.bold('Next steps:')}`);
188
+ console.log(` 1. Run ${chalk.cyan('paymongo dev')} to start development server`);
203
189
  console.log(' 2. Configure your webhook handler at ' +
204
190
  chalk.cyan(`http://localhost:${answers.port}/webhook`));
205
- console.log(' 3. Visit ' + chalk.cyan('https://dashboard.paymongo.com') + ' to view transactions');
206
- console.log('\n' + chalk.yellow('Happy building! 🚀'));
191
+ console.log(` 3. Visit ${chalk.cyan('https://dashboard.paymongo.com')} to view transactions`);
192
+ console.log(`\n${chalk.yellow('Happy building! 🚀')}`);
207
193
  }
208
194
  catch (error) {
209
195
  spinner.stop();
@@ -1,14 +1,13 @@
1
- import { Command } from 'commander';
2
- import * as fs from 'fs';
3
- import * as path from 'path';
4
- import * as crypto from 'crypto';
5
- import * as os from 'os';
1
+ import * as crypto from 'node:crypto';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
6
5
  import chalk from 'chalk';
7
- import ConfigManager from '../services/config/manager.js';
8
- import ApiClient from '../services/api/client.js';
6
+ import { Command } from 'commander';
7
+ import { ApiKeyError, CommandError, NetworkError, PayMongoError } from '../utils/errors.js';
9
8
  import { validateApiKey } from '../utils/validator.js';
10
- import { ApiKeyError, NetworkError, PayMongoError, CommandError } from '../utils/errors.js';
11
- import Spinner from '../utils/spinner.js';
9
+ import { createCredentialValidationConfig } from './shared/auth.js';
10
+ import { createApiClient, createCommandContext } from './shared/runtime.js';
12
11
  class CredentialManager {
13
12
  credentialsPath;
14
13
  encryptionKey;
@@ -105,8 +104,7 @@ command
105
104
  .option('-e, --env <environment>', 'Environment (test or live)', 'test')
106
105
  .option('--logout', 'Clear stored credentials')
107
106
  .action(async (options) => {
108
- const spinner = new Spinner();
109
- const configManager = new ConfigManager();
107
+ const { spinner, configManager } = createCommandContext();
110
108
  const credentialManager = new CredentialManager();
111
109
  try {
112
110
  if (options.logout) {
@@ -171,21 +169,12 @@ command
171
169
  };
172
170
  }
173
171
  spinner.start('Validating API key...');
174
- const tempConfig = {
175
- version: '1.0',
176
- projectName: 'temp',
172
+ const tempConfig = createCredentialValidationConfig({
177
173
  environment: answers.environment,
178
- apiKeys: {
179
- [answers.environment]: {
180
- public: answers.publicKey || '',
181
- secret: answers.secretKey,
182
- },
183
- },
184
- webhooks: { url: '', events: [] },
185
- webhookSecrets: {},
186
- dev: { port: 3000, autoRegisterWebhook: true, verifyWebhookSignatures: true },
187
- };
188
- const apiClient = new ApiClient({ config: tempConfig });
174
+ publicKey: answers.publicKey || '',
175
+ secretKey: answers.secretKey,
176
+ });
177
+ const apiClient = createApiClient(tempConfig);
189
178
  try {
190
179
  await apiClient.validateApiKey();
191
180
  spinner.succeed('API key validated');
@@ -244,14 +233,14 @@ command
244
233
  };
245
234
  await configManager.save(config);
246
235
  }
247
- console.log('\n' + chalk.green('🔐 PayMongo Login Successful'));
248
- console.log('\n' + chalk.bold('Current configuration:'));
236
+ console.log(`\n${chalk.green('🔐 PayMongo Login Successful')}`);
237
+ console.log(`\n${chalk.bold('Current configuration:')}`);
249
238
  console.log(` Environment: ${answers.environment}`);
250
239
  console.log(` Secret Key: ${'*'.repeat(20)}...${answers.secretKey.slice(-4)}`);
251
240
  if (answers.publicKey) {
252
241
  console.log(` Public Key: ${'*'.repeat(20)}...${answers.publicKey.slice(-4)}`);
253
242
  }
254
- console.log('\n' + chalk.gray("Use 'paymongo config show' to view settings"));
243
+ console.log(`\n${chalk.gray("Use 'paymongo config show' to view settings")}`);
255
244
  console.log(chalk.gray("Use 'paymongo login --logout' to clear credentials"));
256
245
  }
257
246
  catch (error) {
@@ -288,4 +277,4 @@ command
288
277
  throw new CommandError();
289
278
  }
290
279
  });
291
- export { command, CredentialManager };
280
+ export { CredentialManager, command };
@@ -1,5 +1,5 @@
1
- import Table from 'cli-table3';
2
1
  import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
3
  import { BulkOperations } from '../../utils/bulk.js';
4
4
  import { createApiClient, createPaymentSimulator, createPaymentsContext, getStatusColor, handlePaymentsError, loadPaymentsConfig, parseBoundedInt, } from './helpers.js';
5
5
  export async function exportAction(options) {
@@ -23,7 +23,7 @@ export async function exportAction(options) {
23
23
  spinner.start(`Exporting to ${filename}...`);
24
24
  await BulkOperations.exportPayments(payments, filename, config.environment);
25
25
  spinner.succeed('Export completed');
26
- console.log('\n' + chalk.green('✅ Payments exported successfully!'));
26
+ console.log(`\n${chalk.green('✅ Payments exported successfully!')}`);
27
27
  console.log('');
28
28
  console.log(`${chalk.bold('File:')} ${filename}`);
29
29
  console.log(`${chalk.bold('Payments:')} ${payments.length}`);
@@ -44,18 +44,18 @@ export async function importAction(filename, options) {
44
44
  console.log(JSON.stringify({ payments, metadata }, null, 2));
45
45
  return;
46
46
  }
47
- console.log('\n' + chalk.green('✅ Payments imported successfully!'));
47
+ console.log(`\n${chalk.green('✅ Payments imported successfully!')}`);
48
48
  console.log('');
49
49
  console.log(`${chalk.bold('Source:')} ${filename}`);
50
50
  console.log(`${chalk.bold('Payments:')} ${payments.length}`);
51
51
  console.log(`${chalk.bold('Exported from:')} ${metadata.environment} environment`);
52
52
  console.log(`${chalk.bold('Export date:')} ${new Date(metadata.exported_at).toLocaleString()}`);
53
- console.log('\n' + chalk.yellow('⚠️ Important Notes:'));
53
+ console.log(`\n${chalk.yellow('⚠️ Important Notes:')}`);
54
54
  console.log(chalk.gray('• Payment data imported for reference only'));
55
55
  console.log(chalk.gray('• Actual payments cannot be recreated through the API'));
56
56
  console.log(chalk.gray('• Use this for data analysis, migration planning, or testing'));
57
57
  if (payments.length > 0) {
58
- console.log('\n' + chalk.bold('Sample Payment IDs:'));
58
+ console.log(`\n${chalk.bold('Sample Payment IDs:')}`);
59
59
  payments.slice(0, 5).forEach((payment, index) => {
60
60
  const amount = (payment.attributes.amount / 100).toFixed(2);
61
61
  console.log(` ${index + 1}. ${payment.id} - ₱${amount} ${payment.attributes.currency}`);
@@ -111,10 +111,10 @@ export async function listAction(options) {
111
111
  chalk.yellow(amount),
112
112
  getStatusColor(status)(status),
113
113
  chalk.gray(created),
114
- chalk.white(description.length > 25 ? description.substring(0, 22) + '...' : description),
114
+ chalk.white(description.length > 25 ? `${description.substring(0, 22)}...` : description),
115
115
  ]);
116
116
  });
117
- console.log('\n' + chalk.bold('Recent Payments'));
117
+ console.log(`\n${chalk.bold('Recent Payments')}`);
118
118
  console.log(chalk.gray('─'.repeat(95)));
119
119
  console.log(table.toString());
120
120
  console.log(chalk.gray(`Total: ${payments.length} payments`));
@@ -142,7 +142,7 @@ export async function showAction(id, options) {
142
142
  const amount = (attrs.amount / 100).toFixed(2);
143
143
  const fees = attrs.fees ? (attrs.fees / 100).toFixed(2) : '0.00';
144
144
  const netAmount = attrs.net_amount ? (attrs.net_amount / 100).toFixed(2) : '0.00';
145
- console.log('\n' + chalk.bold('Payment Details'));
145
+ console.log(`\n${chalk.bold('Payment Details')}`);
146
146
  console.log(chalk.gray('─'.repeat(50)));
147
147
  console.log(`${chalk.bold('ID:')} ${payment.id}`);
148
148
  console.log(`${chalk.bold('Amount:')} ₱${amount} ${attrs.currency}`);
@@ -183,7 +183,7 @@ export async function createIntentAction(options) {
183
183
  return;
184
184
  }
185
185
  const attrs = paymentIntent.attributes;
186
- console.log('\n' + chalk.bold('Payment Intent Created'));
186
+ console.log(`\n${chalk.bold('Payment Intent Created')}`);
187
187
  console.log(chalk.gray('─'.repeat(50)));
188
188
  console.log(`${chalk.bold('ID:')} ${paymentIntent.id}`);
189
189
  console.log(`${chalk.bold('Amount:')} ₱${(attrs.amount / 100).toFixed(2)} ${attrs.currency}`);
@@ -222,10 +222,10 @@ export async function attachAction(intentId, options) {
222
222
  throw new Error(`Invalid simulation outcome. Must be one of: ${validOutcomes.join(', ')}`);
223
223
  }
224
224
  const delayMs = options.delay ? parseInt(options.delay, 10) : undefined;
225
- if (options.delay && (delayMs === undefined || isNaN(delayMs) || delayMs <= 0)) {
225
+ if (options.delay && (delayMs === undefined || Number.isNaN(delayMs) || delayMs <= 0)) {
226
226
  throw new Error('Simulation delay must be a positive number in milliseconds');
227
227
  }
228
- console.log('\n' + chalk.bold('🧪 Payment Simulation Mode'));
228
+ console.log(`\n${chalk.bold('🧪 Payment Simulation Mode')}`);
229
229
  console.log(chalk.gray('─'.repeat(50)));
230
230
  console.log(`${chalk.bold('Method:')} ${options.method.toUpperCase()}`);
231
231
  console.log(`${chalk.bold('Outcome:')} ${options.outcome}`);
@@ -243,7 +243,7 @@ export async function attachAction(intentId, options) {
243
243
  return;
244
244
  }
245
245
  const attrs = result.paymentIntent.attributes;
246
- console.log('\n' + chalk.bold('Payment Intent Confirmed (Simulated)'));
246
+ console.log(`\n${chalk.bold('Payment Intent Confirmed (Simulated)')}`);
247
247
  console.log(chalk.gray('─'.repeat(50)));
248
248
  console.log(`${chalk.bold('ID:')} ${result.paymentIntent.id}`);
249
249
  console.log(`${chalk.bold('Amount:')} ₱${(attrs.amount / 100).toFixed(2)} ${attrs.currency}`);
@@ -264,7 +264,7 @@ export async function attachAction(intentId, options) {
264
264
  return;
265
265
  }
266
266
  const attrs = result.attributes;
267
- console.log('\n' + chalk.bold('Payment Method Attached'));
267
+ console.log(`\n${chalk.bold('Payment Method Attached')}`);
268
268
  console.log(chalk.gray('─'.repeat(50)));
269
269
  console.log(`${chalk.bold('ID:')} ${result.id}`);
270
270
  console.log(`${chalk.bold('Amount:')} ₱${(attrs.amount / 100).toFixed(2)} ${attrs.currency}`);
@@ -295,7 +295,7 @@ export async function captureAction(intentId, options) {
295
295
  return;
296
296
  }
297
297
  const attrs = result.attributes;
298
- console.log('\n' + chalk.bold('Payment Intent Captured'));
298
+ console.log(`\n${chalk.bold('Payment Intent Captured')}`);
299
299
  console.log(chalk.gray('─'.repeat(50)));
300
300
  console.log(`${chalk.bold('ID:')} ${result.id}`);
301
301
  console.log(`${chalk.bold('Amount:')} ₱${(attrs.amount / 100).toFixed(2)} ${attrs.currency}`);
@@ -331,7 +331,7 @@ export async function refundAction(paymentId, options) {
331
331
  return;
332
332
  }
333
333
  const attrs = refund.attributes;
334
- console.log('\n' + chalk.bold('Refund Created'));
334
+ console.log(`\n${chalk.bold('Refund Created')}`);
335
335
  console.log(chalk.gray('─'.repeat(50)));
336
336
  console.log(`${chalk.bold('ID:')} ${refund.id}`);
337
337
  console.log(`${chalk.bold('Payment ID:')} ${attrs.payment_id}`);
@@ -1,14 +1,8 @@
1
1
  import chalk from 'chalk';
2
- import ApiClient from '../../services/api/client.js';
3
- import ConfigManager from '../../services/config/manager.js';
4
- import Spinner from '../../utils/spinner.js';
5
2
  import { PaymentSimulator } from '../../services/payments/simulator.js';
6
- import { CommandError } from '../../utils/errors.js';
3
+ import { createCommandContext, createApiClient as createSharedApiClient, failCommand, loadCommandConfig, } from '../shared/runtime.js';
7
4
  export function createPaymentsContext() {
8
- return {
9
- spinner: new Spinner(),
10
- configManager: new ConfigManager(),
11
- };
5
+ return createCommandContext();
12
6
  }
13
7
  export function getStatusColor(status) {
14
8
  switch (status) {
@@ -30,32 +24,20 @@ export function getStatusColor(status) {
30
24
  }
31
25
  }
32
26
  export async function loadPaymentsConfig(spinner, configManager) {
33
- spinner.start('Loading configuration...');
34
- const config = await configManager.load();
35
- if (!config) {
36
- spinner.fail('No configuration found');
37
- console.log(chalk.yellow('No PayMongo configuration found.'));
38
- console.log(chalk.gray("Run 'paymongo init' to set up your project first."));
39
- return null;
40
- }
41
- spinner.succeed('Configuration loaded');
42
- return config;
27
+ return loadCommandConfig(spinner, configManager);
43
28
  }
44
29
  export function createApiClient(config) {
45
- return new ApiClient({ config });
30
+ return createSharedApiClient(config);
46
31
  }
47
32
  export function createPaymentSimulator() {
48
33
  return new PaymentSimulator();
49
34
  }
50
35
  export function handlePaymentsError(prefix, spinner, error) {
51
- spinner.stop();
52
- const err = error;
53
- console.error(chalk.red(prefix), err.message);
54
- throw new CommandError();
36
+ return failCommand(prefix, error, spinner);
55
37
  }
56
38
  export function parseBoundedInt(value, fallback, errorMessage, validate) {
57
39
  const parsed = parseInt(value || fallback, 10);
58
- if (isNaN(parsed) || !validate(parsed)) {
40
+ if (Number.isNaN(parsed) || !validate(parsed)) {
59
41
  throw new Error(errorMessage);
60
42
  }
61
43
  return parsed;
@@ -54,5 +54,5 @@ command
54
54
  .option('-r, --reason <reason>', 'Refund reason: duplicate, fraudulent, requested_by_customer')
55
55
  .option('-j, --json', 'Output as JSON')
56
56
  .action(refundAction));
57
- export { exportAction, importAction, listAction, showAction, createIntentAction, attachAction, confirmAction, captureAction, refundAction, };
57
+ export { attachAction, captureAction, confirmAction, createIntentAction, exportAction, importAction, listAction, refundAction, showAction, };
58
58
  export default command;
@@ -0,0 +1,23 @@
1
+ export function createCredentialValidationConfig({ projectName = 'temp', environment, publicKey = '', secretKey, webhookUrl = '', events = [], port = 3000, }) {
2
+ return {
3
+ version: '1.0',
4
+ projectName,
5
+ environment,
6
+ apiKeys: {
7
+ [environment]: {
8
+ public: publicKey,
9
+ secret: secretKey,
10
+ },
11
+ },
12
+ webhooks: {
13
+ url: webhookUrl,
14
+ events,
15
+ },
16
+ webhookSecrets: {},
17
+ dev: {
18
+ port,
19
+ autoRegisterWebhook: true,
20
+ verifyWebhookSignatures: true,
21
+ },
22
+ };
23
+ }
@@ -0,0 +1,35 @@
1
+ import chalk from 'chalk';
2
+ import ApiClient from '../../services/api/client.js';
3
+ import ConfigManager from '../../services/config/manager.js';
4
+ import { CommandError } from '../../utils/errors.js';
5
+ import Spinner from '../../utils/spinner.js';
6
+ export function createCommandContext() {
7
+ return {
8
+ spinner: new Spinner(),
9
+ configManager: new ConfigManager(),
10
+ };
11
+ }
12
+ export function showNoConfigMessage(message = "Run 'paymongo init' to set up your project first.") {
13
+ console.log(chalk.yellow('No PayMongo configuration found.'));
14
+ console.log(chalk.gray(message));
15
+ }
16
+ export async function loadCommandConfig(spinner, configManager, loadingText = 'Loading configuration...', missingMessage) {
17
+ spinner.start(loadingText);
18
+ const config = await configManager.load();
19
+ if (!config) {
20
+ spinner.fail('No configuration found');
21
+ showNoConfigMessage(missingMessage);
22
+ return null;
23
+ }
24
+ spinner.succeed('Configuration loaded');
25
+ return config;
26
+ }
27
+ export function createApiClient(config) {
28
+ return new ApiClient({ config });
29
+ }
30
+ export function failCommand(prefix, error, spinner) {
31
+ spinner?.stop();
32
+ const err = error;
33
+ console.error(chalk.red(prefix), err.message);
34
+ throw new CommandError();
35
+ }
@@ -1,10 +1,10 @@
1
+ import chalk from 'chalk';
1
2
  import Table from 'cli-table3';
2
3
  import { Command } from 'commander';
3
- import Spinner from '../../utils/spinner.js';
4
- import chalk from 'chalk';
5
4
  import { ConfigManager } from '../../services/config/manager.js';
6
5
  import { TeamService } from '../../services/team/service.js';
7
6
  import { CommandError } from '../../utils/errors.js';
7
+ import Spinner from '../../utils/spinner.js';
8
8
  const command = new Command('team')
9
9
  .description('Team collaboration with API key sharing')
10
10
  .showHelpAfterError();
@@ -44,7 +44,7 @@ command
44
44
  console.log(chalk.gray(`Environments: ${bundle.environments.join(', ')}`));
45
45
  if (options.copy) {
46
46
  try {
47
- const { execSync } = await import('child_process');
47
+ const { execSync } = await import('node:child_process');
48
48
  const bundleJson = teamService.serializeBundle(bundle);
49
49
  try {
50
50
  execSync(`echo '${bundleJson.replace(/'/g, "'\\''")}' | clip`, { stdio: 'pipe' });
@@ -121,7 +121,7 @@ command
121
121
  console.log(`Imported at: ${new Date().toLocaleString()}`);
122
122
  console.log('');
123
123
  console.log(chalk.yellow('ℹ️ Next steps:'));
124
- console.log('• Run "paymongo config list" to see the imported keys');
124
+ console.log('• Run "paymongo config show" to see the imported keys');
125
125
  console.log('• Test with "paymongo payments list" to verify access');
126
126
  }
127
127
  catch (error) {
@@ -1,5 +1,5 @@
1
- import Table from 'cli-table3';
2
1
  import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
3
  import { CommandError } from '../../utils/errors.js';
4
4
  import { AVAILABLE_TRIGGER_EVENTS, createTriggerContext, failTriggerCommand, generateWebhookPayload, printJsonResponse, sendWebhookRequest, } from './helpers.js';
5
5
  export async function sendWebhookEvent(options) {
@@ -235,7 +235,7 @@ export async function replayWebhookEvent(eventId, options) {
235
235
  console.log(chalk.bold.blue(`\n📋 Recent "${options.event}" Events`));
236
236
  console.log(chalk.gray('─'.repeat(60)));
237
237
  matchingEvents.slice(0, 5).forEach((event, index) => {
238
- console.log(`${chalk.cyan((index + 1).toString() + '.')} ${chalk.yellow(event.id)} - ${chalk.gray(new Date(event.timestamp * 1000).toLocaleString())}`);
238
+ console.log(`${chalk.cyan(`${(index + 1).toString()}.`)} ${chalk.yellow(event.id)} - ${chalk.gray(new Date(event.timestamp * 1000).toLocaleString())}`);
239
239
  });
240
240
  console.log(chalk.gray('\n💡 Use "paymongo trigger replay <eventId>" to replay a specific event'));
241
241
  return;
@@ -1,14 +1,14 @@
1
- import crypto from 'crypto';
2
- import ConfigManager from '../../services/config/manager.js';
3
- import Spinner from '../../utils/spinner.js';
4
- import Logger from '../../utils/logger.js';
5
- import WebhookEventStore from '../../utils/webhook-store.js';
1
+ import crypto from 'node:crypto';
6
2
  import { CLI_VERSION } from '../../utils/constants.js';
7
3
  import { CommandError } from '../../utils/errors.js';
4
+ import Logger from '../../utils/logger.js';
5
+ import WebhookEventStore from '../../utils/webhook-store.js';
6
+ import { createCommandContext } from '../shared/runtime.js';
8
7
  export function createTriggerContext() {
8
+ const { spinner, configManager } = createCommandContext();
9
9
  return {
10
- spinner: new Spinner(),
11
- configManager: new ConfigManager(),
10
+ spinner,
11
+ configManager,
12
12
  logger: new Logger(),
13
13
  store: new WebhookEventStore(),
14
14
  };
@@ -46,7 +46,11 @@ export function buildSignatureHeader(config, webhookUrl, body, livemode) {
46
46
  .createHmac('sha256', secret)
47
47
  .update(`${timestamp}.${body}`)
48
48
  .digest('hex');
49
- const parts = [`t=${timestamp}`, livemode ? 'te=' : `te=${signature}`, livemode ? `li=${signature}` : 'li='];
49
+ const parts = [
50
+ `t=${timestamp}`,
51
+ livemode ? 'te=' : `te=${signature}`,
52
+ livemode ? `li=${signature}` : 'li=',
53
+ ];
50
54
  return parts.join(',');
51
55
  }
52
56
  export async function sendWebhookRequest(config, webhookUrl, payload) {
@@ -219,7 +223,7 @@ export function generateWebhookPayload(eventType) {
219
223
  }
220
224
  export async function printJsonResponse(response) {
221
225
  const contentType = response.headers['content-type'];
222
- if (contentType && contentType.includes('application/json')) {
226
+ if (contentType?.includes('application/json')) {
223
227
  return response.body.json();
224
228
  }
225
229
  return null;