@solvapay/server 2.7.0 → 2.8.0-preview-fac876555de66d02a17ebc2c433d1f2d5250ede7
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 +40 -15
- package/dist/edge.js +77 -35
- package/dist/fetch/index.cjs +84 -42
- package/dist/fetch/index.d.cts +10 -1
- package/dist/fetch/index.d.ts +10 -1
- package/dist/fetch/index.js +75 -33
- package/dist/index.cjs +93 -51
- package/dist/index.d.cts +41 -16
- package/dist/index.d.ts +41 -16
- package/dist/index.js +81 -39
- package/package.json +3 -3
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` / `
|
|
4264
|
-
*
|
|
4265
|
-
*
|
|
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 === '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3735
|
+
throw new SolvaPayError8("Invalid webhook signature");
|
|
3694
3736
|
}
|
|
3695
3737
|
try {
|
|
3696
3738
|
return JSON.parse(body);
|
|
3697
3739
|
} catch {
|
|
3698
|
-
throw new
|
|
3740
|
+
throw new SolvaPayError8("Invalid webhook payload: body is not valid JSON");
|
|
3699
3741
|
}
|
|
3700
3742
|
}
|
|
3701
3743
|
export {
|