@reinconsole/gate 0.1.0

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,561 @@
1
+ import { GateReceipt, ReinEvent } from '@reinconsole/core';
2
+ export { GateReceipt, ReinEvent } from '@reinconsole/core';
3
+ import { PaymentRequirement, PaymentRequired, FetchLike } from '@reinconsole/sdk';
4
+ export { PaymentRequired, PaymentRequirement } from '@reinconsole/sdk';
5
+ import { IncomingMessage, ServerResponse } from 'node:http';
6
+
7
+ /**
8
+ * Why the gate turned a payment away. Stable strings — clients switch on these.
9
+ *
10
+ * Two classes carry no payer fault and MUST NOT feed reputation evidence
11
+ * (@reinconsole/graph skips them): the throttle codes (`rate_limited`,
12
+ * `velocity_exceeded` — the vendor's cap, not payer misbehavior) and the rails
13
+ * codes (`rails_unavailable`, `settle_unknown` — the vendor's infrastructure
14
+ * failing; a settle_unknown payment may even have gone through).
15
+ */
16
+ type GateRefusalCode = 'malformed_payment' | 'scheme_mismatch' | 'network_mismatch' | 'amount_mismatch' | 'recipient_mismatch' | 'payer_denied' | 'payer_not_allowed' | 'payment_replayed'
17
+ /** Too many presentations in the velocity window (429; Retry-After set). */
18
+ | 'rate_limited'
19
+ /** The settled-spend cap would be exceeded (429; Retry-After when a slot frees). */
20
+ | 'velocity_exceeded' | 'verify_failed' | 'settle_failed'
21
+ /** The rails could not be reached and the payment provably did NOT settle
22
+ * (503). The replay slot is released — the same header may be re-presented. */
23
+ | 'rails_unavailable'
24
+ /** Settlement was attempted but its fate is unknown — the request may have
25
+ * reached the rails (503). The slot stays burned; do NOT re-pay blindly:
26
+ * reconcile against transaction records (e.g. the on-chain indexer) first. */
27
+ | 'settle_unknown';
28
+ declare class GateError extends Error {
29
+ readonly code: GateRefusalCode;
30
+ /** Seconds until the refused payment could be admitted (throttle codes). */
31
+ readonly retryAfterSeconds?: number;
32
+ constructor(code: GateRefusalCode, message: string, options?: {
33
+ retryAfterSeconds?: number;
34
+ });
35
+ }
36
+
37
+ /**
38
+ * The gate's settlement seam. The gate decides WHETHER a payment is acceptable
39
+ * (routes, screening, replay, amount cross-checks); the rails decide whether it
40
+ * is VALID and move the money. Both Rein rails plug in via the structural
41
+ * adapters below — @reinconsole/gate deliberately imports neither, so vendors install
42
+ * only what they run.
43
+ */
44
+ interface GateRails {
45
+ /** Throw GateError('verify_failed') if the payment does not check out. */
46
+ verify(paymentHeader: string, requirement: PaymentRequirement): Promise<void>;
47
+ /** Move the money. Throw GateError('settle_failed') if it cannot. */
48
+ settle(paymentHeader: string, requirement: PaymentRequirement): Promise<GateSettlement>;
49
+ }
50
+ /**
51
+ * Error taxonomy at this seam — the gate's retry policy hangs off it:
52
+ *
53
+ * - `GateError` — a semantic verdict (bad signature, rejected authorization).
54
+ * Final; never retried.
55
+ * - `RailsUnreachableError` — the request PROVABLY never reached the rails
56
+ * (connection refused, DNS failure). Safe to retry, even for settle.
57
+ * - anything else — ambiguous transport failure (timeout, connection reset,
58
+ * gateway error): the request MAY have executed. verify is read-only so the
59
+ * gate retries it anyway; an ambiguous settle failure is never retried and
60
+ * refuses `settle_unknown`, because re-settling an authorization that
61
+ * actually landed would misreport a paid payment as failed.
62
+ */
63
+ declare class RailsUnreachableError extends Error {
64
+ constructor(message: string, options?: {
65
+ cause?: unknown;
66
+ });
67
+ }
68
+ interface GateSettlement {
69
+ /** Ready-made X-PAYMENT-RESPONSE header value. */
70
+ header: string;
71
+ /** Settlement transaction hash (mock ledger or on-chain). */
72
+ transaction: string;
73
+ network: string;
74
+ payer?: string;
75
+ }
76
+ /** What the gate needs of @reinconsole/mock-rails' MockFacilitator (structural). */
77
+ interface MockFacilitatorLike {
78
+ verify(paymentHeader: string, requirement: PaymentRequirement): unknown;
79
+ settle(paymentHeader: string, requirement: PaymentRequirement): {
80
+ header: string;
81
+ response: {
82
+ transaction: string;
83
+ network: string;
84
+ payer?: string;
85
+ };
86
+ };
87
+ }
88
+ /** Wire the gate to the mock rails (offline twin; throws map to refusals). */
89
+ declare function mockFacilitatorRails(facilitator: MockFacilitatorLike): GateRails;
90
+ /** What the gate needs of @reinconsole/x402-rails' FacilitatorClient (structural).
91
+ * `requirements` is untyped because the DIALECT varies per payment: v1
92
+ * payments relay Rein's internal (v1-shaped) requirement, v2 payments the
93
+ * converted v2 shape (see facilitatorClientRails). */
94
+ interface FacilitatorClientLike {
95
+ verify(payload: unknown, requirements: unknown): Promise<{
96
+ isValid: boolean;
97
+ invalidReason?: string;
98
+ }>;
99
+ settle(payload: unknown, requirements: unknown): Promise<{
100
+ success: boolean;
101
+ errorReason?: string;
102
+ transaction: string;
103
+ network: string;
104
+ payer?: string;
105
+ }>;
106
+ }
107
+ /**
108
+ * Wire the gate to a real x402 facilitator client. The facilitator wants the
109
+ * DECODED payment envelope; the gate re-decodes the header it already
110
+ * inspected and relays the facilitator's settle response verbatim into the
111
+ * settlement header (tx hash included). Requirements travel in the same
112
+ * dialect the payment arrived in — a v2 envelope is verified against
113
+ * v2-shaped (amount/CAIP-2) requirements.
114
+ */
115
+ declare function facilitatorClientRails(client: FacilitatorClientLike): GateRails;
116
+
117
+ /**
118
+ * One priced route. Anything a route doesn't override falls back to the
119
+ * gate-level payment defaults, so most vendors only write `path` + `price`.
120
+ */
121
+ interface GateRoute {
122
+ /** Glob over the request path, e.g. "/api/reports/*". First match wins. */
123
+ path: string;
124
+ /** HTTP method (case-insensitive); omit to price every method. */
125
+ method?: string;
126
+ /** Human-unit decimal price, e.g. "0.05". */
127
+ price: string;
128
+ description?: string;
129
+ mimeType?: string;
130
+ payTo?: string;
131
+ network?: string;
132
+ asset?: string;
133
+ /** Atomic decimals of the asset (default 6, all Rein stablecoins). */
134
+ decimals?: number;
135
+ maxTimeoutSeconds?: number;
136
+ /** EIP-712 domain hints etc., merged over the gate-level extra. */
137
+ extra?: Record<string, unknown>;
138
+ }
139
+ /** Gate-level payment defaults every route inherits. */
140
+ interface PaymentDefaults {
141
+ payTo: string;
142
+ network: string;
143
+ asset: string;
144
+ decimals?: number;
145
+ maxTimeoutSeconds?: number;
146
+ extra?: Record<string, unknown>;
147
+ }
148
+ /** First route whose method and path glob accept the request, or undefined. */
149
+ declare function matchRoute(routes: readonly GateRoute[], method: string, pathname: string): GateRoute | undefined;
150
+ declare function routeDecimals(route: GateRoute, defaults: PaymentDefaults): number;
151
+ /**
152
+ * Build the v1 payment requirement this route quotes for a concrete resource
153
+ * URL. Fully populated (description/mimeType/maxTimeoutSeconds, absolute
154
+ * resource) because hosted facilitators validate requirements strictly.
155
+ */
156
+ declare function requirementFor(route: GateRoute, defaults: PaymentDefaults, resourceUrl: string): PaymentRequirement;
157
+
158
+ /** Sync for in-memory stores; durable stores return a promise. */
159
+ type MaybePromise<T> = T | Promise<T>;
160
+ /**
161
+ * The gate's storage seam. Two consistency classes live here, split the same
162
+ * way the engine and graph stores are:
163
+ *
164
+ * - **Replay slots are security state** — the gate AWAITS `burnReplay`, and a
165
+ * durable impl persists the burn BEFORE resolving. A payment settles only
166
+ * after its slot is durably burned, so a crash-and-restart cannot let the
167
+ * same header settle twice (the mock ledger, unlike the chain, would).
168
+ * - **Receipts and counters are telemetry** — writes may trail behind
169
+ * (cache-then-persist), with `flush()` as the error channel: it drains
170
+ * pending writes and throws the first failure since the last flush.
171
+ *
172
+ * Reads are synchronous from the working set (`stats()` stays sync).
173
+ */
174
+ interface GateStorePort {
175
+ /**
176
+ * Check-and-burn a replay slot: returns false when this exact payment was
177
+ * already presented. The check-and-set MUST happen synchronously at call
178
+ * time so two concurrent copies of one header cannot both see it fresh;
179
+ * a durable impl then persists the burn before resolving. Slots are never
180
+ * released — a failed settle after the burn stays burned (deliberate:
181
+ * the payer must re-quote).
182
+ */
183
+ burnReplay(key: string): MaybePromise<boolean>;
184
+ /**
185
+ * OPTIONAL: remove a burned slot (memory AND disk). The gate calls this in
186
+ * exactly one situation — the rails PROVABLY never saw the payment
187
+ * (`rails_unavailable`), so re-presenting the same header is safe once they
188
+ * return. Stores that omit it leave the slot burned: conservative, the
189
+ * payer re-signs instead. Never called after an ambiguous settle.
190
+ */
191
+ releaseReplay?(key: string): MaybePromise<void>;
192
+ /** Record a settled payment's receipt (may trail; see flush). Note: the
193
+ * receipt is the ONLY durable record of a settlement — a hard crash with
194
+ * the tail unflushed permanently undercounts that payment's revenue. */
195
+ appendReceipt(receipt: GateReceipt): MaybePromise<void>;
196
+ /** Count a 402 quote served (may trail; see flush). */
197
+ recordQuote(): MaybePromise<void>;
198
+ /** Count a refused payment (may trail; see flush). */
199
+ recordRefusal(): MaybePromise<void>;
200
+ receipts(): readonly GateReceipt[];
201
+ quoted(): number;
202
+ refused(): number;
203
+ /** Durable impls: drain trailing writes, throw the first failure. */
204
+ flush?(): Promise<void>;
205
+ }
206
+ /** In-memory gate store — the default, and the working set durable stores hydrate into. */
207
+ declare class InMemoryGateStore implements GateStorePort {
208
+ private readonly seenPayments;
209
+ private readonly receiptLog;
210
+ private quotedCount;
211
+ private refusedCount;
212
+ burnReplay(key: string): boolean;
213
+ /**
214
+ * Release a burned slot — on the port (optional) since the rails-unreachable
215
+ * path, and still what a persist-then-cache impl uses to leave memory
216
+ * untouched when its own INSERT fails.
217
+ */
218
+ releaseReplay(key: string): void;
219
+ appendReceipt(receipt: GateReceipt): void;
220
+ recordQuote(): void;
221
+ recordRefusal(): void;
222
+ /** Hydration primitive for durable stores: set the resumed counter totals. */
223
+ loadCounters(quoted: number, refused: number): void;
224
+ receipts(): readonly GateReceipt[];
225
+ quoted(): number;
226
+ refused(): number;
227
+ }
228
+
229
+ /**
230
+ * Per-payer velocity limits, all sharing one rolling window. Two evidence
231
+ * sources, deliberately different in durability:
232
+ *
233
+ * - `maxPayments` / `maxAmount` cap SETTLED spend and are derived from the
234
+ * gate's receipts at check time — on a durable store they survive restarts
235
+ * for free (receipts hydrate), and they never drift from revenue truth.
236
+ * - `maxAttempts` caps PRESENTATIONS (any outcome) and lives in memory only —
237
+ * refused attempts leave no durable row, so this counter resets on restart.
238
+ * That is acceptable for what it protects against (hammering), and it keeps
239
+ * the port unchanged.
240
+ *
241
+ * Limits are gate-wide per payer (no per-route caps in v0.1). A payment
242
+ * refused for velocity is NOT replay-burned — the same signed header may be
243
+ * re-presented once the window clears; the refusal carries Retry-After.
244
+ */
245
+ interface GateVelocity {
246
+ /** Rolling window in milliseconds. */
247
+ windowMs: number;
248
+ /** Max settled payments per payer inside the window (any asset). */
249
+ maxPayments?: number;
250
+ /**
251
+ * Max settled decimal amount per payer inside the window, compared per
252
+ * asset (only receipts in the incoming payment's asset count toward it).
253
+ * Also acts as a per-payment ceiling: a single payment above it never
254
+ * clears, and its refusal carries no Retry-After.
255
+ */
256
+ maxAmount?: string;
257
+ /** Max payment presentations per payer inside the window, any outcome. */
258
+ maxAttempts?: number;
259
+ }
260
+ /** Fail fast on misconfiguration — a silent zero-cap gate refuses everyone. */
261
+ declare function validateVelocity(velocity: GateVelocity): void;
262
+
263
+ /** Wallet-address screening. Addresses compare case-insensitively (EVM rule). */
264
+ interface GateScreen {
265
+ /** If set, ONLY these payers may pay. */
266
+ allowPayers?: readonly string[];
267
+ /** These payers are always refused, allowlist or not. */
268
+ denyPayers?: readonly string[];
269
+ /**
270
+ * Dynamic screening hook, consulted after the static lists with the payer
271
+ * address as presented. Return a refusal reason to turn the payer away
272
+ * (403, code `payer_denied`); return undefined to let the payment proceed.
273
+ * Reputation-driven screening (@reinconsole/graph's `payerCheck`) plugs in here.
274
+ */
275
+ check?: (payer: string) => string | undefined;
276
+ }
277
+ interface GateOptions {
278
+ /** Priced routes; first match wins. Unmatched requests pass through free. */
279
+ routes: readonly GateRoute[];
280
+ /** Settlement rails: mockFacilitatorRails(...) or facilitatorClientRails(...). */
281
+ rails: GateRails;
282
+ /** Default recipient address (a route can override). */
283
+ payTo: string;
284
+ /** Default x402 network id, e.g. "base" (mock rails) or "base-sepolia". */
285
+ network: string;
286
+ /** Default asset: a symbol (mock rails) or token contract address (real). */
287
+ asset: string;
288
+ decimals?: number;
289
+ maxTimeoutSeconds?: number;
290
+ /** EIP-712 domain hints etc., quoted on every requirement. */
291
+ extra?: Record<string, unknown>;
292
+ screen?: GateScreen;
293
+ /**
294
+ * Also advertise quotes on the x402 v2 wire: 402 outcomes gain a
295
+ * `paymentRequiredHeader` (base64 PaymentRequired, CAIP-2 networks) that the
296
+ * adapters send as `PAYMENT-REQUIRED`, alongside the unchanged v1 body —
297
+ * dual-stack, each dialect reads its own channel. Off by default. Note the
298
+ * gate ACCEPTS v2 payments (PAYMENT-SIGNATURE envelopes) regardless of this
299
+ * flag; it only controls what quotes advertise.
300
+ */
301
+ advertiseV2?: boolean;
302
+ /** Per-payer velocity limits (see GateVelocity). Off when omitted. */
303
+ velocity?: GateVelocity;
304
+ /**
305
+ * Rails transport-failure retry policy. `attempts` counts EXTRA tries after
306
+ * the first (default 2), spaced `backoffMs * attemptNumber` apart (default
307
+ * 250ms; pass 0 in tests). Applies only to transport failures — a GateError
308
+ * from the rails is a semantic verdict and is never retried. See GateRails
309
+ * for which settle failures are retry-safe.
310
+ */
311
+ retry?: {
312
+ attempts?: number;
313
+ backoffMs?: number;
314
+ };
315
+ /** Injectable clock (tests). */
316
+ now?: () => Date;
317
+ /**
318
+ * Gate storage. Defaults in-memory; pass @reinconsole/store's gate store and
319
+ * receipts, revenue stats, and burned replay slots survive restarts.
320
+ */
321
+ store?: GateStorePort;
322
+ }
323
+ /** The transport-agnostic request shape every adapter reduces to. */
324
+ interface GateRequest {
325
+ method: string;
326
+ /** Absolute request URL (quoted verbatim as the requirement's resource). */
327
+ url: string;
328
+ /** Raw X-PAYMENT header value, or null when absent. */
329
+ payment: string | null;
330
+ }
331
+ type GateOutcome =
332
+ /** No priced route matched — the vendor serves this request for free. */
333
+ {
334
+ kind: 'open';
335
+ }
336
+ /** No payment attached: here is what this resource costs. The optional
337
+ * `paymentRequiredHeader` (advertiseV2) is the base64 v2 PaymentRequired
338
+ * for the PAYMENT-REQUIRED response header. */
339
+ | {
340
+ kind: 'quote';
341
+ status: 402;
342
+ body: PaymentRequired;
343
+ paymentRequiredHeader?: string;
344
+ }
345
+ /**
346
+ * A payment was attached and turned away. 403 = screening, 402 = re-quote,
347
+ * 429 = throttled (retryAfterSeconds set when a slot will free), 503 = the
348
+ * rails failed us (`rails_unavailable` = provably unsettled, slot released;
349
+ * `settle_unknown` = fate unknown, slot stays burned — do not re-pay blindly).
350
+ */
351
+ | {
352
+ kind: 'refused';
353
+ status: 402 | 403 | 429 | 503;
354
+ code: GateRefusalCode;
355
+ reason: string;
356
+ body: unknown;
357
+ retryAfterSeconds?: number;
358
+ /** On 402 re-quotes with advertiseV2: the v2 PAYMENT-REQUIRED value. */
359
+ paymentRequiredHeader?: string;
360
+ }
361
+ /** Verified and settled — serve, attaching settlementHeader as X-PAYMENT-RESPONSE. */
362
+ | {
363
+ kind: 'paid';
364
+ receipt: GateReceipt;
365
+ settlementHeader: string;
366
+ };
367
+ interface GateLineStats {
368
+ settled: number;
369
+ revenue: string;
370
+ }
371
+ interface GateStats {
372
+ quoted: number;
373
+ settled: number;
374
+ refused: number;
375
+ /** Decimal revenue grouped by asset. */
376
+ revenue: Record<string, string>;
377
+ /** Per route pattern. */
378
+ routes: Record<string, GateLineStats>;
379
+ /** Per payer (lowercased address). */
380
+ payers: Record<string, GateLineStats>;
381
+ }
382
+ /**
383
+ * Rein Gate — the vendor side of the wire. One gate fronts one vendor API:
384
+ * it quotes x402 requirements for priced routes, cross-checks and screens
385
+ * incoming payments, settles them through the configured rails, and keeps
386
+ * vendor-side receipts so monetization is observable, not anecdotal.
387
+ *
388
+ * Check order for a presented payment: envelope decode -> rate limit ->
389
+ * quote consistency (scheme/network/amount/recipient) -> payer screening ->
390
+ * velocity caps -> replay burn -> rails verify -> rails settle. The replay
391
+ * slot is burned BEFORE the async legs so two concurrent copies of the same
392
+ * payment cannot both settle (the mock ledger, unlike the chain, would
393
+ * happily double-spend); throttle refusals fire before the burn so a
394
+ * velocity-refused header can be re-presented once its window clears.
395
+ */
396
+ declare class Gate {
397
+ private readonly routes;
398
+ private readonly rails;
399
+ private readonly defaults;
400
+ private readonly allowPayers;
401
+ private readonly denyPayers;
402
+ private readonly screenCheck;
403
+ private readonly advertiseV2;
404
+ private readonly velocity;
405
+ private readonly attempts;
406
+ private readonly retryPolicy;
407
+ private readonly now;
408
+ private readonly bus;
409
+ private readonly store;
410
+ constructor(options: GateOptions);
411
+ onEvent(handler: (event: ReinEvent) => void): void;
412
+ get receipts(): readonly GateReceipt[];
413
+ /** Drain trailing telemetry writes; throws the first failure (durable stores). */
414
+ flush(): Promise<void>;
415
+ /** Telemetry writes may trail (cache-then-persist) — a store failure,
416
+ * rejected OR thrown synchronously, must surface through flush() and can
417
+ * never alter a payment outcome (a receipt write turning a SETTLED payment
418
+ * into a 500 would charge the payer and serve nothing). */
419
+ private fire;
420
+ /** The requirement a given request would be quoted (or undefined if free). */
421
+ quoteFor(method: string, url: string): PaymentRequirement | undefined;
422
+ handle(request: GateRequest): Promise<GateOutcome>;
423
+ stats(): GateStats;
424
+ private emit;
425
+ private checkConsistency;
426
+ /**
427
+ * Presentation rate limit — first check after decode, so a hammering payer
428
+ * is shed before any further work. Every presentation counts, including
429
+ * ones that would go on to fail consistency or screening.
430
+ */
431
+ private checkRate;
432
+ /**
433
+ * Settled-spend velocity caps, derived from receipts at check time (restart
434
+ * -safe on durable stores). Runs BEFORE the replay burn on purpose: a
435
+ * velocity refusal is temporal, not a payment defect, so the same signed
436
+ * header may come back once the window clears.
437
+ */
438
+ private checkVelocity;
439
+ /**
440
+ * Drive both rails legs under the retry policy (see GateRails for the error
441
+ * taxonomy). GateErrors are semantic verdicts and pass straight through.
442
+ * Transport failures:
443
+ *
444
+ * - verify (read-only): every transport failure is retried; exhausted ->
445
+ * `rails_unavailable`, and the slot is released (settle never ran).
446
+ * - settle: only RailsUnreachableError (provably never sent) is retried;
447
+ * exhausted -> `rails_unavailable` + release (still provably unsettled).
448
+ * Anything else is ambiguous — the money MAY have moved — so it refuses
449
+ * `settle_unknown` immediately and the slot STAYS burned: on real rails a
450
+ * re-present would misreport (nonce already used -> settle_failed), and on
451
+ * the mock ledger it would double-spend. Vendors reconcile ambiguous
452
+ * settles against transaction records (e.g. the on-chain indexer).
453
+ */
454
+ private settleThroughRails;
455
+ private railsLeg;
456
+ private screenPayer;
457
+ /**
458
+ * Burn the replay slot. The store's check-and-set is sync at call time
459
+ * (concurrent copies cannot both pass); the await is the durability barrier
460
+ * — on a durable store the burn is on disk before verify/settle run, so a
461
+ * crash mid-settle cannot resurrect the slot on restart. A store WRITE
462
+ * failure is not a GateError: it escapes as a 500, because "we cannot
463
+ * guarantee replay protection" must never settle a payment.
464
+ */
465
+ private burnReplay;
466
+ private refuse;
467
+ /** The v2 half of a dual-stack 402 (see advertiseV2), or nothing. */
468
+ private v2Quote;
469
+ }
470
+ declare function createGate(options: GateOptions): Gate;
471
+
472
+ /** The CAIP-2 id for a network name, or the lowercased name when unknown. */
473
+ declare function caip2Of(network: string): string;
474
+ /** Do two network ids name the same chain, across the v1/CAIP-2 divide? */
475
+ declare function sameNetwork(a: string, b: string): boolean;
476
+ /** x402 v2 PaymentRequirements (one entry of a 402's `accepts`). */
477
+ interface PaymentRequirementsV2 {
478
+ scheme: string;
479
+ /** CAIP-2, e.g. "eip155:84532". */
480
+ network: string;
481
+ /** Atomic units — v2's rename of v1's maxAmountRequired. */
482
+ amount: string;
483
+ asset: string;
484
+ payTo: string;
485
+ maxTimeoutSeconds?: number;
486
+ extra?: Record<string, unknown>;
487
+ }
488
+ /** v2 PaymentRequired — the decoded PAYMENT-REQUIRED header. */
489
+ interface PaymentRequiredV2 {
490
+ x402Version: 2;
491
+ error?: string;
492
+ resource?: {
493
+ url: string;
494
+ description?: string;
495
+ mimeType?: string;
496
+ };
497
+ accepts: PaymentRequirementsV2[];
498
+ extensions?: Record<string, unknown>;
499
+ }
500
+ /** Convert Rein's internal (v1-shaped) requirement to the v2 wire shape. */
501
+ declare function v2Requirements(requirement: PaymentRequirement): PaymentRequirementsV2;
502
+ /** The full v2 PaymentRequired for one quoted requirement. */
503
+ declare function buildPaymentRequiredV2(requirement: PaymentRequirement, error: string): PaymentRequiredV2;
504
+ /** The one codec every x402 header uses: base64 of compact JSON. */
505
+ declare function encodeBase64Json(value: unknown): string;
506
+
507
+ interface GateMiddlewareOptions {
508
+ /**
509
+ * Origin used to absolutize req.url for quotes, e.g. "https://api.vendor.com".
510
+ * Defaults to http://<Host header> — set this when serving behind TLS/proxies
511
+ * so quoted resources match what agents actually requested.
512
+ */
513
+ origin?: string;
514
+ /** Called with every outcome — the vendor's observability hook. */
515
+ onOutcome?: (outcome: GateOutcome, req: IncomingMessage) => void;
516
+ }
517
+ /**
518
+ * Drop the gate in front of any Node HTTP handler. Express-compatible
519
+ * `(req, res, next)` signature; with a raw `node:http` server, pass your
520
+ * content handler as `next`:
521
+ *
522
+ * const paywall = gateMiddleware(gate);
523
+ * http.createServer((req, res) => paywall(req, res, () => serve(req, res)));
524
+ *
525
+ * Free and paid requests reach `next()` (paid ones with X-PAYMENT-RESPONSE
526
+ * already set on the response); quotes and refusals are answered here and
527
+ * never reach the vendor's handler.
528
+ */
529
+ declare function gateMiddleware(gate: Gate, options?: GateMiddlewareOptions): (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
530
+
531
+ interface GatedFetchOptions {
532
+ /** Serves the actual content once the gate clears a request. */
533
+ serve?: (request: {
534
+ url: string;
535
+ method: string;
536
+ }) => Response | Promise<Response>;
537
+ }
538
+ /**
539
+ * The gate as an in-process vendor fetch — a drop-in replacement for
540
+ * createMockVendor()/createRealVendor() anywhere a FetchLike vendor is
541
+ * composed (guard tests, demos, the console world), with the gate's pricing,
542
+ * screening, replay protection, and receipts in the loop.
543
+ */
544
+ declare function createGatedFetch(gate: Gate, options?: GatedFetchOptions): FetchLike;
545
+
546
+ interface InspectedPayment {
547
+ /** Which wire dialect the payment arrived in. */
548
+ version: 1 | 2;
549
+ scheme: string;
550
+ network: string;
551
+ /** The paying wallet address (`from` in the transfer). */
552
+ payer: string;
553
+ to: string;
554
+ /** Atomic-unit amount. */
555
+ value: string;
556
+ /** The decoded envelope exactly as sent, for rails that re-verify it. */
557
+ envelope: unknown;
558
+ }
559
+ declare function inspectPaymentHeader(raw: string): InspectedPayment;
560
+
561
+ export { type FacilitatorClientLike, Gate, GateError, type GateLineStats, type GateMiddlewareOptions, type GateOptions, type GateOutcome, type GateRails, type GateRefusalCode, type GateRequest, type GateRoute, type GateScreen, type GateSettlement, type GateStats, type GateStorePort, type GateVelocity, type GatedFetchOptions, InMemoryGateStore, type InspectedPayment, type MaybePromise, type MockFacilitatorLike, type PaymentDefaults, type PaymentRequiredV2, type PaymentRequirementsV2, RailsUnreachableError, buildPaymentRequiredV2, caip2Of, createGate, createGatedFetch, encodeBase64Json, facilitatorClientRails, gateMiddleware, inspectPaymentHeader, matchRoute, mockFacilitatorRails, requirementFor, routeDecimals, sameNetwork, v2Requirements, validateVelocity };