Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b5fcb22
Add Onchain to x402 Ecosystem - Services/Endpoints (#539)
0xvsr Oct 29, 2025
d7042f3
docs(svm): document facilitator fee payer guards; tests(svm): add fee…
notorious-d-e-v Oct 29, 2025
271f17b
Improve Solana paywall wallet connection (#542)
GuiBibeau Oct 30, 2025
4dbb384
chore: version typescript packages (#551)
CarsonRoscoe Oct 30, 2025
0443cb1
chore: move packages, examples, and e2e clients/servers to /legacy su…
CarsonRoscoe Oct 23, 2025
db6ecb5
feat: initial draft of @x402/core
CarsonRoscoe Oct 23, 2025
4b65d02
feat: initial draft of @x402/evm
CarsonRoscoe Oct 23, 2025
50f74d6
feat: initial draft of @x402/fetch
CarsonRoscoe Oct 23, 2025
14b5f27
feat: initial draft of @x402/express
CarsonRoscoe Oct 23, 2025
598a34a
feat: initial draft of @x402/bazaar
CarsonRoscoe Oct 23, 2025
dad4070
test: passing fetch + express + evm v2 e2e test
CarsonRoscoe Oct 23, 2025
d1824aa
scaffold: svm, axios, hono, next, sign-in-with-x & paywall
CarsonRoscoe Oct 23, 2025
c69809f
chore: added TODO.md files for each package
CarsonRoscoe Oct 23, 2025
29fcf4a
feat: refactor extensions into @x402/extensions
CarsonRoscoe Oct 23, 2025
ce30367
chore: format/lint typescript packages
CarsonRoscoe Oct 23, 2025
a819ecd
chore: updated legacy publish npm workflows
CarsonRoscoe Oct 24, 2025
70fd691
feat: initial draft of the x402 go package
CarsonRoscoe Oct 25, 2025
6ed8426
feat: initial draft of the evm mechanism in go
CarsonRoscoe Oct 25, 2025
3942087
feat: initial draft of the gin middleware in go
CarsonRoscoe Oct 25, 2025
ab34981
test: passing http-client + gin + evm v2 e2e test
CarsonRoscoe Oct 26, 2025
5e96add
test: passing typescript v2, go v2, and legacy go/python/typescript e…
CarsonRoscoe Oct 27, 2025
23b1efe
feat: second draft of @x402/extensions & bazaar extension
CarsonRoscoe Oct 29, 2025
5234720
test: passing bazaar typescript e2e tests
CarsonRoscoe Oct 29, 2025
fa5e48d
feat(paywall): extract paywall into standalone @x402/paywall package
apmcdermott Oct 30, 2025
2b752d3
feat(http): integrate @x402/paywall with HTTP SDKs
apmcdermott Oct 30, 2025
f85d235
feat(paywall): add builder pattern for composable paywall configuration
apmcdermott Oct 30, 2025
cacb379
feat(paywall): add builder pattern with comprehensive unit tests
apmcdermott Oct 30, 2025
579fbe6
feat(paywall): add network-specific modules for tree shaking
apmcdermott Oct 30, 2025
6588093
chore(paywall): fix formatting issues
apmcdermott Oct 30, 2025
99bb0aa
chore(paywall): fix linting and formatting issues
apmcdermott Oct 30, 2025
9966255
refactor(paywall): deduplicate utils and fix eslint config
apmcdermott Oct 30, 2025
77f0ced
Update READMEs
apmcdermott Oct 30, 2025
f4267a6
Update manifest
apmcdermott Oct 30, 2025
c8ae6f8
Add setPaywallProvider
apmcdermott Oct 30, 2025
c330706
Update generated template files
apmcdermott Oct 30, 2025
77ead50
Make paywall provider registration chainable
apmcdermott Oct 31, 2025
6172acd
Pass x402Version param to PaywallNetworkHandler and pull v1 networks …
apmcdermott Oct 31, 2025
9989c4e
Make paywall provider optional
apmcdermott Oct 31, 2025
24d7abc
Split ensureValidAmount into version-specific fns with v2 as the default
apmcdermott Oct 31, 2025
eca66f8
Remove default amount if validation fails; throw instead
apmcdermott Oct 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_coinbase_x402.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
pnpm -r --filter=x402 --filter=@coinbase/x402 run build

- name: Publish coinbase-x402 package
working-directory: ./typescript/packages/coinbase-x402
working-directory: ./typescript/packages/legacy/coinbase-x402
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
pnpm -r --filter=x402 run build

- name: Publish x402 package
working-directory: ./typescript/packages/x402
working-directory: ./typescript/packages/legacy/x402
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402_axios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
pnpm -r --filter=x402 --filter=x402-axios run build

- name: Publish x402-axios package
working-directory: ./typescript/packages/x402-axios
working-directory: ./typescript/packages/legacy/x402-axios
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402_express.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
pnpm -r --filter=x402 --filter=x402-express run build

- name: Publish x402-express package
working-directory: ./typescript/packages/x402-express
working-directory: ./typescript/packages/legacy/x402-express
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402_fetch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
pnpm -r --filter=x402 --filter=x402-fetch run build
- name: Publish x402-fetch package
working-directory: ./typescript/packages/x402-fetch
working-directory: ./typescript/packages/legacy/x402-fetch
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402_hono.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
pnpm -r --filter=x402 --filter=x402-hono run build

- name: Publish x402-hono package
working-directory: ./typescript/packages/x402-hono
working-directory: ./typescript/packages/legacy/x402-hono
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_npm_x402_next.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
pnpm -r --filter=x402 --filter=x402-next run build

- name: Publish x402-next package
working-directory: ./typescript/packages/x402-next
working-directory: ./typescript/packages/legacy/x402-next
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
Expand Down
11 changes: 11 additions & 0 deletions e2e/.env-local
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# E2E Test Configuration
SERVER_EVM_ADDRESS=
SERVER_SVM_ADDRESS=
CLIENT_EVM_PRIVATE_KEY=
CLIENT_SVM_PRIVATE_KEY=

# Server Configuration
SERVER_PORT=
USE_CDP_FACILITATOR=
CDP_API_KEY_ID=
CDP_API_KEY_SECRET=
23 changes: 23 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ pnpm test -d -v # Test with verbose logging
pnpm test -d -ts # Test TypeScript implementations
pnpm test -d -py # Test Python implementations
pnpm test -d -go # Test Go implementations

# Legacy compatibility testing
pnpm test --legacy # Include legacy implementations
pnpm test --legacy -d -ts # Test legacy + new TypeScript implementations
```

## Filtering Tests
Expand Down Expand Up @@ -74,15 +78,34 @@ pnpm test -d -py --client=httpx # Python httpx client development
pnpm test -ts -py # Test TypeScript/Python compatibility
pnpm test -d -py -go # Test Python/Go on testnet

# Legacy Compatibility Testing
pnpm test --legacy -d # Test both new and legacy implementations
pnpm test --legacy --client=legacy-axios # Test specific legacy client with new servers
pnpm test --legacy --server=legacy-express # Test specific legacy server with new clients

# Production Testing
pnpm test --prod=true -ts # Test TypeScript in production
pnpm test --network=base -py # Test Python on base network
```

### Environment Variables

Required environment variables (set in `.env` file):
```bash
CLIENT_EVM_PRIVATE_KEY=0x... # Private key for client wallet
CLIENT_SVM_PRIVATE_KEY=... # Solana private key for client
SERVER_EVM_ADDRESS=0x... # Server's EVM payment address
SERVER_SVM_ADDRESS=... # Server's Solana payment address

# Optional (for real blockchain facilitator)
EVM_RPC_URL=https://sepolia.base.org # RPC endpoint for blockchain access (defaults to Base Sepolia)
```

### Environment Options

```bash
-d, --dev # Development mode (testnet, no CDP)
-v, --verbose # Detailed logging
--legacy # Include legacy implementations from /legacy directory
--log-file=<path> # Save output to file
```
64 changes: 64 additions & 0 deletions e2e/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# End-to-End Testing TODO

## Overview

The e2e test suite validates the complete x402 payment flow across different client and server implementations, ensuring protocol compliance and interoperability between all SDKs.

## Current Status

### Implemented
- ✅ Express server (TypeScript) - reference implementation
- ✅ Fetch client (TypeScript) - reference implementation
- ✅ Test harness with automatic discovery and execution
- ✅ Support for both v1 and v2 protocol testing

### Pending Implementation
- 🔄 Hono server (TypeScript)
- 🔄 Next.js server (TypeScript)
- 🔄 Axios client (TypeScript)
- 🔄 Python servers (FastAPI, Flask)
- 🔄 Python clients (httpx, requests)
- 🔄 Go servers (Gin)
- 🔄 Go clients

## Facilitator Support

### Local Facilitators
Add one facilitator implementation per language in `/facilitators/`:
- `/facilitators/typescript` - TypeScript facilitator implementation
- `/facilitators/python` - Python facilitator implementation
- `/facilitators/go` - Go facilitator implementation

### Remote Facilitators
Add configuration support for external facilitator services:
- x402.org facilitator
- CDP (Coinbase Developer Platform) facilitator
- Custom facilitator endpoints

## Test Runner Improvements

### Configuration Flags
Enhance the test runner to support:
- `--facilitator` flag to select which facilitator(s) to use
- `local` - Use local facilitator implementations
- `x402` - Use x402.org facilitator
- `cdp` - Use CDP facilitator
- `all` - Test against all available facilitators
- `--client` flag to filter which clients to test
- `--server` flag to filter which servers to test
- `--protocol-version` flag to test specific protocol versions (v1, v2, or both)

### Test Matrix
Implement comprehensive testing matrix:
- All client × server combinations
- All facilitator options
- Both protocol versions where applicable
- Different payment mechanisms (EVM, Solana, etc.)

## Protocol Files

The test suite uses standardized protocols for communication:
- `/e2e/servers/text-server-protocol.txt` - Server implementation requirements
- `/e2e/clients/text-client-protocol.txt` - Client implementation requirements

All new implementations must follow these protocols to ensure compatibility with the test harness.
75 changes: 44 additions & 31 deletions e2e/clients/fetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,64 @@
import { config } from "dotenv";
import { Hex } from "viem";
import { createSigner, decodeXPaymentResponse, MultiNetworkSigner, wrapFetchWithPayment } from "x402-fetch";
import { wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch";
import { privateKeyToAccount } from "viem/accounts";
import { ExactEvmClient } from "@x402/evm";
import { ExactEvmClientV1 } from "@x402/evm/v1";

config();

const evmPrivateKey = process.env.EVM_PRIVATE_KEY as Hex;
const svmPrivateKey = process.env.SVM_PRIVATE_KEY as string;
const baseURL = process.env.RESOURCE_SERVER_URL as string;
const endpointPath = process.env.ENDPOINT_PATH as string;
const url = `${baseURL}${endpointPath}`;
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);

if (!baseURL || !evmPrivateKey || !svmPrivateKey || !endpointPath) {
console.error("Missing required environment variables");
process.exit(1);
}

const evmSigner = await createSigner("base-sepolia", evmPrivateKey);
const svmSigner = await createSigner("solana-devnet", svmPrivateKey);
const account = { evm: evmSigner, svm: svmSigner } as MultiNetworkSigner;

const fetchWithPayment = wrapFetchWithPayment(fetch, account);
const fetchWithPayment = wrapFetchWithPayment(fetch, {
schemes: [
{
network: "eip155:*",
client: new ExactEvmClient(account),
},
{
network: "base-sepolia" as `${string}:${string}`,
x402Version: 1,
client: new ExactEvmClientV1(account),
},
{
network: "base" as `${string}:${string}`,
x402Version: 1,
client: new ExactEvmClientV1(account),
},
],
});

fetchWithPayment(url, {
method: "GET",
})
.then(async response => {
const data = await response.json();
const paymentResponse = response.headers.get("x-payment-response");
}).then(async response => {
const data = await response.json();
// Check both v2 (PAYMENT-RESPONSE) and v1 (X-PAYMENT-RESPONSE) headers
const paymentResponse = response.headers.get("PAYMENT-RESPONSE") || response.headers.get("X-PAYMENT-RESPONSE");

if (!paymentResponse) {
// No payment was required
const result = {
success: true,
data: data,
status_code: response.status,
payment_response: decodeXPaymentResponse(paymentResponse!)
};

// Output structured result as JSON for proxy to parse
console.log(JSON.stringify(result));
process.exit(0);
})
.catch(error => {
const errorResult = {
success: false,
error: error.message || String(error),
status_code: error.response?.status
};
return;
}

const decodedPaymentResponse = decodePaymentResponseHeader(paymentResponse);

const result = {
success: decodedPaymentResponse.success,
data: data,
status_code: response.status,
payment_response: decodedPaymentResponse,
};

console.log(JSON.stringify(errorResult));
process.exit(1);
});
// Output structured result as JSON for proxy to parse
console.log(JSON.stringify(result));
process.exit(0);
});
6 changes: 4 additions & 2 deletions e2e/clients/fetch/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "fetch-client-example",
"name": "@x402/fetch-e2e",
"private": true,
"type": "module",
"scripts": {
Expand All @@ -13,7 +13,9 @@
"axios": "^1.7.9",
"dotenv": "^16.4.7",
"viem": "^2.21.26",
"x402-fetch": "workspace:*"
"@x402/evm": "workspace:*",
"@x402/core": "workspace:*",
"@x402/fetch": "workspace:*"
},
"devDependencies": {
"@eslint/js": "^9.24.0",
Expand Down
8 changes: 5 additions & 3 deletions e2e/clients/fetch/test.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
"type": "client",
"language": "typescript",
"protocolFamilies": [
"evm",
"svm"
"evm"
],
"x402Versions": [
1,
2
],
"environment": {
"required": [
"EVM_PRIVATE_KEY",
"SVM_PRIVATE_KEY",
"RESOURCE_SERVER_URL",
"ENDPOINT_PATH"
],
Expand Down
10 changes: 3 additions & 7 deletions e2e/clients/fetch/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
"strict": true,
"resolveJsonModule": true,
"baseUrl": ".",
"types": [
"node"
]
"types": ["node"]
},
"include": [
"index.ts"
]
}
"include": ["index.ts"]
}
Loading