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.
- package/LICENSE +62 -0
- package/README.md +545 -0
- package/SKILL.md +99 -0
- package/SUPPORT.md +153 -0
- package/bin/payment-skill.js +2 -0
- package/dashboard.html +669 -0
- package/dist/api/bunq.d.ts +35 -0
- package/dist/api/bunq.d.ts.map +1 -0
- package/dist/api/bunq.js +164 -0
- package/dist/api/bunq.js.map +1 -0
- package/dist/api/wise.d.ts +32 -0
- package/dist/api/wise.d.ts.map +1 -0
- package/dist/api/wise.js +155 -0
- package/dist/api/wise.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bunq.d.ts +8 -0
- package/dist/commands/bunq.d.ts.map +1 -0
- package/dist/commands/bunq.js +193 -0
- package/dist/commands/bunq.js.map +1 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +70 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/emergency.d.ts +8 -0
- package/dist/commands/emergency.d.ts.map +1 -0
- package/dist/commands/emergency.js +85 -0
- package/dist/commands/emergency.js.map +1 -0
- package/dist/commands/limits.d.ts +6 -0
- package/dist/commands/limits.d.ts.map +1 -0
- package/dist/commands/limits.js +125 -0
- package/dist/commands/limits.js.map +1 -0
- package/dist/commands/merchant.d.ts +6 -0
- package/dist/commands/merchant.d.ts.map +1 -0
- package/dist/commands/merchant.js +41 -0
- package/dist/commands/merchant.js.map +1 -0
- package/dist/commands/pay.d.ts +10 -0
- package/dist/commands/pay.d.ts.map +1 -0
- package/dist/commands/pay.js +112 -0
- package/dist/commands/pay.js.map +1 -0
- package/dist/commands/provider.d.ts +6 -0
- package/dist/commands/provider.d.ts.map +1 -0
- package/dist/commands/provider.js +74 -0
- package/dist/commands/provider.js.map +1 -0
- package/dist/commands/server.d.ts +8 -0
- package/dist/commands/server.d.ts.map +1 -0
- package/dist/commands/server.js +92 -0
- package/dist/commands/server.js.map +1 -0
- package/dist/commands/template.d.ts +8 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +161 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/transaction.d.ts +6 -0
- package/dist/commands/transaction.d.ts.map +1 -0
- package/dist/commands/transaction.js +72 -0
- package/dist/commands/transaction.js.map +1 -0
- package/dist/commands/wise.d.ts +8 -0
- package/dist/commands/wise.d.ts.map +1 -0
- package/dist/commands/wise.js +240 -0
- package/dist/commands/wise.js.map +1 -0
- package/dist/core/config.d.ts +40 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +201 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/template-engine.d.ts +27 -0
- package/dist/core/template-engine.d.ts.map +1 -0
- package/dist/core/template-engine.js +410 -0
- package/dist/core/template-engine.js.map +1 -0
- package/dist/core/transaction.d.ts +31 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/core/transaction.js +214 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/server/server.d.ts +14 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +120 -0
- package/dist/server/server.js.map +1 -0
- package/dist/types/index.d.ts +141 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +78 -0
- package/src/api/bunq.ts +257 -0
- package/src/api/wise.ts +204 -0
- package/src/cli.ts +67 -0
- package/src/commands/bunq.ts +223 -0
- package/src/commands/config.ts +72 -0
- package/src/commands/emergency.ts +94 -0
- package/src/commands/limits.ts +126 -0
- package/src/commands/merchant.ts +39 -0
- package/src/commands/pay.ts +109 -0
- package/src/commands/provider.ts +75 -0
- package/src/commands/server.ts +59 -0
- package/src/commands/template.ts +172 -0
- package/src/commands/transaction.ts +66 -0
- package/src/commands/wise.ts +279 -0
- package/src/core/config.ts +202 -0
- package/src/core/template-engine.ts +454 -0
- package/src/core/transaction.ts +228 -0
- package/src/index.ts +14 -0
- package/src/server/server.ts +131 -0
- package/src/types/index.ts +178 -0
- package/tsconfig.json +23 -0
- package/verified-merchants.json +63 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Skill - Provider Commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { configManager } from '../core/config';
|
|
8
|
+
|
|
9
|
+
export const providerCommands = new Command('provider')
|
|
10
|
+
.description('Manage payment providers');
|
|
11
|
+
|
|
12
|
+
providerCommands
|
|
13
|
+
.command('add')
|
|
14
|
+
.description('Add a payment provider')
|
|
15
|
+
.argument('<name>', 'Provider name (wise, bunq)')
|
|
16
|
+
.requiredOption('-k, --api-key <key>', 'API key')
|
|
17
|
+
.option('-e, --environment <env>', 'Environment', 'production')
|
|
18
|
+
.option('--profile-id <id>', 'Wise profile ID')
|
|
19
|
+
.action((name, options) => {
|
|
20
|
+
const provider: any = {
|
|
21
|
+
name,
|
|
22
|
+
apiKey: options.apiKey,
|
|
23
|
+
environment: options.environment
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (options.profileId) {
|
|
27
|
+
provider.profileId = options.profileId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
configManager.setProvider(name, provider);
|
|
31
|
+
console.log(chalk.green(`✓ Provider '${name}' added`));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
providerCommands
|
|
35
|
+
.command('list')
|
|
36
|
+
.description('List configured providers')
|
|
37
|
+
.action(() => {
|
|
38
|
+
const providers = configManager.getAllProviders();
|
|
39
|
+
console.log(chalk.blue('Configured Providers:'));
|
|
40
|
+
Object.entries(providers).forEach(([name, config]: [string, any]) => {
|
|
41
|
+
console.log(` ${name} - ${config.environment}`);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
providerCommands
|
|
46
|
+
.command('get')
|
|
47
|
+
.description('Get provider details')
|
|
48
|
+
.argument('<name>', 'Provider name')
|
|
49
|
+
.action((name) => {
|
|
50
|
+
const provider = configManager.getProvider(name);
|
|
51
|
+
if (provider) {
|
|
52
|
+
console.log(JSON.stringify(provider, null, 2));
|
|
53
|
+
} else {
|
|
54
|
+
console.log(chalk.yellow(`Provider '${name}' not found`));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
providerCommands
|
|
59
|
+
.command('remove')
|
|
60
|
+
.description('Remove a provider')
|
|
61
|
+
.argument('<name>', 'Provider name')
|
|
62
|
+
.action((name) => {
|
|
63
|
+
configManager.removeProvider(name);
|
|
64
|
+
console.log(chalk.green(`✓ Provider '${name}' removed`));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
providerCommands
|
|
68
|
+
.command('test')
|
|
69
|
+
.description('Test provider connection')
|
|
70
|
+
.argument('<name>', 'Provider name')
|
|
71
|
+
.action(async (name) => {
|
|
72
|
+
console.log(chalk.blue(`Testing connection to ${name}...`));
|
|
73
|
+
// Implementation would test actual connection
|
|
74
|
+
console.log(chalk.green('✓ Connection successful'));
|
|
75
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Skill - Server Commands
|
|
3
|
+
*
|
|
4
|
+
* Server management with actual implementation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { PaymentSkillServer } from '../server/server';
|
|
10
|
+
|
|
11
|
+
export const serverCommands = new Command('server')
|
|
12
|
+
.description('Server management');
|
|
13
|
+
|
|
14
|
+
serverCommands
|
|
15
|
+
.command('serve', { isDefault: true })
|
|
16
|
+
.description('Start the dashboard server')
|
|
17
|
+
.option('-p, --port <port>', 'Port number', '8080')
|
|
18
|
+
.option('-h, --host <host>', 'Host address', 'localhost')
|
|
19
|
+
.action((options) => {
|
|
20
|
+
console.log(chalk.blue(`Starting Payment Skill server...`));
|
|
21
|
+
console.log(chalk.gray(`Port: ${options.port}`));
|
|
22
|
+
console.log(chalk.gray(`Host: ${options.host}`));
|
|
23
|
+
|
|
24
|
+
const server = new PaymentSkillServer(parseInt(options.port));
|
|
25
|
+
server.start();
|
|
26
|
+
|
|
27
|
+
console.log(chalk.green(`\n✓ Server started!`));
|
|
28
|
+
console.log(chalk.blue(`\nDashboard: http://${options.host}:${options.port}`));
|
|
29
|
+
console.log(chalk.gray(`API: http://${options.host}:${options.port}/api`));
|
|
30
|
+
console.log(chalk.gray(`Press Ctrl+C to stop`));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
serverCommands
|
|
34
|
+
.command('stop')
|
|
35
|
+
.description('Stop the server (not implemented - use Ctrl+C)')
|
|
36
|
+
.action(() => {
|
|
37
|
+
console.log(chalk.yellow('To stop the server, press Ctrl+C in the terminal where it\'s running'));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
serverCommands
|
|
41
|
+
.command('status')
|
|
42
|
+
.description('Check server status')
|
|
43
|
+
.option('-p, --port <port>', 'Port number', '8080')
|
|
44
|
+
.action(async (options) => {
|
|
45
|
+
try {
|
|
46
|
+
const fetch = (await import('node-fetch')).default;
|
|
47
|
+
const response = await fetch(`http://localhost:${options.port}/api/health`);
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
|
|
50
|
+
if (data.status === 'ok') {
|
|
51
|
+
console.log(chalk.green('✓ Server is running'));
|
|
52
|
+
console.log(` Version: ${data.version}`);
|
|
53
|
+
console.log(` Emergency Stop: ${data.emergencyStop ? chalk.red('ACTIVE') : chalk.green('inactive')}`);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.log(chalk.red('✗ Server is not running'));
|
|
57
|
+
console.log(chalk.gray(` Could not connect to port ${options.port}`));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Skill - Template Commands
|
|
3
|
+
*
|
|
4
|
+
* Manage and execute payment templates
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { templateEngine } from '../core/template-engine';
|
|
10
|
+
|
|
11
|
+
export const templateCommands = new Command('template')
|
|
12
|
+
.description('Payment templates - HYBRID ARCHITECTURE');
|
|
13
|
+
|
|
14
|
+
templateCommands
|
|
15
|
+
.command('list')
|
|
16
|
+
.description('List available templates')
|
|
17
|
+
.option('-m, --merchant <merchant>', 'Filter by merchant (wise.com, bunq.com, stripe.com)')
|
|
18
|
+
.action((options) => {
|
|
19
|
+
let templates = templateEngine.getAllTemplates();
|
|
20
|
+
|
|
21
|
+
if (options.merchant) {
|
|
22
|
+
templates = templates.filter(t => t.merchant === options.merchant);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(chalk.blue(`Available Templates (${templates.length}):\n`));
|
|
26
|
+
|
|
27
|
+
templates.forEach(t => {
|
|
28
|
+
console.log(chalk.green(`${t.templateId}`));
|
|
29
|
+
console.log(` Merchant: ${t.merchant}`);
|
|
30
|
+
console.log(` Description: ${t.description}`);
|
|
31
|
+
console.log(` Version: ${t.version}`);
|
|
32
|
+
console.log(` Steps: ${t.steps.length}`);
|
|
33
|
+
|
|
34
|
+
if (t.prerequisites.apiKey === 'required') {
|
|
35
|
+
console.log(chalk.yellow(' ⚠️ Requires API key'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log('');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
console.log(chalk.gray('Use "payment-skill template get <id>" for details'));
|
|
42
|
+
console.log(chalk.gray('Use "payment-skill pay --template <id> ..." to execute'));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
templateCommands
|
|
46
|
+
.command('get')
|
|
47
|
+
.description('Get template details')
|
|
48
|
+
.argument('<template-id>', 'Template ID')
|
|
49
|
+
.action((templateId) => {
|
|
50
|
+
const template = templateEngine.getTemplate(templateId);
|
|
51
|
+
|
|
52
|
+
if (!template) {
|
|
53
|
+
console.log(chalk.yellow(`Template '${templateId}' not found`));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(chalk.blue('Template Details:'));
|
|
58
|
+
console.log(` ID: ${template.templateId}`);
|
|
59
|
+
console.log(` Merchant: ${template.merchant}`);
|
|
60
|
+
console.log(` Description: ${template.description}`);
|
|
61
|
+
console.log(` Version: ${template.version}`);
|
|
62
|
+
|
|
63
|
+
console.log(chalk.blue('\nPrerequisites:'));
|
|
64
|
+
console.log(` API Key: ${template.prerequisites.apiKey}`);
|
|
65
|
+
if (template.prerequisites.webhookEndpoint) {
|
|
66
|
+
console.log(` Webhook: ${template.prerequisites.webhookEndpoint}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.blue('\nExecution Steps:'));
|
|
70
|
+
template.steps.sort((a, b) => a.order - b.order).forEach(step => {
|
|
71
|
+
console.log(`\n ${step.order}. ${step.name}`);
|
|
72
|
+
console.log(` Command: ${step.command}`);
|
|
73
|
+
|
|
74
|
+
if (Object.keys(step.params).length > 0) {
|
|
75
|
+
console.log(' Parameters:');
|
|
76
|
+
Object.entries(step.params).forEach(([key, value]) => {
|
|
77
|
+
console.log(` ${key}: ${value}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (step.async) {
|
|
82
|
+
console.log(chalk.yellow(' [ASYNC]'));
|
|
83
|
+
if (step.confirmation) {
|
|
84
|
+
console.log(` Confirmation: ${step.confirmation.type}`);
|
|
85
|
+
if (step.confirmation.timeout) {
|
|
86
|
+
console.log(` Timeout: ${step.confirmation.timeout}s`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (step.output) {
|
|
92
|
+
console.log(' Outputs:');
|
|
93
|
+
Object.entries(step.output).forEach(([key, path]) => {
|
|
94
|
+
console.log(` ${key} -> ${path}`);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log(chalk.blue('\nError Handling:'));
|
|
100
|
+
console.log(` Retry on: ${template.errorHandling.retryOn.join(', ')}`);
|
|
101
|
+
console.log(` Max retries: ${template.errorHandling.maxRetries}`);
|
|
102
|
+
if (template.errorHandling.fallback) {
|
|
103
|
+
console.log(` Fallback: ${template.errorHandling.fallback}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(chalk.blue('\nExample Usage:'));
|
|
107
|
+
console.log(chalk.gray(` payment-skill pay --template ${templateId} --amount 100 --currency EUR ...`));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
templateCommands
|
|
111
|
+
.command('validate')
|
|
112
|
+
.description('Validate template parameters without executing')
|
|
113
|
+
.requiredOption('-t, --template <id>', 'Template ID')
|
|
114
|
+
.option('--amount <amount>', 'Payment amount')
|
|
115
|
+
.option('--currency <currency>', 'Currency code')
|
|
116
|
+
.option('--profile-id <id>', 'Wise profile ID')
|
|
117
|
+
.option('--user-id <id>', 'Bunq user ID')
|
|
118
|
+
.option('--account-id <id>', 'Bunq account ID')
|
|
119
|
+
.option('--recipient-id <id>', 'Recipient ID')
|
|
120
|
+
.option('--recipient-iban <iban>', 'Recipient IBAN')
|
|
121
|
+
.option('--recipient-name <name>', 'Recipient name')
|
|
122
|
+
.action((options) => {
|
|
123
|
+
const template = templateEngine.getTemplate(options.template);
|
|
124
|
+
|
|
125
|
+
if (!template) {
|
|
126
|
+
console.log(chalk.red(`Template '${options.template}' not found`));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(chalk.blue('Validating parameters for template:'), options.template);
|
|
131
|
+
|
|
132
|
+
// Build params
|
|
133
|
+
const params: Record<string, any> = {};
|
|
134
|
+
if (options.amount) params.amount = options.amount;
|
|
135
|
+
if (options.currency) params.currency = options.currency;
|
|
136
|
+
if (options.profileId) params.profileId = options.profileId;
|
|
137
|
+
if (options.userId) params.userId = options.userId;
|
|
138
|
+
if (options.accountId) params.accountId = options.accountId;
|
|
139
|
+
if (options.recipientId) params.recipientId = options.recipientId;
|
|
140
|
+
if (options.recipientIban) params.recipientIban = options.recipientIban;
|
|
141
|
+
if (options.recipientName) params.recipientName = options.recipientName;
|
|
142
|
+
|
|
143
|
+
// Check for missing required params
|
|
144
|
+
const requiredParams = new Set<string>();
|
|
145
|
+
template.steps.forEach(step => {
|
|
146
|
+
Object.values(step.params).forEach(value => {
|
|
147
|
+
if (typeof value === 'string') {
|
|
148
|
+
const matches = value.match(/\{\{(\w+)\}\}/g);
|
|
149
|
+
if (matches) {
|
|
150
|
+
matches.forEach(match => {
|
|
151
|
+
const paramName = match.replace(/\{\{|\}\}/g, '');
|
|
152
|
+
requiredParams.add(paramName);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const missing = Array.from(requiredParams).filter(p => !params[p]);
|
|
160
|
+
|
|
161
|
+
if (missing.length > 0) {
|
|
162
|
+
console.log(chalk.red('\n❌ Missing required parameters:'));
|
|
163
|
+
missing.forEach(p => console.log(` - ${p}`));
|
|
164
|
+
} else {
|
|
165
|
+
console.log(chalk.green('\n✓ All required parameters provided'));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log(chalk.blue('\nProvided parameters:'));
|
|
169
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
170
|
+
console.log(` ${key}: ${value}`);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Skill - Transaction Commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { transactionManager } from '../core/transaction';
|
|
8
|
+
|
|
9
|
+
export const transactionCommands = new Command('transaction')
|
|
10
|
+
.description('Manage transactions');
|
|
11
|
+
|
|
12
|
+
transactionCommands
|
|
13
|
+
.command('list')
|
|
14
|
+
.description('List transactions')
|
|
15
|
+
.option('-s, --status <status>', 'Filter by status')
|
|
16
|
+
.option('-p, --provider <provider>', 'Filter by provider')
|
|
17
|
+
.option('-m, --merchant <merchant>', 'Filter by merchant')
|
|
18
|
+
.option('--from <date>', 'From date (YYYY-MM-DD)')
|
|
19
|
+
.option('--to <date>', 'To date (YYYY-MM-DD)')
|
|
20
|
+
.action((options) => {
|
|
21
|
+
const filters: any = {};
|
|
22
|
+
if (options.status) filters.status = options.status;
|
|
23
|
+
if (options.provider) filters.provider = options.provider;
|
|
24
|
+
if (options.merchant) filters.merchant = options.merchant;
|
|
25
|
+
if (options.from) filters.from = new Date(options.from);
|
|
26
|
+
if (options.to) filters.to = new Date(options.to);
|
|
27
|
+
|
|
28
|
+
const txs = transactionManager.getTransactions(filters);
|
|
29
|
+
console.log(chalk.blue(`Transactions (${txs.length}):`));
|
|
30
|
+
txs.forEach(tx => {
|
|
31
|
+
const statusColor = tx.status === 'completed' ? chalk.green :
|
|
32
|
+
tx.status === 'pending' ? chalk.yellow : chalk.red;
|
|
33
|
+
console.log(` ${tx.id} - ${statusColor(tx.status)} - ${tx.amount} ${tx.currency} - ${tx.merchant}`);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
transactionCommands
|
|
38
|
+
.command('get')
|
|
39
|
+
.description('Get transaction details')
|
|
40
|
+
.argument('<id>', 'Transaction ID')
|
|
41
|
+
.action((id) => {
|
|
42
|
+
const tx = transactionManager.getTransaction(id);
|
|
43
|
+
if (tx) {
|
|
44
|
+
console.log(JSON.stringify(tx, null, 2));
|
|
45
|
+
} else {
|
|
46
|
+
console.log(chalk.yellow(`Transaction '${id}' not found`));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
transactionCommands
|
|
51
|
+
.command('cancel')
|
|
52
|
+
.description('Cancel a transaction')
|
|
53
|
+
.argument('<id>', 'Transaction ID')
|
|
54
|
+
.action((id) => {
|
|
55
|
+
transactionManager.updateTransactionStatus(id, 'cancelled');
|
|
56
|
+
console.log(chalk.green(`✓ Transaction ${id} cancelled`));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
transactionCommands
|
|
60
|
+
.command('delete')
|
|
61
|
+
.description('Delete a transaction')
|
|
62
|
+
.argument('<id>', 'Transaction ID')
|
|
63
|
+
.action((id) => {
|
|
64
|
+
transactionManager.deleteTransaction(id);
|
|
65
|
+
console.log(chalk.green(`✓ Transaction ${id} deleted`));
|
|
66
|
+
});
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Skill - Wise Commands
|
|
3
|
+
*
|
|
4
|
+
* Wise API CLI commands
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import { WiseClient } from '../api/wise';
|
|
11
|
+
import { configManager } from '../core/config';
|
|
12
|
+
import { transactionManager } from '../core/transaction';
|
|
13
|
+
|
|
14
|
+
export const wiseCommands = new Command('wise')
|
|
15
|
+
.description('Wise API operations');
|
|
16
|
+
|
|
17
|
+
// Balance commands
|
|
18
|
+
wiseCommands
|
|
19
|
+
.command('balance')
|
|
20
|
+
.description('Get Wise account balances')
|
|
21
|
+
.option('-p, --profile <id>', 'Profile ID')
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
const spinner = ora('Fetching balances...').start();
|
|
24
|
+
try {
|
|
25
|
+
const config = configManager.getProvider('wise');
|
|
26
|
+
if (!config) {
|
|
27
|
+
throw new Error('Wise not configured. Run: payment-skill provider add wise');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const client = new WiseClient(config as any);
|
|
31
|
+
const profileId = options.profile || config.profileId;
|
|
32
|
+
|
|
33
|
+
if (!profileId) {
|
|
34
|
+
throw new Error('Profile ID required. Use --profile or set default');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const balances = await client.getBalances(profileId);
|
|
38
|
+
spinner.stop();
|
|
39
|
+
|
|
40
|
+
console.log(chalk.blue('Wise Balances:'));
|
|
41
|
+
balances.forEach((balance: any) => {
|
|
42
|
+
console.log(` ${balance.currency}: ${balance.amount.value}`);
|
|
43
|
+
});
|
|
44
|
+
} catch (error: any) {
|
|
45
|
+
spinner.fail(error.message);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Quote commands
|
|
51
|
+
wiseCommands
|
|
52
|
+
.command('quote')
|
|
53
|
+
.description('Create a quote (lock exchange rate)')
|
|
54
|
+
.requiredOption('-s, --source <currency>', 'Source currency')
|
|
55
|
+
.requiredOption('-t, --target <currency>', 'Target currency')
|
|
56
|
+
.option('-a, --amount <amount>', 'Source amount')
|
|
57
|
+
.option('-p, --profile <id>', 'Profile ID')
|
|
58
|
+
.action(async (options) => {
|
|
59
|
+
const spinner = ora('Creating quote...').start();
|
|
60
|
+
try {
|
|
61
|
+
const config = configManager.getProvider('wise');
|
|
62
|
+
if (!config) {
|
|
63
|
+
throw new Error('Wise not configured');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const client = new WiseClient(config as any);
|
|
67
|
+
const profileId = options.profile || config.profileId;
|
|
68
|
+
|
|
69
|
+
const quote = await client.createQuote(
|
|
70
|
+
profileId,
|
|
71
|
+
options.source,
|
|
72
|
+
options.target,
|
|
73
|
+
parseFloat(options.amount)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
spinner.stop();
|
|
77
|
+
console.log(chalk.green('✓ Quote created'));
|
|
78
|
+
console.log(` ID: ${quote.id}`);
|
|
79
|
+
console.log(` Rate: ${quote.rate}`);
|
|
80
|
+
console.log(` Fee: ${quote.fee}`);
|
|
81
|
+
console.log(` Total: ${quote.sourceAmount}`);
|
|
82
|
+
} catch (error: any) {
|
|
83
|
+
spinner.fail(error.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Transfer commands
|
|
89
|
+
wiseCommands
|
|
90
|
+
.command('transfer')
|
|
91
|
+
.description('Create a transfer')
|
|
92
|
+
.requiredOption('-q, --quote <id>', 'Quote ID')
|
|
93
|
+
.requiredOption('-r, --recipient <id>', 'Recipient account ID')
|
|
94
|
+
.option('-m, --reference <text>', 'Payment reference')
|
|
95
|
+
.option('-p, --profile <id>', 'Profile ID')
|
|
96
|
+
.action(async (options) => {
|
|
97
|
+
const spinner = ora('Creating transfer...').start();
|
|
98
|
+
try {
|
|
99
|
+
const config = configManager.getProvider('wise');
|
|
100
|
+
if (!config) {
|
|
101
|
+
throw new Error('Wise not configured');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Create transaction record
|
|
105
|
+
const tx = transactionManager.createTransaction(
|
|
106
|
+
'wise',
|
|
107
|
+
'wise-transfer',
|
|
108
|
+
0, // Will update with actual amount
|
|
109
|
+
'EUR',
|
|
110
|
+
{ quoteId: options.quote, recipientId: options.recipient }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const client = new WiseClient(config as any);
|
|
114
|
+
const profileId = options.profile || config.profileId;
|
|
115
|
+
|
|
116
|
+
const transfer = await client.createTransfer(
|
|
117
|
+
profileId,
|
|
118
|
+
options.quote,
|
|
119
|
+
options.recipient,
|
|
120
|
+
options.reference
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
transactionManager.updateTransactionStatus(tx.id, 'initiated');
|
|
124
|
+
|
|
125
|
+
spinner.stop();
|
|
126
|
+
console.log(chalk.green('✓ Transfer created'));
|
|
127
|
+
console.log(` Transaction ID: ${tx.id}`);
|
|
128
|
+
console.log(` Transfer ID: ${transfer.id}`);
|
|
129
|
+
console.log(` Status: ${transfer.status}`);
|
|
130
|
+
|
|
131
|
+
if (transfer.status === 'pending') {
|
|
132
|
+
console.log(chalk.yellow('\n⚠️ Transfer requires funding'));
|
|
133
|
+
console.log(`Run: payment-skill wise fund ${transfer.id}`);
|
|
134
|
+
}
|
|
135
|
+
} catch (error: any) {
|
|
136
|
+
spinner.fail(error.message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Fund transfer
|
|
142
|
+
wiseCommands
|
|
143
|
+
.command('fund')
|
|
144
|
+
.description('Fund a transfer from balance')
|
|
145
|
+
.argument('<transferId>', 'Transfer ID to fund')
|
|
146
|
+
.option('-p, --profile <id>', 'Profile ID')
|
|
147
|
+
.action(async (transferId, options) => {
|
|
148
|
+
const spinner = ora('Funding transfer...').start();
|
|
149
|
+
try {
|
|
150
|
+
const config = configManager.getProvider('wise');
|
|
151
|
+
if (!config) {
|
|
152
|
+
throw new Error('Wise not configured');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const client = new WiseClient(config as any);
|
|
156
|
+
const profileId = options.profile || config.profileId;
|
|
157
|
+
|
|
158
|
+
const payment = await client.fundTransfer(profileId, transferId);
|
|
159
|
+
|
|
160
|
+
spinner.stop();
|
|
161
|
+
console.log(chalk.green('✓ Transfer funded'));
|
|
162
|
+
console.log(` Status: ${payment.status}`);
|
|
163
|
+
|
|
164
|
+
if (payment.status === 'pending') {
|
|
165
|
+
console.log(chalk.yellow('\n⚠️ PSD2: Open Wise app to confirm'));
|
|
166
|
+
}
|
|
167
|
+
} catch (error: any) {
|
|
168
|
+
spinner.fail(error.message);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// List transfers
|
|
174
|
+
wiseCommands
|
|
175
|
+
.command('list')
|
|
176
|
+
.description('List transfers')
|
|
177
|
+
.option('-p, --profile <id>', 'Profile ID')
|
|
178
|
+
.option('-s, --status <status>', 'Filter by status')
|
|
179
|
+
.option('-l, --limit <number>', 'Limit results', '10')
|
|
180
|
+
.action(async (options) => {
|
|
181
|
+
const spinner = ora('Fetching transfers...').start();
|
|
182
|
+
try {
|
|
183
|
+
const config = configManager.getProvider('wise');
|
|
184
|
+
if (!config) {
|
|
185
|
+
throw new Error('Wise not configured');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const client = new WiseClient(config as any);
|
|
189
|
+
const profileId = options.profile || config.profileId;
|
|
190
|
+
|
|
191
|
+
const transfers = await client.getTransfers(
|
|
192
|
+
profileId,
|
|
193
|
+
options.status,
|
|
194
|
+
parseInt(options.limit)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
spinner.stop();
|
|
198
|
+
console.log(chalk.blue(`Transfers (${transfers.length}):`));
|
|
199
|
+
transfers.forEach((t: any) => {
|
|
200
|
+
const statusColor = t.status === 'completed' ? chalk.green :
|
|
201
|
+
t.status === 'pending' ? chalk.yellow : chalk.red;
|
|
202
|
+
console.log(` ${t.id} - ${statusColor(t.status)} - ${t.sourceValue} ${t.sourceCurrency}`);
|
|
203
|
+
});
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
spinner.fail(error.message);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Get transfer status
|
|
211
|
+
wiseCommands
|
|
212
|
+
.command('status')
|
|
213
|
+
.description('Get transfer status')
|
|
214
|
+
.argument('<transferId>', 'Transfer ID')
|
|
215
|
+
.option('--poll', 'Poll until completion')
|
|
216
|
+
.option('-i, --interval <seconds>', 'Poll interval', '5')
|
|
217
|
+
.action(async (transferId, options) => {
|
|
218
|
+
try {
|
|
219
|
+
const config = configManager.getProvider('wise');
|
|
220
|
+
if (!config) {
|
|
221
|
+
throw new Error('Wise not configured');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const client = new WiseClient(config as any);
|
|
225
|
+
|
|
226
|
+
if (options.poll) {
|
|
227
|
+
console.log(chalk.blue(`Polling transfer ${transferId}...`));
|
|
228
|
+
const spinner = ora('Waiting...').start();
|
|
229
|
+
|
|
230
|
+
while (true) {
|
|
231
|
+
const transfer = await client.getTransfer(transferId);
|
|
232
|
+
spinner.text = `Status: ${transfer.status}`;
|
|
233
|
+
|
|
234
|
+
if (transfer.status === 'completed') {
|
|
235
|
+
spinner.succeed(chalk.green(`Transfer completed!`));
|
|
236
|
+
break;
|
|
237
|
+
} else if (transfer.status === 'cancelled' || transfer.status === 'failed') {
|
|
238
|
+
spinner.fail(`Transfer ${transfer.status}`);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await new Promise(r => setTimeout(r, parseInt(options.interval) * 1000));
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
const transfer = await client.getTransfer(transferId);
|
|
246
|
+
console.log(chalk.blue('Transfer Status:'));
|
|
247
|
+
console.log(` ID: ${transfer.id}`);
|
|
248
|
+
console.log(` Status: ${transfer.status}`);
|
|
249
|
+
console.log(` Amount: ${transfer.sourceValue} ${transfer.sourceCurrency}`);
|
|
250
|
+
console.log(` Created: ${transfer.created}`);
|
|
251
|
+
}
|
|
252
|
+
} catch (error: any) {
|
|
253
|
+
console.error(chalk.red('Error:'), error.message);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Cancel transfer
|
|
259
|
+
wiseCommands
|
|
260
|
+
.command('cancel')
|
|
261
|
+
.description('Cancel a pending transfer')
|
|
262
|
+
.argument('<transferId>', 'Transfer ID to cancel')
|
|
263
|
+
.action(async (transferId) => {
|
|
264
|
+
const spinner = ora('Cancelling transfer...').start();
|
|
265
|
+
try {
|
|
266
|
+
const config = configManager.getProvider('wise');
|
|
267
|
+
if (!config) {
|
|
268
|
+
throw new Error('Wise not configured');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const client = new WiseClient(config as any);
|
|
272
|
+
await client.cancelTransfer(transferId);
|
|
273
|
+
|
|
274
|
+
spinner.succeed(chalk.green('Transfer cancelled'));
|
|
275
|
+
} catch (error: any) {
|
|
276
|
+
spinner.fail(error.message);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
});
|