Skip to content

Conversation

@joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Jan 12, 2026


PR-Codex overview

This PR focuses on updating the x402 payment protocol to support version 2, enhancing payment header management, and ensuring backward compatibility with version 1. It modifies schemas, types, and payment handling functions to accommodate the new version.

Detailed summary

  • Updated x402Version to support both version 1 and 2.
  • Changed decodedPayment.x402Version assignment to preserve client-provided version.
  • Introduced new payment headers for version 1 (X-PAYMENT) and version 2 (PAYMENT-SIGNATURE).
  • Updated functions to resolve and return appropriate payment headers based on version.
  • Adjusted payment verification and settlement logic to handle versioning.
  • Updated tests to reflect changes in payment header handling and versioning.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Support for X.402 payment protocol v2, including dynamic request/response header selection.
    • Expanded payment configuration: token pricing, minPrice, facilitator, routing and extra metadata.
  • Improvements

    • Smarter version fallback and validation across 402 flows; preserves client-provided protocol version when present.
    • Response headers and payment handling adapt to negotiated protocol version.
  • Tests

    • Updated tests to validate dynamic header behavior for multiple protocol versions.
  • Chores

    • Added changelog entry for v2 support.

✏️ Tip: You can customize this high-level summary in your review settings.

@joaquim-verges joaquim-verges requested review from a team as code owners January 12, 2026 20:46
@vercel
Copy link

vercel bot commented Jan 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs-v2 Ready Ready Preview, Comment Jan 12, 2026 10:03pm
nebula Ready Ready Preview, Comment Jan 12, 2026 10:03pm
thirdweb_playground Ready Ready Preview, Comment Jan 12, 2026 10:03pm
thirdweb-www Ready Ready Preview, Comment Jan 12, 2026 10:03pm
wallet-ui Ready Ready Preview, Comment Jan 12, 2026 10:03pm

@changeset-bot
Copy link

changeset-bot bot commented Jan 12, 2026

🦋 Changeset detected

Latest commit: 845889c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch
wagmi-inapp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added packages SDK Involves changes to the thirdweb SDK labels Jan 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

Walkthrough

Adds X402 v2 support: introduces versioned header constants and lookup helpers, expands types and schemas for v2, and makes payment header selection and x402Version assignment dynamic across decode, fetch, verify, and settle flows.

Changes

