@solvapay/server 1.0.0-preview.20 → 1.0.0-preview.21
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 +32 -6
- package/dist/edge.d.ts +1584 -310
- package/dist/edge.js +329 -50
- package/dist/index.cjs +395 -50
- package/dist/index.d.cts +1611 -310
- package/dist/index.d.ts +1611 -310
- package/dist/index.js +388 -50
- package/package.json +7 -6
package/dist/index.cjs
CHANGED
|
@@ -4254,14 +4254,21 @@ var init_esm = __esm({
|
|
|
4254
4254
|
// src/index.ts
|
|
4255
4255
|
var index_exports = {};
|
|
4256
4256
|
__export(index_exports, {
|
|
4257
|
+
McpBearerAuthError: () => McpBearerAuthError,
|
|
4257
4258
|
PaywallError: () => PaywallError,
|
|
4259
|
+
VIRTUAL_TOOL_DEFINITIONS: () => VIRTUAL_TOOL_DEFINITIONS,
|
|
4258
4260
|
cancelPurchaseCore: () => cancelPurchaseCore,
|
|
4259
4261
|
createCheckoutSessionCore: () => createCheckoutSessionCore,
|
|
4260
4262
|
createCustomerSessionCore: () => createCustomerSessionCore,
|
|
4261
4263
|
createPaymentIntentCore: () => createPaymentIntentCore,
|
|
4262
4264
|
createSolvaPay: () => createSolvaPay,
|
|
4263
4265
|
createSolvaPayClient: () => createSolvaPayClient,
|
|
4266
|
+
createVirtualTools: () => createVirtualTools,
|
|
4267
|
+
decodeJwtPayload: () => decodeJwtPayload,
|
|
4268
|
+
extractBearerToken: () => extractBearerToken,
|
|
4264
4269
|
getAuthenticatedUserCore: () => getAuthenticatedUserCore,
|
|
4270
|
+
getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
|
|
4271
|
+
getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
|
|
4265
4272
|
handleRouteError: () => handleRouteError,
|
|
4266
4273
|
isErrorResult: () => isErrorResult,
|
|
4267
4274
|
listPlansCore: () => listPlansCore,
|
|
@@ -4309,10 +4316,12 @@ function createSolvaPayClient(opts) {
|
|
|
4309
4316
|
// POST: /v1/sdk/usages
|
|
4310
4317
|
async trackUsage(params) {
|
|
4311
4318
|
const url = `${base}/v1/sdk/usages`;
|
|
4319
|
+
const { customerRef, ...rest } = params;
|
|
4320
|
+
const body = { ...rest, customerId: customerRef };
|
|
4312
4321
|
const res = await fetch(url, {
|
|
4313
4322
|
method: "POST",
|
|
4314
4323
|
headers,
|
|
4315
|
-
body: JSON.stringify(
|
|
4324
|
+
body: JSON.stringify(body)
|
|
4316
4325
|
});
|
|
4317
4326
|
if (!res.ok) {
|
|
4318
4327
|
const error = await res.text();
|
|
@@ -4416,6 +4425,21 @@ function createSolvaPayClient(opts) {
|
|
|
4416
4425
|
const result = await res.json();
|
|
4417
4426
|
return result;
|
|
4418
4427
|
},
|
|
4428
|
+
// POST: /v1/sdk/products/mcp/bootstrap
|
|
4429
|
+
async bootstrapMcpProduct(params) {
|
|
4430
|
+
const url = `${base}/v1/sdk/products/mcp/bootstrap`;
|
|
4431
|
+
const res = await fetch(url, {
|
|
4432
|
+
method: "POST",
|
|
4433
|
+
headers,
|
|
4434
|
+
body: JSON.stringify(params)
|
|
4435
|
+
});
|
|
4436
|
+
if (!res.ok) {
|
|
4437
|
+
const error = await res.text();
|
|
4438
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4439
|
+
throw new import_core.SolvaPayError(`Bootstrap MCP product failed (${res.status}): ${error}`);
|
|
4440
|
+
}
|
|
4441
|
+
return await res.json();
|
|
4442
|
+
},
|
|
4419
4443
|
// DELETE: /v1/sdk/products/{productRef}
|
|
4420
4444
|
async deleteProduct(productRef) {
|
|
4421
4445
|
const url = `${base}/v1/sdk/products/${productRef}`;
|
|
@@ -4429,6 +4453,21 @@ function createSolvaPayClient(opts) {
|
|
|
4429
4453
|
throw new import_core.SolvaPayError(`Delete product failed (${res.status}): ${error}`);
|
|
4430
4454
|
}
|
|
4431
4455
|
},
|
|
4456
|
+
// POST: /v1/sdk/products/{productRef}/clone
|
|
4457
|
+
async cloneProduct(productRef, overrides) {
|
|
4458
|
+
const url = `${base}/v1/sdk/products/${productRef}/clone`;
|
|
4459
|
+
const res = await fetch(url, {
|
|
4460
|
+
method: "POST",
|
|
4461
|
+
headers,
|
|
4462
|
+
body: JSON.stringify(overrides || {})
|
|
4463
|
+
});
|
|
4464
|
+
if (!res.ok) {
|
|
4465
|
+
const error = await res.text();
|
|
4466
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4467
|
+
throw new import_core.SolvaPayError(`Clone product failed (${res.status}): ${error}`);
|
|
4468
|
+
}
|
|
4469
|
+
return await res.json();
|
|
4470
|
+
},
|
|
4432
4471
|
// GET: /v1/sdk/products/{productRef}/plans
|
|
4433
4472
|
async listPlans(productRef) {
|
|
4434
4473
|
const url = `${base}/v1/sdk/products/${productRef}/plans`;
|
|
@@ -4471,6 +4510,21 @@ function createSolvaPayClient(opts) {
|
|
|
4471
4510
|
const result = await res.json();
|
|
4472
4511
|
return result;
|
|
4473
4512
|
},
|
|
4513
|
+
// PUT: /v1/sdk/products/{productRef}/plans/{planRef}
|
|
4514
|
+
async updatePlan(productRef, planRef, params) {
|
|
4515
|
+
const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
|
|
4516
|
+
const res = await fetch(url, {
|
|
4517
|
+
method: "PUT",
|
|
4518
|
+
headers,
|
|
4519
|
+
body: JSON.stringify(params)
|
|
4520
|
+
});
|
|
4521
|
+
if (!res.ok) {
|
|
4522
|
+
const error = await res.text();
|
|
4523
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4524
|
+
throw new import_core.SolvaPayError(`Update plan failed (${res.status}): ${error}`);
|
|
4525
|
+
}
|
|
4526
|
+
return await res.json();
|
|
4527
|
+
},
|
|
4474
4528
|
// DELETE: /v1/sdk/products/{productRef}/plans/{planRef}
|
|
4475
4529
|
async deletePlan(productRef, planRef) {
|
|
4476
4530
|
const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
|
|
@@ -4583,6 +4637,21 @@ function createSolvaPayClient(opts) {
|
|
|
4583
4637
|
}
|
|
4584
4638
|
return result;
|
|
4585
4639
|
},
|
|
4640
|
+
// POST: /v1/sdk/user-info
|
|
4641
|
+
async getUserInfo(params) {
|
|
4642
|
+
const url = `${base}/v1/sdk/user-info`;
|
|
4643
|
+
const res = await fetch(url, {
|
|
4644
|
+
method: "POST",
|
|
4645
|
+
headers,
|
|
4646
|
+
body: JSON.stringify(params)
|
|
4647
|
+
});
|
|
4648
|
+
if (!res.ok) {
|
|
4649
|
+
const error = await res.text();
|
|
4650
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4651
|
+
throw new import_core.SolvaPayError(`Get user info failed (${res.status}): ${error}`);
|
|
4652
|
+
}
|
|
4653
|
+
return await res.json();
|
|
4654
|
+
},
|
|
4586
4655
|
// POST: /v1/sdk/checkout-sessions
|
|
4587
4656
|
async createCheckoutSession(params) {
|
|
4588
4657
|
const url = `${base}/v1/sdk/checkout-sessions`;
|
|
@@ -4781,11 +4850,13 @@ var SolvaPayPaywall = class {
|
|
|
4781
4850
|
constructor(apiClient, options = {}) {
|
|
4782
4851
|
this.apiClient = apiClient;
|
|
4783
4852
|
this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG === "true";
|
|
4853
|
+
this.limitsCacheTTL = options.limitsCacheTTL ?? 1e4;
|
|
4784
4854
|
}
|
|
4785
4855
|
customerCreationAttempts = /* @__PURE__ */ new Set();
|
|
4786
4856
|
customerRefMapping = /* @__PURE__ */ new Map();
|
|
4787
|
-
// input ref -> backend ref
|
|
4788
4857
|
debug;
|
|
4858
|
+
limitsCache = /* @__PURE__ */ new Map();
|
|
4859
|
+
limitsCacheTTL;
|
|
4789
4860
|
log(...args) {
|
|
4790
4861
|
if (this.debug) {
|
|
4791
4862
|
console.log(...args);
|
|
@@ -4805,7 +4876,9 @@ var SolvaPayPaywall = class {
|
|
|
4805
4876
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4806
4877
|
async protect(handler, metadata = {}, getCustomerRef) {
|
|
4807
4878
|
const product = this.resolveProduct(metadata);
|
|
4808
|
-
const
|
|
4879
|
+
const configuredPlanRef = metadata.plan?.trim();
|
|
4880
|
+
const usagePlanRef = configuredPlanRef || "unspecified";
|
|
4881
|
+
const usageType = metadata.usageType || "requests";
|
|
4809
4882
|
return async (args) => {
|
|
4810
4883
|
const startTime = Date.now();
|
|
4811
4884
|
const requestId = this.generateRequestId();
|
|
@@ -4816,19 +4889,64 @@ var SolvaPayPaywall = class {
|
|
|
4816
4889
|
} else {
|
|
4817
4890
|
backendCustomerRef = await this.ensureCustomer(inputCustomerRef, inputCustomerRef);
|
|
4818
4891
|
}
|
|
4892
|
+
let resolvedMeterName;
|
|
4819
4893
|
try {
|
|
4820
|
-
const
|
|
4821
|
-
const
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4894
|
+
const limitsCacheKey = `${backendCustomerRef}:${product}:${configuredPlanRef || ""}:${usageType}`;
|
|
4895
|
+
const cachedLimits = this.limitsCache.get(limitsCacheKey);
|
|
4896
|
+
const now = Date.now();
|
|
4897
|
+
let withinLimits;
|
|
4898
|
+
let remaining;
|
|
4899
|
+
let checkoutUrl;
|
|
4900
|
+
const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
|
|
4901
|
+
if (hasFreshCachedLimits) {
|
|
4902
|
+
checkoutUrl = cachedLimits.checkoutUrl;
|
|
4903
|
+
resolvedMeterName = cachedLimits.meterName;
|
|
4904
|
+
if (cachedLimits.remaining > 0) {
|
|
4905
|
+
cachedLimits.remaining--;
|
|
4906
|
+
if (cachedLimits.remaining <= 0) {
|
|
4907
|
+
this.limitsCache.delete(limitsCacheKey);
|
|
4908
|
+
}
|
|
4909
|
+
withinLimits = true;
|
|
4910
|
+
remaining = cachedLimits.remaining;
|
|
4911
|
+
} else {
|
|
4912
|
+
withinLimits = false;
|
|
4913
|
+
remaining = 0;
|
|
4914
|
+
this.limitsCache.delete(limitsCacheKey);
|
|
4915
|
+
}
|
|
4916
|
+
} else {
|
|
4917
|
+
if (cachedLimits) {
|
|
4918
|
+
this.limitsCache.delete(limitsCacheKey);
|
|
4919
|
+
}
|
|
4920
|
+
const limitsCheck = await this.apiClient.checkLimits({
|
|
4921
|
+
customerRef: backendCustomerRef,
|
|
4922
|
+
productRef: product,
|
|
4923
|
+
...configuredPlanRef ? { planRef: configuredPlanRef } : {},
|
|
4924
|
+
meterName: usageType
|
|
4925
|
+
});
|
|
4926
|
+
withinLimits = limitsCheck.withinLimits;
|
|
4927
|
+
remaining = limitsCheck.remaining;
|
|
4928
|
+
checkoutUrl = limitsCheck.checkoutUrl;
|
|
4929
|
+
resolvedMeterName = limitsCheck.meterName;
|
|
4930
|
+
const consumedAllowance = withinLimits && remaining > 0;
|
|
4931
|
+
if (consumedAllowance) {
|
|
4932
|
+
remaining = Math.max(0, remaining - 1);
|
|
4933
|
+
}
|
|
4934
|
+
if (consumedAllowance) {
|
|
4935
|
+
this.limitsCache.set(limitsCacheKey, {
|
|
4936
|
+
remaining,
|
|
4937
|
+
checkoutUrl,
|
|
4938
|
+
meterName: resolvedMeterName,
|
|
4939
|
+
timestamp: now
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
if (!withinLimits) {
|
|
4826
4944
|
const latencyMs2 = Date.now() - startTime;
|
|
4827
|
-
|
|
4945
|
+
this.trackUsage(
|
|
4828
4946
|
backendCustomerRef,
|
|
4829
4947
|
product,
|
|
4830
|
-
|
|
4831
|
-
|
|
4948
|
+
usagePlanRef,
|
|
4949
|
+
resolvedMeterName || usageType,
|
|
4832
4950
|
"paywall",
|
|
4833
4951
|
requestId,
|
|
4834
4952
|
latencyMs2
|
|
@@ -4836,17 +4954,17 @@ var SolvaPayPaywall = class {
|
|
|
4836
4954
|
throw new PaywallError("Payment required", {
|
|
4837
4955
|
kind: "payment_required",
|
|
4838
4956
|
product,
|
|
4839
|
-
checkoutUrl:
|
|
4840
|
-
message: `
|
|
4957
|
+
checkoutUrl: checkoutUrl || "",
|
|
4958
|
+
message: `Purchase required. Remaining: ${remaining}`
|
|
4841
4959
|
});
|
|
4842
4960
|
}
|
|
4843
4961
|
const result = await handler(args);
|
|
4844
4962
|
const latencyMs = Date.now() - startTime;
|
|
4845
|
-
|
|
4963
|
+
this.trackUsage(
|
|
4846
4964
|
backendCustomerRef,
|
|
4847
4965
|
product,
|
|
4848
|
-
|
|
4849
|
-
|
|
4966
|
+
usagePlanRef,
|
|
4967
|
+
resolvedMeterName || usageType,
|
|
4850
4968
|
"success",
|
|
4851
4969
|
requestId,
|
|
4852
4970
|
latencyMs
|
|
@@ -4859,18 +4977,18 @@ var SolvaPayPaywall = class {
|
|
|
4859
4977
|
} else {
|
|
4860
4978
|
this.log(`\u274C Error in paywall:`, error);
|
|
4861
4979
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4980
|
+
if (!(error instanceof PaywallError)) {
|
|
4981
|
+
const latencyMs = Date.now() - startTime;
|
|
4982
|
+
this.trackUsage(
|
|
4983
|
+
backendCustomerRef,
|
|
4984
|
+
product,
|
|
4985
|
+
usagePlanRef,
|
|
4986
|
+
resolvedMeterName || usageType,
|
|
4987
|
+
"fail",
|
|
4988
|
+
requestId,
|
|
4989
|
+
latencyMs
|
|
4990
|
+
);
|
|
4991
|
+
}
|
|
4874
4992
|
throw error;
|
|
4875
4993
|
}
|
|
4876
4994
|
};
|
|
@@ -5019,24 +5137,23 @@ var SolvaPayPaywall = class {
|
|
|
5019
5137
|
}
|
|
5020
5138
|
return backendRef;
|
|
5021
5139
|
}
|
|
5022
|
-
async trackUsage(customerRef,
|
|
5140
|
+
async trackUsage(customerRef, _productRef, _planRef, action, outcome, requestId, actionDuration) {
|
|
5023
5141
|
await withRetry(
|
|
5024
5142
|
() => this.apiClient.trackUsage({
|
|
5025
5143
|
customerRef,
|
|
5026
|
-
|
|
5027
|
-
|
|
5144
|
+
actionType: "api_call",
|
|
5145
|
+
units: 1,
|
|
5028
5146
|
outcome,
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5147
|
+
productReference: _productRef,
|
|
5148
|
+
duration: actionDuration,
|
|
5149
|
+
metadata: { action: action || "api_requests", requestId },
|
|
5032
5150
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
5033
5151
|
}),
|
|
5034
5152
|
{
|
|
5035
5153
|
maxRetries: 2,
|
|
5036
5154
|
initialDelay: 500,
|
|
5037
5155
|
shouldRetry: (error) => error.message.includes("Customer not found"),
|
|
5038
|
-
|
|
5039
|
-
onRetry: (error, attempt) => {
|
|
5156
|
+
onRetry: (_error, attempt) => {
|
|
5040
5157
|
console.warn(`\u26A0\uFE0F Customer not found (attempt ${attempt + 1}/3), retrying in 500ms...`);
|
|
5041
5158
|
}
|
|
5042
5159
|
}
|
|
@@ -5077,13 +5194,19 @@ var AdapterUtils = class {
|
|
|
5077
5194
|
}
|
|
5078
5195
|
};
|
|
5079
5196
|
async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
|
|
5197
|
+
const backendRefCache = /* @__PURE__ */ new Map();
|
|
5198
|
+
const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
|
|
5199
|
+
const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
|
|
5080
5200
|
return async (context) => {
|
|
5081
5201
|
try {
|
|
5082
5202
|
const args = await adapter.extractArgs(context);
|
|
5083
5203
|
const customerRef = await adapter.getCustomerRef(context);
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5204
|
+
let backendRef = backendRefCache.get(customerRef);
|
|
5205
|
+
if (!backendRef) {
|
|
5206
|
+
backendRef = await paywall.ensureCustomer(customerRef, customerRef);
|
|
5207
|
+
backendRefCache.set(customerRef, backendRef);
|
|
5208
|
+
}
|
|
5209
|
+
args.auth = { customer_ref: backendRef };
|
|
5087
5210
|
const result = await protectedHandler(args);
|
|
5088
5211
|
return adapter.formatResponse(result, context);
|
|
5089
5212
|
} catch (error) {
|
|
@@ -5332,6 +5455,141 @@ var McpAdapter = class {
|
|
|
5332
5455
|
|
|
5333
5456
|
// src/factory.ts
|
|
5334
5457
|
var import_core2 = require("@solvapay/core");
|
|
5458
|
+
|
|
5459
|
+
// src/virtual-tools.ts
|
|
5460
|
+
var TOOL_GET_USER_INFO = {
|
|
5461
|
+
name: "get_user_info",
|
|
5462
|
+
description: "Get information about the current user and their purchase status for this MCP server. Returns user profile (reference, name, email) and active purchase details including product name, type, dates, and usage limit if applicable.",
|
|
5463
|
+
inputSchema: {
|
|
5464
|
+
type: "object",
|
|
5465
|
+
properties: {},
|
|
5466
|
+
required: []
|
|
5467
|
+
}
|
|
5468
|
+
};
|
|
5469
|
+
var TOOL_UPGRADE = {
|
|
5470
|
+
name: "upgrade",
|
|
5471
|
+
description: "Get available pricing options and checkout URLs for upgrading. Returns a list of available pricing options with their details (price, features) and checkout URLs. Users can click on a checkout URL to purchase. If a specific planRef is provided, returns only the checkout URL for that pricing option.",
|
|
5472
|
+
inputSchema: {
|
|
5473
|
+
type: "object",
|
|
5474
|
+
properties: {
|
|
5475
|
+
planRef: {
|
|
5476
|
+
type: "string",
|
|
5477
|
+
description: 'Optional pricing reference (e.g., "pln_abc123") to get a checkout URL for a specific option. If not provided, returns all available pricing options with their checkout URLs.'
|
|
5478
|
+
}
|
|
5479
|
+
},
|
|
5480
|
+
required: []
|
|
5481
|
+
}
|
|
5482
|
+
};
|
|
5483
|
+
var TOOL_MANAGE_ACCOUNT = {
|
|
5484
|
+
name: "manage_account",
|
|
5485
|
+
description: "Get a URL to the customer portal where users can view and manage their account. The portal shows current account status, billing history, and allows subscription changes. Returns a secure, time-limited URL that the user can click to access their account management page.",
|
|
5486
|
+
inputSchema: {
|
|
5487
|
+
type: "object",
|
|
5488
|
+
properties: {},
|
|
5489
|
+
required: []
|
|
5490
|
+
}
|
|
5491
|
+
};
|
|
5492
|
+
var VIRTUAL_TOOL_DEFINITIONS = [TOOL_GET_USER_INFO, TOOL_UPGRADE, TOOL_MANAGE_ACCOUNT];
|
|
5493
|
+
function mcpTextResult(text) {
|
|
5494
|
+
return { content: [{ type: "text", text }] };
|
|
5495
|
+
}
|
|
5496
|
+
function mcpErrorResult(message2) {
|
|
5497
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message2 }) }], isError: true };
|
|
5498
|
+
}
|
|
5499
|
+
function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
|
|
5500
|
+
return async (args) => {
|
|
5501
|
+
const customerRef = getCustomerRef(args);
|
|
5502
|
+
try {
|
|
5503
|
+
if (!apiClient.getUserInfo) {
|
|
5504
|
+
return mcpErrorResult("getUserInfo is not available on this API client");
|
|
5505
|
+
}
|
|
5506
|
+
const userInfo = await apiClient.getUserInfo({ customerRef, productRef });
|
|
5507
|
+
return mcpTextResult(JSON.stringify(userInfo, null, 2));
|
|
5508
|
+
} catch (error) {
|
|
5509
|
+
return mcpErrorResult(
|
|
5510
|
+
`Failed to retrieve user information: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
5511
|
+
);
|
|
5512
|
+
}
|
|
5513
|
+
};
|
|
5514
|
+
}
|
|
5515
|
+
function createUpgradeHandler(apiClient, productRef, getCustomerRef) {
|
|
5516
|
+
return async (args) => {
|
|
5517
|
+
const customerRef = getCustomerRef(args);
|
|
5518
|
+
const planRef = typeof args.planRef === "string" ? args.planRef : void 0;
|
|
5519
|
+
try {
|
|
5520
|
+
const result = await apiClient.createCheckoutSession({
|
|
5521
|
+
customerReference: customerRef,
|
|
5522
|
+
productRef,
|
|
5523
|
+
...planRef && { planRef }
|
|
5524
|
+
});
|
|
5525
|
+
const checkoutUrl = result.checkoutUrl;
|
|
5526
|
+
if (planRef) {
|
|
5527
|
+
const responseText2 = `## Upgrade
|
|
5528
|
+
|
|
5529
|
+
**[Click here to upgrade \u2192](${checkoutUrl})**
|
|
5530
|
+
|
|
5531
|
+
After completing the checkout, your purchase will be activated immediately.`;
|
|
5532
|
+
return mcpTextResult(responseText2);
|
|
5533
|
+
}
|
|
5534
|
+
const responseText = `## Upgrade Your Subscription
|
|
5535
|
+
|
|
5536
|
+
**[Click here to view pricing options and upgrade \u2192](${checkoutUrl})**
|
|
5537
|
+
|
|
5538
|
+
You'll be able to compare options and select the one that's right for you.`;
|
|
5539
|
+
return mcpTextResult(responseText);
|
|
5540
|
+
} catch (error) {
|
|
5541
|
+
return mcpErrorResult(
|
|
5542
|
+
`Failed to create checkout session: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
5543
|
+
);
|
|
5544
|
+
}
|
|
5545
|
+
};
|
|
5546
|
+
}
|
|
5547
|
+
function createManageAccountHandler(apiClient, productRef, getCustomerRef) {
|
|
5548
|
+
return async (args) => {
|
|
5549
|
+
const customerRef = getCustomerRef(args);
|
|
5550
|
+
try {
|
|
5551
|
+
const session = await apiClient.createCustomerSession({ customerRef, productRef });
|
|
5552
|
+
const portalUrl = session.customerUrl;
|
|
5553
|
+
const responseText = `## Manage Your Account
|
|
5554
|
+
|
|
5555
|
+
Access your account management portal to:
|
|
5556
|
+
- View your current account status
|
|
5557
|
+
- See billing history and invoices
|
|
5558
|
+
- Update payment methods
|
|
5559
|
+
- Cancel or modify your subscription
|
|
5560
|
+
|
|
5561
|
+
**[Open Account Portal \u2192](${portalUrl})**
|
|
5562
|
+
|
|
5563
|
+
This link is secure and will expire after a short period.`;
|
|
5564
|
+
return mcpTextResult(responseText);
|
|
5565
|
+
} catch (error) {
|
|
5566
|
+
return mcpErrorResult(
|
|
5567
|
+
`Failed to create customer portal session: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
};
|
|
5571
|
+
}
|
|
5572
|
+
function createVirtualTools(apiClient, options) {
|
|
5573
|
+
const { product, getCustomerRef, exclude = [] } = options;
|
|
5574
|
+
const excludeSet = new Set(exclude);
|
|
5575
|
+
const allTools = [
|
|
5576
|
+
{
|
|
5577
|
+
...TOOL_GET_USER_INFO,
|
|
5578
|
+
handler: createGetUserInfoHandler(apiClient, product, getCustomerRef)
|
|
5579
|
+
},
|
|
5580
|
+
{
|
|
5581
|
+
...TOOL_UPGRADE,
|
|
5582
|
+
handler: createUpgradeHandler(apiClient, product, getCustomerRef)
|
|
5583
|
+
},
|
|
5584
|
+
{
|
|
5585
|
+
...TOOL_MANAGE_ACCOUNT,
|
|
5586
|
+
handler: createManageAccountHandler(apiClient, product, getCustomerRef)
|
|
5587
|
+
}
|
|
5588
|
+
];
|
|
5589
|
+
return allTools.filter((t) => !excludeSet.has(t.name));
|
|
5590
|
+
}
|
|
5591
|
+
|
|
5592
|
+
// src/factory.ts
|
|
5335
5593
|
function createSolvaPay(config) {
|
|
5336
5594
|
let resolvedConfig;
|
|
5337
5595
|
if (!config) {
|
|
@@ -5348,7 +5606,8 @@ function createSolvaPay(config) {
|
|
|
5348
5606
|
apiBaseUrl: resolvedConfig.apiBaseUrl
|
|
5349
5607
|
});
|
|
5350
5608
|
const paywall = new SolvaPayPaywall(apiClient, {
|
|
5351
|
-
debug: process.env.SOLVAPAY_DEBUG !== "false"
|
|
5609
|
+
debug: process.env.SOLVAPAY_DEBUG !== "false",
|
|
5610
|
+
limitsCacheTTL: resolvedConfig.limitsCacheTTL
|
|
5352
5611
|
});
|
|
5353
5612
|
return {
|
|
5354
5613
|
// Direct access to API client for advanced operations
|
|
@@ -5394,39 +5653,67 @@ function createSolvaPay(config) {
|
|
|
5394
5653
|
createCustomerSession(params) {
|
|
5395
5654
|
return apiClient.createCustomerSession(params);
|
|
5396
5655
|
},
|
|
5656
|
+
bootstrapMcpProduct(params) {
|
|
5657
|
+
if (!apiClient.bootstrapMcpProduct) {
|
|
5658
|
+
throw new import_core2.SolvaPayError("bootstrapMcpProduct is not available on this API client");
|
|
5659
|
+
}
|
|
5660
|
+
return apiClient.bootstrapMcpProduct(params);
|
|
5661
|
+
},
|
|
5662
|
+
getVirtualTools(options) {
|
|
5663
|
+
return createVirtualTools(apiClient, options);
|
|
5664
|
+
},
|
|
5397
5665
|
// Payable API for framework-specific handlers
|
|
5398
5666
|
payable(options = {}) {
|
|
5399
5667
|
const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
5400
|
-
const plan = options.planRef || options.plan
|
|
5401
|
-
const
|
|
5668
|
+
const plan = options.planRef || options.plan;
|
|
5669
|
+
const usageType = options.usageType || "requests";
|
|
5670
|
+
const metadata = { product, plan, usageType };
|
|
5402
5671
|
return {
|
|
5403
5672
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5404
5673
|
http(businessLogic, adapterOptions) {
|
|
5405
|
-
const adapter = new HttpAdapter(
|
|
5674
|
+
const adapter = new HttpAdapter({
|
|
5675
|
+
...adapterOptions,
|
|
5676
|
+
getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
|
|
5677
|
+
});
|
|
5678
|
+
const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
5406
5679
|
return async (req, reply) => {
|
|
5407
|
-
const handler = await
|
|
5680
|
+
const handler = await handlerPromise;
|
|
5408
5681
|
return handler([req, reply]);
|
|
5409
5682
|
};
|
|
5410
5683
|
},
|
|
5411
5684
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5412
5685
|
next(businessLogic, adapterOptions) {
|
|
5413
|
-
const adapter = new NextAdapter(
|
|
5686
|
+
const adapter = new NextAdapter({
|
|
5687
|
+
...adapterOptions,
|
|
5688
|
+
getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
|
|
5689
|
+
});
|
|
5690
|
+
const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
5414
5691
|
return async (request, context) => {
|
|
5415
|
-
const handler = await
|
|
5692
|
+
const handler = await handlerPromise;
|
|
5416
5693
|
return handler([request, context]);
|
|
5417
5694
|
};
|
|
5418
5695
|
},
|
|
5419
5696
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5420
5697
|
mcp(businessLogic, adapterOptions) {
|
|
5421
|
-
const adapter = new McpAdapter(
|
|
5698
|
+
const adapter = new McpAdapter({
|
|
5699
|
+
...adapterOptions,
|
|
5700
|
+
getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
|
|
5701
|
+
});
|
|
5702
|
+
const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
5422
5703
|
return async (args) => {
|
|
5423
|
-
const handler = await
|
|
5704
|
+
const handler = await handlerPromise;
|
|
5424
5705
|
return handler(args);
|
|
5425
5706
|
};
|
|
5426
5707
|
},
|
|
5427
5708
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5428
5709
|
async function(businessLogic) {
|
|
5429
|
-
const getCustomerRef = (args) =>
|
|
5710
|
+
const getCustomerRef = (args) => {
|
|
5711
|
+
const configuredRef = options.getCustomerRef?.(args);
|
|
5712
|
+
if (typeof configuredRef === "string") {
|
|
5713
|
+
return configuredRef;
|
|
5714
|
+
}
|
|
5715
|
+
return args.auth?.customer_ref || "anonymous";
|
|
5716
|
+
};
|
|
5430
5717
|
return paywall.protect(businessLogic, metadata, getCustomerRef);
|
|
5431
5718
|
}
|
|
5432
5719
|
};
|
|
@@ -5434,6 +5721,57 @@ function createSolvaPay(config) {
|
|
|
5434
5721
|
};
|
|
5435
5722
|
}
|
|
5436
5723
|
|
|
5724
|
+
// src/mcp-auth.ts
|
|
5725
|
+
var McpBearerAuthError = class extends Error {
|
|
5726
|
+
constructor(message2) {
|
|
5727
|
+
super(message2);
|
|
5728
|
+
this.name = "McpBearerAuthError";
|
|
5729
|
+
}
|
|
5730
|
+
};
|
|
5731
|
+
function base64UrlDecode(input) {
|
|
5732
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
5733
|
+
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
5734
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
5735
|
+
}
|
|
5736
|
+
function extractBearerToken(authorization) {
|
|
5737
|
+
if (!authorization) return null;
|
|
5738
|
+
if (!authorization.startsWith("Bearer ")) return null;
|
|
5739
|
+
return authorization.slice(7).trim() || null;
|
|
5740
|
+
}
|
|
5741
|
+
function decodeJwtPayload(token) {
|
|
5742
|
+
const parts = token.split(".");
|
|
5743
|
+
if (parts.length < 2) {
|
|
5744
|
+
throw new McpBearerAuthError("Invalid JWT format");
|
|
5745
|
+
}
|
|
5746
|
+
try {
|
|
5747
|
+
const payloadText = base64UrlDecode(parts[1]);
|
|
5748
|
+
const payload = JSON.parse(payloadText);
|
|
5749
|
+
return payload;
|
|
5750
|
+
} catch {
|
|
5751
|
+
throw new McpBearerAuthError("Invalid JWT payload");
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
function getCustomerRefFromJwtPayload(payload, options = {}) {
|
|
5755
|
+
const claimPriority = options.claimPriority || ["customerRef", "customer_ref", "sub"];
|
|
5756
|
+
for (const claim of claimPriority) {
|
|
5757
|
+
const value = payload[claim];
|
|
5758
|
+
if (typeof value === "string" && value.trim()) {
|
|
5759
|
+
return value.trim();
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
throw new McpBearerAuthError(
|
|
5763
|
+
`No customer reference claim found (checked: ${claimPriority.join(", ")})`
|
|
5764
|
+
);
|
|
5765
|
+
}
|
|
5766
|
+
function getCustomerRefFromBearerAuthHeader(authorization, options = {}) {
|
|
5767
|
+
const token = extractBearerToken(authorization);
|
|
5768
|
+
if (!token) {
|
|
5769
|
+
throw new McpBearerAuthError("Missing bearer token");
|
|
5770
|
+
}
|
|
5771
|
+
const payload = decodeJwtPayload(token);
|
|
5772
|
+
return getCustomerRefFromJwtPayload(payload, options);
|
|
5773
|
+
}
|
|
5774
|
+
|
|
5437
5775
|
// src/helpers/error.ts
|
|
5438
5776
|
var import_core3 = require("@solvapay/core");
|
|
5439
5777
|
function isErrorResult(result) {
|
|
@@ -5759,14 +6097,21 @@ function verifyWebhook({
|
|
|
5759
6097
|
}
|
|
5760
6098
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5761
6099
|
0 && (module.exports = {
|
|
6100
|
+
McpBearerAuthError,
|
|
5762
6101
|
PaywallError,
|
|
6102
|
+
VIRTUAL_TOOL_DEFINITIONS,
|
|
5763
6103
|
cancelPurchaseCore,
|
|
5764
6104
|
createCheckoutSessionCore,
|
|
5765
6105
|
createCustomerSessionCore,
|
|
5766
6106
|
createPaymentIntentCore,
|
|
5767
6107
|
createSolvaPay,
|
|
5768
6108
|
createSolvaPayClient,
|
|
6109
|
+
createVirtualTools,
|
|
6110
|
+
decodeJwtPayload,
|
|
6111
|
+
extractBearerToken,
|
|
5769
6112
|
getAuthenticatedUserCore,
|
|
6113
|
+
getCustomerRefFromBearerAuthHeader,
|
|
6114
|
+
getCustomerRefFromJwtPayload,
|
|
5770
6115
|
handleRouteError,
|
|
5771
6116
|
isErrorResult,
|
|
5772
6117
|
listPlansCore,
|