Skip to content

feat(x402): production-grade HTTP payment rails - #468

Open
Unclebaffa wants to merge 2 commits into
Bitcoindefi:mainfrom
Unclebaffa:feat/x402-payment-rails
Open

feat(x402): production-grade HTTP payment rails#468
Unclebaffa wants to merge 2 commits into
Bitcoindefi:mainfrom
Unclebaffa:feat/x402-payment-rails

Conversation

@Unclebaffa

Copy link
Copy Markdown
Contributor

EPIC: x402 Payment Rails — Implementation Summary


1. Persistent Receipt Store

Problem: RECEIPT_REGISTRY was an in-memory Map — all receipts lost on restart.

Solution: lib/protocols/x402-receipt-store.ts — file-backed JSON at .data/x402-receipts.json

  • ensureDb() — auto-creates .data/ dir and empty JSON on first run
  • saveX402Receipt() — appends and persists
  • listX402Receipts(filters?) — filter by agentId, serviceId, or chain
  • getX402Receipt(id) — single lookup

2. Persistent Subscription Store

Problem: No recurring billing — every payment was one-off.

Solution: lib/protocols/x402-subscription-store.ts.data/x402-subscriptions.json

  • Subscriptions track monthlyCap, usedThisPeriod, periodStart, tier
  • settleX402() now checks active subscriptions, enforces the monthly cap, and auto-renews periods older than 30 days
  • renewSubscriptionPeriod() resets usedThisPeriod to 0 and advances periodStart

3. Webhook Callbacks

Problem: No external notification when a payment settled.

Solution: lib/protocols/x402-webhook-store.ts + app/api/protocol/x402/webhooks/route.ts

  • Register webhooks per serviceId via POST /api/protocol/x402/webhooks
  • dispatchX402Webhooks(serviceId, receipt) fires after every settlement — POSTs to all registered endpoints with:
    • x-x402-signature: sha256=<HMAC-SHA256>
    • x-x402-event: settlement
    • x-x402-timestamp
  • Non-blocking (Promise.allSettled) — a failing webhook never breaks payment flow
  • Full CRUD: POST / GET / DELETE on /api/protocol/x402/webhooks

4. Multi-Chain Settlement

Problem: Only Stellar was supported.

Solution: Settlement now supports Stellar, BNB Chain, and Base (Coinbase L2)

  • SettlementChain union type: "stellar" | "bnb" | "base"
  • settleX402() routes to chain-specific logic based on chain field
  • Quotes include chain and chainId (56 for BNB, 8453 for Base)
  • Receipts record which chain was used

5. Developer SDK: @open-stellar/x402

Problem: Integrating x402 required understanding the full quote/settle protocol.

Solution: lib/sdk/x402.ts + packages/x402-sdk/

// 5-line integration
import { withX402 } from '@open-stellar/x402'

export const GET = withX402('my-service-id', async (req) => {
  return Response.json({ data: 'protected content' })
})

withX402 middleware flow:

  1. Checks x-x402-receipt-id header — looks up receipt, verifies it belongs to this service, checks not expired/replayed
  2. Checks x-x402-subscription-token — decodes agentId:serviceId:tier, verifies active subscription with remaining cap
  3. If neither: returns 402 Payment Required with live quote headers
  4. If valid: passes through to the wrapped handler

The package is npm-publishable with CJS + ESM + TypeScript declaration outputs.


6. Service Marketplace UI

Problem: No visual browse experience for API services and prices.

Changes to components/marketplace/service-card.tsx:

  • Chain tags — coloured badges (Stellar = blue, BNB = yellow, Base = indigo)
  • "Subscription Available" pill when tiers exist
  • Per-chain pricing in native token
  • "Test x402 Gate" interactive modal — pick a chain, enter agent ID, calls quote + settle live, shows receipt ID on success

New app/api/protocol/x402/services/route.ts:

  • GET /api/protocol/x402/services — full catalog with per-chain pricing + subscription tiers
  • GET /api/protocol/x402/services?id=<id> — single service

7. Payment Explorer Enhancement

components/explorer/receipt-table.tsx — added Base to the chain filter dropdown.


8. New API Endpoints

Method Endpoint Description
GET/POST/DELETE /api/protocol/x402/webhooks Webhook CRUD
GET /api/protocol/x402/services Service catalog
GET /api/protocol/x402/receipts Persistent receipt list
GET /api/protocol/x402/receipts/[id] Single receipt
POST/GET /api/protocol/x402/subscriptions Subscription management
GET /api/protocol/x402/subscriptions/[agentId]/[serviceId] Single subscription

9. Tests Added

File Tests
__tests__/api/protocol/x402-receipts.test.ts Persistent list + filter, single lookup
__tests__/api/protocol/x402-webhooks.test.ts Registration, HMAC dispatch
__tests__/api/protocol/x402-subscriptions.test.ts Create, lookup, cap enforcement
__tests__/api/protocol/x402-sdk.test.ts 402 gate, receipt access, subscription access
e2e/x402-payment-rails.spec.ts Playwright: marketplace modal, explorer filter

Final results: 577/577 passing, 0 TypeScript errors, 0 lint errors, production build clean.


10. Bug Fixed: Windows renameSync EPERM

The atomic tmp → renameSync write pattern fails on Windows in two ways:

  1. EPERM — Windows locks the destination file during rename
  2. ENOENT (pending delete)unlinkSync marks the file for deletion; existsSync still returns true but readFileSync throws

Fix: All three stores now use a plain writeFileSync(DB_PATH, data) overwrite — cross-platform, no locking issues.


Data Files Created at Runtime

.data/
├── x402-receipts.json       # All payment receipts
├── x402-subscriptions.json  # Active subscriptions
└── x402-webhooks.json       # Registered webhook endpoints

All auto-created on first write, gitignored, and human-readable JSON.

Closes #18

- Persistent receipt store: file-backed JSON in .data/x402-receipts.json
- Persistent subscription store: .data/x402-subscriptions.json (survives restarts)
- Webhook callbacks: register/list/delete endpoints + HMAC SHA-256 signed dispatch
- dispatchX402Webhooks fires on every settlement via settleX402()
- Developer SDK: @open-stellar/x402 with withX402() 5-line route middleware
- lib/sdk/x402.ts: receipt + subscription header verification gate
- Multi-chain: Stellar, BNB, Base shown in marketplace service cards
- Service catalog API: /api/protocol/x402/services with pricing + subscription tiers
- Marketplace UI: chain tags, subscription badge, live Test x402 Gate modal
- Payment Explorer: added Base chain filter option
- Tests: 577/577 passing, 0 TypeScript errors, 0 lint errors
@Unclebaffa
Unclebaffa force-pushed the feat/x402-payment-rails branch 3 times, most recently from cb3afa8 to 39c4f60 Compare July 26, 2026 15:30
@Unclebaffa
Unclebaffa force-pushed the feat/x402-payment-rails branch from 39c4f60 to 365f55c Compare July 26, 2026 15:36
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
33.3% Duplication on New Code (required ≤ 3%)
D Reliability Rating on New Code (required ≥ A)
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EPIC] x402 Payment Rails — production-grade HTTP payment gate

1 participant