payment-skill 1.1.17 → 1.1.19

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/src/cli.ts CHANGED
@@ -24,7 +24,7 @@ const program = new Command();
24
24
 
25
25
  program
26
26
  .name('payment-skill')
27
- .description('Self-hosted payment orchestration for OpenClaw')
27
+ .description('Self-hosted payment skill for OpenClaw')
28
28
  .version('1.0.0')
29
29
  .option('-v, --verbose', 'Enable verbose output')
30
30
  .option('-j, --json', 'Output in JSON format')
@@ -14,8 +14,45 @@ export const configCommands = new Command('config')
14
14
  configCommands
15
15
  .command('get')
16
16
  .description('Get configuration value')
17
- .argument('<key>', 'Configuration key')
17
+ .argument('<key>', 'Configuration key (e.g., providers.wise.environment)')
18
18
  .action((key) => {
19
+ // Handle provider-specific keys
20
+ if (key.startsWith('providers.')) {
21
+ const parts = key.split('.');
22
+ const providerName = parts[1];
23
+ const providerKey = parts[2];
24
+
25
+ const provider = configManager.getProvider(providerName);
26
+ if (provider && providerKey) {
27
+ const value = (provider as any)[providerKey];
28
+ if (value !== undefined) {
29
+ console.log(JSON.stringify(value, null, 2));
30
+ return;
31
+ }
32
+ }
33
+ console.log(chalk.yellow(`Key '${key}' not found`));
34
+ return;
35
+ }
36
+
37
+ // Handle nested keys
38
+ if (key.includes('.')) {
39
+ const parts = key.split('.');
40
+ const config = configManager.getConfig();
41
+ let current = config;
42
+
43
+ for (const part of parts) {
44
+ if (current && typeof current === 'object' && part in current) {
45
+ current = current[part];
46
+ } else {
47
+ console.log(chalk.yellow(`Key '${key}' not found`));
48
+ return;
49
+ }
50
+ }
51
+ console.log(JSON.stringify(current, null, 2));
52
+ return;
53
+ }
54
+
55
+ // Simple key
19
56
  const config = configManager.getConfig();
20
57
  const value = config[key];
21
58
  if (value !== undefined) {
@@ -31,39 +68,47 @@ configCommands
31
68
  .argument('<key>', 'Configuration key (e.g., providers.wise.environment)')
32
69
  .argument('<value>', 'Configuration value')
33
70
  .action((key, value) => {
34
- const setNestedValue = (obj: any, keys: string[], val: any) => {
35
- let current = obj;
36
- for (let i = 0; i < keys.length - 1; i++) {
37
- if (!current[keys[i]]) current[keys[i]] = {};
38
- current = current[keys[i]];
39
- }
40
- current[keys[keys.length - 1]] = val;
41
- };
42
-
71
+ // Parse the value (try JSON first, then use as string)
72
+ let parsedValue: any;
43
73
  try {
44
- const parsedValue = JSON.parse(value);
45
- const config = configManager.getConfig();
74
+ parsedValue = JSON.parse(value);
75
+ } catch {
76
+ parsedValue = value;
77
+ }
78
+
79
+ // Handle nested keys like providers.wise.environment
80
+ if (key.startsWith('providers.')) {
81
+ const parts = key.split('.');
82
+ const providerName = parts[1];
83
+ const providerKey = parts[2];
46
84
 
47
- if (key.includes('.')) {
48
- const parts = key.split('.');
49
- setNestedValue(config, parts, parsedValue);
50
- configManager.setConfig(parts[0], config[parts[0]]);
51
- } else {
52
- configManager.setConfig(key, parsedValue);
53
- }
85
+ const provider = configManager.getProvider(providerName) || { name: providerName };
86
+ (provider as any)[providerKey] = parsedValue;
87
+ configManager.setProvider(providerName, provider as any);
54
88
  console.log(chalk.green(`✓ Set ${key} = ${value}`));
55
- } catch {
89
+ return;
90
+ }
91
+
92
+ // Handle other nested keys
93
+ if (key.includes('.')) {
94
+ const parts = key.split('.');
56
95
  const config = configManager.getConfig();
96
+ let current = config;
57
97
 
58
- if (key.includes('.')) {
59
- const parts = key.split('.');
60
- setNestedValue(config, parts, value);
61
- configManager.setConfig(parts[0], config[parts[0]]);
62
- } else {
63
- configManager.setConfig(key, value);
98
+ for (let i = 0; i < parts.length - 1; i++) {
99
+ if (!current[parts[i]]) current[parts[i]] = {};
100
+ current = current[parts[i]];
64
101
  }
102
+ current[parts[parts.length - 1]] = parsedValue;
103
+
104
+ configManager.setConfig(parts[0], config[parts[0]]);
65
105
  console.log(chalk.green(`✓ Set ${key} = ${value}`));
106
+ return;
66
107
  }
108
+
109
+ // Simple key
110
+ configManager.setConfig(key, parsedValue);
111
+ console.log(chalk.green(`✓ Set ${key} = ${value}`));
67
112
  });
68
113
 
69
114
  configCommands
@@ -113,11 +113,16 @@ wiseCommands
113
113
  const client = new WiseClient(config as any);
114
114
  const profileId = options.profile || config.profileId;
115
115
 
116
+ const txnId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
117
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
118
+ return v.toString(16);
119
+ });
116
120
  const transfer = await client.createTransfer(
117
121
  profileId,
118
122
  options.quote,
119
123
  options.recipient,
120
- options.reference
124
+ options.reference,
125
+ txnId
121
126
  );
122
127
 
123
128
  transactionManager.updateTransactionStatus(tx.id, 'initiated');
@@ -170,6 +175,43 @@ wiseCommands
170
175
  }
171
176
  });
