@x402/hono 0.0.1 → 2.0.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 +254 -0
- package/dist/cjs/index.d.ts +145 -0
- package/dist/cjs/index.js +256 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +145 -0
- package/dist/esm/index.mjs +219 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +66 -8
- package/index.js +0 -3
package/README.md
CHANGED
|
@@ -1 +1,255 @@
|
|
|
1
1
|
# @x402/hono
|
|
2
|
+
|
|
3
|
+
Hono middleware integration for the x402 Payment Protocol. This package provides a simple middleware function for adding x402 payment requirements to your Hono applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install @x402/hono
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Hono } from "hono";
|
|
15
|
+
import { serve } from "@hono/node-server";
|
|
16
|
+
import { paymentMiddleware, x402ResourceServer } from "@x402/hono";
|
|
17
|
+
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
18
|
+
import { HTTPFacilitatorClient } from "@x402/core/server";
|
|
19
|
+
|
|
20
|
+
const app = new Hono();
|
|
21
|
+
|
|
22
|
+
const facilitatorClient = new HTTPFacilitatorClient({ url: "https://facilitator.x402.org" });
|
|
23
|
+
const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
24
|
+
.register("eip155:84532", new ExactEvmScheme());
|
|
25
|
+
|
|
26
|
+
// Apply the payment middleware with your configuration
|
|
27
|
+
app.use(
|
|
28
|
+
paymentMiddleware(
|
|
29
|
+
{
|
|
30
|
+
"GET /protected-route": {
|
|
31
|
+
accepts: {
|
|
32
|
+
scheme: "exact",
|
|
33
|
+
price: "$0.10",
|
|
34
|
+
network: "eip155:84532",
|
|
35
|
+
payTo: "0xYourAddress",
|
|
36
|
+
},
|
|
37
|
+
description: "Access to premium content",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
resourceServer,
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Implement your protected route
|
|
45
|
+
app.get("/protected-route", (c) => {
|
|
46
|
+
return c.json({ message: "This content is behind a paywall" });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
serve({ fetch: app.fetch, port: 3000 });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
The `paymentMiddleware` function accepts the following parameters:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
paymentMiddleware(
|
|
58
|
+
routes: RoutesConfig,
|
|
59
|
+
server: x402ResourceServer,
|
|
60
|
+
paywallConfig?: PaywallConfig,
|
|
61
|
+
paywall?: PaywallProvider,
|
|
62
|
+
syncFacilitatorOnStart?: boolean
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Parameters
|
|
67
|
+
|
|
68
|
+
1. **`routes`** (required): Route configurations for protected endpoints
|
|
69
|
+
2. **`server`** (required): Pre-configured x402ResourceServer instance
|
|
70
|
+
3. **`paywallConfig`** (optional): Configuration for the built-in paywall UI
|
|
71
|
+
4. **`paywall`** (optional): Custom paywall provider
|
|
72
|
+
5. **`syncFacilitatorOnStart`** (optional): Whether to sync with facilitator on startup (defaults to true)
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### HonoAdapter
|
|
77
|
+
|
|
78
|
+
The `HonoAdapter` class implements the `HTTPAdapter` interface from `@x402/core`, providing Hono-specific request handling:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
class HonoAdapter implements HTTPAdapter {
|
|
82
|
+
getHeader(name: string): string | undefined;
|
|
83
|
+
getMethod(): string;
|
|
84
|
+
getPath(): string;
|
|
85
|
+
getUrl(): string;
|
|
86
|
+
getAcceptHeader(): string;
|
|
87
|
+
getUserAgent(): string;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Middleware Function
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
function paymentMiddleware(
|
|
95
|
+
routes: RoutesConfig,
|
|
96
|
+
server: x402ResourceServer,
|
|
97
|
+
paywallConfig?: PaywallConfig,
|
|
98
|
+
paywall?: PaywallProvider,
|
|
99
|
+
syncFacilitatorOnStart?: boolean,
|
|
100
|
+
): MiddlewareHandler;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Creates Hono middleware that:
|
|
104
|
+
|
|
105
|
+
1. Uses the provided x402ResourceServer for payment processing
|
|
106
|
+
2. Checks if the incoming request matches a protected route
|
|
107
|
+
3. Validates payment headers if required
|
|
108
|
+
4. Returns payment instructions (402 status) if payment is missing or invalid
|
|
109
|
+
5. Processes the request if payment is valid
|
|
110
|
+
6. Handles settlement after successful response
|
|
111
|
+
|
|
112
|
+
### Route Configuration
|
|
113
|
+
|
|
114
|
+
Routes are passed as the first parameter to `paymentMiddleware`:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const routes: RoutesConfig = {
|
|
118
|
+
"GET /api/protected": {
|
|
119
|
+
accepts: {
|
|
120
|
+
scheme: "exact",
|
|
121
|
+
price: "$0.10",
|
|
122
|
+
network: "eip155:84532",
|
|
123
|
+
payTo: "0xYourAddress",
|
|
124
|
+
maxTimeoutSeconds: 60,
|
|
125
|
+
},
|
|
126
|
+
description: "Premium API access",
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
app.use(paymentMiddleware(routes, resourceServer));
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Paywall Configuration
|
|
134
|
+
|
|
135
|
+
The middleware automatically displays a paywall UI when browsers request protected endpoints.
|
|
136
|
+
|
|
137
|
+
**Option 1: Full Paywall UI (Recommended)**
|
|
138
|
+
|
|
139
|
+
Install the optional `@x402/paywall` package for a complete wallet connection and payment UI:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pnpm add @x402/paywall
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Then configure it:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const paywallConfig: PaywallConfig = {
|
|
149
|
+
appName: "Your App Name",
|
|
150
|
+
appLogo: "/path/to/logo.svg",
|
|
151
|
+
testnet: true,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
app.use(paymentMiddleware(routes, resourceServer, paywallConfig));
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Option 2: Basic Paywall (No Installation)**
|
|
158
|
+
|
|
159
|
+
Without `@x402/paywall` installed, the middleware returns a basic HTML page with payment instructions.
|
|
160
|
+
|
|
161
|
+
**Option 3: Custom Paywall Provider**
|
|
162
|
+
|
|
163
|
+
Provide your own paywall provider:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
app.use(paymentMiddleware(routes, resourceServer, paywallConfig, customPaywallProvider));
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Advanced Usage
|
|
170
|
+
|
|
171
|
+
### Multiple Protected Routes
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
app.use(
|
|
175
|
+
paymentMiddleware(
|
|
176
|
+
{
|
|
177
|
+
"GET /api/premium/*": {
|
|
178
|
+
accepts: {
|
|
179
|
+
scheme: "exact",
|
|
180
|
+
price: "$1.00",
|
|
181
|
+
network: "eip155:8453",
|
|
182
|
+
payTo: "0xYourAddress",
|
|
183
|
+
},
|
|
184
|
+
description: "Premium API access",
|
|
185
|
+
},
|
|
186
|
+
"GET /api/data": {
|
|
187
|
+
accepts: {
|
|
188
|
+
scheme: "exact",
|
|
189
|
+
price: "$0.50",
|
|
190
|
+
network: "eip155:84532",
|
|
191
|
+
payTo: "0xYourAddress",
|
|
192
|
+
maxTimeoutSeconds: 120,
|
|
193
|
+
},
|
|
194
|
+
description: "Data endpoint access",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
resourceServer,
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Multiple Payment Networks
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
app.use(
|
|
206
|
+
paymentMiddleware(
|
|
207
|
+
{
|
|
208
|
+
"GET /weather": {
|
|
209
|
+
accepts: [
|
|
210
|
+
{
|
|
211
|
+
scheme: "exact",
|
|
212
|
+
price: "$0.001",
|
|
213
|
+
network: "eip155:84532",
|
|
214
|
+
payTo: evmAddress,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
scheme: "exact",
|
|
218
|
+
price: "$0.001",
|
|
219
|
+
network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
|
|
220
|
+
payTo: svmAddress,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
description: "Weather data",
|
|
224
|
+
mimeType: "application/json",
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
new x402ResourceServer(facilitatorClient)
|
|
228
|
+
.register("eip155:84532", new ExactEvmScheme())
|
|
229
|
+
.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Custom Facilitator Client
|
|
235
|
+
|
|
236
|
+
If you need to use a custom facilitator server, configure it when creating the x402ResourceServer:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { HTTPFacilitatorClient } from "@x402/core/server";
|
|
240
|
+
import { x402ResourceServer } from "@x402/hono";
|
|
241
|
+
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
242
|
+
|
|
243
|
+
const customFacilitator = new HTTPFacilitatorClient({
|
|
244
|
+
url: "https://your-facilitator.com",
|
|
245
|
+
createAuthHeaders: async () => ({
|
|
246
|
+
verify: { Authorization: "Bearer your-token" },
|
|
247
|
+
settle: { Authorization: "Bearer your-token" },
|
|
248
|
+
}),
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const resourceServer = new x402ResourceServer(customFacilitator)
|
|
252
|
+
.register("eip155:84532", new ExactEvmScheme());
|
|
253
|
+
|
|
254
|
+
app.use(paymentMiddleware(routes, resourceServer, paywallConfig));
|
|
255
|
+
```
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { HTTPAdapter, RoutesConfig, x402ResourceServer, PaywallConfig, PaywallProvider, FacilitatorClient } from '@x402/core/server';
|
|
2
|
+
export { PaywallConfig, PaywallProvider, RouteConfigurationError, RouteValidationError, x402HTTPResourceServer, x402ResourceServer } from '@x402/core/server';
|
|
3
|
+
import { Network, SchemeNetworkServer } from '@x402/core/types';
|
|
4
|
+
export { Network, PaymentPayload, PaymentRequired, PaymentRequirements, SchemeNetworkServer } from '@x402/core/types';
|
|
5
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hono adapter implementation
|
|
9
|
+
*/
|
|
10
|
+
declare class HonoAdapter implements HTTPAdapter {
|
|
11
|
+
private c;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new HonoAdapter instance.
|
|
14
|
+
*
|
|
15
|
+
* @param c - The Hono context object
|
|
16
|
+
*/
|
|
17
|
+
constructor(c: Context);
|
|
18
|
+
/**
|
|
19
|
+
* Gets a header value from the request.
|
|
20
|
+
*
|
|
21
|
+
* @param name - The header name
|
|
22
|
+
* @returns The header value or undefined
|
|
23
|
+
*/
|
|
24
|
+
getHeader(name: string): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Gets the HTTP method of the request.
|
|
27
|
+
*
|
|
28
|
+
* @returns The HTTP method
|
|
29
|
+
*/
|
|
30
|
+
getMethod(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Gets the path of the request.
|
|
33
|
+
*
|
|
34
|
+
* @returns The request path
|
|
35
|
+
*/
|
|
36
|
+
getPath(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Gets the full URL of the request.
|
|
39
|
+
*
|
|
40
|
+
* @returns The full request URL
|
|
41
|
+
*/
|
|
42
|
+
getUrl(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Gets the Accept header from the request.
|
|
45
|
+
*
|
|
46
|
+
* @returns The Accept header value or empty string
|
|
47
|
+
*/
|
|
48
|
+
getAcceptHeader(): string;
|
|
49
|
+
/**
|
|
50
|
+
* Gets the User-Agent header from the request.
|
|
51
|
+
*
|
|
52
|
+
* @returns The User-Agent header value or empty string
|
|
53
|
+
*/
|
|
54
|
+
getUserAgent(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Gets all query parameters from the request URL.
|
|
57
|
+
*
|
|
58
|
+
* @returns Record of query parameter key-value pairs
|
|
59
|
+
*/
|
|
60
|
+
getQueryParams(): Record<string, string | string[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Gets a specific query parameter by name.
|
|
63
|
+
*
|
|
64
|
+
* @param name - The query parameter name
|
|
65
|
+
* @returns The query parameter value(s) or undefined
|
|
66
|
+
*/
|
|
67
|
+
getQueryParam(name: string): string | string[] | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Gets the parsed request body.
|
|
70
|
+
* Requires appropriate body parsing middleware.
|
|
71
|
+
*
|
|
72
|
+
* @returns The parsed request body
|
|
73
|
+
*/
|
|
74
|
+
getBody(): Promise<unknown>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Configuration for registering a payment scheme with a specific network
|
|
79
|
+
*/
|
|
80
|
+
interface SchemeRegistration {
|
|
81
|
+
/**
|
|
82
|
+
* The network identifier (e.g., 'eip155:84532', 'solana:mainnet')
|
|
83
|
+
*/
|
|
84
|
+
network: Network;
|
|
85
|
+
/**
|
|
86
|
+
* The scheme server implementation for this network
|
|
87
|
+
*/
|
|
88
|
+
server: SchemeNetworkServer;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Hono payment middleware for x402 protocol (direct server instance).
|
|
92
|
+
*
|
|
93
|
+
* Use this when you want to pass a pre-configured x402ResourceServer instance.
|
|
94
|
+
* This provides more flexibility for testing, custom configuration, and reusing
|
|
95
|
+
* server instances across multiple middlewares.
|
|
96
|
+
*
|
|
97
|
+
* @param routes - Route configurations for protected endpoints
|
|
98
|
+
* @param server - Pre-configured x402ResourceServer instance
|
|
99
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
100
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
101
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
102
|
+
* @returns Hono middleware handler
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { paymentMiddleware } from "@x402/hono";
|
|
107
|
+
* import { x402ResourceServer } from "@x402/core/server";
|
|
108
|
+
* import { registerExactEvmScheme } from "@x402/evm/exact/server";
|
|
109
|
+
*
|
|
110
|
+
* const server = new x402ResourceServer(myFacilitatorClient);
|
|
111
|
+
* registerExactEvmScheme(server, {});
|
|
112
|
+
*
|
|
113
|
+
* app.use(paymentMiddleware(routes, server, paywallConfig));
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): MiddlewareHandler;
|
|
117
|
+
/**
|
|
118
|
+
* Hono payment middleware for x402 protocol (configuration-based).
|
|
119
|
+
*
|
|
120
|
+
* Use this when you want to quickly set up middleware with simple configuration.
|
|
121
|
+
* This function creates and configures the x402ResourceServer internally.
|
|
122
|
+
*
|
|
123
|
+
* @param routes - Route configurations for protected endpoints
|
|
124
|
+
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
125
|
+
* @param schemes - Optional array of scheme registrations for server-side payment processing
|
|
126
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
127
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
128
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
129
|
+
* @returns Hono middleware handler
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import { paymentMiddlewareFromConfig } from "@x402/hono";
|
|
134
|
+
*
|
|
135
|
+
* app.use(paymentMiddlewareFromConfig(
|
|
136
|
+
* routes,
|
|
137
|
+
* myFacilitatorClient,
|
|
138
|
+
* [{ network: "eip155:8453", server: evmSchemeServer }],
|
|
139
|
+
* paywallConfig
|
|
140
|
+
* ));
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function paymentMiddlewareFromConfig(routes: RoutesConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): MiddlewareHandler;
|
|
144
|
+
|
|
145
|
+
export { HonoAdapter, type SchemeRegistration, paymentMiddleware, paymentMiddlewareFromConfig };
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
HonoAdapter: () => HonoAdapter,
|
|
34
|
+
RouteConfigurationError: () => import_server3.RouteConfigurationError,
|
|
35
|
+
paymentMiddleware: () => paymentMiddleware,
|
|
36
|
+
paymentMiddlewareFromConfig: () => paymentMiddlewareFromConfig,
|
|
37
|
+
x402HTTPResourceServer: () => import_server2.x402HTTPResourceServer,
|
|
38
|
+
x402ResourceServer: () => import_server2.x402ResourceServer
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(src_exports);
|
|
41
|
+
var import_server = require("@x402/core/server");
|
|
42
|
+
|
|
43
|
+
// src/adapter.ts
|
|
44
|
+
var HonoAdapter = class {
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new HonoAdapter instance.
|
|
47
|
+
*
|
|
48
|
+
* @param c - The Hono context object
|
|
49
|
+
*/
|
|
50
|
+
constructor(c) {
|
|
51
|
+
this.c = c;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets a header value from the request.
|
|
55
|
+
*
|
|
56
|
+
* @param name - The header name
|
|
57
|
+
* @returns The header value or undefined
|
|
58
|
+
*/
|
|
59
|
+
getHeader(name) {
|
|
60
|
+
return this.c.req.header(name);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Gets the HTTP method of the request.
|
|
64
|
+
*
|
|
65
|
+
* @returns The HTTP method
|
|
66
|
+
*/
|
|
67
|
+
getMethod() {
|
|
68
|
+
return this.c.req.method;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Gets the path of the request.
|
|
72
|
+
*
|
|
73
|
+
* @returns The request path
|
|
74
|
+
*/
|
|
75
|
+
getPath() {
|
|
76
|
+
return this.c.req.path;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gets the full URL of the request.
|
|
80
|
+
*
|
|
81
|
+
* @returns The full request URL
|
|
82
|
+
*/
|
|
83
|
+
getUrl() {
|
|
84
|
+
return this.c.req.url;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Gets the Accept header from the request.
|
|
88
|
+
*
|
|
89
|
+
* @returns The Accept header value or empty string
|
|
90
|
+
*/
|
|
91
|
+
getAcceptHeader() {
|
|
92
|
+
return this.c.req.header("Accept") || "";
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets the User-Agent header from the request.
|
|
96
|
+
*
|
|
97
|
+
* @returns The User-Agent header value or empty string
|
|
98
|
+
*/
|
|
99
|
+
getUserAgent() {
|
|
100
|
+
return this.c.req.header("User-Agent") || "";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Gets all query parameters from the request URL.
|
|
104
|
+
*
|
|
105
|
+
* @returns Record of query parameter key-value pairs
|
|
106
|
+
*/
|
|
107
|
+
getQueryParams() {
|
|
108
|
+
const query = this.c.req.query();
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const [key, value] of Object.entries(query)) {
|
|
111
|
+
result[key] = value;
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Gets a specific query parameter by name.
|
|
117
|
+
*
|
|
118
|
+
* @param name - The query parameter name
|
|
119
|
+
* @returns The query parameter value(s) or undefined
|
|
120
|
+
*/
|
|
121
|
+
getQueryParam(name) {
|
|
122
|
+
return this.c.req.query(name);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Gets the parsed request body.
|
|
126
|
+
* Requires appropriate body parsing middleware.
|
|
127
|
+
*
|
|
128
|
+
* @returns The parsed request body
|
|
129
|
+
*/
|
|
130
|
+
async getBody() {
|
|
131
|
+
try {
|
|
132
|
+
return await this.c.req.json();
|
|
133
|
+
} catch {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/index.ts
|
|
140
|
+
var import_server2 = require("@x402/core/server");
|
|
141
|
+
var import_server3 = require("@x402/core/server");
|
|
142
|
+
function checkIfBazaarNeeded(routes) {
|
|
143
|
+
if ("accepts" in routes) {
|
|
144
|
+
return !!(routes.extensions && "bazaar" in routes.extensions);
|
|
145
|
+
}
|
|
146
|
+
return Object.values(routes).some((routeConfig) => {
|
|
147
|
+
return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function paymentMiddleware(routes, server, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
151
|
+
const httpServer = new import_server.x402HTTPResourceServer(server, routes);
|
|
152
|
+
if (paywall) {
|
|
153
|
+
httpServer.registerPaywallProvider(paywall);
|
|
154
|
+
}
|
|
155
|
+
let initPromise = syncFacilitatorOnStart ? httpServer.initialize() : null;
|
|
156
|
+
let bazaarPromise = null;
|
|
157
|
+
if (checkIfBazaarNeeded(routes)) {
|
|
158
|
+
bazaarPromise = import("@x402/extensions/bazaar").then(({ bazaarResourceServerExtension }) => {
|
|
159
|
+
server.registerExtension(bazaarResourceServerExtension);
|
|
160
|
+
}).catch((err) => {
|
|
161
|
+
console.error("Failed to load bazaar extension:", err);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return async (c, next) => {
|
|
165
|
+
const adapter = new HonoAdapter(c);
|
|
166
|
+
const context = {
|
|
167
|
+
adapter,
|
|
168
|
+
path: c.req.path,
|
|
169
|
+
method: c.req.method,
|
|
170
|
+
paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
|
|
171
|
+
};
|
|
172
|
+
if (!httpServer.requiresPayment(context)) {
|
|
173
|
+
return next();
|
|
174
|
+
}
|
|
175
|
+
if (initPromise) {
|
|
176
|
+
await initPromise;
|
|
177
|
+
initPromise = null;
|
|
178
|
+
}
|
|
179
|
+
if (bazaarPromise) {
|
|
180
|
+
await bazaarPromise;
|
|
181
|
+
bazaarPromise = null;
|
|
182
|
+
}
|
|
183
|
+
const result = await httpServer.processHTTPRequest(context, paywallConfig);
|
|
184
|
+
switch (result.type) {
|
|
185
|
+
case "no-payment-required":
|
|
186
|
+
return next();
|
|
187
|
+
case "payment-error":
|
|
188
|
+
const { response } = result;
|
|
189
|
+
Object.entries(response.headers).forEach(([key, value]) => {
|
|
190
|
+
c.header(key, value);
|
|
191
|
+
});
|
|
192
|
+
if (response.isHtml) {
|
|
193
|
+
return c.html(response.body, response.status);
|
|
194
|
+
} else {
|
|
195
|
+
return c.json(response.body || {}, response.status);
|
|
196
|
+
}
|
|
197
|
+
case "payment-verified":
|
|
198
|
+
const { paymentPayload, paymentRequirements } = result;
|
|
199
|
+
await next();
|
|
200
|
+
let res = c.res;
|
|
201
|
+
if (res.status >= 400) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
c.res = void 0;
|
|
205
|
+
try {
|
|
206
|
+
const settleResult = await httpServer.processSettlement(
|
|
207
|
+
paymentPayload,
|
|
208
|
+
paymentRequirements
|
|
209
|
+
);
|
|
210
|
+
if (!settleResult.success) {
|
|
211
|
+
res = c.json(
|
|
212
|
+
{
|
|
213
|
+
error: "Settlement failed",
|
|
214
|
+
details: settleResult.errorReason
|
|
215
|
+
},
|
|
216
|
+
402
|
|
217
|
+
);
|
|
218
|
+
} else {
|
|
219
|
+
Object.entries(settleResult.headers).forEach(([key, value]) => {
|
|
220
|
+
res.headers.set(key, value);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error(error);
|
|
225
|
+
res = c.json(
|
|
226
|
+
{
|
|
227
|
+
error: "Settlement failed",
|
|
228
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
229
|
+
},
|
|
230
|
+
402
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
c.res = res;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function paymentMiddlewareFromConfig(routes, facilitatorClients, schemes, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
239
|
+
const ResourceServer = new import_server.x402ResourceServer(facilitatorClients);
|
|
240
|
+
if (schemes) {
|
|
241
|
+
schemes.forEach(({ network, server: schemeServer }) => {
|
|
242
|
+
ResourceServer.register(network, schemeServer);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);
|
|
246
|
+
}
|
|
247
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
248
|
+
0 && (module.exports = {
|
|
249
|
+
HonoAdapter,
|
|
250
|
+
RouteConfigurationError,
|
|
251
|
+
paymentMiddleware,
|
|
252
|
+
paymentMiddlewareFromConfig,
|
|
253
|
+
x402HTTPResourceServer,
|
|
254
|
+
x402ResourceServer
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/adapter.ts"],"sourcesContent":["import {\n HTTPRequestContext,\n PaywallConfig,\n PaywallProvider,\n x402HTTPResourceServer,\n x402ResourceServer,\n RoutesConfig,\n FacilitatorClient,\n} from \"@x402/core/server\";\nimport { SchemeNetworkServer, Network } from \"@x402/core/types\";\nimport { Context, MiddlewareHandler } from \"hono\";\nimport { HonoAdapter } from \"./adapter\";\n\n/**\n * Check if any routes in the configuration declare bazaar extensions\n *\n * @param routes - Route configuration\n * @returns True if any route has extensions.bazaar defined\n */\nfunction checkIfBazaarNeeded(routes: RoutesConfig): boolean {\n // Handle single route config\n if (\"accepts\" in routes) {\n return !!(routes.extensions && \"bazaar\" in routes.extensions);\n }\n\n // Handle multiple routes\n return Object.values(routes).some(routeConfig => {\n return !!(routeConfig.extensions && \"bazaar\" in routeConfig.extensions);\n });\n}\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme server implementation for this network\n */\n server: SchemeNetworkServer;\n}\n\n/**\n * Hono payment middleware for x402 protocol (direct server instance).\n *\n * Use this when you want to pass a pre-configured x402ResourceServer instance.\n * This provides more flexibility for testing, custom configuration, and reusing\n * server instances across multiple middlewares.\n *\n * @param routes - Route configurations for protected endpoints\n * @param server - Pre-configured x402ResourceServer instance\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Hono middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddleware } from \"@x402/hono\";\n * import { x402ResourceServer } from \"@x402/core/server\";\n * import { registerExactEvmScheme } from \"@x402/evm/exact/server\";\n *\n * const server = new x402ResourceServer(myFacilitatorClient);\n * registerExactEvmScheme(server, {});\n *\n * app.use(paymentMiddleware(routes, server, paywallConfig));\n * ```\n */\nexport function paymentMiddleware(\n routes: RoutesConfig,\n server: x402ResourceServer,\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n): MiddlewareHandler {\n // Create the x402 HTTP server instance with the resource server\n const httpServer = new x402HTTPResourceServer(server, routes);\n\n // Register custom paywall provider if provided\n if (paywall) {\n httpServer.registerPaywallProvider(paywall);\n }\n\n // Store initialization promise (not the result)\n // httpServer.initialize() fetches facilitator support and validates routes\n let initPromise: Promise<void> | null = syncFacilitatorOnStart ? httpServer.initialize() : null;\n\n // Dynamically register bazaar extension if routes declare it\n let bazaarPromise: Promise<void> | null = null;\n if (checkIfBazaarNeeded(routes)) {\n bazaarPromise = import(\"@x402/extensions/bazaar\")\n .then(({ bazaarResourceServerExtension }) => {\n server.registerExtension(bazaarResourceServerExtension);\n })\n .catch(err => {\n console.error(\"Failed to load bazaar extension:\", err);\n });\n }\n\n return async (c: Context, next: () => Promise<void>) => {\n // Create adapter and context\n const adapter = new HonoAdapter(c);\n const context: HTTPRequestContext = {\n adapter,\n path: c.req.path,\n method: c.req.method,\n paymentHeader: adapter.getHeader(\"payment-signature\") || adapter.getHeader(\"x-payment\"),\n };\n\n // Check if route requires payment before initializing facilitator\n if (!httpServer.requiresPayment(context)) {\n return next();\n }\n\n // Only initialize when processing a protected route\n if (initPromise) {\n await initPromise;\n initPromise = null; // Clear after first await\n }\n\n // Await bazaar extension loading if needed\n if (bazaarPromise) {\n await bazaarPromise;\n bazaarPromise = null;\n }\n\n // Process payment requirement check\n const result = await httpServer.processHTTPRequest(context, paywallConfig);\n\n // Handle the different result types\n switch (result.type) {\n case \"no-payment-required\":\n // No payment needed, proceed directly to the route handler\n return next();\n\n case \"payment-error\":\n // Payment required but not provided or invalid\n const { response } = result;\n Object.entries(response.headers).forEach(([key, value]) => {\n c.header(key, value);\n });\n if (response.isHtml) {\n return c.html(response.body as string, response.status as 402);\n } else {\n return c.json(response.body || {}, response.status as 402);\n }\n\n case \"payment-verified\":\n // Payment is valid, need to wrap response for settlement\n const { paymentPayload, paymentRequirements } = result;\n\n // Proceed to the next middleware or route handler\n await next();\n\n // Get the current response\n let res = c.res;\n\n // If the response from the protected route is >= 400, do not settle payment\n if (res.status >= 400) {\n return;\n }\n\n // Clear the response so we can modify headers\n c.res = undefined;\n\n try {\n const settleResult = await httpServer.processSettlement(\n paymentPayload,\n paymentRequirements,\n );\n\n if (!settleResult.success) {\n // Settlement failed - do not return the protected resource\n res = c.json(\n {\n error: \"Settlement failed\",\n details: settleResult.errorReason,\n },\n 402,\n );\n } else {\n // Settlement succeeded - add headers to response\n Object.entries(settleResult.headers).forEach(([key, value]) => {\n res.headers.set(key, value);\n });\n }\n } catch (error) {\n console.error(error);\n // If settlement fails, return an error response\n res = c.json(\n {\n error: \"Settlement failed\",\n details: error instanceof Error ? error.message : \"Unknown error\",\n },\n 402,\n );\n }\n\n // Restore the response (potentially modified with settlement headers)\n c.res = res;\n return;\n }\n };\n}\n\n/**\n * Hono payment middleware for x402 protocol (configuration-based).\n *\n * Use this when you want to quickly set up middleware with simple configuration.\n * This function creates and configures the x402ResourceServer internally.\n *\n * @param routes - Route configurations for protected endpoints\n * @param facilitatorClients - Optional facilitator client(s) for payment processing\n * @param schemes - Optional array of scheme registrations for server-side payment processing\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Hono middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddlewareFromConfig } from \"@x402/hono\";\n *\n * app.use(paymentMiddlewareFromConfig(\n * routes,\n * myFacilitatorClient,\n * [{ network: \"eip155:8453\", server: evmSchemeServer }],\n * paywallConfig\n * ));\n * ```\n */\nexport function paymentMiddlewareFromConfig(\n routes: RoutesConfig,\n facilitatorClients?: FacilitatorClient | FacilitatorClient[],\n schemes?: SchemeRegistration[],\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n): MiddlewareHandler {\n const ResourceServer = new x402ResourceServer(facilitatorClients);\n\n if (schemes) {\n schemes.forEach(({ network, server: schemeServer }) => {\n ResourceServer.register(network, schemeServer);\n });\n }\n\n // Use the direct paymentMiddleware with the configured server\n // Note: paymentMiddleware handles dynamic bazaar registration\n return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);\n}\n\nexport { x402ResourceServer, x402HTTPResourceServer } from \"@x402/core/server\";\n\nexport type {\n PaymentRequired,\n PaymentRequirements,\n PaymentPayload,\n Network,\n SchemeNetworkServer,\n} from \"@x402/core/types\";\n\nexport type { PaywallProvider, PaywallConfig } from \"@x402/core/server\";\n\nexport { RouteConfigurationError } from \"@x402/core/server\";\n\nexport type { RouteValidationError } from \"@x402/core/server\";\n\nexport { HonoAdapter } from \"./adapter\";\n","import { HTTPAdapter } from \"@x402/core/server\";\nimport { Context } from \"hono\";\n\n/**\n * Hono adapter implementation\n */\nexport class HonoAdapter implements HTTPAdapter {\n /**\n * Creates a new HonoAdapter instance.\n *\n * @param c - The Hono context object\n */\n constructor(private c: Context) {}\n\n /**\n * Gets a header value from the request.\n *\n * @param name - The header name\n * @returns The header value or undefined\n */\n getHeader(name: string): string | undefined {\n return this.c.req.header(name);\n }\n\n /**\n * Gets the HTTP method of the request.\n *\n * @returns The HTTP method\n */\n getMethod(): string {\n return this.c.req.method;\n }\n\n /**\n * Gets the path of the request.\n *\n * @returns The request path\n */\n getPath(): string {\n return this.c.req.path;\n }\n\n /**\n * Gets the full URL of the request.\n *\n * @returns The full request URL\n */\n getUrl(): string {\n return this.c.req.url;\n }\n\n /**\n * Gets the Accept header from the request.\n *\n * @returns The Accept header value or empty string\n */\n getAcceptHeader(): string {\n return this.c.req.header(\"Accept\") || \"\";\n }\n\n /**\n * Gets the User-Agent header from the request.\n *\n * @returns The User-Agent header value or empty string\n */\n getUserAgent(): string {\n return this.c.req.header(\"User-Agent\") || \"\";\n }\n\n /**\n * Gets all query parameters from the request URL.\n *\n * @returns Record of query parameter key-value pairs\n */\n getQueryParams(): Record<string, string | string[]> {\n const query = this.c.req.query();\n // Convert single values to match the interface\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(query)) {\n result[key] = value;\n }\n return result;\n }\n\n /**\n * Gets a specific query parameter by name.\n *\n * @param name - The query parameter name\n * @returns The query parameter value(s) or undefined\n */\n getQueryParam(name: string): string | string[] | undefined {\n return this.c.req.query(name);\n }\n\n /**\n * Gets the parsed request body.\n * Requires appropriate body parsing middleware.\n *\n * @returns The parsed request body\n */\n async getBody(): Promise<unknown> {\n try {\n return await this.c.req.json();\n } catch {\n return undefined;\n }\n }\n}\n"],"mappings":"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","names":["import_server"]}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { HTTPAdapter, RoutesConfig, x402ResourceServer, PaywallConfig, PaywallProvider, FacilitatorClient } from '@x402/core/server';
|
|
2
|
+
export { PaywallConfig, PaywallProvider, RouteConfigurationError, RouteValidationError, x402HTTPResourceServer, x402ResourceServer } from '@x402/core/server';
|
|
3
|
+
import { Network, SchemeNetworkServer } from '@x402/core/types';
|
|
4
|
+
export { Network, PaymentPayload, PaymentRequired, PaymentRequirements, SchemeNetworkServer } from '@x402/core/types';
|
|
5
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hono adapter implementation
|
|
9
|
+
*/
|
|
10
|
+
declare class HonoAdapter implements HTTPAdapter {
|
|
11
|
+
private c;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new HonoAdapter instance.
|
|
14
|
+
*
|
|
15
|
+
* @param c - The Hono context object
|
|
16
|
+
*/
|
|
17
|
+
constructor(c: Context);
|
|
18
|
+
/**
|
|
19
|
+
* Gets a header value from the request.
|
|
20
|
+
*
|
|
21
|
+
* @param name - The header name
|
|
22
|
+
* @returns The header value or undefined
|
|
23
|
+
*/
|
|
24
|
+
getHeader(name: string): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Gets the HTTP method of the request.
|
|
27
|
+
*
|
|
28
|
+
* @returns The HTTP method
|
|
29
|
+
*/
|
|
30
|
+
getMethod(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Gets the path of the request.
|
|
33
|
+
*
|
|
34
|
+
* @returns The request path
|
|
35
|
+
*/
|
|
36
|
+
getPath(): string;
|
|
37
|
+
/**
|
|
38
|
+
* Gets the full URL of the request.
|
|
39
|
+
*
|
|
40
|
+
* @returns The full request URL
|
|
41
|
+
*/
|
|
42
|
+
getUrl(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Gets the Accept header from the request.
|
|
45
|
+
*
|
|
46
|
+
* @returns The Accept header value or empty string
|
|
47
|
+
*/
|
|
48
|
+
getAcceptHeader(): string;
|
|
49
|
+
/**
|
|
50
|
+
* Gets the User-Agent header from the request.
|
|
51
|
+
*
|
|
52
|
+
* @returns The User-Agent header value or empty string
|
|
53
|
+
*/
|
|
54
|
+
getUserAgent(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Gets all query parameters from the request URL.
|
|
57
|
+
*
|
|
58
|
+
* @returns Record of query parameter key-value pairs
|
|
59
|
+
*/
|
|
60
|
+
getQueryParams(): Record<string, string | string[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Gets a specific query parameter by name.
|
|
63
|
+
*
|
|
64
|
+
* @param name - The query parameter name
|
|
65
|
+
* @returns The query parameter value(s) or undefined
|
|
66
|
+
*/
|
|
67
|
+
getQueryParam(name: string): string | string[] | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Gets the parsed request body.
|
|
70
|
+
* Requires appropriate body parsing middleware.
|
|
71
|
+
*
|
|
72
|
+
* @returns The parsed request body
|
|
73
|
+
*/
|
|
74
|
+
getBody(): Promise<unknown>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Configuration for registering a payment scheme with a specific network
|
|
79
|
+
*/
|
|
80
|
+
interface SchemeRegistration {
|
|
81
|
+
/**
|
|
82
|
+
* The network identifier (e.g., 'eip155:84532', 'solana:mainnet')
|
|
83
|
+
*/
|
|
84
|
+
network: Network;
|
|
85
|
+
/**
|
|
86
|
+
* The scheme server implementation for this network
|
|
87
|
+
*/
|
|
88
|
+
server: SchemeNetworkServer;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Hono payment middleware for x402 protocol (direct server instance).
|
|
92
|
+
*
|
|
93
|
+
* Use this when you want to pass a pre-configured x402ResourceServer instance.
|
|
94
|
+
* This provides more flexibility for testing, custom configuration, and reusing
|
|
95
|
+
* server instances across multiple middlewares.
|
|
96
|
+
*
|
|
97
|
+
* @param routes - Route configurations for protected endpoints
|
|
98
|
+
* @param server - Pre-configured x402ResourceServer instance
|
|
99
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
100
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
101
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
102
|
+
* @returns Hono middleware handler
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { paymentMiddleware } from "@x402/hono";
|
|
107
|
+
* import { x402ResourceServer } from "@x402/core/server";
|
|
108
|
+
* import { registerExactEvmScheme } from "@x402/evm/exact/server";
|
|
109
|
+
*
|
|
110
|
+
* const server = new x402ResourceServer(myFacilitatorClient);
|
|
111
|
+
* registerExactEvmScheme(server, {});
|
|
112
|
+
*
|
|
113
|
+
* app.use(paymentMiddleware(routes, server, paywallConfig));
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): MiddlewareHandler;
|
|
117
|
+
/**
|
|
118
|
+
* Hono payment middleware for x402 protocol (configuration-based).
|
|
119
|
+
*
|
|
120
|
+
* Use this when you want to quickly set up middleware with simple configuration.
|
|
121
|
+
* This function creates and configures the x402ResourceServer internally.
|
|
122
|
+
*
|
|
123
|
+
* @param routes - Route configurations for protected endpoints
|
|
124
|
+
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
125
|
+
* @param schemes - Optional array of scheme registrations for server-side payment processing
|
|
126
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
127
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
128
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
129
|
+
* @returns Hono middleware handler
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import { paymentMiddlewareFromConfig } from "@x402/hono";
|
|
134
|
+
*
|
|
135
|
+
* app.use(paymentMiddlewareFromConfig(
|
|
136
|
+
* routes,
|
|
137
|
+
* myFacilitatorClient,
|
|
138
|
+
* [{ network: "eip155:8453", server: evmSchemeServer }],
|
|
139
|
+
* paywallConfig
|
|
140
|
+
* ));
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function paymentMiddlewareFromConfig(routes: RoutesConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): MiddlewareHandler;
|
|
144
|
+
|
|
145
|
+
export { HonoAdapter, type SchemeRegistration, paymentMiddleware, paymentMiddlewareFromConfig };
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
x402HTTPResourceServer,
|
|
4
|
+
x402ResourceServer
|
|
5
|
+
} from "@x402/core/server";
|
|
6
|
+
|
|
7
|
+
// src/adapter.ts
|
|
8
|
+
var HonoAdapter = class {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new HonoAdapter instance.
|
|
11
|
+
*
|
|
12
|
+
* @param c - The Hono context object
|
|
13
|
+
*/
|
|
14
|
+
constructor(c) {
|
|
15
|
+
this.c = c;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Gets a header value from the request.
|
|
19
|
+
*
|
|
20
|
+
* @param name - The header name
|
|
21
|
+
* @returns The header value or undefined
|
|
22
|
+
*/
|
|
23
|
+
getHeader(name) {
|
|
24
|
+
return this.c.req.header(name);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Gets the HTTP method of the request.
|
|
28
|
+
*
|
|
29
|
+
* @returns The HTTP method
|
|
30
|
+
*/
|
|
31
|
+
getMethod() {
|
|
32
|
+
return this.c.req.method;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets the path of the request.
|
|
36
|
+
*
|
|
37
|
+
* @returns The request path
|
|
38
|
+
*/
|
|
39
|
+
getPath() {
|
|
40
|
+
return this.c.req.path;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Gets the full URL of the request.
|
|
44
|
+
*
|
|
45
|
+
* @returns The full request URL
|
|
46
|
+
*/
|
|
47
|
+
getUrl() {
|
|
48
|
+
return this.c.req.url;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Gets the Accept header from the request.
|
|
52
|
+
*
|
|
53
|
+
* @returns The Accept header value or empty string
|
|
54
|
+
*/
|
|
55
|
+
getAcceptHeader() {
|
|
56
|
+
return this.c.req.header("Accept") || "";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gets the User-Agent header from the request.
|
|
60
|
+
*
|
|
61
|
+
* @returns The User-Agent header value or empty string
|
|
62
|
+
*/
|
|
63
|
+
getUserAgent() {
|
|
64
|
+
return this.c.req.header("User-Agent") || "";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets all query parameters from the request URL.
|
|
68
|
+
*
|
|
69
|
+
* @returns Record of query parameter key-value pairs
|
|
70
|
+
*/
|
|
71
|
+
getQueryParams() {
|
|
72
|
+
const query = this.c.req.query();
|
|
73
|
+
const result = {};
|
|
74
|
+
for (const [key, value] of Object.entries(query)) {
|
|
75
|
+
result[key] = value;
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Gets a specific query parameter by name.
|
|
81
|
+
*
|
|
82
|
+
* @param name - The query parameter name
|
|
83
|
+
* @returns The query parameter value(s) or undefined
|
|
84
|
+
*/
|
|
85
|
+
getQueryParam(name) {
|
|
86
|
+
return this.c.req.query(name);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Gets the parsed request body.
|
|
90
|
+
* Requires appropriate body parsing middleware.
|
|
91
|
+
*
|
|
92
|
+
* @returns The parsed request body
|
|
93
|
+
*/
|
|
94
|
+
async getBody() {
|
|
95
|
+
try {
|
|
96
|
+
return await this.c.req.json();
|
|
97
|
+
} catch {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/index.ts
|
|
104
|
+
import { x402ResourceServer as x402ResourceServer2, x402HTTPResourceServer as x402HTTPResourceServer2 } from "@x402/core/server";
|
|
105
|
+
import { RouteConfigurationError } from "@x402/core/server";
|
|
106
|
+
function checkIfBazaarNeeded(routes) {
|
|
107
|
+
if ("accepts" in routes) {
|
|
108
|
+
return !!(routes.extensions && "bazaar" in routes.extensions);
|
|
109
|
+
}
|
|
110
|
+
return Object.values(routes).some((routeConfig) => {
|
|
111
|
+
return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function paymentMiddleware(routes, server, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
115
|
+
const httpServer = new x402HTTPResourceServer(server, routes);
|
|
116
|
+
if (paywall) {
|
|
117
|
+
httpServer.registerPaywallProvider(paywall);
|
|
118
|
+
}
|
|
119
|
+
let initPromise = syncFacilitatorOnStart ? httpServer.initialize() : null;
|
|
120
|
+
let bazaarPromise = null;
|
|
121
|
+
if (checkIfBazaarNeeded(routes)) {
|
|
122
|
+
bazaarPromise = import("@x402/extensions/bazaar").then(({ bazaarResourceServerExtension }) => {
|
|
123
|
+
server.registerExtension(bazaarResourceServerExtension);
|
|
124
|
+
}).catch((err) => {
|
|
125
|
+
console.error("Failed to load bazaar extension:", err);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return async (c, next) => {
|
|
129
|
+
const adapter = new HonoAdapter(c);
|
|
130
|
+
const context = {
|
|
131
|
+
adapter,
|
|
132
|
+
path: c.req.path,
|
|
133
|
+
method: c.req.method,
|
|
134
|
+
paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
|
|
135
|
+
};
|
|
136
|
+
if (!httpServer.requiresPayment(context)) {
|
|
137
|
+
return next();
|
|
138
|
+
}
|
|
139
|
+
if (initPromise) {
|
|
140
|
+
await initPromise;
|
|
141
|
+
initPromise = null;
|
|
142
|
+
}
|
|
143
|
+
if (bazaarPromise) {
|
|
144
|
+
await bazaarPromise;
|
|
145
|
+
bazaarPromise = null;
|
|
146
|
+
}
|
|
147
|
+
const result = await httpServer.processHTTPRequest(context, paywallConfig);
|
|
148
|
+
switch (result.type) {
|
|
149
|
+
case "no-payment-required":
|
|
150
|
+
return next();
|
|
151
|
+
case "payment-error":
|
|
152
|
+
const { response } = result;
|
|
153
|
+
Object.entries(response.headers).forEach(([key, value]) => {
|
|
154
|
+
c.header(key, value);
|
|
155
|
+
});
|
|
156
|
+
if (response.isHtml) {
|
|
157
|
+
return c.html(response.body, response.status);
|
|
158
|
+
} else {
|
|
159
|
+
return c.json(response.body || {}, response.status);
|
|
160
|
+
}
|
|
161
|
+
case "payment-verified":
|
|
162
|
+
const { paymentPayload, paymentRequirements } = result;
|
|
163
|
+
await next();
|
|
164
|
+
let res = c.res;
|
|
165
|
+
if (res.status >= 400) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
c.res = void 0;
|
|
169
|
+
try {
|
|
170
|
+
const settleResult = await httpServer.processSettlement(
|
|
171
|
+
paymentPayload,
|
|
172
|
+
paymentRequirements
|
|
173
|
+
);
|
|
174
|
+
if (!settleResult.success) {
|
|
175
|
+
res = c.json(
|
|
176
|
+
{
|
|
177
|
+
error: "Settlement failed",
|
|
178
|
+
details: settleResult.errorReason
|
|
179
|
+
},
|
|
180
|
+
402
|
|
181
|
+
);
|
|
182
|
+
} else {
|
|
183
|
+
Object.entries(settleResult.headers).forEach(([key, value]) => {
|
|
184
|
+
res.headers.set(key, value);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(error);
|
|
189
|
+
res = c.json(
|
|
190
|
+
{
|
|
191
|
+
error: "Settlement failed",
|
|
192
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
193
|
+
},
|
|
194
|
+
402
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
c.res = res;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function paymentMiddlewareFromConfig(routes, facilitatorClients, schemes, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
203
|
+
const ResourceServer = new x402ResourceServer(facilitatorClients);
|
|
204
|
+
if (schemes) {
|
|
205
|
+
schemes.forEach(({ network, server: schemeServer }) => {
|
|
206
|
+
ResourceServer.register(network, schemeServer);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);
|
|
210
|
+
}
|
|
211
|
+
export {
|
|
212
|
+
HonoAdapter,
|
|
213
|
+
RouteConfigurationError,
|
|
214
|
+
paymentMiddleware,
|
|
215
|
+
paymentMiddlewareFromConfig,
|
|
216
|
+
x402HTTPResourceServer2 as x402HTTPResourceServer,
|
|
217
|
+
x402ResourceServer2 as x402ResourceServer
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/adapter.ts"],"sourcesContent":["import {\n HTTPRequestContext,\n PaywallConfig,\n PaywallProvider,\n x402HTTPResourceServer,\n x402ResourceServer,\n RoutesConfig,\n FacilitatorClient,\n} from \"@x402/core/server\";\nimport { SchemeNetworkServer, Network } from \"@x402/core/types\";\nimport { Context, MiddlewareHandler } from \"hono\";\nimport { HonoAdapter } from \"./adapter\";\n\n/**\n * Check if any routes in the configuration declare bazaar extensions\n *\n * @param routes - Route configuration\n * @returns True if any route has extensions.bazaar defined\n */\nfunction checkIfBazaarNeeded(routes: RoutesConfig): boolean {\n // Handle single route config\n if (\"accepts\" in routes) {\n return !!(routes.extensions && \"bazaar\" in routes.extensions);\n }\n\n // Handle multiple routes\n return Object.values(routes).some(routeConfig => {\n return !!(routeConfig.extensions && \"bazaar\" in routeConfig.extensions);\n });\n}\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme server implementation for this network\n */\n server: SchemeNetworkServer;\n}\n\n/**\n * Hono payment middleware for x402 protocol (direct server instance).\n *\n * Use this when you want to pass a pre-configured x402ResourceServer instance.\n * This provides more flexibility for testing, custom configuration, and reusing\n * server instances across multiple middlewares.\n *\n * @param routes - Route configurations for protected endpoints\n * @param server - Pre-configured x402ResourceServer instance\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Hono middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddleware } from \"@x402/hono\";\n * import { x402ResourceServer } from \"@x402/core/server\";\n * import { registerExactEvmScheme } from \"@x402/evm/exact/server\";\n *\n * const server = new x402ResourceServer(myFacilitatorClient);\n * registerExactEvmScheme(server, {});\n *\n * app.use(paymentMiddleware(routes, server, paywallConfig));\n * ```\n */\nexport function paymentMiddleware(\n routes: RoutesConfig,\n server: x402ResourceServer,\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n): MiddlewareHandler {\n // Create the x402 HTTP server instance with the resource server\n const httpServer = new x402HTTPResourceServer(server, routes);\n\n // Register custom paywall provider if provided\n if (paywall) {\n httpServer.registerPaywallProvider(paywall);\n }\n\n // Store initialization promise (not the result)\n // httpServer.initialize() fetches facilitator support and validates routes\n let initPromise: Promise<void> | null = syncFacilitatorOnStart ? httpServer.initialize() : null;\n\n // Dynamically register bazaar extension if routes declare it\n let bazaarPromise: Promise<void> | null = null;\n if (checkIfBazaarNeeded(routes)) {\n bazaarPromise = import(\"@x402/extensions/bazaar\")\n .then(({ bazaarResourceServerExtension }) => {\n server.registerExtension(bazaarResourceServerExtension);\n })\n .catch(err => {\n console.error(\"Failed to load bazaar extension:\", err);\n });\n }\n\n return async (c: Context, next: () => Promise<void>) => {\n // Create adapter and context\n const adapter = new HonoAdapter(c);\n const context: HTTPRequestContext = {\n adapter,\n path: c.req.path,\n method: c.req.method,\n paymentHeader: adapter.getHeader(\"payment-signature\") || adapter.getHeader(\"x-payment\"),\n };\n\n // Check if route requires payment before initializing facilitator\n if (!httpServer.requiresPayment(context)) {\n return next();\n }\n\n // Only initialize when processing a protected route\n if (initPromise) {\n await initPromise;\n initPromise = null; // Clear after first await\n }\n\n // Await bazaar extension loading if needed\n if (bazaarPromise) {\n await bazaarPromise;\n bazaarPromise = null;\n }\n\n // Process payment requirement check\n const result = await httpServer.processHTTPRequest(context, paywallConfig);\n\n // Handle the different result types\n switch (result.type) {\n case \"no-payment-required\":\n // No payment needed, proceed directly to the route handler\n return next();\n\n case \"payment-error\":\n // Payment required but not provided or invalid\n const { response } = result;\n Object.entries(response.headers).forEach(([key, value]) => {\n c.header(key, value);\n });\n if (response.isHtml) {\n return c.html(response.body as string, response.status as 402);\n } else {\n return c.json(response.body || {}, response.status as 402);\n }\n\n case \"payment-verified\":\n // Payment is valid, need to wrap response for settlement\n const { paymentPayload, paymentRequirements } = result;\n\n // Proceed to the next middleware or route handler\n await next();\n\n // Get the current response\n let res = c.res;\n\n // If the response from the protected route is >= 400, do not settle payment\n if (res.status >= 400) {\n return;\n }\n\n // Clear the response so we can modify headers\n c.res = undefined;\n\n try {\n const settleResult = await httpServer.processSettlement(\n paymentPayload,\n paymentRequirements,\n );\n\n if (!settleResult.success) {\n // Settlement failed - do not return the protected resource\n res = c.json(\n {\n error: \"Settlement failed\",\n details: settleResult.errorReason,\n },\n 402,\n );\n } else {\n // Settlement succeeded - add headers to response\n Object.entries(settleResult.headers).forEach(([key, value]) => {\n res.headers.set(key, value);\n });\n }\n } catch (error) {\n console.error(error);\n // If settlement fails, return an error response\n res = c.json(\n {\n error: \"Settlement failed\",\n details: error instanceof Error ? error.message : \"Unknown error\",\n },\n 402,\n );\n }\n\n // Restore the response (potentially modified with settlement headers)\n c.res = res;\n return;\n }\n };\n}\n\n/**\n * Hono payment middleware for x402 protocol (configuration-based).\n *\n * Use this when you want to quickly set up middleware with simple configuration.\n * This function creates and configures the x402ResourceServer internally.\n *\n * @param routes - Route configurations for protected endpoints\n * @param facilitatorClients - Optional facilitator client(s) for payment processing\n * @param schemes - Optional array of scheme registrations for server-side payment processing\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Hono middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddlewareFromConfig } from \"@x402/hono\";\n *\n * app.use(paymentMiddlewareFromConfig(\n * routes,\n * myFacilitatorClient,\n * [{ network: \"eip155:8453\", server: evmSchemeServer }],\n * paywallConfig\n * ));\n * ```\n */\nexport function paymentMiddlewareFromConfig(\n routes: RoutesConfig,\n facilitatorClients?: FacilitatorClient | FacilitatorClient[],\n schemes?: SchemeRegistration[],\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n): MiddlewareHandler {\n const ResourceServer = new x402ResourceServer(facilitatorClients);\n\n if (schemes) {\n schemes.forEach(({ network, server: schemeServer }) => {\n ResourceServer.register(network, schemeServer);\n });\n }\n\n // Use the direct paymentMiddleware with the configured server\n // Note: paymentMiddleware handles dynamic bazaar registration\n return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);\n}\n\nexport { x402ResourceServer, x402HTTPResourceServer } from \"@x402/core/server\";\n\nexport type {\n PaymentRequired,\n PaymentRequirements,\n PaymentPayload,\n Network,\n SchemeNetworkServer,\n} from \"@x402/core/types\";\n\nexport type { PaywallProvider, PaywallConfig } from \"@x402/core/server\";\n\nexport { RouteConfigurationError } from \"@x402/core/server\";\n\nexport type { RouteValidationError } from \"@x402/core/server\";\n\nexport { HonoAdapter } from \"./adapter\";\n","import { HTTPAdapter } from \"@x402/core/server\";\nimport { Context } from \"hono\";\n\n/**\n * Hono adapter implementation\n */\nexport class HonoAdapter implements HTTPAdapter {\n /**\n * Creates a new HonoAdapter instance.\n *\n * @param c - The Hono context object\n */\n constructor(private c: Context) {}\n\n /**\n * Gets a header value from the request.\n *\n * @param name - The header name\n * @returns The header value or undefined\n */\n getHeader(name: string): string | undefined {\n return this.c.req.header(name);\n }\n\n /**\n * Gets the HTTP method of the request.\n *\n * @returns The HTTP method\n */\n getMethod(): string {\n return this.c.req.method;\n }\n\n /**\n * Gets the path of the request.\n *\n * @returns The request path\n */\n getPath(): string {\n return this.c.req.path;\n }\n\n /**\n * Gets the full URL of the request.\n *\n * @returns The full request URL\n */\n getUrl(): string {\n return this.c.req.url;\n }\n\n /**\n * Gets the Accept header from the request.\n *\n * @returns The Accept header value or empty string\n */\n getAcceptHeader(): string {\n return this.c.req.header(\"Accept\") || \"\";\n }\n\n /**\n * Gets the User-Agent header from the request.\n *\n * @returns The User-Agent header value or empty string\n */\n getUserAgent(): string {\n return this.c.req.header(\"User-Agent\") || \"\";\n }\n\n /**\n * Gets all query parameters from the request URL.\n *\n * @returns Record of query parameter key-value pairs\n */\n getQueryParams(): Record<string, string | string[]> {\n const query = this.c.req.query();\n // Convert single values to match the interface\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(query)) {\n result[key] = value;\n }\n return result;\n }\n\n /**\n * Gets a specific query parameter by name.\n *\n * @param name - The query parameter name\n * @returns The query parameter value(s) or undefined\n */\n getQueryParam(name: string): string | string[] | undefined {\n return this.c.req.query(name);\n }\n\n /**\n * Gets the parsed request body.\n * Requires appropriate body parsing middleware.\n *\n * @returns The parsed request body\n */\n async getBody(): Promise<unknown> {\n try {\n return await this.c.req.json();\n } catch {\n return undefined;\n }\n }\n}\n"],"mappings":"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","names":["x402ResourceServer","x402HTTPResourceServer"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x402/hono",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
},
|
|
9
|
-
"author": "Coinbase Inc.",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"main": "./dist/cjs/index.js",
|
|
5
|
+
"module": "./dist/esm/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"keywords": [],
|
|
10
8
|
"license": "Apache-2.0",
|
|
11
|
-
"
|
|
9
|
+
"author": "Coinbase Inc.",
|
|
10
|
+
"repository": "https://github.com/coinbase/x402",
|
|
11
|
+
"description": "x402 Payment Protocol",
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@eslint/js": "^9.24.0",
|
|
14
|
+
"@types/node": "^22.13.4",
|
|
15
|
+
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
|
16
|
+
"@typescript-eslint/parser": "^8.29.1",
|
|
17
|
+
"eslint": "^9.24.0",
|
|
18
|
+
"eslint-plugin-import": "^2.31.0",
|
|
19
|
+
"eslint-plugin-jsdoc": "^50.6.9",
|
|
20
|
+
"eslint-plugin-prettier": "^5.2.6",
|
|
21
|
+
"hono": "^4.7.1",
|
|
22
|
+
"prettier": "3.5.2",
|
|
23
|
+
"tsup": "^8.4.0",
|
|
24
|
+
"tsx": "^4.19.2",
|
|
25
|
+
"typescript": "^5.7.3",
|
|
26
|
+
"vite": "^6.2.6",
|
|
27
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
28
|
+
"vitest": "^3.0.5"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"zod": "^3.24.2",
|
|
32
|
+
"@x402/extensions": "^2.0.0",
|
|
33
|
+
"@x402/core": "^2.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"hono": "^4.0.0",
|
|
37
|
+
"@x402/paywall": "2.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@x402/paywall": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./dist/esm/index.d.mts",
|
|
48
|
+
"default": "./dist/esm/index.mjs"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/cjs/index.d.ts",
|
|
52
|
+
"default": "./dist/cjs/index.js"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"dist"
|
|
58
|
+
],
|
|
59
|
+
"scripts": {
|
|
60
|
+
"start": "tsx --env-file=.env index.ts",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"test:watch": "vitest",
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"watch": "tsc --watch",
|
|
65
|
+
"format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
|
|
66
|
+
"format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
|
|
67
|
+
"lint": "eslint . --ext .ts --fix",
|
|
68
|
+
"lint:check": "eslint . --ext .ts"
|
|
69
|
+
}
|
|
12
70
|
}
|
package/index.js
DELETED