@profullstack/coinpay 0.4.6 → 0.4.7

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/bin/coinpay.js CHANGED
@@ -216,6 +216,7 @@ ${colors.cyan}Commands:${colors.reset}
216
216
  refund <id> Refund funds to depositor
217
217
  dispute <id> Open a dispute
218
218
  events <id> Get escrow audit log
219
+ auth <id> Authenticate with escrow token
219
220
 
220
221
  ${colors.bright}webhook${colors.reset}
221
222
  logs <business-id> Get webhook logs
@@ -226,11 +227,22 @@ ${colors.cyan}Wallet Options:${colors.reset}
226
227
  --chains <BTC,ETH,...> Chains to derive (default: BTC,ETH,SOL,POL,BCH)
227
228
  --chain <chain> Single chain for operations
228
229
  --to <address> Recipient address
229
- --amount <amount> Amount to send
230
+ --amount <amount> Amount to send (crypto)
231
+ --amount-fiat <amount> Amount to send (fiat, requires --fiat)
232
+ --fiat <currency> Fiat currency (USD, EUR, GBP, CAD, AUD, JPY, CHF, CNY, INR, BRL)
230
233
  --password <pass> Wallet encryption password
231
234
  --wallet-file <path> Custom wallet file (default: ~/.coinpay-wallet.gpg)
232
235
  --no-save Don't save wallet locally after create/import
233
236
 
237
+ ${colors.cyan}Escrow Options:${colors.reset}
238
+ --chain <chain> Blockchain (BTC, ETH, SOL, POL, BCH, etc.)
239
+ --amount <amount> Crypto amount to escrow
240
+ --amount-fiat <amount> Fiat amount to escrow (alternative to --amount)
241
+ --fiat <currency> Fiat currency (required with --amount-fiat)
242
+ --depositor <address> Depositor wallet address
243
+ --beneficiary <address> Beneficiary wallet address
244
+ --token <token> Release or beneficiary token (for auth/release/refund)
245
+
234
246
  ${colors.cyan}Swap Options:${colors.reset}
235
247
  --from <coin> Source coin (e.g., BTC)
236
248
  --to <coin> Destination coin (e.g., ETH)
@@ -249,6 +261,18 @@ ${colors.cyan}Examples:${colors.reset}
249
261
 
250
262
  # Send transaction (auto-decrypts for signing)
251
263
  coinpay wallet send --chain ETH --to 0x123... --amount 0.1
264
+
265
+ # Send transaction with fiat amount
266
+ coinpay wallet send --chain SOL --to abc123... --amount-fiat 10 --fiat USD
267
+
268
+ # Create escrow with crypto amount
269
+ coinpay escrow create --chain SOL --amount 0.5 --depositor abc... --beneficiary def...
270
+
271
+ # Create escrow with fiat amount
272
+ coinpay escrow create --chain SOL --amount-fiat 50 --fiat USD --depositor abc... --beneficiary def...
273
+
274
+ # Authenticate with escrow token
275
+ coinpay escrow auth escr_123 --token rel_abc456
252
276
 
253
277
  # Swap BTC to ETH
254
278
  coinpay swap quote --from BTC --to ETH --amount 0.1
