x402z-client 0.0.19 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,6 +26,7 @@ const account = privateKeyToAccount("0x...");
26
26
  const relayer = await createRelayer(SepoliaConfig);
27
27
 
28
28
  const client = createX402zClient({
29
+ preferredMethods: ["erc7984-mind-v1.CONFIDENTIAL_USDC.sepolia"] as const,
29
30
  signer: {
30
31
  address: account.address,
31
32
  signTypedData: account.signTypedData,
@@ -33,28 +34,74 @@ const client = createX402zClient({
33
34
  relayer,
34
35
  });
35
36
 
36
- const response = await client.pay("https://example.com/demo");
37
- console.log(response.status);
37
+ const paid = await client.pay("https://example.com/demo");
38
+ console.log(paid.response.status);
38
39
  ```
39
40
 
40
41
  `createX402zClient` builds the confidential payment input automatically using the
41
42
  `confidential.batcherAddress` provided by the server’s payment requirements.
42
43
 
44
+ ## Choosing Payment Methods (Client)
45
+
46
+ The client picks the **first** preferred method that matches the server’s `accepts` list
47
+ by `scheme + network + asset`.
48
+
49
+ You can specify preferred methods in two ways:
50
+
51
+ 1) **Name string** (scheme + token + network alias):
52
+ ```ts
53
+ preferredMethods: ["exact.USDC.base", "erc7984-mind-v1.CONFIDENTIAL_USDC.sepolia"] as const
54
+ ```
55
+
56
+ 2) **Config entry** (from `SCHEME_CONFIG`):
57
+ ```ts
58
+ import { SCHEME_CONFIG } from "x402z-scheme-config";
59
+
60
+ preferredMethods: [
61
+ SCHEME_CONFIG.exact.USDC.base,
62
+ SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia,
63
+ ]
64
+ ```
65
+
66
+ If `preferredMethods` is omitted, the client defaults to `SCHEME_CONFIG_NAMES`
67
+ (all known methods, in order).
68
+
43
69
  ## API
44
70
 
45
71
  - `createX402zClient(config)`
46
- - `signer` (required): EIP-712 signer for x402 payloads
47
- - `relayer` (required): Zama relayer instance used to build encrypted inputs
72
+ - `preferredMethods` (optional): ordered list of preferred methods.
73
+ - Name form: `"exact.USDC.base"` (matches scheme/token/network alias from `x402z-scheme-config`).
74
+ - Config form: `SCHEME_CONFIG.exact.USDC.base`.
75
+ - `signer` (required): signer used for both exact and confidential schemes
76
+ - `relayer` (required when using `erc7984-mind-v1`): Zama relayer instance used to build encrypted inputs
48
77
  - `fetch` (optional): custom fetch implementation
49
78
  - `client.pay(url, options?)`: performs the 402 handshake and retries with payment headers
79
+ - returns `{ response, paymentRequired?, confidentialRequirements? }`
50
80
 
51
81
  ## Examples
52
82
 
53
83
  See `examples/README.md` for the full-process scripts (`client-pay.ts`, `client-wrap.ts`, `client-unwrap.ts`).
54
84
 
85
+ ## Exact scheme (ERC20) usage
86
+
87
+ ```ts
88
+ import { createX402zClient } from "x402z-client";
89
+ import { privateKeyToAccount } from "viem/accounts";
90
+
91
+ const account = privateKeyToAccount("0x...");
92
+ const client = createX402zClient({
93
+ preferredMethods: ["exact.USDC.sepolia"] as const,
94
+ signer: account,
95
+ });
96
+
97
+ const paid = await client.pay("http://localhost:8090/erc20-demo");
98
+ console.log(paid.response.status);
99
+ ```
100
+
55
101
  ## Notes
56
102
 
57
- - Scheme name: `erc7984-mind-v1`
103
+ - Confidential scheme name: `erc7984-mind-v1`
104
+ - Exact scheme name: `exact`
58
105
  - The client does not expose balance helpers; use `x402z-shared` for that.
59
106
  - For browser usage, use `x402z-client-web` with `x402z-shared-web`.
60
107
 
@@ -71,7 +118,10 @@ Exports:
71
118
  Types:
72
119
  - `X402zClientSchemeOptions`: scheme config for client registration.
73
120
  - `X402zClientRegistrationOptions`: registration options for the scheme.
121
+ - `PreferredPaymentMethod`: preferred payment method selector (object form or scheme-config name string).
74
122
  - `X402zClientOptions`: options for `createX402zClient`.
123
+ - `X402zConfidentialClientOptions`: options for confidential payments.
124
+ - `X402zExactClientOptions`: options for exact payments.
75
125
  - `PayOptions`: per-request payment options.
76
126
  - `AfterPaymentCreationHook`: hook after a payment is built.
77
127
  - `BeforePaymentCreationHook`: hook before a payment is built.
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
- import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network } from '@x402/core/types';
1
+ import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
2
2
  export * from '@x402/core/types';
3
3
  import { ClientEvmSigner } from '@x402/evm';
4
4
  import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared';
5
- import { x402Client } from '@x402/core/client';
5
+ import { x402Client, SelectPaymentRequirements, PaymentPolicy } from '@x402/core/client';
6
6
  export { AfterPaymentCreationHook, BeforePaymentCreationHook, OnPaymentCreationFailureHook, PaymentCreatedContext, PaymentCreationContext, PaymentCreationFailureContext, PaymentPolicy, SchemeRegistration, SelectPaymentRequirements, x402Client, x402ClientConfig, x402HTTPClient } from '@x402/core/client';
7
+ import { SchemeConfigName, ExactTokenConfig, ConfidentialTokenConfig } from 'x402z-scheme-config';
7
8
 
8
9
  type X402zClientSchemeOptions = {
9
10
  signer: ClientEvmSigner;
@@ -31,16 +32,38 @@ declare function registerX402zEvmClientScheme(client: x402Client, config: X402zC
31
32
 
32
33
  declare function buildPaymentInput(relayer: RelayerInstance, requirements: PaymentRequirements, amount: number): Promise<ConfidentialPaymentInput>;
33
34
 
34
- type X402zClientOptions = Omit<X402zClientRegistrationOptions, "buildPayment"> & {
35
- relayer: RelayerInstance;
35
+ type PreferredPaymentMethod = SchemeConfigName | ExactTokenConfig | ConfidentialTokenConfig;
36
+ type BaseClientOptions = {
36
37
  fetch?: typeof fetch;
37
38
  debug?: boolean;
38
39
  };
40
+ type X402zClientOptions = {
41
+ preferredMethods?: readonly PreferredPaymentMethod[];
42
+ signer: ClientEvmSigner;
43
+ relayer?: RelayerInstance;
44
+ networks?: Network[];
45
+ paymentRequirementsSelector?: SelectPaymentRequirements;
46
+ policies?: PaymentPolicy[];
47
+ eip712?: X402zClientSchemeOptions["eip712"];
48
+ hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
49
+ clock?: X402zClientSchemeOptions["clock"];
50
+ onPaymentSelected?: (info: {
51
+ method: PreferredPaymentMethod;
52
+ requirements: PaymentRequirements;
53
+ }) => void;
54
+ } & BaseClientOptions;
39
55
  type PayOptions = {
40
56
  headers?: Record<string, string>;
41
57
  };
42
- declare function createX402zClient(config: X402zClientOptions): {
43
- pay(url: string, options?: PayOptions): Promise<Response>;
58
+ type PayResult = {
59
+ response: Response;
60
+ paymentRequired?: PaymentRequired;
61
+ confidentialRequirements?: PaymentRequirements;
62
+ };
63
+ type X402zClient = {
64
+ relayer?: RelayerInstance;
65
+ pay: (url: string, options?: PayOptions) => Promise<PayResult>;
44
66
  };
67
+ declare function createX402zClient(config: X402zClientOptions): X402zClient;
45
68
 
46
- export { type PayOptions, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, buildPaymentInput, createX402zClient, registerX402zEvmClientScheme };
69
+ export { type PayOptions, type PayResult, type PreferredPaymentMethod, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, buildPaymentInput, createX402zClient, registerX402zEvmClientScheme };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network } from '@x402/core/types';
1
+ import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
2
2
  export * from '@x402/core/types';
3
3
  import { ClientEvmSigner } from '@x402/evm';
4
4
  import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared';
5
- import { x402Client } from '@x402/core/client';
5
+ import { x402Client, SelectPaymentRequirements, PaymentPolicy } from '@x402/core/client';
6
6
  export { AfterPaymentCreationHook, BeforePaymentCreationHook, OnPaymentCreationFailureHook, PaymentCreatedContext, PaymentCreationContext, PaymentCreationFailureContext, PaymentPolicy, SchemeRegistration, SelectPaymentRequirements, x402Client, x402ClientConfig, x402HTTPClient } from '@x402/core/client';
7
+ import { SchemeConfigName, ExactTokenConfig, ConfidentialTokenConfig } from 'x402z-scheme-config';
7
8
 
8
9
  type X402zClientSchemeOptions = {
9
10
  signer: ClientEvmSigner;
@@ -31,16 +32,38 @@ declare function registerX402zEvmClientScheme(client: x402Client, config: X402zC
31
32
 
32
33
  declare function buildPaymentInput(relayer: RelayerInstance, requirements: PaymentRequirements, amount: number): Promise<ConfidentialPaymentInput>;
33
34
 
34
- type X402zClientOptions = Omit<X402zClientRegistrationOptions, "buildPayment"> & {
35
- relayer: RelayerInstance;
35
+ type PreferredPaymentMethod = SchemeConfigName | ExactTokenConfig | ConfidentialTokenConfig;
36
+ type BaseClientOptions = {
36
37
  fetch?: typeof fetch;
37
38
  debug?: boolean;
38
39
  };
40
+ type X402zClientOptions = {
41
+ preferredMethods?: readonly PreferredPaymentMethod[];
42
+ signer: ClientEvmSigner;
43
+ relayer?: RelayerInstance;
44
+ networks?: Network[];
45
+ paymentRequirementsSelector?: SelectPaymentRequirements;
46
+ policies?: PaymentPolicy[];
47
+ eip712?: X402zClientSchemeOptions["eip712"];
48
+ hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
49
+ clock?: X402zClientSchemeOptions["clock"];
50
+ onPaymentSelected?: (info: {
51
+ method: PreferredPaymentMethod;
52
+ requirements: PaymentRequirements;
53
+ }) => void;
54
+ } & BaseClientOptions;
39
55
  type PayOptions = {
40
56
  headers?: Record<string, string>;
41
57
  };
42
- declare function createX402zClient(config: X402zClientOptions): {
43
- pay(url: string, options?: PayOptions): Promise<Response>;
58
+ type PayResult = {
59
+ response: Response;
60
+ paymentRequired?: PaymentRequired;
61
+ confidentialRequirements?: PaymentRequirements;
62
+ };
63
+ type X402zClient = {
64
+ relayer?: RelayerInstance;
65
+ pay: (url: string, options?: PayOptions) => Promise<PayResult>;
44
66
  };
67
+ declare function createX402zClient(config: X402zClientOptions): X402zClient;
45
68
 
46
- export { type PayOptions, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, buildPaymentInput, createX402zClient, registerX402zEvmClientScheme };
69
+ export { type PayOptions, type PayResult, type PreferredPaymentMethod, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, buildPaymentInput, createX402zClient, registerX402zEvmClientScheme };
package/dist/index.js CHANGED
@@ -24,8 +24,8 @@ __export(index_exports, {
24
24
  buildPaymentInput: () => buildPaymentInput,
25
25
  createX402zClient: () => createX402zClient,
26
26
  registerX402zEvmClientScheme: () => registerX402zEvmClientScheme,
27
- x402Client: () => import_client3.x402Client,
28
- x402HTTPClient: () => import_client3.x402HTTPClient
27
+ x402Client: () => import_client4.x402Client,
28
+ x402HTTPClient: () => import_client4.x402HTTPClient
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
31
 
@@ -147,18 +147,59 @@ async function buildPaymentInput(relayer, requirements, amount) {
147
147
  // src/http/client.ts
148
148
  var import_client = require("@x402/core/client");
149
149
  var import_http = require("@x402/core/http");
150
+ var import_client2 = require("@x402/evm/exact/client");
150
151
  var import_viem2 = require("viem");
151
152
  var import_x402z_shared3 = require("x402z-shared");
152
- function createX402zClient(config) {
153
- const { fetch: fetchOverride, ...registerConfig } = config;
154
- const fetchFn = fetchOverride ?? globalThis.fetch;
155
- const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
156
- if (!fetchFn) {
157
- throw new Error("fetch is not available; provide a fetch implementation");
153
+ var import_x402z_scheme_config = require("x402z-scheme-config");
154
+ function resolvePreferredMethod(method) {
155
+ if (typeof method !== "string") {
156
+ return method;
157
+ }
158
+ const config = (0, import_x402z_scheme_config.getSchemeConfigByName)(method);
159
+ if (!config) {
160
+ throw new Error(`Unknown preferred method: ${method}`);
158
161
  }
162
+ return config;
163
+ }
164
+ function matchesMethod(requirements, method) {
165
+ const resolved = resolvePreferredMethod(method);
166
+ const scheme = (0, import_x402z_scheme_config.isConfidentialTokenConfig)(resolved) ? "erc7984-mind-v1" : "exact";
167
+ if (requirements.scheme !== scheme) return false;
168
+ if (requirements.network !== resolved.network) return false;
169
+ if (requirements.asset && requirements.asset?.toLowerCase() !== resolved.asset.toLowerCase()) return false;
170
+ return true;
171
+ }
172
+ function filterPaymentRequired(paymentRequired, method) {
173
+ const accepts = paymentRequired.accepts.filter(
174
+ (requirement) => matchesMethod(requirement, method)
175
+ );
176
+ return { ...paymentRequired, accepts };
177
+ }
178
+ function createExactPayer(config, debugEnabled) {
179
+ const client = new import_client.x402Client(config.paymentRequirementsSelector);
180
+ (0, import_client2.registerExactEvmScheme)(client, {
181
+ signer: config.signer,
182
+ networks: config.networks,
183
+ policies: config.policies,
184
+ paymentRequirementsSelector: config.paymentRequirementsSelector
185
+ });
186
+ const httpClient = new import_http.x402HTTPClient(client);
187
+ return {
188
+ async buildHeaders(paymentRequired) {
189
+ const payload = await httpClient.createPaymentPayload(paymentRequired);
190
+ const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
191
+ if (debugEnabled) {
192
+ console.debug("[x402z-client] payment payload", payload);
193
+ console.debug("[x402z-client] payment headers", payHeaders);
194
+ }
195
+ return { headers: payHeaders };
196
+ }
197
+ };
198
+ }
199
+ function createConfidentialPayer(config, debugEnabled) {
159
200
  const buildPayment = async (requirements) => {
160
201
  if (!(0, import_viem2.isAddress)(requirements.asset)) {
161
- throw new Error(`Invalid TOKEN_ADDRESS from requirements: ${requirements.asset}`);
202
+ throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
162
203
  }
163
204
  const extra = requirements.extra;
164
205
  const batcherAddress = extra?.confidential?.batcherAddress;
@@ -185,51 +226,131 @@ function createX402zClient(config) {
185
226
  };
186
227
  const client = new import_client.x402Client();
187
228
  registerX402zEvmClientScheme(client, {
188
- ...registerConfig,
189
- buildPayment
229
+ signer: config.signer,
230
+ buildPayment,
231
+ eip712: config.eip712,
232
+ hashEncryptedAmountInput: config.hashEncryptedAmountInput,
233
+ clock: config.clock,
234
+ networks: config.networks
190
235
  });
191
236
  const httpClient = new import_http.x402HTTPClient(client);
237
+ const extractConfidentialRequirements = (paymentRequired) => {
238
+ const requirements = paymentRequired.accepts.find(
239
+ (requirement) => requirement.scheme === "erc7984-mind-v1"
240
+ );
241
+ if (!requirements) {
242
+ throw new Error("Missing erc7984-mind-v1 payment requirements");
243
+ }
244
+ return requirements;
245
+ };
192
246
  return {
193
- async pay(url, options) {
194
- const initial = await fetchFn(url, { headers: options?.headers });
195
- if (initial.status !== 402) {
196
- return initial;
197
- }
198
- const paymentRequired = httpClient.getPaymentRequiredResponse(
199
- (name) => initial.headers.get(name),
200
- await initial.json().catch(() => ({}))
201
- );
247
+ relayer: config.relayer,
248
+ extractConfidentialRequirements,
249
+ async buildHeaders(paymentRequired) {
202
250
  const payload = await httpClient.createPaymentPayload(paymentRequired);
203
251
  const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
204
252
  if (debugEnabled) {
205
253
  console.debug("[x402z-client] payment payload", payload);
206
254
  console.debug("[x402z-client] payment headers", payHeaders);
207
255
  }
208
- const mergedHeaders = { ...options?.headers ?? {}, ...payHeaders };
209
- const paidResponse = await fetchFn(url, { headers: mergedHeaders });
210
- if (debugEnabled) {
211
- try {
212
- const body = await paidResponse.clone().text();
213
- console.debug("[x402z-client] response", {
214
- status: paidResponse.status,
215
- headers: Object.fromEntries(paidResponse.headers.entries()),
216
- body
217
- });
218
- } catch (error) {
219
- console.debug("[x402z-client] response", {
220
- status: paidResponse.status,
221
- headers: Object.fromEntries(paidResponse.headers.entries()),
222
- body: "<unavailable>"
223
- });
224
- }
256
+ return { headers: payHeaders };
257
+ }
258
+ };
259
+ }
260
+ function createX402zClient(config) {
261
+ const fetchFn = config.fetch ?? globalThis.fetch;
262
+ const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
263
+ if (!fetchFn) {
264
+ throw new Error("fetch is not available; provide a fetch implementation");
265
+ }
266
+ const preferredMethods = config.preferredMethods?.length ? [...config.preferredMethods] : import_x402z_scheme_config.SCHEME_CONFIG_NAMES;
267
+ const exactPayer = createExactPayer(
268
+ {
269
+ signer: config.signer,
270
+ networks: config.networks,
271
+ paymentRequirementsSelector: config.paymentRequirementsSelector,
272
+ policies: config.policies
273
+ },
274
+ debugEnabled
275
+ );
276
+ const confidentialPayer = config.relayer ? createConfidentialPayer(
277
+ {
278
+ signer: config.signer,
279
+ relayer: config.relayer,
280
+ eip712: config.eip712,
281
+ hashEncryptedAmountInput: config.hashEncryptedAmountInput,
282
+ clock: config.clock,
283
+ networks: config.networks
284
+ },
285
+ debugEnabled
286
+ ) : void 0;
287
+ const parserClient = new import_client.x402Client();
288
+ const parserHttp = new import_http.x402HTTPClient(parserClient);
289
+ const pay = async (url, options) => {
290
+ const initial = await fetchFn(url, { headers: options?.headers });
291
+ if (initial.status !== 402) {
292
+ return { response: initial };
293
+ }
294
+ const paymentRequired = parserHttp.getPaymentRequiredResponse(
295
+ (name) => initial.headers.get(name),
296
+ await initial.json().catch(() => ({}))
297
+ );
298
+ const selectedMethod = preferredMethods.find(
299
+ (method) => paymentRequired.accepts.some(
300
+ (requirement) => matchesMethod(requirement, method)
301
+ )
302
+ );
303
+ if (!selectedMethod) {
304
+ throw new Error("No preferred payment methods are supported by payment requirements");
305
+ }
306
+ const filteredRequirements = filterPaymentRequired(paymentRequired, selectedMethod);
307
+ if (filteredRequirements.accepts.length === 0) {
308
+ throw new Error("Missing payment requirements for selected method");
309
+ }
310
+ let headersResult;
311
+ let confidentialRequirements;
312
+ const selectedRequirements = filteredRequirements.accepts[0];
313
+ config.onPaymentSelected?.({
314
+ method: selectedMethod,
315
+ requirements: selectedRequirements
316
+ });
317
+ if (selectedRequirements.scheme === "exact") {
318
+ headersResult = await exactPayer.buildHeaders(filteredRequirements);
319
+ } else {
320
+ if (!confidentialPayer) {
321
+ throw new Error("Confidential scheme is selected but relayer is missing");
225
322
  }
226
- return paidResponse;
323
+ confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
324
+ headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
227
325
  }
326
+ const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
327
+ const paidResponse = await fetchFn(url, { headers: mergedHeaders });
328
+ if (debugEnabled) {
329
+ try {
330
+ const body = await paidResponse.clone().text();
331
+ console.debug("[x402z-client] response", {
332
+ status: paidResponse.status,
333
+ headers: Object.fromEntries(paidResponse.headers.entries()),
334
+ body
335
+ });
336
+ } catch (error) {
337
+ console.debug("[x402z-client] response", {
338
+ status: paidResponse.status,
339
+ headers: Object.fromEntries(paidResponse.headers.entries()),
340
+ body: "<unavailable>"
341
+ });
342
+ }
343
+ }
344
+ return { response: paidResponse, paymentRequired, confidentialRequirements };
345
+ };
346
+ return {
347
+ relayer: confidentialPayer?.relayer,
348
+ pay
228
349
  };
229
350
  }
230
351
 
231
352
  // src/index.ts
232
- var import_client3 = require("@x402/core/client");
353
+ var import_client4 = require("@x402/core/client");
233
354
  // Annotate the CommonJS export names for ESM import in node:
234
355
  0 && (module.exports = {
235
356
  X402zEvmClientScheme,
package/dist/index.mjs CHANGED
@@ -121,20 +121,61 @@ async function buildPaymentInput(relayer, requirements, amount) {
121
121
  // src/http/client.ts
122
122
  import { x402Client } from "@x402/core/client";
123
123
  import { x402HTTPClient } from "@x402/core/http";
124
+ import { registerExactEvmScheme } from "@x402/evm/exact/client";
124
125
  import { isAddress } from "viem";
125
126
  import {
126
127
  createEncryptedAmountInput as createEncryptedAmountInput2
127
128
  } from "x402z-shared";
128
- function createX402zClient(config) {
129
- const { fetch: fetchOverride, ...registerConfig } = config;
130
- const fetchFn = fetchOverride ?? globalThis.fetch;
131
- const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
132
- if (!fetchFn) {
133
- throw new Error("fetch is not available; provide a fetch implementation");
129
+ import { getSchemeConfigByName, SCHEME_CONFIG_NAMES, isConfidentialTokenConfig } from "x402z-scheme-config";
130
+ function resolvePreferredMethod(method) {
131
+ if (typeof method !== "string") {
132
+ return method;
133
+ }
134
+ const config = getSchemeConfigByName(method);
135
+ if (!config) {
136
+ throw new Error(`Unknown preferred method: ${method}`);
134
137
  }
138
+ return config;
139
+ }
140
+ function matchesMethod(requirements, method) {
141
+ const resolved = resolvePreferredMethod(method);
142
+ const scheme = isConfidentialTokenConfig(resolved) ? "erc7984-mind-v1" : "exact";
143
+ if (requirements.scheme !== scheme) return false;
144
+ if (requirements.network !== resolved.network) return false;
145
+ if (requirements.asset && requirements.asset?.toLowerCase() !== resolved.asset.toLowerCase()) return false;
146
+ return true;
147
+ }
148
+ function filterPaymentRequired(paymentRequired, method) {
149
+ const accepts = paymentRequired.accepts.filter(
150
+ (requirement) => matchesMethod(requirement, method)
151
+ );
152
+ return { ...paymentRequired, accepts };
153
+ }
154
+ function createExactPayer(config, debugEnabled) {
155
+ const client = new x402Client(config.paymentRequirementsSelector);
156
+ registerExactEvmScheme(client, {
157
+ signer: config.signer,
158
+ networks: config.networks,
159
+ policies: config.policies,
160
+ paymentRequirementsSelector: config.paymentRequirementsSelector
161
+ });
162
+ const httpClient = new x402HTTPClient(client);
163
+ return {
164
+ async buildHeaders(paymentRequired) {
165
+ const payload = await httpClient.createPaymentPayload(paymentRequired);
166
+ const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
167
+ if (debugEnabled) {
168
+ console.debug("[x402z-client] payment payload", payload);
169
+ console.debug("[x402z-client] payment headers", payHeaders);
170
+ }
171
+ return { headers: payHeaders };
172
+ }
173
+ };
174
+ }
175
+ function createConfidentialPayer(config, debugEnabled) {
135
176
  const buildPayment = async (requirements) => {
136
177
  if (!isAddress(requirements.asset)) {
137
- throw new Error(`Invalid TOKEN_ADDRESS from requirements: ${requirements.asset}`);
178
+ throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
138
179
  }
139
180
  const extra = requirements.extra;
140
181
  const batcherAddress = extra?.confidential?.batcherAddress;
@@ -161,46 +202,126 @@ function createX402zClient(config) {
161
202
  };
162
203
  const client = new x402Client();
163
204
  registerX402zEvmClientScheme(client, {
164
- ...registerConfig,
165
- buildPayment
205
+ signer: config.signer,
206
+ buildPayment,
207
+ eip712: config.eip712,
208
+ hashEncryptedAmountInput: config.hashEncryptedAmountInput,
209
+ clock: config.clock,
210
+ networks: config.networks
166
211
  });
167
212
  const httpClient = new x402HTTPClient(client);
213
+ const extractConfidentialRequirements = (paymentRequired) => {
214
+ const requirements = paymentRequired.accepts.find(
215
+ (requirement) => requirement.scheme === "erc7984-mind-v1"
216
+ );
217
+ if (!requirements) {
218
+ throw new Error("Missing erc7984-mind-v1 payment requirements");
219
+ }
220
+ return requirements;
221
+ };
168
222
  return {
169
- async pay(url, options) {
170
- const initial = await fetchFn(url, { headers: options?.headers });
171
- if (initial.status !== 402) {
172
- return initial;
173
- }
174
- const paymentRequired = httpClient.getPaymentRequiredResponse(
175
- (name) => initial.headers.get(name),
176
- await initial.json().catch(() => ({}))
177
- );
223
+ relayer: config.relayer,
224
+ extractConfidentialRequirements,
225
+ async buildHeaders(paymentRequired) {
178
226
  const payload = await httpClient.createPaymentPayload(paymentRequired);
179
227
  const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
180
228
  if (debugEnabled) {
181
229
  console.debug("[x402z-client] payment payload", payload);
182
230
  console.debug("[x402z-client] payment headers", payHeaders);
183
231
  }
184
- const mergedHeaders = { ...options?.headers ?? {}, ...payHeaders };
185
- const paidResponse = await fetchFn(url, { headers: mergedHeaders });
186
- if (debugEnabled) {
187
- try {
188
- const body = await paidResponse.clone().text();
189
- console.debug("[x402z-client] response", {
190
- status: paidResponse.status,
191
- headers: Object.fromEntries(paidResponse.headers.entries()),
192
- body
193
- });
194
- } catch (error) {
195
- console.debug("[x402z-client] response", {
196
- status: paidResponse.status,
197
- headers: Object.fromEntries(paidResponse.headers.entries()),
198
- body: "<unavailable>"
199
- });
200
- }
232
+ return { headers: payHeaders };
233
+ }
234
+ };
235
+ }
236
+ function createX402zClient(config) {
237
+ const fetchFn = config.fetch ?? globalThis.fetch;
238
+ const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
239
+ if (!fetchFn) {
240
+ throw new Error("fetch is not available; provide a fetch implementation");
241
+ }
242
+ const preferredMethods = config.preferredMethods?.length ? [...config.preferredMethods] : SCHEME_CONFIG_NAMES;
243
+ const exactPayer = createExactPayer(
244
+ {
245
+ signer: config.signer,
246
+ networks: config.networks,
247
+ paymentRequirementsSelector: config.paymentRequirementsSelector,
248
+ policies: config.policies
249
+ },
250
+ debugEnabled
251
+ );
252
+ const confidentialPayer = config.relayer ? createConfidentialPayer(
253
+ {
254
+ signer: config.signer,
255
+ relayer: config.relayer,
256
+ eip712: config.eip712,
257
+ hashEncryptedAmountInput: config.hashEncryptedAmountInput,
258
+ clock: config.clock,
259
+ networks: config.networks
260
+ },
261
+ debugEnabled
262
+ ) : void 0;
263
+ const parserClient = new x402Client();
264
+ const parserHttp = new x402HTTPClient(parserClient);
265
+ const pay = async (url, options) => {
266
+ const initial = await fetchFn(url, { headers: options?.headers });
267
+ if (initial.status !== 402) {
268
+ return { response: initial };
269
+ }
270
+ const paymentRequired = parserHttp.getPaymentRequiredResponse(
271
+ (name) => initial.headers.get(name),
272
+ await initial.json().catch(() => ({}))
273
+ );
274
+ const selectedMethod = preferredMethods.find(
275
+ (method) => paymentRequired.accepts.some(
276
+ (requirement) => matchesMethod(requirement, method)
277
+ )
278
+ );
279
+ if (!selectedMethod) {
280
+ throw new Error("No preferred payment methods are supported by payment requirements");
281
+ }
282
+ const filteredRequirements = filterPaymentRequired(paymentRequired, selectedMethod);
283
+ if (filteredRequirements.accepts.length === 0) {
284
+ throw new Error("Missing payment requirements for selected method");
285
+ }
286
+ let headersResult;
287
+ let confidentialRequirements;
288
+ const selectedRequirements = filteredRequirements.accepts[0];
289
+ config.onPaymentSelected?.({
290
+ method: selectedMethod,
291
+ requirements: selectedRequirements
292
+ });
293
+ if (selectedRequirements.scheme === "exact") {
294
+ headersResult = await exactPayer.buildHeaders(filteredRequirements);
295
+ } else {
296
+ if (!confidentialPayer) {
297
+ throw new Error("Confidential scheme is selected but relayer is missing");
201
298
  }
202
- return paidResponse;
299
+ confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
300
+ headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
203
301
  }
302
+ const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
303
+ const paidResponse = await fetchFn(url, { headers: mergedHeaders });
304
+ if (debugEnabled) {
305
+ try {
306
+ const body = await paidResponse.clone().text();
307
+ console.debug("[x402z-client] response", {
308
+ status: paidResponse.status,
309
+ headers: Object.fromEntries(paidResponse.headers.entries()),
310
+ body
311
+ });
312
+ } catch (error) {
313
+ console.debug("[x402z-client] response", {
314
+ status: paidResponse.status,
315
+ headers: Object.fromEntries(paidResponse.headers.entries()),
316
+ body: "<unavailable>"
317
+ });
318
+ }
319
+ }
320
+ return { response: paidResponse, paymentRequired, confidentialRequirements };
321
+ };
322
+ return {
323
+ relayer: confidentialPayer?.relayer,
324
+ pay
204
325
  };
205
326
  }
206
327
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402z-client",
3
- "version": "0.0.19",
3
+ "version": "0.1.2",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -15,10 +15,11 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@x402/core": "^2.0.0",
19
- "@x402/evm": "^2.0.0",
18
+ "@x402/core": "^2.2.0",
19
+ "@x402/evm": "^2.2.0",
20
20
  "viem": "^2.39.3",
21
- "x402z-shared": "0.0.19"
21
+ "x402z-shared": "0.1.2",
22
+ "x402z-scheme-config": "0.1.2"
22
23
  },
23
24
  "devDependencies": {
24
25
  "jest": "^29.7.0",