@x402/extensions 0.0.1 → 2.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 CHANGED
@@ -1 +1,483 @@
1
1
  # @x402/extensions
2
+
3
+ x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality like resource discovery and cataloging.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install @x402/extensions
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ Extensions are optional features that can be added to x402 payment flows. They allow servers to provide additional metadata and enable facilitators to offer enhanced services like resource discovery and cataloging.
14
+
15
+ Currently, this package includes:
16
+ - **Bazaar Discovery Extension**: Enables automatic cataloging and indexing of x402-enabled resources
17
+
18
+ ## Bazaar Discovery Extension
19
+
20
+ The Bazaar Discovery Extension enables facilitators to automatically catalog and index x402-enabled resources by following server-declared discovery instructions. This allows users to discover paid APIs and services through facilitator catalogs.
21
+
22
+ ### How It Works
23
+
24
+ 1. **Servers** declare discovery metadata when configuring their payment endpoints
25
+ 2. The HTTP method is automatically inferred from the route definition (e.g., `"GET /weather"`)
26
+ 3. **Facilitators** extract this metadata from payment requests
27
+ 4. **Users** can browse and discover available paid resources through facilitator catalogs
28
+
29
+ ### For Resource Servers
30
+
31
+ Declare endpoint discovery metadata in your payment middleware configuration. This helps facilitators understand how to call your endpoints and what they return.
32
+
33
+ > **Note:** The HTTP method is automatically inferred from the route key (e.g., `"GET /weather"` → GET method). You don't need to specify it in `declareDiscoveryExtension`.
34
+
35
+ #### Basic Example: GET Endpoint with Query Parameters
36
+
37
+ ```typescript
38
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
39
+
40
+ const resources = {
41
+ "GET /weather": {
42
+ accepts: {
43
+ scheme: "exact",
44
+ price: "$0.001",
45
+ network: "eip155:84532",
46
+ payTo: "0xYourAddress"
47
+ },
48
+ extensions: {
49
+ ...declareDiscoveryExtension({
50
+ input: { city: "San Francisco" },
51
+ inputSchema: {
52
+ properties: {
53
+ city: { type: "string" },
54
+ units: { type: "string", enum: ["celsius", "fahrenheit"] }
55
+ },
56
+ required: ["city"]
57
+ },
58
+ output: {
59
+ example: {
60
+ city: "San Francisco",
61
+ weather: "foggy",
62
+ temperature: 15,
63
+ humidity: 85
64
+ }
65
+ },
66
+ }),
67
+ },
68
+ },
69
+ };
70
+ ```
71
+
72
+ #### Example: POST Endpoint with JSON Body
73
+
74
+ For POST, PUT, and PATCH endpoints, specify `bodyType` to indicate the request body format:
75
+
76
+ ```typescript
77
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
78
+
79
+ const resources = {
80
+ "POST /api/translate": {
81
+ accepts: {
82
+ scheme: "exact",
83
+ price: "$0.01",
84
+ network: "eip155:84532",
85
+ payTo: "0xYourAddress"
86
+ },
87
+ extensions: {
88
+ ...declareDiscoveryExtension({
89
+ input: {
90
+ text: "Hello, world!",
91
+ targetLanguage: "es"
92
+ },
93
+ inputSchema: {
94
+ properties: {
95
+ text: { type: "string" },
96
+ targetLanguage: { type: "string", pattern: "^[a-z]{2}$" }
97
+ },
98
+ required: ["text", "targetLanguage"]
99
+ },
100
+ bodyType: "json",
101
+ output: {
102
+ example: {
103
+ translatedText: "¡Hola, mundo!",
104
+ sourceLanguage: "en",
105
+ targetLanguage: "es"
106
+ }
107
+ },
108
+ }),
109
+ },
110
+ },
111
+ };
112
+ ```
113
+
114
+ #### Example: PUT Endpoint with Form Data
115
+
116
+ ```typescript
117
+ const resources = {
118
+ "PUT /api/user/profile": {
119
+ accepts: {
120
+ scheme: "exact",
121
+ price: "$0.05",
122
+ network: "eip155:84532",
123
+ payTo: "0xYourAddress"
124
+ },
125
+ extensions: {
126
+ ...declareDiscoveryExtension({
127
+ input: {
128
+ name: "John Doe",
129
+ email: "john@example.com",
130
+ bio: "Software developer"
131
+ },
132
+ inputSchema: {
133
+ properties: {
134
+ name: { type: "string", minLength: 1 },
135
+ email: { type: "string", format: "email" },
136
+ bio: { type: "string", maxLength: 500 }
137
+ },
138
+ required: ["name", "email"]
139
+ },
140
+ bodyType: "form-data",
141
+ output: {
142
+ example: {
143
+ success: true,
144
+ userId: "123",
145
+ updatedAt: "2024-01-01T00:00:00Z"
146
+ }
147
+ },
148
+ }),
149
+ },
150
+ },
151
+ };
152
+ ```
153
+
154
+ #### Example: DELETE Endpoint
155
+
156
+ ```typescript
157
+ const resources = {
158
+ "DELETE /api/data/:id": {
159
+ accepts: {
160
+ scheme: "exact",
161
+ price: "$0.001",
162
+ network: "eip155:84532",
163
+ payTo: "0xYourAddress"
164
+ },
165
+ extensions: {
166
+ ...declareDiscoveryExtension({
167
+ input: { id: "123" },
168
+ inputSchema: {
169
+ properties: {
170
+ id: { type: "string" }
171
+ },
172
+ required: ["id"]
173
+ },
174
+ output: {
175
+ example: {
176
+ success: true,
177
+ deletedId: "123"
178
+ }
179
+ },
180
+ }),
181
+ },
182
+ },
183
+ };
184
+ ```
185
+
186
+ #### Using with Next.js Middleware
187
+
188
+ ```typescript
189
+ import { paymentProxy, x402ResourceServer } from "@x402/next";
190
+ import { HTTPFacilitatorClient } from "@x402/core/http";
191
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
192
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
193
+
194
+ const facilitatorClient = new HTTPFacilitatorClient({ url: "https://facilitator.x402.org" });
195
+ const resourceServer = new x402ResourceServer(facilitatorClient)
196
+ .register("eip155:84532", new ExactEvmScheme());
197
+
198
+ export const proxy = paymentProxy(
199
+ {
200
+ "/api/weather": {
201
+ accepts: {
202
+ scheme: "exact",
203
+ price: "$0.001",
204
+ network: "eip155:84532",
205
+ payTo: "0xYourAddress",
206
+ },
207
+ extensions: {
208
+ ...declareDiscoveryExtension({
209
+ input: { city: "San Francisco" },
210
+ inputSchema: {
211
+ properties: { city: { type: "string" } },
212
+ required: ["city"],
213
+ },
214
+ output: {
215
+ example: { city: "San Francisco", weather: "foggy" }
216
+ },
217
+ }),
218
+ },
219
+ },
220
+ },
221
+ resourceServer,
222
+ );
223
+ ```
224
+
225
+ ### For Facilitators
226
+
227
+ Extract discovery information from incoming payment requests to catalog resources in the Bazaar.
228
+
229
+ #### Basic Usage
230
+
231
+ ```typescript
232
+ import { extractDiscoveryInfo } from "@x402/extensions/bazaar";
233
+ import type { PaymentPayload, PaymentRequirements } from "@x402/core/types";
234
+
235
+ async function handlePayment(
236
+ paymentPayload: PaymentPayload,
237
+ paymentRequirements: PaymentRequirements
238
+ ) {
239
+ // Extract discovery info from the payment
240
+ const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements);
241
+
242
+ if (discovered) {
243
+ // discovered contains:
244
+ // {
245
+ // resourceUrl: "https://api.example.com/weather",
246
+ // method: "GET",
247
+ // x402Version: 2,
248
+ // discoveryInfo: {
249
+ // input: { type: "http", method: "GET", queryParams: { city: "..." } },
250
+ // output: { type: "json", example: { ... } }
251
+ // }
252
+ // }
253
+
254
+ // Catalog the resource in your Bazaar
255
+ await catalogResource({
256
+ url: discovered.resourceUrl,
257
+ method: discovered.method,
258
+ inputSchema: discovered.discoveryInfo.input,
259
+ outputExample: discovered.discoveryInfo.output?.example,
260
+ });
261
+ }
262
+ }
263
+ ```
264
+
265
+ #### Validating Discovery Extensions
266
+
267
+ ```typescript
268
+ import { validateDiscoveryExtension, extractDiscoveryInfo } from "@x402/extensions/bazaar";
269
+
270
+ function processPayment(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements) {
271
+ const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements);
272
+
273
+ if (discovered && paymentPayload.extensions?.bazaar) {
274
+ // Validate the extension schema
275
+ const validation = validateDiscoveryExtension(paymentPayload.extensions.bazaar);
276
+
277
+ if (!validation.valid) {
278
+ console.warn("Invalid discovery extension:", validation.errors);
279
+ // Handle invalid extension (log, reject, etc.)
280
+ return;
281
+ }
282
+
283
+ // Extension is valid, proceed with cataloging
284
+ catalogResource(discovered);
285
+ }
286
+ }
287
+ ```
288
+
289
+ #### Using with Server Extension Helper
290
+
291
+ The `bazaarResourceServerExtension` automatically enriches discovery extensions with HTTP method information from the request context:
292
+
293
+ ```typescript
294
+ import { bazaarResourceServerExtension } from "@x402/extensions/bazaar";
295
+ import { x402ResourceServer } from "@x402/core/server";
296
+
297
+ // The extension helper automatically extracts discovery info
298
+ const resourceServer = new x402ResourceServer(facilitatorClient)
299
+ .register("eip155:84532", new ExactEvmScheme())
300
+ .useExtension(bazaarResourceServerExtension);
301
+ ```
302
+
303
+ ## API Reference
304
+
305
+ ### `declareDiscoveryExtension(config)`
306
+
307
+ Creates a discovery extension object for resource servers.
308
+
309
+ **Parameters:**
310
+ - `config.input` (optional): Example input values (query params for GET/HEAD/DELETE, body for POST/PUT/PATCH)
311
+ - `config.inputSchema` (optional): JSON Schema for input validation
312
+ - `config.bodyType` (optional): For POST/PUT/PATCH, specify `"json"`, `"form-data"`, or `"text"` (default: `"json"`)
313
+ - `config.output` (optional): Output specification
314
+ - `output.example`: Example output data
315
+ - `output.schema`: JSON Schema for output validation
316
+
317
+ > **Note:** The HTTP method is NOT passed to this function. It is automatically inferred from the route key (e.g., `"GET /weather"`) or enriched by `bazaarResourceServerExtension` at runtime.
318
+
319
+ **Returns:** An object with a `bazaar` key containing the discovery extension.
320
+
321
+ **Example:**
322
+ ```typescript
323
+ const extension = declareDiscoveryExtension({
324
+ input: { query: "search term" },
325
+ inputSchema: {
326
+ properties: { query: { type: "string" } },
327
+ required: ["query"]
328
+ },
329
+ output: {
330
+ example: { results: [] }
331
+ }
332
+ });
333
+ // Returns: { bazaar: { info: {...}, schema: {...} } }
334
+ ```
335
+
336
+ ### `extractDiscoveryInfo(paymentPayload, paymentRequirements, validate?)`
337
+
338
+ Extracts discovery information from a payment request (for facilitators).
339
+
340
+ **Parameters:**
341
+ - `paymentPayload`: The payment payload from the client
342
+ - `paymentRequirements`: The payment requirements from the server
343
+ - `validate` (optional): Whether to validate the extension (default: `true`)
344
+
345
+ **Returns:** `DiscoveredResource` object or `null` if not found.
346
+
347
+ ```typescript
348
+ interface DiscoveredResource {
349
+ resourceUrl: string;
350
+ method: string;
351
+ x402Version: number;
352
+ discoveryInfo: DiscoveryInfo;
353
+ }
354
+ ```
355
+
356
+ **Example:**
357
+ ```typescript
358
+ const info = extractDiscoveryInfo(paymentPayload, paymentRequirements);
359
+ if (info) {
360
+ console.log(info.resourceUrl); // "https://api.example.com/endpoint"
361
+ console.log(info.method); // "GET"
362
+ console.log(info.discoveryInfo); // { input: {...}, output: {...} }
363
+ }
364
+ ```
365
+
366
+ ### `validateDiscoveryExtension(extension)`
367
+
368
+ Validates a discovery extension's info against its schema.
369
+
370
+ **Parameters:**
371
+ - `extension`: A discovery extension object
372
+
373
+ **Returns:** `{ valid: boolean, errors?: string[] }`
374
+
375
+ **Example:**
376
+ ```typescript
377
+ const result = validateDiscoveryExtension(extension);
378
+ if (!result.valid) {
379
+ console.error("Validation errors:", result.errors);
380
+ }
381
+ ```
382
+
383
+ ### `validateAndExtract(extension)`
384
+
385
+ Validates and extracts discovery info in one step.
386
+
387
+ **Parameters:**
388
+ - `extension`: A discovery extension object
389
+
390
+ **Returns:** `{ valid: boolean, info?: DiscoveryInfo, errors?: string[] }`
391
+
392
+ **Example:**
393
+ ```typescript
394
+ const { valid, info, errors } = validateAndExtract(extension);
395
+ if (valid && info) {
396
+ // Use info
397
+ }
398
+ ```
399
+
400
+ ### `bazaarResourceServerExtension`
401
+
402
+ A server extension that automatically enriches discovery extensions with HTTP method information from the request context.
403
+
404
+ **Usage:**
405
+ ```typescript
406
+ import { bazaarResourceServerExtension } from "@x402/extensions/bazaar";
407
+
408
+ const resourceServer = new x402ResourceServer(facilitatorClient)
409
+ .useExtension(bazaarResourceServerExtension);
410
+ ```
411
+
412
+ ### `BAZAAR`
413
+
414
+ The extension identifier constant (`"bazaar"`).
415
+
416
+ ```typescript
417
+ import { BAZAAR } from "@x402/extensions/bazaar";
418
+ // BAZAAR === "bazaar"
419
+ ```
420
+
421
+ ## Use Cases
422
+
423
+ ### 1. API Marketplace Discovery
424
+ Enable users to discover paid APIs through facilitator catalogs. Servers declare their endpoints, and facilitators index them for easy discovery.
425
+
426
+ ### 2. Developer Tools
427
+ Build tools that automatically generate API documentation or client SDKs from discovery metadata.
428
+
429
+ ### 3. Resource Cataloging
430
+ Facilitators can maintain catalogs of available paid resources, making it easier for users to find services.
431
+
432
+ ### 4. Testing and Validation
433
+ Use discovery schemas to validate API requests and responses during development.
434
+
435
+ ## Troubleshooting
436
+
437
+ ### Extension Not Being Extracted
438
+
439
+ **Problem:** `extractDiscoveryInfo` returns `null`.
440
+
441
+ **Solutions:**
442
+ - Ensure the server has declared the extension using `declareDiscoveryExtension`
443
+ - Check that `paymentPayload.extensions.bazaar` exists
444
+ - Verify you're using x402 v2 (v1 uses a different format in `outputSchema`)
445
+
446
+ ### Schema Validation Fails
447
+
448
+ **Problem:** `validateDiscoveryExtension` returns `valid: false`.
449
+
450
+ **Solutions:**
451
+ - Ensure `inputSchema` matches the structure of `input`
452
+ - Check that required fields are marked in `inputSchema.required`
453
+ - Verify JSON Schema syntax is correct
454
+
455
+ ### Missing Discovery Info
456
+
457
+ **Problem:** Discovery info is incomplete.
458
+
459
+ **Solutions:**
460
+ - Ensure both `input` and `inputSchema` are provided
461
+ - For POST/PUT/PATCH, include `bodyType` in the config
462
+ - Check that `output.example` is provided if you want output documentation
463
+
464
+ ### Method Not Being Detected
465
+
466
+ **Problem:** The HTTP method is missing from discovery info.
467
+
468
+ **Solutions:**
469
+ - Use `bazaarResourceServerExtension` which automatically injects the method
470
+ - Ensure the route key follows the format `"METHOD /path"` (e.g., `"GET /weather"`)
471
+
472
+ ## Related Resources
473
+
474
+ - [x402 Core Package](../core/README.md) - Core x402 protocol implementation
475
+ - [x402 Specification](../../../specs/x402-specification.md) - Full protocol specification
476
+
477
+ ## Version Support
478
+
479
+ This package supports both x402 v1 and v2:
480
+ - **v2**: Extensions are in `PaymentPayload.extensions` and `PaymentRequired.extensions`
481
+ - **v1**: Discovery info is in `PaymentRequirements.outputSchema` (automatically converted)
482
+
483
+ The `extractDiscoveryInfo` function automatically handles both versions.