opentool 0.7.2 → 0.7.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.
@@ -1,969 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- // src/helpers/payment.ts
4
- var PAYMENT_SCHEMA_VERSION = 1;
5
- var paymentSchemaVersionSchema = z.literal(PAYMENT_SCHEMA_VERSION);
6
- var decimalStringSchema = z.string().regex(/^(?:0|[1-9]\d*)(?:\.\d+)?$/, "Value must be a positive decimal string");
7
- var currencySchema = z.object({
8
- code: z.string().min(2).max(12).transform((value) => value.toUpperCase()),
9
- symbol: z.string().min(1).max(6).optional(),
10
- decimals: z.number().int().min(0).max(36).optional(),
11
- kind: z.enum(["fiat", "crypto"]).default("crypto").optional(),
12
- description: z.string().optional()
13
- });
14
- var paymentAmountSchema = z.object({
15
- value: decimalStringSchema,
16
- currency: currencySchema,
17
- display: z.string().optional()
18
- });
19
- var cryptoAssetSchema = z.object({
20
- symbol: z.string().min(2).max(12),
21
- network: z.string().min(1).optional(),
22
- chainId: z.number().int().min(0).optional(),
23
- address: z.string().min(1).optional(),
24
- decimals: z.number().int().min(0).max(36).optional(),
25
- standard: z.enum(["erc20", "spl", "custom"]).default("erc20").optional(),
26
- description: z.string().optional()
27
- });
28
- var facilitatorConfigSchema = z.object({
29
- url: z.string().url(),
30
- vendor: z.string().optional(),
31
- verifyPath: z.string().default("/verify").optional(),
32
- settlePath: z.string().default("/settle").optional(),
33
- apiKey: z.string().optional(),
34
- apiKeyEnv: z.string().optional(),
35
- apiKeyHeader: z.string().default("Authorization").optional(),
36
- headers: z.record(z.string(), z.string()).optional(),
37
- timeoutMs: z.number().int().positive().optional()
38
- });
39
- var settlementTermsSchema = z.object({
40
- windowSeconds: z.number().int().positive().optional(),
41
- targetConfirmations: z.number().int().positive().optional(),
42
- finalityDescription: z.string().optional(),
43
- slaDescription: z.string().optional()
44
- });
45
- var paymentFieldSchema = z.object({
46
- key: z.string().min(1),
47
- label: z.string().min(1),
48
- required: z.boolean().default(true).optional(),
49
- description: z.string().optional(),
50
- example: z.string().optional()
51
- });
52
- var x402ProofSchema = z.object({
53
- mode: z.literal("x402"),
54
- scheme: z.string().min(1),
55
- network: z.string().min(1),
56
- version: z.number().int().min(1).optional(),
57
- facilitator: facilitatorConfigSchema.optional(),
58
- verifier: z.string().optional()
59
- });
60
- var directProofSchema = z.object({
61
- mode: z.literal("direct"),
62
- proofTypes: z.array(z.string().min(1)).nonempty(),
63
- verifier: z.string().optional(),
64
- instructions: z.string().optional(),
65
- fields: z.array(paymentFieldSchema).optional(),
66
- allowsManualReview: z.boolean().optional()
67
- });
68
- var paymentProofSchema = z.discriminatedUnion("mode", [
69
- x402ProofSchema,
70
- directProofSchema
71
- ]);
72
- var paymentOptionSchema = z.object({
73
- id: z.string().min(1),
74
- title: z.string().min(1),
75
- description: z.string().optional(),
76
- amount: paymentAmountSchema,
77
- asset: cryptoAssetSchema,
78
- payTo: z.string().min(1),
79
- resource: z.string().url().optional(),
80
- proof: paymentProofSchema,
81
- settlement: settlementTermsSchema.optional(),
82
- metadata: z.record(z.string(), z.unknown()).optional()
83
- });
84
- var paymentRequirementsSchema = z.object({
85
- schemaVersion: paymentSchemaVersionSchema,
86
- message: z.string().optional(),
87
- title: z.string().optional(),
88
- resource: z.string().url().optional(),
89
- accepts: z.array(paymentOptionSchema).nonempty(),
90
- metadata: z.record(z.string(), z.unknown()).optional(),
91
- fallbackText: z.string().optional()
92
- });
93
- var x402PaymentHeaderSchema = z.object({
94
- x402Version: z.number().int().min(1),
95
- scheme: z.string().min(1),
96
- network: z.string().min(1),
97
- payload: z.unknown(),
98
- correlationId: z.string().optional()
99
- });
100
- var directPaymentPayloadSchema = z.object({
101
- schemaVersion: z.literal(1),
102
- optionId: z.string().min(1),
103
- proofType: z.string().min(1),
104
- payload: z.unknown(),
105
- metadata: z.record(z.string(), z.unknown()).optional()
106
- });
107
- var paymentSuccessMetadataSchema = z.object({
108
- optionId: z.string().min(1),
109
- verifier: z.string().optional(),
110
- txHash: z.string().optional(),
111
- networkId: z.string().optional(),
112
- amount: paymentAmountSchema.optional(),
113
- settledAt: z.string().datetime().optional(),
114
- payload: z.unknown().optional()
115
- });
116
- var paymentFailureSchema = z.object({
117
- reason: z.string().min(1),
118
- code: z.enum([
119
- "verifier_not_found",
120
- "verification_failed",
121
- "invalid_payload",
122
- "unsupported_option",
123
- "missing_header",
124
- "unknown"
125
- ]).default("unknown").optional(),
126
- retryable: z.boolean().optional(),
127
- detail: z.unknown().optional()
128
- });
129
-
130
- // src/helpers/payment.ts
131
- var X402_VERSION_DEFAULT = 1;
132
- var HEADER_X402 = "X-PAYMENT";
133
- var HEADER_DIRECT = "X-PAYMENT-PROOF";
134
- var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
135
- var x402RequirementSchema = z.object({
136
- scheme: z.string().min(1),
137
- network: z.string().min(1),
138
- maxAmountRequired: z.string().min(1),
139
- asset: z.string().min(1),
140
- payTo: z.string().min(1),
141
- resource: z.string().optional(),
142
- description: z.string().optional(),
143
- mimeType: z.string().optional(),
144
- outputSchema: z.unknown().optional(),
145
- maxTimeoutSeconds: z.number().int().positive().optional(),
146
- extra: z.record(z.string(), z.unknown()).nullable().optional()
147
- });
148
- function createPaymentRequiredBody(definition) {
149
- const parsed = paymentRequirementsSchema.parse(definition);
150
- const x402Accepts = parsed.accepts.filter((option) => option.proof.mode === "x402").map(
151
- (option) => toX402Requirement(option, parsed.resource, option.settlement)
152
- ).filter((value) => Boolean(value));
153
- const x402Body = x402Accepts.length > 0 ? {
154
- x402Version: resolveX402Version(parsed.accepts),
155
- error: parsed.message ?? "Payment required",
156
- accepts: x402Accepts
157
- } : void 0;
158
- if (x402Body) {
159
- return {
160
- ...parsed,
161
- x402: x402Body
162
- };
163
- }
164
- return parsed;
165
- }
166
- function paymentRequiredResponse(definition, init) {
167
- const body = createPaymentRequiredBody(definition);
168
- const headers = new Headers(init?.headers);
169
- if (!headers.has("content-type")) {
170
- headers.set("content-type", "application/json; charset=utf-8");
171
- }
172
- return new Response(JSON.stringify(body), {
173
- ...init,
174
- status: 402,
175
- headers
176
- });
177
- }
178
- function extractPaymentAttempts(source) {
179
- const attempts = [];
180
- const failures = [];
181
- const x402Header = source.headers.get(HEADER_X402);
182
- if (x402Header) {
183
- const { attempt, failure } = parseX402Header(x402Header);
184
- if (attempt) {
185
- attempts.push(attempt);
186
- } else if (failure) {
187
- failures.push(failure);
188
- }
189
- }
190
- const directHeader = source.headers.get(HEADER_DIRECT);
191
- if (directHeader) {
192
- const { attempt, failure } = parseDirectHeader(directHeader);
193
- if (attempt) {
194
- attempts.push(attempt);
195
- } else if (failure) {
196
- failures.push(failure);
197
- }
198
- }
199
- if (attempts.length === 0 && failures.length === 0) {
200
- failures.push(
201
- paymentFailureSchema.parse({
202
- reason: "No payment headers present",
203
- code: "missing_header",
204
- retryable: false
205
- })
206
- );
207
- }
208
- return { attempts, failures };
209
- }
210
- async function verifyPayment(options) {
211
- const definition = paymentRequirementsSchema.parse(options.definition);
212
- const attempts = options.attempts ? options.attempts : options.request ? extractPaymentAttempts(options.request).attempts : [];
213
- if (attempts.length === 0) {
214
- return {
215
- success: false,
216
- optionId: "",
217
- attemptType: "direct",
218
- failure: paymentFailureSchema.parse({
219
- reason: "No payment attempt found",
220
- code: "missing_header",
221
- retryable: false
222
- })
223
- };
224
- }
225
- for (const attempt of attempts) {
226
- const option = findMatchingOption(definition, attempt);
227
- if (!option) {
228
- continue;
229
- }
230
- if (attempt.type === "x402") {
231
- const proof = option.proof;
232
- const verifierId = proof.verifier ?? (proof.facilitator ? "x402:facilitator" : void 0);
233
- if (verifierId === "x402:facilitator" && proof.facilitator) {
234
- const context2 = {
235
- attempt,
236
- option,
237
- definition
238
- };
239
- if (options.settle !== void 0) {
240
- context2.settle = options.settle;
241
- }
242
- return runFacilitatorVerifier({
243
- ...context2,
244
- fetchImpl: options.fetchImpl ?? fetch
245
- });
246
- }
247
- const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
248
- if (!verifier) {
249
- return {
250
- success: false,
251
- optionId: option.id,
252
- attemptType: attempt.type,
253
- failure: paymentFailureSchema.parse({
254
- reason: `No verifier registered for id: ${verifierId ?? "(missing)"}`,
255
- code: "verifier_not_found",
256
- retryable: false
257
- })
258
- };
259
- }
260
- const context = {
261
- attempt,
262
- option,
263
- definition
264
- };
265
- if (options.settle !== void 0) {
266
- context.settle = options.settle;
267
- }
268
- return verifier(context);
269
- }
270
- if (attempt.type === "direct") {
271
- const proof = option.proof;
272
- const verifierId = proof.verifier ?? `direct:${attempt.payload.proofType}`;
273
- const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
274
- if (!verifier) {
275
- return {
276
- success: false,
277
- optionId: option.id,
278
- attemptType: attempt.type,
279
- failure: paymentFailureSchema.parse({
280
- reason: `No verifier registered for id: ${verifierId}`,
281
- code: "verifier_not_found",
282
- retryable: false
283
- })
284
- };
285
- }
286
- const context = {
287
- attempt,
288
- option,
289
- definition
290
- };
291
- if (options.settle !== void 0) {
292
- context.settle = options.settle;
293
- }
294
- return verifier(context);
295
- }
296
- }
297
- return {
298
- success: false,
299
- optionId: "",
300
- attemptType: attempts[0]?.type ?? "direct",
301
- failure: paymentFailureSchema.parse({
302
- reason: "No matching payment option for attempt",
303
- code: "unsupported_option",
304
- retryable: false
305
- })
306
- };
307
- }
308
- function createPaymentResponseHeader(metadata) {
309
- const parsed = paymentSuccessMetadataSchema.parse(metadata);
310
- return encodeJson(parsed);
311
- }
312
- function parseX402Header(value) {
313
- try {
314
- const payload = decodeJson(value, x402PaymentHeaderSchema);
315
- return {
316
- attempt: {
317
- type: "x402",
318
- headerName: HEADER_X402,
319
- raw: value,
320
- payload
321
- }
322
- };
323
- } catch (error) {
324
- return {
325
- failure: paymentFailureSchema.parse({
326
- reason: `Invalid X-PAYMENT header: ${error.message}`,
327
- code: "invalid_payload",
328
- retryable: false
329
- })
330
- };
331
- }
332
- }
333
- function parseDirectHeader(value) {
334
- try {
335
- const payload = decodeJson(value, directPaymentPayloadSchema);
336
- return {
337
- attempt: {
338
- type: "direct",
339
- headerName: HEADER_DIRECT,
340
- raw: value,
341
- payload
342
- }
343
- };
344
- } catch (error) {
345
- return {
346
- failure: paymentFailureSchema.parse({
347
- reason: `Invalid X-PAYMENT-PROOF header: ${error.message}`,
348
- code: "invalid_payload",
349
- retryable: false
350
- })
351
- };
352
- }
353
- }
354
- function findMatchingOption(definition, attempt) {
355
- return definition.accepts.find((candidate) => {
356
- const option = paymentOptionSchema.parse(candidate);
357
- if (attempt.type === "x402" && option.proof.mode === "x402") {
358
- return option.proof.scheme === attempt.payload.scheme && option.proof.network === attempt.payload.network;
359
- }
360
- if (attempt.type === "direct" && option.proof.mode === "direct") {
361
- return option.id === attempt.payload.optionId;
362
- }
363
- return false;
364
- });
365
- }
366
- function resolveX402Version(options) {
367
- const versions = [];
368
- for (const option of options) {
369
- if (option.proof.mode === "x402" && option.proof.version) {
370
- versions.push(option.proof.version);
371
- }
372
- }
373
- return versions.length > 0 ? Math.max(...versions) : X402_VERSION_DEFAULT;
374
- }
375
- function toX402Requirement(option, fallbackResource, settlement) {
376
- if (option.proof.mode !== "x402") {
377
- return void 0;
378
- }
379
- const decimals = resolveDecimals(option);
380
- const assetAddress = option.asset.address;
381
- if (!assetAddress) {
382
- throw new Error(
383
- `x402 payment option '${option.id}' is missing asset.address`
384
- );
385
- }
386
- const units = decimalToBaseUnits(option.amount.value, decimals);
387
- return x402RequirementSchema.parse({
388
- scheme: option.proof.scheme,
389
- network: option.proof.network,
390
- maxAmountRequired: units,
391
- asset: assetAddress,
392
- payTo: option.payTo,
393
- resource: option.resource ?? fallbackResource,
394
- description: option.description,
395
- maxTimeoutSeconds: settlement?.windowSeconds,
396
- extra: {
397
- symbol: option.asset.symbol,
398
- currencyCode: option.amount.currency.code,
399
- decimals
400
- }
401
- });
402
- }
403
- function resolveDecimals(option) {
404
- if (typeof option.asset.decimals === "number") {
405
- return option.asset.decimals;
406
- }
407
- if (typeof option.amount.currency.decimals === "number") {
408
- return option.amount.currency.decimals;
409
- }
410
- throw new Error(
411
- `Payment option '${option.id}' must specify asset.decimals or currency.decimals`
412
- );
413
- }
414
- function decimalToBaseUnits(value, decimals) {
415
- const [whole, fraction = ""] = value.split(".");
416
- const sanitizedFraction = fraction.slice(0, decimals);
417
- const paddedFraction = sanitizedFraction.padEnd(decimals, "0");
418
- const combined = `${whole}${paddedFraction}`.replace(/^0+/, "");
419
- return combined.length > 0 ? combined : "0";
420
- }
421
- function decodeJson(value, schema) {
422
- const base64 = normalizeBase64(value);
423
- const json = Buffer.from(base64, "base64").toString("utf-8");
424
- const parsed = JSON.parse(json);
425
- return schema.parse(parsed);
426
- }
427
- function encodeJson(value) {
428
- const json = JSON.stringify(value);
429
- return Buffer.from(json, "utf-8").toString("base64");
430
- }
431
- function normalizeBase64(input) {
432
- if (/^[A-Za-z0-9+/=]+$/.test(input)) {
433
- return input;
434
- }
435
- const restored = input.replace(/-/g, "+").replace(/_/g, "/");
436
- const paddingNeeded = (4 - restored.length % 4) % 4;
437
- return restored + "=".repeat(paddingNeeded);
438
- }
439
- async function runFacilitatorVerifier({
440
- attempt,
441
- option,
442
- definition,
443
- settle,
444
- fetchImpl
445
- }) {
446
- if (option.proof.mode !== "x402" || attempt.type !== "x402" || !option.proof.facilitator) {
447
- return {
448
- success: false,
449
- optionId: option.id,
450
- attemptType: attempt.type,
451
- failure: paymentFailureSchema.parse({
452
- reason: "Facilitator verifier invoked for non-x402 option",
453
- code: "verification_failed",
454
- retryable: false
455
- })
456
- };
457
- }
458
- const facilitator = option.proof.facilitator;
459
- const verifierUrl = new URL(
460
- facilitator.verifyPath ?? "/verify",
461
- ensureTrailingSlash(facilitator.url)
462
- ).toString();
463
- const requirement = toX402Requirement(option, definition.resource, option.settlement);
464
- if (!requirement) {
465
- return {
466
- success: false,
467
- optionId: option.id,
468
- attemptType: attempt.type,
469
- failure: paymentFailureSchema.parse({
470
- reason: "Unable to derive x402 requirement for facilitator",
471
- code: "verification_failed",
472
- retryable: false
473
- })
474
- };
475
- }
476
- const headers = buildFacilitatorHeaders(facilitator);
477
- const controller = facilitator.timeoutMs ? new AbortController() : void 0;
478
- const timeout = facilitator.timeoutMs ? setTimeout(() => controller?.abort(), facilitator.timeoutMs) : void 0;
479
- try {
480
- const verifyResponse = await fetchImpl(verifierUrl, {
481
- method: "POST",
482
- headers,
483
- body: JSON.stringify({
484
- x402Version: attempt.payload.x402Version,
485
- paymentHeader: attempt.raw,
486
- paymentRequirements: requirement
487
- }),
488
- signal: controller?.signal ?? null
489
- });
490
- if (!verifyResponse.ok) {
491
- return {
492
- success: false,
493
- optionId: option.id,
494
- attemptType: attempt.type,
495
- failure: paymentFailureSchema.parse({
496
- reason: `Facilitator verify request failed: ${verifyResponse.status}`,
497
- code: "verification_failed",
498
- retryable: verifyResponse.status >= 500
499
- })
500
- };
501
- }
502
- const verifyPayload = await verifyResponse.json();
503
- if (!verifyPayload.isValid) {
504
- return {
505
- success: false,
506
- optionId: option.id,
507
- attemptType: attempt.type,
508
- failure: paymentFailureSchema.parse({
509
- reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
510
- code: "verification_failed",
511
- retryable: false
512
- })
513
- };
514
- }
515
- if (!settle) {
516
- return {
517
- success: true,
518
- optionId: option.id,
519
- attemptType: attempt.type,
520
- metadata: paymentSuccessMetadataSchema.parse({
521
- optionId: option.id,
522
- verifier: facilitator.vendor ?? "facilitator"
523
- })
524
- };
525
- }
526
- const settleUrl = new URL(
527
- facilitator.settlePath ?? "/settle",
528
- ensureTrailingSlash(facilitator.url)
529
- ).toString();
530
- const settleResponse = await fetchImpl(settleUrl, {
531
- method: "POST",
532
- headers,
533
- body: JSON.stringify({
534
- x402Version: attempt.payload.x402Version,
535
- paymentHeader: attempt.raw,
536
- paymentRequirements: requirement
537
- }),
538
- signal: controller?.signal ?? null
539
- });
540
- if (!settleResponse.ok) {
541
- return {
542
- success: false,
543
- optionId: option.id,
544
- attemptType: attempt.type,
545
- failure: paymentFailureSchema.parse({
546
- reason: `Facilitator settle request failed: ${settleResponse.status}`,
547
- code: "verification_failed",
548
- retryable: settleResponse.status >= 500
549
- })
550
- };
551
- }
552
- const settlePayload = await settleResponse.json();
553
- if (!settlePayload.success) {
554
- return {
555
- success: false,
556
- optionId: option.id,
557
- attemptType: attempt.type,
558
- failure: paymentFailureSchema.parse({
559
- reason: settlePayload.error ?? "Facilitator settlement failed",
560
- code: "verification_failed",
561
- retryable: false
562
- })
563
- };
564
- }
565
- const metadata = paymentSuccessMetadataSchema.parse({
566
- optionId: option.id,
567
- verifier: facilitator.vendor ?? "facilitator",
568
- txHash: settlePayload.txHash ?? void 0,
569
- networkId: settlePayload.networkId ?? void 0
570
- });
571
- return {
572
- success: true,
573
- optionId: option.id,
574
- attemptType: attempt.type,
575
- metadata,
576
- responseHeaders: {
577
- [HEADER_PAYMENT_RESPONSE]: createPaymentResponseHeader(metadata)
578
- }
579
- };
580
- } catch (error) {
581
- return {
582
- success: false,
583
- optionId: option.id,
584
- attemptType: attempt.type,
585
- failure: paymentFailureSchema.parse({
586
- reason: `Facilitator request error: ${error.message}`,
587
- code: "verification_failed",
588
- retryable: false
589
- })
590
- };
591
- } finally {
592
- if (timeout) {
593
- clearTimeout(timeout);
594
- }
595
- }
596
- }
597
- function buildFacilitatorHeaders(config) {
598
- const headers = {
599
- "content-type": "application/json"
600
- };
601
- if (config?.headers) {
602
- Object.assign(headers, config.headers);
603
- }
604
- const apiKey = resolveFacilitatorApiKey(config);
605
- if (apiKey) {
606
- const headerName = config?.apiKeyHeader ?? "Authorization";
607
- headers[headerName] = apiKey;
608
- }
609
- return headers;
610
- }
611
- function resolveFacilitatorApiKey(config) {
612
- if (!config) {
613
- return void 0;
614
- }
615
- if (config.apiKey) {
616
- return config.apiKey;
617
- }
618
- if (config.apiKeyEnv && typeof process !== "undefined") {
619
- return process.env?.[config.apiKeyEnv];
620
- }
621
- return void 0;
622
- }
623
- function ensureTrailingSlash(value) {
624
- return value.endsWith("/") ? value : `${value}/`;
625
- }
626
- var PAYMENT_HEADERS = {
627
- x402: HEADER_X402,
628
- direct: HEADER_DIRECT,
629
- response: HEADER_PAYMENT_RESPONSE
630
- };
631
-
632
- // src/payment/index.ts
633
- var DEFAULT_ID_X402 = "x402";
634
- var DEFAULT_ID_402 = "402";
635
- var SUPPORTED_CURRENCIES = {
636
- USDC: {
637
- decimals: 6,
638
- symbol: "USDC",
639
- x402: {
640
- network: "base",
641
- assetAddress: "0x833589fCD6eDb6E08f4c7C37b7b4c6e997E08A43"
642
- }
643
- }
644
- };
645
- var DEFAULT_FACILITATORS = {
646
- opentool: "https://facilitator.opentool.dev/x402",
647
- coinbase: "https://payments.coinbase.com/x402"
648
- };
649
- var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.payment.context");
650
- var PaymentRequiredError = class extends Error {
651
- constructor(response, verification) {
652
- super("Payment required");
653
- this.name = "PaymentRequiredError";
654
- this.response = response;
655
- this.verification = verification;
656
- }
657
- };
658
- function setPaymentContext(request, context) {
659
- try {
660
- Object.defineProperty(request, PAYMENT_CONTEXT_SYMBOL, {
661
- value: context,
662
- configurable: true,
663
- enumerable: false,
664
- writable: true
665
- });
666
- } catch {
667
- request[PAYMENT_CONTEXT_SYMBOL] = context;
668
- }
669
- }
670
- function getPaymentContext(request) {
671
- return request[PAYMENT_CONTEXT_SYMBOL];
672
- }
673
- function applyPaymentHeaders(response, headers) {
674
- const entries = Object.entries(headers ?? {});
675
- if (entries.length === 0) {
676
- return response;
677
- }
678
- let mutated = false;
679
- const merged = new Headers(response.headers);
680
- for (const [key, value] of entries) {
681
- if (!merged.has(key)) {
682
- merged.set(key, value);
683
- mutated = true;
684
- }
685
- }
686
- if (!mutated) {
687
- return response;
688
- }
689
- return new Response(response.body, {
690
- status: response.status,
691
- statusText: response.statusText,
692
- headers: merged
693
- });
694
- }
695
- function withPaymentRequirement(handler, payment, options = {}) {
696
- return async (request) => {
697
- const verification = await requirePayment(request, payment, options);
698
- if (verification instanceof Response) {
699
- return verification;
700
- }
701
- setPaymentContext(request, verification);
702
- const response = await Promise.resolve(handler(request));
703
- return applyPaymentHeaders(response, verification.headers);
704
- };
705
- }
706
- function definePayment(config) {
707
- const verifiers = {
708
- ...config.verifiers ?? {}
709
- };
710
- const methods = config.acceptedMethods ?? ["402"];
711
- const includeX402 = methods.includes("x402");
712
- const includePlain402 = methods.includes("402");
713
- if (!includeX402 && !includePlain402) {
714
- throw new Error(
715
- "definePayment requires at least one payment transport (x402 or 402)"
716
- );
717
- }
718
- const currencyCode = normalizeCurrency(config.currency);
719
- const currencySpec = SUPPORTED_CURRENCIES[currencyCode];
720
- if (!currencySpec) {
721
- throw new Error(`Unsupported currency for payments: ${currencyCode}`);
722
- }
723
- const decimals = currencySpec.decimals;
724
- const symbol = currencySpec.symbol;
725
- const value = toDecimalString(config.amount);
726
- const accepts = [];
727
- if (includeX402) {
728
- const overrides = config.x402 ?? {};
729
- const defaults = currencySpec.x402;
730
- if (!defaults && (!overrides.network || !overrides.assetAddress)) {
731
- throw new Error(
732
- "x402 payments require a network and assetAddress; supply them or choose a supported currency."
733
- );
734
- }
735
- const facilitator = resolveFacilitator(
736
- config.facilitator ?? overrides.facilitator ?? "opentool"
737
- );
738
- accepts.push(
739
- paymentOptionSchema.parse({
740
- id: overrides.id ?? DEFAULT_ID_X402,
741
- title: `Pay ${value} ${currencyCode}`,
742
- amount: {
743
- value,
744
- currency: { code: currencyCode, symbol, decimals }
745
- },
746
- asset: {
747
- symbol,
748
- network: overrides.network ?? defaults?.network ?? "",
749
- address: overrides.assetAddress ?? defaults?.assetAddress ?? "",
750
- decimals,
751
- standard: "erc20"
752
- },
753
- payTo: config.payTo,
754
- proof: {
755
- mode: "x402",
756
- network: overrides.network ?? defaults?.network ?? "",
757
- scheme: overrides.scheme ?? "exact",
758
- version: overrides.version ?? 1,
759
- facilitator,
760
- verifier: facilitator ? "x402:facilitator" : void 0
761
- },
762
- settlement: overrides.settlement
763
- })
764
- );
765
- }
766
- if (includePlain402) {
767
- const overrides = config.direct ?? {};
768
- const id = overrides.id ?? DEFAULT_ID_402;
769
- const verifierId = overrides.verifierId ?? `direct:${id}`;
770
- const proofType = overrides.proofType ?? id;
771
- const verifier = overrides.verify ?? buildDefaultDirectVerifier(overrides.token, verifierId, id);
772
- verifiers[verifierId] = verifier;
773
- accepts.push(
774
- paymentOptionSchema.parse({
775
- id,
776
- title: `Pay ${value} ${currencyCode}`,
777
- amount: {
778
- value,
779
- currency: { code: currencyCode, symbol, decimals }
780
- },
781
- asset: {
782
- symbol,
783
- decimals,
784
- standard: "erc20"
785
- },
786
- payTo: config.payTo,
787
- proof: {
788
- mode: "direct",
789
- proofTypes: [proofType],
790
- verifier: verifierId,
791
- instructions: overrides.instructions,
792
- fields: overrides.fields,
793
- allowsManualReview: overrides.allowsManualReview
794
- },
795
- settlement: overrides.settlement
796
- })
797
- );
798
- }
799
- const facilitatorLabel = includeX402 ? resolveFacilitatorLabel(config.facilitator ?? config.x402?.facilitator) : void 0;
800
- const baseMetadata = {};
801
- if (currencyCode === "USDC") {
802
- baseMetadata.amountUSDC = Number(value);
803
- }
804
- baseMetadata.x402 = includeX402;
805
- baseMetadata.plain402 = includePlain402;
806
- baseMetadata.acceptedMethods = methods;
807
- baseMetadata.acceptedCurrencies = config.acceptedCurrencies ?? [currencyCode];
808
- if (config.chains) {
809
- baseMetadata.chains = config.chains;
810
- }
811
- if (facilitatorLabel) {
812
- baseMetadata.facilitator = facilitatorLabel;
813
- }
814
- const metadata = config.metadata ? { ...baseMetadata, ...config.metadata } : baseMetadata;
815
- const definition = {
816
- schemaVersion: PAYMENT_SCHEMA_VERSION,
817
- accepts,
818
- metadata
819
- };
820
- if (config.message !== void 0) {
821
- definition.message = config.message;
822
- }
823
- if (config.resource !== void 0) {
824
- definition.resource = config.resource;
825
- }
826
- const defined = {
827
- definition,
828
- verifiers,
829
- metadata
830
- };
831
- if (config.message !== void 0) {
832
- defined.message = config.message;
833
- }
834
- return defined;
835
- }
836
- async function requirePayment(request, payment, options = {}) {
837
- const { definition, verifiers } = normalizePayment(payment);
838
- const mergedVerifiers = {
839
- ...verifiers,
840
- ...options.verifiers ?? {}
841
- };
842
- const verifyOptions = {
843
- definition,
844
- request
845
- };
846
- if (Object.keys(mergedVerifiers).length > 0) {
847
- verifyOptions.verifiers = mergedVerifiers;
848
- }
849
- if (options.settle !== void 0) {
850
- verifyOptions.settle = options.settle;
851
- }
852
- if (options.fetchImpl) {
853
- verifyOptions.fetchImpl = options.fetchImpl;
854
- }
855
- const verification = await verifyPayment(verifyOptions);
856
- if (!verification.success || !verification.metadata) {
857
- if (options.onFailure) {
858
- return options.onFailure(verification);
859
- }
860
- const response = paymentRequiredResponse(definition);
861
- throw new PaymentRequiredError(response, verification);
862
- }
863
- return {
864
- payment: verification.metadata,
865
- headers: verification.responseHeaders ?? {},
866
- optionId: verification.optionId,
867
- result: verification
868
- };
869
- }
870
- function normalizePayment(payment) {
871
- if (isDefinedPayment(payment)) {
872
- return {
873
- definition: payment.definition,
874
- verifiers: payment.verifiers ?? {}
875
- };
876
- }
877
- return {
878
- definition: payment,
879
- verifiers: {}
880
- };
881
- }
882
- function isDefinedPayment(value) {
883
- return !!value && typeof value === "object" && "definition" in value && value.definition !== void 0;
884
- }
885
- function resolveFacilitator(value) {
886
- if (!value) {
887
- return void 0;
888
- }
889
- if (typeof value === "string") {
890
- if (value in DEFAULT_FACILITATORS) {
891
- return {
892
- url: DEFAULT_FACILITATORS[value]
893
- };
894
- }
895
- return { url: value };
896
- }
897
- return value;
898
- }
899
- function resolveFacilitatorLabel(value) {
900
- if (!value) {
901
- return "opentool";
902
- }
903
- if (typeof value === "string") {
904
- if (value === "opentool" || value === "coinbase") {
905
- return value;
906
- }
907
- return "custom";
908
- }
909
- return "custom";
910
- }
911
- function normalizeCurrency(currency) {
912
- return (currency ?? "USDC").toUpperCase();
913
- }
914
- function toDecimalString(value) {
915
- return typeof value === "number" ? value.toString() : value;
916
- }
917
- function buildDefaultDirectVerifier(expectedToken, verifierId, optionId) {
918
- return async ({ attempt, option }) => {
919
- if (attempt.type !== "direct") {
920
- return {
921
- success: false,
922
- optionId: option.id,
923
- attemptType: attempt.type,
924
- failure: {
925
- reason: "Expected direct payment payload",
926
- code: "invalid_payload"
927
- }
928
- };
929
- }
930
- const payload = attempt.payload.payload;
931
- if (expectedToken) {
932
- if (payload?.token !== expectedToken) {
933
- return {
934
- success: false,
935
- optionId: option.id,
936
- attemptType: attempt.type,
937
- failure: {
938
- reason: "Invalid or missing payment proof",
939
- code: "verification_failed"
940
- }
941
- };
942
- }
943
- } else if (!payload) {
944
- return {
945
- success: false,
946
- optionId: option.id,
947
- attemptType: attempt.type,
948
- failure: {
949
- reason: "Payment proof is required",
950
- code: "verification_failed"
951
- }
952
- };
953
- }
954
- return {
955
- success: true,
956
- optionId,
957
- attemptType: attempt.type,
958
- metadata: {
959
- optionId,
960
- verifier: verifierId,
961
- payload
962
- }
963
- };
964
- };
965
- }
966
-
967
- export { PAYMENT_HEADERS, PaymentRequiredError, definePayment, getPaymentContext, requirePayment, withPaymentRequirement };
968
- //# sourceMappingURL=index.js.map
969
- //# sourceMappingURL=index.js.map