payment-skill 1.0.0

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 (110) hide show
  1. package/LICENSE +62 -0
  2. package/README.md +545 -0
  3. package/SKILL.md +99 -0
  4. package/SUPPORT.md +153 -0
  5. package/bin/payment-skill.js +2 -0
  6. package/dashboard.html +669 -0
  7. package/dist/api/bunq.d.ts +35 -0
  8. package/dist/api/bunq.d.ts.map +1 -0
  9. package/dist/api/bunq.js +164 -0
  10. package/dist/api/bunq.js.map +1 -0
  11. package/dist/api/wise.d.ts +32 -0
  12. package/dist/api/wise.d.ts.map +1 -0
  13. package/dist/api/wise.js +155 -0
  14. package/dist/api/wise.js.map +1 -0
  15. package/dist/cli.d.ts +8 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +69 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/commands/bunq.d.ts +8 -0
  20. package/dist/commands/bunq.d.ts.map +1 -0
  21. package/dist/commands/bunq.js +193 -0
  22. package/dist/commands/bunq.js.map +1 -0
  23. package/dist/commands/config.d.ts +8 -0
  24. package/dist/commands/config.d.ts.map +1 -0
  25. package/dist/commands/config.js +70 -0
  26. package/dist/commands/config.js.map +1 -0
  27. package/dist/commands/emergency.d.ts +8 -0
  28. package/dist/commands/emergency.d.ts.map +1 -0
  29. package/dist/commands/emergency.js +85 -0
  30. package/dist/commands/emergency.js.map +1 -0
  31. package/dist/commands/limits.d.ts +6 -0
  32. package/dist/commands/limits.d.ts.map +1 -0
  33. package/dist/commands/limits.js +125 -0
  34. package/dist/commands/limits.js.map +1 -0
  35. package/dist/commands/merchant.d.ts +6 -0
  36. package/dist/commands/merchant.d.ts.map +1 -0
  37. package/dist/commands/merchant.js +41 -0
  38. package/dist/commands/merchant.js.map +1 -0
  39. package/dist/commands/pay.d.ts +10 -0
  40. package/dist/commands/pay.d.ts.map +1 -0
  41. package/dist/commands/pay.js +112 -0
  42. package/dist/commands/pay.js.map +1 -0
  43. package/dist/commands/provider.d.ts +6 -0
  44. package/dist/commands/provider.d.ts.map +1 -0
  45. package/dist/commands/provider.js +74 -0
  46. package/dist/commands/provider.js.map +1 -0
  47. package/dist/commands/server.d.ts +8 -0
  48. package/dist/commands/server.d.ts.map +1 -0
  49. package/dist/commands/server.js +92 -0
  50. package/dist/commands/server.js.map +1 -0
  51. package/dist/commands/template.d.ts +8 -0
  52. package/dist/commands/template.d.ts.map +1 -0
  53. package/dist/commands/template.js +161 -0
  54. package/dist/commands/template.js.map +1 -0
  55. package/dist/commands/transaction.d.ts +6 -0
  56. package/dist/commands/transaction.d.ts.map +1 -0
  57. package/dist/commands/transaction.js +72 -0
  58. package/dist/commands/transaction.js.map +1 -0
  59. package/dist/commands/wise.d.ts +8 -0
  60. package/dist/commands/wise.d.ts.map +1 -0
  61. package/dist/commands/wise.js +240 -0
  62. package/dist/commands/wise.js.map +1 -0
  63. package/dist/core/config.d.ts +40 -0
  64. package/dist/core/config.d.ts.map +1 -0
  65. package/dist/core/config.js +201 -0
  66. package/dist/core/config.js.map +1 -0
  67. package/dist/core/template-engine.d.ts +27 -0
  68. package/dist/core/template-engine.d.ts.map +1 -0
  69. package/dist/core/template-engine.js +410 -0
  70. package/dist/core/template-engine.js.map +1 -0
  71. package/dist/core/transaction.d.ts +31 -0
  72. package/dist/core/transaction.d.ts.map +1 -0
  73. package/dist/core/transaction.js +214 -0
  74. package/dist/core/transaction.js.map +1 -0
  75. package/dist/index.d.ts +12 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +36 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/server/server.d.ts +14 -0
  80. package/dist/server/server.d.ts.map +1 -0
  81. package/dist/server/server.js +120 -0
  82. package/dist/server/server.js.map +1 -0
  83. package/dist/types/index.d.ts +141 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/index.js +8 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/logo.png +0 -0
  88. package/package.json +78 -0
  89. package/src/api/bunq.ts +257 -0
  90. package/src/api/wise.ts +204 -0
  91. package/src/cli.ts +67 -0
  92. package/src/commands/bunq.ts +223 -0
  93. package/src/commands/config.ts +72 -0
  94. package/src/commands/emergency.ts +94 -0
  95. package/src/commands/limits.ts +126 -0
  96. package/src/commands/merchant.ts +39 -0
  97. package/src/commands/pay.ts +109 -0
  98. package/src/commands/provider.ts +75 -0
  99. package/src/commands/server.ts +59 -0
  100. package/src/commands/template.ts +172 -0
  101. package/src/commands/transaction.ts +66 -0
  102. package/src/commands/wise.ts +279 -0
  103. package/src/core/config.ts +202 -0
  104. package/src/core/template-engine.ts +454 -0
  105. package/src/core/transaction.ts +228 -0
  106. package/src/index.ts +14 -0
  107. package/src/server/server.ts +131 -0
  108. package/src/types/index.ts +178 -0
  109. package/tsconfig.json +23 -0
  110. package/verified-merchants.json +63 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Payment Skill - Bunq Commands
