@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/coinpay",
3
- "version": "0.6.10",
3
+ "version": "0.6.11",
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/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 BOLT12 Lightning Network operations: node provisioning,
5
- * offer creation, and payment tracking.
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 BOLT12 operations
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
- * Provision a Greenlight CLN node for a wallet.
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 (used server-side to derive LN keys)
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 provisionNode({ wallet_id, mnemonic, business_id }) {
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
- // Offers
68
+ // Lightning Address
62
69
  // ──────────────────────────────────────────────
63
70
 
64
71
  /**
65
- * Create a BOLT12 offer.
72
+ * Register a Lightning Address (username@coinpayportal.com).
73
+ * Requires Lightning to be enabled first.
66
74
  * @param {Object} params
67
- * @param {string} params.node_id - Node UUID
68
- * @param {string} params.description - Human-readable description
69
- * @param {number} [params.amount_msat] - Fixed amount in millisatoshis (omit for any-amount)
70
- * @param {string} [params.currency] - Currency code (default: "BTC")
71
- * @param {string} [params.business_id] - Optional business UUID
72
- * @returns {Promise<Object>}
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 createOffer({ node_id, description, amount_msat, currency, business_id }) {
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({ node_id, description, amount_msat, currency, business_id }),
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>} Includes offer and qr_uri
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] - "active" | "disabled" | "archived"
96
- * @param {number} [params.limit] - Default 20
97
- * @param {number} [params.offset] - Default 0
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