fulgur-bridge-client 0.0.1

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/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # fulgur-bridge-client
2
+
3
+ A Bitcoin Lightning "donate router". Accept payments straight into your own LN wallet and hook up any automation: trigger functions on your web app, ping a server, control IoT devices, whatever you need. A few lines of code to get started; swap in BTCPay Server when you outgrow it.
4
+
5
+ REST API + WebSocket + PoW anti-spam + QR codes + webhook verification. Works in Node.js, Bun, Deno, browsers, and edge runtimes. Webhook verification requires `node:crypto` (Node.js, Bun, Deno).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install fulgur-bridge-client
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { RestGatewayClient } from 'fulgur-bridge-client';
17
+
18
+ const gw = new RestGatewayClient('https://your-gateway.example.com');
19
+
20
+ // Create a donation
21
+ const { id, bolt11 } = await gw.createDonation({
22
+ destination: 'alice@getalby.com',
23
+ amountSat: 100,
24
+ });
25
+
26
+ // Wait for payment via WebSocket
27
+ const status = await gw.waitForPayment(id, {
28
+ onStatusChange(s) { console.log('status:', s); },
29
+ });
30
+
31
+ console.log(status); // "success" | "failed" | "expired"
32
+ ```
33
+
34
+ ## API
35
+
36
+ ### `new RestGatewayClient(gatewayUrl)`
37
+
38
+ ```ts
39
+ const gw = new RestGatewayClient('https://donate.example.com');
40
+ ```
41
+
42
+ ---
43
+
44
+ ### `gw.createDonation(params)`
45
+
46
+ Create a new donation. Automatically solves a PoW challenge if required by the gateway.
47
+
48
+ ```ts
49
+ const { id, bolt11, paymentHash } = await gw.createDonation({
50
+ destination: 'alice@getalby.com', // Lightning Address or BOLT12 offer
51
+ amountSat: 100, // amount in millisatoshis
52
+ webhookUrl: 'https://myapp.com/hook', // optional
53
+ webhookSecret: 'my-hmac-secret', // optional HMAC-SHA256 signing key
54
+ });
55
+ ```
56
+
57
+ | Param | Type | Description |
58
+ |-------|------|-------------|
59
+ | `destination` | `string` | Lightning Address (`user@domain`) or BOLT12 offer (`lno1...`) |
60
+ | `amountSat` | `number` | Amount in satoshis (min 1) |
61
+ | `webhookUrl` | `string?` | URL to POST on successful payout |
62
+ | `webhookSecret` | `string?` | HMAC-SHA256 key for signing webhook payloads (sent to the gateway, use HTTPS) |
63
+
64
+ Returns `{ id, bolt11, paymentHash }`.
65
+
66
+ ---
67
+
68
+ ### `gw.waitForPayment(donationId, options?)`
69
+
70
+ Subscribe to donation status updates via WebSocket. Resolves when the donation reaches a terminal status (`success`, `failed`, or `expired`).
71
+
72
+ ```ts
73
+ const status = await gw.waitForPayment(id, {
74
+ onStatusChange(s) { console.log(s); },
75
+ timeoutMs: 600_000, // 10 minutes
76
+ });
77
+ ```
78
+
79
+ ---
80
+
81
+ ### `gw.getDonation(id)`
82
+
83
+ Fetch a single donation by ID.
84
+
85
+ ```ts
86
+ const donation = await gw.getDonation('550e8400-...');
87
+ console.log(donation.status, donation.amount_msat);
88
+ ```
89
+
90
+ ---
91
+
92
+ ### `gw.listDonations(params?)`
93
+
94
+ List donations with optional pagination.
95
+
96
+ ```ts
97
+ const donations = await gw.listDonations({ limit: 10, offset: 0 });
98
+ ```
99
+
100
+ ---
101
+
102
+ ### `gw.estimateFees(amountMsat)`
103
+
104
+ Estimate gateway fees for a given amount.
105
+
106
+ ```ts
107
+ const fees = await gw.estimateFees(1_000);
108
+ console.log(`Payout: ${fees.payout_estimate_sat} sat`);
109
+ console.log(`Total fee: ${fees.total_fee_estimate_sat} sat`);
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Webhooks
115
+
116
+ When you provide `webhookUrl` on donation creation, the gateway POSTs a JSON payload to that URL immediately after a successful payout. If delivery fails, it retries with increasing delays: 10s, 1m, 2m, 5m, 10m, 30m, 1h, then hourly up to 1 week.
117
+
118
+ ### Payload
119
+
120
+ ```json
121
+ {
122
+ "event": "donation.success",
123
+ "donation": {
124
+ "id": "550e8400-e29b-41d4-a716-446655440000",
125
+ "ln_address": "alice@getalby.com",
126
+ "amount_msat": 100000,
127
+ "status": "success",
128
+ "payment_hash": "abc123...",
129
+ "bolt11": "lnbc1000n1...",
130
+ "payout_fee_msat": 400,
131
+ "created_at": "2025-03-28T10:30:00Z",
132
+ "updated_at": "2025-03-28T10:31:00Z"
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### Headers
138
+
139
+ | Header | Description |
140
+ |--------|-------------|
141
+ | `Content-Type` | `application/json` |
142
+ | `X-Event-Type` | `donation.success` |
143
+ | `X-Signature-256` | HMAC-SHA256 hex signature (only if `webhookSecret` was provided) |
144
+
145
+ ### Verifying signatures
146
+
147
+ The client exports helpers to verify and parse webhook payloads:
148
+
149
+ #### `parseWebhookRequest(request, secret)` / Fetch API (Hono, Next.js, SvelteKit, Remix, Deno, Bun)
150
+
151
+ ```ts
152
+ import { parseWebhookRequest } from 'fulgur-bridge-client';
153
+
154
+ // Hono
155
+ app.post('/webhook', async (c) => {
156
+ const payload = await parseWebhookRequest(c.req.raw, MY_SECRET);
157
+ if (!payload) return c.text('Bad signature', 401);
158
+ console.log('Donation succeeded:', payload.donation.id);
159
+ return c.text('OK');
160
+ });
161
+ ```
162
+
163
+ ```ts
164
+ // Next.js app router (app/api/webhook/route.ts)
165
+ import { parseWebhookRequest } from 'fulgur-bridge-client';
166
+
167
+ export async function POST(req: Request) {
168
+ const payload = await parseWebhookRequest(req, process.env.WEBHOOK_SECRET!);
169
+ if (!payload) return new Response('Bad signature', { status: 401 });
170
+ // payload.donation is fully typed
171
+ return new Response('OK');
172
+ }
173
+ ```
174
+
175
+ #### `parseWebhook(body, signature, secret)` / Express / Fastify / raw body
176
+
177
+ ```ts
178
+ import { parseWebhook } from 'fulgur-bridge-client';
179
+
180
+ // Express (with express.raw() middleware on this route)
181
+ app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
182
+ const sig = req.headers['x-signature-256'] as string;
183
+ const payload = await parseWebhook(req.body, sig, MY_SECRET);
184
+ if (!payload) return res.status(401).send('Bad signature');
185
+ console.log(payload.donation.id);
186
+ res.sendStatus(200);
187
+ });
188
+ ```
189
+
190
+ #### `verifyWebhookSignature(body, signature, secret)`
191
+
192
+ ```ts
193
+ import { verifyWebhookSignature } from 'fulgur-bridge-client';
194
+
195
+ const valid = await verifyWebhookSignature(rawBody, signature, secret);
196
+ ```
197
+
198
+ All functions are async. All signature checks use constant-time comparison.
199
+
200
+ ---
201
+
202
+ ## QR codes
203
+
204
+ Generate QR codes for Lightning invoices or BOLT12 offers. Returns SVG strings, no canvas or browser APIs needed.
205
+
206
+ ```ts
207
+ import { invoiceToSvg, invoiceToDataUrl } from 'fulgur-bridge-client';
208
+
209
+ // SVG string (for innerHTML, SSR, etc.)
210
+ const svg = invoiceToSvg(bolt11, { size: 300, color: '#1a1a2e' });
211
+
212
+ // Data URL (for <img src="...">)
213
+ const dataUrl = invoiceToDataUrl(bolt11);
214
+ ```
215
+
216
+ BOLT11 invoices get a `LIGHTNING:` prefix for better wallet compatibility. BOLT12 offers are encoded as-is.
217
+
218
+ ---
219
+
220
+ ## Utilities
221
+
222
+ ### Lightning address detection
223
+
224
+ Basic format checks for UI heuristics (icon switching, input hints). Full validation happens server-side.
225
+
226
+ ```ts
227
+ import { isLnAddress, isBolt12Offer, detectDestinationType } from 'fulgur-bridge-client';
228
+
229
+ isLnAddress('alice@getalby.com'); // true
230
+ isBolt12Offer('lno1qgsqvgnwgcg35z6...'); // true
231
+ detectDestinationType('alice@getalby.com'); // "lnAddress"
232
+ ```
233
+
234
+ ### PoW solver
235
+
236
+ The gateway requires proof-of-work when an IP creates too many unpaid invoices (anti-spam). `createDonation()` handles this transparently. Picks the fastest available strategy:
237
+
238
+ 1. `node:crypto` (native C++): Node.js, Bun, Deno
239
+ 2. Pure JS SHA-256: Cloudflare Workers, edge runtimes
240
+ 3. `crypto.subtle` (async, yielding): browsers
241
+
242
+ ```ts
243
+ import { solvePow, verifyPow } from 'fulgur-bridge-client';
244
+
245
+ const nonce = await solvePow(challenge, difficulty);
246
+ const valid = await verifyPow(challenge, nonce, difficulty);
247
+ ```
248
+
249
+ ---
250
+
251
+ All types (`Donation`, `DonationStatus`, `CreateDonationParams`, `WebhookPayload`, etc.) are exported from the package root.
252
+
253
+ ## License
254
+
255
+ MIT