facora1
v0.3.5
Published
Gasless x402-style payments (BNB • USDx) — request→permit→settle→unlock.
Maintainers
Readme
Facora SDK
Gasless x402-style payments on BNB with USDx — request → permit → settle → unlock.
Works with your /api/secret paywall, EIP-2612 permits, and facilitator endpoints (Alpha / Beta / Gamma).
Features
⚡ 1-call client: payAndRequest({ resourceUrl, facilitator })
🪪 EIP-2612 permit (spender = facilitator)
⛽ Gasless for callers — facilitator pays BNB gas
🔗 BNB + USDx (addresses configurable)
🧩 Pluggable facilitators: Alpha / Beta / Gamma (pick one or fallback)
Install
```bash
looks like terminal
npm i facora-sdk
or
yarn add facora-sdk
or
pnpm add facora-sdk ```
Peer dep: ethers@^6
Runtime dep (bundled): cross-fetch
Quick Start (Client)
Minimal (same-origin)
```typescript import { payAndRequest } from "facora1";
const { settlement, data } = await payAndRequest({ resourceUrl: "/api/secret", // your paywalled endpoint facilitator: "Beta", // "Alpha" | "Beta" | "Gamma" (default "Alpha") // baseUrl: "https://facora.org/api" // optional for cross-origin/SSR });
console.log("settlement:", settlement); // tx hash, block, payer, gas, etc. console.log("data:", data); // unlocked JSON from /api/secret ```
Reusable client
```typescript import { FacoraClient } from "facora1";
const facora = new FacoraClient(); // or new FacoraClient("https://api.example.com") const { settlement, data } = await facora.payAndRequest("/api/secret", "Gamma"); ```
How It Works
1. Request → 402
Client GET /api/secret with no auth → server returns 402 Payment Required JSON:
```json { "price": "1 USDx", "asset": "0x...USDX", "facilitators": [ { "name":"Alpha","fee":"0.5%","endpoint":"/api/facilitators/alpha","address":"0x...","live":true }, { "name":"Beta","fee":"1.0%","endpoint":"/api/facilitators/beta","address":"0x...","live":true }, { "name":"Gamma","fee":"2.0%","endpoint":"/api/facilitators/gamma","address":"0x...","live":true } ] } ```
2. Sign Permit (EIP-2612)
Client POST /api/permits/sign → returns { owner, spender, value, deadline, v, r, s }
(spender = chosen facilitator address)
3. Settle On-Chain (facilitator)
Client POST the signature to the facilitator endpoint (e.g. /api/facilitators/beta).
Facilitator calls permit() then transferFrom(owner → merchant) (minus fee), pays BNB gas, and returns a receipt { settled: true, txHash, ... }.
4. Unlock
Client re-GETs /api/secret with header x-paid-proof: <alpha|beta|gamma> → server returns 200 OK with the protected JSON.
Client API
```typescript export type FacilitatorName = "Alpha" | "Beta" | "Gamma";
export interface PayAndRequestOptions { resourceUrl: string; // e.g. "/api/secret" facilitator?: FacilitatorName; // defaults to "Alpha" baseUrl?: string; // optional for cross-origin/SSR }
export interface Settlement { settled?: boolean; paid?: boolean; facilitator: string; facilitatorAddress?: string; amount: string; merchant?: string; payer?: string; txHash: string; blockNumber?: number; chain?: string; network?: string; gasUsed?: string; gasCost?: string; timestamp?: number; }
export interface PayAndRequestResult { settlement: Settlement | null; // null if the first GET already returned 200 data: unknown; // unlocked JSON } ```
Functions
payAndRequest(options: PayAndRequestOptions): Promise<PayAndRequestResult>new FacoraClient(baseUrl?: string)→.payAndRequest(resourceUrl: string, facilitator?: FacilitatorName)
Minimal Server Requirements
You already have (adjust names to your project):
GET /api/secret
- If no
x-paid-proof→ return 402 JSON with{ price, asset, facilitators[] }. - If header
x-paid-proof: alpha|beta|gamma→ return 200 with unlocked JSON, e.g.:
```json { "alpha": "whale 0x9b... moved 320,000 USDx to CEX 4 min ago" } ```
POST /api/permits/sign
- Input:
{ price: "1 USDx", facilitator: "Beta" } - Output:
{ owner, spender, value, deadline, v, r, s, chainId } - Uses the USDx ERC-20's EIP-2612 domain (reads name, nonces, decimals, chainId).
POST /api/facilitators/{alpha|beta|gamma}
- Input:
{ owner, value, deadline, v, r, s } - Flow:
permit()→transferFrom(owner → merchant)(minus fee) - Output:
{ settled: true, txHash, blockNumber, facilitator, amount, ... }
Environment (Server)
```bash
looks like terminal
BNB_TESTNET_RPC=... NEXT_PUBLIC_USDX_TOKEN_ADDRESS=0x... # USDx token address MERCHANT_WALLET_ADDRESS=0x... # receives USDx
FACILITATOR_ALPHA_PRIVATE_KEY=0x... FACILITATOR_BETA_PRIVATE_KEY=0x... FACILITATOR_GAMMA_PRIVATE_KEY=0x...
DEPLOYER_PRIVATE_KEY=0x... # used by /api/permits/sign demo ```
Fund facilitator wallets with BNB for gas.
If you keep "legacy" mode, fund facilitators with USDx too.
Troubleshooting
Unexpected status 4xx/5xx
/api/secret didn't return 402 or 200. Log the route and confirm JSON shape.
Sign failed from /api/permits/sign
Check USDx address, chainId, EIP-2612 domain, DEPLOYER_PRIVATE_KEY, and deadline.
Facilitator failed
Common causes: no BNB gas, insufficient USDx (legacy), wrong spender/domain/chainId, expired deadline.
Payment not confirmed by facilitator
Facilitator didn't return { settled: true } or { paid: true }. Check server logs.
Cross-origin / SSR
Pass baseUrl in the client:
```typescript await payAndRequest({ baseUrl: "https://api.yourapp.xyz", resourceUrl: "/api/secret", facilitator: "Alpha" }); ```
Roadmap
- Multi-facilitator fallback & retries
- Typed error classes and richer receipts
- Example apps (
/examples)
License
MIT © Facora