Cohort / File(s) Summary
Header Management
packages/thirdweb/src/x402/headers.ts
New module exporting v1/v2 header constants and helpers getPaymentRequestHeader() / getPaymentResponseHeader() that resolve header names by version.
Type System & Versioning
packages/thirdweb/src/x402/types.ts
Added supportedX402Versions, X402Version type, default x402Version = 2; expanded PaymentArgs with price/minPrice (Money
Schema Validation
packages/thirdweb/src/x402/schemas.ts
Updated FacilitatorSupportedResponseSchema to accept x402Version as `1
Common Payment Logic
packages/thirdweb/src/x402/common.ts
decodePaymentRequest now preserves client-provided x402Version via nullish coalescing assignment (decodedPayment.x402Version ??= x402Version).
HTTP Payment Flow & Tests
packages/thirdweb/src/x402/fetchWithPayment.ts, packages/thirdweb/src/x402/fetchWithPayment.test.ts
Fetch flow now resolves x402Version (parsed or default) and uses getPaymentRequestHeader() / getPaymentResponseHeader() to compute dynamic header names; tests updated to assert on computed header names.
Payment Settlement
packages/thirdweb/src/x402/settle-payment.ts
Uses getPaymentResponseHeader() to populate response headers based on decoded x402Version; response bodies set x402Version via decodedPayment.x402Version ?? x402Version.
Payment Verification
packages/thirdweb/src/x402/verify-payment.ts
Reads payment data from payment-signature header with fallback to x-payment; response bodies set x402Version using decodedPayment.x402Version ?? x402Version.
Changelog
.changeset/eight-pants-drum.md
Adds changeset noting "Support x402 v2."

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant Facilitator

  Client->>Server: Request (no payment)
  Server->>Client: 402 + payment header (name from getPaymentRequestHeader(x402Version))
  Client->>Facilitator: Pay (uses PAYMENT-SIGNATURE / X-PAYMENT header variant per version)
  Facilitator->>Server: Payment confirmation (includes x402Version and payment data)
  Server->>Server: decodePaymentRequest (preserve client x402Version if present)
  Server->>Client: Retry original request with payment response header (name from getPaymentResponseHeader)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description includes the template but is mostly filled with auto-generated PR-Codex summary rather than author-provided notes. Template sections like issue tag, reviewer notes, and testing instructions are either missing or commented out. Fill in the template sections with specific details: provide the issue tag (TEAM-0000 format if applicable), add any important reviewer notes, and document how to test the changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'support x402 v2 headers' accurately and concisely describes the main change in this PR—adding support for version 2 of the x402 protocol with corresponding header management.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/thirdweb/src/x402/headers.ts (1)

8-10: Consider stricter version validation to avoid silent fallback.

The resolveVersion function silently converts any non-1 value (including 0, 3, or other invalid numbers) to version 2. While the type signature accepts number | X402Version, invalid inputs are masked rather than caught.

Consider throwing for unsupported versions or validating against supportedX402Versions:

♻️ Suggested stricter validation
+import { type X402Version, x402Version, supportedX402Versions } from "./types.js";
+
 function resolveVersion(version?: number | X402Version): X402Version {
-  return version === 1 ? 1 : 2;
+  if (version === undefined) {
+    return x402Version;
+  }
+  if (!supportedX402Versions.includes(version as X402Version)) {
+    throw new Error(`Unsupported x402 version: ${version}`);
+  }
+  return version as X402Version;
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5766c90 and 5b83ba2.

📒 Files selected for processing (8)
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/verify-payment.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Place tests alongside code: foo.tsfoo.test.ts in the same directory
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
Use predefined test accounts from test/src/test-wallets.ts in tests
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_CHAIN for isolated tests

**/*.test.{ts,tsx}: Co-locate tests with source files using the pattern foo.ts ↔ foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
For network interactions in tests, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce
Keep tests deterministic and side-effect free; Vitest is pre-configured

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
🧬 Code graph analysis (6)
packages/thirdweb/src/x402/fetchWithPayment.test.ts (1)
packages/thirdweb/src/x402/headers.ts (1)
  • getPaymentRequestHeader (12-17)
packages/thirdweb/src/x402/common.ts (1)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/fetchWithPayment.ts (2)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/headers.ts (2)
  • getPaymentRequestHeader (12-17)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/headers.ts (1)
packages/thirdweb/src/x402/types.ts (2)
  • X402Version (15-15)
  • x402Version (16-16)
packages/thirdweb/src/x402/settle-payment.ts (2)
packages/thirdweb/src/x402/headers.ts (1)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/verify-payment.ts (1)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (17)
packages/thirdweb/src/x402/headers.ts (1)

12-26: LGTM!

The header resolution functions are well-structured with clear return types. The default fallback to x402Version when no version is provided ensures backward compatibility.

packages/thirdweb/src/x402/types.ts (2)

14-16: LGTM!

The versioning implementation using as const with a derived type provides type safety while keeping the supported versions maintainable. The default version set to 2 aligns with the PR objective of supporting x402 v2 headers.


28-31: LGTM!

The JSDoc update accurately documents both header names (PAYMENT-SIGNATURE for v2, X-PAYMENT for v1), which improves discoverability for developers.

packages/thirdweb/src/x402/common.ts (1)

54-55: LGTM!

The nullish coalescing assignment (??=) correctly preserves the client-provided version while defaulting to the current protocol version when missing. This is essential for proper multi-version support where clients may send either v1 or v2 payments.

packages/thirdweb/src/x402/fetchWithPayment.test.ts (3)

4-4: LGTM!

Good addition of the getPaymentRequestHeader import to enable dynamic header validation in tests.


113-117: LGTM!

The assertion now properly validates that the payment header name is dynamically determined based on the x402Version from the mock response. This ensures the implementation correctly handles version-specific headers.


307-311: Good backward compatibility test coverage.

This test validates that v1 payloads (with x402Version: 1 in the base64-encoded header) correctly use the v1 header name (X-PAYMENT). This ensures backward compatibility with existing v1 servers.

packages/thirdweb/src/x402/verify-payment.ts (2)

32-34: LGTM!

The JSDoc example correctly demonstrates the header fallback pattern (v2 payment-signature first, then v1 x-payment), providing clear guidance for implementers.


126-126: LGTM!

Using decodedPayment.x402Version ?? x402Version in error responses ensures the 402 response uses the same protocol version as the client's request. This maintains consistency and allows clients to properly interpret error responses.

Also applies to: 142-142

packages/thirdweb/src/x402/schemas.ts (1)

85-85: LGTM!

The schema correctly expands to accept both X402 protocol versions using a union of literals, maintaining strict type validation while enabling v2 support.

packages/thirdweb/src/x402/settle-payment.ts (4)

4-4: LGTM!

Import correctly added for dynamic header name resolution.


41-43: LGTM!

Example code correctly demonstrates reading payment data from both v2 (payment-signature) and v1 (x-payment) headers with appropriate fallback for backward compatibility.

Also applies to: 110-111


158-173: LGTM!

The dynamic header name resolution correctly uses the client's provided x402Version from the decoded payment, ensuring the response header matches the version the client used in their request.


184-184: Verify the default version fallback behavior in error responses.

The nullish coalescing decodedPayment.x402Version ?? x402Version falls back to the default version (v2) when x402Version is missing from the decoded payment. This means error responses may advertise v2 even if the client originally used v1 but didn't include the version field.

If this is intentional (to encourage clients to upgrade), the current implementation is fine. Otherwise, consider preserving the version from the original 402 response requirements if available.

Also applies to: 200-200

packages/thirdweb/src/x402/fetchWithPayment.ts (3)

8-11: LGTM!

Clean imports with appropriate aliasing. Renaming to defaultX402Version clearly distinguishes the module-level default from the locally-scoped x402Version variable parsed from responses.

Also applies to: 20-20


104-104: LGTM!

Fallback to defaultX402Version ensures backward compatibility with servers that don't include x402Version in their 402 responses. This correctly defaults to v2 for such cases.

Also applies to: 122-122


188-202: LGTM!

Dynamic header name resolution correctly uses the version from the server's 402 response, ensuring the payment request and expected response headers match the protocol version the server indicated.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 12, 2026

size-limit report 📦

Path Size
@thirdweb-dev/nexus (esm) 105.66 KB (0%)
@thirdweb-dev/nexus (cjs) 319.47 KB (0%)

@codecov
Copy link

codecov bot commented Jan 12, 2026

Codecov Report

❌ Patch coverage is 87.87879% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.02%. Comparing base (5766c90) to head (845889c).

Files with missing lines Patch % Lines
packages/thirdweb/src/x402/fetchWithPayment.ts 75.00% 2 Missing ⚠️
packages/thirdweb/src/x402/headers.ts 90.90% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8623      +/-   ##
==========================================
+ Coverage   53.00%   53.02%   +0.02%     
==========================================
  Files         929      930       +1     
  Lines       62109    62136      +27     
  Branches     4077     4088      +11     
==========================================
+ Hits        32921    32949      +28     
+ Misses      29089    29088       -1     
  Partials       99       99              
Flag Coverage Δ
packages 53.02% <87.87%> (+0.02%) ⬆️
Files with missing lines Coverage Δ
packages/thirdweb/src/x402/schemas.ts 63.41% <100.00%> (ø)
packages/thirdweb/src/x402/types.ts 100.00% <100.00%> (ø)
packages/thirdweb/src/x402/fetchWithPayment.ts 63.94% <75.00%> (-0.40%) ⬇️
packages/thirdweb/src/x402/headers.ts 90.90% <90.90%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.changeset/eight-pants-drum.md (1)

1-5: Consider a more descriptive changeset message.

The current description "Support x402 v2" is minimal. Consider expanding it to briefly explain what x402 v2 support includes (e.g., "Add support for x402 v2 protocol with backward compatibility for v1, including versioned headers and dynamic header selection").

Additionally, verify whether this should be a minor version bump rather than patch, as adding support for a new protocol version typically constitutes a feature addition rather than a bug fix.

📝 More descriptive changeset example
 ---
 "thirdweb": patch
 ---
 
-Support x402 v2
+Add x402 v2 protocol support with backward compatibility for v1, including versioned headers and dynamic version detection
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fee1245 and 845889c.

📒 Files selected for processing (1)
  • .changeset/eight-pants-drum.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

packages SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants