You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary
- **x402 amount-field aliasing is now opt-in** — the 402 builders no
longer emit `maxAmountRequired` by default (strict x402 v2 settle
matches the whole `accepts` object byte-for-byte, so an extra field
breaks settle). `aliasAmountFields` stays exported for clients hardcoded
to read `maxAmountRequired`.
- `extractPaymentSigner` tries the x402 header before MPP (matches the
precheck path).
- `directive` amounts route through `usdToAtomic` (exact BigInt,
ROUND_HALF_UP).
- `buildDefaultCheckoutRails` derives tempo-testnet
`network`/`chainId`/`token`.
- Named `ProcessX402SettleSuccess` / `ProcessX402SettleFailure` for
surface parity with python.
- Bump `@agent-score/sdk` → `^2.4.2`, `@x402/*` → `^2.13`; SECURITY
`1.x` → `2.x`.
- Branch coverage ~92.9% (threshold 85); stripped internal/parity
references from public source + docs.
## Test plan
- [x] `bun run lint` + `bun run typecheck` clean
- [x] `bun run test` — 1506 passed, 92.94% branch (gate 85)
- [x] e2e: linked sdk→commerce→4 consumers, all consumer suites green
- [x] verified against the **published** `@agent-score/sdk@2.4.2` (not
local link)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,12 +8,12 @@ Every helper is extracted from a real consumer, not speculated.
8
8
9
9
| Subpath | What it is |
10
10
|---|---|
11
-
| `@agent-score/commerce` (top-level) | `Checkout` orchestrator (the 2.0 high-level surface) for fixed-price one-shot endpoints: one config object + hooks (preValidate, computePricing, mintRecipients, composeMppx, onSettled, gate), auto-derived x402+mppx servers, per-framework adapters `handleHono`/`handleExpress`/`handleFastify`/`handleNextjs`/`handleWeb`, signed UCP routes via `mountUcpRoutes{Hono,Express,Fastify}`, optional `discoveryProbe` config for x402-crawler auto-routing. Plus `computeFirstCheckout` — variable-cost pay-per-result helper (compute-first + exact-x402). Scope is exact-mode rails only (x402-exact Base, tempo/charge, solana/charge, Stripe SPT); does NOT use x402-upto (Permit2) or Settlement-Overrides — variable cost is captured by running the work pre-settle and emitting a 402 at the exact computed price. `createQuoteCache` — content-hash quote cache used by the compute-first helper (in-memory by default; pass `redisUrl` for distributed deployments). `createDefaultOnDenied` — canonical `onDenied(reason)` factory matching `Checkout`'s gate hook (handles `wallet_signer_mismatch`, `wallet_not_trusted` unfixable fallback, `payment_required`, `token_expired`/`invalid_credential`/`api_error`); merchants pass `merchantName` + `supportEmail` and override `walletNotTrustedMessage` / `paymentRequiredMessage` / `supportContext` for vendor-specific copy. `hasPaymentHeader` — discriminator that splits discovery legs (no payment credential → 402) from settle legs (`payment-signature` / `x-payment` / `Authorization: Payment <jwt>`); `hasX402Header` / `hasMppxHeader` — granular dispatch helpers (x402 vs MPP credential present) for routes that branch on rail. `defaultReadOnlyOnDenied(reason)` — canonical `onDenied` for read-only resource gates (`GET /orders/:id`): collapses every denial to 401 `unauthorized` + `Cache-Control: no-store` while still spreading `denialReasonToBody` so `agent_instructions` / `verify_url` ride through. `extractOwnerScope(headers) → { walletAddress?, operatorTokenHash? }` — pull canonical owner identity from `X-Wallet-Address` / `X-Operator-Token` with safe token hashing; pair with a wallet-or-token-scoped resource query so plaintext tokens never leave the request. Plus factories: `pricingResult` (cents → typed PricingResult with optional `decimals` for sub-cent precision), `validationResponse{Hono,Express,Fastify,Nextjs,Web}` (4xx envelope per framework), `Receipt`/`ReceiptNextSteps`/`ProductInfo`/`ShippingAddress` (canonical 200-receipt shape — universal across goods + API merchants) |
11
+
| `@agent-score/commerce` (top-level) | `Checkout` orchestrator (the 2.0 high-level surface) for fixed-price one-shot endpoints: one config object + hooks (preValidate, computePricing, mintRecipients, composeMppx, onSettled, gate), auto-derived x402+mppx servers, per-framework adapters `handleHono`/`handleExpress`/`handleFastify`/`handleNextjs`/`handleWeb`, signed UCP routes via `mountUcpRoutes{Hono,Express,Fastify}`, optional `discoveryProbe` config for x402-crawler auto-routing. Plus `computeFirstCheckout` — variable-cost pay-per-result helper (compute-first + exact-x402). Scope is exact-mode rails only (x402-exact Base, tempo/charge, solana/charge, Stripe SPT); does NOT use x402-upto (Permit2) or Settlement-Overrides — variable cost is captured by running the work pre-settle and emitting a 402 at the exact computed price. `createQuoteCache` — content-hash quote cache used by the compute-first helper (in-memory by default; pass `redisUrl` for distributed deployments). `createDefaultOnDenied` — canonical `onDenied(reason)` factory matching `Checkout`'s gate hook (handles `wallet_signer_mismatch`, `wallet_not_trusted` unfixable fallback, `payment_required`, `token_expired`/`invalid_credential`/`api_error`); merchants pass `merchantName` + `supportEmail` and override `walletNotTrustedMessage` / `paymentRequiredMessage` / `supportContext` for vendor-specific copy. `hasPaymentHeader` — discriminator that splits discovery legs (no payment credential → 402) from settle legs (`payment-signature` / `x-payment` / `Authorization: Payment <jwt>`); `hasX402Header` / `hasMppxHeader` — granular dispatch helpers (x402 vs MPP credential present) for routes that branch on rail. `defaultReadOnlyOnDenied(reason)` — canonical `onDenied` for read-only resource gates (`GET /orders/:id`): collapses every denial to 401 `unauthorized` + `Cache-Control: no-store` while still spreading `denialReasonToBody` so `agent_instructions` / `verify_url` ride through. `extractOwnerScope(headers) → { walletAddress?, operatorTokenHash? }` — pull canonical owner identity from `X-Wallet-Address` / `X-Operator-Token` with safe token hashing; pair with a wallet-or-token-scoped resource query so plaintext tokens never leave the request. Plus factories: `pricingResult` (cents → typed PricingResult with optional `decimals` for sub-cent precision), `validationResponse{Hono,Express,Fastify,Nextjs,Web}` (4xx envelope per framework) |
12
12
|`@agent-score/commerce/identity/{hono,express,fastify,nextjs,web}`| Trust gate middleware (KYC, age, sanctions, jurisdiction). Each adapter exports a `conditionalAgentscoreGate(options)` variant (Next.js / Web Fetch use the wrapper form `withConditionalAgentScoreGate(opts, handler)` / `createConditionalAgentScoreGate(opts) => guard(req)`) that fires only on settle legs — discovery legs (no payment credential) flow through and the handler emits a 402 with all rails. Adapters export ONLY framework-specific surface (gate fns, accessors, `captureWallet`); shared helpers like `hasPaymentHeader` / `denialReasonToBody` import from their canonical home (`@agent-score/commerce/payment` and `@agent-score/commerce` respectively). |
13
13
|`@agent-score/commerce/identity/policy`| Framework-agnostic per-product / per-tier compliance policy helpers: `PolicyBlock`, `buildGateOptionsFromPolicy`, `runGateWithEnforcement`, `shippingCountryAllowed`, `shippingStateAllowed`, `validateShippingAgainstPolicy` (one-call country+state validator that raises `CheckoutValidationError` with the canonical envelope on miss) |
14
14
| `@agent-score/commerce/payment` | Networks/USDC/rails registries, paymentauth.org directive builders, `createX402Server` (peer-dep `@x402/core` + `@coinbase/x402` for the Coinbase facilitator), `buildX402AcceptsFor402` (one-call helper for the 402-emit path: builds the requirements via the registered scheme so `extra.name` matches the on-chain USDC contract per network), `buildDefaultCheckoutRails({tempo?, x402Base?, solanaMpp?, stripe?})` (canonical 4-rail `rails` dict factory: merchants pass per-rail overrides instead of redeclaring the recipient sentinel + network/chainId/token boilerplate. When a caller overrides `network` without pinning `token` / `chainId`, the helper derives them from the network: Base Sepolia → Sepolia USDC + chainId 84532, Solana devnet → devnet USDC mint. Explicit overrides always win. Solana's `network` field accepts both CAIP-2 (`solana:5eykt4UsFv8…` / `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) AND the raw `@solana/mpp` form (`mainnet-beta` / `devnet` / `localnet`)), `buildMppxComposeRails({amountUsd, tempoRecipient?, solanaRecipient?, ...})` (per-call intent factory replacing the hand-rolled `[['tempo/charge',{...}],['solana/charge',{...}],['stripe/charge',{...}]]` array; auto-handles USD→atomic conversion for Solana; auto-drops the `stripe/charge` rail with a one-time `console.warn` when `amountUsd < 0.50` since Stripe's fixed ~$0.30 fee makes sub-50-cent charges unprofitable — many Stripe accounts also reject PI creation below the floor with `amount_too_small`; sub-50-cent APIs pass `includeStripe: false` explicitly to silence the warning), `createMppxServer` (peer-dep `mppx`; the solana rail's `ataCreationRequired` defaults to `true` so Solana settles work zero-config on `@solana/mpp >= 0.6.0` — the SDK propagates it as a self-referential split so the validator's `allowedAtaOwners` accepts the configured recipient; opt out only when every recipient's ATA pre-exists out-of-band), `composeMppxRequest` (typed wrapper around `mppx.compose(...intents)(request)`; replaces the `(mppx as any).compose(...)` cast in custom `composeMppx` hooks), `mppxChallengeHeaders` (one-call extractor for the 402 path's `Object.fromEntries(challenge.headers)`), `processX402Settle` (verify+settle in one call), `isEvmNetwork`/`isSolanaNetwork` (CAIP-2 discriminators that hide the `startsWith('eip155:')` / `startsWith('solana:')` prefix matching), dispatch-by-network, signer extraction, WWW-Authenticate header, Settlement-Overrides header |
15
15
|`@agent-score/commerce/discovery`| Discovery probe middleware (`isDiscoveryProbeRequest`, `buildDiscoveryProbeResponse`), Bazaar wrapper, `/.well-known/mpp.json` builder, `llms.txt` builder, `skill.md` builder (Claude-Skill-compatible agent-discovery manifest), `buildRedemptionSkillMd` (delivery-neutral; printed/emailed/API-trial codes all covered via `deliveryIntro`/`bodyShape`/`bodyRules`/`extraRecoveryRows` overrides), `buildMerchantIndexJson` + `standardEndpointDescriptions({kind})` (canonical `/` discovery body for goods or API merchants), `buildSuccessNextSteps` (universal Passport-active success block), `buildAgentscoreOnboardingSteps`, OpenAPI snippets, `noindexNonDiscoveryPaths` Hono middleware. Plus the UCP/JWKS publish surface: `buildSignedUcpResponse`, `buildSignedJwksResponse`, `wellKnownPreflightResponse`, `defaultA2aServices`, `bootstrapUcpSigningKey`, framework-neutral `SignedDiscoveryResponse` + per-framework wrappers `signedResponse{Hono,Express,Fastify,Nextjs,Web}`|
16
-
|`@agent-score/commerce/challenge`| 402-body builders: accepted_methods, identity metadata (auto-attached by `Checkout` when wallet header present), how_to_pay, agent_instructions, build402Body, pricing, agent_memory, `buildValidationError` (4xx body builder), `Receipt`/`ReceiptNextSteps`/`ProductInfo`/`ShippingAddress` (canonical 200-receipt shape) |
16
+
|`@agent-score/commerce/challenge`| 402-body builders: accepted_methods, identity metadata (auto-attached by `Checkout` when wallet header present), how_to_pay, agent_instructions, build402Body, pricing, agent_memory, `buildValidationError` (4xx body builder), `Receipt` (canonical 200-receipt shape) |
17
17
| `@agent-score/commerce/stripe-multichain` | Multichain PaymentIntent helper (`createMultichainPaymentIntent` returns `{ paymentIntentId, depositAddresses }`; read `depositAddresses[network]` directly), `createPayToAddressFromStripePI({request, amountCents, stripe, piCache, networks?, staticRecipients?, metadata?, orderId?, preferredNetwork?})` — one-call per-order payTo resolver matching `Checkout.mintRecipients`: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `piCache.hasAddress` check OR a `staticRecipients` match — the static address is always-accepted because the merchant owns it); on the discovery leg, mints a fresh PI for the rails NOT covered by `staticRecipients`, caches the merged map, registers static addresses with `piCache.cacheAddress`. `mintMultichainRecipients({...same opts}) => { recipients, paymentIntentId?, reusedFromCredential }` — structured variant that returns the full per-rail map; prefer this when the merchant's `mintRecipients` hook needs all rail addresses (typical multi-rail merchant), and to avoid the "returned-string-is-ambiguous" trap on the settle leg when `staticRecipients` is configured (the bound recipient might be the solana static, not the tempo per-PI). Use `staticRecipients: { solana: '<wallet>' }` for low-margin endpoints where rotating per-PI Solana addresses can't absorb MPP spec §13.6's ~$0.50 ATA rent per call — the SDK skips Stripe minting on that network and reuses the static recipient forever; pair with a one-time external USDC pre-funding of the recipient's ATA and every settle pays only the per-tx fee. Testnet simulator (`simulateCryptoDeposit`, `simulateDepositIfTestMode`), `simulateDepositForOutcome({outcome, depositAddress, getPaymentIntentId, stripeSecretKey, stripeVersion?})` (dispatches the simulator based on a Checkout / computeFirstCheckout settle outcome; replaces the per-merchant rail-switch + thin `simulateDepositIfTestnet(addr, network)` wrapper), `networkForOutcome` (outcome → simulator network arg, handles both Checkout-shaped `railKey` and computeFirstCheckout-shaped `mppMethod`, accepts bare scheme names AND `<scheme>/charge` forms), `createPiCache`, `createMppxStripe` |
18
18
|`@agent-score/commerce/api`| Re-exports `AgentScore` + `AgentScoreError` from `@agent-score/sdk`|
19
19
|`@agent-score/commerce/middleware/{hono,express,fastify,nextjs,web}`| Framework-specific rate-limit middleware. Hono / Express / Fastify expose middleware factories (`rateLimitHono`, `rateLimitExpress`, `rateLimitFastify`); Next.js exposes `withRateLimit(opts, handler)`; Web Fetch exposes `createRateLimit(opts) => guard(req)`. Shared core: `windowSeconds` (default 60), `maxRequests` (default 60), `keyResolver` (default first hop of `x-forwarded-for`), `redisUrl` (optional; lazy-imports `ioredis` when set, falls back to in-memory `Map` otherwise). Mount globally with `app.use('*', rateLimitHono())` before any payment route. `ioredis` is an optional peer dep — merchants without Redis don't install it and get in-memory state per process. |
0 commit comments