@profullstack/coinpay 0.6.10 → 0.6.11
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/CHANGELOG.md +7 -0
- package/bin/coinpay.js +262 -0
- package/package.json +1 -1
- package/src/auth.js +6 -0
- package/src/client.js +85 -0
- package/src/lightning.js +101 -39
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to `@profullstack/coinpay` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.11] - 2026-03-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- OAuth client management methods: `listOAuthClients()`, `createOAuthClient()`, `getOAuthClient()`, `deleteOAuthClient()`
|
|
12
|
+
- CLI `coinpay oauth` commands: `list`, `create`, `get`, `delete`
|
|
13
|
+
- OAuth section in SDK documentation
|
|
14
|
+
|
|
8
15
|
## [0.2.0] - 2025-01-15
|
|
9
16
|
|
|
10
17
|
### Added
|
package/bin/coinpay.js
CHANGED
|
@@ -244,6 +244,12 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
244
244
|
events <id> Get escrow audit log
|
|
245
245
|
auth <id> Authenticate with escrow token
|
|
246
246
|
|
|
247
|
+
${colors.bright}oauth${colors.reset}
|
|
248
|
+
list List OAuth clients
|
|
249
|
+
create Create a new OAuth client
|
|
250
|
+
get <id> Get OAuth client details
|
|
251
|
+
delete <id> Delete an OAuth client
|
|
252
|
+
|
|
247
253
|
${colors.bright}reputation${colors.reset}
|
|
248
254
|
submit Submit a task receipt
|
|
249
255
|
query <agent-did> Query agent reputation
|
|
@@ -2567,6 +2573,147 @@ async function main() {
|
|
|
2567
2573
|
}
|
|
2568
2574
|
|
|
2569
2575
|
try {
|
|
2576
|
+
|
|
2577
|
+
async function handleLightning(subcommand, args, flags) {
|
|
2578
|
+
const client = createClient();
|
|
2579
|
+
|
|
2580
|
+
switch (subcommand) {
|
|
2581
|
+
case 'enable': {
|
|
2582
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2583
|
+
if (!walletId) {
|
|
2584
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2585
|
+
process.exit(1);
|
|
2586
|
+
}
|
|
2587
|
+
const mnemonic = await getDecryptedMnemonic(flags);
|
|
2588
|
+
const result = await client.lightning.enableWallet({
|
|
2589
|
+
wallet_id: walletId,
|
|
2590
|
+
mnemonic,
|
|
2591
|
+
business_id: flags['business-id'],
|
|
2592
|
+
});
|
|
2593
|
+
console.log(colors.green + '⚡ Lightning wallet enabled!' + colors.reset);
|
|
2594
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2595
|
+
break;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
case 'address': {
|
|
2599
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2600
|
+
const username = flags['username'] || args[0];
|
|
2601
|
+
if (!walletId) {
|
|
2602
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2603
|
+
process.exit(1);
|
|
2604
|
+
}
|
|
2605
|
+
if (!username) {
|
|
2606
|
+
// GET current address
|
|
2607
|
+
const result = await client.lightning.getAddress(walletId);
|
|
2608
|
+
if (result.lightning_address) {
|
|
2609
|
+
console.log(colors.green + '⚡ ' + result.lightning_address + colors.reset);
|
|
2610
|
+
} else {
|
|
2611
|
+
console.log('No Lightning Address registered. Use: coinpay ln address --username <name>');
|
|
2612
|
+
}
|
|
2613
|
+
} else {
|
|
2614
|
+
// POST register address
|
|
2615
|
+
const result = await client.lightning.registerAddress({ wallet_id: walletId, username });
|
|
2616
|
+
console.log(colors.green + '⚡ Registered: ' + result.lightning_address + colors.reset);
|
|
2617
|
+
}
|
|
2618
|
+
break;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
case 'address-check': {
|
|
2622
|
+
const username = flags['username'] || args[0];
|
|
2623
|
+
if (!username) {
|
|
2624
|
+
console.error(colors.red + 'Error: --username required' + colors.reset);
|
|
2625
|
+
process.exit(1);
|
|
2626
|
+
}
|
|
2627
|
+
const result = await client.lightning.checkAddressAvailable(username);
|
|
2628
|
+
console.log(result.available
|
|
2629
|
+
? colors.green + '✓ ' + username + ' is available' + colors.reset
|
|
2630
|
+
: colors.red + '✗ ' + username + ' is taken' + colors.reset);
|
|
2631
|
+
break;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
case 'invoice': {
|
|
2635
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2636
|
+
const amount = parseInt(flags['amount'] || args[0]);
|
|
2637
|
+
if (!walletId || !amount) {
|
|
2638
|
+
console.error(colors.red + 'Error: --wallet-id and --amount required' + colors.reset);
|
|
2639
|
+
process.exit(1);
|
|
2640
|
+
}
|
|
2641
|
+
const result = await client.lightning.createInvoice({
|
|
2642
|
+
wallet_id: walletId,
|
|
2643
|
+
amount_sats: amount,
|
|
2644
|
+
description: flags['description'] || flags['desc'] || '',
|
|
2645
|
+
});
|
|
2646
|
+
console.log(colors.green + '⚡ Invoice created' + colors.reset);
|
|
2647
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2648
|
+
break;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
case 'send': {
|
|
2652
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2653
|
+
const destination = flags['to'] || args[0];
|
|
2654
|
+
const amount = flags['amount'] ? parseInt(flags['amount']) : undefined;
|
|
2655
|
+
if (!walletId || !destination) {
|
|
2656
|
+
console.error(colors.red + 'Error: --wallet-id and --to required' + colors.reset);
|
|
2657
|
+
process.exit(1);
|
|
2658
|
+
}
|
|
2659
|
+
const result = await client.lightning.sendPayment({
|
|
2660
|
+
wallet_id: walletId,
|
|
2661
|
+
destination,
|
|
2662
|
+
amount_sats: amount,
|
|
2663
|
+
});
|
|
2664
|
+
console.log(colors.green + '⚡ Payment sent!' + colors.reset);
|
|
2665
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2666
|
+
break;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
case 'payments': {
|
|
2670
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2671
|
+
if (!walletId) {
|
|
2672
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2673
|
+
process.exit(1);
|
|
2674
|
+
}
|
|
2675
|
+
const result = await client.lightning.listPayments({
|
|
2676
|
+
wallet_id: walletId,
|
|
2677
|
+
direction: flags['direction'],
|
|
2678
|
+
limit: flags['limit'] ? parseInt(flags['limit']) : undefined,
|
|
2679
|
+
});
|
|
2680
|
+
const payments = result.data?.payments || result.payments || [];
|
|
2681
|
+
if (payments.length === 0) {
|
|
2682
|
+
console.log('No Lightning payments found.');
|
|
2683
|
+
} else {
|
|
2684
|
+
for (const p of payments) {
|
|
2685
|
+
const dir = p.direction === 'outgoing' ? colors.red + '↑ SENT' : colors.green + '↓ RECV';
|
|
2686
|
+
const sats = Math.floor((p.amount_msat || 0) / 1000);
|
|
2687
|
+
console.log(`${dir}${colors.reset} ${sats} sats ${p.status} ${p.created_at}`);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
break;
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
case 'balance': {
|
|
2694
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2695
|
+
if (!walletId) {
|
|
2696
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2697
|
+
process.exit(1);
|
|
2698
|
+
}
|
|
2699
|
+
// Use the web-wallet balance endpoint filtered by LN
|
|
2700
|
+
const result = await client.request(`/web-wallet/balance?wallet_id=${walletId}`);
|
|
2701
|
+
const balances = result.data?.balances || result.balances || [];
|
|
2702
|
+
const ln = balances.find(b => b.chain === 'LN' || b.currency === 'LN');
|
|
2703
|
+
if (ln) {
|
|
2704
|
+
console.log(colors.green + '⚡ ' + ln.balance + ' BTC (' + Math.floor(parseFloat(ln.balance) * 100_000_000) + ' sats)' + colors.reset);
|
|
2705
|
+
} else {
|
|
2706
|
+
console.log('No Lightning balance. Enable Lightning first: coinpay ln enable');
|
|
2707
|
+
}
|
|
2708
|
+
break;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
default:
|
|
2712
|
+
console.log(`Unknown lightning command: ${subcommand}`);
|
|
2713
|
+
showHelp();
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2570
2717
|
switch (command) {
|
|
2571
2718
|
case 'config':
|
|
2572
2719
|
await handleConfig(subcommand, args);
|
|
@@ -2596,6 +2743,11 @@ async function main() {
|
|
|
2596
2743
|
await handleEscrow(subcommand, args, flags);
|
|
2597
2744
|
break;
|
|
2598
2745
|
|
|
2746
|
+
case 'ln':
|
|
2747
|
+
case 'lightning':
|
|
2748
|
+
await handleLightning(subcommand, args, flags);
|
|
2749
|
+
break;
|
|
2750
|
+
|
|
2599
2751
|
case 'webhook':
|
|
2600
2752
|
await handleWebhook(subcommand, args, flags);
|
|
2601
2753
|
break;
|
|
@@ -2619,6 +2771,10 @@ async function main() {
|
|
|
2619
2771
|
case 'x402':
|
|
2620
2772
|
await handleX402(subcommand, args, flags);
|
|
2621
2773
|
break;
|
|
2774
|
+
|
|
2775
|
+
case 'oauth':
|
|
2776
|
+
await handleOAuth(subcommand, args, flags);
|
|
2777
|
+
break;
|
|
2622
2778
|
|
|
2623
2779
|
default:
|
|
2624
2780
|
print.error(`Unknown command: ${command}`);
|
|
@@ -2634,6 +2790,112 @@ async function main() {
|
|
|
2634
2790
|
}
|
|
2635
2791
|
}
|
|
2636
2792
|
|
|
2793
|
+
/**
|
|
2794
|
+
* OAuth commands
|
|
2795
|
+
*/
|
|
2796
|
+
async function handleOAuth(subcommand, args, flags) {
|
|
2797
|
+
const client = createClient();
|
|
2798
|
+
|
|
2799
|
+
switch (subcommand) {
|
|
2800
|
+
case 'list': {
|
|
2801
|
+
const result = await client.listOAuthClients();
|
|
2802
|
+
const clients = result.clients || [];
|
|
2803
|
+
|
|
2804
|
+
if (clients.length === 0) {
|
|
2805
|
+
print.info('No OAuth clients found.');
|
|
2806
|
+
} else {
|
|
2807
|
+
console.log(`\n${colors.bright}OAuth Clients${colors.reset} (${clients.length})\n`);
|
|
2808
|
+
for (const c of clients) {
|
|
2809
|
+
console.log(` ${colors.bright}${c.name}${colors.reset}`);
|
|
2810
|
+
console.log(` Client ID: ${c.client_id}`);
|
|
2811
|
+
if (c.description) console.log(` Description: ${c.description}`);
|
|
2812
|
+
if (c.redirect_uris) console.log(` Redirect URIs: ${c.redirect_uris.join(', ')}`);
|
|
2813
|
+
if (c.scopes) console.log(` Scopes: ${Array.isArray(c.scopes) ? c.scopes.join(', ') : c.scopes}`);
|
|
2814
|
+
console.log();
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
if (flags.json) print.json(result);
|
|
2819
|
+
break;
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
case 'create': {
|
|
2823
|
+
const name = flags.name;
|
|
2824
|
+
const redirectUri = flags['redirect-uri'];
|
|
2825
|
+
const scope = flags.scope;
|
|
2826
|
+
const description = flags.description;
|
|
2827
|
+
|
|
2828
|
+
if (!name || !redirectUri) {
|
|
2829
|
+
print.error('Required: --name, --redirect-uri');
|
|
2830
|
+
print.info('Example: coinpay oauth create --name "My App" --redirect-uri https://myapp.com/callback --scope openid,profile');
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
const redirectUris = redirectUri.split(',').map(u => u.trim());
|
|
2835
|
+
const scopes = scope ? scope.split(',').map(s => s.trim()) : undefined;
|
|
2836
|
+
|
|
2837
|
+
const result = await client.createOAuthClient({
|
|
2838
|
+
name,
|
|
2839
|
+
redirectUris,
|
|
2840
|
+
scopes,
|
|
2841
|
+
description,
|
|
2842
|
+
});
|
|
2843
|
+
|
|
2844
|
+
print.success('OAuth client created');
|
|
2845
|
+
if (result.client_id) {
|
|
2846
|
+
console.log(`\n ${colors.bright}Client ID:${colors.reset} ${result.client_id}`);
|
|
2847
|
+
}
|
|
2848
|
+
if (result.client_secret) {
|
|
2849
|
+
console.log(` ${colors.bright}Client Secret:${colors.reset} ${colors.yellow}${result.client_secret}${colors.reset}`);
|
|
2850
|
+
console.log();
|
|
2851
|
+
print.warn('Save the client secret — it is only shown once!');
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
if (flags.json) print.json(result);
|
|
2855
|
+
break;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
case 'get': {
|
|
2859
|
+
const id = args[0];
|
|
2860
|
+
if (!id) {
|
|
2861
|
+
print.error('OAuth client ID required');
|
|
2862
|
+
print.info('Usage: coinpay oauth get <client-id>');
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
const result = await client.getOAuthClient(id);
|
|
2867
|
+
|
|
2868
|
+
print.success(`OAuth Client: ${result.name || id}`);
|
|
2869
|
+
if (result.client_id) print.info(` Client ID: ${result.client_id}`);
|
|
2870
|
+
if (result.description) print.info(` Description: ${result.description}`);
|
|
2871
|
+
if (result.redirect_uris) print.info(` Redirect URIs: ${result.redirect_uris.join(', ')}`);
|
|
2872
|
+
if (result.scopes) print.info(` Scopes: ${Array.isArray(result.scopes) ? result.scopes.join(', ') : result.scopes}`);
|
|
2873
|
+
if (result.created_at) print.info(` Created: ${result.created_at}`);
|
|
2874
|
+
|
|
2875
|
+
if (flags.json) print.json(result);
|
|
2876
|
+
break;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
case 'delete': {
|
|
2880
|
+
const id = args[0];
|
|
2881
|
+
if (!id) {
|
|
2882
|
+
print.error('OAuth client ID required');
|
|
2883
|
+
print.info('Usage: coinpay oauth delete <client-id>');
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
await client.deleteOAuthClient(id);
|
|
2888
|
+
print.success(`OAuth client ${id} deleted`);
|
|
2889
|
+
break;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
default:
|
|
2893
|
+
print.error(`Unknown oauth command: ${subcommand}`);
|
|
2894
|
+
print.info('Available: list, create, get, delete');
|
|
2895
|
+
process.exit(1);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2637
2899
|
/**
|
|
2638
2900
|
* Handle x402 commands
|
|
2639
2901
|
*/
|
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
19
19
|
*/
|
|
20
20
|
export async function registerMerchant(client, { email, password, name }) {
|
|
21
|
+
if (!email) throw new Error('Email is required');
|
|
22
|
+
if (!password) throw new Error('Password is required');
|
|
23
|
+
|
|
21
24
|
return client.requestUnauthenticated('/auth/register', {
|
|
22
25
|
method: 'POST',
|
|
23
26
|
body: JSON.stringify({
|
|
@@ -37,6 +40,9 @@ export async function registerMerchant(client, { email, password, name }) {
|
|
|
37
40
|
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
38
41
|
*/
|
|
39
42
|
export async function loginMerchant(client, { email, password }) {
|
|
43
|
+
if (!email) throw new Error('Email is required');
|
|
44
|
+
if (!password) throw new Error('Password is required');
|
|
45
|
+
|
|
40
46
|
return client.requestUnauthenticated('/auth/login', {
|
|
41
47
|
method: 'POST',
|
|
42
48
|
body: JSON.stringify({
|
package/src/client.js
CHANGED
|
@@ -826,6 +826,91 @@ export class CoinPayClient {
|
|
|
826
826
|
method: 'DELETE',
|
|
827
827
|
});
|
|
828
828
|
}
|
|
829
|
+
|
|
830
|
+
// ── OAuth Client Methods ─────────────────────────────────
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* List OAuth clients
|
|
834
|
+
*
|
|
835
|
+
* Returns all OAuth clients registered under your account.
|
|
836
|
+
*
|
|
837
|
+
* @returns {Promise<Object>} List of OAuth clients
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* const result = await client.listOAuthClients();
|
|
841
|
+
* for (const app of result.clients) {
|
|
842
|
+
* console.log(app.client_id, app.name);
|
|
843
|
+
* }
|
|
844
|
+
*/
|
|
845
|
+
async listOAuthClients() {
|
|
846
|
+
return this.request('/oauth/clients');
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Create a new OAuth client
|
|
851
|
+
*
|
|
852
|
+
* Registers a new OAuth application. Returns the client_id and client_secret.
|
|
853
|
+
* Store the client_secret securely — it is only shown once.
|
|
854
|
+
*
|
|
855
|
+
* @param {Object} params - OAuth client parameters
|
|
856
|
+
* @param {string} params.name - Application name
|
|
857
|
+
* @param {string[]} params.redirectUris - Authorized redirect URIs
|
|
858
|
+
* @param {string[]} [params.scopes] - Requested scopes (e.g., ['openid', 'profile'])
|
|
859
|
+
* @param {string} [params.description] - Application description
|
|
860
|
+
* @returns {Promise<Object>} Created OAuth client with client_id and client_secret
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* const app = await client.createOAuthClient({
|
|
864
|
+
* name: 'My App',
|
|
865
|
+
* redirectUris: ['https://myapp.com/callback'],
|
|
866
|
+
* scopes: ['openid', 'profile'],
|
|
867
|
+
* description: 'My awesome application',
|
|
868
|
+
* });
|
|
869
|
+
* console.log('Client ID:', app.client_id);
|
|
870
|
+
* console.log('Client Secret:', app.client_secret);
|
|
871
|
+
*/
|
|
872
|
+
async createOAuthClient({ name, redirectUris, scopes, description }) {
|
|
873
|
+
return this.request('/oauth/clients', {
|
|
874
|
+
method: 'POST',
|
|
875
|
+
body: JSON.stringify({
|
|
876
|
+
name,
|
|
877
|
+
redirect_uris: redirectUris,
|
|
878
|
+
scopes,
|
|
879
|
+
description,
|
|
880
|
+
}),
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Get an OAuth client by ID
|
|
886
|
+
*
|
|
887
|
+
* @param {string} id - OAuth client ID
|
|
888
|
+
* @returns {Promise<Object>} OAuth client details
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* const app = await client.getOAuthClient('oac_abc123');
|
|
892
|
+
* console.log(app.name, app.redirect_uris);
|
|
893
|
+
*/
|
|
894
|
+
async getOAuthClient(id) {
|
|
895
|
+
return this.request(`/oauth/clients/${id}`);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Delete an OAuth client
|
|
900
|
+
*
|
|
901
|
+
* Permanently removes an OAuth client and revokes all associated tokens.
|
|
902
|
+
*
|
|
903
|
+
* @param {string} id - OAuth client ID
|
|
904
|
+
* @returns {Promise<Object>} Deletion confirmation
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* await client.deleteOAuthClient('oac_abc123');
|
|
908
|
+
*/
|
|
909
|
+
async deleteOAuthClient(id) {
|
|
910
|
+
return this.request(`/oauth/clients/${id}`, {
|
|
911
|
+
method: 'DELETE',
|
|
912
|
+
});
|
|
913
|
+
}
|
|
829
914
|
}
|
|
830
915
|
|
|
831
916
|
export default CoinPayClient;
|
package/src/lightning.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lightning Module for CoinPay SDK
|
|
3
3
|
*
|
|
4
|
-
* Manages
|
|
5
|
-
*
|
|
4
|
+
* Manages Lightning Network operations: wallet provisioning (via LNbits),
|
|
5
|
+
* lightning address registration, invoice creation, payment sending, and history.
|
|
6
|
+
*
|
|
7
|
+
* Lightning wallets are custodial — funds are held on CoinPay's server.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
const DEFAULT_BASE_URL = 'https://coinpayportal.com/api';
|
|
9
|
-
|
|
10
10
|
/**
|
|
11
|
-
* Lightning client for
|
|
11
|
+
* Lightning client for LN operations
|
|
12
12
|
*/
|
|
13
13
|
export class LightningClient {
|
|
14
14
|
#client;
|
|
@@ -21,24 +21,31 @@ export class LightningClient {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// ──────────────────────────────────────────────
|
|
24
|
-
// Nodes
|
|
24
|
+
// Nodes / Wallet Provisioning
|
|
25
25
|
// ──────────────────────────────────────────────
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* Enable Lightning for a wallet (provisions an LNbits custodial wallet).
|
|
29
29
|
* @param {Object} params
|
|
30
30
|
* @param {string} params.wallet_id - Wallet UUID
|
|
31
|
-
* @param {string} params.mnemonic - BIP39 mnemonic
|
|
31
|
+
* @param {string} params.mnemonic - BIP39 mnemonic
|
|
32
32
|
* @param {string} [params.business_id] - Optional business UUID
|
|
33
|
-
* @returns {Promise<Object>} The provisioned node
|
|
33
|
+
* @returns {Promise<Object>} The provisioned node record
|
|
34
34
|
*/
|
|
35
|
-
async
|
|
35
|
+
async enableWallet({ wallet_id, mnemonic, business_id }) {
|
|
36
36
|
return this.#client.request('/lightning/nodes', {
|
|
37
37
|
method: 'POST',
|
|
38
38
|
body: JSON.stringify({ wallet_id, mnemonic, business_id }),
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use enableWallet() instead
|
|
44
|
+
*/
|
|
45
|
+
async provisionNode(params) {
|
|
46
|
+
return this.enableWallet(params);
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
/**
|
|
43
50
|
* Get node status by ID.
|
|
44
51
|
* @param {string} nodeId
|
|
@@ -58,30 +65,81 @@ export class LightningClient {
|
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
// ──────────────────────────────────────────────
|
|
61
|
-
//
|
|
68
|
+
// Lightning Address
|
|
62
69
|
// ──────────────────────────────────────────────
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
|
-
*
|
|
72
|
+
* Register a Lightning Address (username@coinpayportal.com).
|
|
73
|
+
* Requires Lightning to be enabled first.
|
|
66
74
|
* @param {Object} params
|
|
67
|
-
* @param {string} params.
|
|
68
|
-
* @param {string} params.
|
|
69
|
-
* @
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
* @param {string} params.wallet_id - Wallet UUID
|
|
76
|
+
* @param {string} params.username - Desired username (3-32 chars, lowercase alphanumeric)
|
|
77
|
+
* @returns {Promise<Object>} { lightning_address, username }
|
|
78
|
+
*/
|
|
79
|
+
async registerAddress({ wallet_id, username }) {
|
|
80
|
+
return this.#client.request('/lightning/address', {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
body: JSON.stringify({ wallet_id, username }),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get Lightning Address for a wallet.
|
|
88
|
+
* @param {string} walletId - Wallet UUID
|
|
89
|
+
* @returns {Promise<Object>} { lightning_address, username } or { lightning_address: null }
|
|
90
|
+
*/
|
|
91
|
+
async getAddress(walletId) {
|
|
92
|
+
return this.#client.request(`/lightning/address?wallet_id=${walletId}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if a Lightning Address username is available.
|
|
97
|
+
* @param {string} username
|
|
98
|
+
* @returns {Promise<Object>} { available: boolean }
|
|
99
|
+
*/
|
|
100
|
+
async checkAddressAvailable(username) {
|
|
101
|
+
return this.#client.request(`/lightning/address?username=${encodeURIComponent(username)}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ──────────────────────────────────────────────
|
|
105
|
+
// Invoices
|
|
106
|
+
// ──────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a BOLT11 invoice to receive a payment.
|
|
110
|
+
* @param {Object} params
|
|
111
|
+
* @param {string} params.wallet_id - Wallet UUID
|
|
112
|
+
* @param {number} params.amount_sats - Amount in satoshis
|
|
113
|
+
* @param {string} [params.description] - Invoice description/memo
|
|
114
|
+
* @returns {Promise<Object>} { payment_request, payment_hash, ... }
|
|
73
115
|
*/
|
|
74
|
-
async
|
|
116
|
+
async createInvoice({ wallet_id, amount_sats, description }) {
|
|
117
|
+
return this.#client.request('/lightning/invoices', {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
body: JSON.stringify({ wallet_id, amount_sats, description }),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ──────────────────────────────────────────────
|
|
124
|
+
// Offers (BOLT12) — currently not supported
|
|
125
|
+
// ──────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create a BOLT12 offer.
|
|
129
|
+
* @deprecated BOLT12 offers are not currently supported (LNbits limitation).
|
|
130
|
+
* Use createInvoice() or registerAddress() instead.
|
|
131
|
+
*/
|
|
132
|
+
async createOffer(params) {
|
|
75
133
|
return this.#client.request('/lightning/offers', {
|
|
76
134
|
method: 'POST',
|
|
77
|
-
body: JSON.stringify(
|
|
135
|
+
body: JSON.stringify(params),
|
|
78
136
|
});
|
|
79
137
|
}
|
|
80
138
|
|
|
81
139
|
/**
|
|
82
140
|
* Get offer by ID.
|
|
83
141
|
* @param {string} offerId
|
|
84
|
-
* @returns {Promise<Object>}
|
|
142
|
+
* @returns {Promise<Object>}
|
|
85
143
|
*/
|
|
86
144
|
async getOffer(offerId) {
|
|
87
145
|
return this.#client.request(`/lightning/offers/${offerId}`);
|
|
@@ -92,9 +150,9 @@ export class LightningClient {
|
|
|
92
150
|
* @param {Object} [params]
|
|
93
151
|
* @param {string} [params.business_id]
|
|
94
152
|
* @param {string} [params.node_id]
|
|
95
|
-
* @param {string} [params.status]
|
|
96
|
-
* @param {number} [params.limit]
|
|
97
|
-
* @param {number} [params.offset]
|
|
153
|
+
* @param {string} [params.status]
|
|
154
|
+
* @param {number} [params.limit]
|
|
155
|
+
* @param {number} [params.offset]
|
|
98
156
|
* @returns {Promise<Object>} { offers, total, limit, offset }
|
|
99
157
|
*/
|
|
100
158
|
async listOffers(params = {}) {
|
|
@@ -109,12 +167,31 @@ export class LightningClient {
|
|
|
109
167
|
// Payments
|
|
110
168
|
// ──────────────────────────────────────────────
|
|
111
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Send a Lightning payment. Accepts:
|
|
172
|
+
* - BOLT11 invoice string
|
|
173
|
+
* - Lightning address (user@domain)
|
|
174
|
+
* @param {Object} params
|
|
175
|
+
* @param {string} params.wallet_id - Wallet UUID
|
|
176
|
+
* @param {string} params.destination - BOLT11 invoice or lightning address (user@domain)
|
|
177
|
+
* @param {number} [params.amount_sats] - Amount in satoshis (required for lightning addresses, optional for invoices with amount)
|
|
178
|
+
* @returns {Promise<Object>} { payment_hash, status }
|
|
179
|
+
*/
|
|
180
|
+
async sendPayment({ wallet_id, destination, amount_sats }) {
|
|
181
|
+
return this.#client.request('/lightning/payments', {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
body: JSON.stringify({ wallet_id, bolt12: destination, amount_sats }),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
112
187
|
/**
|
|
113
188
|
* List Lightning payments.
|
|
114
189
|
* @param {Object} [params]
|
|
190
|
+
* @param {string} [params.wallet_id]
|
|
115
191
|
* @param {string} [params.business_id]
|
|
116
192
|
* @param {string} [params.node_id]
|
|
117
193
|
* @param {string} [params.offer_id]
|
|
194
|
+
* @param {string} [params.direction] - "incoming" | "outgoing"
|
|
118
195
|
* @param {string} [params.status] - "pending" | "settled" | "failed"
|
|
119
196
|
* @param {number} [params.limit] - Default 50
|
|
120
197
|
* @param {number} [params.offset] - Default 0
|
|
@@ -128,21 +205,6 @@ export class LightningClient {
|
|
|
128
205
|
return this.#client.request(`/lightning/payments?${qs}`);
|
|
129
206
|
}
|
|
130
207
|
|
|
131
|
-
/**
|
|
132
|
-
* Send a payment to a BOLT12 offer or invoice.
|
|
133
|
-
* @param {Object} params
|
|
134
|
-
* @param {string} params.node_id - Node UUID
|
|
135
|
-
* @param {string} params.bolt12 - BOLT12 offer or invoice string
|
|
136
|
-
* @param {number} params.amount_sats - Amount in satoshis
|
|
137
|
-
* @returns {Promise<Object>}
|
|
138
|
-
*/
|
|
139
|
-
async sendPayment({ node_id, bolt12, amount_sats }) {
|
|
140
|
-
return this.#client.request('/lightning/payments', {
|
|
141
|
-
method: 'POST',
|
|
142
|
-
body: JSON.stringify({ node_id, bolt12, amount_sats }),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
208
|
/**
|
|
147
209
|
* Get payment status by payment hash.
|
|
148
210
|
* @param {string} paymentHash
|