@circle-fin/x402-batching 2.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.
@@ -0,0 +1,668 @@
1
+ // src/server/BatchFacilitatorClient.ts
2
+ var BatchFacilitatorClient = class {
3
+ url;
4
+ _createAuthHeaders;
5
+ /**
6
+ * Creates a new BatchFacilitatorClient.
7
+ *
8
+ * @param config - Configuration including Gateway URL and optional auth headers
9
+ */
10
+ constructor(config = {}) {
11
+ this.url = (config.url ?? "https://gateway-api-testnet.circle.com").replace(
12
+ /\/$/,
13
+ ""
14
+ );
15
+ this._createAuthHeaders = config.createAuthHeaders;
16
+ }
17
+ /**
18
+ * Verify a payment with Circle Gateway.
19
+ *
20
+ * @param paymentPayload - The payment payload to verify
21
+ * @param paymentRequirements - The payment requirements
22
+ * @returns Verification response
23
+ */
24
+ async verify(paymentPayload, paymentRequirements) {
25
+ let headers = {
26
+ "Content-Type": "application/json"
27
+ };
28
+ if (this._createAuthHeaders) {
29
+ const authHeaders = await this._createAuthHeaders();
30
+ headers = { ...headers, ...authHeaders.verify };
31
+ }
32
+ const response = await fetch(`${this.url}/v1/x402/verify`, {
33
+ method: "POST",
34
+ headers,
35
+ body: JSON.stringify({
36
+ paymentPayload: this.toJsonSafe(paymentPayload),
37
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
38
+ })
39
+ });
40
+ const data = await response.json();
41
+ if (typeof data === "object" && data !== null && "isValid" in data) {
42
+ return data;
43
+ }
44
+ throw new Error(
45
+ `Circle Gateway verify failed (${response.status}): ${JSON.stringify(data)}`
46
+ );
47
+ }
48
+ /**
49
+ * Settle a payment with Circle Gateway.
50
+ *
51
+ * @param paymentPayload - The payment payload to settle
52
+ * @param paymentRequirements - The payment requirements
53
+ * @returns Settlement response
54
+ */
55
+ async settle(paymentPayload, paymentRequirements) {
56
+ let headers = {
57
+ "Content-Type": "application/json"
58
+ };
59
+ if (this._createAuthHeaders) {
60
+ const authHeaders = await this._createAuthHeaders();
61
+ headers = { ...headers, ...authHeaders.settle };
62
+ }
63
+ const response = await fetch(`${this.url}/v1/x402/settle`, {
64
+ method: "POST",
65
+ headers,
66
+ body: JSON.stringify({
67
+ paymentPayload: this.toJsonSafe(paymentPayload),
68
+ paymentRequirements: this.toJsonSafe(paymentRequirements)
69
+ })
70
+ });
71
+ const text = await response.text();
72
+ if (!text) {
73
+ throw new Error(
74
+ `Circle Gateway settle returned empty response (${response.status})`
75
+ );
76
+ }
77
+ const data = JSON.parse(text);
78
+ if (typeof data === "object" && data !== null && "success" in data) {
79
+ return data;
80
+ }
81
+ throw new Error(`Circle Gateway settle failed (${response.status}): ${text}`);
82
+ }
83
+ /**
84
+ * Get supported payment kinds from Circle Gateway.
85
+ *
86
+ * This fetches the supported networks and their GatewayWallet contract addresses.
87
+ * The response includes `extra.verifyingContract` for each supported network.
88
+ *
89
+ * @returns Supported payment kinds and extensions
90
+ */
91
+ async getSupported() {
92
+ let headers = {
93
+ "Content-Type": "application/json"
94
+ };
95
+ if (this._createAuthHeaders) {
96
+ const authHeaders = await this._createAuthHeaders();
97
+ headers = { ...headers, ...authHeaders.supported };
98
+ }
99
+ const response = await fetch(`${this.url}/v1/x402/supported`, {
100
+ method: "GET",
101
+ headers
102
+ });
103
+ if (!response.ok) {
104
+ const errorText = await response.text().catch(() => response.statusText);
105
+ throw new Error(
106
+ `Circle Gateway getSupported failed (${response.status}): ${errorText}`
107
+ );
108
+ }
109
+ return await response.json();
110
+ }
111
+ /**
112
+ * Helper to convert objects to JSON-safe format.
113
+ * Handles BigInt and other non-JSON types.
114
+ */
115
+ toJsonSafe(obj) {
116
+ return JSON.parse(
117
+ JSON.stringify(
118
+ obj,
119
+ (_, value) => typeof value === "bigint" ? value.toString() : value
120
+ )
121
+ );
122
+ }
123
+ };
124
+
125
+ // src/server/GatewayEvmScheme.ts
126
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
127
+
128
+ // src/client/GatewayClient.ts
129
+ import {
130
+ createPublicClient,
131
+ createWalletClient,
132
+ http,
133
+ parseUnits,
134
+ formatUnits,
135
+ pad,
136
+ zeroAddress,
137
+ maxUint256,
138
+ defineChain,
139
+ erc20Abi
140
+ } from "viem";
141
+ import { privateKeyToAccount } from "viem/accounts";
142
+ import * as chains from "viem/chains";
143
+ var sonicTestnet = defineChain({
144
+ id: 14601,
145
+ name: "Sonic Testnet",
146
+ nativeCurrency: { decimals: 18, name: "Sonic", symbol: "S" },
147
+ rpcUrls: {
148
+ default: { http: ["https://rpc.testnet.soniclabs.com"] }
149
+ },
150
+ blockExplorers: {
151
+ default: { name: "Sonic Testnet Explorer", url: "https://testnet.soniclabs.com/" }
152
+ },
153
+ testnet: true
154
+ });
155
+ var GATEWAY_DOMAINS = {
156
+ // Testnet
157
+ arbitrumSepolia: 3,
158
+ arcTestnet: 26,
159
+ avalancheFuji: 1,
160
+ baseSepolia: 6,
161
+ sepolia: 0,
162
+ hyperEvmTestnet: 19,
163
+ optimismSepolia: 2,
164
+ polygonAmoy: 7,
165
+ seiAtlantic: 16,
166
+ sonicTestnet: 13,
167
+ unichainSepolia: 10,
168
+ worldChainSepolia: 14,
169
+ // Mainnet
170
+ arbitrum: 3,
171
+ avalanche: 1,
172
+ base: 6,
173
+ mainnet: 0,
174
+ hyperEvm: 19,
175
+ optimism: 2,
176
+ polygon: 7,
177
+ sei: 16,
178
+ sonic: 13,
179
+ unichain: 10,
180
+ worldChain: 14
181
+ };
182
+ var TESTNET_GATEWAY_WALLET = "0x0077777d7EBA4688BDeF3E311b846F25870A19B9";
183
+ var TESTNET_GATEWAY_MINTER = "0x0022222ABE238Cc2C7Bb1f21003F0a260052475B";
184
+ var MAINNET_GATEWAY_WALLET = "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE";
185
+ var MAINNET_GATEWAY_MINTER = "0x2222222d7164433c4C09B0b0D809a9b52C04C205";
186
+ var CHAIN_CONFIGS = {
187
+ // Testnet chains
188
+ arbitrumSepolia: {
189
+ chain: chains.arbitrumSepolia,
190
+ domain: GATEWAY_DOMAINS.arbitrumSepolia,
191
+ usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
192
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
193
+ gatewayMinter: TESTNET_GATEWAY_MINTER
194
+ },
195
+ arcTestnet: {
196
+ chain: chains.arcTestnet,
197
+ domain: GATEWAY_DOMAINS.arcTestnet,
198
+ usdc: "0x3600000000000000000000000000000000000000",
199
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
200
+ gatewayMinter: TESTNET_GATEWAY_MINTER,
201
+ rpcUrl: "https://rpc.testnet.arc.network"
202
+ },
203
+ avalancheFuji: {
204
+ chain: chains.avalancheFuji,
205
+ domain: GATEWAY_DOMAINS.avalancheFuji,
206
+ usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
207
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
208
+ gatewayMinter: TESTNET_GATEWAY_MINTER
209
+ },
210
+ baseSepolia: {
211
+ chain: chains.baseSepolia,
212
+ domain: GATEWAY_DOMAINS.baseSepolia,
213
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
214
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
215
+ gatewayMinter: TESTNET_GATEWAY_MINTER,
216
+ rpcUrl: "https://sepolia-preconf.base.org"
217
+ },
218
+ sepolia: {
219
+ chain: chains.sepolia,
220
+ domain: GATEWAY_DOMAINS.sepolia,
221
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
222
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
223
+ gatewayMinter: TESTNET_GATEWAY_MINTER
224
+ },
225
+ hyperEvmTestnet: {
226
+ chain: chains.hyperliquidEvmTestnet,
227
+ domain: GATEWAY_DOMAINS.hyperEvmTestnet,
228
+ usdc: "0x2B3370eE501B4a559b57D449569354196457D8Ab",
229
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
230
+ gatewayMinter: TESTNET_GATEWAY_MINTER
231
+ },
232
+ optimismSepolia: {
233
+ chain: chains.optimismSepolia,
234
+ domain: GATEWAY_DOMAINS.optimismSepolia,
235
+ usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
236
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
237
+ gatewayMinter: TESTNET_GATEWAY_MINTER
238
+ },
239
+ polygonAmoy: {
240
+ chain: chains.polygonAmoy,
241
+ domain: GATEWAY_DOMAINS.polygonAmoy,
242
+ usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
243
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
244
+ gatewayMinter: TESTNET_GATEWAY_MINTER
245
+ },
246
+ seiAtlantic: {
247
+ chain: chains.seiTestnet,
248
+ domain: GATEWAY_DOMAINS.seiAtlantic,
249
+ usdc: "0x4fCF1784B31630811181f670Aea7A7bEF803eaED",
250
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
251
+ gatewayMinter: TESTNET_GATEWAY_MINTER
252
+ },
253
+ sonicTestnet: {
254
+ chain: sonicTestnet,
255
+ domain: GATEWAY_DOMAINS.sonicTestnet,
256
+ usdc: "0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51",
257
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
258
+ gatewayMinter: TESTNET_GATEWAY_MINTER
259
+ },
260
+ unichainSepolia: {
261
+ chain: chains.unichainSepolia,
262
+ domain: GATEWAY_DOMAINS.unichainSepolia,
263
+ usdc: "0x31d0220469e10c4E71834a79b1f276d740d3768F",
264
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
265
+ gatewayMinter: TESTNET_GATEWAY_MINTER
266
+ },
267
+ worldChainSepolia: {
268
+ chain: chains.worldchainSepolia,
269
+ domain: GATEWAY_DOMAINS.worldChainSepolia,
270
+ usdc: "0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88",
271
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
272
+ gatewayMinter: TESTNET_GATEWAY_MINTER
273
+ },
274
+ // Mainnet chains
275
+ arbitrum: {
276
+ chain: chains.arbitrum,
277
+ domain: GATEWAY_DOMAINS.arbitrum,
278
+ usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
279
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
280
+ gatewayMinter: MAINNET_GATEWAY_MINTER
281
+ },
282
+ avalanche: {
283
+ chain: chains.avalanche,
284
+ domain: GATEWAY_DOMAINS.avalanche,
285
+ usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
286
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
287
+ gatewayMinter: MAINNET_GATEWAY_MINTER
288
+ },
289
+ base: {
290
+ chain: chains.base,
291
+ domain: GATEWAY_DOMAINS.base,
292
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
293
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
294
+ gatewayMinter: MAINNET_GATEWAY_MINTER
295
+ },
296
+ mainnet: {
297
+ chain: chains.mainnet,
298
+ domain: GATEWAY_DOMAINS.mainnet,
299
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
300
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
301
+ gatewayMinter: MAINNET_GATEWAY_MINTER
302
+ },
303
+ hyperEvm: {
304
+ chain: chains.hyperEvm,
305
+ domain: GATEWAY_DOMAINS.hyperEvm,
306
+ usdc: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
307
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
308
+ gatewayMinter: MAINNET_GATEWAY_MINTER
309
+ },
310
+ optimism: {
311
+ chain: chains.optimism,
312
+ domain: GATEWAY_DOMAINS.optimism,
313
+ usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
314
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
315
+ gatewayMinter: MAINNET_GATEWAY_MINTER
316
+ },
317
+ polygon: {
318
+ chain: chains.polygon,
319
+ domain: GATEWAY_DOMAINS.polygon,
320
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
321
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
322
+ gatewayMinter: MAINNET_GATEWAY_MINTER
323
+ },
324
+ sei: {
325
+ chain: chains.sei,
326
+ domain: GATEWAY_DOMAINS.sei,
327
+ usdc: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
328
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
329
+ gatewayMinter: MAINNET_GATEWAY_MINTER
330
+ },
331
+ sonic: {
332
+ chain: chains.sonic,
333
+ domain: GATEWAY_DOMAINS.sonic,
334
+ usdc: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
335
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
336
+ gatewayMinter: MAINNET_GATEWAY_MINTER
337
+ },
338
+ unichain: {
339
+ chain: chains.unichain,
340
+ domain: GATEWAY_DOMAINS.unichain,
341
+ usdc: "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
342
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
343
+ gatewayMinter: MAINNET_GATEWAY_MINTER
344
+ },
345
+ worldChain: {
346
+ chain: chains.worldchain,
347
+ domain: GATEWAY_DOMAINS.worldChain,
348
+ usdc: "0x79A02482A880bCe3F13E09da970dC34dB4cD24D1",
349
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
350
+ gatewayMinter: MAINNET_GATEWAY_MINTER
351
+ }
352
+ };
353
+
354
+ // src/server/GatewayEvmScheme.ts
355
+ var GatewayEvmScheme = class extends ExactEvmScheme {
356
+ constructor() {
357
+ super();
358
+ this.registerGatewayMoneyParsers();
359
+ }
360
+ /**
361
+ * Enhances payment requirements by merging the facilitator's extra data.
362
+ *
363
+ * The base `ExactEvmScheme.enhancePaymentRequirements` returns requirements
364
+ * unchanged, dropping `supportedKind.extra`. Gateway payments require
365
+ * `extra.verifyingContract` (and other fields like `name`, `version`) to
366
+ * be present for clients to construct the correct signing domain.
367
+ */
368
+ async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
369
+ void extensionKeys;
370
+ return {
371
+ ...paymentRequirements,
372
+ maxTimeoutSeconds: 345600,
373
+ // 4 days — Gateway batches settlements asynchronously
374
+ extra: {
375
+ ...paymentRequirements.extra,
376
+ ...supportedKind.extra
377
+ }
378
+ };
379
+ }
380
+ /**
381
+ * Registers money parsers for all Gateway-supported networks.
382
+ *
383
+ * Builds a `network → USDC address` lookup from `CHAIN_CONFIGS` and
384
+ * registers a single parser that converts dollar amounts to USDC atomic
385
+ * units (6 decimals) for any Gateway-supported chain.
386
+ *
387
+ * Returns `null` for unknown networks so `ExactEvmScheme`'s built-in
388
+ * parser handles them.
389
+ */
390
+ registerGatewayMoneyParsers() {
391
+ const networkUsdcMap = /* @__PURE__ */ new Map();
392
+ for (const config of Object.values(CHAIN_CONFIGS)) {
393
+ const network = `eip155:${config.chain.id}`;
394
+ networkUsdcMap.set(network, config.usdc);
395
+ }
396
+ this.registerMoneyParser(async (amount, network) => {
397
+ const usdcAddress = networkUsdcMap.get(network);
398
+ if (!usdcAddress) {
399
+ return null;
400
+ }
401
+ return {
402
+ amount: Math.round(amount * 1e6).toString(),
403
+ asset: usdcAddress
404
+ };
405
+ });
406
+ }
407
+ };
408
+
409
+ // src/constants.ts
410
+ var CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
411
+ var CIRCLE_BATCHING_VERSION = "1";
412
+ var CIRCLE_BATCHING_SCHEME = "exact";
413
+
414
+ // src/detection.ts
415
+ function supportsBatching(requirements) {
416
+ const extra = requirements.extra;
417
+ if (!extra) {
418
+ return false;
419
+ }
420
+ return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
421
+ }
422
+ var isBatchPayment = supportsBatching;
423
+
424
+ // src/server/middleware.ts
425
+ function getUsdcAddress(kind) {
426
+ const assets = kind.extra?.assets;
427
+ if (!assets || assets.length === 0) return null;
428
+ const usdc = assets.find((a) => a.symbol === "USDC");
429
+ return usdc?.address ?? null;
430
+ }
431
+ function parsePrice(price) {
432
+ const numericPrice = price.replace(/[$]/g, "");
433
+ const amount = parseFloat(numericPrice);
434
+ if (isNaN(amount) || amount <= 0) {
435
+ throw new Error(`Invalid price: ${price}`);
436
+ }
437
+ return Math.round(amount * 1e6).toString();
438
+ }
439
+ function createGatewayMiddleware(config) {
440
+ const facilitator = new BatchFacilitatorClient({
441
+ url: config.facilitatorUrl
442
+ });
443
+ const configuredNetworks = config.networks ? Array.isArray(config.networks) ? config.networks : [config.networks] : null;
444
+ let cachedSupportedKinds = null;
445
+ async function getSupportedKinds() {
446
+ if (cachedSupportedKinds) {
447
+ return cachedSupportedKinds;
448
+ }
449
+ const supported = await facilitator.getSupported();
450
+ cachedSupportedKinds = supported.kinds;
451
+ return cachedSupportedKinds;
452
+ }
453
+ async function getAcceptedNetworks() {
454
+ const allKinds = await getSupportedKinds();
455
+ if (configuredNetworks) {
456
+ return allKinds.filter(
457
+ (k) => configuredNetworks.includes(k.network) && k.extra?.verifyingContract
458
+ );
459
+ }
460
+ return allKinds.filter((k) => k.extra?.verifyingContract);
461
+ }
462
+ async function createAllPaymentRequirements(price) {
463
+ const networks = await getAcceptedNetworks();
464
+ const amount = parsePrice(price);
465
+ return networks.filter((kind) => getUsdcAddress(kind)).map((kind) => ({
466
+ scheme: CIRCLE_BATCHING_SCHEME,
467
+ // Must be 'exact' for Gateway API
468
+ network: kind.network,
469
+ asset: getUsdcAddress(kind),
470
+ // Use actual USDC address
471
+ amount,
472
+ payTo: config.sellerAddress,
473
+ maxTimeoutSeconds: 345600,
474
+ // 4 days (same as digital-dungeon)
475
+ extra: {
476
+ name: CIRCLE_BATCHING_NAME,
477
+ version: CIRCLE_BATCHING_VERSION,
478
+ verifyingContract: kind.extra.verifyingContract
479
+ }
480
+ }));
481
+ }
482
+ async function createPaymentRequirements(price, network) {
483
+ const networks = await getAcceptedNetworks();
484
+ const kind = networks.find((k) => k.network === network);
485
+ if (!kind || !kind.extra?.verifyingContract) {
486
+ return null;
487
+ }
488
+ const usdcAddress = getUsdcAddress(kind);
489
+ if (!usdcAddress) {
490
+ return null;
491
+ }
492
+ return {
493
+ scheme: CIRCLE_BATCHING_SCHEME,
494
+ // Must be 'exact' for Gateway API
495
+ network: kind.network,
496
+ asset: usdcAddress,
497
+ // Use actual USDC address
498
+ amount: parsePrice(price),
499
+ payTo: config.sellerAddress,
500
+ maxTimeoutSeconds: 345600,
501
+ // 4 days
502
+ extra: {
503
+ name: CIRCLE_BATCHING_NAME,
504
+ version: CIRCLE_BATCHING_VERSION,
505
+ verifyingContract: kind.extra.verifyingContract
506
+ }
507
+ };
508
+ }
509
+ return {
510
+ require: (price) => {
511
+ return async (req, res, next) => {
512
+ try {
513
+ const paymentHeader = req.headers["payment-signature"];
514
+ if (!paymentHeader) {
515
+ const allRequirements = await createAllPaymentRequirements(price);
516
+ const url = req.url ?? "/";
517
+ const description = config.description ?? "Paid resource";
518
+ if (allRequirements.length === 0) {
519
+ res.statusCode = 503;
520
+ res.setHeader("Content-Type", "application/json");
521
+ res.end(JSON.stringify({ error: "No payment networks available" }));
522
+ return;
523
+ }
524
+ const paymentRequired = {
525
+ x402Version: 2,
526
+ // Version 2 required by Gateway API
527
+ resource: {
528
+ url,
529
+ description,
530
+ mimeType: "application/json"
531
+ },
532
+ accepts: allRequirements
533
+ };
534
+ const paymentRequiredHeader = Buffer.from(
535
+ JSON.stringify(paymentRequired)
536
+ ).toString("base64");
537
+ res.statusCode = 402;
538
+ res.setHeader("PAYMENT-REQUIRED", paymentRequiredHeader);
539
+ res.setHeader("Content-Type", "application/json");
540
+ res.end(JSON.stringify({}));
541
+ return;
542
+ }
543
+ const paymentPayload = JSON.parse(
544
+ Buffer.from(paymentHeader, "base64").toString("utf-8")
545
+ );
546
+ const acceptedRequirements = paymentPayload.accepted;
547
+ if (!acceptedRequirements?.network) {
548
+ res.statusCode = 400;
549
+ res.setHeader("Content-Type", "application/json");
550
+ res.end(
551
+ JSON.stringify({ error: "Missing accepted requirements in payment" })
552
+ );
553
+ return;
554
+ }
555
+ const requirements = await createPaymentRequirements(
556
+ price,
557
+ acceptedRequirements.network
558
+ );
559
+ if (!requirements) {
560
+ res.statusCode = 400;
561
+ res.setHeader("Content-Type", "application/json");
562
+ res.end(
563
+ JSON.stringify({
564
+ error: `Network ${acceptedRequirements.network} not accepted`
565
+ })
566
+ );
567
+ return;
568
+ }
569
+ const verifyResult = await facilitator.verify(paymentPayload, requirements);
570
+ if (!verifyResult.isValid) {
571
+ res.statusCode = 402;
572
+ res.setHeader("Content-Type", "application/json");
573
+ res.end(
574
+ JSON.stringify({
575
+ error: "Payment verification failed",
576
+ reason: verifyResult.invalidReason
577
+ })
578
+ );
579
+ return;
580
+ }
581
+ const settleResult = await facilitator.settle(paymentPayload, requirements);
582
+ if (!settleResult.success) {
583
+ res.statusCode = 402;
584
+ res.setHeader("Content-Type", "application/json");
585
+ res.end(
586
+ JSON.stringify({
587
+ error: "Payment settlement failed",
588
+ reason: settleResult.errorReason
589
+ })
590
+ );
591
+ return;
592
+ }
593
+ req.payment = {
594
+ verified: true,
595
+ payer: settleResult.payer ?? verifyResult.payer ?? "",
596
+ amount: parsePrice(price),
597
+ network: requirements.network,
598
+ transaction: settleResult.transaction
599
+ };
600
+ const settleResponseHeader = Buffer.from(
601
+ JSON.stringify({
602
+ success: true,
603
+ transaction: settleResult.transaction,
604
+ network: requirements.network,
605
+ payer: settleResult.payer ?? verifyResult.payer ?? ""
606
+ })
607
+ ).toString("base64");
608
+ res.setHeader("PAYMENT-RESPONSE", settleResponseHeader);
609
+ next();
610
+ } catch (error) {
611
+ res.statusCode = 500;
612
+ res.setHeader("Content-Type", "application/json");
613
+ res.end(
614
+ JSON.stringify({
615
+ error: "Payment processing error",
616
+ message: error.message
617
+ })
618
+ );
619
+ }
620
+ };
621
+ },
622
+ verify: async (payment) => {
623
+ try {
624
+ const paymentPayload = payment;
625
+ const result = await facilitator.verify(
626
+ paymentPayload.paymentPayload,
627
+ paymentPayload.paymentRequirements
628
+ );
629
+ return {
630
+ valid: result.isValid,
631
+ payer: result.payer,
632
+ error: result.invalidReason
633
+ };
634
+ } catch (error) {
635
+ return {
636
+ valid: false,
637
+ error: error.message
638
+ };
639
+ }
640
+ },
641
+ settle: async (payment) => {
642
+ try {
643
+ const paymentPayload = payment;
644
+ const result = await facilitator.settle(
645
+ paymentPayload.paymentPayload,
646
+ paymentPayload.paymentRequirements
647
+ );
648
+ return {
649
+ success: result.success,
650
+ transaction: result.transaction,
651
+ error: result.errorReason
652
+ };
653
+ } catch (error) {
654
+ return {
655
+ success: false,
656
+ error: error.message
657
+ };
658
+ }
659
+ }
660
+ };
661
+ }
662
+ export {
663
+ BatchFacilitatorClient,
664
+ GatewayEvmScheme,
665
+ createGatewayMiddleware,
666
+ isBatchPayment
667
+ };
668
+ //# sourceMappingURL=index.mjs.map