@solvapay/server 1.0.1-preview.2 → 1.0.1-preview.4
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 +1 -1
- package/dist/edge.d.ts +1722 -1194
- package/dist/edge.js +182 -32
- package/dist/index.cjs +356 -32
- package/dist/index.d.cts +1780 -1187
- package/dist/index.d.ts +1780 -1187
- package/dist/index.js +349 -32
- package/package.json +14 -4
package/dist/index.cjs
CHANGED
|
@@ -4314,9 +4314,11 @@ __export(index_exports, {
|
|
|
4314
4314
|
McpBearerAuthError: () => McpBearerAuthError,
|
|
4315
4315
|
PaywallError: () => PaywallError,
|
|
4316
4316
|
VIRTUAL_TOOL_DEFINITIONS: () => VIRTUAL_TOOL_DEFINITIONS,
|
|
4317
|
+
buildAuthInfoFromBearer: () => buildAuthInfoFromBearer,
|
|
4317
4318
|
cancelPurchaseCore: () => cancelPurchaseCore,
|
|
4318
4319
|
createCheckoutSessionCore: () => createCheckoutSessionCore,
|
|
4319
4320
|
createCustomerSessionCore: () => createCustomerSessionCore,
|
|
4321
|
+
createMcpOAuthBridge: () => createMcpOAuthBridge,
|
|
4320
4322
|
createPaymentIntentCore: () => createPaymentIntentCore,
|
|
4321
4323
|
createSolvaPay: () => createSolvaPay,
|
|
4322
4324
|
createSolvaPayClient: () => createSolvaPayClient,
|
|
@@ -4326,10 +4328,15 @@ __export(index_exports, {
|
|
|
4326
4328
|
getAuthenticatedUserCore: () => getAuthenticatedUserCore,
|
|
4327
4329
|
getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
|
|
4328
4330
|
getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
|
|
4331
|
+
getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
|
|
4332
|
+
getOAuthProtectedResourceResponse: () => getOAuthProtectedResourceResponse,
|
|
4329
4333
|
handleRouteError: () => handleRouteError,
|
|
4330
4334
|
isErrorResult: () => isErrorResult,
|
|
4335
|
+
jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
|
|
4331
4336
|
listPlansCore: () => listPlansCore,
|
|
4337
|
+
paywallErrorToClientPayload: () => paywallErrorToClientPayload,
|
|
4332
4338
|
processPaymentIntentCore: () => processPaymentIntentCore,
|
|
4339
|
+
registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
|
|
4333
4340
|
syncCustomerCore: () => syncCustomerCore,
|
|
4334
4341
|
verifyWebhook: () => verifyWebhook,
|
|
4335
4342
|
withRetry: () => withRetry
|
|
@@ -4497,6 +4504,21 @@ function createSolvaPayClient(opts) {
|
|
|
4497
4504
|
}
|
|
4498
4505
|
return await res.json();
|
|
4499
4506
|
},
|
|
4507
|
+
// PUT: /v1/sdk/products/{productRef}/mcp/plans
|
|
4508
|
+
async configureMcpPlans(productRef, params) {
|
|
4509
|
+
const url = `${base}/v1/sdk/products/${productRef}/mcp/plans`;
|
|
4510
|
+
const res = await fetch(url, {
|
|
4511
|
+
method: "PUT",
|
|
4512
|
+
headers,
|
|
4513
|
+
body: JSON.stringify(params)
|
|
4514
|
+
});
|
|
4515
|
+
if (!res.ok) {
|
|
4516
|
+
const error = await res.text();
|
|
4517
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4518
|
+
throw new import_core.SolvaPayError(`Configure MCP plans failed (${res.status}): ${error}`);
|
|
4519
|
+
}
|
|
4520
|
+
return await res.json();
|
|
4521
|
+
},
|
|
4500
4522
|
// DELETE: /v1/sdk/products/{productRef}
|
|
4501
4523
|
async deleteProduct(productRef) {
|
|
4502
4524
|
const url = `${base}/v1/sdk/products/${productRef}`;
|
|
@@ -4895,6 +4917,24 @@ var PaywallError = class extends Error {
|
|
|
4895
4917
|
this.name = "PaywallError";
|
|
4896
4918
|
}
|
|
4897
4919
|
};
|
|
4920
|
+
function paywallErrorToClientPayload(error) {
|
|
4921
|
+
const sc = error.structuredContent;
|
|
4922
|
+
const base = {
|
|
4923
|
+
success: false,
|
|
4924
|
+
error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
|
|
4925
|
+
product: sc.product,
|
|
4926
|
+
checkoutUrl: sc.checkoutUrl,
|
|
4927
|
+
message: sc.message
|
|
4928
|
+
};
|
|
4929
|
+
if (sc.kind === "activation_required") {
|
|
4930
|
+
base.kind = "activation_required";
|
|
4931
|
+
if (sc.plans !== void 0) base.plans = sc.plans;
|
|
4932
|
+
if (sc.balance !== void 0) base.balance = sc.balance;
|
|
4933
|
+
if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
|
|
4934
|
+
if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
|
|
4935
|
+
}
|
|
4936
|
+
return base;
|
|
4937
|
+
}
|
|
4898
4938
|
var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
|
|
4899
4939
|
cacheTTL: 6e4,
|
|
4900
4940
|
// Cache results for 60 seconds (reduces API calls significantly)
|
|
@@ -4954,6 +4994,7 @@ var SolvaPayPaywall = class {
|
|
|
4954
4994
|
let withinLimits;
|
|
4955
4995
|
let remaining;
|
|
4956
4996
|
let checkoutUrl;
|
|
4997
|
+
let lastLimitsCheck;
|
|
4957
4998
|
const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
|
|
4958
4999
|
if (hasFreshCachedLimits) {
|
|
4959
5000
|
checkoutUrl = cachedLimits.checkoutUrl;
|
|
@@ -4980,6 +5021,7 @@ var SolvaPayPaywall = class {
|
|
|
4980
5021
|
...configuredPlanRef ? { planRef: configuredPlanRef } : {},
|
|
4981
5022
|
meterName: usageType
|
|
4982
5023
|
});
|
|
5024
|
+
lastLimitsCheck = limitsCheck;
|
|
4983
5025
|
withinLimits = limitsCheck.withinLimits;
|
|
4984
5026
|
remaining = limitsCheck.remaining;
|
|
4985
5027
|
checkoutUrl = limitsCheck.checkoutUrl;
|
|
@@ -5008,6 +5050,20 @@ var SolvaPayPaywall = class {
|
|
|
5008
5050
|
requestId,
|
|
5009
5051
|
latencyMs2
|
|
5010
5052
|
);
|
|
5053
|
+
if (lastLimitsCheck?.activationRequired) {
|
|
5054
|
+
const confirmationUrl = lastLimitsCheck.confirmationUrl;
|
|
5055
|
+
const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
|
|
5056
|
+
throw new PaywallError("Activation required", {
|
|
5057
|
+
kind: "activation_required",
|
|
5058
|
+
product,
|
|
5059
|
+
message: "Product activation is required before this tool can be used.",
|
|
5060
|
+
checkoutUrl: payCheckoutUrl,
|
|
5061
|
+
confirmationUrl,
|
|
5062
|
+
plans: lastLimitsCheck.plans,
|
|
5063
|
+
balance: lastLimitsCheck.balance,
|
|
5064
|
+
productDetails: lastLimitsCheck.product
|
|
5065
|
+
});
|
|
5066
|
+
}
|
|
5011
5067
|
throw new PaywallError("Payment required", {
|
|
5012
5068
|
kind: "payment_required",
|
|
5013
5069
|
product,
|
|
@@ -5110,7 +5166,8 @@ var SolvaPayPaywall = class {
|
|
|
5110
5166
|
this.customerCreationAttempts.add(customerRef);
|
|
5111
5167
|
try {
|
|
5112
5168
|
const createParams = {
|
|
5113
|
-
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local
|
|
5169
|
+
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5170
|
+
metadata: {}
|
|
5114
5171
|
};
|
|
5115
5172
|
if (options?.name) {
|
|
5116
5173
|
createParams.name = options.name;
|
|
@@ -5134,7 +5191,10 @@ var SolvaPayPaywall = class {
|
|
|
5134
5191
|
return searchResult.customerRef;
|
|
5135
5192
|
}
|
|
5136
5193
|
} catch (lookupError) {
|
|
5137
|
-
this.log(
|
|
5194
|
+
this.log(
|
|
5195
|
+
`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
|
|
5196
|
+
lookupError instanceof Error ? lookupError.message : lookupError
|
|
5197
|
+
);
|
|
5138
5198
|
}
|
|
5139
5199
|
}
|
|
5140
5200
|
const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
|
|
@@ -5157,7 +5217,8 @@ var SolvaPayPaywall = class {
|
|
|
5157
5217
|
try {
|
|
5158
5218
|
const retryParams = {
|
|
5159
5219
|
email: `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5160
|
-
externalRef
|
|
5220
|
+
externalRef,
|
|
5221
|
+
metadata: {}
|
|
5161
5222
|
};
|
|
5162
5223
|
if (options?.name) {
|
|
5163
5224
|
retryParams.name = options.name;
|
|
@@ -5254,10 +5315,10 @@ async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
|
|
|
5254
5315
|
const backendRefCache = /* @__PURE__ */ new Map();
|
|
5255
5316
|
const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
|
|
5256
5317
|
const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
|
|
5257
|
-
return async (context) => {
|
|
5318
|
+
return async (context, extra) => {
|
|
5258
5319
|
try {
|
|
5259
5320
|
const args = await adapter.extractArgs(context);
|
|
5260
|
-
const customerRef = await adapter.getCustomerRef(context);
|
|
5321
|
+
const customerRef = await adapter.getCustomerRef(context, extra);
|
|
5261
5322
|
let backendRef = backendRefCache.get(customerRef);
|
|
5262
5323
|
if (!backendRef) {
|
|
5263
5324
|
backendRef = await paywall.ensureCustomer(customerRef, customerRef);
|
|
@@ -5448,18 +5509,18 @@ var McpAdapter = class {
|
|
|
5448
5509
|
extractArgs(args) {
|
|
5449
5510
|
return args;
|
|
5450
5511
|
}
|
|
5451
|
-
async getCustomerRef(args) {
|
|
5512
|
+
async getCustomerRef(args, extra) {
|
|
5452
5513
|
if (this.options.getCustomerRef) {
|
|
5453
|
-
const ref = await this.options.getCustomerRef(args);
|
|
5514
|
+
const ref = await this.options.getCustomerRef(args, extra);
|
|
5454
5515
|
return AdapterUtils.ensureCustomerRef(ref);
|
|
5455
5516
|
}
|
|
5456
|
-
const
|
|
5457
|
-
const customerRef =
|
|
5517
|
+
const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
|
|
5518
|
+
const customerRef = typeof customerRefFromExtra === "string" && customerRefFromExtra.trim() ? customerRefFromExtra.trim() : "anonymous";
|
|
5458
5519
|
return AdapterUtils.ensureCustomerRef(customerRef);
|
|
5459
5520
|
}
|
|
5460
5521
|
formatResponse(result, _context) {
|
|
5461
5522
|
const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
|
|
5462
|
-
|
|
5523
|
+
const response = {
|
|
5463
5524
|
content: [
|
|
5464
5525
|
{
|
|
5465
5526
|
type: "text",
|
|
@@ -5467,6 +5528,10 @@ var McpAdapter = class {
|
|
|
5467
5528
|
}
|
|
5468
5529
|
]
|
|
5469
5530
|
};
|
|
5531
|
+
if (transformed && typeof transformed === "object" && !Array.isArray(transformed)) {
|
|
5532
|
+
response.structuredContent = transformed;
|
|
5533
|
+
}
|
|
5534
|
+
return response;
|
|
5470
5535
|
}
|
|
5471
5536
|
formatError(error, _context) {
|
|
5472
5537
|
if (error instanceof PaywallError) {
|
|
@@ -5474,17 +5539,7 @@ var McpAdapter = class {
|
|
|
5474
5539
|
content: [
|
|
5475
5540
|
{
|
|
5476
5541
|
type: "text",
|
|
5477
|
-
text: JSON.stringify(
|
|
5478
|
-
{
|
|
5479
|
-
success: false,
|
|
5480
|
-
error: "Payment required",
|
|
5481
|
-
product: error.structuredContent.product,
|
|
5482
|
-
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
5483
|
-
message: error.structuredContent.message
|
|
5484
|
-
},
|
|
5485
|
-
null,
|
|
5486
|
-
2
|
|
5487
|
-
)
|
|
5542
|
+
text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
|
|
5488
5543
|
}
|
|
5489
5544
|
],
|
|
5490
5545
|
isError: true,
|
|
@@ -5554,8 +5609,8 @@ function mcpErrorResult(message2) {
|
|
|
5554
5609
|
return { content: [{ type: "text", text: JSON.stringify({ error: message2 }) }], isError: true };
|
|
5555
5610
|
}
|
|
5556
5611
|
function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
|
|
5557
|
-
return async (args) => {
|
|
5558
|
-
const customerRef = getCustomerRef(args);
|
|
5612
|
+
return async (args, extra) => {
|
|
5613
|
+
const customerRef = getCustomerRef(args, extra);
|
|
5559
5614
|
try {
|
|
5560
5615
|
if (!apiClient.getUserInfo) {
|
|
5561
5616
|
return mcpErrorResult("getUserInfo is not available on this API client");
|
|
@@ -5570,12 +5625,12 @@ function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
|
|
|
5570
5625
|
};
|
|
5571
5626
|
}
|
|
5572
5627
|
function createUpgradeHandler(apiClient, productRef, getCustomerRef) {
|
|
5573
|
-
return async (args) => {
|
|
5574
|
-
const customerRef = getCustomerRef(args);
|
|
5628
|
+
return async (args, extra) => {
|
|
5629
|
+
const customerRef = getCustomerRef(args, extra);
|
|
5575
5630
|
const planRef = typeof args.planRef === "string" ? args.planRef : void 0;
|
|
5576
5631
|
try {
|
|
5577
5632
|
const result = await apiClient.createCheckoutSession({
|
|
5578
|
-
|
|
5633
|
+
customerRef,
|
|
5579
5634
|
productRef,
|
|
5580
5635
|
...planRef && { planRef }
|
|
5581
5636
|
});
|
|
@@ -5602,8 +5657,8 @@ You'll be able to compare options and select the one that's right for you.`;
|
|
|
5602
5657
|
};
|
|
5603
5658
|
}
|
|
5604
5659
|
function createManageAccountHandler(apiClient, productRef, getCustomerRef) {
|
|
5605
|
-
return async (args) => {
|
|
5606
|
-
const customerRef = getCustomerRef(args);
|
|
5660
|
+
return async (args, extra) => {
|
|
5661
|
+
const customerRef = getCustomerRef(args, extra);
|
|
5607
5662
|
try {
|
|
5608
5663
|
const session = await apiClient.createCustomerSession({ customerRef, productRef });
|
|
5609
5664
|
const portalUrl = session.customerUrl;
|
|
@@ -5646,6 +5701,98 @@ function createVirtualTools(apiClient, options) {
|
|
|
5646
5701
|
return allTools.filter((t) => !excludeSet.has(t.name));
|
|
5647
5702
|
}
|
|
5648
5703
|
|
|
5704
|
+
// src/register-virtual-tools-mcp.ts
|
|
5705
|
+
var import_node_module = require("module");
|
|
5706
|
+
function getNodeRequire() {
|
|
5707
|
+
try {
|
|
5708
|
+
return (0, eval)("require");
|
|
5709
|
+
} catch {
|
|
5710
|
+
return (0, import_node_module.createRequire)(`${process.cwd()}/package.json`);
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
var nodeRequire = getNodeRequire();
|
|
5714
|
+
function getZod() {
|
|
5715
|
+
try {
|
|
5716
|
+
const zodModule = nodeRequire("zod");
|
|
5717
|
+
return zodModule.z ?? zodModule;
|
|
5718
|
+
} catch {
|
|
5719
|
+
throw new Error(
|
|
5720
|
+
"zod is required to use registerVirtualToolsMcp(). Install it as a dependency in your MCP server project."
|
|
5721
|
+
);
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
function isJsonSchemaObject(value) {
|
|
5725
|
+
return Boolean(value && typeof value === "object");
|
|
5726
|
+
}
|
|
5727
|
+
function toZodSchema(property) {
|
|
5728
|
+
const z = getZod();
|
|
5729
|
+
if (Array.isArray(property.enum) && property.enum.length > 0) {
|
|
5730
|
+
if (property.enum.every((value) => typeof value === "string")) {
|
|
5731
|
+
const [first, ...rest] = property.enum;
|
|
5732
|
+
return z.enum([first, ...rest]);
|
|
5733
|
+
}
|
|
5734
|
+
if (property.enum.length === 1) {
|
|
5735
|
+
return z.literal(property.enum[0]);
|
|
5736
|
+
}
|
|
5737
|
+
return z.union(property.enum.map((value) => z.literal(value)));
|
|
5738
|
+
}
|
|
5739
|
+
switch (property.type) {
|
|
5740
|
+
case "string":
|
|
5741
|
+
return z.string();
|
|
5742
|
+
case "number":
|
|
5743
|
+
return z.number();
|
|
5744
|
+
case "integer":
|
|
5745
|
+
return z.number().int();
|
|
5746
|
+
case "boolean":
|
|
5747
|
+
return z.boolean();
|
|
5748
|
+
case "array":
|
|
5749
|
+
return z.array(
|
|
5750
|
+
isJsonSchemaObject(property.items) ? toZodSchema(property.items) : z.any()
|
|
5751
|
+
);
|
|
5752
|
+
case "object":
|
|
5753
|
+
return z.object(jsonSchemaToZodRawShape(property.properties || {}));
|
|
5754
|
+
default:
|
|
5755
|
+
return z.any();
|
|
5756
|
+
}
|
|
5757
|
+
}
|
|
5758
|
+
function jsonSchemaToZodRawShape(properties, required = []) {
|
|
5759
|
+
const requiredSet = new Set(required);
|
|
5760
|
+
return Object.fromEntries(
|
|
5761
|
+
Object.entries(properties).map(([name, property]) => {
|
|
5762
|
+
const schema = toZodSchema(property);
|
|
5763
|
+
const finalSchema = requiredSet.has(name) ? schema : schema.optional();
|
|
5764
|
+
return [name, finalSchema];
|
|
5765
|
+
})
|
|
5766
|
+
);
|
|
5767
|
+
}
|
|
5768
|
+
function defaultGetCustomerRef(_args, extra) {
|
|
5769
|
+
const fromExtra = extra?.authInfo?.extra?.customer_ref;
|
|
5770
|
+
return typeof fromExtra === "string" && fromExtra.trim() ? fromExtra.trim() : "anonymous";
|
|
5771
|
+
}
|
|
5772
|
+
function registerVirtualToolsMcpImpl(server, apiClient, options) {
|
|
5773
|
+
const { filter, mapDefinition, wrapHandler, getCustomerRef, ...virtualToolsOptions } = options;
|
|
5774
|
+
const virtualTools = createVirtualTools(apiClient, {
|
|
5775
|
+
...virtualToolsOptions,
|
|
5776
|
+
getCustomerRef: getCustomerRef || defaultGetCustomerRef
|
|
5777
|
+
});
|
|
5778
|
+
for (const toolDefinition of virtualTools) {
|
|
5779
|
+
if (filter && !filter(toolDefinition)) continue;
|
|
5780
|
+
const mappedDefinition = mapDefinition ? mapDefinition(toolDefinition) : toolDefinition;
|
|
5781
|
+
const wrappedHandler = wrapHandler ? wrapHandler(mappedDefinition.handler, mappedDefinition) : mappedDefinition.handler;
|
|
5782
|
+
server.registerTool(
|
|
5783
|
+
mappedDefinition.name,
|
|
5784
|
+
{
|
|
5785
|
+
description: mappedDefinition.description,
|
|
5786
|
+
inputSchema: jsonSchemaToZodRawShape(
|
|
5787
|
+
mappedDefinition.inputSchema.properties,
|
|
5788
|
+
mappedDefinition.inputSchema.required || []
|
|
5789
|
+
)
|
|
5790
|
+
},
|
|
5791
|
+
wrappedHandler
|
|
5792
|
+
);
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
|
|
5649
5796
|
// src/factory.ts
|
|
5650
5797
|
function createSolvaPay(config) {
|
|
5651
5798
|
let resolvedConfig;
|
|
@@ -5695,14 +5842,14 @@ function createSolvaPay(config) {
|
|
|
5695
5842
|
if (!apiClient.createCustomer) {
|
|
5696
5843
|
throw new import_core2.SolvaPayError("createCustomer is not available on this API client");
|
|
5697
5844
|
}
|
|
5698
|
-
return apiClient.createCustomer(params);
|
|
5845
|
+
return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
|
|
5699
5846
|
},
|
|
5700
5847
|
getCustomer(params) {
|
|
5701
5848
|
return apiClient.getCustomer(params);
|
|
5702
5849
|
},
|
|
5703
5850
|
createCheckoutSession(params) {
|
|
5704
5851
|
return apiClient.createCheckoutSession({
|
|
5705
|
-
|
|
5852
|
+
customerRef: params.customerRef,
|
|
5706
5853
|
productRef: params.productRef,
|
|
5707
5854
|
planRef: params.planRef
|
|
5708
5855
|
});
|
|
@@ -5716,9 +5863,18 @@ function createSolvaPay(config) {
|
|
|
5716
5863
|
}
|
|
5717
5864
|
return apiClient.bootstrapMcpProduct(params);
|
|
5718
5865
|
},
|
|
5866
|
+
configureMcpPlans(productRef, params) {
|
|
5867
|
+
if (!apiClient.configureMcpPlans) {
|
|
5868
|
+
throw new import_core2.SolvaPayError("configureMcpPlans is not available on this API client");
|
|
5869
|
+
}
|
|
5870
|
+
return apiClient.configureMcpPlans(productRef, params);
|
|
5871
|
+
},
|
|
5719
5872
|
getVirtualTools(options) {
|
|
5720
5873
|
return createVirtualTools(apiClient, options);
|
|
5721
5874
|
},
|
|
5875
|
+
async registerVirtualToolsMcp(server, options) {
|
|
5876
|
+
await registerVirtualToolsMcpImpl(server, apiClient, options);
|
|
5877
|
+
},
|
|
5722
5878
|
// Payable API for framework-specific handlers
|
|
5723
5879
|
payable(options = {}) {
|
|
5724
5880
|
const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
@@ -5757,9 +5913,9 @@ function createSolvaPay(config) {
|
|
|
5757
5913
|
getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
|
|
5758
5914
|
});
|
|
5759
5915
|
const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
5760
|
-
return async (args) => {
|
|
5916
|
+
return async (args, extra) => {
|
|
5761
5917
|
const handler = await handlerPromise;
|
|
5762
|
-
return handler(args);
|
|
5918
|
+
return handler(args, extra);
|
|
5763
5919
|
};
|
|
5764
5920
|
},
|
|
5765
5921
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -5829,6 +5985,167 @@ function getCustomerRefFromBearerAuthHeader(authorization, options = {}) {
|
|
|
5829
5985
|
return getCustomerRefFromJwtPayload(payload, options);
|
|
5830
5986
|
}
|
|
5831
5987
|
|
|
5988
|
+
// src/mcp/auth-bridge.ts
|
|
5989
|
+
function getClientId(payload, explicitClientId) {
|
|
5990
|
+
if (explicitClientId) return explicitClientId;
|
|
5991
|
+
const payloadClientId = typeof payload.client_id === "string" && payload.client_id || typeof payload.azp === "string" && payload.azp || typeof payload.aud === "string" && payload.aud || null;
|
|
5992
|
+
return payloadClientId || "solvapay-mcp-client";
|
|
5993
|
+
}
|
|
5994
|
+
function getScopes(payload, defaultScopes) {
|
|
5995
|
+
if (Array.isArray(payload.scp)) {
|
|
5996
|
+
return payload.scp.filter((scope) => typeof scope === "string");
|
|
5997
|
+
}
|
|
5998
|
+
if (typeof payload.scope === "string" && payload.scope.trim()) {
|
|
5999
|
+
return payload.scope.split(/\s+/).map((scope) => scope.trim()).filter(Boolean);
|
|
6000
|
+
}
|
|
6001
|
+
return defaultScopes;
|
|
6002
|
+
}
|
|
6003
|
+
function getExpiresAt(payload) {
|
|
6004
|
+
return typeof payload.exp === "number" ? payload.exp : void 0;
|
|
6005
|
+
}
|
|
6006
|
+
function buildAuthInfoFromBearer(authorization, options = {}) {
|
|
6007
|
+
const token = extractBearerToken(authorization);
|
|
6008
|
+
if (!token) return null;
|
|
6009
|
+
const payload = decodeJwtPayload(token);
|
|
6010
|
+
const customerRef = getCustomerRefFromJwtPayload(payload, options);
|
|
6011
|
+
const clientId = getClientId(payload, options.clientId);
|
|
6012
|
+
const scopes = getScopes(payload, options.defaultScopes || []);
|
|
6013
|
+
const expiresAt = getExpiresAt(payload);
|
|
6014
|
+
return {
|
|
6015
|
+
token,
|
|
6016
|
+
clientId,
|
|
6017
|
+
scopes,
|
|
6018
|
+
expiresAt,
|
|
6019
|
+
extra: {
|
|
6020
|
+
customer_ref: customerRef,
|
|
6021
|
+
...options.includePayload ? { payload } : {}
|
|
6022
|
+
}
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
6025
|
+
|
|
6026
|
+
// src/mcp/oauth-bridge.ts
|
|
6027
|
+
function withoutTrailingSlash(value) {
|
|
6028
|
+
return value.replace(/\/$/, "");
|
|
6029
|
+
}
|
|
6030
|
+
function getRequestAuthHeader(req) {
|
|
6031
|
+
const header = req.headers?.authorization;
|
|
6032
|
+
if (typeof header === "string") return header;
|
|
6033
|
+
if (Array.isArray(header)) return header[0] || null;
|
|
6034
|
+
return null;
|
|
6035
|
+
}
|
|
6036
|
+
function getRequestJsonRpcId(body) {
|
|
6037
|
+
if (body && typeof body === "object" && "id" in body) {
|
|
6038
|
+
const id = body.id;
|
|
6039
|
+
return id ?? null;
|
|
6040
|
+
}
|
|
6041
|
+
return null;
|
|
6042
|
+
}
|
|
6043
|
+
function makeUnauthorizedJsonRpc(id) {
|
|
6044
|
+
return {
|
|
6045
|
+
jsonrpc: "2.0",
|
|
6046
|
+
id,
|
|
6047
|
+
error: {
|
|
6048
|
+
code: -32001,
|
|
6049
|
+
message: "Unauthorized"
|
|
6050
|
+
}
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
function setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath) {
|
|
6054
|
+
res.setHeader(
|
|
6055
|
+
"WWW-Authenticate",
|
|
6056
|
+
`Bearer resource_metadata="${withoutTrailingSlash(publicBaseUrl)}${protectedResourcePath}"`
|
|
6057
|
+
);
|
|
6058
|
+
}
|
|
6059
|
+
function getOAuthProtectedResourceResponse(publicBaseUrl) {
|
|
6060
|
+
const resource = withoutTrailingSlash(publicBaseUrl);
|
|
6061
|
+
return {
|
|
6062
|
+
resource,
|
|
6063
|
+
authorization_servers: [resource],
|
|
6064
|
+
scopes_supported: ["openid", "profile", "email"]
|
|
6065
|
+
};
|
|
6066
|
+
}
|
|
6067
|
+
function getOAuthAuthorizationServerResponse({
|
|
6068
|
+
apiBaseUrl,
|
|
6069
|
+
productRef
|
|
6070
|
+
}) {
|
|
6071
|
+
const normalizedApiBaseUrl = withoutTrailingSlash(apiBaseUrl);
|
|
6072
|
+
const registrationEndpoint = `${normalizedApiBaseUrl}/v1/customer/auth/register?product_ref=${encodeURIComponent(productRef)}`;
|
|
6073
|
+
return {
|
|
6074
|
+
issuer: normalizedApiBaseUrl,
|
|
6075
|
+
authorization_endpoint: `${normalizedApiBaseUrl}/v1/customer/auth/authorize`,
|
|
6076
|
+
token_endpoint: `${normalizedApiBaseUrl}/v1/customer/auth/token`,
|
|
6077
|
+
registration_endpoint: registrationEndpoint,
|
|
6078
|
+
token_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
|
|
6079
|
+
response_types_supported: ["code"],
|
|
6080
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
6081
|
+
scopes_supported: ["openid", "profile", "email"],
|
|
6082
|
+
code_challenge_methods_supported: ["S256"]
|
|
6083
|
+
};
|
|
6084
|
+
}
|
|
6085
|
+
function createMcpOAuthBridge(options) {
|
|
6086
|
+
const {
|
|
6087
|
+
publicBaseUrl,
|
|
6088
|
+
apiBaseUrl,
|
|
6089
|
+
productRef,
|
|
6090
|
+
mcpPath = "/mcp",
|
|
6091
|
+
requireAuth = true,
|
|
6092
|
+
authInfo,
|
|
6093
|
+
protectedResourcePath = "/.well-known/oauth-protected-resource",
|
|
6094
|
+
authorizationServerPath = "/.well-known/oauth-authorization-server"
|
|
6095
|
+
} = options;
|
|
6096
|
+
const protectedResourceMiddleware = (req, res, next) => {
|
|
6097
|
+
if (req.method !== "GET" || req.path !== protectedResourcePath) {
|
|
6098
|
+
next();
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
res.json(getOAuthProtectedResourceResponse(publicBaseUrl));
|
|
6102
|
+
};
|
|
6103
|
+
const authorizationServerMiddleware = (req, res, next) => {
|
|
6104
|
+
if (req.method !== "GET" || req.path !== authorizationServerPath) {
|
|
6105
|
+
next();
|
|
6106
|
+
return;
|
|
6107
|
+
}
|
|
6108
|
+
if (!productRef) {
|
|
6109
|
+
res.status(500).json({ error: "SOLVAPAY_PRODUCT_REF missing" });
|
|
6110
|
+
return;
|
|
6111
|
+
}
|
|
6112
|
+
res.json(
|
|
6113
|
+
getOAuthAuthorizationServerResponse({
|
|
6114
|
+
apiBaseUrl,
|
|
6115
|
+
productRef
|
|
6116
|
+
})
|
|
6117
|
+
);
|
|
6118
|
+
};
|
|
6119
|
+
const mcpAuthMiddleware = (req, res, next) => {
|
|
6120
|
+
if (req.path !== mcpPath) {
|
|
6121
|
+
next();
|
|
6122
|
+
return;
|
|
6123
|
+
}
|
|
6124
|
+
const authHeader = getRequestAuthHeader(req);
|
|
6125
|
+
const id = getRequestJsonRpcId(req.body);
|
|
6126
|
+
if (!authHeader && !requireAuth) {
|
|
6127
|
+
next();
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
6130
|
+
try {
|
|
6131
|
+
const auth = buildAuthInfoFromBearer(authHeader, authInfo);
|
|
6132
|
+
if (!auth) {
|
|
6133
|
+
throw new McpBearerAuthError("Missing bearer token");
|
|
6134
|
+
}
|
|
6135
|
+
req.auth = auth;
|
|
6136
|
+
next();
|
|
6137
|
+
} catch {
|
|
6138
|
+
setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath);
|
|
6139
|
+
if (req.method === "POST") {
|
|
6140
|
+
res.status(401).json(makeUnauthorizedJsonRpc(id));
|
|
6141
|
+
return;
|
|
6142
|
+
}
|
|
6143
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
6144
|
+
}
|
|
6145
|
+
};
|
|
6146
|
+
return [protectedResourceMiddleware, authorizationServerMiddleware, mcpAuthMiddleware];
|
|
6147
|
+
}
|
|
6148
|
+
|
|
5832
6149
|
// src/helpers/error.ts
|
|
5833
6150
|
var import_core3 = require("@solvapay/core");
|
|
5834
6151
|
function isErrorResult(result) {
|
|
@@ -6183,9 +6500,11 @@ function verifyWebhook({
|
|
|
6183
6500
|
McpBearerAuthError,
|
|
6184
6501
|
PaywallError,
|
|
6185
6502
|
VIRTUAL_TOOL_DEFINITIONS,
|
|
6503
|
+
buildAuthInfoFromBearer,
|
|
6186
6504
|
cancelPurchaseCore,
|
|
6187
6505
|
createCheckoutSessionCore,
|
|
6188
6506
|
createCustomerSessionCore,
|
|
6507
|
+
createMcpOAuthBridge,
|
|
6189
6508
|
createPaymentIntentCore,
|
|
6190
6509
|
createSolvaPay,
|
|
6191
6510
|
createSolvaPayClient,
|
|
@@ -6195,10 +6514,15 @@ function verifyWebhook({
|
|
|
6195
6514
|
getAuthenticatedUserCore,
|
|
6196
6515
|
getCustomerRefFromBearerAuthHeader,
|
|
6197
6516
|
getCustomerRefFromJwtPayload,
|
|
6517
|
+
getOAuthAuthorizationServerResponse,
|
|
6518
|
+
getOAuthProtectedResourceResponse,
|
|
6198
6519
|
handleRouteError,
|
|
6199
6520
|
isErrorResult,
|
|
6521
|
+
jsonSchemaToZodRawShape,
|
|
6200
6522
|
listPlansCore,
|
|
6523
|
+
paywallErrorToClientPayload,
|
|
6201
6524
|
processPaymentIntentCore,
|
|
6525
|
+
registerVirtualToolsMcpImpl,
|
|
6202
6526
|
syncCustomerCore,
|
|
6203
6527
|
verifyWebhook,
|
|
6204
6528
|
withRetry
|