@ktmcp-cli/nowpayments 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/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@ktmcp-cli/nowpayments",
3
+ "version": "1.0.0",
4
+ "description": "Production-ready CLI for NOWPayments cryptocurrency payment processing API",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "nowpayments": "./bin/nowpayments.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/nowpayments.js",
11
+ "test": "node --test",
12
+ "lint": "eslint ."
13
+ },
14
+ "keywords": [
15
+ "nowpayments",
16
+ "cryptocurrency",
17
+ "payment",
18
+ "cli",
19
+ "crypto",
20
+ "bitcoin",
21
+ "ethereum",
22
+ "commander"
23
+ ],
24
+ "author": "KTMCP",
25
+ "license": "MIT",
26
+ "engines": {
27
+ "node": ">=16.0.0"
28
+ },
29
+ "dependencies": {
30
+ "commander": "^12.0.0",
31
+ "chalk": "^4.1.2",
32
+ "ora": "^5.4.1",
33
+ "dotenv": "^16.4.1",
34
+ "node-fetch": "^2.7.0",
35
+ "cli-table3": "^0.6.3"
36
+ },
37
+ "devDependencies": {
38
+ "eslint": "^8.56.0"
39
+ }
40
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Authentication command
3
+ *
4
+ * Manage API keys and authentication configuration.
5
+ */
6
+
7
+ const { Command } = require('commander');
8
+ const chalk = require('chalk');
9
+ const { setConfig, getConfig, CONFIG_FILE } = require('../lib/config');
10
+ const { validateApiKeyFormat } = require('../lib/auth');
11
+
12
+ const command = new Command('auth');
13
+
14
+ command
15
+ .description('Manage API authentication')
16
+ .summary('Configure and manage API keys');
17
+
18
+ // Set API key
19
+ command
20
+ .command('set <api-key>')
21
+ .description('Save API key to config file')
22
+ .action((apiKey) => {
23
+ if (!validateApiKeyFormat(apiKey)) {
24
+ console.error(chalk.red('Error: Invalid API key format'));
25
+ console.error(chalk.yellow('API keys should be 20-100 alphanumeric characters'));
26
+ process.exit(1);
27
+ }
28
+
29
+ setConfig('apiKey', apiKey);
30
+ console.log(chalk.green('✓ API key saved successfully'));
31
+ console.log(chalk.gray(`Config file: ${CONFIG_FILE}`));
32
+ });
33
+
34
+ // Show current API key (masked)
35
+ command
36
+ .command('show')
37
+ .description('Display current API key configuration (masked)')
38
+ .action(() => {
39
+ const apiKey = getConfig('apiKey');
40
+
41
+ if (!apiKey) {
42
+ console.log(chalk.yellow('No API key configured'));
43
+ console.log(chalk.gray('\nSet an API key with: nowpayments auth set YOUR_KEY'));
44
+ return;
45
+ }
46
+
47
+ // Mask the API key (show first 4 and last 4 characters)
48
+ const masked = apiKey.length > 8
49
+ ? `${apiKey.substring(0, 4)}${'*'.repeat(apiKey.length - 8)}${apiKey.substring(apiKey.length - 4)}`
50
+ : '*'.repeat(apiKey.length);
51
+
52
+ console.log(chalk.cyan('API Key:'), masked);
53
+ console.log(chalk.gray(`Config file: ${CONFIG_FILE}`));
54
+ });
55
+
56
+ // Clear API key
57
+ command
58
+ .command('clear')
59
+ .description('Remove API key from config')
60
+ .action(() => {
61
+ setConfig('apiKey', null);
62
+ console.log(chalk.green('✓ API key cleared'));
63
+ });
64
+
65
+ module.exports = command;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Currencies command
3
+ *
4
+ * List and query available cryptocurrencies.
5
+ */
6
+
7
+ const { Command } = require('commander');
8
+ const chalk = require('chalk');
9
+ const Table = require('cli-table3');
10
+ const { makeRequest, handleApiError, displayResponse } = require('../lib/api');
11
+
12
+ const command = new Command('currencies');
13
+
14
+ command
15
+ .description('List available currencies')
16
+ .summary('Query supported cryptocurrencies');
17
+
18
+ // List all available currencies
19
+ command
20
+ .command('list')
21
+ .description('List all available cryptocurrencies')
22
+ .option('-a, --available', 'Show only available currencies for payments')
23
+ .option('-s, --selected', 'Show only selected currencies')
24
+ .action(async (options) => {
25
+ try {
26
+ let endpoint = '/currencies';
27
+
28
+ if (options.available) {
29
+ endpoint = '/merchant/coins';
30
+ } else if (options.selected) {
31
+ endpoint = '/currencies?fixed_rate=true';
32
+ }
33
+
34
+ const data = await makeRequest(command.parent.parent, endpoint);
35
+
36
+ displayResponse(command.parent.parent, data, (response) => {
37
+ const currencies = response.selectedCurrencies || response.currencies || [];
38
+
39
+ if (!currencies || currencies.length === 0) {
40
+ console.log(chalk.yellow('No currencies found'));
41
+ return;
42
+ }
43
+
44
+ console.log(chalk.cyan(`\nAvailable Currencies: ${currencies.length}`));
45
+
46
+ // Display as formatted list
47
+ const columns = 4;
48
+ for (let i = 0; i < currencies.length; i += columns) {
49
+ const row = currencies.slice(i, i + columns);
50
+ console.log(' ' + row.map(c => chalk.bold(c.toUpperCase())).join(' '));
51
+ }
52
+ });
53
+ } catch (error) {
54
+ handleApiError(error);
55
+ }
56
+ });
57
+
58
+ // Get currency details
59
+ command
60
+ .command('info <currency>')
61
+ .description('Get information about a specific currency')
62
+ .action(async (currency) => {
63
+ try {
64
+ // First get all currencies
65
+ const data = await makeRequest(command.parent.parent, '/currencies');
66
+
67
+ const currencies = data.currencies || [];
68
+ const currencyUpper = currency.toUpperCase();
69
+
70
+ if (currencies.includes(currencyUpper.toLowerCase())) {
71
+ console.log(chalk.cyan('\nCurrency:'), chalk.bold(currencyUpper));
72
+ console.log(chalk.green('✓ Supported by NOWPayments'));
73
+
74
+ // Get minimum amount
75
+ try {
76
+ const minData = await makeRequest(command.parent.parent, '/min-amount', {
77
+ queryParams: { currency_from: 'USD', currency_to: currencyUpper }
78
+ });
79
+
80
+ if (minData.min_amount) {
81
+ console.log(chalk.cyan('Minimum amount:'), minData.min_amount, currencyUpper);
82
+ }
83
+ } catch (e) {
84
+ // Minimum amount not available
85
+ }
86
+ } else {
87
+ console.log(chalk.red(`\n✗ Currency ${currencyUpper} is not supported`));
88
+ process.exit(1);
89
+ }
90
+ } catch (error) {
91
+ handleApiError(error);
92
+ }
93
+ });
94
+
95
+ module.exports = command;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Estimate command
3
+ *
4
+ * Calculate cryptocurrency price estimates and conversions.
5
+ */
6
+
7
+ const { Command } = require('commander');
8
+ const chalk = require('chalk');
9
+ const { makeRequest, handleApiError, displayResponse } = require('../lib/api');
10
+
11
+ const command = new Command('estimate');
12
+
13
+ command
14
+ .description('Estimate cryptocurrency amounts')
15
+ .summary('Calculate price conversions and estimates');
16
+
17
+ // Estimate conversion
18
+ command
19
+ .command('convert')
20
+ .description('Estimate cryptocurrency amount for a given price')
21
+ .requiredOption('-f, --from <currency>', 'Currency to convert from (e.g., USD, EUR)')
22
+ .requiredOption('-t, --to <currency>', 'Cryptocurrency to convert to (e.g., BTC, ETH)')
23
+ .requiredOption('-a, --amount <amount>', 'Amount to convert')
24
+ .action(async (options) => {
25
+ try {
26
+ const amount = parseFloat(options.amount);
27
+
28
+ if (isNaN(amount) || amount <= 0) {
29
+ console.error(chalk.red('Error: Amount must be a positive number'));
30
+ process.exit(1);
31
+ }
32
+
33
+ const data = await makeRequest(command.parent.parent, '/estimate', {
34
+ queryParams: {
35
+ amount,
36
+ currency_from: options.from.toUpperCase(),
37
+ currency_to: options.to.toUpperCase()
38
+ }
39
+ });
40
+
41
+ displayResponse(command.parent.parent, data, (response) => {
42
+ console.log(chalk.cyan('\nEstimate:'));
43
+ console.log(chalk.bold(` ${amount} ${options.from.toUpperCase()}`), '→',
44
+ chalk.green(`${response.estimated_amount || response.amount_to} ${options.to.toUpperCase()}`));
45
+
46
+ if (response.fee_amount) {
47
+ console.log(chalk.gray(` Network fee: ${response.fee_amount} ${options.to.toUpperCase()}`));
48
+ }
49
+ });
50
+ } catch (error) {
51
+ handleApiError(error);
52
+ }
53
+ });
54
+
55
+ // Get minimum amount
56
+ command
57
+ .command('min')
58
+ .description('Get minimum payment amount for a currency pair')
59
+ .requiredOption('-f, --from <currency>', 'Currency from (e.g., USD)')
60
+ .requiredOption('-t, --to <currency>', 'Cryptocurrency to (e.g., BTC)')
61
+ .option('--fiat', 'Get fiat equivalent of minimum')
62
+ .action(async (options) => {
63
+ try {
64
+ const data = await makeRequest(command.parent.parent, '/min-amount', {
65
+ queryParams: {
66
+ currency_from: options.from.toUpperCase(),
67
+ currency_to: options.to.toUpperCase(),
68
+ fiat_equivalent: options.fiat ? 'true' : undefined
69
+ }
70
+ });
71
+
72
+ displayResponse(command.parent.parent, data, (response) => {
73
+ console.log(chalk.cyan('\nMinimum Payment Amount:'));
74
+ console.log(chalk.bold(` ${response.min_amount} ${options.to.toUpperCase()}`));
75
+
76
+ if (response.fiat_equivalent) {
77
+ console.log(chalk.gray(` ≈ ${response.fiat_equivalent} ${options.from.toUpperCase()}`));
78
+ }
79
+ });
80
+ } catch (error) {
81
+ handleApiError(error);
82
+ }
83
+ });
84
+
85
+ module.exports = command;
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Invoice command
3
+ *
4
+ * Create and manage payment invoices.
5
+ */
6
+
7
+ const { Command } = require('commander');
8
+ const chalk = require('chalk');
9
+ const Table = require('cli-table3');
10
+ const { makeRequest, handleApiError, displayResponse } = require('../lib/api');
11
+
12
+ const command = new Command('invoice');
13
+
14
+ command
15
+ .description('Manage invoices')
16
+ .summary('Create and query payment invoices');
17
+
18
+ // Create a new invoice
19
+ command
20
+ .command('create')
21
+ .description('Create a new invoice')
22
+ .requiredOption('-p, --price <amount>', 'Invoice amount')
23
+ .requiredOption('-c, --currency <currency>', 'Price currency (e.g., USD)')
24
+ .option('--order-id <id>', 'Your order ID')
25
+ .option('--order-description <desc>', 'Order description')
26
+ .option('--ipn-url <url>', 'IPN callback URL')
27
+ .option('--success-url <url>', 'Success redirect URL')
28
+ .option('--cancel-url <url>', 'Cancel redirect URL')
29
+ .option('--partially-paid-url <url>', 'Partially paid redirect URL')
30
+ .option('--payout-currency <currency>', 'Payout currency')
31
+ .option('--payout-address <address>', 'Payout address')
32
+ .action(async (options) => {
33
+ try {
34
+ const price = parseFloat(options.price);
35
+
36
+ if (isNaN(price) || price <= 0) {
37
+ console.error(chalk.red('Error: Price must be a positive number'));
38
+ process.exit(1);
39
+ }
40
+
41
+ const invoiceData = {
42
+ price_amount: price,
43
+ price_currency: options.currency.toUpperCase(),
44
+ order_id: options.orderId,
45
+ order_description: options.orderDescription,
46
+ ipn_callback_url: options.ipnUrl,
47
+ success_url: options.successUrl,
48
+ cancel_url: options.cancelUrl,
49
+ partially_paid_url: options.partiallyPaidUrl,
50
+ payout_currency: options.payoutCurrency?.toUpperCase(),
51
+ payout_address: options.payoutAddress
52
+ };
53
+
54
+ // Remove undefined values
55
+ Object.keys(invoiceData).forEach(key => {
56
+ if (invoiceData[key] === undefined) {
57
+ delete invoiceData[key];
58
+ }
59
+ });
60
+
61
+ const data = await makeRequest(command.parent.parent, '/invoice', {
62
+ method: 'POST',
63
+ body: invoiceData
64
+ });
65
+
66
+ displayResponse(command.parent.parent, data, (response) => {
67
+ console.log(chalk.green('\n✓ Invoice created successfully'));
68
+ console.log(chalk.cyan('\nInvoice Details:'));
69
+ console.log(chalk.bold(' Invoice ID:'), response.id);
70
+ console.log(chalk.bold(' Status:'), response.invoice_status);
71
+ console.log(chalk.bold(' Price:'), `${response.price_amount} ${response.price_currency}`);
72
+ console.log(chalk.bold(' Invoice URL:'), chalk.yellow(response.invoice_url));
73
+
74
+ if (response.order_id) {
75
+ console.log(chalk.bold(' Order ID:'), response.order_id);
76
+ }
77
+
78
+ console.log(chalk.gray('\n Created:'), new Date(response.created_at).toLocaleString());
79
+ });
80
+ } catch (error) {
81
+ handleApiError(error);
82
+ }
83
+ });
84
+
85
+ // Get invoice details
86
+ command
87
+ .command('get <invoice-id>')
88
+ .description('Get invoice details by ID')
89
+ .action(async (invoiceId) => {
90
+ try {
91
+ const data = await makeRequest(command.parent.parent, `/invoice/${invoiceId}`);
92
+
93
+ displayResponse(command.parent.parent, data, (response) => {
94
+ console.log(chalk.cyan('\nInvoice Details:'));
95
+
96
+ const table = new Table({
97
+ chars: { 'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }
98
+ });
99
+
100
+ table.push(
101
+ ['Invoice ID', response.id],
102
+ ['Status', response.invoice_status],
103
+ ['Price', `${response.price_amount} ${response.price_currency}`],
104
+ ['Invoice URL', response.invoice_url]
105
+ );
106
+
107
+ if (response.order_id) {
108
+ table.push(['Order ID', response.order_id]);
109
+ }
110
+
111
+ if (response.order_description) {
112
+ table.push(['Description', response.order_description]);
113
+ }
114
+
115
+ if (response.payment_id) {
116
+ table.push(['Payment ID', response.payment_id]);
117
+ }
118
+
119
+ table.push(
120
+ ['Created', new Date(response.created_at).toLocaleString()],
121
+ ['Updated', new Date(response.updated_at).toLocaleString()]
122
+ );
123
+
124
+ console.log(table.toString());
125
+ });
126
+ } catch (error) {
127
+ handleApiError(error);
128
+ }
129
+ });
130
+
131
+ // List invoices
132
+ command
133
+ .command('list')
134
+ .description('List all invoices')
135
+ .option('-l, --limit <number>', 'Number of invoices to return', '10')
136
+ .option('-p, --page <number>', 'Page number', '0')
137
+ .option('--sort-by <field>', 'Sort by field (created_at, updated_at)')
138
+ .option('--order <order>', 'Sort order (asc, desc)', 'desc')
139
+ .option('--date-from <date>', 'Filter by start date (YYYY-MM-DD)')
140
+ .option('--date-to <date>', 'Filter by end date (YYYY-MM-DD)')
141
+ .action(async (options) => {
142
+ try {
143
+ const queryParams = {
144
+ limit: parseInt(options.limit),
145
+ page: parseInt(options.page),
146
+ sortBy: options.sortBy,
147
+ orderBy: options.order,
148
+ dateFrom: options.dateFrom,
149
+ dateTo: options.dateTo
150
+ };
151
+
152
+ const data = await makeRequest(command.parent.parent, '/invoice', {
153
+ queryParams
154
+ });
155
+
156
+ displayResponse(command.parent.parent, data, (response) => {
157
+ const invoices = response.data || [];
158
+
159
+ if (invoices.length === 0) {
160
+ console.log(chalk.yellow('\nNo invoices found'));
161
+ return;
162
+ }
163
+
164
+ console.log(chalk.cyan(`\nInvoices (${invoices.length}):`));
165
+
166
+ const table = new Table({
167
+ head: ['ID', 'Status', 'Amount', 'Currency', 'Created'],
168
+ colWidths: [20, 15, 12, 10, 25]
169
+ });
170
+
171
+ invoices.forEach(invoice => {
172
+ table.push([
173
+ invoice.id?.toString().substring(0, 18) || 'N/A',
174
+ invoice.invoice_status || 'N/A',
175
+ invoice.price_amount || 'N/A',
176
+ invoice.price_currency || 'N/A',
177
+ new Date(invoice.created_at).toLocaleString()
178
+ ]);
179
+ });
180
+
181
+ console.log(table.toString());
182
+ });
183
+ } catch (error) {
184
+ handleApiError(error);
185
+ }
186
+ });
187
+
188
+ module.exports = command;