Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- Marked submodules ai-mcp-pmm-controller, explorer-monorepo, and smom-dbis-138 as dirty to reflect recent changes. - Updated documentation to clarify operator script usage, including dotenv loading and task execution instructions. - Enhanced the README and various index files to provide clearer navigation and task completion guidance. Made-with: Cursor
117 lines
4.8 KiB
Markdown
117 lines
4.8 KiB
Markdown
# x402 Endpoint Contract — Alltra (651940) + USDC
|
||
|
||
**Purpose:** Spec for Alltra-native x402 paid endpoints: 402 challenge, retry with PAYMENT-SIGNATURE, and local verification on chain 651940 with USDC. Settlement is on Alltra; no dependency on Base or external facilitator.
|
||
|
||
**References:** [coinbase/x402](https://github.com/coinbase/x402), [HTTP 402 — x402](https://docs.x402.org/core-concepts/http-402), [ADDRESS_MATRIX_AND_STATUS.md](../11-references/ADDRESS_MATRIX_AND_STATUS.md) §2.3 (Alltra USDC).
|
||
|
||
---
|
||
|
||
## 1. Overview
|
||
|
||
- **Chain:** `eip155:651940` (ALL Mainnet / Alltra)
|
||
- **Payment token:** USDC at `0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881`
|
||
- **Recipient:** Server treasury (e.g. `SERVER_WALLET_ADDRESS`)
|
||
- **Verification:** Local (recommended): server verifies signature, intent, and on-chain settlement; optional facilitator-like `/verify` later.
|
||
|
||
---
|
||
|
||
## 2. Step 1 — Client calls paid endpoint (unpaid)
|
||
|
||
**Request:** `GET /api/resource` (or any paid route)
|
||
|
||
**Response when unpaid:** `402 Payment Required`
|
||
|
||
**Headers:**
|
||
|
||
- `PAYMENT-REQUIRED: <base64 PaymentRequired>`
|
||
|
||
**PaymentRequired (JSON, then base64-encoded):**
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `network` | string | `eip155:651940` |
|
||
| `asset` | string | USDC contract address (0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881) |
|
||
| `amount` | string | Price in base units (e.g. "10000" for 0.01 USDC if 6 decimals) |
|
||
| `recipient` | string | Treasury address |
|
||
| `nonce` | string | Unique per request (e.g. UUID) |
|
||
| `expiresAt` | string | ISO 8601 (e.g. now + 5 minutes) |
|
||
| `resourceId` | string | Identifies the resource (e.g. URL or hash) so payment is bound to the request |
|
||
|
||
**Example (decoded):**
|
||
|
||
```json
|
||
{
|
||
"network": "eip155:651940",
|
||
"asset": "0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881",
|
||
"amount": "10000",
|
||
"recipient": "0x...",
|
||
"nonce": "550e8400-e29b-41d4-a716-446655440000",
|
||
"expiresAt": "2026-03-04T12:05:00.000Z",
|
||
"resourceId": "GET /api/premium"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Step 2 — Client pays and retries
|
||
|
||
Client performs USDC transfer (or authorization) on chain 651940 to `recipient` for `amount`, then retries the same request with:
|
||
|
||
**Headers:**
|
||
|
||
- `PAYMENT-SIGNATURE: <base64 PaymentPayload>`
|
||
|
||
**PaymentPayload (JSON, then base64-encoded):**
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `payer` | string | Payer wallet address |
|
||
| `signature` | string | Signature over the payment intent (e.g. EIP-191 or EIP-712 of PaymentRequired or its hash) |
|
||
| `paymentRequired` | object | Copy of PaymentRequired so server can verify match |
|
||
| `txHash` | string (optional) | Transaction hash on 651940 proving transfer (for on-chain verification) |
|
||
|
||
Server uses `txHash` to verify settlement via `eth_getTransactionReceipt` on 651940 when doing local verification.
|
||
|
||
---
|
||
|
||
## 4. Step 3 — Server verification (Alltra-native local)
|
||
|
||
1. **Decode** PaymentPayload from base64.
|
||
2. **Verify signature** — signature belongs to `payer` (recover signer from signature over payment intent).
|
||
3. **Verify intent** — PaymentPayload.paymentRequired matches the server-issued PaymentRequired (same amount, asset, chain, recipient, resourceId); `expiresAt` is in the future.
|
||
4. **Verify settlement:**
|
||
- If `txHash` present: call 651940 RPC `eth_getTransactionReceipt(txHash)`; confirm success and that transfer is to `recipient` for `amount` (USDC) from `payer`.
|
||
- If authorization-based: verify authorization and that a transfer occurred (per your scheme).
|
||
5. **Replay:** Mark `(payer, resourceId, nonce)` as consumed (store in DB or cache with TTL); reject if already consumed.
|
||
6. **Respond:** Return 200 with resource body; optionally set `PAYMENT-RESPONSE` header (per x402) with settlement response.
|
||
|
||
---
|
||
|
||
## 5. Replay protection
|
||
|
||
- Key: `(payer, resourceId, nonce)`.
|
||
- Store consumed keys with expiry ≥ `expiresAt` so the same nonce cannot be reused.
|
||
- Production: use Redis or DB; development: in-memory Map with TTL is acceptable.
|
||
|
||
---
|
||
|
||
## 6. PAYMENT-RESPONSE (optional)
|
||
|
||
Per [docs.x402.org](https://docs.x402.org/core-concepts/http-402), server may return `PAYMENT-RESPONSE` header with settlement confirmation (e.g. txHash, status). Optional for minimal implementation.
|
||
|
||
---
|
||
|
||
## 7. Separation from sponsorship
|
||
|
||
- **Sponsorship (paymaster):** Covers gas for app actions (e.g. CoreApp writes) on 651940.
|
||
- **x402:** User-paid USDC for API/service access; validated by this flow.
|
||
|
||
The two are independent: x402 payment tx is user-funded; sponsored txs are paymaster-funded.
|
||
|
||
---
|
||
|
||
## 8. Implementation
|
||
|
||
- **x402-api:** When `X402_USE_ALLTRA=true`, the server can use this local verification path: return 402 + PAYMENT-REQUIRED when unpaid; on PAYMENT-SIGNATURE, run steps 1–6 and serve the resource on success.
|
||
- **USDC decimals:** 6 for Alltra USDC; `amount` in PaymentRequired is in base units (e.g. 10000 = 0.01 USDC).
|