|
| 1 | +# x402 Paid Action → CLAS Receipt (Example Only) |
| 2 | + |
| 3 | +This example demonstrates a **mock** integration flow where a paid action request is linked to a simulated x402 payment acceptance event and emitted as a CLAS-style action receipt. |
| 4 | + |
| 5 | +> This is not production settlement or production signing. It is an educational example. |
| 6 | +
|
| 7 | +## What this example does |
| 8 | + |
| 9 | +- Accepts a mock paid action request. |
| 10 | +- Simulates an x402 `payment.accepted` event input. |
| 11 | +- Executes a mock agent action (`summarize.text`). |
| 12 | +- Emits a CLAS-style receipt containing: |
| 13 | + - `metadata.trace` for correlation. |
| 14 | + - `metadata.proof.payment` and `metadata.proof.execution`. |
| 15 | + - `proof.signature` placeholders for `payer`, `agent`, `runtime`, and `verifier`. |
| 16 | + |
| 17 | +## Setup |
| 18 | + |
| 19 | +```bash |
| 20 | +cp examples/x402-paid-action-receipt/.env.example .env |
| 21 | +``` |
| 22 | + |
| 23 | +No secrets are required for this mock example. Do not add private keys to `.env`. |
| 24 | + |
| 25 | +## Environment variables |
| 26 | + |
| 27 | +- `PORT` (default `4000`): local server port. |
| 28 | +- `WORKFLOW_ID` (optional): trace workflow correlation id. |
| 29 | +- `RUNTIME_SIGNING_KEY_ID` (optional): key identifier string used in placeholder runtime signature metadata. |
| 30 | + |
| 31 | +## Run locally |
| 32 | + |
| 33 | +```bash |
| 34 | +node examples/x402-paid-action-receipt/server.js |
| 35 | +``` |
| 36 | + |
| 37 | +Health check: |
| 38 | + |
| 39 | +```bash |
| 40 | +curl -s http://localhost:4000/health |
| 41 | +``` |
| 42 | + |
| 43 | +## Sample curl command |
| 44 | + |
| 45 | +```bash |
| 46 | +curl -s -X POST http://localhost:4000/paid-action \ |
| 47 | + -H 'content-type: application/json' \ |
| 48 | + -d '{ |
| 49 | + "paid_action_request": { |
| 50 | + "request_id": "req_9f2f5f25", |
| 51 | + "action": "summarize.text", |
| 52 | + "input": {"text": "CommandLayer receipts prove execution attestation separate from payment settlement."}, |
| 53 | + "payment": {"required": true, "plan": "pro", "max_amount": "0.05", "currency": "USD"} |
| 54 | + }, |
| 55 | + "payment_accepted": { |
| 56 | + "event": "payment.accepted", |
| 57 | + "request_id": "req_9f2f5f25", |
| 58 | + "payment_id": "pay_x402_7f31", |
| 59 | + "provider": "x402-compatible", |
| 60 | + "settled_amount": "0.05", |
| 61 | + "currency": "USD", |
| 62 | + "accepted_at": "2026-05-22T12:00:01Z" |
| 63 | + } |
| 64 | + }' |
| 65 | +``` |
| 66 | + |
| 67 | +## Expected output |
| 68 | + |
| 69 | +A `200` JSON response containing: |
| 70 | + |
| 71 | +- `duplicate: false` on first execution. |
| 72 | +- `receipt.receipt_id`, `request_id`, `payment_id`. |
| 73 | +- `metadata.trace` fields including `request_id`, `payment_id`, `receipt_id`, `workflow_id`. |
| 74 | +- `metadata.proof.commandlayer_signing_hook` placeholder to replace with real CommandLayer signing. |
| 75 | +- `proof.signature` role entries for `payer`, `agent`, `runtime`, `verifier`. |
| 76 | + |
| 77 | +If the same `request_id + payment_id` is sent again, response includes `duplicate: true` and returns the original receipt. |
| 78 | + |
| 79 | +## Trust boundary |
| 80 | + |
| 81 | +- **x402/payment provider proves settlement**: payment requirement, acceptance/rejection, settlement status. |
| 82 | +- **CommandLayer proves execution**: action request, runtime execution output, and signed receipt artifact. |
| 83 | +- **Do not conflate them**: payment acceptance does not prove execution correctness. |
| 84 | + |
| 85 | +## Failure modes |
| 86 | + |
| 87 | +The API documents and returns error states for: |
| 88 | + |
| 89 | +- missing payment |
| 90 | +- invalid payment |
| 91 | +- duplicate `request_id` / `payment_id` (idempotent replay returns canonical receipt) |
| 92 | +- action execution failed |
| 93 | +- receipt signing failed |
| 94 | +- verifier unavailable (documented operational dependency; this mock does not call an external verifier) |
| 95 | + |
| 96 | +## Idempotency model |
| 97 | + |
| 98 | +Use and persist three IDs: |
| 99 | + |
| 100 | +- `request_id`: semantic request identity. |
| 101 | +- `payment_id`: payment-settlement identity. |
| 102 | +- `receipt_id`: emitted CLAS receipt identity. |
| 103 | + |
| 104 | +Dedupe key in this example is `request_id + payment_id`. |
| 105 | + |
| 106 | +## Production-readiness path |
| 107 | + |
| 108 | +To move this example to production: |
| 109 | + |
| 110 | +1. Replace mock payment validation with real x402 provider verification. |
| 111 | +2. Persist idempotency state in durable storage (DB/cache), not in-memory maps. |
| 112 | +3. Implement canonical receipt signing using CommandLayer keys/HSM/KMS (replace placeholders). |
| 113 | +4. Add verifier invocation and signed verifier attestations for `verifier` role. |
| 114 | +5. Add schema validation for incoming request/event payloads. |
| 115 | +6. Add audit logging, retries, and alerting for `RECEIPT_SIGNING_FAILED` and verifier outages. |
| 116 | +7. Protect endpoints with authn/authz and rate limiting. |
| 117 | + |
| 118 | +## Validation commands |
| 119 | + |
| 120 | +```bash |
| 121 | +npm test |
| 122 | +node --test tests/x402-paid-action-receipt.test.js |
| 123 | +``` |
0 commit comments