172
177
 
178
+ // Create recipient
179
+ wiseCommands
180
+ .command('create-recipient')
181
+ .description('Create a recipient account')
182
+ .requiredOption('-n, --name <name>', 'Account holder name')
183
+ .requiredOption('-c, --currency <currency>', 'Currency (EUR, USD, etc)')
184
+ .requiredOption('--iban <iban>', 'IBAN number')
185
+ .option('-p, --profile <id>', 'Profile ID')
186
+ .action(async (options) => {
187
+ const spinner = ora('Creating recipient...').start();
188
+ try {
189
+ const config = configManager.getProvider('wise');
190
+ if (!config) {
191
+ throw new Error('Wise not configured');
192
+ }
193
+
194
+ const client = new WiseClient(config as any);
195
+ const profileId = options.profile || config.profileId;
196
+
197
+ const recipient = await client.createRecipient(
198
+ profileId,
199
+ options.currency,
200
+ options.name,
201
+ { iban: options.iban }
202
+ );
203
+
204
+ spinner.stop();
205
+ console.log(chalk.green('✓ Recipient created'));
206
+ console.log(` ID: ${recipient.id}`);
207
+ console.log(` Name: ${recipient.accountHolderName}`);
208
+ console.log(` IBAN: ${recipient.details.iban}`);
209
+ } catch (error: any) {
210
+ spinner.fail(error.message);
211
+ process.exit(1);
212
+ }
213
+ });
214
+
173
215
  // List transfers
174
216
  wiseCommands
175
217
  .command('list')
@@ -15,7 +15,15 @@ import {
15
15
  BunqConfig
16
16
  } from '../types';
17
17
 
18
- const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.payment-skill');
18
+ // Support both old location (~/.payment-skill) and new location (./data)
19
+ const OLD_CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.payment-skill');
20
+ const NEW_CONFIG_DIR = path.join(__dirname, '..', '..', 'data');
21
+
22
+ // Use new location if it exists and has config, otherwise fall back to old location
23
+ const CONFIG_DIR = fs.existsSync(path.join(NEW_CONFIG_DIR, 'config.json'))
24
+ ? NEW_CONFIG_DIR
25
+ : (fs.existsSync(OLD_CONFIG_DIR) ? OLD_CONFIG_DIR : NEW_CONFIG_DIR);
26
+
19
27
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
20
28
  const EMERGENCY_FILE = path.join(CONFIG_DIR, 'emergency.json');
21
29
 
@@ -9,7 +9,15 @@ import * as path from 'path';
9
9
  import { Transaction, TransactionStatus } from '../types';
10
10
  import { configManager } from './config';
11
11
 
12
- const DATA_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.payment-skill');
12
+ // Support both old location (~/.payment-skill) and new location (./data)
13
+ const OLD_DATA_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.payment-skill');
14
+ const NEW_DATA_DIR = path.join(__dirname, '..', '..', 'data');
15
+
16
+ // Use new location if it exists and has config, otherwise fall back to old location
17
+ const DATA_DIR = fs.existsSync(path.join(NEW_DATA_DIR, 'config.json'))
18
+ ? NEW_DATA_DIR
19
+ : (fs.existsSync(OLD_DATA_DIR) ? OLD_DATA_DIR : NEW_DATA_DIR);
20
+
13
21
  const TRANSACTIONS_FILE = path.join(DATA_DIR, 'transactions.json');
14
22
 
