@solvapay/server 2.7.0 → 2.8.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/edge.d.ts CHANGED
@@ -254,6 +254,15 @@ interface components {
254
254
  };
255
255
  CheckLimitRequest: {
256
256
  customerRef: string;
257
+ /** @description SDK-only free-tool allowance. Authenticated by the provider secret key, so `cap` is the provider's own declaration, not customer input. */
258
+ freeAllowance?: {
259
+ cap?: number;
260
+ /** @description Free meter name. Must match `/^free-[a-z0-9-]+$/`. */
261
+ meter?: string;
262
+ /** @enum {string} */
263
+ scope?: 'rolling_window' | 'lifetime';
264
+ windowDays?: number;
265
+ };
257
266
  includeCheckoutSession?: boolean;
258
267
  meterName?: string;
259
268
  productRef: string;
@@ -909,7 +918,7 @@ interface components {
909
918
  * Authoritative paywall classification shared with Managed MCP. Present on denial responses only.
910
919
  * @enum {string}
911
920
  */
912
- paywallReason?: 'activation_required' | 'topup_required' | 'payment_required';
921
+ paywallReason?: 'activation_required' | 'topup_required' | 'payment_required' | 'limit_reached';
913
922
  /** @description Display name of the active or default plan */
914
923
  planName?: string;
915
924
  /** @description Active plan reference when the customer already holds a purchase */
@@ -3919,6 +3928,16 @@ type AttachBusinessDetailsParams = {
3919
3928
  } & components['schemas']['BusinessDetailsDto'];
3920
3929
  type AttachBusinessDetailsResult = components['schemas']['AttachBusinessDetailsResponse'];
3921
3930
  type CheckLimitsRequest = components['schemas']['CheckLimitRequest'];
3931
+ /**
3932
+ * Per-customer cap declared in code on a `registerFree` tool. Not a
3933
+ * registered `Meter` — the name must match `/^free-[a-z0-9-]+$/`.
3934
+ *
3935
+ * Derived from `CheckLimitRequest.freeAllowance`. nestjs-zod omits
3936
+ * nested `required`, so OpenAPI marks every property optional; the
3937
+ * object schema only treats `windowDays` as optional.
3938
+ */
3939
+ type GeneratedFreeAllowance = NonNullable<CheckLimitsRequest['freeAllowance']>;
3940
+ type FreeLimit = Required<Omit<GeneratedFreeAllowance, 'windowDays'>> & Pick<GeneratedFreeAllowance, 'windowDays'>;
3922
3941
  /**
3923
3942
  * `LimitResponse` plus a deprecated SDK-only `plan` alias.
3924
3943
  *
@@ -4224,6 +4243,11 @@ interface PaywallMetadata {
4224
4243
  * attributable. Set by `registerPayable` / `buildPayableHandler`.
4225
4244
  */
4226
4245
  toolName?: string;
4246
+ /**
4247
+ * SDK-only free-tool allowance. When set, `decide()` sends this as
4248
+ * `freeAllowance` and the free meter wins over `meterName`.
4249
+ */
4250
+ freeLimit?: FreeLimit;
4227
4251
  }
4228
4252
  /**
4229
4253
  * Structured content for paywall errors (MCP structuredContent and manual handling).
@@ -4260,9 +4284,9 @@ type PaywallGateRecoveryFields = {
4260
4284
  creditBalance?: number;
4261
4285
  /**
4262
4286
  * `PaywallState.kind` — same vocabulary as the classifier, not a third enum.
4263
- * `upgrade_required` / `limit_reached` / `reactivation_required` are
4264
- * SDK-only; the backend's `paywallReason` is a smaller set
4265
- * (`activation_required` / `topup_required` / `payment_required`).
4287
+ * `upgrade_required` / `reactivation_required` are SDK-only. The
4288
+ * backend's `paywallReason` covers `activation_required` /
4289
+ * `topup_required` / `payment_required` / `limit_reached`.
4266
4290
  */
4267
4291
  reason?: PaywallReason;
4268
4292
  /** Single primary recovery the agent should take. */
@@ -4386,6 +4410,7 @@ type PaywallDecision<T> = {
4386
4410
  * Types for configuring various aspects of the SDK including retry behavior,
4387
4411
  * payable protection, and framework adapters.
4388
4412
  */
4413
+
4389
4414
  /**
4390
4415
  * Retry configuration options
4391
4416
  */
@@ -4464,6 +4489,11 @@ interface PayableOptions {
4464
4489
  * Product reference (alias for product, preferred for consistency with backend API)
4465
4490
  */
4466
4491
  productRef?: string;
4492
+ /**
4493
+ * SDK-only free-tool allowance. When set, this meter's name wins over
4494
+ * `meterName` / `usageType`, and `checkLimits` sends a `freeAllowance` block.
4495
+ */
4496
+ freeLimit?: FreeLimit;
4467
4497
  /**
4468
4498
  * Meter to charge against (defaults to `requests`).
4469
4499
  */
@@ -5096,14 +5126,7 @@ interface SolvaPay {
5096
5126
  * }
5097
5127
  * ```
5098
5128
  */
5099
- checkLimits(params: {
5100
- customerRef: string;
5101
- productRef: string;
5102
- planRef?: string;
5103
- meterName?: string;
5104
- /** @deprecated Use `meterName`. */
5105
- usageType?: string;
5106
- }): Promise<LimitResponseWithPlan>;
5129
+ checkLimits(params: CheckLimitsRequest): Promise<LimitResponseWithPlan>;
5107
5130
  /**
5108
5131
  * Track usage for a customer action.
5109
5132
  *
@@ -5509,8 +5532,8 @@ declare const PaywallStructuredContentSchema: z.ZodDiscriminatedUnion<[z.ZodObje
5509
5532
  topup_required: "topup_required";
5510
5533
  payment_required: "payment_required";
5511
5534
  activation_required: "activation_required";
5512
- upgrade_required: "upgrade_required";
5513
5535
  limit_reached: "limit_reached";
5536
+ upgrade_required: "upgrade_required";
5514
5537
  reactivation_required: "reactivation_required";
5515
5538
  }>>;
5516
5539
  nextAction: z.ZodOptional<z.ZodEnum<{
@@ -5558,8 +5581,8 @@ declare const PaywallStructuredContentSchema: z.ZodDiscriminatedUnion<[z.ZodObje
5558
5581
  topup_required: "topup_required";
5559
5582
  payment_required: "payment_required";
5560
5583
  activation_required: "activation_required";
5561
- upgrade_required: "upgrade_required";
5562
5584
  limit_reached: "limit_reached";
5585
+ upgrade_required: "upgrade_required";
5563
5586
  reactivation_required: "reactivation_required";
5564
5587
  }>>;
5565
5588
  nextAction: z.ZodOptional<z.ZodEnum<{
@@ -5640,7 +5663,9 @@ declare function nextActionFor(state: PaywallState): PaywallNextAction;
5640
5663
  *
5641
5664
  * Precedence:
5642
5665
  * 1. `activationRequired` / `paywallReason === 'activation_required'`.
5643
- * 2. `paywallReason === 'topup_required'` — backend stays authoritative
5666
+ * 2. `paywallReason === 'limit_reached'` — free-allowance exhaustion
5667
+ * (no purchaseRef / planRef) must not fall through to upgrade_required.
5668
+ * 3. `paywallReason === 'topup_required'` — backend stays authoritative
5644
5669
  * for credit-based denials (same rule as Managed MCP).
5645
5670
  * 3. Authoritative `needsTopUp` / `needsUpgrade` flags from `decideLimit`.
5646
5671
  * 4. Credit-field presence + a real shortfall (`balance < cost`).
package/dist/edge.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/edge.ts
2
- import { SolvaPayError as SolvaPayError7 } from "@solvapay/core";
2
+ import { SolvaPayError as SolvaPayError8 } from "@solvapay/core";
3
3
 
4
4
  // src/client.ts
5
5
  import { SolvaPayError } from "@solvapay/core";
@@ -723,6 +723,9 @@ function classifyPaywallState(limits) {
723
723
  if (limits.activationRequired === true || limits.paywallReason === "activation_required") {
724
724
  return { kind: "activation_required" };
725
725
  }
726
+ if (limits.paywallReason === "limit_reached") {
727
+ return { kind: "limit_reached" };
728
+ }
726
729
  if (limits.paywallReason === "topup_required") {
727
730
  return { kind: "topup_required" };
728
731
  }
@@ -786,9 +789,12 @@ function formatCreditsWithMoney(credits, gate) {
786
789
  }
787
790
  return `${amount} credits`;
788
791
  }
792
+ function isFreeMeter(name) {
793
+ return typeof name === "string" && name.startsWith("free-");
794
+ }
789
795
  function meterLabel(gate) {
790
796
  if (!gate.meterName) return "units";
791
- return gate.meterName.replace(/_/g, " ");
797
+ return gate.meterName.replace(/[_-]/g, " ");
792
798
  }
793
799
  function namedCheckoutMarkdown(url, label = "Open checkout") {
794
800
  return `[${label}](${url})`;
@@ -824,6 +830,12 @@ function buildGateMessage(state, gate) {
824
830
  switch (state.kind) {
825
831
  case "limit_reached": {
826
832
  const included = gate.included;
833
+ if (isFreeMeter(gate.meterName)) {
834
+ const total = included?.total;
835
+ const usedLine2 = total !== void 0 ? `You've used all ${total} ${meterLabel(gate)} for this period.` : `You've used all ${meterLabel(gate)} for this period.`;
836
+ const switchLine2 = ladder ? ` Pick a plan to keep going: ${ladder}. ${callViewer("account").replace(/^c/, "C")} for usage and recovery.` : recoverClause(url, "keep going", "checkout");
837
+ return `${usedLine2}${switchLine2}`;
838
+ }
827
839
  const price = gate.unitPriceMinor != null && gate.currency ? formatMinor(gate.unitPriceMinor, gate.currency) : null;
828
840
  const usedLine = included ? `You've used ${included.used} of ${included.total} included ${meterLabel(gate)} this period.` : `You've reached the included usage for this period.`;
829
841
  const nextLine = price ? ` The next call is ${price}.` : "";
@@ -1150,6 +1162,7 @@ function createRequestDeduplicator(options = {}) {
1150
1162
  }
1151
1163
 
1152
1164
  // src/paywall.ts
1165
+ import { SolvaPayError as SolvaPayError4 } from "@solvapay/core";
1153
1166
  var PaywallError = class extends Error {
1154
1167
  /**
1155
1168
  * Creates a new PaywallError instance.
@@ -1200,6 +1213,15 @@ var sharedLimitsFetchDeduplicator = createRequestDeduplicator({
1200
1213
  });
1201
1214
  var sharedLimitsFetchClaims = /* @__PURE__ */ new Map();
1202
1215
  var EXTRA_FORWARD_KEY = "__solvapayExtra";
1216
+ function trackUsageExtra(metadata, outcome, consequence) {
1217
+ if (metadata.freeLimit) {
1218
+ return { meterName: metadata.freeLimit.meter };
1219
+ }
1220
+ if (outcome === "success") {
1221
+ return { usageClass: consequence === "overage" ? "overage" : "included" };
1222
+ }
1223
+ return void 0;
1224
+ }
1203
1225
  var SolvaPayPaywall = class {
1204
1226
  constructor(apiClient, options = {}) {
1205
1227
  this.apiClient = apiClient;
@@ -1259,10 +1281,19 @@ var SolvaPayPaywall = class {
1259
1281
  */
1260
1282
  async decide(args, metadata = {}, getCustomerRef) {
1261
1283
  const product = this.resolveProduct(metadata);
1262
- const usageType = metadata.meterName || metadata.usageType || "requests";
1284
+ const usageType = metadata.freeLimit?.meter || metadata.meterName || metadata.usageType || "requests";
1263
1285
  const requestId = this.generateRequestId();
1264
1286
  const startTime = Date.now();
1265
1287
  const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
1288
+ if (metadata.freeLimit) {
1289
+ const resolved = typeof inputCustomerRef === "string" ? inputCustomerRef.trim() : "";
1290
+ if (!resolved || resolved === "anonymous") {
1291
+ throw new SolvaPayError4("identity required", {
1292
+ status: 401,
1293
+ code: "identity_required"
1294
+ });
1295
+ }
1296
+ }
1266
1297
  let backendCustomerRef;
1267
1298
  if (inputCustomerRef.startsWith("cus_")) {
1268
1299
  backendCustomerRef = inputCustomerRef;
@@ -1313,7 +1344,8 @@ var SolvaPayPaywall = class {
1313
1344
  // `checkLimitsCore`, which powers the React `useLimits` hook)
1314
1345
  // leave this unset and the backend skips the session-creation
1315
1346
  // side effect.
1316
- includeCheckoutSession: true
1347
+ includeCheckoutSession: true,
1348
+ ...metadata.freeLimit ? { freeAllowance: metadata.freeLimit } : {}
1317
1349
  });
1318
1350
  }
1319
1351
  );
@@ -1357,7 +1389,8 @@ var SolvaPayPaywall = class {
1357
1389
  "paywall",
1358
1390
  requestId,
1359
1391
  latencyMs,
1360
- metadata.toolName
1392
+ metadata.toolName,
1393
+ metadata.freeLimit ? { meterName: metadata.freeLimit.meter } : void 0
1361
1394
  ).catch(() => void 0);
1362
1395
  const gate = buildPaywallGate(
1363
1396
  product,
@@ -1404,7 +1437,7 @@ var SolvaPayPaywall = class {
1404
1437
  */
1405
1438
  async runAllow(decision, handler, metadata, args) {
1406
1439
  const product = this.resolveProduct(metadata);
1407
- const usageType = metadata.meterName || metadata.usageType || "requests";
1440
+ const usageType = metadata.freeLimit?.meter || metadata.meterName || metadata.usageType || "requests";
1408
1441
  const requestId = decision.requestId;
1409
1442
  const startTime = Date.now();
1410
1443
  const forwardedExtra = args[EXTRA_FORWARD_KEY];
@@ -1423,7 +1456,8 @@ var SolvaPayPaywall = class {
1423
1456
  "success",
1424
1457
  requestId,
1425
1458
  latencyMs,
1426
- metadata.toolName
1459
+ metadata.toolName,
1460
+ trackUsageExtra(metadata, "success", decision.consequence)
1427
1461
  ).catch(() => void 0);
1428
1462
  return result;
1429
1463
  } catch (error) {
@@ -1442,7 +1476,8 @@ var SolvaPayPaywall = class {
1442
1476
  "fail",
1443
1477
  requestId,
1444
1478
  latencyMs,
1445
- metadata.toolName
1479
+ metadata.toolName,
1480
+ trackUsageExtra(metadata, "fail")
1446
1481
  ).catch(() => void 0);
1447
1482
  }
1448
1483
  throw error;
@@ -1633,7 +1668,7 @@ var SolvaPayPaywall = class {
1633
1668
  }
1634
1669
  return backendRef;
1635
1670
  }
1636
- async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration, toolName) {
1671
+ async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration, toolName, extra) {
1637
1672
  await withRetry(
1638
1673
  () => this.apiClient.trackUsage({
1639
1674
  customerRef,
@@ -1646,7 +1681,9 @@ var SolvaPayPaywall = class {
1646
1681
  metadata: {
1647
1682
  action: action || "api_requests",
1648
1683
  requestId,
1649
- ...toolName ? { toolName } : {}
1684
+ ...toolName ? { toolName } : {},
1685
+ ...extra?.meterName ? { meterName: extra.meterName } : {},
1686
+ ...extra?.usageClass ? { usageClass: extra.usageClass } : {}
1650
1687
  },
1651
1688
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1652
1689
  }),
@@ -1986,7 +2023,7 @@ var McpAdapter = class {
1986
2023
  };
1987
2024
 
1988
2025
  // src/factory.ts
1989
- import { SolvaPayError as SolvaPayError4, getSolvaPayConfig } from "@solvapay/core";
2026
+ import { SolvaPayError as SolvaPayError5, getSolvaPayConfig } from "@solvapay/core";
1990
2027
 
1991
2028
  // src/virtual-tools.ts
1992
2029
  var TOOL_GET_USER_INFO = {
@@ -2263,25 +2300,25 @@ function createSolvaPay(config) {
2263
2300
  },
2264
2301
  createPaymentIntent(params) {
2265
2302
  if (!apiClient.createPaymentIntent) {
2266
- throw new SolvaPayError4("createPaymentIntent is not available on this API client");
2303
+ throw new SolvaPayError5("createPaymentIntent is not available on this API client");
2267
2304
  }
2268
2305
  return apiClient.createPaymentIntent(params);
2269
2306
  },
2270
2307
  createTopupPaymentIntent(params) {
2271
2308
  if (!apiClient.createTopupPaymentIntent) {
2272
- throw new SolvaPayError4("createTopupPaymentIntent is not available on this API client");
2309
+ throw new SolvaPayError5("createTopupPaymentIntent is not available on this API client");
2273
2310
  }
2274
2311
  return apiClient.createTopupPaymentIntent(params);
2275
2312
  },
2276
2313
  processPaymentIntent(params) {
2277
2314
  if (!apiClient.processPaymentIntent) {
2278
- throw new SolvaPayError4("processPaymentIntent is not available on this API client");
2315
+ throw new SolvaPayError5("processPaymentIntent is not available on this API client");
2279
2316
  }
2280
2317
  return apiClient.processPaymentIntent(params);
2281
2318
  },
2282
2319
  attachBusinessDetails(params) {
2283
2320
  if (!apiClient.attachBusinessDetails) {
2284
- throw new SolvaPayError4("attachBusinessDetails is not available on this API client");
2321
+ throw new SolvaPayError5("attachBusinessDetails is not available on this API client");
2285
2322
  }
2286
2323
  return apiClient.attachBusinessDetails(params);
2287
2324
  },
@@ -2293,13 +2330,13 @@ function createSolvaPay(config) {
2293
2330
  },
2294
2331
  trackUsageBulk(params) {
2295
2332
  if (!apiClient.trackUsageBulk) {
2296
- throw new SolvaPayError4("trackUsageBulk is not available on this API client");
2333
+ throw new SolvaPayError5("trackUsageBulk is not available on this API client");
2297
2334
  }
2298
2335
  return apiClient.trackUsageBulk(params);
2299
2336
  },
2300
2337
  createCustomer(params) {
2301
2338
  if (!apiClient.createCustomer) {
2302
- throw new SolvaPayError4("createCustomer is not available on this API client");
2339
+ throw new SolvaPayError5("createCustomer is not available on this API client");
2303
2340
  }
2304
2341
  return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
2305
2342
  },
@@ -2308,13 +2345,13 @@ function createSolvaPay(config) {
2308
2345
  },
2309
2346
  assignCredits(params) {
2310
2347
  if (!apiClient.assignCredits) {
2311
- throw new SolvaPayError4("assignCredits is not available on this API client");
2348
+ throw new SolvaPayError5("assignCredits is not available on this API client");
2312
2349
  }
2313
2350
  return apiClient.assignCredits(params);
2314
2351
  },
2315
2352
  getCustomerBalance(params) {
2316
2353
  if (!apiClient.getCustomerBalance) {
2317
- throw new SolvaPayError4("getCustomerBalance is not available on this API client");
2354
+ throw new SolvaPayError5("getCustomerBalance is not available on this API client");
2318
2355
  }
2319
2356
  return apiClient.getCustomerBalance(params);
2320
2357
  },
@@ -2332,19 +2369,19 @@ function createSolvaPay(config) {
2332
2369
  },
2333
2370
  activatePlan(params) {
2334
2371
  if (!apiClient.activatePlan) {
2335
- throw new SolvaPayError4("activatePlan is not available on this API client");
2372
+ throw new SolvaPayError5("activatePlan is not available on this API client");
2336
2373
  }
2337
2374
  return apiClient.activatePlan(params);
2338
2375
  },
2339
2376
  bootstrapMcpProduct(params) {
2340
2377
  if (!apiClient.bootstrapMcpProduct) {
2341
- throw new SolvaPayError4("bootstrapMcpProduct is not available on this API client");
2378
+ throw new SolvaPayError5("bootstrapMcpProduct is not available on this API client");
2342
2379
  }
2343
2380
  return apiClient.bootstrapMcpProduct(params);
2344
2381
  },
2345
2382
  configureMcpPlans(productRef, params) {
2346
2383
  if (!apiClient.configureMcpPlans) {
2347
- throw new SolvaPayError4("configureMcpPlans is not available on this API client");
2384
+ throw new SolvaPayError5("configureMcpPlans is not available on this API client");
2348
2385
  }
2349
2386
  return apiClient.configureMcpPlans(productRef, params);
2350
2387
  },
@@ -2357,12 +2394,13 @@ function createSolvaPay(config) {
2357
2394
  // Payable API for framework-specific handlers
2358
2395
  payable(options = {}) {
2359
2396
  const product = resolveProductRef(options.productRef || options.product);
2360
- const usageType = options.meterName || options.usageType || "requests";
2397
+ const usageType = options.freeLimit?.meter || options.meterName || options.usageType || "requests";
2361
2398
  const metadata = {
2362
2399
  product,
2363
2400
  meterName: usageType,
2364
2401
  usageType,
2365
- ...options.toolName ? { toolName: options.toolName } : {}
2402
+ ...options.toolName ? { toolName: options.toolName } : {},
2403
+ ...options.freeLimit ? { freeLimit: options.freeLimit } : {}
2366
2404
  };
2367
2405
  return {
2368
2406
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2451,6 +2489,8 @@ function createSolvaPay(config) {
2451
2489
  const errMeta = opts?.error !== void 0 ? {
2452
2490
  error: opts.error instanceof Error ? opts.error.message : String(opts.error)
2453
2491
  } : {};
2492
+ const isFree = Boolean(decideMetadata.freeLimit);
2493
+ const usageClass = !isFree && outcome === "success" ? decision.consequence === "overage" ? "overage" : "included" : void 0;
2454
2494
  const trackPromise = apiClient.trackUsage({
2455
2495
  customerRef,
2456
2496
  productRef,
@@ -2463,6 +2503,8 @@ function createSolvaPay(config) {
2463
2503
  action: meterName,
2464
2504
  requestId,
2465
2505
  ...decideMetadata.toolName ? { toolName: decideMetadata.toolName } : {},
2506
+ ...isFree && decideMetadata.freeLimit ? { meterName: decideMetadata.freeLimit.meter } : {},
2507
+ ...usageClass ? { usageClass } : {},
2466
2508
  ...errMeta,
2467
2509
  ...opts?.metadata ?? {}
2468
2510
  },
@@ -2568,13 +2610,13 @@ var PaywallStructuredContentSchema = z.discriminatedUnion("kind", [
2568
2610
  ]);
2569
2611
 
2570
2612
  // src/helpers/error.ts
2571
- import { SolvaPayError as SolvaPayError5 } from "@solvapay/core";
2613
+ import { SolvaPayError as SolvaPayError6 } from "@solvapay/core";
2572
2614
  function isErrorResult(result) {
2573
2615
  return typeof result === "object" && result !== null && "error" in result && "status" in result;
2574
2616
  }
2575
2617
  function handleRouteError(error, operationName, defaultMessage) {
2576
2618
  console.error(`[${operationName}] Error:`, error);
2577
- if (error instanceof SolvaPayError5) {
2619
+ if (error instanceof SolvaPayError6) {
2578
2620
  const errorMessage2 = error.message;
2579
2621
  return {
2580
2622
  error: errorMessage2,
@@ -3073,7 +3115,7 @@ async function createCustomerSessionCore(request, options = {}) {
3073
3115
  }
3074
3116
 
3075
3117
  // src/helpers/renewal.ts
3076
- import { SolvaPayError as SolvaPayError6 } from "@solvapay/core";
3118
+ import { SolvaPayError as SolvaPayError7 } from "@solvapay/core";
3077
3119
  async function cancelPurchaseCore(request, body, options = {}) {
3078
3120
  try {
3079
3121
  if (!body.purchaseRef) {
@@ -3119,7 +3161,7 @@ async function cancelPurchaseCore(request, body, options = {}) {
3119
3161
  await new Promise((resolve) => setTimeout(resolve, 500));
3120
3162
  return cancelledPurchase;
3121
3163
  } catch (error) {
3122
- if (error instanceof SolvaPayError6) {
3164
+ if (error instanceof SolvaPayError7) {
3123
3165
  const errorMessage = error.message;
3124
3166
  if (errorMessage.includes("not found")) {
3125
3167
  return {
@@ -3187,7 +3229,7 @@ async function reactivatePurchaseCore(request, body, options = {}) {
3187
3229
  await new Promise((resolve) => setTimeout(resolve, 500));
3188
3230
  return reactivatedPurchase;
3189
3231
  } catch (error) {
3190
- if (error instanceof SolvaPayError6) {
3232
+ if (error instanceof SolvaPayError7) {
3191
3233
  const errorMessage = error.message;
3192
3234
  if (errorMessage.includes("not found")) {
3193
3235
  return {
@@ -3661,22 +3703,22 @@ async function verifyWebhook({
3661
3703
  secret
3662
3704
  }) {
3663
3705
  const toleranceSec = 300;
3664
- if (!signature) throw new SolvaPayError7("Missing webhook signature");
3706
+ if (!signature) throw new SolvaPayError8("Missing webhook signature");
3665
3707
  const parts = signature.split(",");
3666
3708
  const tPart = parts.find((p) => p.startsWith("t="));
3667
3709
  const v1Part = parts.find((p) => p.startsWith("v1="));
3668
3710
  if (!tPart || !v1Part) {
3669
- throw new SolvaPayError7("Malformed webhook signature");
3711
+ throw new SolvaPayError8("Malformed webhook signature");
3670
3712
  }
3671
3713
  const timestamp = parseInt(tPart.slice(2), 10);
3672
3714
  const receivedHmac = v1Part.slice(3);
3673
3715
  if (Number.isNaN(timestamp) || !receivedHmac) {
3674
- throw new SolvaPayError7("Malformed webhook signature");
3716
+ throw new SolvaPayError8("Malformed webhook signature");
3675
3717
  }
3676
3718
  if (toleranceSec > 0) {
3677
3719
  const age = Math.abs(Math.floor(Date.now() / 1e3) - timestamp);
3678
3720
  if (age > toleranceSec) {
3679
- throw new SolvaPayError7("Webhook signature timestamp too old");
3721
+ throw new SolvaPayError8("Webhook signature timestamp too old");
3680
3722
  }
3681
3723
  }
3682
3724
  const enc = new TextEncoder();
@@ -3690,12 +3732,12 @@ async function verifyWebhook({
3690
3732
  const sigBuf = await crypto.subtle.sign("HMAC", key, enc.encode(`${timestamp}.${body}`));
3691
3733
  const expectedHmac = Array.from(new Uint8Array(sigBuf)).map((b) => b.toString(16).padStart(2, "0")).join("");
3692
3734
  if (!timingSafeEqual(expectedHmac, receivedHmac)) {
3693
- throw new SolvaPayError7("Invalid webhook signature");
3735
+ throw new SolvaPayError8("Invalid webhook signature");
3694
3736
  }
3695
3737
  try {
3696
3738
  return JSON.parse(body);
3697
3739
  } catch {
3698
- throw new SolvaPayError7("Invalid webhook payload: body is not valid JSON");
3740
+ throw new SolvaPayError8("Invalid webhook payload: body is not valid JSON");
3699
3741
  }
3700
3742
  }
3701
3743
  export {