# Taiwan Laws + U.S. Company Financials — x402 API + MCP — v0.4.0

Pay-per-call data for AI agents that work with Taiwan: **full-text search and retrieval of Taiwan laws (法律)** from the National Laws & Regulations Database (全國法規資料庫, 法務部). Price: **0.01 USD in USDC per successful call** via x402 v2 (`exact`, EIP-3009) on Base mainnet (`eip155:8453`), facilitator **PayAI**. Invalid input and errors are never charged.

## Paid means delivered (v0.3.0): signed receipts + idempotent replay
- **Receipt**: every paid 200 (HTTP and MCP) carries `receipt` in the body and an `X-Receipt` header (base64url JSON). Fields: `v, kid, alg, network, tx_hash, payer, pay_to, asset, amount, request, request_hash, response_hash, idempotency_key, issued_at, sig`. `request` = normalized request (`GET /path?k=v&…`, query pairs sorted, RFC 3986; MCP: `MCP tools/call <tool>?<sorted args>`), `request_hash = sha256(request)`, `response_hash = sha256(JSON.stringify(body without "receipt"))`, `idempotency_key = sha256(tx_hash + "\n" + request)`. `sig` = Ed25519 over the canonical JSON (sorted keys) of the other fields. Public key: `GET /.well-known/x402-receipt-key` (JWK, raw base64, PEM, kid).
- **Idempotent replay**: re-send the same request with the same payment proof (`PAYMENT-SIGNATURE`, or MCP `_meta["x402/payment"]`) within 24 h → the stored result and receipt come back with `X-Idempotent-Replay: true`; nothing is settled again. Same proof for a *different* request → 409 `payment_already_used`.
- **Crash between settlement and response**: the settlement is recorded (proof → tx hash) right after the facilitator confirms, before the response is sent. A replay rebuilds the result (`X-Idempotent-Replay: recovered`) without settling again.
- **Facilitator timeout although the tx went through** (x402 issue #1062): if the settle error carries a tx hash, it is stored as *pending* (the resource-server analogue of the SDK's `PendingSettlementStore`) and checked read-only on Base RPC (USDC `Transfer` payer→payTo ≥ amount + EIP-3009 `AuthorizationUsed(payer, nonce)`). Confirmed → delivered (in the same request if it confirms within ~20 s, otherwise on replay); not yet → 503 `settlement_pending`, retry with the same proof.
- **Limitation (Render free tier)**: the store is memory + `CACHE_DIR` (default `/tmp`). It survives a process restart on the same instance but **not** a redeploy, instance replacement or spin-down/up on a new host, and it is per-instance. Durable replay would need an external store (e.g. Redis/Postgres). No funds are moved and no refunds are issued by this service.
- Optional env: `RECEIPT_KEY_FILE` (default `/etc/secrets/x402-receipt-ed25519.pem`; if missing an ephemeral key is used and `/.well-known/x402-receipt-key` shows `ephemeral: true`), `BASE_RPC_URL` (default `https://mainnet.base.org`), `PENDING_WAIT_MS` (default 20000).

## Async jobs (v0.4.0)
- A paid request whose work takes longer than `ASYNC_AFTER_MS` (default 8000 ms; e.g. a cold U.S. filings cache, `/v1/fin/peers`) answers **202** with `{job_id, status:"running", poll_url, settle_by, charged:false, retry_after}`, `Location: /v1/jobs/{job_id}` and `Retry-After: 5`.
- **Charge on delivery**: the payment is verified when the job is admitted, but settled only after the result exists (and before the authorization's `validBefore`/`maxTimeoutSeconds` window, `settle_by`). Failed jobs (e.g. not found, upstream down) and jobs that outlive the authorization are **never charged** (`status:"failed", charged:false`); just retry the original request with a new payment.
- Poll `GET /v1/jobs/{job_id}` (free) with the **same `PAYMENT-SIGNATURE`** header, or with the job's receipt (`X-Receipt` header or `?receipt=`). 202 while running / `settlement_pending`; 200 = the paid result + signed receipt (`receipt.job_id`); repeated polls return the same result, never a new charge. 401 without a matching proof/receipt; 404 unknown/expired job.
- Re-sending the same paid request with the same proof returns the same job (one payment admits at most one job); after completion it returns the cached result. Uses the v0.3.0 ledger (proof → tx, sha256(tx + request) → result + receipt) and #1062 pending-tx recovery.
- Jobs are kept 24 h in memory + `CACHE_DIR` (default `/tmp`); a job interrupted by a process restart resumes on the next poll. Not durable across Render redeploys / instance changes (same limitation as the ledger).
- MCP: a slow paid tool call returns `isError` with `{error:"job_pending", job_id}` and is **not settled**; the work keeps running, and calling the tool again with the same arguments and payment returns the result (settled then).
- Compatibility: there is no official x402 async-job spec yet (see issues below). This follows HTTP 202 + Location + Retry-After semantics, keeps payment state separate from job state, records the client's `payment-identifier` extension id (if sent) on the job, and reuses the same-proof-replay idea of x402 docs PR #3605. Related: x402-foundation/x402 #3208 (settlement status "not yet"), #1645 (settlement/delivery ordering, expiration), #1062 (facilitator timeout), #3605 (payment-identifier recovery), #3694 (job-escrow scheme proposal), #3381 (async relay proposal).

## Data source (政府資料開放授權條款－第1版 / Open Government Data License v1.0, https://data.gov.tw/license — commercial use allowed with attribution)
| dataset | provider | source | license evidence | freshness here |
|---|---|---|---|---|
| 中文法規_法律資料檔 (全國法規資料庫) | 法務部 | https://law.moj.gov.tw/api/Ch/Law/JSON | https://data.gov.tw/dataset/18289 (授權方式: 政府資料開放授權條款-第1版), https://law.moj.gov.tw/Service/Copyright.aspx | bulk file loaded at boot, re-checked every 12 h (source updates monthly) |

Every response has a `source` block: dataset, provider, license, license URL, source URL and the OGDL attribution statement (顯名聲明). `GET /sources` lists it.

## U.S. company financials (XBRL) — added in v0.3.0
> **Data source: U.S. SEC public filings. Not investment advice. Not affiliated with or endorsed by the SEC.**

Figures come from the XBRL data in public 10-K / 10-Q filings (`/A` amendments included) via `https://data.sec.gov/api/xbrl/companyfacts|companyconcept|frames` and the ticker map `https://www.sec.gov/files/company_tickers.json`. License basis: sec.gov "Website Dissemination" — information on sec.gov is public information and may be copied or further distributed (https://www.sec.gov/privacy). Only XBRL numbers and company names are served: no ownership forms (3/4/5) and no person names. Upstream fair access: a global limit of ~9 requests/s (guideline: max 10/s), User-Agent with app name + contact e-mail from `SEC_USER_AGENT` (required; without it these endpoints are not exposed), caches: ticker map daily, companyfacts 24 h (memory + `CACHE_DIR`), concept/frames 24 h.

| path | MCP tool | parameters | price |
|---|---|---|---|
| `/v1/fin/summary` | `get_company_financials` | `ticker` **or** `cik`; `period` = `annual` (default) / `quarterly`; `limit` 1–8 (default 4) | 0.01 USDC |
| `/v1/fin/concept` | `get_financial_concept` | `ticker` **or** `cik`; `concept` (e.g. `Revenues`); `taxonomy` `us-gaap` (default) / `dei`; `unit` (default `USD`); `form` (optional, e.g. `10-K`); `limit` 1–100 (default 20) | 0.01 USDC |
| `/v1/fin/peers` | `get_financial_peers` | `concept`; `period` (`CY2025`, `CY2025Q2`, `CY2025Q2I` for instants); `top` 1–100 (default 20) | 0.01 USDC |
| `/v1/fin/lookup?q=` | `lookup_company` | ticker / name fragment / CIK | free |
| `/v1/fin/concepts?ticker=` | — | `ticker` or `cik` | free |

Summary rules: each period lists `fiscal_year, fiscal_period, end_date, form, filed, accession` and the fields `revenue, net_income, eps_basic, eps_diluted, total_assets, total_liabilities, stockholders_equity, operating_cash_flow, shares_outstanding`, each as `{value, concept_used, unit, filed, accession}` (`value: null` if not reported). Concept fallbacks are tried in order per period (revenue: `Revenues` → `RevenueFromContractWithCustomerExcludingAssessedTax` → `SalesRevenueNet` → `RevenueFromContractWithCustomerIncludingAssessedTax`; net income: `NetIncomeLoss` → `ProfitLoss` → …). Duplicate values for the same period: the latest `filed` wins. Annual = ~1-year durations from 10-K; quarterly = ~90-day durations (80–100 days). Every response has `source_urls`/`source_url`, `disclaimer` and `data_as_of` (when the upstream document was fetched).

Errors: unknown ticker/CIK/concept/unit/period → **404 before payment** (no 402, never charged); upstream filings API down → **503**, never settled.

## Paid endpoints — Taiwan laws (GET, query parameters)
| path | MCP tool | parameters |
|---|---|---|
| `/v1/laws/search` | `search_tw_laws` | `q` (required, space-separated terms), `limit` (1–50, default 10), `includeAbandoned` |
| `/v1/laws/get` | `get_tw_law` | `pcode` **or** `name` (exact), optional `article` (`184`, `15-1`, `第 184 條`) |

Unpaid request → HTTP 402 with base64 `PAYMENT-REQUIRED` header (x402 v2); the JSON body is the same decoded PaymentRequired object (HEAD: header only). Retry with `PAYMENT-SIGNATURE` signed by your own wallet. Invalid input → 400 before any payment request. 404 (law not found) and 503 (dataset still loading after a cold start) are not charged.

Examples:
```bash
curl -s "https://x402-tw-data.onrender.com/v1/laws/search?q=個人資料%20告知&limit=3"     # -> 402 until paid
curl -s "https://x402-tw-data.onrender.com/v1/laws/get?name=民法&article=184"
```

## MCP
`POST /mcp` (Streamable HTTP, stateless). Paid tools as above (x402 MCP transport: unpaid call → `isError` + `PaymentRequired` in `structuredContent`; retry with `params._meta["x402/payment"]`; receipt in `result._meta["x402/payment-response"]`). Free tools: `get_pricing`, `list_sources`, `lookup_company`.

## Free routes
`/health`, `/pricing`, `/sources`, `/llms.txt`, `/docs.md`, `/terms`, `/openapi.json`, `/.well-known/x402`, `/server.json`, `/.well-known/mcp-registry-auth`, `/v1/fin/lookup`, `/v1/fin/concepts`.

## Limitations
- Laws only (法律 level, ~1,350 acts); regulations/orders (命令) are not included.
- The official text published by the competent authority prevails. Not legal advice.
- U.S. financials: as-reported XBRL values (no adjustments, splits or currency conversion); companies that do not file XBRL 10-K/10-Q (e.g. most foreign private issuers) have no summary. Not investment advice.

Every successful settlement logs one line `[x402] settled payer=0x… amount=… tx=… resource=…` (to tell outside buyers from self-tests).

## Run locally
```bash
npm install
npm start            # Base mainnet + PayAI by default, 127.0.0.1:4041
npm test             # server must be running (BASE=http://127.0.0.1:4041)
```
