@okxweb3/app-x402-express 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -0
- package/dist/cjs/index.d.ts +176 -0
- package/dist/cjs/index.js +357 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +176 -0
- package/dist/esm/index.mjs +330 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# @okxweb3/app-x402-express
|
|
2
|
+
|
|
3
|
+
Express middleware for the x402 Payment Protocol. Adds x402 payment requirements to Express.js applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @okxweb3/app-x402-express
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import express from "express";
|
|
15
|
+
import { paymentMiddleware, x402ResourceServer } from "@okxweb3/app-x402-express";
|
|
16
|
+
import { ExactEvmScheme } from "@okxweb3/app-x402-evm/exact/server";
|
|
17
|
+
import { OKXFacilitatorClient } from "@okxweb3/app-x402-core";
|
|
18
|
+
|
|
19
|
+
const app = express();
|
|
20
|
+
|
|
21
|
+
const facilitatorClient = new OKXFacilitatorClient();
|
|
22
|
+
const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
23
|
+
.register("eip155:196", new ExactEvmScheme());
|
|
24
|
+
|
|
25
|
+
app.use(
|
|
26
|
+
paymentMiddleware(
|
|
27
|
+
{
|
|
28
|
+
"GET /protected-route": {
|
|
29
|
+
accepts: {
|
|
30
|
+
scheme: "exact",
|
|
31
|
+
price: "$0.10",
|
|
32
|
+
network: "eip155:196",
|
|
33
|
+
payTo: "0xYourAddress",
|
|
34
|
+
},
|
|
35
|
+
description: "Access to premium content",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
resourceServer,
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
app.get("/protected-route", (req, res) => {
|
|
43
|
+
res.json({ message: "Premium content" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
app.listen(3000);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### paymentMiddleware
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
function paymentMiddleware(
|
|
55
|
+
routes: RoutesConfig,
|
|
56
|
+
server: x402ResourceServer,
|
|
57
|
+
paywallConfig?: PaywallConfig,
|
|
58
|
+
paywall?: PaywallProvider,
|
|
59
|
+
syncFacilitatorOnStart?: boolean,
|
|
60
|
+
): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Creates Express middleware that:
|
|
64
|
+
|
|
65
|
+
1. Checks if the incoming request matches a protected route
|
|
66
|
+
2. Validates payment headers if required
|
|
67
|
+
3. Returns payment instructions (402 status) if payment is missing or invalid
|
|
68
|
+
4. Processes the request if payment is valid
|
|
69
|
+
5. Handles settlement after successful response
|
|
70
|
+
|
|
71
|
+
#### Parameters
|
|
72
|
+
|
|
73
|
+
| Parameter | Required | Description |
|
|
74
|
+
|-----------|----------|-------------|
|
|
75
|
+
| `routes` | Yes | Route configurations for protected endpoints |
|
|
76
|
+
| `server` | Yes | Pre-configured `x402ResourceServer` instance |
|
|
77
|
+
| `paywallConfig` | No | Configuration for the built-in paywall UI shown to browser visitors (`Accept: text/html` + `Mozilla` UA). Ignored for API/SDK clients. |
|
|
78
|
+
| `paywall` | No | Custom `PaywallProvider` that overrides the default HTML generator. Only used for browser visitors. |
|
|
79
|
+
| `syncFacilitatorOnStart` | No | Whether to sync with facilitator on startup (default: `true`) |
|
|
80
|
+
|
|
81
|
+
### Paywall
|
|
82
|
+
|
|
83
|
+
When an unpaid request comes from a web browser (`Accept` header contains `text/html` **and** `User-Agent` contains `Mozilla`), the middleware returns an HTML paywall page instead of a JSON 402. API/SDK clients are unaffected — they continue to receive JSON 402 with the `PAYMENT-REQUIRED` header.
|
|
84
|
+
|
|
85
|
+
`paywallConfig` lets you brand the built-in paywall:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { paymentMiddleware, PaywallConfig } from "@okxweb3/app-x402-express";
|
|
89
|
+
|
|
90
|
+
const paywallConfig: PaywallConfig = {
|
|
91
|
+
appName: "My App",
|
|
92
|
+
appLogo: "https://example.com/logo.png",
|
|
93
|
+
currentUrl: "https://example.com/protected",
|
|
94
|
+
testnet: false,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
app.use(paymentMiddleware(routes, resourceServer, paywallConfig));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`paywall` lets you fully replace the HTML generator:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { paymentMiddleware, PaywallProvider } from "@okxweb3/app-x402-express";
|
|
104
|
+
|
|
105
|
+
const customPaywall: PaywallProvider = {
|
|
106
|
+
generateHtml(paymentRequired, config) {
|
|
107
|
+
return `<!DOCTYPE html><html>... your UI ...</html>`;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
app.use(paymentMiddleware(routes, resourceServer, undefined, customPaywall));
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
You can also set a per-route HTML template via `RouteConfig.customPaywallHtml`, which takes precedence over both `paywall` and the default generator.
|
|
115
|
+
|
|
116
|
+
If you don't need a browser paywall (machine-to-machine APIs only), leave both `paywallConfig` and `paywall` as `undefined` — the paywall code path is never reached for non-browser clients.
|
|
117
|
+
|
|
118
|
+
### setSettlementOverrides
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
function setSettlementOverrides(res: Response, overrides: SettlementOverrides): void;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Set settlement overrides on the response for partial settlement.
|
|
125
|
+
|
|
126
|
+
### Route Configuration
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const routes: RoutesConfig = {
|
|
130
|
+
"GET /api/protected": {
|
|
131
|
+
accepts: {
|
|
132
|
+
scheme: "exact",
|
|
133
|
+
price: "$0.10",
|
|
134
|
+
network: "eip155:196",
|
|
135
|
+
payTo: "0xYourAddress",
|
|
136
|
+
maxTimeoutSeconds: 60,
|
|
137
|
+
},
|
|
138
|
+
description: "Premium API access",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
app.use(paymentMiddleware(routes, resourceServer));
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Multiple Protected Routes
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
app.use(
|
|
149
|
+
paymentMiddleware(
|
|
150
|
+
{
|
|
151
|
+
"GET /api/premium/*": {
|
|
152
|
+
accepts: {
|
|
153
|
+
scheme: "exact",
|
|
154
|
+
price: "$1.00",
|
|
155
|
+
network: "eip155:196",
|
|
156
|
+
payTo: "0xYourAddress",
|
|
157
|
+
},
|
|
158
|
+
description: "Premium API access",
|
|
159
|
+
},
|
|
160
|
+
"GET /api/data": {
|
|
161
|
+
accepts: {
|
|
162
|
+
scheme: "exact",
|
|
163
|
+
price: "$0.50",
|
|
164
|
+
network: "eip155:196",
|
|
165
|
+
payTo: "0xYourAddress",
|
|
166
|
+
maxTimeoutSeconds: 120,
|
|
167
|
+
},
|
|
168
|
+
description: "Data endpoint access",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
resourceServer,
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
```
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { HTTPAdapter, RoutesConfig, x402ResourceServer, PaywallConfig, PaywallProvider, FacilitatorClient, x402HTTPResourceServer, SettlementOverrides } from '@okxweb3/app-x402-core/server';
|
|
2
|
+
export { PaywallConfig, PaywallProvider, RouteConfigurationError, RouteValidationError, SETTLEMENT_OVERRIDES_HEADER, SettlementOverrides, x402HTTPResourceServer, x402ResourceServer } from '@okxweb3/app-x402-core/server';
|
|
3
|
+
import { Network, SchemeNetworkServer } from '@okxweb3/app-x402-core/types';
|
|
4
|
+
export { Network, PaymentPayload, PaymentRequired, PaymentRequirements, SchemeNetworkServer } from '@okxweb3/app-x402-core/types';
|
|
5
|
+
import { Request, Response, NextFunction } from 'express';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Express adapter implementation
|
|
9
|
+
*/
|
|
10
|
+
declare class ExpressAdapter implements HTTPAdapter {
|
|
11
|
+
private req;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new ExpressAdapter instance.
|
|
14
|
+
*
|
|
15
|
+
* @param req - The Express request object
|
|
16
|
+
*/
|
|
17
|
+
constructor(req: Request);
|
|
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 express.json() or express.urlencoded() middleware.
|
|
71
|
+
*
|
|
72
|
+
* @returns The parsed request body
|
|
73
|
+
*/
|
|
74
|
+
getBody(): unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set settlement overrides on the response for partial settlement.
|
|
79
|
+
* The middleware will extract these before settlement and strip the header from the client response.
|
|
80
|
+
*
|
|
81
|
+
* @param res - Express response object
|
|
82
|
+
* @param overrides - Settlement overrides (e.g., { amount: "500" } for partial settlement)
|
|
83
|
+
*/
|
|
84
|
+
declare function setSettlementOverrides(res: Response, overrides: SettlementOverrides): void;
|
|
85
|
+
/**
|
|
86
|
+
* Configuration for registering a payment scheme with a specific network
|
|
87
|
+
*/
|
|
88
|
+
interface SchemeRegistration {
|
|
89
|
+
/**
|
|
90
|
+
* The network identifier (e.g., 'eip155:196', 'eip155:196')
|
|
91
|
+
*/
|
|
92
|
+
network: Network;
|
|
93
|
+
/**
|
|
94
|
+
* The scheme server implementation for this network
|
|
95
|
+
*/
|
|
96
|
+
server: SchemeNetworkServer;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Express payment middleware for x402 protocol (direct HTTP server instance).
|
|
100
|
+
*
|
|
101
|
+
* Use this when you need to configure HTTP-level hooks.
|
|
102
|
+
*
|
|
103
|
+
* @param httpServer - Pre-configured x402HTTPResourceServer instance
|
|
104
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
105
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
106
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
107
|
+
* @returns Express middleware handler
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* import { paymentMiddlewareFromHTTPServer, x402ResourceServer, x402HTTPResourceServer } from "@okxweb3/app-x402-express";
|
|
112
|
+
*
|
|
113
|
+
* const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
114
|
+
* .register(NETWORK, new ExactEvmScheme())
|
|
115
|
+
*
|
|
116
|
+
* const httpServer = new x402HTTPResourceServer(resourceServer, routes)
|
|
117
|
+
* .onProtectedRequest(requestHook);
|
|
118
|
+
*
|
|
119
|
+
* app.use(paymentMiddlewareFromHTTPServer(httpServer));
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function paymentMiddlewareFromHTTPServer(httpServer: x402HTTPResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Express payment middleware for x402 protocol (direct server instance).
|
|
125
|
+
*
|
|
126
|
+
* Use this when you want to pass a pre-configured x402ResourceServer instance.
|
|
127
|
+
* This provides more flexibility for testing, custom configuration, and reusing
|
|
128
|
+
* server instances across multiple middlewares.
|
|
129
|
+
*
|
|
130
|
+
* @param routes - Route configurations for protected endpoints
|
|
131
|
+
* @param server - Pre-configured x402ResourceServer instance
|
|
132
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
133
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
134
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
135
|
+
* @returns Express middleware handler
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* import { paymentMiddleware } from "@okxweb3/app-x402-express";
|
|
140
|
+
*
|
|
141
|
+
* const server = new x402ResourceServer(myFacilitatorClient)
|
|
142
|
+
* .register(NETWORK, new ExactEvmScheme());
|
|
143
|
+
*
|
|
144
|
+
* app.use(paymentMiddleware(routes, server, paywallConfig));
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Express payment middleware for x402 protocol (configuration-based).
|
|
150
|
+
*
|
|
151
|
+
* Use this when you want to quickly set up middleware with simple configuration.
|
|
152
|
+
* This function creates and configures the x402ResourceServer internally.
|
|
153
|
+
*
|
|
154
|
+
* @param routes - Route configurations for protected endpoints
|
|
155
|
+
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
156
|
+
* @param schemes - Optional array of scheme registrations for server-side payment processing
|
|
157
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
158
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
159
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
160
|
+
* @returns Express middleware handler
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* import { paymentMiddlewareFromConfig } from "@okxweb3/app-x402-express";
|
|
165
|
+
*
|
|
166
|
+
* app.use(paymentMiddlewareFromConfig(
|
|
167
|
+
* routes,
|
|
168
|
+
* myFacilitatorClient,
|
|
169
|
+
* [{ network: "eip155:196", server: evmSchemeServer }],
|
|
170
|
+
* paywallConfig
|
|
171
|
+
* ));
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare function paymentMiddlewareFromConfig(routes: RoutesConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
175
|
+
|
|
176
|
+
export { ExpressAdapter, type SchemeRegistration, paymentMiddleware, paymentMiddlewareFromConfig, paymentMiddlewareFromHTTPServer, setSettlementOverrides };
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
ExpressAdapter: () => ExpressAdapter,
|
|
24
|
+
RouteConfigurationError: () => import_server3.RouteConfigurationError,
|
|
25
|
+
SETTLEMENT_OVERRIDES_HEADER: () => import_server3.SETTLEMENT_OVERRIDES_HEADER,
|
|
26
|
+
paymentMiddleware: () => paymentMiddleware,
|
|
27
|
+
paymentMiddlewareFromConfig: () => paymentMiddlewareFromConfig,
|
|
28
|
+
paymentMiddlewareFromHTTPServer: () => paymentMiddlewareFromHTTPServer,
|
|
29
|
+
setSettlementOverrides: () => setSettlementOverrides,
|
|
30
|
+
x402HTTPResourceServer: () => import_server2.x402HTTPResourceServer,
|
|
31
|
+
x402ResourceServer: () => import_server2.x402ResourceServer
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(src_exports);
|
|
34
|
+
var import_server = require("@okxweb3/app-x402-core/server");
|
|
35
|
+
|
|
36
|
+
// src/adapter.ts
|
|
37
|
+
var ExpressAdapter = class {
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new ExpressAdapter instance.
|
|
40
|
+
*
|
|
41
|
+
* @param req - The Express request object
|
|
42
|
+
*/
|
|
43
|
+
constructor(req) {
|
|
44
|
+
this.req = req;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Gets a header value from the request.
|
|
48
|
+
*
|
|
49
|
+
* @param name - The header name
|
|
50
|
+
* @returns The header value or undefined
|
|
51
|
+
*/
|
|
52
|
+
getHeader(name) {
|
|
53
|
+
const value = this.req.header(name);
|
|
54
|
+
return Array.isArray(value) ? value[0] : value;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Gets the HTTP method of the request.
|
|
58
|
+
*
|
|
59
|
+
* @returns The HTTP method
|
|
60
|
+
*/
|
|
61
|
+
getMethod() {
|
|
62
|
+
return this.req.method;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Gets the path of the request.
|
|
66
|
+
*
|
|
67
|
+
* @returns The request path
|
|
68
|
+
*/
|
|
69
|
+
getPath() {
|
|
70
|
+
return this.req.path;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Gets the full URL of the request.
|
|
74
|
+
*
|
|
75
|
+
* @returns The full request URL
|
|
76
|
+
*/
|
|
77
|
+
getUrl() {
|
|
78
|
+
return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Gets the Accept header from the request.
|
|
82
|
+
*
|
|
83
|
+
* @returns The Accept header value or empty string
|
|
84
|
+
*/
|
|
85
|
+
getAcceptHeader() {
|
|
86
|
+
return this.req.header("Accept") || "";
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Gets the User-Agent header from the request.
|
|
90
|
+
*
|
|
91
|
+
* @returns The User-Agent header value or empty string
|
|
92
|
+
*/
|
|
93
|
+
getUserAgent() {
|
|
94
|
+
return this.req.header("User-Agent") || "";
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Gets all query parameters from the request URL.
|
|
98
|
+
*
|
|
99
|
+
* @returns Record of query parameter key-value pairs
|
|
100
|
+
*/
|
|
101
|
+
getQueryParams() {
|
|
102
|
+
return this.req.query;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Gets a specific query parameter by name.
|
|
106
|
+
*
|
|
107
|
+
* @param name - The query parameter name
|
|
108
|
+
* @returns The query parameter value(s) or undefined
|
|
109
|
+
*/
|
|
110
|
+
getQueryParam(name) {
|
|
111
|
+
const value = this.req.query[name];
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Gets the parsed request body.
|
|
116
|
+
* Requires express.json() or express.urlencoded() middleware.
|
|
117
|
+
*
|
|
118
|
+
* @returns The parsed request body
|
|
119
|
+
*/
|
|
120
|
+
getBody() {
|
|
121
|
+
return this.req.body;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/index.ts
|
|
126
|
+
var import_server2 = require("@okxweb3/app-x402-core/server");
|
|
127
|
+
var import_server3 = require("@okxweb3/app-x402-core/server");
|
|
128
|
+
function setSettlementOverrides(res, overrides) {
|
|
129
|
+
res.setHeader(import_server.SETTLEMENT_OVERRIDES_HEADER, JSON.stringify(overrides));
|
|
130
|
+
}
|
|
131
|
+
function sendFacilitatorError(res, error) {
|
|
132
|
+
res.status(502).json({ error: error.message });
|
|
133
|
+
}
|
|
134
|
+
function paymentMiddlewareFromHTTPServer(httpServer, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
135
|
+
if (paywall) {
|
|
136
|
+
httpServer.registerPaywallProvider(paywall);
|
|
137
|
+
}
|
|
138
|
+
let initPromise = syncFacilitatorOnStart ? httpServer.initialize() : null;
|
|
139
|
+
let isInitialized = false;
|
|
140
|
+
async function initializeHttpServer() {
|
|
141
|
+
if (!syncFacilitatorOnStart || isInitialized) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!initPromise) {
|
|
145
|
+
initPromise = httpServer.initialize();
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
await initPromise;
|
|
149
|
+
isInitialized = true;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
initPromise = null;
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return async (req, res, next) => {
|
|
156
|
+
const adapter = new ExpressAdapter(req);
|
|
157
|
+
const context = {
|
|
158
|
+
adapter,
|
|
159
|
+
path: req.path,
|
|
160
|
+
method: req.method,
|
|
161
|
+
paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
|
|
162
|
+
};
|
|
163
|
+
if (!httpServer.requiresPayment(context)) {
|
|
164
|
+
return next();
|
|
165
|
+
}
|
|
166
|
+
if (syncFacilitatorOnStart && !isInitialized) {
|
|
167
|
+
try {
|
|
168
|
+
await initializeHttpServer();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const facilitatorError = (0, import_server.getFacilitatorResponseError)(error);
|
|
171
|
+
if (facilitatorError) {
|
|
172
|
+
sendFacilitatorError(res, facilitatorError);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
return next(error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let result;
|
|
179
|
+
try {
|
|
180
|
+
result = await httpServer.processHTTPRequest(context, paywallConfig);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (error instanceof import_server.FacilitatorResponseError) {
|
|
183
|
+
sendFacilitatorError(res, error);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
return next(error);
|
|
187
|
+
}
|
|
188
|
+
switch (result.type) {
|
|
189
|
+
case "no-payment-required":
|
|
190
|
+
return next();
|
|
191
|
+
case "payment-error":
|
|
192
|
+
const { response } = result;
|
|
193
|
+
res.status(response.status);
|
|
194
|
+
Object.entries(response.headers).forEach(([key, value]) => {
|
|
195
|
+
res.setHeader(key, value);
|
|
196
|
+
});
|
|
197
|
+
if (response.isHtml) {
|
|
198
|
+
res.send(response.body);
|
|
199
|
+
} else {
|
|
200
|
+
res.json(response.body || {});
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
case "payment-verified":
|
|
204
|
+
const { paymentPayload, paymentRequirements, declaredExtensions } = result;
|
|
205
|
+
const originalWriteHead = res.writeHead.bind(res);
|
|
206
|
+
const originalWrite = res.write.bind(res);
|
|
207
|
+
const originalEnd = res.end.bind(res);
|
|
208
|
+
const originalFlushHeaders = res.flushHeaders.bind(res);
|
|
209
|
+
let bufferedCalls = [];
|
|
210
|
+
let settled = false;
|
|
211
|
+
let endCalled;
|
|
212
|
+
const endPromise = new Promise((resolve) => {
|
|
213
|
+
endCalled = resolve;
|
|
214
|
+
});
|
|
215
|
+
res.writeHead = function(...args) {
|
|
216
|
+
if (!settled) {
|
|
217
|
+
bufferedCalls.push(["writeHead", args]);
|
|
218
|
+
return res;
|
|
219
|
+
}
|
|
220
|
+
return originalWriteHead(...args);
|
|
221
|
+
};
|
|
222
|
+
res.write = function(...args) {
|
|
223
|
+
if (!settled) {
|
|
224
|
+
bufferedCalls.push(["write", args]);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
return originalWrite(...args);
|
|
228
|
+
};
|
|
229
|
+
res.end = function(...args) {
|
|
230
|
+
if (!settled) {
|
|
231
|
+
bufferedCalls.push(["end", args]);
|
|
232
|
+
endCalled();
|
|
233
|
+
return res;
|
|
234
|
+
}
|
|
235
|
+
return originalEnd(...args);
|
|
236
|
+
};
|
|
237
|
+
res.flushHeaders = function() {
|
|
238
|
+
if (!settled) {
|
|
239
|
+
bufferedCalls.push(["flushHeaders", []]);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
return originalFlushHeaders();
|
|
243
|
+
};
|
|
244
|
+
next();
|
|
245
|
+
await endPromise;
|
|
246
|
+
if (res.statusCode >= 400) {
|
|
247
|
+
settled = true;
|
|
248
|
+
res.writeHead = originalWriteHead;
|
|
249
|
+
res.write = originalWrite;
|
|
250
|
+
res.end = originalEnd;
|
|
251
|
+
res.flushHeaders = originalFlushHeaders;
|
|
252
|
+
for (const [method, args] of bufferedCalls) {
|
|
253
|
+
if (method === "writeHead")
|
|
254
|
+
originalWriteHead(...args);
|
|
255
|
+
else if (method === "write")
|
|
256
|
+
originalWrite(...args);
|
|
257
|
+
else if (method === "end") originalEnd(...args);
|
|
258
|
+
else if (method === "flushHeaders") originalFlushHeaders();
|
|
259
|
+
}
|
|
260
|
+
bufferedCalls = [];
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const responseBody = Buffer.concat(
|
|
265
|
+
bufferedCalls.flatMap(
|
|
266
|
+
([m, args]) => (m === "write" || m === "end") && args[0] ? [Buffer.from(args[0])] : []
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
const overridesHeaderValue = res.getHeader(import_server.SETTLEMENT_OVERRIDES_HEADER);
|
|
270
|
+
const responseHeaders = {};
|
|
271
|
+
if (overridesHeaderValue) {
|
|
272
|
+
responseHeaders[import_server.SETTLEMENT_OVERRIDES_HEADER] = String(overridesHeaderValue);
|
|
273
|
+
res.removeHeader(import_server.SETTLEMENT_OVERRIDES_HEADER);
|
|
274
|
+
}
|
|
275
|
+
const settleResult = await httpServer.processSettlement(
|
|
276
|
+
paymentPayload,
|
|
277
|
+
paymentRequirements,
|
|
278
|
+
declaredExtensions,
|
|
279
|
+
{ request: context, responseBody, responseHeaders }
|
|
280
|
+
);
|
|
281
|
+
if (!settleResult.success) {
|
|
282
|
+
bufferedCalls = [];
|
|
283
|
+
const { response: response2 } = settleResult;
|
|
284
|
+
Object.entries(response2.headers).forEach(([key, value]) => {
|
|
285
|
+
res.setHeader(key, value);
|
|
286
|
+
});
|
|
287
|
+
if (response2.isHtml) {
|
|
288
|
+
res.status(response2.status).send(response2.body);
|
|
289
|
+
} else {
|
|
290
|
+
res.status(response2.status).json(response2.body ?? {});
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
Object.entries(settleResult.headers).forEach(([key, value]) => {
|
|
295
|
+
res.setHeader(key, value);
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (error instanceof import_server.FacilitatorResponseError) {
|
|
299
|
+
bufferedCalls = [];
|
|
300
|
+
sendFacilitatorError(res, error);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.error(error);
|
|
304
|
+
bufferedCalls = [];
|
|
305
|
+
res.status(402).json({});
|
|
306
|
+
return;
|
|
307
|
+
} finally {
|
|
308
|
+
settled = true;
|
|
309
|
+
res.writeHead = originalWriteHead;
|
|
310
|
+
res.write = originalWrite;
|
|
311
|
+
res.end = originalEnd;
|
|
312
|
+
res.flushHeaders = originalFlushHeaders;
|
|
313
|
+
for (const [method, args] of bufferedCalls) {
|
|
314
|
+
if (method === "writeHead")
|
|
315
|
+
originalWriteHead(...args);
|
|
316
|
+
else if (method === "write")
|
|
317
|
+
originalWrite(...args);
|
|
318
|
+
else if (method === "end") originalEnd(...args);
|
|
319
|
+
else if (method === "flushHeaders") originalFlushHeaders();
|
|
320
|
+
}
|
|
321
|
+
bufferedCalls = [];
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function paymentMiddleware(routes, server, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
328
|
+
const httpServer = new import_server.x402HTTPResourceServer(server, routes);
|
|
329
|
+
return paymentMiddlewareFromHTTPServer(
|
|
330
|
+
httpServer,
|
|
331
|
+
paywallConfig,
|
|
332
|
+
paywall,
|
|
333
|
+
syncFacilitatorOnStart
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function paymentMiddlewareFromConfig(routes, facilitatorClients, schemes, paywallConfig, paywall, syncFacilitatorOnStart = true) {
|
|
337
|
+
const ResourceServer = new import_server.x402ResourceServer(facilitatorClients);
|
|
338
|
+
if (schemes) {
|
|
339
|
+
schemes.forEach(({ network, server: schemeServer }) => {
|
|
340
|
+
ResourceServer.register(network, schemeServer);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);
|
|
344
|
+
}
|
|
345
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
346
|
+
0 && (module.exports = {
|
|
347
|
+
ExpressAdapter,
|
|
348
|
+
RouteConfigurationError,
|
|
349
|
+
SETTLEMENT_OVERRIDES_HEADER,
|
|
350
|
+
paymentMiddleware,
|
|
351
|
+
paymentMiddlewareFromConfig,
|
|
352
|
+
paymentMiddlewareFromHTTPServer,
|
|
353
|
+
setSettlementOverrides,
|
|
354
|
+
x402HTTPResourceServer,
|
|
355
|
+
x402ResourceServer
|
|
356
|
+
});
|
|
357
|
+
//# sourceMappingURL=index.js.map
|