3
+ *
4
+ * Bunq API CLI commands
5
+ */
6
+
7
+ import { Command } from 'commander';
8
+ import chalk from 'chalk';
9
+ import ora from 'ora';
10
+ import { BunqClient } from '../api/bunq';
11
+ import { configManager } from '../core/config';
12
+ import { transactionManager } from '../core/transaction';
13
+
14
+ export const bunqCommands = new Command('bunq')
15
+ .description('Bunq API operations');
16
+
17
+ // List accounts
18
+ bunqCommands
19
+ .command('accounts')
20
+ .description('List monetary accounts')
21
+ .requiredOption('-u, --user <id>', 'User ID')
22
+ .action(async (options) => {
23
+ const spinner = ora('Fetching accounts...').start();
24
+ try {
25
+ const config = configManager.getProvider('bunq');
26
+ if (!config) {
27
+ throw new Error('Bunq not configured. Run: payment-skill provider add bunq');
28
+ }
29
+
30
+ const client = new BunqClient(config as any);
31
+ const accounts = await client.getMonetaryAccounts(options.user);
32
+
33
+ spinner.stop();
34
+ console.log(chalk.blue('Monetary Accounts:'));
35
+ accounts.forEach((acc: any) => {
36
+ console.log(` ${acc.id} - ${acc.description} - ${acc.balance.value} ${acc.balance.currency}`);
37
+ });
38
+ } catch (error: any) {
39
+ spinner.fail(error.message);
40
+ process.exit(1);
41
+ }
42
+ });
43
+
44
+ // Get balance
45
+ bunqCommands
46
+ .command('balance')
47
+ .description('Get account balance')
48
+ .requiredOption('-u, --user <id>', 'User ID')
49
+ .requiredOption('-a, --account <id>', 'Account ID')
50
+ .action(async (options) => {
51
+ const spinner = ora('Fetching balance...').start();
52
+ try {
53
+ const config = configManager.getProvider('bunq');
54
+ if (!config) {
55
+ throw new Error('Bunq not configured');
56
+ }
57
+
58
+ const client = new BunqClient(config as any);
59
+ const account = await client.getMonetaryAccount(options.user, options.account);
60
+
61
+ spinner.stop();
62
+ console.log(chalk.blue('Account Balance:'));
63
+ console.log(` ${account.balance.value} ${account.balance.currency}`);
64
+ } catch (error: any) {
65
+ spinner.fail(error.message);
66
+ process.exit(1);
67
+ }
68
+ });
69
+
70
+ // Create payment
71
+ bunqCommands
72
+ .command('pay')
73
+ .description('Create a payment')
74
+ .requiredOption('-u, --user <id>', 'User ID')
75
+ .requiredOption('-a, --account <id>', 'Account ID')
76
+ .requiredOption('--amount <amount>', 'Payment amount')
77
+ .requiredOption('--currency <currency>', 'Currency (e.g., EUR)')
78
+ .requiredOption('--to-iban <iban>', 'Recipient IBAN')
79
+ .requiredOption('--to-name <name>', 'Recipient name')
80
+ .requiredOption('--description <text>', 'Payment description')
81
+ .action(async (options) => {
82
+ const spinner = ora('Creating payment...').start();
83
+ try {
84
+ const config = configManager.getProvider('bunq');
85
+ if (!config) {
86
+ throw new Error('Bunq not configured');
87
+ }
88
+
89
+ // Create transaction record
90
+ const tx = transactionManager.createTransaction(
91
+ 'bunq',
92
+ 'bunq-payment',
93
+ parseFloat(options.amount),
94
+ options.currency,
95
+ { toIban: options.toIban, description: options.description }
96
+ );
97
+
98
+ const client = new BunqClient(config as any);
99
+ const payment = await client.createPayment(
100
+ options.user,
101
+ options.account,
102
+ options.amount,
103
+ options.currency,
104
+ options.toIban,
105
+ options.toName,
106
+ options.description
107
+ );
108
+
109
+ transactionManager.updateTransactionStatus(tx.id, 'completed');
110
+
111
+ spinner.succeed(chalk.green('Payment created'));
112
+ console.log(` Transaction ID: ${tx.id}`);
113
+ console.log(` Payment ID: ${payment.id}`);
114
+ console.log(` Status: ${payment.status}`);
115
+ } catch (error: any) {
116
+ spinner.fail(error.message);
117
+ process.exit(1);
118
+ }
119
+ });
120
+
121
+ // Create payment request
122
+ bunqCommands
123
+ .command('request')
124
+ .description('Create a payment request (RequestInquiry)')
125
+ .requiredOption('-u, --user <id>', 'User ID')
126
+ .requiredOption('-a, --account <id>', 'Account ID')
127
+ .requiredOption('--amount <amount>', 'Request amount')
128
+ .requiredOption('--currency <currency>', 'Currency')
129
+ .requiredOption('--to <alias>', 'Counterparty alias (email/phone/IBAN)')
130
+ .requiredOption('--description <text>', 'Request description')
131
+ .option('--type <type>', 'Alias type (EMAIL/PHONE/IBAN)', 'EMAIL')
132
+ .action(async (options) => {
133
+ const spinner = ora('Creating payment request...').start();
134
+ try {
135
+ const config = configManager.getProvider('bunq');
136
+ if (!config) {
137
+ throw new Error('Bunq not configured');
138
+ }
139
+
140
+ const client = new BunqClient(config as any);
141
+ const request = await client.createRequestInquiry(
142
+ options.user,
143
+ options.account,
144
+ options.amount,
145
+ options.currency,
146
+ {
147
+ type: options.type,
148
+ value: options.to,
149
+ name: options.to
150
+ },
151
+ options.description
152
+ );
153
+
154
+ spinner.succeed(chalk.green('Payment request created'));
155
+ console.log(` Request ID: ${request.id}`);
156
+ console.log(` Status: ${request.status}`);
157
+ console.log(chalk.yellow(' Waiting for recipient to accept...'));
158
+ } catch (error: any) {
159
+ spinner.fail(error.message);
160
+ process.exit(1);
161
+ }
162
+ });
163
+
164
+ // List payments
165
+ bunqCommands
166
+ .command('payments')
167
+ .description('List payments')
168
+ .requiredOption('-u, --user <id>', 'User ID')
169
+ .requiredOption('-a, --account <id>', 'Account ID')
170
+ .option('-l, --limit <number>', 'Limit results', '10')
171
+ .action(async (options) => {
172
+ const spinner = ora('Fetching payments...').start();
173
+ try {
174
+ const config = configManager.getProvider('bunq');
175
+ if (!config) {
176
+ throw new Error('Bunq not configured');
177
+ }
178
+
179
+ const client = new BunqClient(config as any);
180
+ const payments = await client.getPayments(options.user, options.account, parseInt(options.limit));
181
+
182
+ spinner.stop();
183
+ console.log(chalk.blue(`Payments (${payments.length}):`));
184
+ payments.forEach((p: any) => {
185
+ const direction = p.amount.value.startsWith('-') ? 'OUT' : 'IN';
186
+ const color = direction === 'OUT' ? chalk.red : chalk.green;
187
+ console.log(` ${p.id} - ${color(direction)} - ${p.amount.value} ${p.amount.currency} - ${p.description}`);
188
+ });
189
+ } catch (error: any) {
190
+ spinner.fail(error.message);
191
+ process.exit(1);
192
+ }
193
+ });
194
+
195
+ // List requests
196
+ bunqCommands
197
+ .command('requests')
198
+ .description('List payment requests')
199
+ .requiredOption('-u, --user <id>', 'User ID')
200
+ .requiredOption('-a, --account <id>', 'Account ID')
201
+ .action(async (options) => {
202
+ const spinner = ora('Fetching requests...').start();
203
+ try {
204
+ const config = configManager.getProvider('bunq');
205
+ if (!config) {
206
+ throw new Error('Bunq not configured');
207
+ }
208
+
209
+ const client = new BunqClient(config as any);
210
+ const requests = await client.getRequestInquiries(options.user, options.account);
211
+
212
+ spinner.stop();
213
+ console.log(chalk.blue(`Payment Requests (${requests.length}):`));
214
+ requests.forEach((r: any) => {
215
+ const statusColor = r.status === 'ACCEPTED' ? chalk.green :
216
+ r.status === 'PENDING' ? chalk.yellow : chalk.red;
217
+ console.log(` ${r.id} - ${statusColor(r.status)} - ${r.amount_inquired.value} ${r.amount_inquired.currency}`);
218
+ });
219
+ } catch (error: any) {
220
+ spinner.fail(error.message);
221
+ process.exit(1);
222
+ }
223
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Payment Skill - Config Commands
3
+ *
4
+ * Configuration management CLI commands
5
+ */
6
+
7
+ import { Command } from 'commander';
8
+ import chalk from 'chalk';
9
+ import { configManager } from '../core/config';
10
+
11
+ export const configCommands = new Command('config')
12
+ .description('Manage payment-skill configuration');
13
+
14
+ configCommands
15
+ .command('get')
16
+ .description('Get configuration value')
17
+ .argument('<key>', 'Configuration key')
18
+ .action((key) => {
19
+ const config = configManager.getConfig();
20
+ const value = config[key];
21
+ if (value !== undefined) {
22
+ console.log(JSON.stringify(value, null, 2));
23
+ } else {
24
+ console.log(chalk.yellow(`Key '${key}' not found`));
25
+ }
26
+ });
27
+
28
+ configCommands
29
+ .command('set')
30
+ .description('Set configuration value')
31
+ .argument('<key>', 'Configuration key')
32
+ .argument('<value>', 'Configuration value')
33
+ .action((key, value) => {
34
+ try {
35
+ const parsedValue = JSON.parse(value);
36
+ configManager.setConfig(key, parsedValue);
37
+ console.log(chalk.green(`✓ Set ${key} = ${value}`));
38
+ } catch {
39
+ configManager.setConfig(key, value);
40
+ console.log(chalk.green(`✓ Set ${key} = ${value}`));
41
+ }
42
+ });
43
+
44
+ configCommands
45
+ .command('list')
46
+ .description('List all configuration')
47
+ .action(() => {
48
+ const config = configManager.getConfig();
49
+ console.log(chalk.blue('Configuration:'));
50
+ console.log(JSON.stringify(config, null, 2));
51
+ });
52
+
53
+ configCommands
54
+ .command('init')
55
+ .description('Initialize configuration')
56
+ .action(() => {
57
+ console.log(chalk.blue('Initializing payment-skill configuration...'));
58
+ console.log(chalk.green('✓ Configuration initialized'));
59
+ console.log(chalk.gray('Use "payment-skill provider add" to configure providers'));
60
+ });
61
+
62
+ configCommands
63
+ .command('path')
64
+ .description('Show configuration file path')
65
+ .action(() => {
66
+ const path = require('path').join(
67
+ process.env.HOME || process.env.USERPROFILE || '.',
68
+ '.payment-skill',
69
+ 'config.json'
70
+ );
71
+ console.log(path);
72
+ });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Payment Skill - Emergency Commands
3
+ *
4
+ * Emergency stop and safety CLI commands
5
+ */
6
+
7
+ import { Command } from 'commander';
8
+ import chalk from 'chalk';
9
+ import { configManager } from '../core/config';
10
+ import { transactionManager } from '../core/transaction';
11
+
12
+ export const emergencyCommands = new Command('emergency')
13
+ .description('Emergency stop and safety controls');
14
+
15
+ emergencyCommands
16
+ .command('stop')
17
+ .description('Activate emergency stop - halt all transactions')
18
+ .option('-r, --reason <reason>', 'Reason for emergency stop')
19
+ .action((options) => {
20
+ const reason = options.reason || 'Manual activation';
21
+
22
+ console.log(chalk.red.bold('🚨 ACTIVATING EMERGENCY STOP'));
23
+ console.log(chalk.red(`Reason: ${reason}`));
24
+
25
+ // Get pending transactions
26
+ const pending = transactionManager.getPendingTransactions();
27
+ console.log(chalk.yellow(`Found ${pending.length} pending transactions`));
28
+
29
+ // Activate emergency stop
30
+ configManager.activateEmergencyStop(reason);
31
+
32
+ // Cancel all pending
33
+ const cancelled = transactionManager.cancelAllPending();
34
+
35
+ console.log(chalk.green(`✓ Emergency stop activated`));
36
+ console.log(chalk.green(`✓ Cancelled ${cancelled} pending transactions`));
37
+ console.log(chalk.red.bold('\n⚠️ All API operations are now BLOCKED'));
38
+ console.log(chalk.gray('Use "payment-skill emergency resume" to re-enable'));
39
+ });
40
+
41
+ emergencyCommands
42
+ .command('resume')
43
+ .description('Deactivate emergency stop - resume operations')
44
+ .action(() => {
45
+ if (!configManager.isEmergencyStopActive()) {
46
+ console.log(chalk.yellow('Emergency stop is not active'));
47
+ return;
48
+ }
49
+
50
+ console.log(chalk.blue('Deactivating emergency stop...'));
51
+ configManager.deactivateEmergencyStop();
52
+ console.log(chalk.green('✓ Emergency stop deactivated'));
53
+ console.log(chalk.green('✓ API operations resumed'));
54
+ });
55
+
56
+ emergencyCommands
57
+ .command('status')
58
+ .description('Check emergency stop status')
59
+ .action(() => {
60
+ const state = configManager.getEmergencyStopState();
61
+
62
+ if (state.active) {
63
+ console.log(chalk.red.bold('🚨 EMERGENCY STOP IS ACTIVE'));
64
+ console.log(chalk.red(`Activated: ${state.activatedAt}`));
65
+ console.log(chalk.red(`Reason: ${state.reason}`));
66
+ console.log(chalk.yellow(`Pending transactions on hold: ${state.pendingTransactions.length}`));
67
+ } else {
68
+ console.log(chalk.green('✓ Emergency stop is inactive'));
69
+ console.log(chalk.gray('All operations normal'));
70
+ }
71
+ });
72
+
73
+ emergencyCommands
74
+ .command('kill-all')
75
+ .description('Kill all pending transactions (use with caution)')
76
+ .option('-f, --force', 'Force without confirmation')
77
+ .action((options) => {
78
+ const pending = transactionManager.getPendingTransactions();
79
+
80
+ if (pending.length === 0) {
81
+ console.log(chalk.yellow('No pending transactions to kill'));
82
+ return;
83
+ }
84
+
85
+ console.log(chalk.red.bold(`⚠️ About to kill ${pending.length} pending transactions`));
86
+
87
+ if (!options.force) {
88
+ console.log(chalk.yellow('Use --force to confirm'));
89
+ return;
90
+ }
91
+
92
+ const cancelled = transactionManager.cancelAllPending();
93
+ console.log(chalk.green(`✓ Killed ${cancelled} transactions`));
94
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Payment Skill - Limit Commands
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import chalk from 'chalk';
7
+ import { configManager } from '../core/config';
8
+
9
+ export const limitCommands = new Command('limits')
10
+ .description('Manage payment limits and controls');
11
+
12
+ limitCommands
13
+ .command('get')
14
+ .description('Get current limits')
15
+ .action(() => {
16
+ const limits = configManager.getLimits();
17
+ console.log(chalk.blue('Current Limits:'));
18
+ console.log(` Per Transaction: ${limits.perTransaction}`);
19
+ console.log(` Daily: ${limits.daily}`);
20
+ console.log(` Weekly: ${limits.weekly}`);
21
+ console.log(` Monthly: ${limits.monthly}`);
22
+ console.log(` Max per Hour: ${limits.maxTransactionsPerHour}`);
23
+ });
24
+
25
+ limitCommands
26
+ .command('set')
27
+ .description('Set limits')
28
+ .option('--per-transaction <amount>', 'Per transaction limit')
29
+ .option('--daily <amount>', 'Daily limit')
30
+ .option('--weekly <amount>', 'Weekly limit')
31
+ .option('--monthly <amount>', 'Monthly limit')
32
+ .option('--max-per-hour <count>', 'Max transactions per hour')
33
+ .action((options) => {
34
+ const limits: any = {};
35
+ if (options.perTransaction) limits.perTransaction = parseInt(options.perTransaction);
36
+ if (options.daily) limits.daily = parseInt(options.daily);
37
+ if (options.weekly) limits.weekly = parseInt(options.weekly);
38
+ if (options.monthly) limits.monthly = parseInt(options.monthly);
39
+ if (options.maxPerHour) limits.maxTransactionsPerHour = parseInt(options.maxPerHour);
40
+
41
+ configManager.setLimits(limits);
42
+ console.log(chalk.green('✓ Limits updated'));
43
+ });
44
+
45
+ const timeWindowCommands = new Command('time-window')
46
+ .description('Time window controls');
47
+
48
+ timeWindowCommands
49
+ .command('get')
50
+ .description('Get time window settings')
51
+ .action(() => {
52
+ const tw = configManager.getTimeWindow();
53
+ console.log(chalk.blue('Time Window:'));
54
+ console.log(` Enabled: ${tw.enabled}`);
55
+ console.log(` Start: ${tw.start}`);
56
+ console.log(` End: ${tw.end}`);
57
+ console.log(` Timezone: ${tw.timezone}`);
58
+ });
59
+
60
+ timeWindowCommands
61
+ .command('set')
62
+ .description('Set time window')
63
+ .option('--start <time>', 'Start time (HH:MM)')
64
+ .option('--end <time>', 'End time (HH:MM)')
65
+ .option('--timezone <tz>', 'Timezone')
66
+ .action((options) => {
67
+ const tw: any = {};
68
+ if (options.start) tw.start = options.start;
69
+ if (options.end) tw.end = options.end;
70
+ if (options.timezone) tw.timezone = options.timezone;
71
+
72
+ configManager.setTimeWindow(tw);
73
+ console.log(chalk.green('✓ Time window updated'));
74
+ });
75
+
76
+ timeWindowCommands
77
+ .command('enable')
78
+ .description('Enable time window')
79
+ .action(() => {
80
+ configManager.setTimeWindow({ enabled: true });
81
+ console.log(chalk.green('✓ Time window enabled'));
82
+ });
83
+
84
+ timeWindowCommands
85
+ .command('disable')
86
+ .description('Disable time window')
87
+ .action(() => {
88
+ configManager.setTimeWindow({ enabled: false });
89
+ console.log(chalk.green('✓ Time window disabled'));
90
+ });
91
+
92
+ limitCommands.addCommand(timeWindowCommands);
93
+
94
+ limitCommands
95
+ .command('block')
96
+ .description('Block a merchant')
97
+ .argument('<merchant-id>', 'Merchant ID')
98
+ .action((merchantId) => {
99
+ console.log(chalk.yellow(`Merchant blocking not yet implemented for ${merchantId}`));
100
+ });
101
+
102
+ limitCommands
103
+ .command('unblock')
104
+ .description('Unblock a merchant')
105
+ .argument('<merchant-id>', 'Merchant ID')
106
+ .action((merchantId) => {
107
+ console.log(chalk.yellow(`Merchant unblocking not yet implemented for ${merchantId}`));
108
+ });
109
+
110
+ limitCommands
111
+ .command('block-category')
112
+ .description('Block a category')
113
+ .argument('<category>', 'Category name')
114
+ .action((category) => {
115
+ configManager.addBlockedCategory(category);
116
+ console.log(chalk.green(`✓ Category '${category}' blocked`));
117
+ });
118
+
119
+ limitCommands
120
+ .command('unblock-category')
121
+ .description('Unblock a category')
122
+ .argument('<category>', 'Category name')
123
+ .action((category) => {
124
+ configManager.removeBlockedCategory(category);
125
+ console.log(chalk.green(`✓ Category '${category}' unblocked`));
126
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Payment Skill - Merchant Commands
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import chalk from 'chalk';
7
+
8
+ export const merchantCommands = new Command('merchant')
9
+ .description('Merchant detection and management');
10
+
11
+ merchantCommands
12
+ .command('detect')
13
+ .description('Detect merchant API capabilities')
14
+ .argument('<domain>', 'Merchant domain')
15
+ .action(async (domain) => {
16
+ console.log(chalk.blue(`Detecting API capabilities for ${domain}...`));
17
+ // Implementation would detect actual API
18
+ console.log(chalk.yellow('Merchant detection not yet implemented'));
19
+ });
20
+
21
+ merchantCommands
22
+ .command('list-apis')
23
+ .description('List supported merchant APIs')
24
+ .action(() => {
25
+ console.log(chalk.blue('Supported Merchant APIs:'));
26
+ console.log(' - wise.com (Wise Platform)');
27
+ console.log(' - bunq.com (Bunq API)');
28
+ console.log(' - stripe.com (Stripe Connect)');
29
+ console.log(' - airwallex.com (Airwallex API)');
30
+ });
31
+
32
+ merchantCommands
33
+ .command('capabilities')
34
+ .description('Get merchant capabilities')
35
+ .argument('<merchant-id>', 'Merchant ID')
36
+ .action((merchantId) => {
37
+ console.log(chalk.blue(`Capabilities for ${merchantId}:`));
38
+ console.log(chalk.yellow('Not yet implemented'));
39
+ });
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Payment Skill - Pay Command
3
+ *
4
+ * Execute payment using templates - HYBRID ARCHITECTURE
5
+ * OpenClaw selects template and provides parameters
6
+ * Payment-skill executes the predefined flow
7
+ */
8
+
9
+ import { Command } from 'commander';
10
+ import chalk from 'chalk';
11
+ import ora from 'ora';
12
+ import { templateEngine } from '../core/template-engine';
13
+
14
+ export const payCommand = new Command('pay')
15
+ .description('Execute a payment using a template (HYBRID ARCHITECTURE)')
16
+ .requiredOption('-t, --template <id>', 'Template ID (e.g., wise_standard_transfer, bunq_instant_payment)')
17
+ .requiredOption('--amount <amount>', 'Payment amount')
18
+ .requiredOption('--currency <currency>', 'Currency code (EUR, USD, etc.)')
19
+ .option('--profile-id <id>', 'Wise profile ID')
20
+ .option('--user-id <id>', 'Bunq user ID')
21
+ .option('--account-id <id>', 'Bunq account ID')
22
+ .option('--recipient-id <id>', 'Recipient/account ID')
23
+ .option('--recipient-iban <iban>', 'Recipient IBAN')
24
+ .option('--recipient-name <name>', 'Recipient name')
25
+ .option('--reference <text>', 'Payment reference')
26
+ .option('--description <text>', 'Payment description')
27
+ .option('--dry-run', 'Show what would be executed without actually doing it')
28
+ .action(async (options) => {
29
+ const spinner = ora('Initializing payment...').start();
30
+
31
+ try {
32
+ // Get template
33
+ const template = templateEngine.getTemplate(options.template);
34
+ if (!template) {
35
+ spinner.fail(`Template '${options.template}' not found`);
36
+ console.log(chalk.yellow('\nAvailable templates:'));
37
+ const templates = templateEngine.getAllTemplates();
38
+ templates.forEach(t => {
39
+ console.log(` ${t.templateId} - ${t.description}`);
40
+ });
41
+ process.exit(1);
42
+ }
43
+
44
+ spinner.succeed(`Using template: ${template.templateId}`);
45
+ console.log(chalk.blue(`Description: ${template.description}`));
46
+ console.log(chalk.gray(`Merchant: ${template.merchant}`));
47
+
48
+ // Build parameters
49
+ const params: Record<string, any> = {
50
+ amount: options.amount,
51
+ currency: options.currency.toUpperCase()
52
+ };
53
+
54
+ if (options.profileId) params.profileId = options.profileId;
55
+ if (options.userId) params.userId = options.userId;
56
+ if (options.accountId) params.accountId = options.accountId;
57
+ if (options.recipientId) params.recipientId = options.recipientId;
58
+ if (options.recipientIban) params.recipientIban = options.recipientIban;
59
+ if (options.recipientName) params.recipientName = options.recipientName;
60
+ if (options.reference) params.reference = options.reference;
61
+ if (options.description) params.description = options.description;
62
+
63
+ // Show execution plan
64
+ console.log(chalk.blue('\nExecution Plan:'));
65
+ template.steps.sort((a, b) => a.order - b.order).forEach(step => {
66
+ console.log(` ${step.order}. ${step.name}`);
67
+ if (step.async) {
68
+ console.log(chalk.yellow(` [ASYNC - ${step.confirmation?.type || 'manual'} confirmation]`));
69
+ }
70
+ });
71
+
72
+ console.log(chalk.blue('\nParameters:'));
73
+ Object.entries(params).forEach(([key, value]) => {
74
+ console.log(` ${key}: ${value}`);
75
+ });
76
+
77
+ if (options.dryRun) {
78
+ console.log(chalk.yellow('\n[DRY RUN - No actual payment will be made]'));
79
+ return;
80
+ }
81
+
82
+ // Confirm execution
83
+ console.log(chalk.yellow('\n⚠️ This will execute the payment flow above.'));
84
+ console.log(chalk.gray('Use --dry-run to preview without executing.'));
85
+
86
+ // Execute template
87
+ const execSpinner = ora('Executing payment flow...').start();
88
+
89
+ try {
90
+ const tx = await templateEngine.executeTemplate(options.template, params);
91
+
92
+ execSpinner.succeed(chalk.green('Payment flow completed!'));
93
+ console.log(chalk.green(`\n✓ Transaction ID: ${tx.id}`));
94
+ console.log(` Status: ${tx.status}`);
95
+ console.log(` Amount: ${tx.amount} ${tx.currency}`);
96
+
97
+ if (template.steps.some(s => s.async)) {
98
+ console.log(chalk.yellow('\n⚠️ Some steps are async and may require confirmation.'));
99
+ console.log(chalk.gray('Check status with: payment-skill transaction status ' + tx.id));
100
+ }
101
+ } catch (error: any) {
102
+ execSpinner.fail(chalk.red('Payment failed'));
103
+ throw error;
104
+ }
105
+ } catch (error: any) {
106
+ console.error(chalk.red('\nError:'), error.message);
107
+ process.exit(1);
108
+ }
109
+ });