Skip to content
blocktrailsPublic

About

HTTP 402 Payment Required specification with NIP-98 authentication

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

HTTP 402 Payment Required

A specification for token-gated API access using NIP-98 authentication.

Overview

HTTP 402 Payment Required is an underutilized status code designed for digital payment systems. This specification defines how to use it with:

  • NIP-98 for authentication (Nostr HTTP Auth)
  • Token balances for authorization
  • Automatic deduction on successful requests

Flow

Client                                  Server
   |                                       |
   |  GET /api/resource                    |
   |-------------------------------------->|
   |                                       |
   |  402 Payment Required                 |
   |  { cost: 1, nip98: {...} }            |
   |<--------------------------------------|
   |                                       |
   |  GET /api/resource                    |
   |  Authorization: Nostr <base64-event>  |
   |-------------------------------------->|
   |                                       |
   |  (verify event, deduct tokens)        |
   |                                       |
   |  200 OK                               |
   |  { data: ..., remaining: 99 }         |
   |<--------------------------------------|

NIP-98 Event Format

Create a kind 27235 event with the request details:

{
  "kind": 27235,
  "created_at": 1704000000,
  "tags": [
    ["u", "http://api.example.com/resource"],
    ["method", "GET"]
  ],
  "content": "",
  "pubkey": "<64-char-hex>",
  "id": "<64-char-hex>",
  "sig": "<128-char-hex>"
}

Base64 encode and send in the Authorization header:

Authorization: Nostr eyJraW5kIjoyNzIzNSwi...

402 Response Format

When payment is required:

{
  "error": "Insufficient balance",
  "cost": 1,
  "balance": 0,
  "depositAddress": "tb1p...",
  "nip98": {
    "kind": 27235,
    "url": "http://api.example.com/resource",
    "method": "GET"
  }
}

Server Verification

  1. Parse Authorization: Nostr <base64> header
  2. Decode and parse JSON event
  3. Verify event kind is 27235
  4. Verify created_at is within 60 seconds
  5. Verify u tag matches request URL exactly
  6. Verify method tag matches request method
  7. Verify event ID matches serialized content hash
  8. Verify schnorr signature
  9. Check user balance >= cost
  10. Deduct tokens and process request

Success Response

Include remaining balance in response:

{
  "data": { ... },
  "paid": 1,
  "remaining": 99
}

Token Acquisition

Users acquire tokens through:

  • AMM deposit - Send sats to deposit address, receive tokens
  • Transfer - Receive tokens from another user
  • Faucet - Free tokens for testing

Identity

Users are identified by their Nostr public key as a DID:

did:nostr:<64-char-hex-pubkey>

Implementation

Reference implementation: gitmark-amm

// Server-side verification
function verify402Payment(req, cost, fullUrl) {
  const authHeader = req.headers['authorization'];
  if (!authHeader?.startsWith('Nostr ')) {
    return { authorized: false, error: 'Missing NIP-98 Authorization header' };
  }

  const base64 = authHeader.slice(6);
  const event = JSON.parse(Buffer.from(base64, 'base64').toString('utf-8'));

  // Verify event...
  if (event.kind !== 27235) return { authorized: false, error: 'Invalid kind' };
  // ... (see full implementation in gitmark-amm)
}

// Client-side event creation
function createNip98Event(method, url, privkey) {
  const event = {
    kind: 27235,
    created_at: Math.floor(Date.now() / 1000),
    tags: [['u', url], ['method', method]],
    content: '',
    pubkey: getPublicKey(privkey)
  };

  event.id = sha256(serialize(event));
  event.sig = schnorr.sign(event.id, privkey);
  return event;
}

Security Considerations

  • Replay protection: 60-second timestamp window
  • URL binding: Event is bound to specific URL
  • Method binding: Event is bound to HTTP method
  • No secrets in URL: Use POST body for sensitive data
  • HTTPS recommended: Prevents header interception

Related

License

Public Domain (CC0)

About

HTTP 402 Payment Required specification with NIP-98 authentication

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors