@wtflabs/x402-server 0.0.1-beta.8 → 0.0.1-beta.9
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 +449 -212
- package/dist/cjs/index.d.ts +1313 -0
- package/dist/cjs/index.js +599 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +1313 -0
- package/dist/esm/index.mjs +558 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +43 -15
- package/dist/index.d.mts +0 -217
- package/dist/index.d.ts +0 -217
- package/dist/index.js +0 -640
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -610
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
DELETED
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
// src/token-detection.ts
|
|
2
|
-
var EIP3009_SIGNATURES = ["0xe3ee160e", "0xcf092995"];
|
|
3
|
-
var EIP2612_PERMIT = "0xd505accf";
|
|
4
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
5
|
-
var EIP1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
6
|
-
var EIP1822_IMPLEMENTATION_SLOT = "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3";
|
|
7
|
-
async function getImplementationAddress(client, proxyAddress) {
|
|
8
|
-
try {
|
|
9
|
-
try {
|
|
10
|
-
const implSlotData = await client.getStorageAt({
|
|
11
|
-
address: proxyAddress,
|
|
12
|
-
slot: EIP1967_IMPLEMENTATION_SLOT
|
|
13
|
-
});
|
|
14
|
-
if (implSlotData && implSlotData !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
15
|
-
const implAddress = `0x${implSlotData.slice(-40)}`;
|
|
16
|
-
if (implAddress !== "0x0000000000000000000000000000000000000000") {
|
|
17
|
-
console.log(` \u{1F4E6} Detected EIP-1967 proxy, implementation: ${implAddress}`);
|
|
18
|
-
return implAddress;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const uupsSlotData = await client.getStorageAt({
|
|
25
|
-
address: proxyAddress,
|
|
26
|
-
slot: EIP1822_IMPLEMENTATION_SLOT
|
|
27
|
-
});
|
|
28
|
-
if (uupsSlotData && uupsSlotData !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
29
|
-
const implAddress = `0x${uupsSlotData.slice(-40)}`;
|
|
30
|
-
if (implAddress !== "0x0000000000000000000000000000000000000000") {
|
|
31
|
-
console.log(` \u{1F4E6} Detected EIP-1822 UUPS proxy, implementation: ${implAddress}`);
|
|
32
|
-
return implAddress;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
} catch {
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
const implABI = [
|
|
39
|
-
{
|
|
40
|
-
inputs: [],
|
|
41
|
-
name: "implementation",
|
|
42
|
-
outputs: [{ name: "", type: "address" }],
|
|
43
|
-
stateMutability: "view",
|
|
44
|
-
type: "function"
|
|
45
|
-
}
|
|
46
|
-
];
|
|
47
|
-
const implAddress = await client.readContract({
|
|
48
|
-
address: proxyAddress,
|
|
49
|
-
abi: implABI,
|
|
50
|
-
functionName: "implementation"
|
|
51
|
-
});
|
|
52
|
-
if (implAddress && implAddress !== "0x0000000000000000000000000000000000000000") {
|
|
53
|
-
console.log(` \u{1F4E6} Detected proxy via implementation(), implementation: ${implAddress}`);
|
|
54
|
-
return implAddress;
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("Error detecting proxy implementation:", error);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async function hasMethod(client, tokenAddress, methodSelector) {
|
|
65
|
-
try {
|
|
66
|
-
const code = await client.getBytecode({ address: tokenAddress });
|
|
67
|
-
if (!code) return false;
|
|
68
|
-
const hasMethodInProxy = code.toLowerCase().includes(methodSelector.slice(2).toLowerCase());
|
|
69
|
-
if (hasMethodInProxy) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
const implAddress = await getImplementationAddress(client, tokenAddress);
|
|
73
|
-
if (implAddress) {
|
|
74
|
-
const implCode = await client.getBytecode({ address: implAddress });
|
|
75
|
-
if (implCode) {
|
|
76
|
-
const hasMethodInImpl = implCode.toLowerCase().includes(methodSelector.slice(2).toLowerCase());
|
|
77
|
-
if (hasMethodInImpl) {
|
|
78
|
-
console.log(` \u2705 Method ${methodSelector} found in implementation contract`);
|
|
79
|
-
}
|
|
80
|
-
return hasMethodInImpl;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(`Error checking method ${methodSelector}:`, error);
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
async function hasAnyMethod(client, tokenAddress, methodSelectors) {
|
|
90
|
-
try {
|
|
91
|
-
const code = await client.getBytecode({ address: tokenAddress });
|
|
92
|
-
if (!code) return false;
|
|
93
|
-
const codeLower = code.toLowerCase();
|
|
94
|
-
const hasMethodInProxy = methodSelectors.some(
|
|
95
|
-
(selector) => codeLower.includes(selector.slice(2).toLowerCase())
|
|
96
|
-
);
|
|
97
|
-
if (hasMethodInProxy) {
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
const implAddress = await getImplementationAddress(client, tokenAddress);
|
|
101
|
-
if (implAddress) {
|
|
102
|
-
const implCode = await client.getBytecode({ address: implAddress });
|
|
103
|
-
if (implCode) {
|
|
104
|
-
const implCodeLower = implCode.toLowerCase();
|
|
105
|
-
const hasMethodInImpl = methodSelectors.some(
|
|
106
|
-
(selector) => implCodeLower.includes(selector.slice(2).toLowerCase())
|
|
107
|
-
);
|
|
108
|
-
if (hasMethodInImpl) {
|
|
109
|
-
console.log(` \u2705 Method(s) found in implementation contract`);
|
|
110
|
-
}
|
|
111
|
-
return hasMethodInImpl;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return false;
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`Error checking methods ${methodSelectors.join(", ")}:`, error);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function checkPermit2Support(client) {
|
|
121
|
-
try {
|
|
122
|
-
const permit2Code = await client.getBytecode({ address: PERMIT2_ADDRESS });
|
|
123
|
-
if (!permit2Code) return false;
|
|
124
|
-
return true;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error("Error checking Permit2 support:", error);
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
async function detectTokenPaymentMethods(tokenAddress, client) {
|
|
131
|
-
const address = tokenAddress.toLowerCase();
|
|
132
|
-
console.log(`\u{1F50D} Detecting payment methods for token ${address}...`);
|
|
133
|
-
const [hasEIP3009, hasPermit, hasPermit2Approval] = await Promise.all([
|
|
134
|
-
hasAnyMethod(client, address, EIP3009_SIGNATURES),
|
|
135
|
-
hasMethod(client, address, EIP2612_PERMIT),
|
|
136
|
-
checkPermit2Support(client)
|
|
137
|
-
]);
|
|
138
|
-
const supportedMethods = [];
|
|
139
|
-
if (hasEIP3009) {
|
|
140
|
-
supportedMethods.push("eip3009");
|
|
141
|
-
console.log(" \u2705 EIP-3009 (transferWithAuthorization) detected");
|
|
142
|
-
}
|
|
143
|
-
if (hasPermit) {
|
|
144
|
-
supportedMethods.push("permit");
|
|
145
|
-
console.log(" \u2705 EIP-2612 (permit) detected");
|
|
146
|
-
}
|
|
147
|
-
if (hasPermit2Approval) {
|
|
148
|
-
supportedMethods.push("permit2");
|
|
149
|
-
supportedMethods.push("permit2-witness");
|
|
150
|
-
console.log(" \u2705 Permit2 support available (universal)");
|
|
151
|
-
}
|
|
152
|
-
if (supportedMethods.length === 0) {
|
|
153
|
-
console.log(" \u26A0\uFE0F No advanced payment methods detected (standard ERC-20 only)");
|
|
154
|
-
}
|
|
155
|
-
return {
|
|
156
|
-
address,
|
|
157
|
-
supportedMethods,
|
|
158
|
-
details: {
|
|
159
|
-
hasEIP3009,
|
|
160
|
-
hasPermit,
|
|
161
|
-
hasPermit2Approval
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function getRecommendedPaymentMethod(capabilities) {
|
|
166
|
-
const { supportedMethods } = capabilities;
|
|
167
|
-
if (supportedMethods.includes("eip3009")) return "eip3009";
|
|
168
|
-
if (supportedMethods.includes("permit")) return "permit";
|
|
169
|
-
if (supportedMethods.includes("permit2") || supportedMethods.includes("permit2-witness")) {
|
|
170
|
-
return "permit2";
|
|
171
|
-
}
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
async function getTokenInfo(tokenAddress, client) {
|
|
175
|
-
const address = tokenAddress.toLowerCase();
|
|
176
|
-
const erc20ABI = [
|
|
177
|
-
{
|
|
178
|
-
inputs: [],
|
|
179
|
-
name: "name",
|
|
180
|
-
outputs: [{ name: "", type: "string" }],
|
|
181
|
-
stateMutability: "view",
|
|
182
|
-
type: "function"
|
|
183
|
-
}
|
|
184
|
-
];
|
|
185
|
-
const eip712DomainABI = [
|
|
186
|
-
{
|
|
187
|
-
inputs: [],
|
|
188
|
-
name: "eip712Domain",
|
|
189
|
-
outputs: [
|
|
190
|
-
{ name: "fields", type: "bytes1" },
|
|
191
|
-
{ name: "name", type: "string" },
|
|
192
|
-
{ name: "version", type: "string" },
|
|
193
|
-
{ name: "chainId", type: "uint256" },
|
|
194
|
-
{ name: "verifyingContract", type: "address" },
|
|
195
|
-
{ name: "salt", type: "bytes32" },
|
|
196
|
-
{ name: "extensions", type: "uint256[]" }
|
|
197
|
-
],
|
|
198
|
-
stateMutability: "view",
|
|
199
|
-
type: "function"
|
|
200
|
-
}
|
|
201
|
-
];
|
|
202
|
-
const versionABI = [
|
|
203
|
-
{
|
|
204
|
-
inputs: [],
|
|
205
|
-
name: "version",
|
|
206
|
-
outputs: [{ name: "", type: "string" }],
|
|
207
|
-
stateMutability: "view",
|
|
208
|
-
type: "function"
|
|
209
|
-
}
|
|
210
|
-
];
|
|
211
|
-
try {
|
|
212
|
-
const implAddress = await getImplementationAddress(client, address);
|
|
213
|
-
if (implAddress) {
|
|
214
|
-
console.log(` \u{1F4E6} Reading token info from proxy, actual calls will be delegated to implementation`);
|
|
215
|
-
}
|
|
216
|
-
const name = await client.readContract({
|
|
217
|
-
address,
|
|
218
|
-
abi: erc20ABI,
|
|
219
|
-
functionName: "name"
|
|
220
|
-
});
|
|
221
|
-
let version = "1";
|
|
222
|
-
try {
|
|
223
|
-
const result = await client.readContract({
|
|
224
|
-
address,
|
|
225
|
-
abi: eip712DomainABI,
|
|
226
|
-
functionName: "eip712Domain"
|
|
227
|
-
});
|
|
228
|
-
version = result[2];
|
|
229
|
-
} catch {
|
|
230
|
-
try {
|
|
231
|
-
version = await client.readContract({
|
|
232
|
-
address,
|
|
233
|
-
abi: versionABI,
|
|
234
|
-
functionName: "version"
|
|
235
|
-
});
|
|
236
|
-
} catch {
|
|
237
|
-
console.log(` \u2139\uFE0F Using default version "1" for token ${address}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return {
|
|
241
|
-
name,
|
|
242
|
-
version
|
|
243
|
-
};
|
|
244
|
-
} catch (error) {
|
|
245
|
-
console.error(`Error getting token info for ${address}:`, error);
|
|
246
|
-
throw new Error(`Failed to get token info: ${error}`);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/server.ts
|
|
251
|
-
var X402Server = class {
|
|
252
|
-
constructor(config) {
|
|
253
|
-
this.initialized = false;
|
|
254
|
-
this.facilitator = config.facilitator;
|
|
255
|
-
this.schema = config.schema;
|
|
256
|
-
this.client = config.client;
|
|
257
|
-
this._initialize();
|
|
258
|
-
}
|
|
259
|
-
async _initialize() {
|
|
260
|
-
try {
|
|
261
|
-
this.schema.verify();
|
|
262
|
-
const schemaAsset = this.schema.get("asset");
|
|
263
|
-
if (schemaAsset) {
|
|
264
|
-
try {
|
|
265
|
-
const tokenInfo = await getTokenInfo(schemaAsset, this.client);
|
|
266
|
-
this.schema.setExtra({
|
|
267
|
-
relayer: this.facilitator.relayer,
|
|
268
|
-
name: tokenInfo.name,
|
|
269
|
-
version: tokenInfo.version
|
|
270
|
-
});
|
|
271
|
-
console.log(`\u2705 Token info retrieved: ${tokenInfo.name} v${tokenInfo.version}`);
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.warn(`\u26A0\uFE0F Failed to get token info, signatures may fail:`, error);
|
|
274
|
-
this.schema.setExtra({
|
|
275
|
-
relayer: this.facilitator.relayer
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
this.schema.setExtra({
|
|
280
|
-
relayer: this.facilitator.relayer
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
const verifyResult = await this._verify();
|
|
284
|
-
if (!verifyResult.success) {
|
|
285
|
-
return {
|
|
286
|
-
success: false,
|
|
287
|
-
error: verifyResult.errors?.[0] || "Verification failed"
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
this.initialized = true;
|
|
291
|
-
return { success: true };
|
|
292
|
-
} catch (error) {
|
|
293
|
-
return {
|
|
294
|
-
success: false,
|
|
295
|
-
error: error instanceof Error ? error.message : "Initialization failed"
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* 验证配置
|
|
301
|
-
* 1. 验证 client network 是否和 schema 的 network 匹配
|
|
302
|
-
* 2. 验证 facilitator recipientAddress 和 schema payTo 是否一致
|
|
303
|
-
*/
|
|
304
|
-
async _verify() {
|
|
305
|
-
const errors = [];
|
|
306
|
-
try {
|
|
307
|
-
const schemaAsset = this.schema.get("asset");
|
|
308
|
-
if (schemaAsset) {
|
|
309
|
-
const tokenCapabilities = await detectTokenPaymentMethods(
|
|
310
|
-
schemaAsset,
|
|
311
|
-
this.client
|
|
312
|
-
);
|
|
313
|
-
const currentPaymentType = this.schema.get("paymentType");
|
|
314
|
-
if (!currentPaymentType) {
|
|
315
|
-
const recommendedMethod = getRecommendedPaymentMethod(tokenCapabilities);
|
|
316
|
-
if (recommendedMethod) {
|
|
317
|
-
this.schema.set("paymentType", recommendedMethod);
|
|
318
|
-
} else {
|
|
319
|
-
errors.push(
|
|
320
|
-
`Token ${schemaAsset} does not support any advanced payment methods (permit, eip3009, permit2). Please specify paymentType manually.`
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
} else {
|
|
324
|
-
if (!tokenCapabilities.supportedMethods.includes(currentPaymentType)) {
|
|
325
|
-
errors.push(
|
|
326
|
-
`Token ${schemaAsset} does not support the specified payment method "${currentPaymentType}". Supported methods: ${tokenCapabilities.supportedMethods.join(", ")}`
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
if (tokenCapabilities.supportedMethods.length === 0) {
|
|
331
|
-
errors.push(
|
|
332
|
-
`Token ${schemaAsset} does not support any advanced payment methods (permit, eip3009, permit2)`
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
const clientChainId = this.client.chain?.id;
|
|
337
|
-
const schemaNetwork = this.schema.get("network");
|
|
338
|
-
if (clientChainId && schemaAsset) {
|
|
339
|
-
const facilitatorSupported = await this.facilitator.supported({
|
|
340
|
-
chainId: clientChainId,
|
|
341
|
-
tokenAddress: schemaAsset
|
|
342
|
-
});
|
|
343
|
-
const isSupportedByFacilitator = facilitatorSupported.kinds.some(
|
|
344
|
-
(kind) => {
|
|
345
|
-
const networkMatches = kind.network === schemaNetwork;
|
|
346
|
-
const assetsInKind = kind.extra?.assets || [];
|
|
347
|
-
const assetMatches = assetsInKind.some(
|
|
348
|
-
(asset) => asset.address.toLowerCase() === schemaAsset.toLowerCase()
|
|
349
|
-
);
|
|
350
|
-
return networkMatches && assetMatches;
|
|
351
|
-
}
|
|
352
|
-
);
|
|
353
|
-
if (!isSupportedByFacilitator) {
|
|
354
|
-
errors.push(
|
|
355
|
-
`Facilitator does not support token ${schemaAsset} on network ${schemaNetwork} (chainId: ${clientChainId})`
|
|
356
|
-
);
|
|
357
|
-
} else {
|
|
358
|
-
}
|
|
359
|
-
const chainSupported = facilitatorSupported.kinds.some(
|
|
360
|
-
(kind) => kind.network === schemaNetwork
|
|
361
|
-
);
|
|
362
|
-
if (!chainSupported) {
|
|
363
|
-
errors.push(
|
|
364
|
-
`Facilitator does not support network ${schemaNetwork} (chainId: ${clientChainId})`
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (clientChainId) {
|
|
369
|
-
const networkValid = this.validateNetwork(
|
|
370
|
-
schemaNetwork,
|
|
371
|
-
clientChainId
|
|
372
|
-
);
|
|
373
|
-
if (!networkValid) {
|
|
374
|
-
errors.push(
|
|
375
|
-
`Network mismatch: client chainId ${clientChainId} does not match schema network ${schemaNetwork}`
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
const schemaPayTo = this.schema.get("payTo");
|
|
380
|
-
const facilitatorRecipientAddress = this.facilitator.recipientAddress;
|
|
381
|
-
if (schemaPayTo.toLowerCase() !== facilitatorRecipientAddress.toLowerCase()) {
|
|
382
|
-
errors.push(
|
|
383
|
-
`Address mismatch: schema payTo ${schemaPayTo} does not match facilitator recipientAddress ${facilitatorRecipientAddress}`
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
success: errors.length === 0,
|
|
388
|
-
errors: errors.length > 0 ? errors : void 0
|
|
389
|
-
};
|
|
390
|
-
} catch (error) {
|
|
391
|
-
errors.push(
|
|
392
|
-
error instanceof Error ? error.message : "Unknown verification error"
|
|
393
|
-
);
|
|
394
|
-
return {
|
|
395
|
-
success: false,
|
|
396
|
-
errors
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* 结算支付
|
|
402
|
-
* @param paymentPayload 支付负载
|
|
403
|
-
* @param paymentRequirements 支付要求
|
|
404
|
-
*/
|
|
405
|
-
async settle(paymentPayload, paymentRequirements) {
|
|
406
|
-
if (!this.initialized) {
|
|
407
|
-
return {
|
|
408
|
-
success: false,
|
|
409
|
-
error: "Server not initialized. Please call initialize() first."
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
try {
|
|
413
|
-
const result = await this.facilitator.settle(
|
|
414
|
-
paymentPayload,
|
|
415
|
-
paymentRequirements
|
|
416
|
-
);
|
|
417
|
-
if (!result.success) {
|
|
418
|
-
return {
|
|
419
|
-
success: false,
|
|
420
|
-
error: result.error || result.errorMessage
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
return {
|
|
424
|
-
success: true,
|
|
425
|
-
transaction: result.transaction
|
|
426
|
-
};
|
|
427
|
-
} catch (error) {
|
|
428
|
-
return {
|
|
429
|
-
success: false,
|
|
430
|
-
error: error instanceof Error ? error.message : "Settlement failed"
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* 验证支付负载
|
|
436
|
-
* @param paymentPayload 支付负载
|
|
437
|
-
* @param paymentRequirements 支付要求
|
|
438
|
-
*/
|
|
439
|
-
async verify(paymentPayload, paymentRequirements) {
|
|
440
|
-
if (!this.initialized) {
|
|
441
|
-
return {
|
|
442
|
-
success: false,
|
|
443
|
-
error: "Server not initialized. Please call initialize() first."
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
try {
|
|
447
|
-
const result = await this.facilitator.verify(
|
|
448
|
-
paymentPayload,
|
|
449
|
-
paymentRequirements
|
|
450
|
-
);
|
|
451
|
-
return {
|
|
452
|
-
success: result.success,
|
|
453
|
-
data: result.payer,
|
|
454
|
-
error: result.error || result.errorMessage
|
|
455
|
-
};
|
|
456
|
-
} catch (error) {
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
error: error instanceof Error ? error.message : "Payment verification failed"
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* 获取 facilitator
|
|
465
|
-
*/
|
|
466
|
-
getFacilitator() {
|
|
467
|
-
return this.facilitator;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* 获取 schema
|
|
471
|
-
*/
|
|
472
|
-
getSchema() {
|
|
473
|
-
return this.schema;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* 获取 client
|
|
477
|
-
*/
|
|
478
|
-
getClient() {
|
|
479
|
-
return this.client;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* 检查是否已初始化
|
|
483
|
-
*/
|
|
484
|
-
isInitialized() {
|
|
485
|
-
return this.initialized;
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* 验证网络是否匹配
|
|
489
|
-
* @param schemaNetwork schema 中的 network
|
|
490
|
-
* @param clientChainId client 的 chainId
|
|
491
|
-
* @returns 是否匹配
|
|
492
|
-
*/
|
|
493
|
-
validateNetwork(schemaNetwork, clientChainId) {
|
|
494
|
-
if (schemaNetwork.startsWith("eip155:")) {
|
|
495
|
-
const chainId = parseInt(schemaNetwork.split(":")[1] || "0");
|
|
496
|
-
return chainId === clientChainId;
|
|
497
|
-
}
|
|
498
|
-
const networkMap = {
|
|
499
|
-
"ethereum": 1,
|
|
500
|
-
"goerli": 5,
|
|
501
|
-
"sepolia": 11155111,
|
|
502
|
-
"base": 8453,
|
|
503
|
-
"base-sepolia": 84532,
|
|
504
|
-
"bsc": 56,
|
|
505
|
-
"bsc-testnet": 97,
|
|
506
|
-
"polygon": 137,
|
|
507
|
-
"polygon-mumbai": 80001,
|
|
508
|
-
"arbitrum": 42161,
|
|
509
|
-
"arbitrum-goerli": 421613,
|
|
510
|
-
"optimism": 10,
|
|
511
|
-
"optimism-goerli": 420,
|
|
512
|
-
"avalanche": 43114,
|
|
513
|
-
"avalanche-fuji": 43113
|
|
514
|
-
};
|
|
515
|
-
const expectedChainId = networkMap[schemaNetwork];
|
|
516
|
-
if (expectedChainId !== void 0) {
|
|
517
|
-
return expectedChainId === clientChainId;
|
|
518
|
-
}
|
|
519
|
-
return true;
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* 解析支付 header
|
|
523
|
-
* @param paymentHeaderBase64 Base64 编码的支付 header
|
|
524
|
-
* @returns 解析结果,成功时返回 paymentPayload 和 paymentRequirements,失败时返回服务端的支付要求
|
|
525
|
-
*/
|
|
526
|
-
parsePaymentHeader(paymentHeaderBase64) {
|
|
527
|
-
const paymentRequirements = this.schema.toJSON();
|
|
528
|
-
if (!paymentHeaderBase64) {
|
|
529
|
-
return {
|
|
530
|
-
success: false,
|
|
531
|
-
data: paymentRequirements,
|
|
532
|
-
error: "No X-PAYMENT header"
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
let paymentPayload;
|
|
536
|
-
try {
|
|
537
|
-
const paymentHeaderJson = Buffer.from(
|
|
538
|
-
paymentHeaderBase64,
|
|
539
|
-
"base64"
|
|
540
|
-
).toString("utf-8");
|
|
541
|
-
paymentPayload = JSON.parse(paymentHeaderJson);
|
|
542
|
-
} catch (err) {
|
|
543
|
-
return {
|
|
544
|
-
success: false,
|
|
545
|
-
data: paymentRequirements,
|
|
546
|
-
error: "Invalid payment header format"
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
const validationError = this.validatePaymentPayload(
|
|
550
|
-
paymentPayload,
|
|
551
|
-
paymentRequirements
|
|
552
|
-
);
|
|
553
|
-
if (validationError) {
|
|
554
|
-
return {
|
|
555
|
-
success: false,
|
|
556
|
-
data: paymentRequirements,
|
|
557
|
-
error: validationError
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
return {
|
|
561
|
-
success: true,
|
|
562
|
-
data: {
|
|
563
|
-
paymentPayload,
|
|
564
|
-
paymentRequirements
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* 验证客户端的支付数据是否与服务端要求一致
|
|
570
|
-
* @param paymentPayload 客户端的支付负载
|
|
571
|
-
* @param paymentRequirements 服务端的支付要求
|
|
572
|
-
* @returns 错误信息,如果验证通过则返回 null
|
|
573
|
-
*/
|
|
574
|
-
validatePaymentPayload(paymentPayload, paymentRequirements) {
|
|
575
|
-
if (paymentPayload.scheme !== paymentRequirements.scheme) {
|
|
576
|
-
return `Scheme mismatch: expected '${paymentRequirements.scheme}', got '${paymentPayload.scheme}'`;
|
|
577
|
-
}
|
|
578
|
-
if (paymentPayload.network !== paymentRequirements.network) {
|
|
579
|
-
return `Network mismatch: expected '${paymentRequirements.network}', got '${paymentPayload.network}'`;
|
|
580
|
-
}
|
|
581
|
-
if (paymentPayload.payload) {
|
|
582
|
-
const authorization = paymentPayload.payload.authorization;
|
|
583
|
-
if (authorization?.value) {
|
|
584
|
-
const paymentAmount = BigInt(authorization.value);
|
|
585
|
-
const maxAmount = BigInt(paymentRequirements.maxAmountRequired);
|
|
586
|
-
if (paymentAmount !== maxAmount) {
|
|
587
|
-
return `Payment amount error ${paymentAmount} !== ${maxAmount}`;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
if (authorization?.to) {
|
|
591
|
-
const expectedPayTo = paymentRequirements.payTo.toLowerCase();
|
|
592
|
-
const actualPayTo = authorization.to.toLowerCase();
|
|
593
|
-
if (actualPayTo !== expectedPayTo && actualPayTo !== paymentRequirements.extra?.relayer?.toLowerCase()) {
|
|
594
|
-
return `PayTo address mismatch: expected '${expectedPayTo}', got '${actualPayTo}'`;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
const authorizationType = paymentPayload.payload.authorizationType;
|
|
598
|
-
if (authorizationType === "permit" || authorizationType === "permit2" || authorizationType === "eip3009") {
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
return null;
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
export {
|
|
605
|
-
X402Server,
|
|
606
|
-
detectTokenPaymentMethods,
|
|
607
|
-
getRecommendedPaymentMethod,
|
|
608
|
-
getTokenInfo
|
|
609
|
-
};
|
|
610
|
-
//# sourceMappingURL=index.mjs.map
|