Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
c3395f6
spec + sdk
phdargen Apr 1, 2026
c5d1799
add auto recover and topUp; add filestorage
phdargen Apr 2, 2026
1be339d
add session manager
phdargen Apr 2, 2026
78ac55d
fix nonce reset
phdargen Apr 2, 2026
46d16c1
add cooperativeWithdraw to spec/contract
phdargen Apr 2, 2026
4fd832e
add cooperative withdraw to sdk
phdargen Apr 2, 2026
b6107fc
update sdk to match spec v1.2
phdargen Apr 9, 2026
308988a
change to auto-generated getters
phdargen Apr 10, 2026
98ac366
improve server devx
phdargen Apr 10, 2026
dc0d25c
improve client devx
phdargen Apr 10, 2026
8fa203c
added concurrent request example
phdargen Apr 10, 2026
b2d3781
minor fixes
phdargen Apr 13, 2026
2750058
add cas
phdargen Apr 14, 2026
26fd2b0
add streaming example
phdargen Apr 15, 2026
e7b909c
rename files to batched*
phdargen Apr 15, 2026
d86b485
updated scheme name deferred -> batched
phdargen Apr 15, 2026
67f719f
update to new contracts
phdargen Apr 15, 2026
8148235
align paymentresponse.extra for all payloads
phdargen Apr 15, 2026
245e7f6
fix recovery after refund
phdargen Apr 15, 2026
37f7158
fix withdraw -> refund flag
phdargen Apr 15, 2026
b245915
fix channel manager
phdargen Apr 15, 2026
c5a6ebf
rename to batch-settlement
phdargen Apr 16, 2026
3b96f71
align with cloudflare specs
phdargen Apr 16, 2026
6d16287
remove claim/refund without sign
phdargen Apr 16, 2026
cb35c59
add tx sim
phdargen Apr 16, 2026
0a89bf4
improve specs
phdargen Apr 16, 2026
05367ef
specs ready
phdargen Apr 16, 2026
bed72b0
add unit/integration tests
phdargen Apr 17, 2026
0e7fddb
fix tests
phdargen Apr 17, 2026
c001db2
improve typing, remove dead code
phdargen Apr 17, 2026
f0b71b0
add readmes
phdargen Apr 17, 2026
dc7cb7b
add e2e tests
phdargen Apr 17, 2026
9d1501f
fix fileStorage
phdargen Apr 17, 2026
e94d2b5
increase depositMultiplier for streaming
phdargen Apr 20, 2026
a52eda2
refund without paid request
phdargen Apr 22, 2026
4d8de03
refactor convertToTokenAmount
phdargen Apr 23, 2026
bbeca41
refactor scheme
phdargen Apr 23, 2026
36044a1
fix recovery for refund flow
phdargen Apr 23, 2026
88a8762
refactor client scheme
phdargen Apr 24, 2026
c1a098f
rename session -> channel
phdargen Apr 24, 2026
c8ed8c7
remove chargedCumulativeAmount from deposit payload
phdargen Apr 24, 2026
568c892
scheme hook adapters
phdargen Apr 27, 2026
893d87a
add enrichSettlementPayload/enrichSettlementResponse hooks
phdargen Apr 27, 2026
6729586
move batch-settlement scheme to scheme hook adapters and enrichSettle…
phdargen Apr 27, 2026
cd10d82
refactor refund and other payloads
phdargen Apr 27, 2026
a28b024
add client hook adapters
phdargen Apr 27, 2026
08652da
align payment-response shapes
phdargen Apr 27, 2026
e3c582e
move corrective 402 info from errorDetails to requirements.extra.chan…
phdargen Apr 28, 2026
6375cf4
fix corrective deposit flow
phdargen Apr 28, 2026
8cef142
update to latest deployment and change channelId computation to EIP-712
phdargen Apr 28, 2026
e50d81e
add hook to handle route handler failures
phdargen Apr 28, 2026
15750be
add atomic channel update and request context primitives
phdargen Apr 29, 2026
81d04bc
guard channels with pending reservations
phdargen Apr 29, 2026
566ae91
fix concurrent claim/settle
phdargen Apr 29, 2026
37e9944
skip facilitator and verify locally if onchain state fresh
phdargen Apr 29, 2026
446556e
add redis storage
phdargen Apr 29, 2026
f959448
add fullstack next example with redis and fix settlementOverride for …
phdargen Apr 29, 2026
76c3e82
align payment-response for all payloads
phdargen Apr 29, 2026
d4cef65
add permit2 support and fix extra enrichment for nested objects
phdargen Apr 30, 2026
d3de03c
fix DEPOSIT_WITNESS_TYPE_STRING
phdargen Apr 30, 2026
c70837d
test client state-loss recovery scenario in e2e
phdargen Apr 30, 2026
ce51015
fix recovery for axios
phdargen Apr 30, 2026
af80680
improve channel manager
phdargen Apr 30, 2026
2d190b8
update client deposit policy
phdargen Apr 30, 2026
0eac0cf
clean up examples
phdargen Apr 30, 2026
1ef2dda
add to testnet facilitator
phdargen Apr 30, 2026
4f970ca
update READMEs
phdargen Apr 30, 2026
586de15
for the culture
phdargen Apr 30, 2026
ec3155a
add changesets
phdargen Apr 30, 2026
4b31597
update to latest deployment
phdargen May 1, 2026
3eb2912
align error codes
phdargen May 4, 2026
291636f
remove receiverAuthorizer fallback
phdargen May 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Launches an interactive CLI where you can select:
- **Clients** - Payment-capable HTTP clients (axios, fetch, httpx, requests, etc.)
- **Extensions** - Additional features like Bazaar discovery
- **Protocols** - EVM, SVM, Aptos, and/or Hedera networks
- **Payment schemes** (when multiple apply) - `exact`, `upto`, or `batch-settlement`

