x402-express-mantle 1.0.0 → 1.0.3
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 +52 -30
- package/lib/cjs/index.d.ts +119 -81
- package/lib/cjs/index.js +3 -3
- package/lib/esm/index.d.mts +5 -5
- package/lib/esm/index.mjs +3 -3
- package/package.json +17 -5
- package/lib/cjs/client/index.d.ts +0 -243
- package/lib/cjs/client/index.js +0 -413
- package/lib/cjs/client/index.js.map +0 -1
- package/lib/cjs/exact/client/index.d.ts +0 -37
- package/lib/cjs/exact/client/index.js +0 -281
- package/lib/cjs/exact/client/index.js.map +0 -1
- package/lib/cjs/exact/facilitator/index.d.ts +0 -110
- package/lib/cjs/exact/facilitator/index.js +0 -714
- package/lib/cjs/exact/facilitator/index.js.map +0 -1
- package/lib/cjs/exact/server/index.d.ts +0 -87
- package/lib/cjs/exact/server/index.js +0 -209
- package/lib/cjs/exact/server/index.js.map +0 -1
- package/lib/cjs/exact/v1/client/index.d.ts +0 -33
- package/lib/cjs/exact/v1/client/index.js +0 -169
- package/lib/cjs/exact/v1/client/index.js.map +0 -1
- package/lib/cjs/exact/v1/facilitator/index.d.ts +0 -71
- package/lib/cjs/exact/v1/facilitator/index.js +0 -384
- package/lib/cjs/exact/v1/facilitator/index.js.map +0 -1
- package/lib/cjs/facilitator/index.d.ts +0 -192
- package/lib/cjs/facilitator/index.js +0 -398
- package/lib/cjs/facilitator/index.js.map +0 -1
- package/lib/cjs/http/index.d.ts +0 -52
- package/lib/cjs/http/index.js +0 -827
- package/lib/cjs/http/index.js.map +0 -1
- package/lib/cjs/mechanisms-CzuGzYsS.d.ts +0 -270
- package/lib/cjs/scheme-MoBRXFM8.d.ts +0 -29
- package/lib/cjs/server/index.d.ts +0 -2
- package/lib/cjs/server/index.js +0 -1305
- package/lib/cjs/server/index.js.map +0 -1
- package/lib/cjs/signer-5OVDxViv.d.ts +0 -79
- package/lib/cjs/signer-BMkbhFYE.d.ts +0 -123
- package/lib/cjs/types/index.d.ts +0 -1
- package/lib/cjs/types/index.js +0 -66
- package/lib/cjs/types/index.js.map +0 -1
- package/lib/cjs/types/v1/index.d.ts +0 -1
- package/lib/cjs/types/v1/index.js +0 -19
- package/lib/cjs/types/v1/index.js.map +0 -1
- package/lib/cjs/utils/index.d.ts +0 -48
- package/lib/cjs/utils/index.js +0 -116
- package/lib/cjs/utils/index.js.map +0 -1
- package/lib/cjs/v1/index.d.ts +0 -12
- package/lib/cjs/v1/index.js +0 -180
- package/lib/cjs/v1/index.js.map +0 -1
- package/lib/cjs/x402HTTPResourceServer-D1YtlH_r.d.ts +0 -719
- package/lib/esm/chunk-3CEIVWNN.mjs +0 -339
- package/lib/esm/chunk-3CEIVWNN.mjs.map +0 -1
- package/lib/esm/chunk-BJTO5JO5.mjs +0 -11
- package/lib/esm/chunk-BJTO5JO5.mjs.map +0 -1
- package/lib/esm/chunk-EEA7DKZI.mjs +0 -111
- package/lib/esm/chunk-EEA7DKZI.mjs.map +0 -1
- package/lib/esm/chunk-FOUXRQAV.mjs +0 -88
- package/lib/esm/chunk-FOUXRQAV.mjs.map +0 -1
- package/lib/esm/chunk-IKSTWKEM.mjs +0 -157
- package/lib/esm/chunk-IKSTWKEM.mjs.map +0 -1
- package/lib/esm/chunk-JYZWCLMP.mjs +0 -305
- package/lib/esm/chunk-JYZWCLMP.mjs.map +0 -1
- package/lib/esm/chunk-PNSAJQCF.mjs +0 -108
- package/lib/esm/chunk-PNSAJQCF.mjs.map +0 -1
- package/lib/esm/chunk-PSA4YVU2.mjs +0 -92
- package/lib/esm/chunk-PSA4YVU2.mjs.map +0 -1
- package/lib/esm/chunk-QLXM7BIB.mjs +0 -23
- package/lib/esm/chunk-QLXM7BIB.mjs.map +0 -1
- package/lib/esm/chunk-TDLQZ6MP.mjs +0 -86
- package/lib/esm/chunk-TDLQZ6MP.mjs.map +0 -1
- package/lib/esm/chunk-VE37GDG2.mjs +0 -7
- package/lib/esm/chunk-VE37GDG2.mjs.map +0 -1
- package/lib/esm/chunk-WWACQNRQ.mjs +0 -7
- package/lib/esm/chunk-WWACQNRQ.mjs.map +0 -1
- package/lib/esm/chunk-X4W4S5RB.mjs +0 -39
- package/lib/esm/chunk-X4W4S5RB.mjs.map +0 -1
- package/lib/esm/chunk-Z4QX3O5V.mjs +0 -748
- package/lib/esm/chunk-Z4QX3O5V.mjs.map +0 -1
- package/lib/esm/chunk-ZYXTTU74.mjs +0 -88
- package/lib/esm/chunk-ZYXTTU74.mjs.map +0 -1
- package/lib/esm/client/index.d.mts +0 -243
- package/lib/esm/client/index.mjs +0 -260
- package/lib/esm/client/index.mjs.map +0 -1
- package/lib/esm/exact/client/index.d.mts +0 -37
- package/lib/esm/exact/client/index.mjs +0 -36
- package/lib/esm/exact/client/index.mjs.map +0 -1
- package/lib/esm/exact/facilitator/index.d.mts +0 -110
- package/lib/esm/exact/facilitator/index.mjs +0 -350
- package/lib/esm/exact/facilitator/index.mjs.map +0 -1
- package/lib/esm/exact/server/index.d.mts +0 -87
- package/lib/esm/exact/server/index.mjs +0 -129
- package/lib/esm/exact/server/index.mjs.map +0 -1
- package/lib/esm/exact/v1/client/index.d.mts +0 -33
- package/lib/esm/exact/v1/client/index.mjs +0 -8
- package/lib/esm/exact/v1/client/index.mjs.map +0 -1
- package/lib/esm/exact/v1/facilitator/index.d.mts +0 -71
- package/lib/esm/exact/v1/facilitator/index.mjs +0 -8
- package/lib/esm/exact/v1/facilitator/index.mjs.map +0 -1
- package/lib/esm/facilitator/index.d.mts +0 -192
- package/lib/esm/facilitator/index.mjs +0 -373
- package/lib/esm/facilitator/index.mjs.map +0 -1
- package/lib/esm/http/index.d.mts +0 -52
- package/lib/esm/http/index.mjs +0 -29
- package/lib/esm/http/index.mjs.map +0 -1
- package/lib/esm/mechanisms-CzuGzYsS.d.mts +0 -270
- package/lib/esm/scheme-fjF-9LhT.d.mts +0 -29
- package/lib/esm/server/index.d.mts +0 -2
- package/lib/esm/server/index.mjs +0 -563
- package/lib/esm/server/index.mjs.map +0 -1
- package/lib/esm/signer-5OVDxViv.d.mts +0 -79
- package/lib/esm/signer-BMkbhFYE.d.mts +0 -123
- package/lib/esm/types/index.d.mts +0 -1
- package/lib/esm/types/index.mjs +0 -10
- package/lib/esm/types/index.mjs.map +0 -1
- package/lib/esm/types/v1/index.d.mts +0 -1
- package/lib/esm/types/v1/index.mjs +0 -1
- package/lib/esm/types/v1/index.mjs.map +0 -1
- package/lib/esm/utils/index.d.mts +0 -48
- package/lib/esm/utils/index.mjs +0 -20
- package/lib/esm/utils/index.mjs.map +0 -1
- package/lib/esm/v1/index.d.mts +0 -12
- package/lib/esm/v1/index.mjs +0 -13
- package/lib/esm/v1/index.mjs.map +0 -1
- package/lib/esm/x402HTTPResourceServer-BIfIK5HS.d.mts +0 -719
- package/src/index.js +0 -4
package/lib/cjs/server/index.js
DELETED
|
@@ -1,1305 +0,0 @@
|
|
|
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/server/index.ts
|
|
21
|
-
var server_exports = {};
|
|
22
|
-
__export(server_exports, {
|
|
23
|
-
HTTPFacilitatorClient: () => HTTPFacilitatorClient,
|
|
24
|
-
RouteConfigurationError: () => RouteConfigurationError,
|
|
25
|
-
x402HTTPResourceServer: () => x402HTTPResourceServer,
|
|
26
|
-
x402ResourceServer: () => x402ResourceServer
|
|
27
|
-
});
|
|
28
|
-
module.exports = __toCommonJS(server_exports);
|
|
29
|
-
|
|
30
|
-
// src/utils/index.ts
|
|
31
|
-
var findSchemesByNetwork = (map, network) => {
|
|
32
|
-
let implementationsByScheme = map.get(network);
|
|
33
|
-
if (!implementationsByScheme) {
|
|
34
|
-
for (const [registeredNetworkPattern, implementations] of map.entries()) {
|
|
35
|
-
const pattern = registeredNetworkPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
36
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
37
|
-
if (regex.test(network)) {
|
|
38
|
-
implementationsByScheme = implementations;
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return implementationsByScheme;
|
|
44
|
-
};
|
|
45
|
-
var findByNetworkAndScheme = (map, scheme, network) => {
|
|
46
|
-
return findSchemesByNetwork(map, network)?.get(scheme);
|
|
47
|
-
};
|
|
48
|
-
var Base64EncodedRegex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
49
|
-
function safeBase64Encode(data) {
|
|
50
|
-
if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") {
|
|
51
|
-
const bytes = new TextEncoder().encode(data);
|
|
52
|
-
const binaryString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
53
|
-
return globalThis.btoa(binaryString);
|
|
54
|
-
}
|
|
55
|
-
return Buffer.from(data, "utf8").toString("base64");
|
|
56
|
-
}
|
|
57
|
-
function safeBase64Decode(data) {
|
|
58
|
-
if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") {
|
|
59
|
-
const binaryString = globalThis.atob(data);
|
|
60
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
61
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
62
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
63
|
-
}
|
|
64
|
-
const decoder = new TextDecoder("utf-8");
|
|
65
|
-
return decoder.decode(bytes);
|
|
66
|
-
}
|
|
67
|
-
return Buffer.from(data, "base64").toString("utf-8");
|
|
68
|
-
}
|
|
69
|
-
function deepEqual(obj1, obj2) {
|
|
70
|
-
const normalize = (obj) => {
|
|
71
|
-
if (obj === null || obj === void 0) return JSON.stringify(obj);
|
|
72
|
-
if (typeof obj !== "object") return JSON.stringify(obj);
|
|
73
|
-
if (Array.isArray(obj)) {
|
|
74
|
-
return JSON.stringify(
|
|
75
|
-
obj.map(
|
|
76
|
-
(item) => typeof item === "object" && item !== null ? JSON.parse(normalize(item)) : item
|
|
77
|
-
)
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
const sorted = {};
|
|
81
|
-
Object.keys(obj).sort().forEach((key) => {
|
|
82
|
-
const value = obj[key];
|
|
83
|
-
sorted[key] = typeof value === "object" && value !== null ? JSON.parse(normalize(value)) : value;
|
|
84
|
-
});
|
|
85
|
-
return JSON.stringify(sorted);
|
|
86
|
-
};
|
|
87
|
-
try {
|
|
88
|
-
return normalize(obj1) === normalize(obj2);
|
|
89
|
-
} catch {
|
|
90
|
-
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// src/types/facilitator.ts
|
|
95
|
-
var VerifyError = class extends Error {
|
|
96
|
-
/**
|
|
97
|
-
* Creates a VerifyError from a failed verification response.
|
|
98
|
-
*
|
|
99
|
-
* @param statusCode - HTTP status code from the facilitator
|
|
100
|
-
* @param response - The verify response containing error details
|
|
101
|
-
*/
|
|
102
|
-
constructor(statusCode, response) {
|
|
103
|
-
super(`verification failed: ${response.invalidReason || "unknown reason"}`);
|
|
104
|
-
this.name = "VerifyError";
|
|
105
|
-
this.statusCode = statusCode;
|
|
106
|
-
this.invalidReason = response.invalidReason;
|
|
107
|
-
this.payer = response.payer;
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
var SettleError = class extends Error {
|
|
111
|
-
/**
|
|
112
|
-
* Creates a SettleError from a failed settlement response.
|
|
113
|
-
*
|
|
114
|
-
* @param statusCode - HTTP status code from the facilitator
|
|
115
|
-
* @param response - The settle response containing error details
|
|
116
|
-
*/
|
|
117
|
-
constructor(statusCode, response) {
|
|
118
|
-
super(`settlement failed: ${response.errorReason || "unknown reason"}`);
|
|
119
|
-
this.name = "SettleError";
|
|
120
|
-
this.statusCode = statusCode;
|
|
121
|
-
this.errorReason = response.errorReason;
|
|
122
|
-
this.payer = response.payer;
|
|
123
|
-
this.transaction = response.transaction;
|
|
124
|
-
this.network = response.network;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// src/http/httpFacilitatorClient.ts
|
|
129
|
-
var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
|
|
130
|
-
var HTTPFacilitatorClient = class {
|
|
131
|
-
/**
|
|
132
|
-
* Creates a new HTTPFacilitatorClient instance.
|
|
133
|
-
*
|
|
134
|
-
* @param config - Configuration options for the facilitator client
|
|
135
|
-
*/
|
|
136
|
-
constructor(config) {
|
|
137
|
-
this.url = config?.url || DEFAULT_FACILITATOR_URL;
|
|
138
|
-
this._createAuthHeaders = config?.createAuthHeaders;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Verify a payment with the facilitator
|
|
142
|
-
*
|
|
143
|
-
* @param paymentPayload - The payment to verify
|
|
144
|
-
* @param paymentRequirements - The requirements to verify against
|
|
145
|
-
* @returns Verification response
|
|
146
|
-
*/
|
|
147
|
-
async verify(paymentPayload, paymentRequirements) {
|
|
148
|
-
let headers = {
|
|
149
|
-
"Content-Type": "application/json"
|
|
150
|
-
};
|
|
151
|
-
if (this._createAuthHeaders) {
|
|
152
|
-
const authHeaders = await this.createAuthHeaders("verify");
|
|
153
|
-
headers = { ...headers, ...authHeaders.headers };
|
|
154
|
-
}
|
|
155
|
-
const response = await fetch(`${this.url}/verify`, {
|
|
156
|
-
method: "POST",
|
|
157
|
-
headers,
|
|
158
|
-
body: JSON.stringify({
|
|
159
|
-
x402Version: paymentPayload.x402Version,
|
|
160
|
-
paymentPayload: this.toJsonSafe(paymentPayload),
|
|
161
|
-
paymentRequirements: this.toJsonSafe(paymentRequirements)
|
|
162
|
-
})
|
|
163
|
-
});
|
|
164
|
-
const data = await response.json();
|
|
165
|
-
if (typeof data === "object" && data !== null && "isValid" in data) {
|
|
166
|
-
const verifyResponse = data;
|
|
167
|
-
if (!response.ok) {
|
|
168
|
-
throw new VerifyError(response.status, verifyResponse);
|
|
169
|
-
}
|
|
170
|
-
return verifyResponse;
|
|
171
|
-
}
|
|
172
|
-
throw new Error(`Facilitator verify failed (${response.status}): ${JSON.stringify(data)}`);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Settle a payment with the facilitator
|
|
176
|
-
*
|
|
177
|
-
* @param paymentPayload - The payment to settle
|
|
178
|
-
* @param paymentRequirements - The requirements for settlement
|
|
179
|
-
* @returns Settlement response
|
|
180
|
-
*/
|
|
181
|
-
async settle(paymentPayload, paymentRequirements) {
|
|
182
|
-
let headers = {
|
|
183
|
-
"Content-Type": "application/json"
|
|
184
|
-
};
|
|
185
|
-
if (this._createAuthHeaders) {
|
|
186
|
-
const authHeaders = await this.createAuthHeaders("settle");
|
|
187
|
-
headers = { ...headers, ...authHeaders.headers };
|
|
188
|
-
}
|
|
189
|
-
const response = await fetch(`${this.url}/settle`, {
|
|
190
|
-
method: "POST",
|
|
191
|
-
headers,
|
|
192
|
-
body: JSON.stringify({
|
|
193
|
-
x402Version: paymentPayload.x402Version,
|
|
194
|
-
paymentPayload: this.toJsonSafe(paymentPayload),
|
|
195
|
-
paymentRequirements: this.toJsonSafe(paymentRequirements)
|
|
196
|
-
})
|
|
197
|
-
});
|
|
198
|
-
const data = await response.json();
|
|
199
|
-
if (typeof data === "object" && data !== null && "success" in data) {
|
|
200
|
-
const settleResponse = data;
|
|
201
|
-
if (!response.ok) {
|
|
202
|
-
throw new SettleError(response.status, settleResponse);
|
|
203
|
-
}
|
|
204
|
-
return settleResponse;
|
|
205
|
-
}
|
|
206
|
-
throw new Error(`Facilitator settle failed (${response.status}): ${JSON.stringify(data)}`);
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Get supported payment kinds and extensions from the facilitator
|
|
210
|
-
*
|
|
211
|
-
* @returns Supported payment kinds and extensions
|
|
212
|
-
*/
|
|
213
|
-
async getSupported() {
|
|
214
|
-
let headers = {
|
|
215
|
-
"Content-Type": "application/json"
|
|
216
|
-
};
|
|
217
|
-
if (this._createAuthHeaders) {
|
|
218
|
-
const authHeaders = await this.createAuthHeaders("supported");
|
|
219
|
-
headers = { ...headers, ...authHeaders.headers };
|
|
220
|
-
}
|
|
221
|
-
const response = await fetch(`${this.url}/supported`, {
|
|
222
|
-
method: "GET",
|
|
223
|
-
headers
|
|
224
|
-
});
|
|
225
|
-
if (!response.ok) {
|
|
226
|
-
const errorText = await response.text().catch(() => response.statusText);
|
|
227
|
-
throw new Error(`Facilitator getSupported failed (${response.status}): ${errorText}`);
|
|
228
|
-
}
|
|
229
|
-
return await response.json();
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Creates authentication headers for a specific path.
|
|
233
|
-
*
|
|
234
|
-
* @param path - The path to create authentication headers for (e.g., "verify", "settle", "supported")
|
|
235
|
-
* @returns An object containing the authentication headers for the specified path
|
|
236
|
-
*/
|
|
237
|
-
async createAuthHeaders(path) {
|
|
238
|
-
if (this._createAuthHeaders) {
|
|
239
|
-
const authHeaders = await this._createAuthHeaders();
|
|
240
|
-
return {
|
|
241
|
-
headers: authHeaders[path] ?? {}
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
headers: {}
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Helper to convert objects to JSON-safe format.
|
|
250
|
-
* Handles BigInt and other non-JSON types.
|
|
251
|
-
*
|
|
252
|
-
* @param obj - The object to convert
|
|
253
|
-
* @returns The JSON-safe representation of the object
|
|
254
|
-
*/
|
|
255
|
-
toJsonSafe(obj) {
|
|
256
|
-
return JSON.parse(
|
|
257
|
-
JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() : value)
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// src/index.ts
|
|
263
|
-
var x402Version = 2;
|
|
264
|
-
|
|
265
|
-
// src/server/x402ResourceServer.ts
|
|
266
|
-
var x402ResourceServer = class {
|
|
267
|
-
/**
|
|
268
|
-
* Creates a new x402ResourceServer instance.
|
|
269
|
-
*
|
|
270
|
-
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
271
|
-
*/
|
|
272
|
-
constructor(facilitatorClients) {
|
|
273
|
-
this.registeredServerSchemes = /* @__PURE__ */ new Map();
|
|
274
|
-
this.supportedResponsesMap = /* @__PURE__ */ new Map();
|
|
275
|
-
this.facilitatorClientsMap = /* @__PURE__ */ new Map();
|
|
276
|
-
this.registeredExtensions = /* @__PURE__ */ new Map();
|
|
277
|
-
this.beforeVerifyHooks = [];
|
|
278
|
-
this.afterVerifyHooks = [];
|
|
279
|
-
this.onVerifyFailureHooks = [];
|
|
280
|
-
this.beforeSettleHooks = [];
|
|
281
|
-
this.afterSettleHooks = [];
|
|
282
|
-
this.onSettleFailureHooks = [];
|
|
283
|
-
if (!facilitatorClients) {
|
|
284
|
-
this.facilitatorClients = [new HTTPFacilitatorClient()];
|
|
285
|
-
} else if (Array.isArray(facilitatorClients)) {
|
|
286
|
-
this.facilitatorClients = facilitatorClients.length > 0 ? facilitatorClients : [new HTTPFacilitatorClient()];
|
|
287
|
-
} else {
|
|
288
|
-
this.facilitatorClients = [facilitatorClients];
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Register a scheme/network server implementation.
|
|
293
|
-
*
|
|
294
|
-
* @param network - The network identifier
|
|
295
|
-
* @param server - The scheme/network server implementation
|
|
296
|
-
* @returns The x402ResourceServer instance for chaining
|
|
297
|
-
*/
|
|
298
|
-
register(network, server) {
|
|
299
|
-
if (!this.registeredServerSchemes.has(network)) {
|
|
300
|
-
this.registeredServerSchemes.set(network, /* @__PURE__ */ new Map());
|
|
301
|
-
}
|
|
302
|
-
const serverByScheme = this.registeredServerSchemes.get(network);
|
|
303
|
-
if (!serverByScheme.has(server.scheme)) {
|
|
304
|
-
serverByScheme.set(server.scheme, server);
|
|
305
|
-
}
|
|
306
|
-
return this;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Check if a scheme is registered for a given network.
|
|
310
|
-
*
|
|
311
|
-
* @param network - The network identifier
|
|
312
|
-
* @param scheme - The payment scheme name
|
|
313
|
-
* @returns True if the scheme is registered for the network, false otherwise
|
|
314
|
-
*/
|
|
315
|
-
hasRegisteredScheme(network, scheme) {
|
|
316
|
-
return !!findByNetworkAndScheme(this.registeredServerSchemes, scheme, network);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Registers a resource service extension that can enrich extension declarations.
|
|
320
|
-
*
|
|
321
|
-
* @param extension - The extension to register
|
|
322
|
-
* @returns The x402ResourceServer instance for chaining
|
|
323
|
-
*/
|
|
324
|
-
registerExtension(extension) {
|
|
325
|
-
this.registeredExtensions.set(extension.key, extension);
|
|
326
|
-
return this;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Enriches declared extensions using registered extension hooks.
|
|
330
|
-
*
|
|
331
|
-
* @param declaredExtensions - Extensions declared on the route
|
|
332
|
-
* @param transportContext - Transport-specific context (HTTP, A2A, MCP, etc.)
|
|
333
|
-
* @returns Enriched extensions map
|
|
334
|
-
*/
|
|
335
|
-
enrichExtensions(declaredExtensions, transportContext) {
|
|
336
|
-
const enriched = {};
|
|
337
|
-
for (const [key, declaration] of Object.entries(declaredExtensions)) {
|
|
338
|
-
const extension = this.registeredExtensions.get(key);
|
|
339
|
-
if (extension?.enrichDeclaration) {
|
|
340
|
-
enriched[key] = extension.enrichDeclaration(declaration, transportContext);
|
|
341
|
-
} else {
|
|
342
|
-
enriched[key] = declaration;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return enriched;
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Register a hook to execute before payment verification.
|
|
349
|
-
* Can abort verification by returning { abort: true, reason: string }
|
|
350
|
-
*
|
|
351
|
-
* @param hook - The hook function to register
|
|
352
|
-
* @returns The x402ResourceServer instance for chaining
|
|
353
|
-
*/
|
|
354
|
-
onBeforeVerify(hook) {
|
|
355
|
-
this.beforeVerifyHooks.push(hook);
|
|
356
|
-
return this;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Register a hook to execute after successful payment verification.
|
|
360
|
-
*
|
|
361
|
-
* @param hook - The hook function to register
|
|
362
|
-
* @returns The x402ResourceServer instance for chaining
|
|
363
|
-
*/
|
|
364
|
-
onAfterVerify(hook) {
|
|
365
|
-
this.afterVerifyHooks.push(hook);
|
|
366
|
-
return this;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Register a hook to execute when payment verification fails.
|
|
370
|
-
* Can recover from failure by returning { recovered: true, result: VerifyResponse }
|
|
371
|
-
*
|
|
372
|
-
* @param hook - The hook function to register
|
|
373
|
-
* @returns The x402ResourceServer instance for chaining
|
|
374
|
-
*/
|
|
375
|
-
onVerifyFailure(hook) {
|
|
376
|
-
this.onVerifyFailureHooks.push(hook);
|
|
377
|
-
return this;
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Register a hook to execute before payment settlement.
|
|
381
|
-
* Can abort settlement by returning { abort: true, reason: string }
|
|
382
|
-
*
|
|
383
|
-
* @param hook - The hook function to register
|
|
384
|
-
* @returns The x402ResourceServer instance for chaining
|
|
385
|
-
*/
|
|
386
|
-
onBeforeSettle(hook) {
|
|
387
|
-
this.beforeSettleHooks.push(hook);
|
|
388
|
-
return this;
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Register a hook to execute after successful payment settlement.
|
|
392
|
-
*
|
|
393
|
-
* @param hook - The hook function to register
|
|
394
|
-
* @returns The x402ResourceServer instance for chaining
|
|
395
|
-
*/
|
|
396
|
-
onAfterSettle(hook) {
|
|
397
|
-
this.afterSettleHooks.push(hook);
|
|
398
|
-
return this;
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Register a hook to execute when payment settlement fails.
|
|
402
|
-
* Can recover from failure by returning { recovered: true, result: SettleResponse }
|
|
403
|
-
*
|
|
404
|
-
* @param hook - The hook function to register
|
|
405
|
-
* @returns The x402ResourceServer instance for chaining
|
|
406
|
-
*/
|
|
407
|
-
onSettleFailure(hook) {
|
|
408
|
-
this.onSettleFailureHooks.push(hook);
|
|
409
|
-
return this;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Initialize by fetching supported kinds from all facilitators
|
|
413
|
-
* Creates mappings for supported responses and facilitator clients
|
|
414
|
-
* Earlier facilitators in the array get precedence
|
|
415
|
-
*/
|
|
416
|
-
async initialize() {
|
|
417
|
-
this.supportedResponsesMap.clear();
|
|
418
|
-
this.facilitatorClientsMap.clear();
|
|
419
|
-
for (const facilitatorClient of this.facilitatorClients) {
|
|
420
|
-
try {
|
|
421
|
-
const supported = await facilitatorClient.getSupported();
|
|
422
|
-
for (const kind of supported.kinds) {
|
|
423
|
-
const x402Version2 = kind.x402Version;
|
|
424
|
-
if (!this.supportedResponsesMap.has(x402Version2)) {
|
|
425
|
-
this.supportedResponsesMap.set(x402Version2, /* @__PURE__ */ new Map());
|
|
426
|
-
}
|
|
427
|
-
const responseVersionMap = this.supportedResponsesMap.get(x402Version2);
|
|
428
|
-
if (!this.facilitatorClientsMap.has(x402Version2)) {
|
|
429
|
-
this.facilitatorClientsMap.set(x402Version2, /* @__PURE__ */ new Map());
|
|
430
|
-
}
|
|
431
|
-
const clientVersionMap = this.facilitatorClientsMap.get(x402Version2);
|
|
432
|
-
if (!responseVersionMap.has(kind.network)) {
|
|
433
|
-
responseVersionMap.set(kind.network, /* @__PURE__ */ new Map());
|
|
434
|
-
}
|
|
435
|
-
const responseNetworkMap = responseVersionMap.get(kind.network);
|
|
436
|
-
if (!clientVersionMap.has(kind.network)) {
|
|
437
|
-
clientVersionMap.set(kind.network, /* @__PURE__ */ new Map());
|
|
438
|
-
}
|
|
439
|
-
const clientNetworkMap = clientVersionMap.get(kind.network);
|
|
440
|
-
if (!responseNetworkMap.has(kind.scheme)) {
|
|
441
|
-
responseNetworkMap.set(kind.scheme, supported);
|
|
442
|
-
clientNetworkMap.set(kind.scheme, facilitatorClient);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
} catch (error) {
|
|
446
|
-
console.warn(`Failed to fetch supported kinds from facilitator: ${error}`);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Get supported kind for a specific version, network, and scheme
|
|
452
|
-
*
|
|
453
|
-
* @param x402Version - The x402 version
|
|
454
|
-
* @param network - The network identifier
|
|
455
|
-
* @param scheme - The payment scheme
|
|
456
|
-
* @returns The supported kind or undefined if not found
|
|
457
|
-
*/
|
|
458
|
-
getSupportedKind(x402Version2, network, scheme) {
|
|
459
|
-
const versionMap = this.supportedResponsesMap.get(x402Version2);
|
|
460
|
-
if (!versionMap) return void 0;
|
|
461
|
-
const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
|
|
462
|
-
if (!supportedResponse) return void 0;
|
|
463
|
-
return supportedResponse.kinds.find(
|
|
464
|
-
(kind) => kind.x402Version === x402Version2 && kind.network === network && kind.scheme === scheme
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Get facilitator extensions for a specific version, network, and scheme
|
|
469
|
-
*
|
|
470
|
-
* @param x402Version - The x402 version
|
|
471
|
-
* @param network - The network identifier
|
|
472
|
-
* @param scheme - The payment scheme
|
|
473
|
-
* @returns The facilitator extensions or empty array if not found
|
|
474
|
-
*/
|
|
475
|
-
getFacilitatorExtensions(x402Version2, network, scheme) {
|
|
476
|
-
const versionMap = this.supportedResponsesMap.get(x402Version2);
|
|
477
|
-
if (!versionMap) return [];
|
|
478
|
-
const supportedResponse = findByNetworkAndScheme(versionMap, scheme, network);
|
|
479
|
-
return supportedResponse?.extensions || [];
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Build payment requirements for a protected resource
|
|
483
|
-
*
|
|
484
|
-
* @param resourceConfig - Configuration for the protected resource
|
|
485
|
-
* @returns Array of payment requirements
|
|
486
|
-
*/
|
|
487
|
-
async buildPaymentRequirements(resourceConfig) {
|
|
488
|
-
const requirements = [];
|
|
489
|
-
const scheme = resourceConfig.scheme;
|
|
490
|
-
const SchemeNetworkServer = findByNetworkAndScheme(
|
|
491
|
-
this.registeredServerSchemes,
|
|
492
|
-
scheme,
|
|
493
|
-
resourceConfig.network
|
|
494
|
-
);
|
|
495
|
-
if (!SchemeNetworkServer) {
|
|
496
|
-
console.warn(
|
|
497
|
-
`No server implementation registered for scheme: ${scheme}, network: ${resourceConfig.network}`
|
|
498
|
-
);
|
|
499
|
-
return requirements;
|
|
500
|
-
}
|
|
501
|
-
const supportedKind = this.getSupportedKind(
|
|
502
|
-
x402Version,
|
|
503
|
-
resourceConfig.network,
|
|
504
|
-
SchemeNetworkServer.scheme
|
|
505
|
-
);
|
|
506
|
-
if (!supportedKind) {
|
|
507
|
-
throw new Error(
|
|
508
|
-
`Facilitator does not support ${SchemeNetworkServer.scheme} on ${resourceConfig.network}. Make sure to call initialize() to fetch supported kinds from facilitators.`
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
const facilitatorExtensions = this.getFacilitatorExtensions(
|
|
512
|
-
x402Version,
|
|
513
|
-
resourceConfig.network,
|
|
514
|
-
SchemeNetworkServer.scheme
|
|
515
|
-
);
|
|
516
|
-
const parsedPrice = await SchemeNetworkServer.parsePrice(
|
|
517
|
-
resourceConfig.price,
|
|
518
|
-
resourceConfig.network
|
|
519
|
-
);
|
|
520
|
-
const baseRequirements = {
|
|
521
|
-
scheme: SchemeNetworkServer.scheme,
|
|
522
|
-
network: resourceConfig.network,
|
|
523
|
-
amount: parsedPrice.amount,
|
|
524
|
-
asset: parsedPrice.asset,
|
|
525
|
-
payTo: resourceConfig.payTo,
|
|
526
|
-
maxTimeoutSeconds: resourceConfig.maxTimeoutSeconds || 300,
|
|
527
|
-
// Default 5 minutes
|
|
528
|
-
extra: {
|
|
529
|
-
...parsedPrice.extra
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
const requirement = await SchemeNetworkServer.enhancePaymentRequirements(
|
|
533
|
-
baseRequirements,
|
|
534
|
-
{
|
|
535
|
-
...supportedKind,
|
|
536
|
-
x402Version
|
|
537
|
-
},
|
|
538
|
-
facilitatorExtensions
|
|
539
|
-
);
|
|
540
|
-
requirements.push(requirement);
|
|
541
|
-
return requirements;
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Build payment requirements from multiple payment options
|
|
545
|
-
* This method handles resolving dynamic payTo/price functions and builds requirements for each option
|
|
546
|
-
*
|
|
547
|
-
* @param paymentOptions - Array of payment options to convert
|
|
548
|
-
* @param context - HTTP request context for resolving dynamic functions
|
|
549
|
-
* @returns Array of payment requirements (one per option)
|
|
550
|
-
*/
|
|
551
|
-
async buildPaymentRequirementsFromOptions(paymentOptions, context) {
|
|
552
|
-
const allRequirements = [];
|
|
553
|
-
for (const option of paymentOptions) {
|
|
554
|
-
const resolvedPayTo = typeof option.payTo === "function" ? await option.payTo(context) : option.payTo;
|
|
555
|
-
const resolvedPrice = typeof option.price === "function" ? await option.price(context) : option.price;
|
|
556
|
-
const resourceConfig = {
|
|
557
|
-
scheme: option.scheme,
|
|
558
|
-
payTo: resolvedPayTo,
|
|
559
|
-
price: resolvedPrice,
|
|
560
|
-
network: option.network,
|
|
561
|
-
maxTimeoutSeconds: option.maxTimeoutSeconds
|
|
562
|
-
};
|
|
563
|
-
const requirements = await this.buildPaymentRequirements(resourceConfig);
|
|
564
|
-
allRequirements.push(...requirements);
|
|
565
|
-
}
|
|
566
|
-
return allRequirements;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Create a payment required response
|
|
570
|
-
*
|
|
571
|
-
* @param requirements - Payment requirements
|
|
572
|
-
* @param resourceInfo - Resource information
|
|
573
|
-
* @param error - Error message
|
|
574
|
-
* @param extensions - Optional extensions
|
|
575
|
-
* @returns Payment required response object
|
|
576
|
-
*/
|
|
577
|
-
createPaymentRequiredResponse(requirements, resourceInfo, error, extensions) {
|
|
578
|
-
const response = {
|
|
579
|
-
x402Version: 2,
|
|
580
|
-
error,
|
|
581
|
-
resource: resourceInfo,
|
|
582
|
-
accepts: requirements
|
|
583
|
-
};
|
|
584
|
-
if (extensions && Object.keys(extensions).length > 0) {
|
|
585
|
-
response.extensions = extensions;
|
|
586
|
-
}
|
|
587
|
-
return response;
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Verify a payment against requirements
|
|
591
|
-
*
|
|
592
|
-
* @param paymentPayload - The payment payload to verify
|
|
593
|
-
* @param requirements - The payment requirements
|
|
594
|
-
* @returns Verification response
|
|
595
|
-
*/
|
|
596
|
-
async verifyPayment(paymentPayload, requirements) {
|
|
597
|
-
const context = {
|
|
598
|
-
paymentPayload,
|
|
599
|
-
requirements
|
|
600
|
-
};
|
|
601
|
-
for (const hook of this.beforeVerifyHooks) {
|
|
602
|
-
const result = await hook(context);
|
|
603
|
-
if (result && "abort" in result && result.abort) {
|
|
604
|
-
return {
|
|
605
|
-
isValid: false,
|
|
606
|
-
invalidReason: result.reason
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
try {
|
|
611
|
-
const facilitatorClient = this.getFacilitatorClient(
|
|
612
|
-
paymentPayload.x402Version,
|
|
613
|
-
requirements.network,
|
|
614
|
-
requirements.scheme
|
|
615
|
-
);
|
|
616
|
-
let verifyResult;
|
|
617
|
-
if (!facilitatorClient) {
|
|
618
|
-
let lastError;
|
|
619
|
-
for (const client of this.facilitatorClients) {
|
|
620
|
-
try {
|
|
621
|
-
verifyResult = await client.verify(paymentPayload, requirements);
|
|
622
|
-
break;
|
|
623
|
-
} catch (error) {
|
|
624
|
-
lastError = error;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
if (!verifyResult) {
|
|
628
|
-
throw lastError || new Error(
|
|
629
|
-
`No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
} else {
|
|
633
|
-
verifyResult = await facilitatorClient.verify(paymentPayload, requirements);
|
|
634
|
-
}
|
|
635
|
-
const resultContext = {
|
|
636
|
-
...context,
|
|
637
|
-
result: verifyResult
|
|
638
|
-
};
|
|
639
|
-
for (const hook of this.afterVerifyHooks) {
|
|
640
|
-
await hook(resultContext);
|
|
641
|
-
}
|
|
642
|
-
return verifyResult;
|
|
643
|
-
} catch (error) {
|
|
644
|
-
const failureContext = {
|
|
645
|
-
...context,
|
|
646
|
-
error
|
|
647
|
-
};
|
|
648
|
-
for (const hook of this.onVerifyFailureHooks) {
|
|
649
|
-
const result = await hook(failureContext);
|
|
650
|
-
if (result && "recovered" in result && result.recovered) {
|
|
651
|
-
return result.result;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
throw error;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Settle a verified payment
|
|
659
|
-
*
|
|
660
|
-
* @param paymentPayload - The payment payload to settle
|
|
661
|
-
* @param requirements - The payment requirements
|
|
662
|
-
* @returns Settlement response
|
|
663
|
-
*/
|
|
664
|
-
async settlePayment(paymentPayload, requirements) {
|
|
665
|
-
const context = {
|
|
666
|
-
paymentPayload,
|
|
667
|
-
requirements
|
|
668
|
-
};
|
|
669
|
-
for (const hook of this.beforeSettleHooks) {
|
|
670
|
-
const result = await hook(context);
|
|
671
|
-
if (result && "abort" in result && result.abort) {
|
|
672
|
-
throw new Error(`Settlement aborted: ${result.reason}`);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
try {
|
|
676
|
-
const facilitatorClient = this.getFacilitatorClient(
|
|
677
|
-
paymentPayload.x402Version,
|
|
678
|
-
requirements.network,
|
|
679
|
-
requirements.scheme
|
|
680
|
-
);
|
|
681
|
-
let settleResult;
|
|
682
|
-
if (!facilitatorClient) {
|
|
683
|
-
let lastError;
|
|
684
|
-
for (const client of this.facilitatorClients) {
|
|
685
|
-
try {
|
|
686
|
-
settleResult = await client.settle(paymentPayload, requirements);
|
|
687
|
-
break;
|
|
688
|
-
} catch (error) {
|
|
689
|
-
lastError = error;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (!settleResult) {
|
|
693
|
-
throw lastError || new Error(
|
|
694
|
-
`No facilitator supports ${requirements.scheme} on ${requirements.network} for v${paymentPayload.x402Version}`
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
} else {
|
|
698
|
-
settleResult = await facilitatorClient.settle(paymentPayload, requirements);
|
|
699
|
-
}
|
|
700
|
-
const resultContext = {
|
|
701
|
-
...context,
|
|
702
|
-
result: settleResult
|
|
703
|
-
};
|
|
704
|
-
for (const hook of this.afterSettleHooks) {
|
|
705
|
-
await hook(resultContext);
|
|
706
|
-
}
|
|
707
|
-
return settleResult;
|
|
708
|
-
} catch (error) {
|
|
709
|
-
const failureContext = {
|
|
710
|
-
...context,
|
|
711
|
-
error
|
|
712
|
-
};
|
|
713
|
-
for (const hook of this.onSettleFailureHooks) {
|
|
714
|
-
const result = await hook(failureContext);
|
|
715
|
-
if (result && "recovered" in result && result.recovered) {
|
|
716
|
-
return result.result;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
throw error;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Find matching payment requirements for a payment
|
|
724
|
-
*
|
|
725
|
-
* @param availableRequirements - Array of available payment requirements
|
|
726
|
-
* @param paymentPayload - The payment payload
|
|
727
|
-
* @returns Matching payment requirements or undefined
|
|
728
|
-
*/
|
|
729
|
-
findMatchingRequirements(availableRequirements, paymentPayload) {
|
|
730
|
-
switch (paymentPayload.x402Version) {
|
|
731
|
-
case 2:
|
|
732
|
-
return availableRequirements.find(
|
|
733
|
-
(paymentRequirements) => deepEqual(paymentRequirements, paymentPayload.accepted)
|
|
734
|
-
);
|
|
735
|
-
case 1:
|
|
736
|
-
return availableRequirements.find(
|
|
737
|
-
(req) => req.scheme === paymentPayload.accepted.scheme && req.network === paymentPayload.accepted.network
|
|
738
|
-
);
|
|
739
|
-
default:
|
|
740
|
-
throw new Error(
|
|
741
|
-
`Unsupported x402 version: ${paymentPayload.x402Version}`
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* Process a payment request
|
|
747
|
-
*
|
|
748
|
-
* @param paymentPayload - Optional payment payload if provided
|
|
749
|
-
* @param resourceConfig - Configuration for the protected resource
|
|
750
|
-
* @param resourceInfo - Information about the resource being accessed
|
|
751
|
-
* @param extensions - Optional extensions to include in the response
|
|
752
|
-
* @returns Processing result
|
|
753
|
-
*/
|
|
754
|
-
async processPaymentRequest(paymentPayload, resourceConfig, resourceInfo, extensions) {
|
|
755
|
-
const requirements = await this.buildPaymentRequirements(resourceConfig);
|
|
756
|
-
if (!paymentPayload) {
|
|
757
|
-
return {
|
|
758
|
-
success: false,
|
|
759
|
-
requiresPayment: this.createPaymentRequiredResponse(
|
|
760
|
-
requirements,
|
|
761
|
-
resourceInfo,
|
|
762
|
-
"Payment required",
|
|
763
|
-
extensions
|
|
764
|
-
)
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
const matchingRequirements = this.findMatchingRequirements(requirements, paymentPayload);
|
|
768
|
-
if (!matchingRequirements) {
|
|
769
|
-
return {
|
|
770
|
-
success: false,
|
|
771
|
-
requiresPayment: this.createPaymentRequiredResponse(
|
|
772
|
-
requirements,
|
|
773
|
-
resourceInfo,
|
|
774
|
-
"No matching payment requirements found",
|
|
775
|
-
extensions
|
|
776
|
-
)
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
const verificationResult = await this.verifyPayment(paymentPayload, matchingRequirements);
|
|
780
|
-
if (!verificationResult.isValid) {
|
|
781
|
-
return {
|
|
782
|
-
success: false,
|
|
783
|
-
error: verificationResult.invalidReason,
|
|
784
|
-
verificationResult
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
return {
|
|
788
|
-
success: true,
|
|
789
|
-
verificationResult
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Get facilitator client for a specific version, network, and scheme
|
|
794
|
-
*
|
|
795
|
-
* @param x402Version - The x402 version
|
|
796
|
-
* @param network - The network identifier
|
|
797
|
-
* @param scheme - The payment scheme
|
|
798
|
-
* @returns The facilitator client or undefined if not found
|
|
799
|
-
*/
|
|
800
|
-
getFacilitatorClient(x402Version2, network, scheme) {
|
|
801
|
-
const versionMap = this.facilitatorClientsMap.get(x402Version2);
|
|
802
|
-
if (!versionMap) return void 0;
|
|
803
|
-
return findByNetworkAndScheme(versionMap, scheme, network);
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
// src/http/index.ts
|
|
808
|
-
function decodePaymentSignatureHeader(paymentSignatureHeader) {
|
|
809
|
-
if (!Base64EncodedRegex.test(paymentSignatureHeader)) {
|
|
810
|
-
throw new Error("Invalid payment signature header");
|
|
811
|
-
}
|
|
812
|
-
return JSON.parse(safeBase64Decode(paymentSignatureHeader));
|
|
813
|
-
}
|
|
814
|
-
function encodePaymentRequiredHeader(paymentRequired) {
|
|
815
|
-
return safeBase64Encode(JSON.stringify(paymentRequired));
|
|
816
|
-
}
|
|
817
|
-
function encodePaymentResponseHeader(paymentResponse) {
|
|
818
|
-
return safeBase64Encode(JSON.stringify(paymentResponse));
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// src/http/x402HTTPResourceServer.ts
|
|
822
|
-
var RouteConfigurationError = class extends Error {
|
|
823
|
-
/**
|
|
824
|
-
* Creates a new RouteConfigurationError with the given validation errors.
|
|
825
|
-
*
|
|
826
|
-
* @param errors - The validation errors that caused this exception.
|
|
827
|
-
*/
|
|
828
|
-
constructor(errors) {
|
|
829
|
-
const message = `x402 Route Configuration Errors:
|
|
830
|
-
${errors.map((e) => ` - ${e.message}`).join("\n")}`;
|
|
831
|
-
super(message);
|
|
832
|
-
this.name = "RouteConfigurationError";
|
|
833
|
-
this.errors = errors;
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
var x402HTTPResourceServer = class {
|
|
837
|
-
/**
|
|
838
|
-
* Creates a new x402HTTPResourceServer instance.
|
|
839
|
-
*
|
|
840
|
-
* @param ResourceServer - The core x402ResourceServer instance to use
|
|
841
|
-
* @param routes - Route configuration for payment-protected endpoints
|
|
842
|
-
*/
|
|
843
|
-
constructor(ResourceServer, routes) {
|
|
844
|
-
this.compiledRoutes = [];
|
|
845
|
-
this.ResourceServer = ResourceServer;
|
|
846
|
-
this.routesConfig = routes;
|
|
847
|
-
const normalizedRoutes = typeof routes === "object" && !("accepts" in routes) ? routes : { "*": routes };
|
|
848
|
-
for (const [pattern, config] of Object.entries(normalizedRoutes)) {
|
|
849
|
-
const parsed = this.parseRoutePattern(pattern);
|
|
850
|
-
this.compiledRoutes.push({
|
|
851
|
-
verb: parsed.verb,
|
|
852
|
-
regex: parsed.regex,
|
|
853
|
-
config
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Initialize the HTTP resource server.
|
|
859
|
-
*
|
|
860
|
-
* This method initializes the underlying resource server (fetching facilitator support)
|
|
861
|
-
* and then validates that all route payment configurations have corresponding
|
|
862
|
-
* registered schemes and facilitator support.
|
|
863
|
-
*
|
|
864
|
-
* @throws RouteConfigurationError if any route's payment options don't have
|
|
865
|
-
* corresponding registered schemes or facilitator support
|
|
866
|
-
*
|
|
867
|
-
* @example
|
|
868
|
-
* ```typescript
|
|
869
|
-
* const httpServer = new x402HTTPResourceServer(server, routes);
|
|
870
|
-
* await httpServer.initialize();
|
|
871
|
-
* ```
|
|
872
|
-
*/
|
|
873
|
-
async initialize() {
|
|
874
|
-
await this.ResourceServer.initialize();
|
|
875
|
-
const errors = this.validateRouteConfiguration();
|
|
876
|
-
if (errors.length > 0) {
|
|
877
|
-
throw new RouteConfigurationError(errors);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* Register a custom paywall provider for generating HTML
|
|
882
|
-
*
|
|
883
|
-
* @param provider - PaywallProvider instance
|
|
884
|
-
* @returns This service instance for chaining
|
|
885
|
-
*/
|
|
886
|
-
registerPaywallProvider(provider) {
|
|
887
|
-
this.paywallProvider = provider;
|
|
888
|
-
return this;
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Process HTTP request and return response instructions
|
|
892
|
-
* This is the main entry point for framework middleware
|
|
893
|
-
*
|
|
894
|
-
* @param context - HTTP request context
|
|
895
|
-
* @param paywallConfig - Optional paywall configuration
|
|
896
|
-
* @returns Process result indicating next action for middleware
|
|
897
|
-
*/
|
|
898
|
-
async processHTTPRequest(context, paywallConfig) {
|
|
899
|
-
const { adapter, path, method } = context;
|
|
900
|
-
const routeConfig = this.getRouteConfig(path, method);
|
|
901
|
-
if (!routeConfig) {
|
|
902
|
-
return { type: "no-payment-required" };
|
|
903
|
-
}
|
|
904
|
-
const paymentOptions = this.normalizePaymentOptions(routeConfig);
|
|
905
|
-
const paymentPayload = this.extractPayment(adapter);
|
|
906
|
-
const resourceInfo = {
|
|
907
|
-
url: routeConfig.resource || context.adapter.getUrl(),
|
|
908
|
-
description: routeConfig.description || "",
|
|
909
|
-
mimeType: routeConfig.mimeType || ""
|
|
910
|
-
};
|
|
911
|
-
const requirements = await this.ResourceServer.buildPaymentRequirementsFromOptions(
|
|
912
|
-
paymentOptions,
|
|
913
|
-
context
|
|
914
|
-
);
|
|
915
|
-
let extensions = routeConfig.extensions;
|
|
916
|
-
if (extensions) {
|
|
917
|
-
extensions = this.ResourceServer.enrichExtensions(extensions, context);
|
|
918
|
-
}
|
|
919
|
-
const paymentRequired = this.ResourceServer.createPaymentRequiredResponse(
|
|
920
|
-
requirements,
|
|
921
|
-
resourceInfo,
|
|
922
|
-
!paymentPayload ? "Payment required" : void 0,
|
|
923
|
-
extensions
|
|
924
|
-
);
|
|
925
|
-
if (!paymentPayload) {
|
|
926
|
-
const unpaidBody = routeConfig.unpaidResponseBody ? await routeConfig.unpaidResponseBody(context) : void 0;
|
|
927
|
-
return {
|
|
928
|
-
type: "payment-error",
|
|
929
|
-
response: this.createHTTPResponse(
|
|
930
|
-
paymentRequired,
|
|
931
|
-
this.isWebBrowser(adapter),
|
|
932
|
-
paywallConfig,
|
|
933
|
-
routeConfig.customPaywallHtml,
|
|
934
|
-
unpaidBody
|
|
935
|
-
)
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
try {
|
|
939
|
-
const matchingRequirements = this.ResourceServer.findMatchingRequirements(
|
|
940
|
-
paymentRequired.accepts,
|
|
941
|
-
paymentPayload
|
|
942
|
-
);
|
|
943
|
-
if (!matchingRequirements) {
|
|
944
|
-
const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
|
|
945
|
-
requirements,
|
|
946
|
-
resourceInfo,
|
|
947
|
-
"No matching payment requirements",
|
|
948
|
-
routeConfig.extensions
|
|
949
|
-
);
|
|
950
|
-
return {
|
|
951
|
-
type: "payment-error",
|
|
952
|
-
response: this.createHTTPResponse(errorResponse, false, paywallConfig)
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
const verifyResult = await this.ResourceServer.verifyPayment(
|
|
956
|
-
paymentPayload,
|
|
957
|
-
matchingRequirements
|
|
958
|
-
);
|
|
959
|
-
if (!verifyResult.isValid) {
|
|
960
|
-
const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
|
|
961
|
-
requirements,
|
|
962
|
-
resourceInfo,
|
|
963
|
-
verifyResult.invalidReason,
|
|
964
|
-
routeConfig.extensions
|
|
965
|
-
);
|
|
966
|
-
return {
|
|
967
|
-
type: "payment-error",
|
|
968
|
-
response: this.createHTTPResponse(errorResponse, false, paywallConfig)
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
return {
|
|
972
|
-
type: "payment-verified",
|
|
973
|
-
paymentPayload,
|
|
974
|
-
paymentRequirements: matchingRequirements
|
|
975
|
-
};
|
|
976
|
-
} catch (error) {
|
|
977
|
-
const errorResponse = this.ResourceServer.createPaymentRequiredResponse(
|
|
978
|
-
requirements,
|
|
979
|
-
resourceInfo,
|
|
980
|
-
error instanceof Error ? error.message : "Payment verification failed",
|
|
981
|
-
routeConfig.extensions
|
|
982
|
-
);
|
|
983
|
-
return {
|
|
984
|
-
type: "payment-error",
|
|
985
|
-
response: this.createHTTPResponse(errorResponse, false, paywallConfig)
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
/**
|
|
990
|
-
* Process settlement after successful response
|
|
991
|
-
*
|
|
992
|
-
* @param paymentPayload - The verified payment payload
|
|
993
|
-
* @param requirements - The matching payment requirements
|
|
994
|
-
* @returns ProcessSettleResultResponse - SettleResponse with headers if success or errorReason if failure
|
|
995
|
-
*/
|
|
996
|
-
async processSettlement(paymentPayload, requirements) {
|
|
997
|
-
try {
|
|
998
|
-
const settleResponse = await this.ResourceServer.settlePayment(paymentPayload, requirements);
|
|
999
|
-
if (!settleResponse.success) {
|
|
1000
|
-
return {
|
|
1001
|
-
...settleResponse,
|
|
1002
|
-
success: false,
|
|
1003
|
-
errorReason: settleResponse.errorReason || "Settlement failed"
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
return {
|
|
1007
|
-
...settleResponse,
|
|
1008
|
-
success: true,
|
|
1009
|
-
headers: this.createSettlementHeaders(settleResponse, requirements),
|
|
1010
|
-
requirements
|
|
1011
|
-
};
|
|
1012
|
-
} catch (error) {
|
|
1013
|
-
if (error instanceof SettleError) {
|
|
1014
|
-
return {
|
|
1015
|
-
success: false,
|
|
1016
|
-
errorReason: error.errorReason || error.message,
|
|
1017
|
-
payer: error.payer,
|
|
1018
|
-
network: error.network,
|
|
1019
|
-
transaction: error.transaction
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
return {
|
|
1023
|
-
success: false,
|
|
1024
|
-
errorReason: error instanceof Error ? error.message : "Settlement failed",
|
|
1025
|
-
network: requirements.network,
|
|
1026
|
-
transaction: ""
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Check if a request requires payment based on route configuration
|
|
1032
|
-
*
|
|
1033
|
-
* @param context - HTTP request context
|
|
1034
|
-
* @returns True if the route requires payment, false otherwise
|
|
1035
|
-
*/
|
|
1036
|
-
requiresPayment(context) {
|
|
1037
|
-
const routeConfig = this.getRouteConfig(context.path, context.method);
|
|
1038
|
-
return routeConfig !== void 0;
|
|
1039
|
-
}
|
|
1040
|
-
/**
|
|
1041
|
-
* Normalizes a RouteConfig's accepts field into an array of PaymentOptions
|
|
1042
|
-
* Handles both single PaymentOption and array formats
|
|
1043
|
-
*
|
|
1044
|
-
* @param routeConfig - Route configuration
|
|
1045
|
-
* @returns Array of payment options
|
|
1046
|
-
*/
|
|
1047
|
-
normalizePaymentOptions(routeConfig) {
|
|
1048
|
-
return Array.isArray(routeConfig.accepts) ? routeConfig.accepts : [routeConfig.accepts];
|
|
1049
|
-
}
|
|
1050
|
-
/**
|
|
1051
|
-
* Validates that all payment options in routes have corresponding registered schemes
|
|
1052
|
-
* and facilitator support.
|
|
1053
|
-
*
|
|
1054
|
-
* @returns Array of validation errors (empty if all routes are valid)
|
|
1055
|
-
*/
|
|
1056
|
-
validateRouteConfiguration() {
|
|
1057
|
-
const errors = [];
|
|
1058
|
-
const normalizedRoutes = typeof this.routesConfig === "object" && !("accepts" in this.routesConfig) ? Object.entries(this.routesConfig) : [["*", this.routesConfig]];
|
|
1059
|
-
for (const [pattern, config] of normalizedRoutes) {
|
|
1060
|
-
const paymentOptions = this.normalizePaymentOptions(config);
|
|
1061
|
-
for (const option of paymentOptions) {
|
|
1062
|
-
if (!this.ResourceServer.hasRegisteredScheme(option.network, option.scheme)) {
|
|
1063
|
-
errors.push({
|
|
1064
|
-
routePattern: pattern,
|
|
1065
|
-
scheme: option.scheme,
|
|
1066
|
-
network: option.network,
|
|
1067
|
-
reason: "missing_scheme",
|
|
1068
|
-
message: `Route "${pattern}": No scheme implementation registered for "${option.scheme}" on network "${option.network}"`
|
|
1069
|
-
});
|
|
1070
|
-
continue;
|
|
1071
|
-
}
|
|
1072
|
-
const supportedKind = this.ResourceServer.getSupportedKind(
|
|
1073
|
-
x402Version,
|
|
1074
|
-
option.network,
|
|
1075
|
-
option.scheme
|
|
1076
|
-
);
|
|
1077
|
-
if (!supportedKind) {
|
|
1078
|
-
errors.push({
|
|
1079
|
-
routePattern: pattern,
|
|
1080
|
-
scheme: option.scheme,
|
|
1081
|
-
network: option.network,
|
|
1082
|
-
reason: "missing_facilitator",
|
|
1083
|
-
message: `Route "${pattern}": Facilitator does not support scheme "${option.scheme}" on network "${option.network}"`
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
return errors;
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Get route configuration for a request
|
|
1092
|
-
*
|
|
1093
|
-
* @param path - Request path
|
|
1094
|
-
* @param method - HTTP method
|
|
1095
|
-
* @returns Route configuration or undefined if no match
|
|
1096
|
-
*/
|
|
1097
|
-
getRouteConfig(path, method) {
|
|
1098
|
-
const normalizedPath = this.normalizePath(path);
|
|
1099
|
-
const upperMethod = method.toUpperCase();
|
|
1100
|
-
const matchingRoute = this.compiledRoutes.find(
|
|
1101
|
-
(route) => route.regex.test(normalizedPath) && (route.verb === "*" || route.verb === upperMethod)
|
|
1102
|
-
);
|
|
1103
|
-
return matchingRoute?.config;
|
|
1104
|
-
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Extract payment from HTTP headers (handles v1 and v2)
|
|
1107
|
-
*
|
|
1108
|
-
* @param adapter - HTTP adapter
|
|
1109
|
-
* @returns Decoded payment payload or null
|
|
1110
|
-
*/
|
|
1111
|
-
extractPayment(adapter) {
|
|
1112
|
-
const header = adapter.getHeader("payment-signature") || adapter.getHeader("PAYMENT-SIGNATURE");
|
|
1113
|
-
if (header) {
|
|
1114
|
-
try {
|
|
1115
|
-
return decodePaymentSignatureHeader(header);
|
|
1116
|
-
} catch (error) {
|
|
1117
|
-
console.warn("Failed to decode PAYMENT-SIGNATURE header:", error);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
return null;
|
|
1121
|
-
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Check if request is from a web browser
|
|
1124
|
-
*
|
|
1125
|
-
* @param adapter - HTTP adapter
|
|
1126
|
-
* @returns True if request appears to be from a browser
|
|
1127
|
-
*/
|
|
1128
|
-
isWebBrowser(adapter) {
|
|
1129
|
-
const accept = adapter.getAcceptHeader();
|
|
1130
|
-
const userAgent = adapter.getUserAgent();
|
|
1131
|
-
return accept.includes("text/html") && userAgent.includes("Mozilla");
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Create HTTP response instructions from payment required
|
|
1135
|
-
*
|
|
1136
|
-
* @param paymentRequired - Payment requirements
|
|
1137
|
-
* @param isWebBrowser - Whether request is from browser
|
|
1138
|
-
* @param paywallConfig - Paywall configuration
|
|
1139
|
-
* @param customHtml - Custom HTML template
|
|
1140
|
-
* @param unpaidResponse - Optional custom response (content type and body) for unpaid API requests
|
|
1141
|
-
* @returns Response instructions
|
|
1142
|
-
*/
|
|
1143
|
-
createHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) {
|
|
1144
|
-
if (isWebBrowser) {
|
|
1145
|
-
const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
|
|
1146
|
-
return {
|
|
1147
|
-
status: 402,
|
|
1148
|
-
headers: { "Content-Type": "text/html" },
|
|
1149
|
-
body: html,
|
|
1150
|
-
isHtml: true
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
const response = this.createHTTPPaymentRequiredResponse(paymentRequired);
|
|
1154
|
-
const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json";
|
|
1155
|
-
const body = unpaidResponse ? unpaidResponse.body : {};
|
|
1156
|
-
return {
|
|
1157
|
-
status: 402,
|
|
1158
|
-
headers: {
|
|
1159
|
-
"Content-Type": contentType,
|
|
1160
|
-
...response.headers
|
|
1161
|
-
},
|
|
1162
|
-
body
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Create HTTP payment required response (v1 puts in body, v2 puts in header)
|
|
1167
|
-
*
|
|
1168
|
-
* @param paymentRequired - Payment required object
|
|
1169
|
-
* @returns Headers and body for the HTTP response
|
|
1170
|
-
*/
|
|
1171
|
-
createHTTPPaymentRequiredResponse(paymentRequired) {
|
|
1172
|
-
return {
|
|
1173
|
-
headers: {
|
|
1174
|
-
"PAYMENT-REQUIRED": encodePaymentRequiredHeader(paymentRequired)
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Create settlement response headers
|
|
1180
|
-
*
|
|
1181
|
-
* @param settleResponse - Settlement response
|
|
1182
|
-
* @param requirements - Payment requirements that were settled
|
|
1183
|
-
* @returns Headers to add to response
|
|
1184
|
-
*/
|
|
1185
|
-
createSettlementHeaders(settleResponse, requirements) {
|
|
1186
|
-
const encoded = encodePaymentResponseHeader({
|
|
1187
|
-
...settleResponse,
|
|
1188
|
-
requirements
|
|
1189
|
-
});
|
|
1190
|
-
return { "PAYMENT-RESPONSE": encoded };
|
|
1191
|
-
}
|
|
1192
|
-
/**
|
|
1193
|
-
* Parse route pattern into verb and regex
|
|
1194
|
-
*
|
|
1195
|
-
* @param pattern - Route pattern like "GET /api/*" or "/api/[id]"
|
|
1196
|
-
* @returns Parsed pattern with verb and regex
|
|
1197
|
-
*/
|
|
1198
|
-
parseRoutePattern(pattern) {
|
|
1199
|
-
const [verb, path] = pattern.includes(" ") ? pattern.split(/\s+/) : ["*", pattern];
|
|
1200
|
-
const regex = new RegExp(
|
|
1201
|
-
`^${path.replace(/[$()+.?^{|}]/g, "\\$&").replace(/\*/g, ".*?").replace(/\[([^\]]+)\]/g, "[^/]+").replace(/\//g, "\\/")}$`,
|
|
1202
|
-
"i"
|
|
1203
|
-
);
|
|
1204
|
-
return { verb: verb.toUpperCase(), regex };
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Normalize path for matching
|
|
1208
|
-
*
|
|
1209
|
-
* @param path - Raw path from request
|
|
1210
|
-
* @returns Normalized path
|
|
1211
|
-
*/
|
|
1212
|
-
normalizePath(path) {
|
|
1213
|
-
try {
|
|
1214
|
-
const pathWithoutQuery = path.split(/[?#]/)[0];
|
|
1215
|
-
const decodedPath = decodeURIComponent(pathWithoutQuery);
|
|
1216
|
-
return decodedPath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/(.+?)\/+$/, "$1");
|
|
1217
|
-
} catch {
|
|
1218
|
-
return path;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
/**
|
|
1222
|
-
* Generate paywall HTML for browser requests
|
|
1223
|
-
*
|
|
1224
|
-
* @param paymentRequired - Payment required response
|
|
1225
|
-
* @param paywallConfig - Optional paywall configuration
|
|
1226
|
-
* @param customHtml - Optional custom HTML template
|
|
1227
|
-
* @returns HTML string
|
|
1228
|
-
*/
|
|
1229
|
-
generatePaywallHTML(paymentRequired, paywallConfig, customHtml) {
|
|
1230
|
-
if (customHtml) {
|
|
1231
|
-
return customHtml;
|
|
1232
|
-
}
|
|
1233
|
-
if (this.paywallProvider) {
|
|
1234
|
-
return this.paywallProvider.generateHtml(paymentRequired, paywallConfig);
|
|
1235
|
-
}
|
|
1236
|
-
try {
|
|
1237
|
-
const paywall = require("@x402/paywall");
|
|
1238
|
-
const displayAmount2 = this.getDisplayAmount(paymentRequired);
|
|
1239
|
-
const resource2 = paymentRequired.resource;
|
|
1240
|
-
return paywall.getPaywallHtml({
|
|
1241
|
-
amount: displayAmount2,
|
|
1242
|
-
paymentRequired,
|
|
1243
|
-
currentUrl: resource2?.url || paywallConfig?.currentUrl || "",
|
|
1244
|
-
testnet: paywallConfig?.testnet ?? true,
|
|
1245
|
-
appName: paywallConfig?.appName,
|
|
1246
|
-
appLogo: paywallConfig?.appLogo,
|
|
1247
|
-
sessionTokenEndpoint: paywallConfig?.sessionTokenEndpoint
|
|
1248
|
-
});
|
|
1249
|
-
} catch {
|
|
1250
|
-
}
|
|
1251
|
-
const resource = paymentRequired.resource;
|
|
1252
|
-
const displayAmount = this.getDisplayAmount(paymentRequired);
|
|
1253
|
-
return `
|
|
1254
|
-
<!DOCTYPE html>
|
|
1255
|
-
<html>
|
|
1256
|
-
<head>
|
|
1257
|
-
<title>Payment Required</title>
|
|
1258
|
-
<meta charset="UTF-8">
|
|
1259
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1260
|
-
</head>
|
|
1261
|
-
<body>
|
|
1262
|
-
<div style="max-width: 600px; margin: 50px auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
|
|
1263
|
-
${paywallConfig?.appLogo ? `<img src="${paywallConfig.appLogo}" alt="${paywallConfig.appName || "App"}" style="max-width: 200px; margin-bottom: 20px;">` : ""}
|
|
1264
|
-
<h1>Payment Required</h1>
|
|
1265
|
-
${resource ? `<p><strong>Resource:</strong> ${resource.description || resource.url}</p>` : ""}
|
|
1266
|
-
<p><strong>Amount:</strong> $${displayAmount.toFixed(2)} USDC</p>
|
|
1267
|
-
<div id="payment-widget"
|
|
1268
|
-
data-requirements='${JSON.stringify(paymentRequired)}'
|
|
1269
|
-
data-app-name="${paywallConfig?.appName || ""}"
|
|
1270
|
-
data-testnet="${paywallConfig?.testnet || false}">
|
|
1271
|
-
<!-- Install @x402/paywall for full wallet integration -->
|
|
1272
|
-
<p style="margin-top: 2rem; padding: 1rem; background: #fef3c7; border-radius: 0.5rem;">
|
|
1273
|
-
<strong>Note:</strong> Install <code>@x402/paywall</code> for full wallet connection and payment UI.
|
|
1274
|
-
</p>
|
|
1275
|
-
</div>
|
|
1276
|
-
</div>
|
|
1277
|
-
</body>
|
|
1278
|
-
</html>
|
|
1279
|
-
`;
|
|
1280
|
-
}
|
|
1281
|
-
/**
|
|
1282
|
-
* Extract display amount from payment requirements.
|
|
1283
|
-
*
|
|
1284
|
-
* @param paymentRequired - The payment required object
|
|
1285
|
-
* @returns The display amount in decimal format
|
|
1286
|
-
*/
|
|
1287
|
-
getDisplayAmount(paymentRequired) {
|
|
1288
|
-
const accepts = paymentRequired.accepts;
|
|
1289
|
-
if (accepts && accepts.length > 0) {
|
|
1290
|
-
const firstReq = accepts[0];
|
|
1291
|
-
if ("amount" in firstReq) {
|
|
1292
|
-
return parseFloat(firstReq.amount) / 1e6;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
return 0;
|
|
1296
|
-
}
|
|
1297
|
-
};
|
|
1298
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1299
|
-
0 && (module.exports = {
|
|
1300
|
-
HTTPFacilitatorClient,
|
|
1301
|
-
RouteConfigurationError,
|
|
1302
|
-
x402HTTPResourceServer,
|
|
1303
|
-
x402ResourceServer
|
|
1304
|
-
});
|
|
1305
|
-
//# sourceMappingURL=index.js.map
|