x402z-client-web 0.0.19 → 0.1.0
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 +11 -5
- package/dist/index.d.mts +34 -14
- package/dist/index.d.ts +34 -14
- package/dist/index.js +137 -37
- package/dist/index.mjs +137 -37
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,12 +35,13 @@ const relayer = await createRelayer(relayerConfig);
|
|
|
35
35
|
const signer = await createBrowserSigner(provider);
|
|
36
36
|
|
|
37
37
|
const client = createX402zClient({
|
|
38
|
+
schemePreference: ["erc7984-mind-v1"],
|
|
38
39
|
signer,
|
|
39
40
|
relayer,
|
|
40
41
|
});
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
-
console.log(response.status);
|
|
43
|
+
const paid = await client.pay("https://example.com/demo");
|
|
44
|
+
console.log(paid.response.status);
|
|
44
45
|
```
|
|
45
46
|
|
|
46
47
|
`createX402zClient` builds the confidential payment input automatically using the
|
|
@@ -50,10 +51,12 @@ console.log(response.status);
|
|
|
50
51
|
## API
|
|
51
52
|
|
|
52
53
|
- `createX402zClient(config)`
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
54
|
+
- `schemePreference` (required): ordered list of schemes to try (e.g. `["erc7984-mind-v1", "exact"]`)
|
|
55
|
+
- `signer` (required): signer used for both exact and confidential schemes
|
|
56
|
+
- `relayer` (required when using `erc7984-mind-v1`): relayer instance (browser)
|
|
55
57
|
- `fetch` (optional): custom fetch implementation
|
|
56
58
|
- `client.pay(url, options?)`: performs the 402 handshake and retries with payment headers
|
|
59
|
+
- returns `{ response, paymentRequired?, confidentialRequirements? }`
|
|
57
60
|
|
|
58
61
|
## Examples
|
|
59
62
|
|
|
@@ -63,7 +66,8 @@ See `examples/README.md` for the full-process web examples (`examples/full-proce
|
|
|
63
66
|
|
|
64
67
|
- Web builds use `@zama-fhe/relayer-sdk/web` (no CDN script tag or bundle).
|
|
65
68
|
- Requires an injected wallet that supports `eth_signTypedData_v4` (e.g. MetaMask).
|
|
66
|
-
-
|
|
69
|
+
- Confidential scheme name: `erc7984-mind-v1`
|
|
70
|
+
- Exact scheme name: `exact`
|
|
67
71
|
|
|
68
72
|
## API surface
|
|
69
73
|
|
|
@@ -75,6 +79,8 @@ Exports:
|
|
|
75
79
|
Types:
|
|
76
80
|
- `X402zClient`: client instance type.
|
|
77
81
|
- `X402zClientOptions`: options for `createX402zClient`.
|
|
82
|
+
- `X402zConfidentialClientOptions`: options for confidential payments.
|
|
83
|
+
- `X402zExactClientOptions`: options for exact payments.
|
|
78
84
|
- `PayOptions`: per-request payment options.
|
|
79
85
|
- `X402zClientSchemeOptions`: scheme config for client registration.
|
|
80
86
|
- `X402zClientRegistrationOptions`: registration options for the scheme.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
3
|
-
import { x402Client } from '@x402/core/client';
|
|
4
|
-
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network } from '@x402/core/types';
|
|
1
|
+
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
|
|
5
2
|
export * from '@x402/core/types';
|
|
3
|
+
import { SelectPaymentRequirements, PaymentPolicy, x402Client } from '@x402/core/client';
|
|
6
4
|
import { ClientEvmSigner } from '@x402/evm';
|
|
5
|
+
import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared-web';
|
|
6
|
+
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
7
7
|
|
|
8
8
|
type X402zClientSchemeOptions = {
|
|
9
9
|
signer: ClientEvmSigner;
|
|
@@ -24,23 +24,43 @@ declare class X402zEvmClientScheme implements SchemeNetworkClient {
|
|
|
24
24
|
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
type
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
31
|
-
|
|
32
|
-
type X402zClientOptions = Omit<X402zClientRegistrationOptions, "buildPayment"> & {
|
|
33
|
-
relayer: RelayerInstance;
|
|
27
|
+
type Scheme = "exact" | "erc7984-mind-v1";
|
|
28
|
+
type BaseClientOptions = {
|
|
34
29
|
fetch?: typeof fetch;
|
|
35
30
|
debug?: boolean;
|
|
36
31
|
};
|
|
32
|
+
type X402zClientOptions = {
|
|
33
|
+
schemePreference: Scheme[];
|
|
34
|
+
signer: ClientEvmSigner;
|
|
35
|
+
relayer?: RelayerInstance;
|
|
36
|
+
networks?: Network[];
|
|
37
|
+
paymentRequirementsSelector?: SelectPaymentRequirements;
|
|
38
|
+
policies?: PaymentPolicy[];
|
|
39
|
+
eip712?: X402zClientSchemeOptions["eip712"];
|
|
40
|
+
hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
|
|
41
|
+
clock?: X402zClientSchemeOptions["clock"];
|
|
42
|
+
onPaymentSelected?: (info: {
|
|
43
|
+
scheme: Scheme;
|
|
44
|
+
requirements: PaymentRequirements;
|
|
45
|
+
}) => void;
|
|
46
|
+
} & BaseClientOptions;
|
|
37
47
|
type PayOptions = {
|
|
38
48
|
headers?: Record<string, string>;
|
|
39
49
|
};
|
|
50
|
+
type PayResult = {
|
|
51
|
+
response: Response;
|
|
52
|
+
paymentRequired?: PaymentRequired;
|
|
53
|
+
confidentialRequirements?: PaymentRequirements;
|
|
54
|
+
};
|
|
40
55
|
type X402zClient = {
|
|
41
|
-
relayer
|
|
42
|
-
pay: (url: string, options?: PayOptions) => Promise<
|
|
56
|
+
relayer?: RelayerInstance;
|
|
57
|
+
pay: (url: string, options?: PayOptions) => Promise<PayResult>;
|
|
43
58
|
};
|
|
44
59
|
declare function createX402zClient(config: X402zClientOptions): X402zClient;
|
|
45
60
|
|
|
46
|
-
|
|
61
|
+
type X402zClientRegistrationOptions = X402zClientSchemeOptions & {
|
|
62
|
+
networks?: Network[];
|
|
63
|
+
};
|
|
64
|
+
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
65
|
+
|
|
66
|
+
export { type PayOptions, type PayResult, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, createX402zClient, registerX402zEvmClientScheme };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
3
|
-
import { x402Client } from '@x402/core/client';
|
|
4
|
-
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network } from '@x402/core/types';
|
|
1
|
+
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
|
|
5
2
|
export * from '@x402/core/types';
|
|
3
|
+
import { SelectPaymentRequirements, PaymentPolicy, x402Client } from '@x402/core/client';
|
|
6
4
|
import { ClientEvmSigner } from '@x402/evm';
|
|
5
|
+
import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared-web';
|
|
6
|
+
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
7
7
|
|
|
8
8
|
type X402zClientSchemeOptions = {
|
|
9
9
|
signer: ClientEvmSigner;
|
|
@@ -24,23 +24,43 @@ declare class X402zEvmClientScheme implements SchemeNetworkClient {
|
|
|
24
24
|
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
type
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
31
|
-
|
|
32
|
-
type X402zClientOptions = Omit<X402zClientRegistrationOptions, "buildPayment"> & {
|
|
33
|
-
relayer: RelayerInstance;
|
|
27
|
+
type Scheme = "exact" | "erc7984-mind-v1";
|
|
28
|
+
type BaseClientOptions = {
|
|
34
29
|
fetch?: typeof fetch;
|
|
35
30
|
debug?: boolean;
|
|
36
31
|
};
|
|
32
|
+
type X402zClientOptions = {
|
|
33
|
+
schemePreference: Scheme[];
|
|
34
|
+
signer: ClientEvmSigner;
|
|
35
|
+
relayer?: RelayerInstance;
|
|
36
|
+
networks?: Network[];
|
|
37
|
+
paymentRequirementsSelector?: SelectPaymentRequirements;
|
|
38
|
+
policies?: PaymentPolicy[];
|
|
39
|
+
eip712?: X402zClientSchemeOptions["eip712"];
|
|
40
|
+
hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
|
|
41
|
+
clock?: X402zClientSchemeOptions["clock"];
|
|
42
|
+
onPaymentSelected?: (info: {
|
|
43
|
+
scheme: Scheme;
|
|
44
|
+
requirements: PaymentRequirements;
|
|
45
|
+
}) => void;
|
|
46
|
+
} & BaseClientOptions;
|
|
37
47
|
type PayOptions = {
|
|
38
48
|
headers?: Record<string, string>;
|
|
39
49
|
};
|
|
50
|
+
type PayResult = {
|
|
51
|
+
response: Response;
|
|
52
|
+
paymentRequired?: PaymentRequired;
|
|
53
|
+
confidentialRequirements?: PaymentRequirements;
|
|
54
|
+
};
|
|
40
55
|
type X402zClient = {
|
|
41
|
-
relayer
|
|
42
|
-
pay: (url: string, options?: PayOptions) => Promise<
|
|
56
|
+
relayer?: RelayerInstance;
|
|
57
|
+
pay: (url: string, options?: PayOptions) => Promise<PayResult>;
|
|
43
58
|
};
|
|
44
59
|
declare function createX402zClient(config: X402zClientOptions): X402zClient;
|
|
45
60
|
|
|
46
|
-
|
|
61
|
+
type X402zClientRegistrationOptions = X402zClientSchemeOptions & {
|
|
62
|
+
networks?: Network[];
|
|
63
|
+
};
|
|
64
|
+
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
65
|
+
|
|
66
|
+
export { type PayOptions, type PayResult, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, createX402zClient, registerX402zEvmClientScheme };
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
29
|
// src/http/client.ts
|
|
30
30
|
var import_client = require("@x402/core/client");
|
|
31
31
|
var import_http = require("@x402/core/http");
|
|
32
|
+
var import_client2 = require("@x402/evm/exact/client");
|
|
32
33
|
var import_viem2 = require("viem");
|
|
33
34
|
var import_x402z_shared_web2 = require("x402z-shared-web");
|
|
34
35
|
|
|
@@ -128,16 +129,35 @@ function registerX402zEvmClientScheme(client, config) {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
// src/http/client.ts
|
|
131
|
-
function
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
function filterPaymentRequired(paymentRequired, scheme) {
|
|
133
|
+
const accepts = paymentRequired.accepts.filter((requirement) => requirement.scheme === scheme);
|
|
134
|
+
return { ...paymentRequired, accepts };
|
|
135
|
+
}
|
|
136
|
+
function createExactPayer(config, debugEnabled) {
|
|
137
|
+
const client = new import_client.x402Client(config.paymentRequirementsSelector);
|
|
138
|
+
(0, import_client2.registerExactEvmScheme)(client, {
|
|
139
|
+
signer: config.signer,
|
|
140
|
+
networks: config.networks,
|
|
141
|
+
policies: config.policies,
|
|
142
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector
|
|
143
|
+
});
|
|
144
|
+
const httpClient = new import_http.x402HTTPClient(client);
|
|
145
|
+
return {
|
|
146
|
+
async buildHeaders(paymentRequired) {
|
|
147
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
148
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
149
|
+
if (debugEnabled) {
|
|
150
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
151
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
152
|
+
}
|
|
153
|
+
return { headers: payHeaders };
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function createConfidentialPayer(config, debugEnabled) {
|
|
138
158
|
const buildPayment = async (requirements) => {
|
|
139
159
|
if (!(0, import_viem2.isAddress)(requirements.asset)) {
|
|
140
|
-
throw new Error(`Invalid
|
|
160
|
+
throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
|
|
141
161
|
}
|
|
142
162
|
const extra = requirements.extra;
|
|
143
163
|
const batcherAddress = extra?.confidential?.batcherAddress;
|
|
@@ -164,47 +184,127 @@ function createX402zClient(config) {
|
|
|
164
184
|
};
|
|
165
185
|
const client = new import_client.x402Client();
|
|
166
186
|
registerX402zEvmClientScheme(client, {
|
|
167
|
-
|
|
168
|
-
buildPayment
|
|
187
|
+
signer: config.signer,
|
|
188
|
+
buildPayment,
|
|
189
|
+
eip712: config.eip712,
|
|
190
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
191
|
+
clock: config.clock,
|
|
192
|
+
networks: config.networks
|
|
169
193
|
});
|
|
170
194
|
const httpClient = new import_http.x402HTTPClient(client);
|
|
195
|
+
const extractConfidentialRequirements = (paymentRequired) => {
|
|
196
|
+
const requirements = paymentRequired.accepts.find(
|
|
197
|
+
(requirement) => requirement.scheme === "erc7984-mind-v1"
|
|
198
|
+
);
|
|
199
|
+
if (!requirements) {
|
|
200
|
+
throw new Error("Missing erc7984-mind-v1 payment requirements");
|
|
201
|
+
}
|
|
202
|
+
return requirements;
|
|
203
|
+
};
|
|
171
204
|
return {
|
|
172
205
|
relayer: config.relayer,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (initial.status !== 402) {
|
|
176
|
-
return initial;
|
|
177
|
-
}
|
|
178
|
-
const paymentRequired = httpClient.getPaymentRequiredResponse(
|
|
179
|
-
(name) => initial.headers.get(name),
|
|
180
|
-
await initial.json().catch(() => ({}))
|
|
181
|
-
);
|
|
206
|
+
extractConfidentialRequirements,
|
|
207
|
+
async buildHeaders(paymentRequired) {
|
|
182
208
|
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
183
209
|
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
184
210
|
if (debugEnabled) {
|
|
185
211
|
console.debug("[x402z-client] payment payload", payload);
|
|
186
212
|
console.debug("[x402z-client] payment headers", payHeaders);
|
|
187
213
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
214
|
+
return { headers: payHeaders };
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function createX402zClient(config) {
|
|
219
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
220
|
+
const debugEnabled = config.debug ?? false;
|
|
221
|
+
if (!fetchFn) {
|
|
222
|
+
throw new Error("fetch is not available; provide a fetch implementation");
|
|
223
|
+
}
|
|
224
|
+
const schemePreference = config.schemePreference?.length ? config.schemePreference : ["exact"];
|
|
225
|
+
const exactPayer = createExactPayer(
|
|
226
|
+
{
|
|
227
|
+
signer: config.signer,
|
|
228
|
+
networks: config.networks,
|
|
229
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector,
|
|
230
|
+
policies: config.policies
|
|
231
|
+
},
|
|
232
|
+
debugEnabled
|
|
233
|
+
);
|
|
234
|
+
const confidentialPayer = config.relayer ? createConfidentialPayer(
|
|
235
|
+
{
|
|
236
|
+
signer: config.signer,
|
|
237
|
+
relayer: config.relayer,
|
|
238
|
+
eip712: config.eip712,
|
|
239
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
240
|
+
clock: config.clock,
|
|
241
|
+
networks: config.networks
|
|
242
|
+
},
|
|
243
|
+
debugEnabled
|
|
244
|
+
) : void 0;
|
|
245
|
+
const parserClient = new import_client.x402Client();
|
|
246
|
+
const parserHttp = new import_http.x402HTTPClient(parserClient);
|
|
247
|
+
const pay = async (url, options) => {
|
|
248
|
+
const initial = await fetchFn(url, { headers: options?.headers });
|
|
249
|
+
if (initial.status !== 402) {
|
|
250
|
+
return { response: initial };
|
|
251
|
+
}
|
|
252
|
+
const paymentRequired = parserHttp.getPaymentRequiredResponse(
|
|
253
|
+
(name) => initial.headers.get(name),
|
|
254
|
+
await initial.json().catch(() => ({}))
|
|
255
|
+
);
|
|
256
|
+
const supportedSchemes = new Set(
|
|
257
|
+
paymentRequired.accepts.map((accept) => accept.scheme)
|
|
258
|
+
);
|
|
259
|
+
const selectedScheme = schemePreference.find(
|
|
260
|
+
(scheme) => supportedSchemes.has(scheme)
|
|
261
|
+
);
|
|
262
|
+
if (!selectedScheme) {
|
|
263
|
+
throw new Error("No preferred schemes are supported by payment requirements");
|
|
264
|
+
}
|
|
265
|
+
const filteredRequirements = filterPaymentRequired(paymentRequired, selectedScheme);
|
|
266
|
+
if (filteredRequirements.accepts.length === 0) {
|
|
267
|
+
throw new Error(`Missing ${selectedScheme} payment requirements`);
|
|
268
|
+
}
|
|
269
|
+
let headersResult;
|
|
270
|
+
let confidentialRequirements;
|
|
271
|
+
const selectedRequirements = filteredRequirements.accepts[0];
|
|
272
|
+
config.onPaymentSelected?.({
|
|
273
|
+
scheme: selectedScheme,
|
|
274
|
+
requirements: selectedRequirements
|
|
275
|
+
});
|
|
276
|
+
if (selectedScheme === "exact") {
|
|
277
|
+
headersResult = await exactPayer.buildHeaders(filteredRequirements);
|
|
278
|
+
} else {
|
|
279
|
+
if (!confidentialPayer) {
|
|
280
|
+
throw new Error("Confidential scheme is selected but relayer is missing");
|
|
205
281
|
}
|
|
206
|
-
|
|
282
|
+
confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
|
|
283
|
+
headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
|
|
207
284
|
}
|
|
285
|
+
const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
|
|
286
|
+
const paidResponse = await fetchFn(url, { headers: mergedHeaders });
|
|
287
|
+
if (debugEnabled) {
|
|
288
|
+
try {
|
|
289
|
+
const body = await paidResponse.clone().text();
|
|
290
|
+
console.debug("[x402z-client] response", {
|
|
291
|
+
status: paidResponse.status,
|
|
292
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
293
|
+
body
|
|
294
|
+
});
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.debug("[x402z-client] response", {
|
|
297
|
+
status: paidResponse.status,
|
|
298
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
299
|
+
body: "<unavailable>"
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return { response: paidResponse, paymentRequired, confidentialRequirements };
|
|
304
|
+
};
|
|
305
|
+
return {
|
|
306
|
+
relayer: confidentialPayer?.relayer,
|
|
307
|
+
pay
|
|
208
308
|
};
|
|
209
309
|
}
|
|
210
310
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/http/client.ts
|
|
2
2
|
import { x402Client } from "@x402/core/client";
|
|
3
3
|
import { x402HTTPClient } from "@x402/core/http";
|
|
4
|
+
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
4
5
|
import { isAddress } from "viem";
|
|
5
6
|
import {
|
|
6
7
|
createEncryptedAmountInput
|
|
@@ -107,16 +108,35 @@ function registerX402zEvmClientScheme(client, config) {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
// src/http/client.ts
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
function filterPaymentRequired(paymentRequired, scheme) {
|
|
112
|
+
const accepts = paymentRequired.accepts.filter((requirement) => requirement.scheme === scheme);
|
|
113
|
+
return { ...paymentRequired, accepts };
|
|
114
|
+
}
|
|
115
|
+
function createExactPayer(config, debugEnabled) {
|
|
116
|
+
const client = new x402Client(config.paymentRequirementsSelector);
|
|
117
|
+
registerExactEvmScheme(client, {
|
|
118
|
+
signer: config.signer,
|
|
119
|
+
networks: config.networks,
|
|
120
|
+
policies: config.policies,
|
|
121
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector
|
|
122
|
+
});
|
|
123
|
+
const httpClient = new x402HTTPClient(client);
|
|
124
|
+
return {
|
|
125
|
+
async buildHeaders(paymentRequired) {
|
|
126
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
127
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
128
|
+
if (debugEnabled) {
|
|
129
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
130
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
131
|
+
}
|
|
132
|
+
return { headers: payHeaders };
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function createConfidentialPayer(config, debugEnabled) {
|
|
117
137
|
const buildPayment = async (requirements) => {
|
|
118
138
|
if (!isAddress(requirements.asset)) {
|
|
119
|
-
throw new Error(`Invalid
|
|
139
|
+
throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
|
|
120
140
|
}
|
|
121
141
|
const extra = requirements.extra;
|
|
122
142
|
const batcherAddress = extra?.confidential?.batcherAddress;
|
|
@@ -143,47 +163,127 @@ function createX402zClient(config) {
|
|
|
143
163
|
};
|
|
144
164
|
const client = new x402Client();
|
|
145
165
|
registerX402zEvmClientScheme(client, {
|
|
146
|
-
|
|
147
|
-
buildPayment
|
|
166
|
+
signer: config.signer,
|
|
167
|
+
buildPayment,
|
|
168
|
+
eip712: config.eip712,
|
|
169
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
170
|
+
clock: config.clock,
|
|
171
|
+
networks: config.networks
|
|
148
172
|
});
|
|
149
173
|
const httpClient = new x402HTTPClient(client);
|
|
174
|
+
const extractConfidentialRequirements = (paymentRequired) => {
|
|
175
|
+
const requirements = paymentRequired.accepts.find(
|
|
176
|
+
(requirement) => requirement.scheme === "erc7984-mind-v1"
|
|
177
|
+
);
|
|
178
|
+
if (!requirements) {
|
|
179
|
+
throw new Error("Missing erc7984-mind-v1 payment requirements");
|
|
180
|
+
}
|
|
181
|
+
return requirements;
|
|
182
|
+
};
|
|
150
183
|
return {
|
|
151
184
|
relayer: config.relayer,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (initial.status !== 402) {
|
|
155
|
-
return initial;
|
|
156
|
-
}
|
|
157
|
-
const paymentRequired = httpClient.getPaymentRequiredResponse(
|
|
158
|
-
(name) => initial.headers.get(name),
|
|
159
|
-
await initial.json().catch(() => ({}))
|
|
160
|
-
);
|
|
185
|
+
extractConfidentialRequirements,
|
|
186
|
+
async buildHeaders(paymentRequired) {
|
|
161
187
|
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
162
188
|
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
163
189
|
if (debugEnabled) {
|
|
164
190
|
console.debug("[x402z-client] payment payload", payload);
|
|
165
191
|
console.debug("[x402z-client] payment headers", payHeaders);
|
|
166
192
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
return { headers: payHeaders };
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function createX402zClient(config) {
|
|
198
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
199
|
+
const debugEnabled = config.debug ?? false;
|
|
200
|
+
if (!fetchFn) {
|
|
201
|
+
throw new Error("fetch is not available; provide a fetch implementation");
|
|
202
|
+
}
|
|
203
|
+
const schemePreference = config.schemePreference?.length ? config.schemePreference : ["exact"];
|
|
204
|
+
const exactPayer = createExactPayer(
|
|
205
|
+
{
|
|
206
|
+
signer: config.signer,
|
|
207
|
+
networks: config.networks,
|
|
208
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector,
|
|
209
|
+
policies: config.policies
|
|
210
|
+
},
|
|
211
|
+
debugEnabled
|
|
212
|
+
);
|
|
213
|
+
const confidentialPayer = config.relayer ? createConfidentialPayer(
|
|
214
|
+
{
|
|
215
|
+
signer: config.signer,
|
|
216
|
+
relayer: config.relayer,
|
|
217
|
+
eip712: config.eip712,
|
|
218
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
219
|
+
clock: config.clock,
|
|
220
|
+
networks: config.networks
|
|
221
|
+
},
|
|
222
|
+
debugEnabled
|
|
223
|
+
) : void 0;
|
|
224
|
+
const parserClient = new x402Client();
|
|
225
|
+
const parserHttp = new x402HTTPClient(parserClient);
|
|
226
|
+
const pay = async (url, options) => {
|
|
227
|
+
const initial = await fetchFn(url, { headers: options?.headers });
|
|
228
|
+
if (initial.status !== 402) {
|
|
229
|
+
return { response: initial };
|
|
230
|
+
}
|
|
231
|
+
const paymentRequired = parserHttp.getPaymentRequiredResponse(
|
|
232
|
+
(name) => initial.headers.get(name),
|
|
233
|
+
await initial.json().catch(() => ({}))
|
|
234
|
+
);
|
|
235
|
+
const supportedSchemes = new Set(
|
|
236
|
+
paymentRequired.accepts.map((accept) => accept.scheme)
|
|
237
|
+
);
|
|
238
|
+
const selectedScheme = schemePreference.find(
|
|
239
|
+
(scheme) => supportedSchemes.has(scheme)
|
|
240
|
+
);
|
|
241
|
+
if (!selectedScheme) {
|
|
242
|
+
throw new Error("No preferred schemes are supported by payment requirements");
|
|
243
|
+
}
|
|
244
|
+
const filteredRequirements = filterPaymentRequired(paymentRequired, selectedScheme);
|
|
245
|
+
if (filteredRequirements.accepts.length === 0) {
|
|
246
|
+
throw new Error(`Missing ${selectedScheme} payment requirements`);
|
|
247
|
+
}
|
|
248
|
+
let headersResult;
|
|
249
|
+
let confidentialRequirements;
|
|
250
|
+
const selectedRequirements = filteredRequirements.accepts[0];
|
|
251
|
+
config.onPaymentSelected?.({
|
|
252
|
+
scheme: selectedScheme,
|
|
253
|
+
requirements: selectedRequirements
|
|
254
|
+
});
|
|
255
|
+
if (selectedScheme === "exact") {
|
|
256
|
+
headersResult = await exactPayer.buildHeaders(filteredRequirements);
|
|
257
|
+
} else {
|
|
258
|
+
if (!confidentialPayer) {
|
|
259
|
+
throw new Error("Confidential scheme is selected but relayer is missing");
|
|
184
260
|
}
|
|
185
|
-
|
|
261
|
+
confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
|
|
262
|
+
headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
|
|
186
263
|
}
|
|
264
|
+
const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
|
|
265
|
+
const paidResponse = await fetchFn(url, { headers: mergedHeaders });
|
|
266
|
+
if (debugEnabled) {
|
|
267
|
+
try {
|
|
268
|
+
const body = await paidResponse.clone().text();
|
|
269
|
+
console.debug("[x402z-client] response", {
|
|
270
|
+
status: paidResponse.status,
|
|
271
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
272
|
+
body
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.debug("[x402z-client] response", {
|
|
276
|
+
status: paidResponse.status,
|
|
277
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
278
|
+
body: "<unavailable>"
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return { response: paidResponse, paymentRequired, confidentialRequirements };
|
|
283
|
+
};
|
|
284
|
+
return {
|
|
285
|
+
relayer: confidentialPayer?.relayer,
|
|
286
|
+
pay
|
|
187
287
|
};
|
|
188
288
|
}
|
|
189
289
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402z-client-web",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@x402/core": "^2.0.0",
|
|
19
19
|
"@x402/evm": "^2.0.0",
|
|
20
20
|
"viem": "^2.39.3",
|
|
21
|
-
"x402z-shared-web": "0.0
|
|
21
|
+
"x402z-shared-web": "0.1.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"jest": "^29.7.0",
|