@@ -1046,11 +1070,19 @@ async function handleWallet(subcommand, args, flags) {
1046
1070
  const chain = (flags.chain || '').toUpperCase();
1047
1071
  const to = flags.to;
1048
1072
  const amount = flags.amount;
1073
+ const amountFiat = flags['amount-fiat'] ? parseFloat(flags['amount-fiat']) : undefined;
1074
+ const fiatCurrency = flags.fiat;
1049
1075
  const priority = flags.priority || 'medium';
1050
1076
 
1051
- if (!chain || !to || !amount) {
1052
- print.error('Required: --chain, --to, --amount');
1077
+ if (!chain || !to || (!amount && !amountFiat)) {
1078
+ print.error('Required: --chain, --to, --amount (or --amount-fiat --fiat)');
1053
1079
  print.info('Usage: coinpay wallet send --chain ETH --to 0x123... --amount 0.1');
1080
+ print.info(' or: coinpay wallet send --chain SOL --to abc123... --amount-fiat 10 --fiat USD');
1081
+ return;
1082
+ }
1083
+
1084
+ if (amountFiat && !fiatCurrency) {
1085
+ print.error('--fiat is required when using --amount-fiat');
1054
1086
  return;
1055
1087
  }
1056
1088
 
@@ -1065,12 +1097,23 @@ async function handleWallet(subcommand, args, flags) {
1065
1097
  }
1066
1098
 
1067
1099
  try {
1100
+ let finalAmount = amount;
1101
+
1102
+ // Convert fiat to crypto if needed
1103
+ if (amountFiat && fiatCurrency) {
1104
+ print.info(`Converting ${fiatCurrency} ${amountFiat.toFixed(2)} to ${chain}...`);
1105
+ const apiClient = createClient();
1106
+ const conversion = await apiClient.convertFiatToCrypto(amountFiat, fiatCurrency, chain);
1107
+ finalAmount = conversion.cryptoAmount.toString();
1108
+ print.success(`Converting ${fiatCurrency} ${amountFiat.toFixed(2)} → ${conversion.cryptoAmount.toFixed(6)} ${chain} (rate: 1 ${chain} = ${fiatCurrency} ${conversion.rate.toFixed(2)})`);
1109
+ }
1110
+
1068
1111
  const mnemonic = await getDecryptedMnemonic(flags);
1069
1112
  const wallet = await WalletClient.fromSeed(mnemonic, { baseUrl });
1070
1113
 
1071
- print.info(`Sending ${amount} ${chain} to ${to}...`);
1114
+ print.info(`Sending ${finalAmount} ${chain} to ${to}...`);
1072
1115
 
1073
- const result = await wallet.send({ chain, to, amount, priority });
1116
+ const result = await wallet.send({ chain, to, amount: finalAmount, priority });
1074
1117
 
1075
1118
  print.success('Transaction sent!');
1076
1119
  if (result.tx_hash) {
@@ -1369,20 +1412,36 @@ async function handleEscrow(subcommand, args, flags) {
1369
1412
  switch (subcommand) {
1370
1413
  case 'create': {
1371
1414
  const chain = flags.chain || flags.blockchain;
1372
- const amount = parseFloat(flags.amount);
1415
+ const amount = flags.amount ? parseFloat(flags.amount) : undefined;
1416
+ const amountFiat = flags['amount-fiat'] ? parseFloat(flags['amount-fiat']) : undefined;
1417
+ const fiatCurrency = flags.fiat;
1373
1418
  const depositor = flags.depositor || flags['depositor-address'];
1374
1419
  const beneficiary = flags.beneficiary || flags['beneficiary-address'];
1375
1420
 
1376
- if (!chain || !amount || !depositor || !beneficiary) {
1377
- print.error('Required: --chain, --amount, --depositor, --beneficiary');
1421
+ if (!chain || (!amount && !amountFiat) || !depositor || !beneficiary) {
1422
+ print.error('Required: --chain, --amount (or --amount-fiat --fiat), --depositor, --beneficiary');
1378
1423
  process.exit(1);
1379
1424
  }
1380
1425
 
1381
- print.info(`Creating escrow: ${amount} ${chain}`);
1426
+ if (amountFiat && !fiatCurrency) {
1427
+ print.error('--fiat is required when using --amount-fiat');
1428
+ process.exit(1);
1429
+ }
1430
+
1431
+ // Show conversion if using fiat
1432
+ let finalAmount = amount;
1433
+ if (amountFiat && fiatCurrency) {
1434
+ print.info(`Converting ${fiatCurrency} ${amountFiat.toFixed(2)} to ${chain}...`);
1435
+ const conversion = await client.convertFiatToCrypto(amountFiat, fiatCurrency, chain);
1436
+ finalAmount = conversion.cryptoAmount;
1437
+ print.success(`Converting ${fiatCurrency} ${amountFiat.toFixed(2)} → ${finalAmount.toFixed(6)} ${chain} (rate: 1 ${chain} = ${fiatCurrency} ${conversion.rate.toFixed(2)})`);
1438
+ }
1439
+
1440
+ print.info(`Creating escrow: ${finalAmount} ${chain}`);
1382
1441
 
1383
1442
  const escrow = await client.createEscrow({
1384
1443
  chain,
1385
- amount,
1444
+ amount: finalAmount,
1386
1445
  depositorAddress: depositor,
1387
1446
  beneficiaryAddress: beneficiary,
1388
1447
  metadata: flags.metadata ? JSON.parse(flags.metadata) : undefined,
@@ -1393,7 +1452,9 @@ async function handleEscrow(subcommand, args, flags) {
1393
1452
  print.info(` Deposit to: ${escrow.escrowAddress}`);
1394
1453
  print.info(` Status: ${escrow.status}`);
1395
1454
  print.warn(` Release Token: ${escrow.releaseToken}`);
1455
+ print.warn(` Beneficiary Token: ${escrow.beneficiaryToken}`);
1396
1456
  print.warn(' ⚠️ Save these tokens!');
1457
+ print.info(` Manage: coinpay escrow auth ${escrow.id} --token <token>`);
1397
1458
 
1398
1459
  if (flags.json) print.json(escrow);
1399
1460
  break;
@@ -1476,9 +1537,42 @@ async function handleEscrow(subcommand, args, flags) {
1476
1537
  break;
1477
1538
  }
1478
1539
 
1540
+ case 'auth': {
1541
+ const id = args[0];
1542
+ const token = flags.token;
1543
+ if (!id || !token) {
1544
+ print.error('Required: <id> --token <token>');
1545
+ process.exit(1);
1546
+ }
1547
+
1548
+ const auth = await client.authenticateEscrow(id, token);
1549
+ print.success(`Authenticated as: ${auth.role}`);
1550
+ print.info(`Escrow Details:`);
1551
+ print.info(` ID: ${auth.escrow.id}`);
1552
+ print.info(` Status: ${auth.escrow.status}`);
1553
+ print.info(` Chain: ${auth.escrow.chain}`);
1554
+ print.info(` Amount: ${auth.escrow.amount}`);
1555
+ print.info(` Depositor: ${auth.escrow.depositorAddress}`);
1556
+ print.info(` Beneficiary: ${auth.escrow.beneficiaryAddress}`);
1557
+
1558
+ // Show available actions based on role and status
1559
+ if (auth.escrow.status === 'funded') {
1560
+ if (auth.role === 'depositor') {
1561
+ print.info(`Available actions: release, refund, dispute`);
1562
+ } else if (auth.role === 'beneficiary') {
1563
+ print.info(`Available actions: dispute`);
1564
+ }
1565
+ } else if (auth.escrow.status === 'pending') {
1566
+ print.info(`Waiting for deposit to: ${auth.escrow.escrowAddress}`);
1567
+ }
1568
+
1569
+ if (flags.json) print.json(auth);
1570
+ break;
1571
+ }
1572
+
1479
1573
  default:
1480
1574
  print.error(`Unknown escrow command: ${subcommand}`);
1481
- print.info('Available: create, get, list, release, refund, dispute, events');
1575
+ print.info('Available: create, get, list, release, refund, dispute, events, auth');
1482
1576
  process.exit(1);
1483
1577
  }
1484
1578
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/coinpay",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "CoinPay SDK & CLI — Accept cryptocurrency payments (BTC, ETH, SOL, POL, BCH, USDC) with wallet and swap support",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/client.js CHANGED
@@ -306,6 +306,34 @@ export class CoinPayClient {
306
306
  return this.request(`/rates?crypto=${cryptocurrency}&fiat=${fiatCurrency}`);
307
307
  }
308
308
 
309
+ /**
310
+ * Convert fiat amount to cryptocurrency amount
311
+ * @param {number} fiatAmount - Fiat amount to convert
312
+ * @param {string} fiatCurrency - Fiat currency code (USD, EUR, etc.)
313
+ * @param {string} cryptoCurrency - Cryptocurrency code (BTC, ETH, SOL, etc.)
314
+ * @returns {Promise<Object>} { cryptoAmount, rate, fiat, crypto }
315
+ *
316
+ * @example
317
+ * const result = await client.convertFiatToCrypto(50, 'USD', 'SOL');
318
+ * console.log(`$50 USD = ${result.cryptoAmount} SOL (rate: 1 SOL = $${result.rate})`);
319
+ */
320
+ async convertFiatToCrypto(fiatAmount, fiatCurrency, cryptoCurrency) {
321
+ const rateData = await this.request(`/rates?coin=${cryptoCurrency}&fiat=${fiatCurrency}`);
322
+
323
+ if (!rateData.success || !rateData.rate) {
324
+ throw new Error(`Failed to get exchange rate for ${cryptoCurrency}/${fiatCurrency}`);
325
+ }
326
+
327
+ const cryptoAmount = fiatAmount / rateData.rate;
328
+
329
+ return {
330
+ cryptoAmount: cryptoAmount,
331
+ rate: rateData.rate,
332
+ fiat: fiatCurrency,
333
+ crypto: cryptoCurrency
334
+ };
335
+ }
336
+
309
337
  /**
310
338
  * Get multiple exchange rates
311
339
  * @param {string[]} cryptocurrencies - Array of cryptocurrency codes
@@ -483,6 +511,17 @@ export class CoinPayClient {
483
511
  const { waitForEscrow } = await import('./escrow.js');
484
512
  return waitForEscrow(this, escrowId, options);
485
513
  }
514
+
515
+ /**
516
+ * Authenticate with escrow using token
517
+ * @param {string} escrowId - Escrow ID
518
+ * @param {string} token - Release token or beneficiary token
519
+ * @returns {Promise<Object>} { escrow, role }
520
+ */
521
+ async authenticateEscrow(escrowId, token) {
522
+ const { authenticateEscrow } = await import('./escrow.js');
523
+ return authenticateEscrow(this, escrowId, token);
524
+ }
486
525
  }
487
526
 
488
527
  export default CoinPayClient;
package/src/escrow.js CHANGED
@@ -31,7 +31,9 @@
31
31
  * @param {CoinPayClient} client - API client instance
32
32
  * @param {Object} params - Escrow parameters
33
33
  * @param {string} params.chain - Blockchain (BTC, ETH, SOL, POL, etc.)
34
- * @param {number} params.amount - Crypto amount to escrow
34
+ * @param {number} [params.amount] - Crypto amount to escrow
35
+ * @param {number} [params.amountFiat] - Fiat amount to escrow (alternative to amount)
36
+ * @param {string} [params.fiatCurrency] - Fiat currency (required if amountFiat is used)
35
37
  * @param {string} params.depositorAddress - Wallet address for refunds
36
38
  * @param {string} params.beneficiaryAddress - Wallet address for releases
37
39
  * @param {string} [params.arbiterAddress] - Optional dispute resolver address
@@ -42,15 +44,33 @@
42
44
  export async function createEscrow(client, {
43
45
  chain,
44
46
  amount,
47
+ amountFiat,
48
+ fiatCurrency,
45
49
  depositorAddress,
46
50
  beneficiaryAddress,
47
51
  arbiterAddress,
48
52
  metadata,
49
53
  expiresInHours,
50
54
  }) {
55
+ let finalAmount = amount;
56
+
57
+ // If fiat amount is provided, convert to crypto first
58
+ if (amountFiat && fiatCurrency) {
59
+ if (amount) {
60
+ throw new Error('Cannot specify both amount and amountFiat. Use one or the other.');
61
+ }
62
+
63
+ const conversion = await client.convertFiatToCrypto(amountFiat, fiatCurrency, chain);
64
+ finalAmount = conversion.cryptoAmount;
65
+ } else if (amountFiat && !fiatCurrency) {
66
+ throw new Error('fiatCurrency is required when amountFiat is specified');
67
+ } else if (!amount && !amountFiat) {
68
+ throw new Error('Either amount or amountFiat must be specified');
69
+ }
70
+
51
71
  const body = {
52
72
  chain,
53
- amount,
73
+ amount: finalAmount,
54
74
  depositor_address: depositorAddress,
55
75
  beneficiary_address: beneficiaryAddress,
56
76
  };
@@ -165,6 +185,30 @@ export async function disputeEscrow(client, escrowId, token, reason) {
165
185
  return normalizeEscrow(data);
166
186
  }
167
187
 
188
+ /**
189
+ * Authenticate with escrow using token
190
+ * @param {CoinPayClient} client
191
+ * @param {string} escrowId - Escrow ID
192
+ * @param {string} token - Release token or beneficiary token
193
+ * @returns {Promise<Object>} { escrow, role } where role is 'depositor' or 'beneficiary'
194
+ *
195
+ * @example
196
+ * const auth = await client.authenticateEscrow('escrow_123', 'rel_abc123');
197
+ * console.log(`Authenticated as: ${auth.role}`);
198
+ * console.log(`Escrow status: ${auth.escrow.status}`);
199
+ */
200
+ export async function authenticateEscrow(client, escrowId, token) {
201
+ const data = await client.request(`/escrow/${escrowId}/auth`, {
202
+ method: 'POST',
203
+ body: JSON.stringify({ token }),
204
+ });
205
+
206
+ return {
207
+ escrow: normalizeEscrow(data.escrow),
208
+ role: data.role,
209
+ };
210
+ }
211
+
168
212
  /**
169
213
  * Get escrow event log
170
214
  * @param {CoinPayClient} client
package/src/index.js CHANGED
@@ -63,6 +63,7 @@ import {
63
63
  disputeEscrow,
64
64
  getEscrowEvents,
65
65
  waitForEscrow,
66
+ authenticateEscrow,
66
67
  } from './escrow.js';
67
68
 
68
69
  // Wallet exports
@@ -106,6 +107,7 @@ export {
106
107
  disputeEscrow,
107
108
  getEscrowEvents,
108
109
  waitForEscrow,
110
+ authenticateEscrow,
109
111
 
110
112
  // Wallet
111
113
  WalletClient,
package/src/payments.js CHANGED
@@ -169,6 +169,11 @@ export const FiatCurrency = {
169
169
  GBP: 'GBP',
170
170
  CAD: 'CAD',
171
171
  AUD: 'AUD',
172
+ JPY: 'JPY',
173
+ CHF: 'CHF',
174
+ CNY: 'CNY',
175
+ INR: 'INR',
176
+ BRL: 'BRL',
172
177
  };
173
178
 
174
179
  export default {
package/src/wallet.js CHANGED
@@ -9,11 +9,11 @@
9
9
  import * as bip39 from '@scure/bip39';
10
10
  import { wordlist } from '@scure/bip39/wordlists/english';
11
11
  import { HDKey } from '@scure/bip32';
12
- import { secp256k1 } from '@noble/curves/secp256k1';
12
+ import { secp256k1 } from '@noble/curves/secp256k1.js';
13
13
  import { sha256 } from '@noble/hashes/sha2.js';
14
- import { ripemd160 } from '@noble/hashes/ripemd160.js';
14
+ import { ripemd160 } from '@noble/hashes/legacy.js';
15
15
  import { keccak_256 } from '@noble/hashes/sha3.js';
16
- import { ed25519 } from '@noble/curves/ed25519';
16
+ import { ed25519 } from '@noble/curves/ed25519.js';
17
17
  import { hmac } from '@noble/hashes/hmac.js';
18
18
  import { sha512 } from '@noble/hashes/sha2.js';
19
19
  import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
@@ -218,8 +218,8 @@ function cashAddrPolymod(values) {
218
218
  */
219
219
  function deriveETHAddress(compressedPubKey) {
220
220
  // Decompress: get uncompressed point (65 bytes: 04 || x || y)
221
- const point = secp256k1.ProjectivePoint.fromHex(compressedPubKey);
222
- const uncompressed = point.toRawBytes(false); // 65 bytes with 04 prefix
221
+ const point = secp256k1.Point.fromBytes(compressedPubKey);
222
+ const uncompressed = point.toBytes(false); // 65 bytes with 04 prefix
223
223
  // Keccak256 of the 64 bytes (without 04 prefix)
224
224
  const hash = keccak_256(uncompressed.slice(1));
225
225
  // Last 20 bytes — EIP-55 checksummed