@parallel-protocol/x402 0.5.3 → 0.7.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.
package/dist/express.cjs CHANGED
@@ -1,9 +1,38 @@
1
1
  'use strict';
2
2
 
3
- var chunkXUNNMCO5_cjs = require('./chunk-XUNNMCO5.cjs');
4
- var chunkKQPIZ6SZ_cjs = require('./chunk-KQPIZ6SZ.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ require('./chunk-JPMK5YBU.cjs');
5
5
 
6
6
  // src/express/adapter.ts
7
+ function chunkBytes(args) {
8
+ const [chunk, encoding] = args;
9
+ if (typeof chunk === "string") {
10
+ return Buffer.from(
11
+ chunk,
12
+ typeof encoding === "string" ? encoding : "utf8"
13
+ );
14
+ }
15
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk);
16
+ return void 0;
17
+ }
18
+ function headerValue(value) {
19
+ if (value === void 0) return void 0;
20
+ return Array.isArray(value) ? value[0] : String(value);
21
+ }
22
+ function writeHeadContentType(args) {
23
+ const headers = args.find((arg) => typeof arg === "object" && arg !== null);
24
+ if (!headers) return void 0;
25
+ if (Array.isArray(headers)) {
26
+ const index = headers.findIndex(
27
+ (entry, i) => i % 2 === 0 && typeof entry === "string" && entry.toLowerCase() === "content-type"
28
+ );
29
+ return index === -1 ? void 0 : headerValue(headers[index + 1]);
30
+ }
31
+ for (const [name, value] of Object.entries(headers)) {
32
+ if (name.toLowerCase() === "content-type") return headerValue(value);
33
+ }
34
+ return void 0;
35
+ }
7
36
  var TIMEOUT_BODY = JSON.stringify({
8
37
  error: "HANDLER_TIMEOUT",
9
38
  message: "The resource handler did not respond in time."
@@ -30,7 +59,12 @@ var ExpressAdapter = class {
30
59
  getUrl() {
31
60
  return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`;
32
61
  }
33
- async runHandler() {
62
+ // Whatever a body parser mounted before this middleware left on `req.body`
63
+ // (`express.json()` for JSON); `undefined` when none ran.
64
+ async getBody() {
65
+ return this.req.body;
66
+ }
67
+ async runHandler(options) {
34
68
  const res = this.res;
35
69
  const next = this.next;
36
70
  const originalWriteHead = res.writeHead.bind(res);
@@ -74,14 +108,17 @@ var ExpressAdapter = class {
74
108
  return originalEnd(...args);
75
109
  });
76
110
  next();
77
- const outcome = await chunkXUNNMCO5_cjs.raceTimeout(endPromise);
78
- if (outcome === chunkXUNNMCO5_cjs.HANDLER_TIMEOUT) {
111
+ const outcome = await chunkPOU7EH3E_cjs.raceTimeout(endPromise, options?.timeoutMs);
112
+ if (outcome === chunkPOU7EH3E_cjs.HANDLER_TIMEOUT) {
79
113
  discarded = true;
80
114
  return {
81
115
  status: 504,
82
116
  setResponseHeader: (name, value) => {
83
117
  if (!res.headersSent) res.setHeader(name, value);
84
118
  },
119
+ getResponseHeader: () => void 0,
120
+ removeResponseHeader: () => {
121
+ },
85
122
  sendResponse: () => {
86
123
  if (res.headersSent) return;
87
124
  originalWriteHead(504, { "Content-Type": "application/json" });
@@ -94,6 +131,26 @@ var ExpressAdapter = class {
94
131
  return {
95
132
  status: res.statusCode,
96
133
  setResponseHeader: (name, value) => res.setHeader(name, value),
134
+ getResponseHeader: (name) => headerValue(res.getHeader(name)),
135
+ removeResponseHeader: (name) => res.removeHeader(name),
136
+ // The buffered chunks are the whole response: parse them when the
137
+ // handler declared JSON.
138
+ getResponseBody: async () => {
139
+ const bytes = [];
140
+ let contentType = headerValue(res.getHeader("content-type"));
141
+ for (const [method, args] of buffered) {
142
+ if (method === "writeHead") {
143
+ contentType ??= writeHeadContentType(args);
144
+ continue;
145
+ }
146
+ const chunk = chunkBytes(args);
147
+ if (chunk) bytes.push(chunk);
148
+ }
149
+ return chunkPOU7EH3E_cjs.parseJsonBody(
150
+ contentType,
151
+ Buffer.concat(bytes).toString("utf8")
152
+ );
153
+ },
97
154
  sendResponse: () => {
98
155
  settled = true;
99
156
  restore();
@@ -113,7 +170,7 @@ var ExpressAdapter = class {
113
170
 
114
171
  // src/express/index.ts
115
172
  function paymentMiddleware(config) {
116
- const middleware = chunkKQPIZ6SZ_cjs.createPaymentMiddleware(config);
173
+ const middleware = chunkPOU7EH3E_cjs.createPaymentMiddleware(config);
117
174
  return async (req, res, next) => {
118
175
  const result = await middleware(new ExpressAdapter(req, res, next));
119
176
  if ("pass" in result) {
@@ -1,5 +1,5 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
- import { H as HTTPAdapter, R as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CUi55YSx.cjs';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class ExpressAdapter implements HTTPAdapter {
@@ -11,7 +11,8 @@ declare class ExpressAdapter implements HTTPAdapter {
11
11
  getMethod(): string;
12
12
  getPath(): string;
13
13
  getUrl(): string;
14
- runHandler(): Promise<RunHandlerResult>;
14
+ getBody(): Promise<unknown>;
15
+ runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
15
16
  }
16
17
 
17
18
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
package/dist/express.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
- import { H as HTTPAdapter, R as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CUi55YSx.js';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class ExpressAdapter implements HTTPAdapter {
@@ -11,7 +11,8 @@ declare class ExpressAdapter implements HTTPAdapter {
11
11
  getMethod(): string;
12
12
  getPath(): string;
13
13
  getUrl(): string;
14
- runHandler(): Promise<RunHandlerResult>;
14
+ getBody(): Promise<unknown>;
15
+ runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
15
16
  }
16
17
 
17
18
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
package/dist/express.js CHANGED
@@ -1,7 +1,36 @@
1
- import { raceTimeout, HANDLER_TIMEOUT } from './chunk-2UEAVMCH.js';
2
- import { createPaymentMiddleware } from './chunk-75GLDK7L.js';
1
+ import { raceTimeout, HANDLER_TIMEOUT, parseJsonBody, createPaymentMiddleware } from './chunk-AENXD4OC.js';
2
+ import './chunk-AFRDHGDT.js';
3
3
 
4
4
  // src/express/adapter.ts
5
+ function chunkBytes(args) {
6
+ const [chunk, encoding] = args;
7
+ if (typeof chunk === "string") {
8
+ return Buffer.from(
9
+ chunk,
10
+ typeof encoding === "string" ? encoding : "utf8"
11
+ );
12
+ }
13
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk);
14
+ return void 0;
15
+ }
16
+ function headerValue(value) {
17
+ if (value === void 0) return void 0;
18
+ return Array.isArray(value) ? value[0] : String(value);
19
+ }
20
+ function writeHeadContentType(args) {
21
+ const headers = args.find((arg) => typeof arg === "object" && arg !== null);
22
+ if (!headers) return void 0;
23
+ if (Array.isArray(headers)) {
24
+ const index = headers.findIndex(
25
+ (entry, i) => i % 2 === 0 && typeof entry === "string" && entry.toLowerCase() === "content-type"
26
+ );
27
+ return index === -1 ? void 0 : headerValue(headers[index + 1]);
28
+ }
29
+ for (const [name, value] of Object.entries(headers)) {
30
+ if (name.toLowerCase() === "content-type") return headerValue(value);
31
+ }
32
+ return void 0;
33
+ }
5
34
  var TIMEOUT_BODY = JSON.stringify({
6
35
  error: "HANDLER_TIMEOUT",
7
36
  message: "The resource handler did not respond in time."
@@ -28,7 +57,12 @@ var ExpressAdapter = class {
28
57
  getUrl() {
29
58
  return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`;
30
59
  }
31
- async runHandler() {
60
+ // Whatever a body parser mounted before this middleware left on `req.body`
61
+ // (`express.json()` for JSON); `undefined` when none ran.
62
+ async getBody() {
63
+ return this.req.body;
64
+ }
65
+ async runHandler(options) {
32
66
  const res = this.res;
33
67
  const next = this.next;
34
68
  const originalWriteHead = res.writeHead.bind(res);
@@ -72,7 +106,7 @@ var ExpressAdapter = class {
72
106
  return originalEnd(...args);
73
107
  });
74
108
  next();
75
- const outcome = await raceTimeout(endPromise);
109
+ const outcome = await raceTimeout(endPromise, options?.timeoutMs);
76
110
  if (outcome === HANDLER_TIMEOUT) {
77
111
  discarded = true;
78
112
  return {
@@ -80,6 +114,9 @@ var ExpressAdapter = class {
80
114
  setResponseHeader: (name, value) => {
81
115
  if (!res.headersSent) res.setHeader(name, value);
82
116
  },
117
+ getResponseHeader: () => void 0,
118
+ removeResponseHeader: () => {
119
+ },
83
120
  sendResponse: () => {
84
121
  if (res.headersSent) return;
85
122
  originalWriteHead(504, { "Content-Type": "application/json" });
@@ -92,6 +129,26 @@ var ExpressAdapter = class {
92
129
  return {
93
130
  status: res.statusCode,
94
131
  setResponseHeader: (name, value) => res.setHeader(name, value),
132
+ getResponseHeader: (name) => headerValue(res.getHeader(name)),
133
+ removeResponseHeader: (name) => res.removeHeader(name),
134
+ // The buffered chunks are the whole response: parse them when the
135
+ // handler declared JSON.
136
+ getResponseBody: async () => {
137
+ const bytes = [];
138
+ let contentType = headerValue(res.getHeader("content-type"));
139
+ for (const [method, args] of buffered) {
140
+ if (method === "writeHead") {
141
+ contentType ??= writeHeadContentType(args);
142
+ continue;
143
+ }
144
+ const chunk = chunkBytes(args);
145
+ if (chunk) bytes.push(chunk);
146
+ }
147
+ return parseJsonBody(
148
+ contentType,
149
+ Buffer.concat(bytes).toString("utf8")
150
+ );
151
+ },
95
152
  sendResponse: () => {
96
153
  settled = true;
97
154
  restore();
package/dist/fastify.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkKQPIZ6SZ_cjs = require('./chunk-KQPIZ6SZ.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ require('./chunk-JPMK5YBU.cjs');
4
5
  var fp = require('fastify-plugin');
5
6
 
6
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -26,14 +27,50 @@ var FastifyAdapter = class {
26
27
  getUrl() {
27
28
  return `${this.req.protocol}://${this.req.host || this.req.hostname}${this.req.url}`;
28
29
  }
30
+ // Fastify has parsed the body by the time the preHandler stage runs, which
31
+ // is where routes priced per request are gated.
32
+ async getBody() {
33
+ return this.req.body;
34
+ }
29
35
  };
30
36
 
31
37
  // src/fastify/index.ts
38
+ function payloadJson(contentType, payload) {
39
+ const text = typeof payload === "string" ? payload : payload instanceof Uint8Array ? Buffer.from(payload).toString("utf8") : void 0;
40
+ return chunkPOU7EH3E_cjs.parseJsonBody(contentType, text);
41
+ }
42
+ function refuse(reply, body) {
43
+ const json = JSON.stringify(body);
44
+ reply.status(402);
45
+ reply.header("Content-Type", "application/json");
46
+ reply.header("Content-Length", Buffer.byteLength(json));
47
+ return json;
48
+ }
49
+ function headerValue(value) {
50
+ if (value === void 0) return void 0;
51
+ return Array.isArray(value) ? value[0] : String(value);
52
+ }
32
53
  function paymentMiddleware(config) {
33
54
  return fp__default.default(async (fastify) => {
34
- const gate = chunkKQPIZ6SZ_cjs.createPaymentGate(config);
35
- const settleMap = /* @__PURE__ */ new WeakMap();
55
+ const gate = chunkPOU7EH3E_cjs.createPaymentGate(config);
56
+ const verifiedMap = /* @__PURE__ */ new WeakMap();
57
+ const rawWriters = /* @__PURE__ */ new WeakMap();
58
+ const needsBody = (req) => {
59
+ const route = chunkPOU7EH3E_cjs.matchRoute(
60
+ new FastifyAdapter(req).getPath(),
61
+ config.routes
62
+ );
63
+ return typeof route?.price === "function" || route?.meter !== void 0;
64
+ };
36
65
  fastify.addHook("onRequest", async (req, reply) => {
66
+ if (needsBody(req)) return;
67
+ await runGate(req, reply);
68
+ });
69
+ fastify.addHook("preHandler", async (req, reply) => {
70
+ if (!needsBody(req)) return;
71
+ await runGate(req, reply);
72
+ });
73
+ async function runGate(req, reply) {
37
74
  const gateResult = await gate(new FastifyAdapter(req));
38
75
  if (gateResult.type === "pass") return;
39
76
  if (gateResult.type === "error") {
@@ -43,32 +80,69 @@ function paymentMiddleware(config) {
43
80
  reply.status(gateResult.result.status).send(gateResult.result.body);
44
81
  return;
45
82
  }
46
- settleMap.set(req, gateResult.settle);
47
- });
83
+ verifiedMap.set(req, gateResult);
84
+ const raw = reply.raw;
85
+ rawWriters.set(req, {
86
+ write: raw.write,
87
+ end: raw.end,
88
+ writeHead: raw.writeHead
89
+ });
90
+ const refuse2 = () => {
91
+ throw new Error(
92
+ "x402: raw responses are not supported on paid routes \u2014 return the body from the handler so the payment can settle"
93
+ );
94
+ };
95
+ raw.write = refuse2;
96
+ raw.end = refuse2;
97
+ raw.writeHead = refuse2;
98
+ }
48
99
  fastify.addHook("onSend", async (req, reply, payload) => {
49
- const settle = settleMap.get(req);
50
- if (!settle || reply.statusCode < 200 || reply.statusCode >= 300) {
100
+ const writers = rawWriters.get(req);
101
+ if (writers) {
102
+ const raw2 = reply.raw;
103
+ raw2.write = writers.write;
104
+ raw2.end = writers.end;
105
+ raw2.writeHead = writers.writeHead;
106
+ rawWriters.delete(req);
107
+ }
108
+ const verified = verifiedMap.get(req);
109
+ if (!verified) return payload;
110
+ let raw;
111
+ if (verified.scheme === "upto") {
112
+ raw = headerValue(reply.getHeader(chunkPOU7EH3E_cjs.SETTLE_AMOUNT_HEADER));
113
+ reply.removeHeader(chunkPOU7EH3E_cjs.SETTLE_AMOUNT_HEADER);
114
+ }
115
+ if (reply.statusCode < 200 || reply.statusCode >= 300) {
51
116
  return payload;
52
117
  }
118
+ let settleOptions;
119
+ if (verified.scheme === "upto") {
120
+ const resolved = await chunkPOU7EH3E_cjs.resolveCharge(verified, raw, {
121
+ status: reply.statusCode,
122
+ header: (name) => headerValue(reply.getHeader(name)),
123
+ body: async () => payloadJson(headerValue(reply.getHeader("content-type")), payload)
124
+ });
125
+ if (resolved.kind === "invalid") {
126
+ return refuse(reply, chunkPOU7EH3E_cjs.invalidSettleAmountResponse(resolved.reason));
127
+ }
128
+ if (resolved.kind === "skip") return payload;
129
+ settleOptions = { amount: resolved.amount };
130
+ }
53
131
  let settled = false;
54
132
  try {
55
- const settleResponse = await settle();
133
+ const settleResponse = settleOptions ? await verified.settle(settleOptions) : await verified.settle();
56
134
  if (settleResponse.success) {
57
- reply.header("payment-response", chunkKQPIZ6SZ_cjs.encodeBase64(settleResponse));
135
+ reply.header("payment-response", chunkPOU7EH3E_cjs.encodeBase64(settleResponse));
58
136
  reply.header("access-control-expose-headers", "payment-response");
59
137
  settled = true;
60
138
  }
61
139
  } catch {
62
140
  }
63
141
  if (!settled) {
64
- const body = JSON.stringify({
142
+ return refuse(reply, {
65
143
  error: "PAYMENT_SETTLEMENT_FAILED",
66
144
  message: "Payment could not be confirmed after multiple attempts. Please retry."
67
145
  });
68
- reply.status(402);
69
- reply.header("Content-Type", "application/json");
70
- reply.header("Content-Length", Buffer.byteLength(body));
71
- return body;
72
146
  }
73
147
  return payload;
74
148
  });
@@ -1,5 +1,5 @@
1
1
  import { FastifyRequest, FastifyPluginAsync } from 'fastify';
2
- import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-CUi55YSx.cjs';
2
+ import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class FastifyAdapter implements HTTPAdapter {
@@ -9,6 +9,7 @@ declare class FastifyAdapter implements HTTPAdapter {
9
9
  getMethod(): string;
10
10
  getPath(): string;
11
11
  getUrl(): string;
12
+ getBody(): Promise<unknown>;
12
13
  }
13
14
 
14
15
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): FastifyPluginAsync;
package/dist/fastify.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FastifyRequest, FastifyPluginAsync } from 'fastify';
2
- import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-CUi55YSx.js';
2
+ import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class FastifyAdapter implements HTTPAdapter {
@@ -9,6 +9,7 @@ declare class FastifyAdapter implements HTTPAdapter {
9
9
  getMethod(): string;
10
10
  getPath(): string;
11
11
  getUrl(): string;
12
+ getBody(): Promise<unknown>;
12
13
  }
13
14
 
14
15
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): FastifyPluginAsync;
package/dist/fastify.js CHANGED
@@ -1,4 +1,5 @@
1
- import { createPaymentGate, encodeBase64 } from './chunk-75GLDK7L.js';
1
+ import { createPaymentGate, SETTLE_AMOUNT_HEADER, resolveCharge, invalidSettleAmountResponse, encodeBase64, matchRoute, parseJsonBody } from './chunk-AENXD4OC.js';
2
+ import './chunk-AFRDHGDT.js';
2
3
  import fp from 'fastify-plugin';
3
4
 
4
5
  // src/fastify/adapter.ts
@@ -20,14 +21,50 @@ var FastifyAdapter = class {
20
21
  getUrl() {
21
22
  return `${this.req.protocol}://${this.req.host || this.req.hostname}${this.req.url}`;
22
23
  }
24
+ // Fastify has parsed the body by the time the preHandler stage runs, which
25
+ // is where routes priced per request are gated.
26
+ async getBody() {
27
+ return this.req.body;
28
+ }
23
29
  };
24
30
 
25
31
  // src/fastify/index.ts
32
+ function payloadJson(contentType, payload) {
33
+ const text = typeof payload === "string" ? payload : payload instanceof Uint8Array ? Buffer.from(payload).toString("utf8") : void 0;
34
+ return parseJsonBody(contentType, text);
35
+ }
36
+ function refuse(reply, body) {
37
+ const json = JSON.stringify(body);
38
+ reply.status(402);
39
+ reply.header("Content-Type", "application/json");
40
+ reply.header("Content-Length", Buffer.byteLength(json));
41
+ return json;
42
+ }
43
+ function headerValue(value) {
44
+ if (value === void 0) return void 0;
45
+ return Array.isArray(value) ? value[0] : String(value);
46
+ }
26
47
  function paymentMiddleware(config) {
27
48
  return fp(async (fastify) => {
28
49
  const gate = createPaymentGate(config);
29
- const settleMap = /* @__PURE__ */ new WeakMap();
50
+ const verifiedMap = /* @__PURE__ */ new WeakMap();
51
+ const rawWriters = /* @__PURE__ */ new WeakMap();
52
+ const needsBody = (req) => {
53
+ const route = matchRoute(
54
+ new FastifyAdapter(req).getPath(),
55
+ config.routes
56
+ );
57
+ return typeof route?.price === "function" || route?.meter !== void 0;
58
+ };
30
59
  fastify.addHook("onRequest", async (req, reply) => {
60
+ if (needsBody(req)) return;
61
+ await runGate(req, reply);
62
+ });
63
+ fastify.addHook("preHandler", async (req, reply) => {
64
+ if (!needsBody(req)) return;
65
+ await runGate(req, reply);
66
+ });
67
+ async function runGate(req, reply) {
31
68
  const gateResult = await gate(new FastifyAdapter(req));
32
69
  if (gateResult.type === "pass") return;
33
70
  if (gateResult.type === "error") {
@@ -37,16 +74,57 @@ function paymentMiddleware(config) {
37
74
  reply.status(gateResult.result.status).send(gateResult.result.body);
38
75
  return;
39
76
  }
40
- settleMap.set(req, gateResult.settle);
41
- });
77
+ verifiedMap.set(req, gateResult);
78
+ const raw = reply.raw;
79
+ rawWriters.set(req, {
80
+ write: raw.write,
81
+ end: raw.end,
82
+ writeHead: raw.writeHead
83
+ });
84
+ const refuse2 = () => {
85
+ throw new Error(
86
+ "x402: raw responses are not supported on paid routes \u2014 return the body from the handler so the payment can settle"
87
+ );
88
+ };
89
+ raw.write = refuse2;
90
+ raw.end = refuse2;
91
+ raw.writeHead = refuse2;
92
+ }
42
93
  fastify.addHook("onSend", async (req, reply, payload) => {
43
- const settle = settleMap.get(req);
44
- if (!settle || reply.statusCode < 200 || reply.statusCode >= 300) {
94
+ const writers = rawWriters.get(req);
95
+ if (writers) {
96
+ const raw2 = reply.raw;
97
+ raw2.write = writers.write;
98
+ raw2.end = writers.end;
99
+ raw2.writeHead = writers.writeHead;
100
+ rawWriters.delete(req);
101
+ }
102
+ const verified = verifiedMap.get(req);
103
+ if (!verified) return payload;
104
+ let raw;
105
+ if (verified.scheme === "upto") {
106
+ raw = headerValue(reply.getHeader(SETTLE_AMOUNT_HEADER));
107
+ reply.removeHeader(SETTLE_AMOUNT_HEADER);
108
+ }
109
+ if (reply.statusCode < 200 || reply.statusCode >= 300) {
45
110
  return payload;
46
111
  }
112
+ let settleOptions;
113
+ if (verified.scheme === "upto") {
114
+ const resolved = await resolveCharge(verified, raw, {
115
+ status: reply.statusCode,
116
+ header: (name) => headerValue(reply.getHeader(name)),
117
+ body: async () => payloadJson(headerValue(reply.getHeader("content-type")), payload)
118
+ });
119
+ if (resolved.kind === "invalid") {
120
+ return refuse(reply, invalidSettleAmountResponse(resolved.reason));
121
+ }
122
+ if (resolved.kind === "skip") return payload;
123
+ settleOptions = { amount: resolved.amount };
124
+ }
47
125
  let settled = false;
48
126
  try {
49
- const settleResponse = await settle();
127
+ const settleResponse = settleOptions ? await verified.settle(settleOptions) : await verified.settle();
50
128
  if (settleResponse.success) {
51
129
  reply.header("payment-response", encodeBase64(settleResponse));
52
130
  reply.header("access-control-expose-headers", "payment-response");
@@ -55,14 +133,10 @@ function paymentMiddleware(config) {
55
133
  } catch {
56
134
  }
57
135
  if (!settled) {
58
- const body = JSON.stringify({
136
+ return refuse(reply, {
59
137
  error: "PAYMENT_SETTLEMENT_FAILED",
60
138
  message: "Payment could not be confirmed after multiple attempts. Please retry."
61
139
  });
62
- reply.status(402);
63
- reply.header("Content-Type", "application/json");
64
- reply.header("Content-Length", Buffer.byteLength(body));
65
- return body;
66
140
  }
67
141
  return payload;
68
142
  });
package/dist/hono.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkXUNNMCO5_cjs = require('./chunk-XUNNMCO5.cjs');
4
- var chunkKQPIZ6SZ_cjs = require('./chunk-KQPIZ6SZ.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ require('./chunk-JPMK5YBU.cjs');
5
5
 
6
6
  // src/hono/adapter.ts
7
7
  var TIMEOUT_BODY = JSON.stringify({
@@ -27,13 +27,26 @@ var HonoAdapter = class {
27
27
  getUrl() {
28
28
  return this.c.req.url;
29
29
  }
30
- async runHandler() {
31
- const outcome = await chunkXUNNMCO5_cjs.raceTimeout(this.next());
32
- if (outcome === chunkXUNNMCO5_cjs.HANDLER_TIMEOUT) {
30
+ // Hono caches the parsed body on the request, so the handler can still read
31
+ // it after the price function did.
32
+ async getBody() {
33
+ if (!chunkPOU7EH3E_cjs.isJsonContentType(this.c.req.header("content-type"))) return void 0;
34
+ try {
35
+ return await this.c.req.json();
36
+ } catch {
37
+ return void 0;
38
+ }
39
+ }
40
+ async runHandler(options) {
41
+ const outcome = await chunkPOU7EH3E_cjs.raceTimeout(this.next(), options?.timeoutMs);
42
+ if (outcome === chunkPOU7EH3E_cjs.HANDLER_TIMEOUT) {
33
43
  return {
34
44
  status: 504,
35
45
  setResponseHeader: () => {
36
46
  },
47
+ getResponseHeader: () => void 0,
48
+ removeResponseHeader: () => {
49
+ },
37
50
  sendResponse: () => {
38
51
  this.c.res = new Response(TIMEOUT_BODY, {
39
52
  status: 504,
@@ -48,9 +61,17 @@ var HonoAdapter = class {
48
61
  return {
49
62
  status: res.status,
50
63
  setResponseHeader: (name, value) => res.headers.set(name, value),
64
+ getResponseHeader: (name) => res.headers.get(name) ?? void 0,
65
+ removeResponseHeader: (name) => res.headers.delete(name),
66
+ // Read from a clone: the response body streams once, to the client.
67
+ getResponseBody: () => chunkPOU7EH3E_cjs.jsonFromResponse(res),
51
68
  sendResponse: () => {
52
69
  },
70
+ // The response is replaced: release its body (and whatever upstream
71
+ // stream feeds it) instead of leaving it open until collected.
53
72
  discardResponse: () => {
73
+ res.body?.cancel().catch(() => {
74
+ });
54
75
  }
55
76
  };
56
77
  }
@@ -58,7 +79,7 @@ var HonoAdapter = class {
58
79
 
59
80
  // src/hono/index.ts
60
81
  function paymentMiddleware(config) {
61
- const middleware = chunkKQPIZ6SZ_cjs.createPaymentMiddleware(config);
82
+ const middleware = chunkPOU7EH3E_cjs.createPaymentMiddleware(config);
62
83
  return async (c, next) => {
63
84
  const result = await middleware(new HonoAdapter(c, next));
64
85
  if ("pass" in result) {
package/dist/hono.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, Next, MiddlewareHandler } from 'hono';
2
- import { H as HTTPAdapter, R as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CUi55YSx.cjs';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
@@ -10,7 +10,8 @@ declare class HonoAdapter implements HTTPAdapter {
10
10
  getMethod(): string;
11
11
  getPath(): string;
12
12
  getUrl(): string;
13
- runHandler(): Promise<RunHandlerResult>;
13
+ getBody(): Promise<unknown>;
14
+ runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
14
15
  }
15
16
 
16
17
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): MiddlewareHandler;
package/dist/hono.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, Next, MiddlewareHandler } from 'hono';
2
- import { H as HTTPAdapter, R as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CUi55YSx.js';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
@@ -10,7 +10,8 @@ declare class HonoAdapter implements HTTPAdapter {
10
10
  getMethod(): string;
11
11
  getPath(): string;
12
12
  getUrl(): string;
13
- runHandler(): Promise<RunHandlerResult>;
13
+ getBody(): Promise<unknown>;
14
+ runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
14
15
  }
15
16
 
16
17
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): MiddlewareHandler;