@profullstack/coinpay 0.6.10 → 0.8.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.
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Invoice SDK Module
3
+ *
4
+ * Create, manage, and send invoices with crypto + card payment support.
5
+ * Invoices can be sent to clients who pay via generated crypto addresses
6
+ * or optional Stripe checkout.
7
+ *
8
+ * @example
9
+ * import { CoinPayClient } from '@profullstack/coinpay';
10
+ * import { createInvoice, sendInvoice, getInvoicePaymentData } from '@profullstack/coinpay';
11
+ *
12
+ * const client = new CoinPayClient({ apiKey: 'your-key' });
13
+ *
14
+ * // Create an invoice
15
+ * const invoice = await createInvoice(client, {
16
+ * businessId: 'biz_123',
17
+ * currency: 'USD',
18
+ * amount: 250.00,
19
+ * cryptoCurrency: 'ETH',
20
+ * dueDate: '2026-04-01',
21
+ * notes: 'Web development - March 2026',
22
+ * });
23
+ *
24
+ * // Send it to the client
25
+ * const sent = await sendInvoice(client, invoice.id);
26
+ *
27
+ * // Public: get payment data (no auth)
28
+ * const paymentData = await getInvoicePaymentData(client, invoice.id);
29
+ */
30
+
31
+ /**
32
+ * Create a new invoice
33
+ * @param {CoinPayClient} client - API client instance
34
+ * @param {Object} options - Invoice parameters
35
+ * @param {string} [options.businessId] - Business ID (optional for business-scoped API keys)
36
+ * @param {string} [options.clientId] - Client ID (optional)
37
+ * @param {string} options.currency - Fiat currency code (USD, EUR, etc.)
38
+ * @param {number} options.amount - Invoice amount
39
+ * @param {string} [options.cryptoCurrency] - Preferred crypto for payment
40
+ * @param {string} [options.dueDate] - Due date (ISO string or YYYY-MM-DD)
41
+ * @param {string} [options.notes] - Notes / description
42
+ * @param {string} [options.walletId] - Wallet ID for receiving payment
43
+ * @param {string} [options.merchantWalletAddress] - Direct wallet address for receiving payment
44
+ * @returns {Promise<Object>} Created invoice
45
+ */
46
+ export async function createInvoice(client, {
47
+ businessId,
48
+ clientId,
49
+ currency,
50
+ amount,
51
+ cryptoCurrency,
52
+ dueDate,
53
+ notes,
54
+ walletId,
55
+ merchantWalletAddress,
56
+ }) {
57
+ const body = {
58
+ business_id: businessId,
59
+ currency,
60
+ amount,
61
+ };
62
+ if (clientId) body.client_id = clientId;
63
+ if (cryptoCurrency) body.crypto_currency = cryptoCurrency;
64
+ if (dueDate) body.due_date = dueDate;
65
+ if (notes) body.notes = notes;
66
+ if (walletId) body.wallet_id = walletId;
67
+ if (merchantWalletAddress) body.merchant_wallet_address = merchantWalletAddress;
68
+
69
+ const data = await client.request('/invoices', {
70
+ method: 'POST',
71
+ body: JSON.stringify(body),
72
+ });
73
+
74
+ return normalizeInvoice(data);
75
+ }
76
+
77
+ /**
78
+ * List invoices with optional filters
79
+ * @param {CoinPayClient} client
80
+ * @param {Object} [filters]
81
+ * @param {string} [filters.businessId] - Filter by business ID
82
+ * @param {string} [filters.status] - Filter by status (draft, sent, paid, overdue, cancelled)
83
+ * @param {string} [filters.clientId] - Filter by client ID
84
+ * @param {string} [filters.dateFrom] - Filter from date (ISO string)
85
+ * @param {string} [filters.dateTo] - Filter to date (ISO string)
86
+ * @returns {Promise<Object>} { invoices, total?, ... }
87
+ */
88
+ export async function listInvoices(client, filters = {}) {
89
+ const params = new URLSearchParams();
90
+ if (filters.businessId) params.set('business_id', filters.businessId);
91
+ if (filters.status) params.set('status', filters.status);
92
+ if (filters.clientId) params.set('client_id', filters.clientId);
93
+ if (filters.dateFrom) params.set('date_from', filters.dateFrom);
94
+ if (filters.dateTo) params.set('date_to', filters.dateTo);
95
+
96
+ const query = params.toString();
97
+ const endpoint = query ? `/invoices?${query}` : '/invoices';
98
+ const data = await client.request(endpoint);
99
+
100
+ // Handle both array and paginated responses
101
+ if (Array.isArray(data)) {
102
+ return { invoices: data.map(normalizeInvoice) };
103
+ }
104
+
105
+ return {
106
+ invoices: (data.invoices || []).map(normalizeInvoice),
107
+ total: data.total,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Get a single invoice by ID
113
+ * @param {CoinPayClient} client
114
+ * @param {string} id - Invoice ID
115
+ * @returns {Promise<Object>} Invoice details
116
+ */
117
+ export async function getInvoice(client, id) {
118
+ const data = await client.request(`/invoices/${encodeURIComponent(id)}`);
119
+ return normalizeInvoice(data);
120
+ }
121
+
122
+ /**
123
+ * Update an invoice
124
+ * @param {CoinPayClient} client
125
+ * @param {string} id - Invoice ID
126
+ * @param {Object} updates - Partial invoice fields to update
127
+ * @param {number} [updates.amount] - Updated amount
128
+ * @param {string} [updates.currency] - Updated currency
129
+ * @param {string} [updates.cryptoCurrency] - Updated crypto currency
130
+ * @param {string} [updates.dueDate] - Updated due date
131
+ * @param {string} [updates.notes] - Updated notes
132
+ * @param {string} [updates.clientId] - Updated client ID
133
+ * @param {string} [updates.walletId] - Updated wallet ID
134
+ * @param {string} [updates.merchantWalletAddress] - Updated wallet address
135
+ * @returns {Promise<Object>} Updated invoice
136
+ */
137
+ export async function updateInvoice(client, id, updates) {
138
+ const body = {};
139
+ if (updates.amount !== undefined) body.amount = updates.amount;
140
+ if (updates.currency !== undefined) body.currency = updates.currency;
141
+ if (updates.cryptoCurrency !== undefined) body.crypto_currency = updates.cryptoCurrency;
142
+ if (updates.dueDate !== undefined) body.due_date = updates.dueDate;
143
+ if (updates.notes !== undefined) body.notes = updates.notes;
144
+ if (updates.clientId !== undefined) body.client_id = updates.clientId;
145
+ if (updates.walletId !== undefined) body.wallet_id = updates.walletId;
146
+ if (updates.merchantWalletAddress !== undefined) body.merchant_wallet_address = updates.merchantWalletAddress;
147
+
148
+ const data = await client.request(`/invoices/${encodeURIComponent(id)}`, {
149
+ method: 'PUT',
150
+ body: JSON.stringify(body),
151
+ });
152
+
153
+ return normalizeInvoice(data);
154
+ }
155
+
156
+ /**
157
+ * Delete a draft invoice
158
+ * @param {CoinPayClient} client
159
+ * @param {string} id - Invoice ID (must be in draft status)
160
+ * @returns {Promise<Object>} Deletion confirmation
161
+ */
162
+ export async function deleteInvoice(client, id) {
163
+ return client.request(`/invoices/${encodeURIComponent(id)}`, {
164
+ method: 'DELETE',
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Send an invoice to the client
170
+ * Generates a payment address and optional Stripe checkout link.
171
+ * @param {CoinPayClient} client
172
+ * @param {string} id - Invoice ID
173
+ * @returns {Promise<Object>} Send result with payment details
174
+ */
175
+ export async function sendInvoice(client, id) {
176
+ const data = await client.request(`/invoices/${encodeURIComponent(id)}/send`, {
177
+ method: 'POST',
178
+ });
179
+ return data;
180
+ }
181
+
182
+ /**
183
+ * Get invoice payment data (public, no auth required)
184
+ * Used by clients/customers to view payment instructions.
185
+ * @param {CoinPayClient} client
186
+ * @param {string} id - Invoice ID
187
+ * @returns {Promise<Object>} Payment data (address, amount, status, etc.)
188
+ */
189
+ export async function getInvoicePaymentData(client, id) {
190
+ const data = await client.requestUnauthenticated(`/invoices/${encodeURIComponent(id)}/pay`);
191
+ return data;
192
+ }
193
+
194
+ // ── Constants ──
195
+
196
+ /**
197
+ * Invoice status constants
198
+ */
199
+ export const InvoiceStatus = {
200
+ DRAFT: 'draft',
201
+ SENT: 'sent',
202
+ PAID: 'paid',
203
+ OVERDUE: 'overdue',
204
+ CANCELLED: 'cancelled',
205
+ };
206
+
207
+ // ── Helpers ──
208
+
209
+ function normalizeInvoice(data) {
210
+ if (!data) return data;
211
+
212
+ // Single-invoice API responses are wrapped, while older SDK callers and
213
+ // tests may still supply the invoice object directly.
214
+ const invoice = data.invoice ?? data;
215
+
216
+ return {
217
+ id: invoice.id,
218
+ invoiceNumber: invoice.invoice_number,
219
+ businessId: invoice.business_id,
220
+ clientId: invoice.client_id,
221
+ currency: invoice.currency,
222
+ amount: invoice.amount,
223
+ cryptoCurrency: invoice.crypto_currency,
224
+ cryptoAmount: invoice.crypto_amount,
225
+ status: invoice.status,
226
+ dueDate: invoice.due_date,
227
+ notes: invoice.notes,
228
+ walletId: invoice.wallet_id,
229
+ merchantWalletAddress: invoice.merchant_wallet_address,
230
+ paymentAddress: invoice.payment_address,
231
+ stripeCheckoutUrl: invoice.stripe_checkout_url,
232
+ paidAt: invoice.paid_at,
233
+ sentAt: invoice.sent_at,
234
+ createdAt: invoice.created_at,
235
+ updatedAt: invoice.updated_at,
236
+ };
237
+ }
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
@@ -0,0 +1,14 @@
1
+ import { CoinPayClient, SupportedCoinsParams, SupportedCoinsResponse, TokensResponse } from './client.js';
2
+
3
+ export interface TokenDiscoveryParams extends SupportedCoinsParams {
4
+ /** API key — required if `client` is not provided */
5
+ apiKey?: string;
6
+ /** Existing CoinPayClient instance */
7
+ client?: CoinPayClient;
8
+ }
9
+
10
+ /** List checkout-friendly tokens for a business. */
11
+ export function getTokens(params?: TokenDiscoveryParams): Promise<TokensResponse>;
12
+
13
+ /** List supported payment coins for a business. */
14
+ export function getSupportedCoins(params?: TokenDiscoveryParams): Promise<SupportedCoinsResponse>;
package/src/tokens.js ADDED
@@ -0,0 +1,34 @@
1
+ import { CoinPayClient } from './client.js';
2
+
3
+ /**
4
+ * List checkout tokens for a business.
5
+ * @param {Object} params
6
+ * @param {string} [params.apiKey] - API key if not using client
7
+ * @param {CoinPayClient} [params.client] - Existing client
8
+ * @param {string} [params.businessId] - Business ID
9
+ * @param {boolean} [params.activeOnly] - Only return active wallets
10
+ * @returns {Promise<Object>} Tokens response
11
+ */
12
+ export async function getTokens({ apiKey, client, businessId, activeOnly } = {}) {
13
+ const coinpay = client || new CoinPayClient({ apiKey });
14
+ return coinpay.getTokens({ businessId, activeOnly });
15
+ }
16
+
17
+ /**
18
+ * List supported coins for a business.
19
+ * @param {Object} params
20
+ * @param {string} [params.apiKey] - API key if not using client
21
+ * @param {CoinPayClient} [params.client] - Existing client
22
+ * @param {string} [params.businessId] - Business ID
23
+ * @param {boolean} [params.activeOnly] - Only return active wallets
24
+ * @returns {Promise<Object>} Supported coins response
25
+ */
26
+ export async function getSupportedCoins({ apiKey, client, businessId, activeOnly } = {}) {
27
+ const coinpay = client || new CoinPayClient({ apiKey });
28
+ return coinpay.getSupportedCoins({ businessId, activeOnly });
29
+ }
30
+
31
+ export default {
32
+ getTokens,
33
+ getSupportedCoins,
34
+ };
package/src/wallet.d.ts CHANGED
@@ -251,6 +251,17 @@ export declare function validateMnemonic(mnemonic: string): boolean;
251
251
  */
252
252
  export declare function getDerivationPath(chain: string, index?: number): string;
253
253
 
254
+ /**
255
+ * Derive the address for a chain from a raw BIP-39 seed.
256
+ * (Standalone counterpart to `WalletClient.deriveAddress`; exported from
257
+ * wallet.js but previously missing from these type declarations.)
258
+ *
259
+ * @param seed - Raw seed bytes, e.g. bip39.mnemonicToSeedSync(mnemonic)
260
+ * @param chain - Chain id (e.g. 'BTC', 'ETH', 'SOL', 'POL', 'BCH')
261
+ * @param index - Address index (default 0)
262
+ */
263
+ export declare function deriveAddress(seed: Uint8Array, chain: string, index?: number): string;
264
+
254
265
  /**
255
266
  * Restore a seed from encrypted backup
256
267
  */