l402-server 0.1.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.
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/dist/index.cjs +267 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +300 -0
- package/dist/index.d.ts +300 -0
- package/dist/index.js +235 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the L402 server SDK. Mirrors the wire contract of
|
|
3
|
+
* Lightning Enable's hosted producer API at
|
|
4
|
+
* https://api.lightningenable.com/api/l402/challenges.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the {@link L402Server} client.
|
|
8
|
+
*/
|
|
9
|
+
interface L402ServerOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Your Lightning Enable merchant API key. Required.
|
|
12
|
+
*
|
|
13
|
+
* Generate one at https://api.lightningenable.com/dashboard/settings.
|
|
14
|
+
* Tied to a specific merchant + an Agentic Commerce plan (L402 must be
|
|
15
|
+
* enabled on the plan).
|
|
16
|
+
*/
|
|
17
|
+
apiKey: string;
|
|
18
|
+
/**
|
|
19
|
+
* Base URL for the producer API. Defaults to the hosted Lightning Enable
|
|
20
|
+
* instance. Override for testing against a local dev instance.
|
|
21
|
+
*
|
|
22
|
+
* @default "https://api.lightningenable.com"
|
|
23
|
+
*/
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Per-request timeout in milliseconds. Defaults to 10s.
|
|
27
|
+
*
|
|
28
|
+
* @default 10000
|
|
29
|
+
*/
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Custom fetch implementation. Defaults to the global `fetch`. Inject a
|
|
33
|
+
* mock here for tests, or a fetch-with-retry wrapper if you want
|
|
34
|
+
* application-level retry policy on top of the SDK's per-call timeout.
|
|
35
|
+
*/
|
|
36
|
+
fetch?: typeof fetch;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Arguments for {@link L402Server.createChallenge}.
|
|
40
|
+
*/
|
|
41
|
+
interface CreateChallengeArgs {
|
|
42
|
+
/**
|
|
43
|
+
* The resource the challenge is for, typically the request path. Bound as
|
|
44
|
+
* a caveat in the macaroon so the resulting token can only access this
|
|
45
|
+
* exact resource. Example: "/api/weather/forecast".
|
|
46
|
+
*/
|
|
47
|
+
resource: string;
|
|
48
|
+
/**
|
|
49
|
+
* Price in satoshis. Must be ≥ 1.
|
|
50
|
+
*/
|
|
51
|
+
priceSats: number;
|
|
52
|
+
/**
|
|
53
|
+
* Optional description embedded in the Lightning invoice. Visible to the
|
|
54
|
+
* payer in their wallet UI.
|
|
55
|
+
*/
|
|
56
|
+
description?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional idempotency key. If the same key is sent twice within the
|
|
59
|
+
* invoice's expiry window, the same challenge is returned (no duplicate
|
|
60
|
+
* invoice). Useful for retry-safe issue from a middleware. Truncated to
|
|
61
|
+
* 256 chars server-side.
|
|
62
|
+
*
|
|
63
|
+
* Defaults to none — the server falls back to client IP for
|
|
64
|
+
* deduplication, which is usually correct for middleware use.
|
|
65
|
+
*/
|
|
66
|
+
idempotencyKey?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The 402 challenge returned from {@link L402Server.createChallenge}.
|
|
70
|
+
* Present this to the calling client/agent; they must pay the Lightning
|
|
71
|
+
* invoice to obtain the preimage required for L402 authentication.
|
|
72
|
+
*/
|
|
73
|
+
interface Challenge {
|
|
74
|
+
/**
|
|
75
|
+
* BOLT11 Lightning invoice the client must pay.
|
|
76
|
+
*/
|
|
77
|
+
invoice: string;
|
|
78
|
+
/**
|
|
79
|
+
* Base64-encoded macaroon containing the payment hash and caveats
|
|
80
|
+
* (resource, merchant_id, amount_sats). Sent as part of the
|
|
81
|
+
* `WWW-Authenticate` header on the 402 response.
|
|
82
|
+
*/
|
|
83
|
+
macaroon: string;
|
|
84
|
+
/**
|
|
85
|
+
* Payment hash (hex) — links the macaroon to the invoice. Useful for
|
|
86
|
+
* matching incoming payment-confirmation webhooks or for debugging.
|
|
87
|
+
*/
|
|
88
|
+
paymentHash: string;
|
|
89
|
+
/**
|
|
90
|
+
* When the Lightning invoice expires (ISO 8601). After this time the
|
|
91
|
+
* invoice can no longer be paid and a fresh challenge must be issued.
|
|
92
|
+
*/
|
|
93
|
+
expiresAt: string;
|
|
94
|
+
/**
|
|
95
|
+
* The resource the challenge is bound to. Echoed back from the request
|
|
96
|
+
* so middleware can build the 402 response header without re-tracking
|
|
97
|
+
* the original argument.
|
|
98
|
+
*/
|
|
99
|
+
resource: string;
|
|
100
|
+
/**
|
|
101
|
+
* Price in satoshis. Echoed from the request.
|
|
102
|
+
*/
|
|
103
|
+
priceSats: number;
|
|
104
|
+
/**
|
|
105
|
+
* MPP-formatted `WWW-Authenticate` challenge header value. Only present
|
|
106
|
+
* if the server has MPP support enabled. Clients that speak MPP can
|
|
107
|
+
* consume this directly without needing to know about macaroons.
|
|
108
|
+
*/
|
|
109
|
+
mppChallenge?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Arguments for {@link L402Server.verifyToken}.
|
|
113
|
+
*/
|
|
114
|
+
interface VerifyTokenArgs {
|
|
115
|
+
/**
|
|
116
|
+
* Base64-encoded macaroon from the L402 credential
|
|
117
|
+
* (`Authorization: L402 <macaroon>:<preimage>`).
|
|
118
|
+
*
|
|
119
|
+
* Omit only if verifying an MPP-style preimage-only token — and only if
|
|
120
|
+
* your Lightning Enable instance has MPP enabled. Defaults to required.
|
|
121
|
+
*/
|
|
122
|
+
macaroon?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Hex-encoded payment preimage (64 chars).
|
|
125
|
+
*/
|
|
126
|
+
preimage: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Result from {@link L402Server.verifyToken}. The producer API returns
|
|
130
|
+
* `200 OK` for both valid and invalid tokens — `valid` is the gate.
|
|
131
|
+
*/
|
|
132
|
+
interface VerificationResult {
|
|
133
|
+
/**
|
|
134
|
+
* Whether the token is valid. When `false`, `error` is populated.
|
|
135
|
+
*/
|
|
136
|
+
valid: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Human-readable failure reason. Only populated when `valid === false`.
|
|
139
|
+
* Examples: "Invalid preimage", "Token bound to a different resource",
|
|
140
|
+
* "Macaroon signature invalid".
|
|
141
|
+
*/
|
|
142
|
+
error?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The resource the token was bound to (from the macaroon's path caveat).
|
|
145
|
+
* Only populated when `valid === true`. Use this to assert the token
|
|
146
|
+
* matches the resource the caller is actually requesting.
|
|
147
|
+
*/
|
|
148
|
+
resource?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Merchant ID the token was bound to. Only populated when valid.
|
|
151
|
+
*/
|
|
152
|
+
merchantId?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Amount the token was issued for, in satoshis. Only populated when
|
|
155
|
+
* valid.
|
|
156
|
+
*/
|
|
157
|
+
amountSats?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Payment hash from the macaroon identifier.
|
|
160
|
+
*/
|
|
161
|
+
paymentHash?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Server-side client for Lightning Enable's L402 producer API. Wraps two
|
|
166
|
+
* endpoints:
|
|
167
|
+
*
|
|
168
|
+
* - {@link createChallenge} → `POST /api/l402/challenges` — mint a
|
|
169
|
+
* Lightning invoice + macaroon for a given resource and price.
|
|
170
|
+
* - {@link verifyToken} → `POST /api/l402/challenges/verify` — validate
|
|
171
|
+
* an incoming L402 token (macaroon + preimage).
|
|
172
|
+
*
|
|
173
|
+
* **No protocol logic lives in this SDK.** The Lightning Enable backend
|
|
174
|
+
* signs macaroons, mints invoices, verifies preimages, and tracks consumed
|
|
175
|
+
* tokens for replay protection. The SDK is purely an HTTP client with
|
|
176
|
+
* typed inputs/outputs.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* import { L402Server } from "l402-server";
|
|
181
|
+
*
|
|
182
|
+
* const l402 = new L402Server({
|
|
183
|
+
* apiKey: process.env.LIGHTNING_ENABLE_API_KEY!,
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* // On an unauthenticated incoming request:
|
|
187
|
+
* const challenge = await l402.createChallenge({
|
|
188
|
+
* resource: "/api/premium/weather",
|
|
189
|
+
* priceSats: 100,
|
|
190
|
+
* description: "Premium weather forecast",
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // Send back as 402 Payment Required with the challenge headers.
|
|
194
|
+
*
|
|
195
|
+
* // When client comes back with Authorization: L402 mac:preimage,
|
|
196
|
+
* // parse and verify:
|
|
197
|
+
* const verification = await l402.verifyToken({
|
|
198
|
+
* macaroon: parsedMacaroon,
|
|
199
|
+
* preimage: parsedPreimage,
|
|
200
|
+
* });
|
|
201
|
+
* if (verification.valid) {
|
|
202
|
+
* // Serve the response.
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
declare class L402Server {
|
|
207
|
+
private readonly apiKey;
|
|
208
|
+
private readonly baseUrl;
|
|
209
|
+
private readonly timeoutMs;
|
|
210
|
+
private readonly fetchImpl;
|
|
211
|
+
constructor(options: L402ServerOptions);
|
|
212
|
+
/**
|
|
213
|
+
* Mint an L402 challenge — a Lightning invoice plus a macaroon scoped to
|
|
214
|
+
* the given resource. Present this to the requesting client/agent in a
|
|
215
|
+
* `402 Payment Required` response. Once they pay the invoice and obtain
|
|
216
|
+
* the preimage, they will retry the request with
|
|
217
|
+
* `Authorization: L402 <macaroon>:<preimage>`.
|
|
218
|
+
*
|
|
219
|
+
* @param args - resource path, price in sats, optional description and idempotency key.
|
|
220
|
+
* @returns The {@link Challenge} containing the invoice, macaroon, and metadata.
|
|
221
|
+
* @throws {@link L402AuthError} on 401 (invalid API key).
|
|
222
|
+
* @throws {@link L402PlanError} on 403 (L402 not enabled on merchant's plan).
|
|
223
|
+
* @throws {@link L402ApiError} on other non-2xx responses.
|
|
224
|
+
* @throws {@link L402NetworkError} on timeout or transport failure.
|
|
225
|
+
*/
|
|
226
|
+
createChallenge(args: CreateChallengeArgs): Promise<Challenge>;
|
|
227
|
+
/**
|
|
228
|
+
* Verify an L402 token. Returns a {@link VerificationResult} indicating
|
|
229
|
+
* whether the token is valid plus metadata extracted from the macaroon
|
|
230
|
+
* (resource, merchant ID, amount).
|
|
231
|
+
*
|
|
232
|
+
* The producer API returns `200 OK` for both valid and invalid tokens;
|
|
233
|
+
* inspect `result.valid` rather than relying on HTTP status. Non-200
|
|
234
|
+
* responses indicate a higher-level problem (auth, plan, transport).
|
|
235
|
+
*
|
|
236
|
+
* @param args - macaroon (required for L402; omit only for MPP) + preimage.
|
|
237
|
+
* @returns The {@link VerificationResult}.
|
|
238
|
+
* @throws {@link L402AuthError} on 401 (invalid API key).
|
|
239
|
+
* @throws {@link L402PlanError} on 403 (L402 not enabled on merchant's plan).
|
|
240
|
+
* @throws {@link L402ApiError} on other non-2xx responses.
|
|
241
|
+
* @throws {@link L402NetworkError} on timeout or transport failure.
|
|
242
|
+
*/
|
|
243
|
+
verifyToken(args: VerifyTokenArgs): Promise<VerificationResult>;
|
|
244
|
+
private request;
|
|
245
|
+
private throwForStatus;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Base error class for all SDK-thrown errors. Distinguishable from arbitrary
|
|
250
|
+
* `Error` instances via `instanceof L402ServerError`.
|
|
251
|
+
*/
|
|
252
|
+
declare class L402ServerError extends Error {
|
|
253
|
+
constructor(message: string, options?: ErrorOptions);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Thrown on `401 Unauthorized` from the producer API. Almost always means
|
|
257
|
+
* the merchant API key is missing, malformed, expired, or revoked.
|
|
258
|
+
*/
|
|
259
|
+
declare class L402AuthError extends L402ServerError {
|
|
260
|
+
constructor(message?: string);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Thrown on `403 Forbidden` from the producer API. Means the merchant
|
|
264
|
+
* exists and the key is valid, but L402 is not enabled on their plan.
|
|
265
|
+
* The merchant needs to upgrade to an Agentic Commerce plan.
|
|
266
|
+
*/
|
|
267
|
+
declare class L402PlanError extends L402ServerError {
|
|
268
|
+
/**
|
|
269
|
+
* The plan tier currently on the merchant (e.g., "starter").
|
|
270
|
+
* Populated when the server includes it in the error payload.
|
|
271
|
+
*/
|
|
272
|
+
readonly currentPlan?: string;
|
|
273
|
+
constructor(message?: string, currentPlan?: string);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Thrown for transport-level failures: timeout, DNS error, TLS error,
|
|
277
|
+
* unreachable host. The `cause` carries the original error.
|
|
278
|
+
*/
|
|
279
|
+
declare class L402NetworkError extends L402ServerError {
|
|
280
|
+
constructor(message: string, cause?: unknown);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Thrown when the server returns a non-success status that doesn't map to
|
|
284
|
+
* a more specific error class above (e.g., 400 with a request-validation
|
|
285
|
+
* problem, 500 from upstream wallet failure, 429 from rate limiting).
|
|
286
|
+
*/
|
|
287
|
+
declare class L402ApiError extends L402ServerError {
|
|
288
|
+
/**
|
|
289
|
+
* HTTP status code returned by the producer API.
|
|
290
|
+
*/
|
|
291
|
+
readonly statusCode: number;
|
|
292
|
+
/**
|
|
293
|
+
* Raw response body, useful for debugging. May be a parsed object or a
|
|
294
|
+
* string if parsing failed.
|
|
295
|
+
*/
|
|
296
|
+
readonly responseBody?: unknown;
|
|
297
|
+
constructor(statusCode: number, message: string, responseBody?: unknown);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { type Challenge, type CreateChallengeArgs, L402ApiError, L402AuthError, L402NetworkError, L402PlanError, L402Server, L402ServerError, type L402ServerOptions, type VerificationResult, type VerifyTokenArgs };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the L402 server SDK. Mirrors the wire contract of
|
|
3
|
+
* Lightning Enable's hosted producer API at
|
|
4
|
+
* https://api.lightningenable.com/api/l402/challenges.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the {@link L402Server} client.
|
|
8
|
+
*/
|
|
9
|
+
interface L402ServerOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Your Lightning Enable merchant API key. Required.
|
|
12
|
+
*
|
|
13
|
+
* Generate one at https://api.lightningenable.com/dashboard/settings.
|
|
14
|
+
* Tied to a specific merchant + an Agentic Commerce plan (L402 must be
|
|
15
|
+
* enabled on the plan).
|
|
16
|
+
*/
|
|
17
|
+
apiKey: string;
|
|
18
|
+
/**
|
|
19
|
+
* Base URL for the producer API. Defaults to the hosted Lightning Enable
|
|
20
|
+
* instance. Override for testing against a local dev instance.
|
|
21
|
+
*
|
|
22
|
+
* @default "https://api.lightningenable.com"
|
|
23
|
+
*/
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Per-request timeout in milliseconds. Defaults to 10s.
|
|
27
|
+
*
|
|
28
|
+
* @default 10000
|
|
29
|
+
*/
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Custom fetch implementation. Defaults to the global `fetch`. Inject a
|
|
33
|
+
* mock here for tests, or a fetch-with-retry wrapper if you want
|
|
34
|
+
* application-level retry policy on top of the SDK's per-call timeout.
|
|
35
|
+
*/
|
|
36
|
+
fetch?: typeof fetch;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Arguments for {@link L402Server.createChallenge}.
|
|
40
|
+
*/
|
|
41
|
+
interface CreateChallengeArgs {
|
|
42
|
+
/**
|
|
43
|
+
* The resource the challenge is for, typically the request path. Bound as
|
|
44
|
+
* a caveat in the macaroon so the resulting token can only access this
|
|
45
|
+
* exact resource. Example: "/api/weather/forecast".
|
|
46
|
+
*/
|
|
47
|
+
resource: string;
|
|
48
|
+
/**
|
|
49
|
+
* Price in satoshis. Must be ≥ 1.
|
|
50
|
+
*/
|
|
51
|
+
priceSats: number;
|
|
52
|
+
/**
|
|
53
|
+
* Optional description embedded in the Lightning invoice. Visible to the
|
|
54
|
+
* payer in their wallet UI.
|
|
55
|
+
*/
|
|
56
|
+
description?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional idempotency key. If the same key is sent twice within the
|
|
59
|
+
* invoice's expiry window, the same challenge is returned (no duplicate
|
|
60
|
+
* invoice). Useful for retry-safe issue from a middleware. Truncated to
|
|
61
|
+
* 256 chars server-side.
|
|
62
|
+
*
|
|
63
|
+
* Defaults to none — the server falls back to client IP for
|
|
64
|
+
* deduplication, which is usually correct for middleware use.
|
|
65
|
+
*/
|
|
66
|
+
idempotencyKey?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The 402 challenge returned from {@link L402Server.createChallenge}.
|
|
70
|
+
* Present this to the calling client/agent; they must pay the Lightning
|
|
71
|
+
* invoice to obtain the preimage required for L402 authentication.
|
|
72
|
+
*/
|
|
73
|
+
interface Challenge {
|
|
74
|
+
/**
|
|
75
|
+
* BOLT11 Lightning invoice the client must pay.
|
|
76
|
+
*/
|
|
77
|
+
invoice: string;
|
|
78
|
+
/**
|
|
79
|
+
* Base64-encoded macaroon containing the payment hash and caveats
|
|
80
|
+
* (resource, merchant_id, amount_sats). Sent as part of the
|
|
81
|
+
* `WWW-Authenticate` header on the 402 response.
|
|
82
|
+
*/
|
|
83
|
+
macaroon: string;
|
|
84
|
+
/**
|
|
85
|
+
* Payment hash (hex) — links the macaroon to the invoice. Useful for
|
|
86
|
+
* matching incoming payment-confirmation webhooks or for debugging.
|
|
87
|
+
*/
|
|
88
|
+
paymentHash: string;
|
|
89
|
+
/**
|
|
90
|
+
* When the Lightning invoice expires (ISO 8601). After this time the
|
|
91
|
+
* invoice can no longer be paid and a fresh challenge must be issued.
|
|
92
|
+
*/
|
|
93
|
+
expiresAt: string;
|
|
94
|
+
/**
|
|
95
|
+
* The resource the challenge is bound to. Echoed back from the request
|
|
96
|
+
* so middleware can build the 402 response header without re-tracking
|
|
97
|
+
* the original argument.
|
|
98
|
+
*/
|
|
99
|
+
resource: string;
|
|
100
|
+
/**
|
|
101
|
+
* Price in satoshis. Echoed from the request.
|
|
102
|
+
*/
|
|
103
|
+
priceSats: number;
|
|
104
|
+
/**
|
|
105
|
+
* MPP-formatted `WWW-Authenticate` challenge header value. Only present
|
|
106
|
+
* if the server has MPP support enabled. Clients that speak MPP can
|
|
107
|
+
* consume this directly without needing to know about macaroons.
|
|
108
|
+
*/
|
|
109
|
+
mppChallenge?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Arguments for {@link L402Server.verifyToken}.
|
|
113
|
+
*/
|
|
114
|
+
interface VerifyTokenArgs {
|
|
115
|
+
/**
|
|
116
|
+
* Base64-encoded macaroon from the L402 credential
|
|
117
|
+
* (`Authorization: L402 <macaroon>:<preimage>`).
|
|
118
|
+
*
|
|
119
|
+
* Omit only if verifying an MPP-style preimage-only token — and only if
|
|
120
|
+
* your Lightning Enable instance has MPP enabled. Defaults to required.
|
|
121
|
+
*/
|
|
122
|
+
macaroon?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Hex-encoded payment preimage (64 chars).
|
|
125
|
+
*/
|
|
126
|
+
preimage: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Result from {@link L402Server.verifyToken}. The producer API returns
|
|
130
|
+
* `200 OK` for both valid and invalid tokens — `valid` is the gate.
|
|
131
|
+
*/
|
|
132
|
+
interface VerificationResult {
|
|
133
|
+
/**
|
|
134
|
+
* Whether the token is valid. When `false`, `error` is populated.
|
|
135
|
+
*/
|
|
136
|
+
valid: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Human-readable failure reason. Only populated when `valid === false`.
|
|
139
|
+
* Examples: "Invalid preimage", "Token bound to a different resource",
|
|
140
|
+
* "Macaroon signature invalid".
|
|
141
|
+
*/
|
|
142
|
+
error?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The resource the token was bound to (from the macaroon's path caveat).
|
|
145
|
+
* Only populated when `valid === true`. Use this to assert the token
|
|
146
|
+
* matches the resource the caller is actually requesting.
|
|
147
|
+
*/
|
|
148
|
+
resource?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Merchant ID the token was bound to. Only populated when valid.
|
|
151
|
+
*/
|
|
152
|
+
merchantId?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Amount the token was issued for, in satoshis. Only populated when
|
|
155
|
+
* valid.
|
|
156
|
+
*/
|
|
157
|
+
amountSats?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Payment hash from the macaroon identifier.
|
|
160
|
+
*/
|
|
161
|
+
paymentHash?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Server-side client for Lightning Enable's L402 producer API. Wraps two
|
|
166
|
+
* endpoints:
|
|
167
|
+
*
|
|
168
|
+
* - {@link createChallenge} → `POST /api/l402/challenges` — mint a
|
|
169
|
+
* Lightning invoice + macaroon for a given resource and price.
|
|
170
|
+
* - {@link verifyToken} → `POST /api/l402/challenges/verify` — validate
|
|
171
|
+
* an incoming L402 token (macaroon + preimage).
|
|
172
|
+
*
|
|
173
|
+
* **No protocol logic lives in this SDK.** The Lightning Enable backend
|
|
174
|
+
* signs macaroons, mints invoices, verifies preimages, and tracks consumed
|
|
175
|
+
* tokens for replay protection. The SDK is purely an HTTP client with
|
|
176
|
+
* typed inputs/outputs.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* import { L402Server } from "l402-server";
|
|
181
|
+
*
|
|
182
|
+
* const l402 = new L402Server({
|
|
183
|
+
* apiKey: process.env.LIGHTNING_ENABLE_API_KEY!,
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* // On an unauthenticated incoming request:
|
|
187
|
+
* const challenge = await l402.createChallenge({
|
|
188
|
+
* resource: "/api/premium/weather",
|
|
189
|
+
* priceSats: 100,
|
|
190
|
+
* description: "Premium weather forecast",
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // Send back as 402 Payment Required with the challenge headers.
|
|
194
|
+
*
|
|
195
|
+
* // When client comes back with Authorization: L402 mac:preimage,
|
|
196
|
+
* // parse and verify:
|
|
197
|
+
* const verification = await l402.verifyToken({
|
|
198
|
+
* macaroon: parsedMacaroon,
|
|
199
|
+
* preimage: parsedPreimage,
|
|
200
|
+
* });
|
|
201
|
+
* if (verification.valid) {
|
|
202
|
+
* // Serve the response.
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
declare class L402Server {
|
|
207
|
+
private readonly apiKey;
|
|
208
|
+
private readonly baseUrl;
|
|
209
|
+
private readonly timeoutMs;
|
|
210
|
+
private readonly fetchImpl;
|
|
211
|
+
constructor(options: L402ServerOptions);
|
|
212
|
+
/**
|
|
213
|
+
* Mint an L402 challenge — a Lightning invoice plus a macaroon scoped to
|
|
214
|
+
* the given resource. Present this to the requesting client/agent in a
|
|
215
|
+
* `402 Payment Required` response. Once they pay the invoice and obtain
|
|
216
|
+
* the preimage, they will retry the request with
|
|
217
|
+
* `Authorization: L402 <macaroon>:<preimage>`.
|
|
218
|
+
*
|
|
219
|
+
* @param args - resource path, price in sats, optional description and idempotency key.
|
|
220
|
+
* @returns The {@link Challenge} containing the invoice, macaroon, and metadata.
|
|
221
|
+
* @throws {@link L402AuthError} on 401 (invalid API key).
|
|
222
|
+
* @throws {@link L402PlanError} on 403 (L402 not enabled on merchant's plan).
|
|
223
|
+
* @throws {@link L402ApiError} on other non-2xx responses.
|
|
224
|
+
* @throws {@link L402NetworkError} on timeout or transport failure.
|
|
225
|
+
*/
|
|
226
|
+
createChallenge(args: CreateChallengeArgs): Promise<Challenge>;
|
|
227
|
+
/**
|
|
228
|
+
* Verify an L402 token. Returns a {@link VerificationResult} indicating
|
|
229
|
+
* whether the token is valid plus metadata extracted from the macaroon
|
|
230
|
+
* (resource, merchant ID, amount).
|
|
231
|
+
*
|
|
232
|
+
* The producer API returns `200 OK` for both valid and invalid tokens;
|
|
233
|
+
* inspect `result.valid` rather than relying on HTTP status. Non-200
|
|
234
|
+
* responses indicate a higher-level problem (auth, plan, transport).
|
|
235
|
+
*
|
|
236
|
+
* @param args - macaroon (required for L402; omit only for MPP) + preimage.
|
|
237
|
+
* @returns The {@link VerificationResult}.
|
|
238
|
+
* @throws {@link L402AuthError} on 401 (invalid API key).
|
|
239
|
+
* @throws {@link L402PlanError} on 403 (L402 not enabled on merchant's plan).
|
|
240
|
+
* @throws {@link L402ApiError} on other non-2xx responses.
|
|
241
|
+
* @throws {@link L402NetworkError} on timeout or transport failure.
|
|
242
|
+
*/
|
|
243
|
+
verifyToken(args: VerifyTokenArgs): Promise<VerificationResult>;
|
|
244
|
+
private request;
|
|
245
|
+
private throwForStatus;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Base error class for all SDK-thrown errors. Distinguishable from arbitrary
|
|
250
|
+
* `Error` instances via `instanceof L402ServerError`.
|
|
251
|
+
*/
|
|
252
|
+
declare class L402ServerError extends Error {
|
|
253
|
+
constructor(message: string, options?: ErrorOptions);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Thrown on `401 Unauthorized` from the producer API. Almost always means
|
|
257
|
+
* the merchant API key is missing, malformed, expired, or revoked.
|
|
258
|
+
*/
|
|
259
|
+
declare class L402AuthError extends L402ServerError {
|
|
260
|
+
constructor(message?: string);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Thrown on `403 Forbidden` from the producer API. Means the merchant
|
|
264
|
+
* exists and the key is valid, but L402 is not enabled on their plan.
|
|
265
|
+
* The merchant needs to upgrade to an Agentic Commerce plan.
|
|
266
|
+
*/
|
|
267
|
+
declare class L402PlanError extends L402ServerError {
|
|
268
|
+
/**
|
|
269
|
+
* The plan tier currently on the merchant (e.g., "starter").
|
|
270
|
+
* Populated when the server includes it in the error payload.
|
|
271
|
+
*/
|
|
272
|
+
readonly currentPlan?: string;
|
|
273
|
+
constructor(message?: string, currentPlan?: string);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Thrown for transport-level failures: timeout, DNS error, TLS error,
|
|
277
|
+
* unreachable host. The `cause` carries the original error.
|
|
278
|
+
*/
|
|
279
|
+
declare class L402NetworkError extends L402ServerError {
|
|
280
|
+
constructor(message: string, cause?: unknown);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Thrown when the server returns a non-success status that doesn't map to
|
|
284
|
+
* a more specific error class above (e.g., 400 with a request-validation
|
|
285
|
+
* problem, 500 from upstream wallet failure, 429 from rate limiting).
|
|
286
|
+
*/
|
|
287
|
+
declare class L402ApiError extends L402ServerError {
|
|
288
|
+
/**
|
|
289
|
+
* HTTP status code returned by the producer API.
|
|
290
|
+
*/
|
|
291
|
+
readonly statusCode: number;
|
|
292
|
+
/**
|
|
293
|
+
* Raw response body, useful for debugging. May be a parsed object or a
|
|
294
|
+
* string if parsing failed.
|
|
295
|
+
*/
|
|
296
|
+
readonly responseBody?: unknown;
|
|
297
|
+
constructor(statusCode: number, message: string, responseBody?: unknown);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { type Challenge, type CreateChallengeArgs, L402ApiError, L402AuthError, L402NetworkError, L402PlanError, L402Server, L402ServerError, type L402ServerOptions, type VerificationResult, type VerifyTokenArgs };
|