Every valid combination of your selections will be tested. For example, selecting 2 facilitators, 3 servers, and 2 clients will generate and run all compatible test scenarios.

Expand Down Expand Up @@ -134,6 +135,14 @@ FACILITATOR_HEDERA_PRIVATE_KEY=0x... # Hedera ECDSA private key for facilitator
FACILITATOR_STELLAR_PRIVATE_KEY=... # Stellar private key for facilitator
```

Optional environment variables (batch-settlement scheme):

```bash
SERVER_EVM_RECEIVER_AUTHORIZER_PRIVATE_KEY=0x... # server-side self-managed claim/refund signer
CLIENT_EVM_VOUCHER_SIGNER_PRIVATE_KEY=0x... # EOA the client uses to sign vouchers
BATCH_SETTLEMENT_RECOVERY=true # test client state-loss recovery scenario (default: true)
```

### Account Setup Instructions

#### Stellar Testnet
Expand Down
170 changes: 131 additions & 39 deletions e2e/clients/axios/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
UptoEvmScheme as UptoEvmClientScheme,
type UptoEvmSchemeOptions,
} from "@x402/evm/upto/client";
import { BatchSettlementEvmScheme } from "@x402/evm/batch-settlement/client";
import { ExactEvmSchemeV1 } from "@x402/evm/v1";
import { toClientEvmSigner } from "@x402/evm";
import { ExactSvmScheme } from "@x402/svm/exact/client";
Expand Down Expand Up @@ -54,6 +55,23 @@ const uptoSchemeOptions: UptoEvmSchemeOptions | undefined = process.env.EVM_RPC_
? { rpcUrl: process.env.EVM_RPC_URL }
: undefined;

// Batch-settlement scheme uses a per-scenario salt (CHANNEL_SALT) so concurrent
// e2e runs don't collide on the same on-chain channel id. An optional voucher
// signer (EVM_VOUCHER_SIGNER_PRIVATE_KEY) exercises the alt-EOA voucher branch
// while deposits keep using the main client signer.
const channelSalt = process.env.CHANNEL_SALT as `0x${string}` | undefined;
const voucherSignerKey = process.env.EVM_VOUCHER_SIGNER_PRIVATE_KEY as
| `0x${string}`
| undefined;
const voucherSigner = voucherSignerKey
? toClientEvmSigner(privateKeyToAccount(voucherSignerKey), publicClient)
: undefined;
const batchSettlementOptions =
channelSalt || voucherSigner
? { ...(channelSalt ? { salt: channelSalt } : {}), ...(voucherSigner ? { voucherSigner } : {}) }
: undefined;
const batchSettlementScheme = new BatchSettlementEvmScheme(evmSigner, batchSettlementOptions);

// Initialize Aptos signer if key is provided
let aptosAccount: Account | undefined;
if (process.env.APTOS_PRIVATE_KEY) {
Expand Down Expand Up @@ -93,6 +111,7 @@ if (process.env.AVM_PRIVATE_KEY) {
const client = new x402Client()
.register("eip155:*", new ExactEvmScheme(evmSigner, evmSchemeOptions))
.register("eip155:*", new UptoEvmClientScheme(evmSigner, uptoSchemeOptions))
.register("eip155:*", batchSettlementScheme)
.registerV1("base-sepolia", new ExactEvmSchemeV1(evmSigner))
.registerV1("base", new ExactEvmSchemeV1(evmSigner))
.register("solana:*", new ExactSvmScheme(svmSigner))
Expand All @@ -113,46 +132,119 @@ if (avmSigner) {

const axiosWithPayment = wrapAxiosWithPayment(axios.create(), client);

axiosWithPayment
.get(url)
.then(async response => {
const data = response.data;
// Check both v2 (PAYMENT-RESPONSE) and v1 (X-PAYMENT-RESPONSE) headers
const paymentResponse =
response.headers["payment-response"] || response.headers["x-payment-response"];

if (!paymentResponse) {
// No payment was required
const result = {
success: true,
data: data,
status_code: response.status,
};
console.log(JSON.stringify(result));
process.exit(0);
return;
}

const decodedPaymentResponse = decodePaymentResponseHeader(paymentResponse);

const result = {
success: decodedPaymentResponse.success,
data: data,
status_code: response.status,
payment_response: decodedPaymentResponse,
};
const batchSettlementPhase = process.env.BATCH_SETTLEMENT_PHASE as
| "initial"
| "recovery-refund"
| "full"
| undefined;

/**
* Issues a single paid request and returns the parsed result.
*
* @returns Structured result with response data and decoded payment-response.
*/
interface RequestResult {
success: boolean;
data: unknown;
status_code: number;
payment_response?: any;
}

async function issueRequest(): Promise<RequestResult> {
const response = await axiosWithPayment.get(url);
const paymentResponseHeader =
response.headers["payment-response"] || response.headers["x-payment-response"];

if (!paymentResponseHeader) {
return { success: true, data: response.data, status_code: response.status };
}

const decodedPaymentResponse = decodePaymentResponseHeader(paymentResponseHeader);
return {
success: decodedPaymentResponse.success,
data: response.data,
status_code: response.status,
payment_response: decodedPaymentResponse,
};
}

function aggregateBatchResult(
phase: "initial" | "recovery-refund" | "full",
results: RequestResult[],
details: Record<string, RequestResult>,
) {
const last = results[results.length - 1]!;
return {
success: results.every(result => result.success),
data: {
batchSettlement: {
phase,
requests: results,
...details,
},
},
status_code: last.status_code,
payment_response: last.payment_response,
};
}

// Output structured result as JSON for proxy to parse
try {
if (!batchSettlementPhase) {
const result = await issueRequest();
console.log(JSON.stringify(result));
process.exit(0);
})
.catch(error => {
console.error(
JSON.stringify({
success: false,
error: error.message || "Request failed",
status_code: error.response?.status || 500,
}),
}

if (batchSettlementPhase === "initial") {
const deposit = await issueRequest();
const voucher = await issueRequest();
console.log(JSON.stringify(aggregateBatchResult("initial", [deposit, voucher], { deposit, voucher })));
process.exit(0);
}

if (batchSettlementPhase === "recovery-refund") {
const recoveryVoucher = await issueRequest();
const refundSettle = await batchSettlementScheme.refund(url);
const refund = {
success: refundSettle.success,
data: { refund: true },
status_code: 200,
payment_response: refundSettle,
};
console.log(
JSON.stringify(
aggregateBatchResult("recovery-refund", [recoveryVoucher, refund], {
recoveryVoucher,
refund,
}),
),
);
process.exit(1);
});
process.exit(0);
}

if (batchSettlementPhase === "full") {
const deposit = await issueRequest();
const voucher = await issueRequest();
const refundSettle = await batchSettlementScheme.refund(url);
const refund = {
success: refundSettle.success,
data: { refund: true },
status_code: 200,
payment_response: refundSettle,
};
console.log(JSON.stringify(aggregateBatchResult("full", [deposit, voucher, refund], { deposit, voucher, refund })));
process.exit(0);
}

throw new Error(`Unknown BATCH_SETTLEMENT_PHASE: ${batchSettlementPhase}`);
} catch (error: unknown) {
const err = error as { message?: string; response?: { status?: number } };
console.error(
JSON.stringify({
success: false,
error: err.message || "Request failed",
status_code: err.response?.status || 500,
}),
);
process.exit(1);
}
11 changes: 7 additions & 4 deletions e2e/clients/axios/test.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
2
],
"evm": {
"transferMethods": [
"assetTransferMethods": [
"eip3009",
"permit2",
"upto"
"permit2"
]
},
"extensions": [
Expand All @@ -39,7 +38,11 @@
"HEDERA_PRIVATE_KEY",
"HEDERA_NETWORK",
"HEDERA_NODE_URL",
"STELLAR_PRIVATE_KEY"
"STELLAR_PRIVATE_KEY",
"CHANNEL_SALT",
"BATCH_SETTLEMENT_PHASE",
"BATCH_SETTLEMENT_RECOVERY",
"EVM_VOUCHER_SIGNER_PRIVATE_KEY"
]
}
}
Loading
Loading