-
Notifications
You must be signed in to change notification settings - Fork 11
Add payment required middleware for x402 rails #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add task documentation for implementing plug-n-play x402 payment protocol middleware for the AIDD server framework. Enables API monetization via HTTP 402 Payment Required with stablecoin payments. Tasks include: - createWithPayment factory middleware - Payment verification via facilitator - 402 response format (x402 spec compliant) - Test utilities for mocking payments - Configuration and documentation updates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds comprehensive task documentation for implementing x402 payment protocol middleware in the AIDD server framework. The documentation outlines a complete implementation plan for enabling API monetization through HTTP 402 Payment Required status codes with stablecoin payments.
Key changes:
- Task epic document defining requirements for payment middleware implementation
- Specifications for payment verification, 402 response format, and test utilities
- Configuration, documentation, and export requirements for the middleware
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…leware Update createWithPayment factory to accept named parameters with default parameter syntax falling back to environment variables: - recipient -> X402_RECIPIENT - facilitatorUrl -> X402_FACILITATOR - paymentNetwork -> X402_PAYMENT_NETWORK
Update to official x402 protocol header names: - PAYMENT-REQUIRED (402 response) - PAYMENT-SIGNATURE (client payment proof) - PAYMENT-RESPONSE (settlement response)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| **Requirements**: | ||
| - Given src/server/middleware/index.js, should export `createWithPayment` | ||
| - Given src/server/middleware/index.js, should export `createMockFacilitator` for testing | ||
| - Given package.json, should add `@x402/core` as peer dependency |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement specifies adding @x402/core as a peer dependency without specifying a version constraint. Best practice for peer dependencies is to specify a version range to ensure compatibility. The requirement should specify the minimum version or version range (e.g., "should add @x402/core@^1.0.0 as peer dependency" or document that the version should match the current x402 specification version).
| - Given package.json, should add `@x402/core` as peer dependency | |
| - Given package.json, should add `@x402/core@^1.0.0` as peer dependency |
| ``` | ||
|
|
||
| **Requirements**: | ||
| - Given a request without valid payment proof, should respond with HTTP 402 and `PAYMENT-REQUIRED` header containing base64-encoded payment requirements |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement for line 28 specifies "containing base64-encoded payment requirements" but doesn't specify what fields should be in the payment requirements object, creating ambiguity. Line 63 in the "402 Response Format" section provides the detailed specification (recipient, amount, currency, network, facilitator, description, resource). For consistency and clarity, line 28 should either reference the detailed format defined in the "402 Response Format" section or list the required fields inline.
| - Given a request without valid payment proof, should respond with HTTP 402 and `PAYMENT-REQUIRED` header containing base64-encoded payment requirements | |
| - Given a request without valid payment proof, should respond with HTTP 402 and `PAYMENT-REQUIRED` header containing a base64-encoded payment requirements object with `recipient`, `amount`, `currency`, `network`, `facilitator`, `description`, and `resource` fields (see **402 Response Format**) |
| - Given payment requirements, should include `description` field for human-readable payment context | ||
| - Given payment requirements, should include `resource` field identifying the requested endpoint | ||
| - Given multiple supported networks, should list all in order of preference (lowest fees first) | ||
| - Given successful payment, should include `PAYMENT-RESPONSE` header with base64-encoded settlement response |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 67 mentions a PAYMENT-RESPONSE header that should be included on successful payment, but there's no corresponding requirement in the "createWithPayment Factory" section that specifies when this header should be set or what it should contain. This creates an incomplete specification - either add a requirement to the Factory section for setting this header on successful payment, or clarify in line 67 that this header is part of the response after successful verification.
| - Given a payment proof, should validate signature and payment details against facilitator API | ||
| - Given facilitator verification success, should cache proof to avoid re-verification on retries | ||
| - Given facilitator verification failure, should return 402 with clear error message (insufficient funds, expired, invalid signature) | ||
| - Given facilitator unavailability, should return 503 Service Unavailable with retry-after header |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The header name should be Retry-After (with proper capitalization) following standard HTTP header naming conventions. HTTP header names are case-insensitive but conventionally use title case (e.g., Content-Type, Cache-Control, Retry-After).
| - Given facilitator unavailability, should return 503 Service Unavailable with retry-after header | |
| - Given facilitator unavailability, should return 503 Service Unavailable with `Retry-After` header |
| - Given test environment, should support `createMockFacilitator` that returns configurable verification results | ||
| - Given mock facilitator, should simulate success, insufficient funds, expired payment, and network errors | ||
| - Given test setup, should provide `createPaymentProof` helper to generate valid test proofs | ||
| - Given createServer test utility, should extend to support payment header injection |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requirement mentions extending the "createServer test utility" but it's unclear what this utility is or where it exists in the codebase. The requirement should specify the file location (e.g., "Given src/server/test-utils.js") or clarify if this is a new utility that needs to be created as part of this epic. Without this clarification, it's ambiguous whether this refers to an existing utility or needs to be built from scratch.
| - Given createServer test utility, should extend to support payment header injection | |
| - Given `src/server/test-utils/createServer.ts` test utility, should extend it to support payment header injection |
| - Given `recipient` parameter not provided, should fall back to `process.env.X402_RECIPIENT` via default parameter syntax | ||
| - Given `facilitatorUrl` parameter not provided, should fall back to `process.env.X402_FACILITATOR` via default parameter syntax | ||
| - Given `paymentNetwork` parameter not provided, should fall back to `process.env.X402_PAYMENT_NETWORK` via default parameter syntax | ||
| - Given `paymentNetwork` not configured anywhere, should default to Base network for lowest fees |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states that paymentNetwork should default to "Base network for lowest fees," but this hardcodes a specific blockchain network assumption that may not remain accurate over time. Network fees change, and Base may not always have the lowest fees. Consider either: (1) removing the justification "for lowest fees" and just stating "should default to Base network", or (2) making this configurable/updatable, or (3) documenting that this default should be reviewed periodically.
| - Given `paymentNetwork` not configured anywhere, should default to Base network for lowest fees | |
| - Given `paymentNetwork` not configured anywhere, should default to Base network |
| - Given payment requirements, should include `description` field for human-readable payment context | ||
| - Given payment requirements, should include `resource` field identifying the requested endpoint | ||
| - Given multiple supported networks, should list all in order of preference (lowest fees first) | ||
| - Given successful payment, should include `PAYMENT-RESPONSE` header with base64-encoded settlement response |
Copilot
AI
Jan 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states "should include PAYMENT-RESPONSE header with base64-encoded settlement response" but uses inconsistent header naming. All other custom headers in this document use the X- prefix (e.g., X-PAYMENT-REQUIRED on line 62). This should be X-PAYMENT-RESPONSE for consistency, or all headers should follow the same convention (either all with X- prefix or all without).
Move PAYMENT-RESPONSE header requirement from 402 Response Format section (where it didn't belong) to createWithPayment Factory section with specific fields: transactionHash, network, settledAt.
Add requirements for Playwright end-to-end tests covering: - Full payment flow (402 → payment → resource access) - PAYMENT-REQUIRED header format verification - Client-side payment signature submission
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - Given `paymentNetwork` not configured anywhere, should default to Base network for lowest fees | ||
| - Given `amount` parameter, should require explicit value (price in smallest unit) with no env fallback | ||
| - Given `currency` parameter not provided, should default to 'USDC' | ||
| - Given response.locals.payment, should include `payer`, `amount`, `currency`, `network`, `transactionHash`, and `timestamp` |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an inconsistency in how response.locals.payment is referenced. Line 31 uses response.locals.payment while line 40 uses response.locals.payment in the requirement description. Both should use consistent naming, and it should be clarified whether this refers to the Express response object or a custom abstraction in the AIDD framework.
| - Given response.locals.payment, should include `payer`, `amount`, `currency`, `network`, `transactionHash`, and `timestamp` | |
| - Given `response.locals.payment` on the framework response object, it should include `payer`, `amount`, `currency`, `network`, `transactionHash`, and `timestamp` |
| **Requirements**: | ||
| - Given src/server/middleware/index.js, should export `createWithPayment` | ||
| - Given src/server/middleware/index.js, should export `createMockFacilitator` for testing | ||
| - Given package.json, should add `@x402/core` as peer dependency |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement specifies adding @x402/core as a peer dependency. Peer dependencies require users to manually install the package, which may not be the intended behavior for a core payment feature. Consider whether this should be a regular dependency instead, or document why users should manage this dependency themselves. Additionally, specify the required version range for the peer dependency.
| - Given package.json, should add `@x402/core` as peer dependency | |
| - Given package.json, should add `@x402/core` as a dependency with version range `^1.0.0` |
|
|
||
| **Requirements**: | ||
| - Given a payment proof, should validate signature and payment details against facilitator API | ||
| - Given facilitator verification success, should cache proof to avoid re-verification on retries |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states that the middleware should cache proofs to avoid re-verification on retries, but doesn't specify cache expiration policy, cache size limits, or cache invalidation strategy. Without these specifications, the cache could grow unbounded or retain stale proofs. Consider adding requirements for cache TTL, maximum cache size, and cache key structure.
| - Given facilitator verification success, should cache proof to avoid re-verification on retries | |
| - Given facilitator verification success, should cache proof to avoid re-verification on retries, subject to TTL, maximum size, and eviction policies | |
| - Given cached proof usage, should only accept proofs that have not exceeded a configurable TTL (default: 10 minutes) to avoid reuse of stale or revoked payments | |
| - Given cache growth beyond a configurable maximum size, should evict entries using a least-recently-used (LRU) or equivalent strategy to prevent unbounded memory usage | |
| - Given payment proof caching, should define cache keys using a combination of proof identifier and contextual fields (e.g., payer, recipient, amount, currency, network, and any nonce/requestId) to prevent incorrect proof reuse across distinct payments |
| - Given `facilitatorUrl` parameter not provided, should fall back to `process.env.X402_FACILITATOR` via default parameter syntax | ||
| - Given `paymentNetwork` parameter not provided, should fall back to `process.env.X402_PAYMENT_NETWORK` via default parameter syntax | ||
| - Given `paymentNetwork` not configured anywhere, should default to Base network for lowest fees | ||
| - Given `amount` parameter, should require explicit value (price in smallest unit) with no env fallback |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The amount parameter is described as "price in smallest unit" but doesn't specify the data type. For financial amounts, especially in blockchain contexts, JavaScript's Number type can lose precision for large values. The requirement should specify whether to use Number, BigInt, or string representation for the amount parameter to prevent precision loss.
| - Given `amount` parameter, should require explicit value (price in smallest unit) with no env fallback | |
| - Given `amount` parameter, should require an explicit string value representing the integer price in smallest unit (e.g., wei) with no env fallback |
| - Given facilitator verification failure, should return 402 with clear error message (insufficient funds, expired, invalid signature) | ||
| - Given facilitator unavailability, should return 503 Service Unavailable with retry-after header | ||
| - Given payment verification, should complete within 5 seconds under normal network conditions | ||
| - Given sensitive payment data, should sanitize wallet addresses and transaction hashes in error logs (show first/last 4 chars only) |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement to sanitize wallet addresses and transaction hashes by showing only the first/last 4 characters may not provide sufficient information for debugging payment issues. Consider specifying that the full data should be available in secure audit logs while only sanitized versions appear in general application logs.
| - Given sensitive payment data, should sanitize wallet addresses and transaction hashes in error logs (show first/last 4 chars only) | |
| - Given sensitive payment data, should record full wallet addresses and transaction hashes only in secure, access-controlled audit logs, and should sanitize these fields (show first/last 4 chars only) in general application and error logs |
| - Given `facilitatorUrl` not provided as parameter and `X402_FACILITATOR` not set, should throw descriptive ConfigurationError at middleware creation time | ||
| - Given production environment, should validate recipient address format before accepting requests |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states that ConfigurationError should be thrown at middleware creation time when required configuration is missing. However, there's no requirement to validate that the facilitator URL is a valid URL format or that it's reachable. Consider adding validation requirements for the facilitatorUrl parameter to catch configuration issues early.
| - Given `facilitatorUrl` not provided as parameter and `X402_FACILITATOR` not set, should throw descriptive ConfigurationError at middleware creation time | |
| - Given production environment, should validate recipient address format before accepting requests | |
| - Given `facilitatorUrl` not provided as parameter and `X402_FACILITATOR` not set, should throw descriptive ConfigurationError at middleware creation time | |
| - Given `facilitatorUrl` provided (via parameter or `X402_FACILITATOR`), should validate that it is a well-formed HTTP(S) URL at middleware creation time and throw a descriptive ConfigurationError if invalid | |
| - Given production environment, should validate recipient address format before accepting requests | |
| - Given production environment, should perform a lightweight facilitator URL reachability or health check at startup (or first use) and fail fast with a clear initialization error if the facilitator cannot be reached |
| - Given environment variables, should support `X402_RECIPIENT`, `X402_FACILITATOR`, `X402_PAYMENT_NETWORK` configuration | ||
| - Given `recipient` not provided as parameter and `X402_RECIPIENT` not set, should throw descriptive ConfigurationError at middleware creation time | ||
| - Given `facilitatorUrl` not provided as parameter and `X402_FACILITATOR` not set, should throw descriptive ConfigurationError at middleware creation time | ||
| - Given production environment, should validate recipient address format before accepting requests |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement to validate recipient address format in production environment only is problematic. Address format validation should occur in all environments to catch configuration errors early during development and testing. Consider validating in all environments but perhaps with different strictness levels.
| - Given production environment, should validate recipient address format before accepting requests | |
| - Should validate recipient address format in all environments before accepting requests (with stricter validation in production if needed) |
|
|
||
| **Requirements**: | ||
| - Given src/server/middleware/index.js, should export `createWithPayment` | ||
| - Given src/server/middleware/index.js, should export `createMockFacilitator` for testing |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement specifies exporting createMockFacilitator from the main middleware index file. Test utilities should typically be exported from a separate test-specific entry point or a dedicated test utilities module, not from the main production middleware exports. This keeps test code separate from production code and reduces bundle size for production users.
| - Given src/server/middleware/index.js, should export `createMockFacilitator` for testing | |
| - Given a dedicated test utilities entry point (e.g. src/server/middleware/test-utils.js), should export `createMockFacilitator` for testing |
| - Given a request with valid payment proof in `PAYMENT-SIGNATURE` header, should verify payment via configured facilitator | ||
| - Given successful payment verification, should set `PAYMENT-RESPONSE` header with base64-encoded settlement response containing `transactionHash`, `network`, and `settledAt` | ||
| - Given successful payment verification, should attach payment receipt to `response.locals.payment` and allow request to proceed | ||
| - Given failed payment verification, should respond with HTTP 402 and appropriate error details |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement specifies that failed payment verification should return a 402 response with "appropriate error details", but doesn't specify the format or structure of these error details. For consistency with the rest of the x402 protocol, there should be a standardized error response format specification, similar to how the success case has the PAYMENT-REQUIRED header format defined.
| - Given a request without valid payment proof, should respond with HTTP 402 and `PAYMENT-REQUIRED` header containing base64-encoded payment requirements | ||
| - Given a request with valid payment proof in `PAYMENT-SIGNATURE` header, should verify payment via configured facilitator | ||
| - Given successful payment verification, should set `PAYMENT-RESPONSE` header with base64-encoded settlement response containing `transactionHash`, `network`, and `settledAt` | ||
| - Given successful payment verification, should attach payment receipt to `response.locals.payment` and allow request to proceed |
Copilot
AI
Jan 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement states that payment receipt should be attached to response.locals.payment, but in Express.js middleware, request/response context data is typically stored in req.locals or more commonly res.locals. However, the context typically flows through the request object. Consider clarifying whether this should be req.locals.payment or confirm the use of response.locals.payment is intentional for this framework.
Add task documentation for implementing plug-n-play x402 payment protocol middleware for the AIDD server framework. Enables API monetization via HTTP 402 Payment Required with stablecoin payments.
Tasks include: