@parallel-protocol/x402 0.10.0 → 0.10.1
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 +20 -10
- package/dist/{chunk-KZZY5LS6.cjs → chunk-4CEMNRCZ.cjs} +2 -0
- package/dist/{chunk-MYCUOJ3H.cjs → chunk-AYMDIX7A.cjs} +223 -53
- package/dist/{chunk-VSNDEQGS.js → chunk-CNASNROQ.js} +181 -16
- package/dist/{chunk-QAECRSWF.js → chunk-MZCHBBUN.js} +2 -0
- package/dist/express.cjs +34 -9
- package/dist/express.d.cts +2 -1
- package/dist/express.d.ts +2 -1
- package/dist/express.js +30 -5
- package/dist/fastify.cjs +78 -22
- package/dist/fastify.d.cts +1 -1
- package/dist/fastify.d.ts +1 -1
- package/dist/fastify.js +71 -15
- package/dist/hono.cjs +22 -14
- package/dist/hono.d.cts +1 -1
- package/dist/hono.d.ts +1 -1
- package/dist/hono.js +17 -9
- package/dist/index.cjs +25 -21
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +2 -2
- package/dist/llm.cjs +13 -13
- package/dist/llm.d.cts +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.js +1 -1
- package/dist/next.cjs +16 -15
- package/dist/next.d.cts +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.js +4 -3
- package/dist/{types-BWL9h1eY.d.cts → types-CRBMTQaq.d.cts} +12 -0
- package/dist/{types-BWL9h1eY.d.ts → types-CRBMTQaq.d.ts} +12 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -40,11 +40,12 @@ Agent Merchant (this SDK) Facilitator
|
|
|
40
40
|
- The handler only runs after the signature is verified.
|
|
41
41
|
- The on-chain settlement happens after the handler produces a 2xx response — the agent is not charged on errors.
|
|
42
42
|
- The settle is bound to the merchant that verified: `POST /x402/verify` hands the middleware a settle token that `POST /x402/settle` must present. The token stays in memory for the life of the request and never reaches the agent.
|
|
43
|
-
- If settlement fails, the middleware discards the handler's response and returns `402` with `{ error: "PAYMENT_SETTLEMENT_FAILED", reason, message }`: `reason` is the facilitator's code (`INSUFFICIENT_BALANCE`, `AUTHORIZATION_USED`, `PAYMENT_EXPIRED`, ...) or this SDK's (`SETTLE_TIMEOUT`, `FACILITATOR_UNAVAILABLE`, `FACILITATOR_INVALID_RESPONSE`, `AMOUNT_MISMATCH`, `UNKNOWN`), `message` the facilitator's text or this SDK's, and the facilitator's `Retry-After` is copied when it sent one. A settle that timed out or could not reach the facilitator is retried once first, with the same settle token: once the first attempt has finished, the facilitator answers its recorded outcome, and any other answer to the retry tells the agent to check with the merchant.
|
|
43
|
+
- If settlement fails, the middleware discards the handler's response, its headers, cookies and trailers included (for the `cookies()` of Next.js, see [Next.js](#nextjs-app-router)), and returns `402` with `{ error: "PAYMENT_SETTLEMENT_FAILED", reason, message }`: `reason` is the facilitator's code (`INSUFFICIENT_BALANCE`, `AUTHORIZATION_USED`, `PAYMENT_EXPIRED`, ...) or this SDK's (`SETTLE_TIMEOUT`, `FACILITATOR_UNAVAILABLE`, `FACILITATOR_INVALID_RESPONSE`, `AMOUNT_MISMATCH`, `UNKNOWN`), `message` the facilitator's text or this SDK's, and the facilitator's `Retry-After` is copied when it sent one. A settle that timed out or could not reach the facilitator is retried once first, with the same settle token: once the first attempt has finished, the facilitator answers its recorded outcome, and any other answer to the retry tells the agent to check with the merchant.
|
|
44
44
|
- A metered settle must settle the amount the handler asked for: a confirmation for another amount is refused and the response discarded.
|
|
45
45
|
- Handlers are bounded by a 30-second timeout on Express, Hono and Next.js — a slow handler returns `504` and the agent is not charged. (Fastify manages handler execution itself, so no timeout is applied there.) Metered routes get their announced window minus two minutes kept for the settlement and its retry.
|
|
46
|
+
- On paid Express and Fastify routes, the handler does not see the conditional request headers (`If-Match`, `If-None-Match`, `If-Modified-Since`, `If-Unmodified-Since`): Express, or a plugin such as `@fastify/etag`, would turn the paid response into a `304` or a `412`, which is not settled. A paid request always gets the resource.
|
|
46
47
|
- On paid Fastify routes, writing to `reply.raw` (the SSE/streaming pattern) fails loudly instead of sending: a raw response skips the settlement hooks and would go out unpaid. Streaming is not supported on paid routes.
|
|
47
|
-
- CORS preflight (`OPTIONS`) is
|
|
48
|
+
- CORS preflight (`OPTIONS`) is passed through without challenge, unless its path has `.` or `..` segments. On a paid route the handler still answers it, but only its status code and its `Access-Control-*`, `Vary` and `Allow` headers go out, with the standard reason phrase: no body, no other header, no trailer and no early hints. A handler that answers every method does not serve the paid content. On Next.js the handler does not run, see [Next.js](#nextjs-app-router).
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
@@ -61,8 +62,8 @@ Install your framework separately (all are optional peer dependencies):
|
|
|
61
62
|
```bash
|
|
62
63
|
npm install express # Express 4 or 5
|
|
63
64
|
npm install next # Next.js 14 or 15
|
|
64
|
-
npm install fastify # Fastify
|
|
65
|
-
npm install hono # Hono 4
|
|
65
|
+
npm install fastify # Fastify 5.12.7 or a later 5.x
|
|
66
|
+
npm install hono # Hono 4
|
|
66
67
|
```
|
|
67
68
|
|
|
68
69
|
TypeScript consumers should also install `viem` (peer dependency) — the public types use its `Address` type. Node ≥ 18 is required.
|
|
@@ -123,6 +124,8 @@ export const GET = withPayment(config, async (req) => {
|
|
|
123
124
|
});
|
|
124
125
|
```
|
|
125
126
|
|
|
127
|
+
On a paid route, a CORS preflight (`OPTIONS`) gets an empty `204` without running the handler, so set the route's CORS headers in the `headers` of `next.config.js`. Next.js adds the cookies set with `cookies()` from `next/headers` to the response the route returns, even to the `402` of a failed settlement. Set the cookies of a paid response on the `NextResponse` the handler returns instead: they leave with it only.
|
|
128
|
+
|
|
126
129
|
### Fastify
|
|
127
130
|
|
|
128
131
|
```typescript
|
|
@@ -149,6 +152,8 @@ app.get("/api/data", async () => {
|
|
|
149
152
|
});
|
|
150
153
|
```
|
|
151
154
|
|
|
155
|
+
On a refusal after the handler, only the cookies already set on the reply before the handler go out. Cookies that a plugin such as `@fastify/cookie` writes from its own `onSend` hook are left out of the `402`, even those set with `reply.setCookie` before the handler.
|
|
156
|
+
|
|
152
157
|
### Hono
|
|
153
158
|
|
|
154
159
|
```typescript
|
|
@@ -207,7 +212,7 @@ interface PaymentMiddlewareConfig {
|
|
|
207
212
|
| `meter` | `(ctx) => string \| bigint \| undefined` | No | `upto` routes only: reads the amount to charge from the handler's response, see [Reading the amount from the response](#reading-the-amount-from-the-response-meter). |
|
|
208
213
|
| `maxTimeoutSeconds` | `number` | No | Settlement window announced to the payer (default 300). `@parallel-protocol/x402-fetch` signs for it on `upto` routes, and for five minutes on `exact` routes. Metered routes announce how long their handler may take. On an `upto` route it is 3600 (one hour) at most: a larger value raises `X402ConfigError` at construction, and the facilitator refuses an authorization valid for longer with `WINDOW_TOO_LONG`. On metered routes, Express, Hono and Next.js derive the handler timeout from it, minus two minutes kept for the settlement and its retry: a 300 s window leaves the handler 180 s, and a handler always gets at least 30 s. Fastify does not time handlers out, set the server's `connectionTimeout` / `requestTimeout` yourself. |
|
|
209
214
|
|
|
210
|
-
Route matching uses **longest prefix wins**: `/api/v1/users` will match a `/api/v1` route config before a `/api` one.
|
|
215
|
+
Route matching uses **longest prefix wins**: `/api/v1/users` will match a `/api/v1` route config before a `/api` one. The request path and the route keys are compared once normalized: case, percent-encoding, repeated or trailing slashes, a fragment, `;` path parameters and an absolute-form origin make no difference, so another form of a paid path is charged like the path itself. A key with a trailing slash, such as `/api/`, covers the paths below it as `/api` does. Routers and proxies do not all resolve `.` and `..` segments, so a path that has some and reaches a paid route is refused with a `400` and `INVALID_PATH`. Under an Express mount prefix, such as `app.use("/api", ...)`, a key can be written with the full path or relative to the mount, and the key `/` covers only the mount's root: write `/api` to cover every path below it. A Next.js route handler sees the path without the `basePath`, so write keys without it.
|
|
211
216
|
|
|
212
217
|
---
|
|
213
218
|
|
|
@@ -495,7 +500,7 @@ routes: {
|
|
|
495
500
|
},
|
|
496
501
|
```
|
|
497
502
|
|
|
498
|
-
The function gets `{ method, path, url, header(name), body }`. `body` is the request body as the framework parsed it (`undefined` when there is none), so it is available where your framework parses it:
|
|
503
|
+
The function gets `{ method, path, routePath, url, header(name), body }`. `path` is the request path as the framework gives it, and `routePath` the path the route matched, in the form routes are matched on. Under an Express mount, that is the full path when the route key is written with it. To price by path, compare `routePath`, so that another form of the same path, such as `/API/premium`, gets the same price. `body` is the request body as the framework parsed it (`undefined` when there is none), so it is available where your framework parses it:
|
|
499
504
|
|
|
500
505
|
| Framework | `body` |
|
|
501
506
|
|-----------|--------|
|
|
@@ -676,6 +681,7 @@ import {
|
|
|
676
681
|
|
|
677
682
|
// Utils
|
|
678
683
|
encodeBase64,
|
|
684
|
+
normalizeRoutePath,
|
|
679
685
|
parsePrice,
|
|
680
686
|
toEip155Network,
|
|
681
687
|
validateAddress,
|
|
@@ -688,8 +694,8 @@ import {
|
|
|
688
694
|
|-------------|--------|-----------|
|
|
689
695
|
| `@parallel-protocol/x402/express` | `paymentMiddleware(config)` | Express 4/5 |
|
|
690
696
|
| `@parallel-protocol/x402/next` | `withPayment(config, handler)` | Next.js 14/15 |
|
|
691
|
-
| `@parallel-protocol/x402/fastify` | `paymentMiddleware(config)` | Fastify
|
|
692
|
-
| `@parallel-protocol/x402/hono` | `paymentMiddleware(config)` | Hono 4
|
|
697
|
+
| `@parallel-protocol/x402/fastify` | `paymentMiddleware(config)` | Fastify 5 (5.12.7 or later) |
|
|
698
|
+
| `@parallel-protocol/x402/hono` | `paymentMiddleware(config)` | Hono 4 |
|
|
693
699
|
|
|
694
700
|
Each subpath also exports its adapter class (`ExpressAdapter`, `NextAdapter`, `FastifyAdapter`, `HonoAdapter`) for use with [`createPaymentGate`](#advanced-createpaymentgate).
|
|
695
701
|
|
|
@@ -743,7 +749,9 @@ const result = await gate({
|
|
|
743
749
|
});
|
|
744
750
|
|
|
745
751
|
if (result.type === "pass") {
|
|
746
|
-
// Route not configured for payment, proceed normally
|
|
752
|
+
// Route not configured for payment, proceed normally.
|
|
753
|
+
// result.preflight: an OPTIONS on a paid route, send only the handler's
|
|
754
|
+
// status code and its Access-Control-*, Vary and Allow headers
|
|
747
755
|
} else if (result.type === "error") {
|
|
748
756
|
// Send result.result.status (402, or the status of a price refusal)
|
|
749
757
|
// with result.result.headers and result.result.body
|
|
@@ -820,12 +828,13 @@ What the middleware answers on a paid route instead of the handler's response:
|
|
|
820
828
|
| 402 | `{ error: "INVALID_SETTLE_AMOUNT", message }` | A metered route named an amount the middleware cannot settle, see [Metered routes](#metered-routes-upto) |
|
|
821
829
|
| 504 | `{ error: "HANDLER_TIMEOUT", message }` on Express and Hono, an empty body on Next.js | The handler did not answer within its timeout, and nothing is settled. Next.js also answers this empty 504 when the handler throws |
|
|
822
830
|
| 400, or the status the price function chose | `{ error, message }` | A price function refused the request with `X402PriceRejectedError`, and nothing was invoiced |
|
|
831
|
+
| 400 | `{ error: "INVALID_PATH", message }` | The path has `.` or `..` segments, and reaches a paid route. Nothing was invoiced |
|
|
823
832
|
|
|
824
833
|
### Thrown by the SDK
|
|
825
834
|
|
|
826
835
|
| Error class | When |
|
|
827
836
|
|-------------|------|
|
|
828
|
-
| `X402ConfigError` | Invalid config at construction, the message naming the first field at fault: a bad `payTo`, a facilitator URL that is neither https nor plain http on `localhost`, `127.0.0.1` or `[::1]`, empty `routes`, a `network` of the catalog other than `ethereum`, `base`, `avalanche` or `hyperevm`, a malformed or non-positive `price`, a `meter` on a route that is not `upto`, or an `upto` route with `maxTimeoutSeconds` above 3600, a token other than USDp and the network's USDC paid through Permit2, or a bigint price or an explicit `decimals` while it accepts tokens with different decimals. Then, on a request to a route: an unknown network with no `acceptedTokens`, an accepted token whose decimals are neither in the catalog nor set via `decimals`, a price finer than the token's precision, or a price function that throws, returns something that is not a price, or returns a bigint on an `upto` route that accepts tokens with different decimals |
|
|
837
|
+
| `X402ConfigError` | Invalid config at construction, the message naming the first field at fault: a bad `payTo`, a facilitator URL that is neither https nor plain http on `localhost`, `127.0.0.1` or `[::1]`, empty `routes`, a `network` of the catalog other than `ethereum`, `base`, `avalanche` or `hyperevm`, a malformed or non-positive `price`, two routes that are the same path once normalized with different configs, a `meter` on a route that is not `upto`, or an `upto` route with `maxTimeoutSeconds` above 3600, a token other than USDp and the network's USDC paid through Permit2, or a bigint price or an explicit `decimals` while it accepts tokens with different decimals. Then, on a request to a route: an unknown network with no `acceptedTokens`, an accepted token whose decimals are neither in the catalog nor set via `decimals`, a price finer than the token's precision, or a price function that throws, returns something that is not a price, or returns a bigint on an `upto` route that accepts tokens with different decimals |
|
|
829
838
|
| `X402RuntimeError` | Runtime failure; `.code` is one of the SDK codes below, or a facilitator error code propagated verbatim (see next table) |
|
|
830
839
|
| `X402PriceRejectedError` | Thrown by a price function to refuse a request before anything is invoiced; the client gets `.status` (400 by default) with `{ error: .code, message }`. `isPriceRejection(err)` recognises one by shape. |
|
|
831
840
|
|
|
@@ -948,6 +957,7 @@ import {
|
|
|
948
957
|
| `validateAddress` | `(address: string) => address is Address` | Check 0x + 40-hex-char format |
|
|
949
958
|
| `toEip155Network` | `(network: string) => string` | `"base"` → `"eip155:8453"` |
|
|
950
959
|
| `encodeBase64` | `(obj: unknown) => string` | `JSON.stringify` → base64 |
|
|
960
|
+
| `normalizeRoutePath` | `(path: string) => string` | The form routes are matched on: `normalizeRoutePath("/API//Data/")` is `"/api/data"` |
|
|
951
961
|
| `getDefaultAcceptedTokens` | `(network: string) => [Address, Address, Address] \| undefined` | `[USDp, USDC, sUSDp]` for known networks |
|
|
952
962
|
|
|
953
963
|
---
|
|
@@ -5,6 +5,8 @@ var X402_ERROR_CODES = {
|
|
|
5
5
|
FACILITATOR_UNAVAILABLE: "FACILITATOR_UNAVAILABLE",
|
|
6
6
|
FACILITATOR_INVALID_RESPONSE: "FACILITATOR_INVALID_RESPONSE",
|
|
7
7
|
INVALID_PAYMENT: "INVALID_PAYMENT",
|
|
8
|
+
/** A path with `.` or `..` segments on a paid route, refused with 400. */
|
|
9
|
+
INVALID_PATH: "INVALID_PATH",
|
|
8
10
|
/** The facilitator did not answer a settle within the client's timeout: the payment may still settle. */
|
|
9
11
|
SETTLE_TIMEOUT: "SETTLE_TIMEOUT"
|
|
10
12
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk4CEMNRCZ_cjs = require('./chunk-4CEMNRCZ.cjs');
|
|
4
4
|
var chains = require('@parallel-protocol/chains');
|
|
5
5
|
var paymentCore = require('@parallel-protocol/payment-core');
|
|
6
6
|
var zod = require('zod');
|
|
@@ -116,18 +116,144 @@ function encodeBase64(obj) {
|
|
|
116
116
|
function decodeBase64(str) {
|
|
117
117
|
return JSON.parse(Buffer.from(str, "base64").toString("utf8"));
|
|
118
118
|
}
|
|
119
|
-
|
|
120
|
-
if
|
|
119
|
+
var CONDITIONAL_HEADERS = [
|
|
120
|
+
"if-match",
|
|
121
|
+
"if-none-match",
|
|
122
|
+
"if-modified-since",
|
|
123
|
+
"if-unmodified-since"
|
|
124
|
+
];
|
|
125
|
+
function dropConditionalHeaders(headers) {
|
|
126
|
+
for (const name of CONDITIONAL_HEADERS) delete headers[name];
|
|
127
|
+
}
|
|
128
|
+
var UTF8 = new TextDecoder();
|
|
129
|
+
var DECODE_ROUNDS = 8;
|
|
130
|
+
function decodePercent(path) {
|
|
131
|
+
try {
|
|
132
|
+
return decodeURIComponent(path);
|
|
133
|
+
} catch {
|
|
134
|
+
return path.replace(
|
|
135
|
+
/(?:%[\da-f]{2})+/gi,
|
|
136
|
+
(run) => UTF8.decode(
|
|
137
|
+
Uint8Array.from(
|
|
138
|
+
run.slice(1).split("%"),
|
|
139
|
+
(hex) => Number.parseInt(hex, 16)
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function hasLongMarkRun(segment) {
|
|
146
|
+
for (const [run] of segment.matchAll(/\p{M}+/gu))
|
|
147
|
+
if (run.length > 8) return true;
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
function foldCase(segment) {
|
|
151
|
+
const composed = hasLongMarkRun(segment) ? segment : segment.normalize("NFC");
|
|
152
|
+
return composed.toLowerCase().toUpperCase().toLowerCase();
|
|
153
|
+
}
|
|
154
|
+
function pathSegments(path) {
|
|
155
|
+
let decoded = path;
|
|
156
|
+
for (let round = 0; round < DECODE_ROUNDS && decoded.includes("%"); round++) {
|
|
157
|
+
const next = decodePercent(decoded);
|
|
158
|
+
if (next === decoded) break;
|
|
159
|
+
decoded = next;
|
|
160
|
+
}
|
|
161
|
+
const target = decoded.replaceAll("\\", "/").replace(/^[a-z][\d+.a-z-]*:\/\/[^/]*/i, "").split(/[?#]/, 1)[0] ?? "";
|
|
162
|
+
return target.split("/").map((segment) => foldCase(segment).split(";", 1)[0] ?? "").filter((segment) => segment !== "");
|
|
163
|
+
}
|
|
164
|
+
function resolveDots(segments) {
|
|
165
|
+
const resolved = [];
|
|
166
|
+
for (const segment of segments) {
|
|
167
|
+
if (segment === "..") resolved.pop();
|
|
168
|
+
else if (segment !== ".") resolved.push(segment);
|
|
169
|
+
}
|
|
170
|
+
return resolved;
|
|
171
|
+
}
|
|
172
|
+
function normalizeRoutePath(path) {
|
|
173
|
+
return `/${resolveDots(pathSegments(path)).join("/")}`;
|
|
174
|
+
}
|
|
175
|
+
function routeTable(routes) {
|
|
176
|
+
return Object.entries(routes).map(([key, route]) => [
|
|
177
|
+
normalizeRoutePath(key),
|
|
178
|
+
route
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
function longestMatch(target, table) {
|
|
121
182
|
let bestMatch;
|
|
122
183
|
let bestLength = 0;
|
|
123
|
-
for (const [
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
184
|
+
for (const [key, route] of table) {
|
|
185
|
+
if (key === target) return route;
|
|
186
|
+
if (target.startsWith(`${key}/`) && key.length > bestLength) {
|
|
187
|
+
bestMatch = route;
|
|
188
|
+
bestLength = key.length;
|
|
127
189
|
}
|
|
128
190
|
}
|
|
129
191
|
return bestMatch;
|
|
130
192
|
}
|
|
193
|
+
function findRoute(path, table) {
|
|
194
|
+
const segments = pathSegments(path);
|
|
195
|
+
const dotSegments = segments.some(
|
|
196
|
+
(segment) => segment === "." || segment === ".."
|
|
197
|
+
);
|
|
198
|
+
const routePath = `/${resolveDots(segments).join("/")}`;
|
|
199
|
+
const route = longestMatch(routePath, table) ?? (dotSegments ? longestMatch(`/${segments.join("/")}`, table) : void 0);
|
|
200
|
+
return { route, dotSegments, routePath };
|
|
201
|
+
}
|
|
202
|
+
function isPreflightHeader(name) {
|
|
203
|
+
return /^(?:access-control-|vary$|allow$)/i.test(String(name));
|
|
204
|
+
}
|
|
205
|
+
function preflightHeaders(headers) {
|
|
206
|
+
if (Array.isArray(headers)) {
|
|
207
|
+
if (Array.isArray(headers[0]))
|
|
208
|
+
return headers.filter((pair) => isPreflightHeader(pair[0]));
|
|
209
|
+
return headers.flatMap(
|
|
210
|
+
(item, i) => i % 2 === 0 && isPreflightHeader(item) ? [item, headers[i + 1]] : []
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (headers !== null && typeof headers === "object")
|
|
214
|
+
return Object.fromEntries(
|
|
215
|
+
Object.entries(headers).filter(([name]) => isPreflightHeader(name))
|
|
216
|
+
);
|
|
217
|
+
return headers;
|
|
218
|
+
}
|
|
219
|
+
function answerAsPreflight(res) {
|
|
220
|
+
const writeHead = res.writeHead.bind(res);
|
|
221
|
+
const end = res.end.bind(res);
|
|
222
|
+
const callbackOf = (args) => args.find((arg) => typeof arg === "function");
|
|
223
|
+
const dropOtherHeaders = () => {
|
|
224
|
+
for (const name of res.getHeaderNames())
|
|
225
|
+
if (!isPreflightHeader(name)) res.removeHeader(name);
|
|
226
|
+
};
|
|
227
|
+
res.writeHead = ((statusCode, ...rest) => {
|
|
228
|
+
dropOtherHeaders();
|
|
229
|
+
res.statusMessage = "";
|
|
230
|
+
const headers = rest.find((arg) => typeof arg !== "string");
|
|
231
|
+
return headers === void 0 ? writeHead(statusCode) : writeHead(statusCode, preflightHeaders(headers));
|
|
232
|
+
});
|
|
233
|
+
res.write = ((...args) => {
|
|
234
|
+
const done = callbackOf(args);
|
|
235
|
+
if (done) queueMicrotask(done);
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
res.end = ((...args) => {
|
|
239
|
+
if (!res.headersSent) dropOtherHeaders();
|
|
240
|
+
const done = callbackOf(args);
|
|
241
|
+
return done ? end(done) : end();
|
|
242
|
+
});
|
|
243
|
+
res.addTrailers = () => {
|
|
244
|
+
};
|
|
245
|
+
res.writeEarlyHints = ((_hints, done) => {
|
|
246
|
+
if (done) queueMicrotask(done);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function preflightInit(res) {
|
|
250
|
+
res.body?.cancel().catch(() => {
|
|
251
|
+
});
|
|
252
|
+
const headers = new Headers();
|
|
253
|
+
for (const [name, value] of res.headers)
|
|
254
|
+
if (isPreflightHeader(name)) headers.append(name, value);
|
|
255
|
+
return { status: res.status, headers };
|
|
256
|
+
}
|
|
131
257
|
function permit2Extra(network, asset) {
|
|
132
258
|
const domain = USDC_EIP712_DOMAIN[network];
|
|
133
259
|
const escrow = chains.getUptoEscrowAddress(network);
|
|
@@ -298,9 +424,31 @@ var routeConfigSchema = zod.z.object({
|
|
|
298
424
|
(route) => route.scheme !== "upto" || !hasMixedDecimals(route.network, route.acceptedTokens) || typeof route.price !== "bigint" && route.decimals === void 0,
|
|
299
425
|
'An upto route that accepts tokens with different decimals needs a price like "0.01", not a bigint or an explicit decimals'
|
|
300
426
|
);
|
|
427
|
+
function sameValue(a, b) {
|
|
428
|
+
if (Object.is(a, b)) return true;
|
|
429
|
+
if (typeof a !== "object" || typeof b !== "object" || !a || !b) return false;
|
|
430
|
+
const entries = Object.entries(a);
|
|
431
|
+
return Array.isArray(a) === Array.isArray(b) && entries.length === Object.keys(b).length && entries.every(
|
|
432
|
+
([key, value]) => sameValue(value, b[key])
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
function consistentRoutePaths(routes, ctx) {
|
|
436
|
+
const keyByPath = /* @__PURE__ */ new Map();
|
|
437
|
+
for (const [key, route] of Object.entries(routes)) {
|
|
438
|
+
const path = normalizeRoutePath(key);
|
|
439
|
+
const other = keyByPath.get(path);
|
|
440
|
+
if (other === void 0) keyByPath.set(path, key);
|
|
441
|
+
else if (!sameValue(routes[other], route))
|
|
442
|
+
ctx.addIssue({
|
|
443
|
+
code: "custom",
|
|
444
|
+
path: [key],
|
|
445
|
+
message: `Same path as "${other}" once normalized, with another config: keep one of the two routes`
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
301
449
|
var paymentMiddlewareConfigSchema = zod.z.object({
|
|
302
450
|
facilitator: facilitatorConfigSchema,
|
|
303
|
-
routes: zod.z.record(zod.z.string(), routeConfigSchema).refine((r) => Object.keys(r).length > 0, "At least one route is required")
|
|
451
|
+
routes: zod.z.record(zod.z.string(), routeConfigSchema).refine((r) => Object.keys(r).length > 0, "At least one route is required").superRefine(consistentRoutePaths)
|
|
304
452
|
});
|
|
305
453
|
var facilitatorResponseSchema = zod.z.discriminatedUnion("success", [
|
|
306
454
|
zod.z.object({
|
|
@@ -371,7 +519,7 @@ function throwIfRedirect(response) {
|
|
|
371
519
|
if (!redirected) return;
|
|
372
520
|
response.body?.cancel().catch(() => {
|
|
373
521
|
});
|
|
374
|
-
throw new
|
|
522
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE, {
|
|
375
523
|
status: response.status
|
|
376
524
|
});
|
|
377
525
|
}
|
|
@@ -379,7 +527,7 @@ function decodePaymentHeader(paymentHeader, network) {
|
|
|
379
527
|
try {
|
|
380
528
|
return toFacilitatorPayload(decodeBase64(paymentHeader), network);
|
|
381
529
|
} catch {
|
|
382
|
-
throw new
|
|
530
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.INVALID_PAYMENT);
|
|
383
531
|
}
|
|
384
532
|
}
|
|
385
533
|
function payerOf(paymentPayload) {
|
|
@@ -394,7 +542,7 @@ var FacilitatorClient = class {
|
|
|
394
542
|
constructor(config) {
|
|
395
543
|
const url = facilitatorUrlSchema.safeParse(config.url);
|
|
396
544
|
if (!url.success) {
|
|
397
|
-
throw new
|
|
545
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
398
546
|
url.error.issues[0]?.message ?? "Invalid facilitator URL",
|
|
399
547
|
url.error
|
|
400
548
|
);
|
|
@@ -419,8 +567,8 @@ var FacilitatorClient = class {
|
|
|
419
567
|
throwIfRedirect(response);
|
|
420
568
|
if (!response.ok) {
|
|
421
569
|
const body = await readErrorBody(response);
|
|
422
|
-
throw new
|
|
423
|
-
body.error ??
|
|
570
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(
|
|
571
|
+
body.error ?? chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE,
|
|
424
572
|
{
|
|
425
573
|
detail: body.message,
|
|
426
574
|
status: response.status,
|
|
@@ -430,14 +578,14 @@ var FacilitatorClient = class {
|
|
|
430
578
|
}
|
|
431
579
|
json = await response.json();
|
|
432
580
|
} catch (e) {
|
|
433
|
-
if (e instanceof
|
|
434
|
-
throw new
|
|
581
|
+
if (e instanceof chunk4CEMNRCZ_cjs.X402RuntimeError) throw e;
|
|
582
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
435
583
|
}
|
|
436
584
|
try {
|
|
437
585
|
const { settleToken } = verifyResponseSchema.parse(json);
|
|
438
586
|
return { settleToken };
|
|
439
587
|
} catch {
|
|
440
|
-
throw new
|
|
588
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
441
589
|
}
|
|
442
590
|
}
|
|
443
591
|
async settle(paymentHeader, paymentRequirements, options) {
|
|
@@ -469,7 +617,7 @@ var FacilitatorClient = class {
|
|
|
469
617
|
if (!response.ok) {
|
|
470
618
|
const body = await readErrorBody(response);
|
|
471
619
|
if (body.error === void 0) {
|
|
472
|
-
throw new
|
|
620
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE, {
|
|
473
621
|
status: response.status
|
|
474
622
|
});
|
|
475
623
|
}
|
|
@@ -486,11 +634,11 @@ var FacilitatorClient = class {
|
|
|
486
634
|
}
|
|
487
635
|
json = await response.json();
|
|
488
636
|
} catch (e) {
|
|
489
|
-
if (e instanceof
|
|
637
|
+
if (e instanceof chunk4CEMNRCZ_cjs.X402RuntimeError) throw e;
|
|
490
638
|
if (e instanceof Error && e.name === "TimeoutError") {
|
|
491
|
-
throw new
|
|
639
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT);
|
|
492
640
|
}
|
|
493
|
-
throw new
|
|
641
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
494
642
|
}
|
|
495
643
|
try {
|
|
496
644
|
const parsed = settleResponseSchema.parse(json);
|
|
@@ -523,7 +671,7 @@ var FacilitatorClient = class {
|
|
|
523
671
|
error: { code: parsed.error, message: parsed.message }
|
|
524
672
|
};
|
|
525
673
|
} catch {
|
|
526
|
-
throw new
|
|
674
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
527
675
|
}
|
|
528
676
|
}
|
|
529
677
|
/**
|
|
@@ -543,17 +691,17 @@ var FacilitatorClient = class {
|
|
|
543
691
|
});
|
|
544
692
|
throwIfRedirect(response);
|
|
545
693
|
if (!response.ok) {
|
|
546
|
-
throw new
|
|
694
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
547
695
|
}
|
|
548
696
|
json = await response.json();
|
|
549
697
|
} catch (e) {
|
|
550
|
-
if (e instanceof
|
|
551
|
-
throw new
|
|
698
|
+
if (e instanceof chunk4CEMNRCZ_cjs.X402RuntimeError) throw e;
|
|
699
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE);
|
|
552
700
|
}
|
|
553
701
|
try {
|
|
554
702
|
return facilitatorResponseSchema.parse(json);
|
|
555
703
|
} catch {
|
|
556
|
-
throw new
|
|
704
|
+
throw new chunk4CEMNRCZ_cjs.X402RuntimeError(chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE);
|
|
557
705
|
}
|
|
558
706
|
}
|
|
559
707
|
buildHeaders() {
|
|
@@ -585,10 +733,11 @@ async function raceTimeout(promise, ms = HANDLER_TIMEOUT_MS) {
|
|
|
585
733
|
|
|
586
734
|
// src/middleware.ts
|
|
587
735
|
var PRICE_FORMAT = /^\d+(?:\.\d+)?$/;
|
|
588
|
-
async function requestContext(adapter) {
|
|
736
|
+
async function requestContext(adapter, routePath) {
|
|
589
737
|
return {
|
|
590
738
|
method: adapter.getMethod(),
|
|
591
739
|
path: adapter.getPath(),
|
|
740
|
+
routePath,
|
|
592
741
|
url: adapter.getUrl(),
|
|
593
742
|
header: (name) => adapter.getHeader(name),
|
|
594
743
|
body: adapter.getBody ? await adapter.getBody() : void 0
|
|
@@ -596,27 +745,27 @@ async function requestContext(adapter) {
|
|
|
596
745
|
}
|
|
597
746
|
async function resolvePrice(config, ctx, tokens) {
|
|
598
747
|
if (typeof config.price !== "function") return config.price;
|
|
599
|
-
if (!ctx) throw new
|
|
748
|
+
if (!ctx) throw new chunk4CEMNRCZ_cjs.X402ConfigError("A price function needs the request");
|
|
600
749
|
let price;
|
|
601
750
|
try {
|
|
602
751
|
price = await config.price(ctx);
|
|
603
752
|
} catch (cause) {
|
|
604
|
-
if (
|
|
605
|
-
throw new
|
|
753
|
+
if (chunk4CEMNRCZ_cjs.isPriceRejection(cause)) throw cause;
|
|
754
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
606
755
|
`The price function of route "${config.network}${ctx.path}" threw`,
|
|
607
756
|
cause
|
|
608
757
|
);
|
|
609
758
|
}
|
|
610
759
|
const valid = typeof price === "bigint" ? price > 0n : typeof price === "string" && PRICE_FORMAT.test(price) && Number(price) > 0;
|
|
611
760
|
if (!valid) {
|
|
612
|
-
throw new
|
|
761
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
613
762
|
`The price function of route "${config.network}${ctx.path}" returned ${JSON.stringify(
|
|
614
763
|
typeof price === "bigint" ? price.toString() : price
|
|
615
764
|
)} \u2014 expected a positive amount like "0.01" or a bigint`
|
|
616
765
|
);
|
|
617
766
|
}
|
|
618
767
|
if (typeof price === "bigint" && config.scheme === "upto" && hasMixedDecimals(config.network, tokens)) {
|
|
619
|
-
throw new
|
|
768
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
620
769
|
`The price function of route "${config.network}${ctx.path}" returned a bigint, but the route accepts tokens with different decimals: return a price like "0.01"`
|
|
621
770
|
);
|
|
622
771
|
}
|
|
@@ -675,7 +824,7 @@ function refusingBigints(meter) {
|
|
|
675
824
|
return async (ctx) => {
|
|
676
825
|
const amount = await meter(ctx);
|
|
677
826
|
if (typeof amount === "bigint") {
|
|
678
|
-
throw new
|
|
827
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
679
828
|
'The meter returned a bigint, but the route accepts tokens with different decimals: return an amount like "0.75"'
|
|
680
829
|
);
|
|
681
830
|
}
|
|
@@ -686,7 +835,7 @@ var AMOUNT_FORMAT = /^\d+(?:\.\d+)?$/;
|
|
|
686
835
|
async function resolveCharge(verified, settleHeader, response) {
|
|
687
836
|
if (verified.meter) {
|
|
688
837
|
if (!verified.request) {
|
|
689
|
-
throw new
|
|
838
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
690
839
|
"A metered payment carries a meter but no request context"
|
|
691
840
|
);
|
|
692
841
|
}
|
|
@@ -737,14 +886,14 @@ function resolveAcceptedTokens(config) {
|
|
|
737
886
|
}
|
|
738
887
|
const defaults = config.scheme === "upto" ? getMeteredAcceptedTokens(config.network) : getDefaultAcceptedTokens(config.network);
|
|
739
888
|
if (defaults) return defaults;
|
|
740
|
-
throw new
|
|
889
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
741
890
|
`acceptedTokens is required for network "${config.network}" \u2014 no defaults available for this chain`
|
|
742
891
|
);
|
|
743
892
|
}
|
|
744
893
|
function resolveDecimals(config, asset) {
|
|
745
894
|
const decimals = config.decimals ?? getTokenDecimals(config.network, asset);
|
|
746
895
|
if (decimals === void 0) {
|
|
747
|
-
throw new
|
|
896
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
748
897
|
`Unknown decimals for token ${asset} on "${config.network}" \u2014 add it to the @parallel-protocol/chains catalog or set the route's \`decimals\` explicitly`
|
|
749
898
|
);
|
|
750
899
|
}
|
|
@@ -753,7 +902,7 @@ function resolveDecimals(config, asset) {
|
|
|
753
902
|
function priceInUnits(price, decimals, asset) {
|
|
754
903
|
const units = parsePrice(price, decimals);
|
|
755
904
|
if (units === 0n) {
|
|
756
|
-
throw new
|
|
905
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
757
906
|
`Price ${typeof price === "bigint" ? price.toString() : `"${price}"`} is below the precision of token ${asset} (${decimals} decimals)`
|
|
758
907
|
);
|
|
759
908
|
}
|
|
@@ -861,7 +1010,7 @@ function paymentRequiredResponse(url, config, resolvedTokens, price, refusal) {
|
|
|
861
1010
|
price
|
|
862
1011
|
);
|
|
863
1012
|
if (refusal) {
|
|
864
|
-
paymentRequired.error = shapedCode(refusal.code) ??
|
|
1013
|
+
paymentRequired.error = shapedCode(refusal.code) ?? chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE;
|
|
865
1014
|
const message = boundedMessage(refusal.detail);
|
|
866
1015
|
if (message) paymentRequired.message = message;
|
|
867
1016
|
}
|
|
@@ -878,24 +1027,39 @@ function createPaymentGate(config) {
|
|
|
878
1027
|
if (!checked.success) {
|
|
879
1028
|
const issue = checked.error.issues[0];
|
|
880
1029
|
const where = issue?.path.length ? ` at ${issue.path.join(".")}` : "";
|
|
881
|
-
throw new
|
|
1030
|
+
throw new chunk4CEMNRCZ_cjs.X402ConfigError(
|
|
882
1031
|
`Invalid paymentMiddleware config${where}: ${issue?.message ?? "check your routes, facilitator URL and payTo addresses"}`,
|
|
883
1032
|
checked.error
|
|
884
1033
|
);
|
|
885
1034
|
}
|
|
886
1035
|
const client = new FacilitatorClient(config.facilitator);
|
|
1036
|
+
const routes = routeTable(config.routes);
|
|
887
1037
|
return async (adapter) => {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1038
|
+
const path = adapter.getPath();
|
|
1039
|
+
const fullPath = adapter.getFullPath?.();
|
|
1040
|
+
let match = findRoute(path, routes);
|
|
1041
|
+
if (!match.route && fullPath !== void 0 && fullPath !== path)
|
|
1042
|
+
match = findRoute(fullPath, routes);
|
|
1043
|
+
const { route: routeConfig, dotSegments, routePath } = match;
|
|
891
1044
|
if (!routeConfig) return { type: "pass" };
|
|
1045
|
+
if (dotSegments) {
|
|
1046
|
+
return {
|
|
1047
|
+
type: "error",
|
|
1048
|
+
result: jsonResult(400, {
|
|
1049
|
+
error: chunk4CEMNRCZ_cjs.X402_ERROR_CODES.INVALID_PATH,
|
|
1050
|
+
message: "A paid route is not served on a path with . or .. segments: send the path resolved"
|
|
1051
|
+
})
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
if (adapter.getMethod().toUpperCase() === "OPTIONS")
|
|
1055
|
+
return { type: "pass", preflight: true };
|
|
892
1056
|
const resolvedTokens = resolveAcceptedTokens(routeConfig);
|
|
893
|
-
let request = typeof routeConfig.price === "function" ? await requestContext(adapter) : void 0;
|
|
1057
|
+
let request = typeof routeConfig.price === "function" ? await requestContext(adapter, routePath) : void 0;
|
|
894
1058
|
let price;
|
|
895
1059
|
try {
|
|
896
1060
|
price = await resolvePrice(routeConfig, request, resolvedTokens);
|
|
897
1061
|
} catch (err) {
|
|
898
|
-
if (!
|
|
1062
|
+
if (!chunk4CEMNRCZ_cjs.isPriceRejection(err)) throw err;
|
|
899
1063
|
const status = Number.isInteger(err.status) && err.status >= 400 && err.status <= 599 ? err.status : 400;
|
|
900
1064
|
return {
|
|
901
1065
|
type: "error",
|
|
@@ -932,7 +1096,7 @@ function createPaymentGate(config) {
|
|
|
932
1096
|
try {
|
|
933
1097
|
({ settleToken } = await client.verify(paymentHeader, requirements));
|
|
934
1098
|
} catch (e) {
|
|
935
|
-
const refusal = e instanceof
|
|
1099
|
+
const refusal = e instanceof chunk4CEMNRCZ_cjs.X402RuntimeError ? { code: e.code, detail: e.detail, retryAfter: e.retryAfter } : { code: chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE };
|
|
936
1100
|
return {
|
|
937
1101
|
type: "error",
|
|
938
1102
|
result: paymentRequiredResponse(
|
|
@@ -945,7 +1109,7 @@ function createPaymentGate(config) {
|
|
|
945
1109
|
};
|
|
946
1110
|
}
|
|
947
1111
|
const meter = routeConfig.meter && hasMixedDecimals(routeConfig.network, resolvedTokens) ? refusingBigints(routeConfig.meter) : routeConfig.meter;
|
|
948
|
-
if (meter && !request) request = await requestContext(adapter);
|
|
1112
|
+
if (meter && !request) request = await requestContext(adapter, routePath);
|
|
949
1113
|
return {
|
|
950
1114
|
type: "verified",
|
|
951
1115
|
scheme: routeConfig.scheme ?? "exact",
|
|
@@ -961,9 +1125,9 @@ var DEFAULT_SETTLEMENT_MESSAGE = "The payment could not be settled.";
|
|
|
961
1125
|
var MERCHANT_SETTLEMENT_MESSAGE = "The merchant could not complete the settlement of this payment. The payment was not taken: do not sign another payment for this request before checking with the merchant.";
|
|
962
1126
|
var UNAVAILABLE_SETTLEMENT_MESSAGE = "The facilitator could not settle the payment for now. It may still settle until its authorization expires, or an earlier attempt may already have settled it: do not sign another payment for this request before checking with the merchant.";
|
|
963
1127
|
var AGENT_MESSAGES = {
|
|
964
|
-
[
|
|
965
|
-
[
|
|
966
|
-
[
|
|
1128
|
+
[chunk4CEMNRCZ_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT]: "The facilitator did not confirm the settlement in time. The payment may still settle: do not sign another payment for this request before checking with the merchant.",
|
|
1129
|
+
[chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE]: "The facilitator could not be reached to settle the payment. It may still have settled: do not sign another payment for this request before checking with the merchant.",
|
|
1130
|
+
[chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_INVALID_RESPONSE]: "The facilitator answered the settlement with an unexpected response. The payment may have settled: do not sign another payment for this request before checking with the merchant.",
|
|
967
1131
|
SETTLE_IN_PROGRESS: "The facilitator is still settling this payment. Do not sign another payment for this request before checking with the merchant.",
|
|
968
1132
|
RECEIPT_UNAVAILABLE: "The facilitator sent the transaction but could not read its confirmation. The payment may have settled: do not sign another payment for this request before checking with the merchant.",
|
|
969
1133
|
SETTLE_TOKEN_INVALID: MERCHANT_SETTLEMENT_MESSAGE,
|
|
@@ -988,8 +1152,8 @@ function settlementFailure(code, failure) {
|
|
|
988
1152
|
};
|
|
989
1153
|
}
|
|
990
1154
|
var RETRY_ONCE_CODES = /* @__PURE__ */ new Set([
|
|
991
|
-
|
|
992
|
-
|
|
1155
|
+
chunk4CEMNRCZ_cjs.X402_ERROR_CODES.SETTLE_TIMEOUT,
|
|
1156
|
+
chunk4CEMNRCZ_cjs.X402_ERROR_CODES.FACILITATOR_UNAVAILABLE
|
|
993
1157
|
]);
|
|
994
1158
|
async function settleVerified(verified, options) {
|
|
995
1159
|
let response;
|
|
@@ -997,7 +1161,7 @@ async function settleVerified(verified, options) {
|
|
|
997
1161
|
try {
|
|
998
1162
|
response = await verified.settle(options);
|
|
999
1163
|
} catch (e) {
|
|
1000
|
-
const code = e instanceof
|
|
1164
|
+
const code = e instanceof chunk4CEMNRCZ_cjs.X402RuntimeError ? e.code : "UNKNOWN";
|
|
1001
1165
|
if (attempt > 0 || !RETRY_ONCE_CODES.has(code)) {
|
|
1002
1166
|
return settlementFailure(code);
|
|
1003
1167
|
}
|
|
@@ -1027,7 +1191,8 @@ function createPaymentMiddleware(config) {
|
|
|
1027
1191
|
const gate = createPaymentGate(config);
|
|
1028
1192
|
return async (adapter) => {
|
|
1029
1193
|
const gateResult = await gate(adapter);
|
|
1030
|
-
if (gateResult.type === "pass")
|
|
1194
|
+
if (gateResult.type === "pass")
|
|
1195
|
+
return gateResult.preflight ? { pass: true, preflight: true } : { pass: true };
|
|
1031
1196
|
if (gateResult.type === "error") return gateResult.result;
|
|
1032
1197
|
let handlerResult;
|
|
1033
1198
|
try {
|
|
@@ -1083,19 +1248,24 @@ exports.FacilitatorClient = FacilitatorClient;
|
|
|
1083
1248
|
exports.HANDLER_TIMEOUT = HANDLER_TIMEOUT;
|
|
1084
1249
|
exports.PARALLEL_TOKENS = PARALLEL_TOKENS;
|
|
1085
1250
|
exports.SETTLE_AMOUNT_HEADER = SETTLE_AMOUNT_HEADER;
|
|
1251
|
+
exports.answerAsPreflight = answerAsPreflight;
|
|
1086
1252
|
exports.createPaymentGate = createPaymentGate;
|
|
1087
1253
|
exports.createPaymentMiddleware = createPaymentMiddleware;
|
|
1254
|
+
exports.dropConditionalHeaders = dropConditionalHeaders;
|
|
1088
1255
|
exports.encodeBase64 = encodeBase64;
|
|
1256
|
+
exports.findRoute = findRoute;
|
|
1089
1257
|
exports.getDefaultAcceptedTokens = getDefaultAcceptedTokens;
|
|
1090
1258
|
exports.invalidSettleAmountResponse = invalidSettleAmountResponse;
|
|
1091
1259
|
exports.isJsonContentType = isJsonContentType;
|
|
1092
1260
|
exports.jsonFromResponse = jsonFromResponse;
|
|
1093
|
-
exports.
|
|
1261
|
+
exports.normalizeRoutePath = normalizeRoutePath;
|
|
1094
1262
|
exports.parseJsonBody = parseJsonBody;
|
|
1095
1263
|
exports.parsePrice = parsePrice;
|
|
1264
|
+
exports.preflightInit = preflightInit;
|
|
1096
1265
|
exports.raceTimeout = raceTimeout;
|
|
1097
1266
|
exports.resolveCharge = resolveCharge;
|
|
1098
1267
|
exports.resolveSettleAmount = resolveSettleAmount;
|
|
1268
|
+
exports.routeTable = routeTable;
|
|
1099
1269
|
exports.settleVerified = settleVerified;
|
|
1100
1270
|
exports.settlementFailureResponse = settlementFailureResponse;
|
|
1101
1271
|
exports.toEip155Network = toEip155Network;
|