@moneydevkit/nextjs 0.11.0-beta.2 → 0.11.0-beta.4

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.
Files changed (2) hide show
  1. package/README.md +178 -1
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -234,4 +234,181 @@ export default function SuccessPage() {
234
234
  </div>
235
235
  )
236
236
  }
237
- ```
237
+ ```
238
+
239
+ ## MDK402: Pay-per-call API Endpoints
240
+
241
+ Gate any API route behind a Lightning payment using the HTTP 402 protocol. No accounts, no subscriptions — clients pay a Lightning invoice and get immediate access.
242
+
243
+ ### How it works
244
+
245
+ ```
246
+ Client Your Server Lightning
247
+ │ │ │
248
+ │ GET /api/premium │ │
249
+ │──────────────────────────────► │ │
250
+ │ │ │
251
+ │ 402 Payment Required │ │
252
+ │ { invoice, token, amount } │ │
253
+ │ ◄──────────────────────────── │ │
254
+ │ │ │
255
+ │ pay invoice ──────────────────┼────────────────────────────► │
256
+ │ ◄── preimage ─────────────────┼──────────────────────────────│
257
+ │ │ │
258
+ │ GET /api/premium │ │
259
+ │ Authorization: MDK402 <token>:<preimage> │
260
+ │──────────────────────────────► │ │
261
+ │ │ verify token + preimage │
262
+ │ 200 OK { data } │ │
263
+ │ ◄──────────────────────────── │ │
264
+ ```
265
+
266
+ 1. Client requests a protected endpoint without credentials
267
+ 2. Server returns **402** with a Lightning invoice and a signed token
268
+ 3. Client pays the invoice and receives a preimage (proof of payment)
269
+ 4. Client retries with `Authorization: MDK402 <token>:<preimage>`
270
+ 5. Server verifies the token, expiry, and preimage — then forwards to the handler
271
+
272
+ ### Setup
273
+
274
+ Make sure `MDK_ACCESS_TOKEN` is set in your environment (same key used for checkout):
275
+
276
+ ```env
277
+ MDK_ACCESS_TOKEN=your_api_key_here
278
+ MDK_MNEMONIC=your_mnemonic_here
279
+ ```
280
+
281
+ ### Basic usage
282
+
283
+ ```ts
284
+ // app/api/premium/route.ts
285
+ import { withPayment } from '@moneydevkit/nextjs/server'
286
+
287
+ const handler = async (req: Request) => {
288
+ return Response.json({ content: 'Premium data' })
289
+ }
290
+
291
+ export const GET = withPayment(
292
+ { amount: 100, currency: 'SAT' },
293
+ handler,
294
+ )
295
+ ```
296
+
297
+ Every `GET /api/premium` request without valid credentials returns a 402 with a Lightning invoice. After payment, the same request with the authorization header returns the premium data.
298
+
299
+ ### Dynamic pricing
300
+
301
+ Pass a function instead of a fixed number to compute the price from the request:
302
+
303
+ ```ts
304
+ // app/api/ai/route.ts
305
+ import { withPayment } from '@moneydevkit/nextjs/server'
306
+
307
+ const handler = async (req: Request) => {
308
+ const { model } = await req.json()
309
+ const result = await runInference(model)
310
+ return Response.json({ result })
311
+ }
312
+
313
+ export const POST = withPayment(
314
+ {
315
+ amount: (req: Request) => {
316
+ const url = new URL(req.url)
317
+ const tier = url.searchParams.get('tier')
318
+ if (tier === 'pro') return 500
319
+ return 100
320
+ },
321
+ currency: 'SAT',
322
+ },
323
+ handler,
324
+ )
325
+ ```
326
+
327
+ The pricing function is evaluated both when creating the invoice and when verifying the token. If the price changes between issuance and verification (e.g., the client replays a cheap token on an expensive tier), the request is rejected with `amount_mismatch`.
328
+
329
+ ### Fiat pricing
330
+
331
+ Use `currency: 'USD'` to price in US cents. The SDK converts to sats at the current exchange rate when generating the invoice:
332
+
333
+ ```ts
334
+ export const GET = withPayment(
335
+ { amount: 50, currency: 'USD' }, // $0.50
336
+ handler,
337
+ )
338
+ ```
339
+
340
+ ### Token expiry
341
+
342
+ Tokens (and their invoices) expire after 15 minutes by default. Override with `expirySeconds`:
343
+
344
+ ```ts
345
+ export const GET = withPayment(
346
+ { amount: 100, currency: 'SAT', expirySeconds: 300 }, // 5 minutes
347
+ handler,
348
+ )
349
+ ```
350
+
351
+ ### Client integration
352
+
353
+ Any HTTP client can consume an MDK402 endpoint:
354
+
355
+ ```bash
356
+ # 1. Request the protected resource
357
+ curl -s https://example.com/api/premium
358
+
359
+ # Response: 402
360
+ # {
361
+ # "token": "eyJ...",
362
+ # "invoice": "lnbc...",
363
+ # "paymentHash": "abc123...",
364
+ # "amountSats": 100,
365
+ # "expiresAt": 1234567890
366
+ # }
367
+
368
+ # 2. Pay the invoice with any Lightning wallet and get the preimage
369
+
370
+ # 3. Retry with the token and preimage
371
+ curl -s https://example.com/api/premium \
372
+ -H "Authorization: MDK402 eyJ...:ff00aa..."
373
+
374
+ # Response: 200 { "content": "Premium data" }
375
+ ```
376
+
377
+ The `WWW-Authenticate` header also contains the token and invoice:
378
+
379
+ ```
380
+ WWW-Authenticate: MDK402 token="eyJ...", invoice="lnbc..."
381
+ ```
382
+
383
+ ### Programmatic client (Node.js / agent)
384
+
385
+ ```ts
386
+ async function callPaidEndpoint(url: string, payFn: (invoice: string) => Promise<string>) {
387
+ // Step 1: get the 402 challenge
388
+ const challenge = await fetch(url)
389
+ if (challenge.status !== 402) return challenge
390
+
391
+ const { token, invoice } = await challenge.json()
392
+
393
+ // Step 2: pay the invoice (returns preimage)
394
+ const preimage = await payFn(invoice)
395
+
396
+ // Step 3: retry with proof of payment
397
+ return fetch(url, {
398
+ headers: { Authorization: `MDK402 ${token}:${preimage}` },
399
+ })
400
+ }
401
+ ```
402
+
403
+ ### Error codes
404
+
405
+ | Status | Code | Meaning |
406
+ |--------|------|---------|
407
+ | 402 | `payment_required` | No valid credentials — pay the returned invoice |
408
+ | 401 | `invalid_token` | Token is malformed or has a bad signature |
409
+ | 401 | `invalid_payment_proof` | Preimage does not match the payment hash |
410
+ | 403 | `resource_mismatch` | Token was issued for a different endpoint |
411
+ | 403 | `amount_mismatch` | Token was issued for a different price |
412
+ | 500 | `configuration_error` | `MDK_ACCESS_TOKEN` is not set |
413
+ | 500 | `pricing_error` | Dynamic pricing function threw an error |
414
+ | 502 | `checkout_creation_failed` | Failed to create the checkout or invoice |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneydevkit/nextjs",
3
- "version": "0.11.0-beta.2",
3
+ "version": "0.11.0-beta.4",
4
4
  "title": "@moneydevkit/nextjs",
5
5
  "description": "moneydevkit checkout components for Next.js.",
6
6
  "repository": {
@@ -64,7 +64,7 @@
64
64
  "vaul": "^1.1.2",
65
65
  "zod": "^3.25.42",
66
66
  "@moneydevkit/api-contract": "0.1.21",
67
- "@moneydevkit/core": "0.11.0-beta.2"
67
+ "@moneydevkit/core": "0.11.0-beta.4"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/node": "^20.10.5",