15
23
  export class TransactionManager {
@@ -8,6 +8,7 @@ import express from 'express';
8
8
  import cors from 'cors';
9
9
  import helmet from 'helmet';
10
10
  import path from 'path';
11
+ import * as fs from 'fs-extra';
11
12
  import { configManager } from '../core/config';
12
13
  import { transactionManager } from '../core/transaction';
13
14
  import { fetchAndSaveWiseProfile } from '../api/wise-profile';
@@ -16,7 +17,7 @@ export class PaymentSkillServer {
16
17
  private app: express.Application;
17
18
  private port: number;
18
19
 
19
- constructor(port: number = 8080) {
20
+ constructor(port: number = 18790) {
20
21
  this.app = express();
21
22
  this.port = port;
22
23
  this.setupMiddleware();
@@ -250,6 +251,24 @@ export class PaymentSkillServer {
250
251
  res.status(200).send('OK');
251
252
  });
252
253
 
254
+ // Verified Merchants
255
+ this.app.get('/api/verified-merchants', (req, res) => {
256
+ try {
257
+ const merchantsPath = path.join(__dirname, '../../verified-merchants.json');
258
+ console.log('Looking for verified merchants at:', merchantsPath);
259
+ if (fs.existsSync(merchantsPath)) {
260
+ const data = fs.readJsonSync(merchantsPath);
261
+ res.json(data);
262
+ } else {
263
+ console.log('File not found, returning empty list');
264
+ res.json({ merchants: [], version: '1.0.0' });
265
+ }
266
+ } catch (error: any) {
267
+ console.error('Error loading verified merchants:', error.message);
268
+ res.status(500).json({ error: 'Failed to load verified merchants', details: error.message });
269
+ }
270
+ });
271
+
253
272
  // Dashboard route - serve dashboard.html
254
273
  this.app.get('/', (req, res) => {
255
274
  res.sendFile(path.join(__dirname, '../../dashboard.html'));
@@ -270,7 +289,7 @@ export class PaymentSkillServer {
270
289
 
271
290
  // Start server if run directly
272
291
  if (require.main === module) {
273
- const port = parseInt(process.env.PORT || '8080');
292
+ const port = parseInt(process.env.PORT || '18790');
274
293
  const server = new PaymentSkillServer(port);
275
294
  server.start();
276
295
  }
@@ -38,8 +38,8 @@
38
38
  },
39
39
  {
40
40
  "id": "clickclack-market",
41
- "name": "ClickClack Market",
42
- "domains": ["clickclack.market"],
41
+ "name": "ManyClaws",
42
+ "domains": ["manyclaws.store"],
43
43
  "categories": ["marketplace", "shopping"],
44
44
  "verified": true,
45
45
  "riskLevel": "low",
@@ -1,8 +0,0 @@
1
- {\rtf1\ansi\ansicpg1252\cocoartf2822
2
- \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3
- {\colortbl;\red255\green255\blue255;}
4
- {\*\expandedcolortbl;;}
5
- \paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
6
- \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
7
-
8
- \f0\fs24 \cf0 53704ee2-f996-4081-9a29-4f094f6744d2}
@@ -1,80 +0,0 @@
1
- #!/bin/bash
2
- cd /Users/apple/.openclaw/workspace/payment-skill-app
3
-
4
- # Fix 1: wise.ts - Add customerTransactionId
5
- cat > /tmp/fix_wise.py << 'PYEOF'
6
- import re
7
- with open('src/api/wise.ts', 'r') as f:
8
- content = f.read()
9
-
10
- # Fix createTransfer
11
- content = content.replace(
12
- '''async createTransfer(
13
- profileId: string,
14
- quoteId: string,
15
- targetAccountId: string,
16
- reference?: string
17
- ) {
18
- const body: any = {
19
- targetAccount: targetAccountId,
20
- quoteUuid: quoteId
21
- };''',
22
- '''async createTransfer(
23
- profileId: string,
24
- quoteId: string,
25
- targetAccountId: string,
26
- reference?: string,
27
- customerTransactionId?: string
28
- ) {
29
- const body: any = {
30
- targetAccount: targetAccountId,
31
- quoteUuid: quoteId,
32
- customerTransactionId: customerTransactionId or f"txn-{int(time.time())}"
33
- };'''
34
- )
35
-
36
- # Fix fundTransfer URL
37
- content = content.replace(
38
- '/v3/profiles/${profileId}/transfers/${transferId}/payments',
39
- '/v1/transfers/${transferId}/payments'
40
- )
41
-
42
- with open('src/api/wise.ts', 'w') as f:
43
- f.write(content)
44
- print("Fixed wise.ts")
45
- PYEOF
46
-
47
- python3 /tmp/fix_wise.py
48
-
49
- # Fix 2: Add updateProvider to config.ts
50
- cat > /tmp/fix_config.py << 'PYEOF'
51
- with open('src/core/config.ts', 'r') as f:
52
- content = f.read()
53
-
54
- if 'updateProvider' not in content:
55
- method = '''
56
- // Update provider without overwriting
57
- updateProvider(name: string, updates: Partial<ProviderConfig>): void {
58
- const current = this.config.providers[name];
59
- if (!current) throw new Error(`Provider ${name} not found`);
60
- this.config.providers[name] = { ...current, ...updates };
61
- this.saveConfig();
62
- }'''
63
-
64
- # Insert before last closing brace
65
- pos = content.rfind('}')
66
- content = content[:pos] + method + '\n' + content[pos:]
67
-
68
- with open('src/core/config.ts', 'w') as f:
69
- f.write(content)
70
- print("Fixed config.ts")
71
- else:
72
- print("Already fixed")
73
- PYEOF
74
-
75
- python3 /tmp/fix_config.py
76
-
77
- # Rebuild
78
- npm run build && npm link
79
-
80
- echo "✅ All fixes applied!"