# x402-xpr-hello-world — Pay with XPR on the Web

This project demonstrates a paywalled web app using HTTP 402, XPR (Xprnetwork/Proton), and optional a facilitator Worker for JWT-based proof of payment. It is designed to be reproducible: you can recreate it from this description and the linked docs.

## Summary

- **Main app:** Cloudflare Worker (Hono) that serves free routes and one paywalled route. Unpaid requests to the protected route get 402 with payment details; after paying on XPR Network, the client sends the transaction id (or a facilitator-issued JWT) to unlock.
- **Optional facilitator:** A second Worker that verifies payments on Proton (Hyperion) and issues short-lived JWTs so the main Worker can trust payment without calling the chain on every request. Supports single-tenant (one shared secret) or multi-tenant (per-partner registration and secrets).
- **Payment flow:** 402 → pay on Proton → proof via tx_id or Bearer token → 200 and content. Each tx_id is one-time (stored in KV).

## Stack

- Runtime: Cloudflare Workers
- Framework: Hono
- Chain: XPR Network (Proton) mainnet — eosio.token (XPR), xtokens (XUSDC)
- Verification: Hyperion history API for transaction lookup
- Optional: Facilitator Worker (separate deploy) for JWT issuance; HS256 JWT with shared or per-audience secret

## Repo structure

- src/index.ts — Main Worker: routes /, /ai, /x402, /xpr, /readme, /docs/facilitator, /docs/samples, /llm.txt, /llms.txt, /llm.md, /robots.txt, /sitemap.xml, /protected-route; 402 payload; JWT verification; KV for used tx_ids
- facilitator/src/index.ts — Facilitator Worker: POST /verify (tx_id, resource, optional audience); POST /register and PUT /keys/:audience (admin); KV for issued tx_ids and partner secrets
- wrangler.toml — Main Worker config (vars, KV, routes)
- facilitator/wrangler.toml — Facilitator config
- docs/FACILITATOR_FLOW_AND_TESTING.md — Full flow, setup, and testing (PowerShell + curl)
- README.md — User-facing docs, config, deploy, JWT secret, multi-tenant

## Environment (main Worker)

- XPR_RECEIVER_ACCOUNT — Account that receives payments
- XPR_MIN_AMOUNT — e.g. "0.1000 XPR" or "0.100000 XUSDC"
- USED_TX_IDS — KV namespace for one-time tx_id use (replay protection)
- FACILITATOR_URL — (optional) Base URL of facilitator; 402 includes facilitatorUrl
- FACILITATOR_JWT_SECRET — (optional) Secret to verify facilitator JWTs
- FACILITATOR_AUDIENCE — (optional) Audience id when using shared facilitator; 402 includes facilitatorAudience

## Environment (facilitator)

- XPR_RECEIVER_ACCOUNT, XPR_MIN_AMOUNT — Same semantics as main Worker
- USED_TX_IDS — KV for "already issued" tx_ids
- FACILITATOR_ADMIN_SECRET — Required for POST /register and PUT /keys/:audience
- FACILITATOR_JWT_SECRET — (single-tenant only) Shared secret to sign JWTs; omit for multi-tenant and use partner secrets from KV

## How to recreate

1. Clone or create a Worker project with Hono. Implement GET / that returns a simple page and JSON for API clients.
2. Add a protected route that, when no payment proof is present, returns 402 with a JSON body containing payTo (receiver), maxAmountRequired, resource, network (e.g. proton-mainnet), and howToPay. Use x402-style fields (see README and x402.org).
3. Implement payment verification: on receipt of tx_id (prefer headers like X-Payment-Authorization / Authorization: Payment; query is legacy/dev convenience), call Proton Hyperion to fetch the transaction; check that it includes a transfer of the required token to the receiver with amount >= min; store tx_id in KV and return 200. Reject if tx_id already in KV.
4. Optional: Add a facilitator Worker that accepts POST /verify with tx_id and resource; verifies the tx on Hyperion; stores "issued" in KV; returns a short-lived JWT (e.g. 5 min) signed with HMAC-SHA256. Main Worker then accepts Authorization: Bearer <token>, verifies JWT, extracts tx_id, marks used in KV, returns 200.
5. For multi-tenant facilitator: store per-audience secrets in KV; register via POST /register (admin); sign JWTs with the audience’s secret when client sends audience in POST /verify.
6. Build steps: inline README and facilitator doc into TS (e.g. scripts/inline-readme.cjs, inline-facilitator-doc.cjs) so GET /readme and GET /docs/facilitator serve HTML; optionally inline llm.txt for GET /llm.txt.
7. Deploy main Worker and optionally facilitator with wrangler; set secrets (FACILITATOR_JWT_SECRET, FACILITATOR_ADMIN_SECRET) via wrangler secret put.

## Key URLs (this deployment)

- Site: https://xpr.testsitout.com
- Readme (HTML): https://xpr.testsitout.com/readme
- Readme (raw markdown): https://xpr.testsitout.com/readme?format=md
- Facilitator flow doc (HTML): https://xpr.testsitout.com/docs/facilitator
- Facilitator flow (raw markdown): https://xpr.testsitout.com/docs/facilitator?format=md
- Client & agent samples (HTML): https://xpr.testsitout.com/docs/samples
- Samples (raw markdown): https://xpr.testsitout.com/docs/samples?format=md
- This file: https://xpr.testsitout.com/llm.txt
- Markdown version: https://xpr.testsitout.com/llm.md

## Easy test flows

Get 402 (payment required) as JSON (required for API/agent; omit header to get HTML in browser):
  curl -sS -H "Accept: application/json" https://xpr.testsitout.com/protected-route

Unlock with transaction id (after paying):
  curl -sS -H "Accept: application/json" -H "X-Payment-Authorization: YOUR_TX_ID" "https://xpr.testsitout.com/protected-route"

Full flow with facilitator (after paying, set TX_ID then): POST to https://facilitator-xpr.testsitout.com/verify with body {"tx_id":"TX_ID","resource":"/protected-route","audience":"mpc-test"}. If response 402, wait 2–5s and retry (indexer lag). Then GET https://xpr.testsitout.com/protected-route with Authorization: Bearer <token> and Accept: application/json. Complete copy-paste curl sequence at /docs/samples.

## Flows in practice

- Browser: open protected URL → pay → paste tx_id in form or use facilitator token (POST /verify then Bearer).
- Agent/CLI: GET 402 with Accept: application/json → parse payTo, maxAmountRequired, facilitatorUrl, facilitatorAudience, resource → pay → if facilitatorUrl: POST /verify (retry on 402 after 2s, 3s, 5s), then GET with Bearer; else GET with ?tx_id=.

## Samples (client and agentic)

- Full facilitator curl flow and client/agent steps: https://xpr.testsitout.com/docs/samples (or ?format=md). Includes retry-on-402 (2s, 3s, 5s) for indexer lag.
- Agentic: GET 402 → parse → pay → POST facilitator (retry 2s, 3s, 5s on 402) → GET with Bearer; or GET with ?tx_id= when no facilitator.

## References

- x402: https://www.x402.org — payment standard; whitepaper and docs linked there
- XPR Network: https://docs.xprnetwork.org
- Source code: https://github.com/French-River-Hosting/xpr-x402
- Proton Hyperion (history): used for transaction lookup by id
