@thecryptodonkey/toll-booth 3.2.4 → 3.2.5

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.
@@ -136,7 +136,8 @@ export function createExpressMiddleware(engineOrConfig, upstreamArg) {
136
136
  const tollCostHeader = upstream_res.headers.get('x-toll-cost');
137
137
  if (tollCostHeader !== null && /^\d+$/.test(tollCostHeader)) {
138
138
  const actualCost = parseInt(tollCostHeader, 10);
139
- if (Number.isSafeInteger(actualCost) && actualCost >= 0) {
139
+ const maxAllowed = (result.estimatedCost ?? actualCost) * 10;
140
+ if (Number.isSafeInteger(actualCost) && actualCost >= 0 && actualCost <= maxAllowed) {
140
141
  const reconciled = engine.reconcile(result.paymentHash, actualCost);
141
142
  if (reconciled.adjusted) {
142
143
  res.setHeader('X-Credit-Balance', String(reconciled.newBalance));
@@ -123,7 +123,8 @@ export function createWebStandardMiddleware(engineOrConfig, upstreamArg) {
123
123
  const tollCostHeader = res.headers.get('x-toll-cost');
124
124
  if (tollCostHeader !== null && /^\d+$/.test(tollCostHeader)) {
125
125
  const actualCost = parseInt(tollCostHeader, 10);
126
- if (Number.isSafeInteger(actualCost) && actualCost >= 0) {
126
+ const maxAllowed = (result.estimatedCost ?? actualCost) * 10;
127
+ if (Number.isSafeInteger(actualCost) && actualCost >= 0 && actualCost <= maxAllowed) {
127
128
  const reconciled = engine.reconcile(result.paymentHash, actualCost);
128
129
  if (reconciled.adjusted) {
129
130
  responseHeaders.set('X-Credit-Balance', String(reconciled.newBalance));
package/dist/booth.d.ts CHANGED
@@ -34,6 +34,7 @@ export declare class Booth {
34
34
  private readonly rootKey;
35
35
  private readonly redeemCashu?;
36
36
  private readonly pruneTimer?;
37
+ private closed;
37
38
  constructor(config: BoothOptions & EventHandler);
38
39
  /** Reset free-tier counters for all IPs. */
39
40
  resetFreeTier(): void;
package/dist/booth.js CHANGED
@@ -54,6 +54,7 @@ export class Booth {
54
54
  rootKey;
55
55
  redeemCashu;
56
56
  pruneTimer;
57
+ closed = false;
57
58
  constructor(config) {
58
59
  if (!config.backend && !config.redeemCashu && !config.x402) {
59
60
  throw new Error('At least one payment method required: provide a Lightning backend, redeemCashu callback, or x402 config');
@@ -197,8 +198,8 @@ export class Booth {
197
198
  // Auto-recover any pending Cashu claims from a previous crash
198
199
  if (this.redeemCashu) {
199
200
  const fn = this.redeemCashu;
200
- this.recoverPendingClaims(fn).catch(() => {
201
- // Recovery failures are non-fatal; claims stay pending for next restart
201
+ this.recoverPendingClaims(fn).catch((err) => {
202
+ console.warn('[toll-booth] Cashu recovery failed:', err instanceof Error ? err.message : err);
202
203
  });
203
204
  }
204
205
  }
@@ -222,12 +223,18 @@ export class Booth {
222
223
  let recovered = 0;
223
224
  const renewIntervalMs = Math.max(1_000, Math.floor(REDEEM_LEASE_MS / 2));
224
225
  for (const claim of claims) {
226
+ if (this.closed)
227
+ break;
225
228
  // Respect active leases so startup recovery does not race in-flight requests
226
229
  // (or another process already handling this claim).
227
230
  const leasedClaim = this.storage.tryAcquireRecoveryLease(claim.paymentHash, REDEEM_LEASE_MS);
228
231
  if (!leasedClaim)
229
232
  continue;
230
233
  const timer = setInterval(() => {
234
+ if (this.closed) {
235
+ clearInterval(timer);
236
+ return;
237
+ }
231
238
  this.storage.extendRecoveryLease(leasedClaim.paymentHash, REDEEM_LEASE_MS);
232
239
  }, renewIntervalMs);
233
240
  try {
@@ -254,6 +261,7 @@ export class Booth {
254
261
  return recovered;
255
262
  }
256
263
  close() {
264
+ this.closed = true;
257
265
  if (this.pruneTimer)
258
266
  clearInterval(this.pruneTimer);
259
267
  this.storage.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecryptodonkey/toll-booth",
3
- "version": "3.2.4",
3
+ "version": "3.2.5",
4
4
  "type": "module",
5
5
  "description": "Monetise any API with HTTP 402 payments. Payment-rail agnostic middleware for Express, Hono, Deno, Bun, and Workers.",
6
6
  "license": "MIT",