@solvapay/server 1.0.6 → 1.0.9-preview.1
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 +1035 -1749
- package/dist/edge.js +432 -35
- package/dist/index.cjs +441 -36
- package/dist/index.d.cts +1036 -1747
- package/dist/index.d.ts +1036 -1747
- package/dist/index.js +434 -36
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -4314,18 +4314,22 @@ __export(index_exports, {
|
|
|
4314
4314
|
McpBearerAuthError: () => McpBearerAuthError,
|
|
4315
4315
|
PaywallError: () => PaywallError,
|
|
4316
4316
|
VIRTUAL_TOOL_DEFINITIONS: () => VIRTUAL_TOOL_DEFINITIONS,
|
|
4317
|
+
activatePlanCore: () => activatePlanCore,
|
|
4317
4318
|
buildAuthInfoFromBearer: () => buildAuthInfoFromBearer,
|
|
4318
4319
|
cancelPurchaseCore: () => cancelPurchaseCore,
|
|
4320
|
+
checkPurchaseCore: () => checkPurchaseCore,
|
|
4319
4321
|
createCheckoutSessionCore: () => createCheckoutSessionCore,
|
|
4320
4322
|
createCustomerSessionCore: () => createCustomerSessionCore,
|
|
4321
4323
|
createMcpOAuthBridge: () => createMcpOAuthBridge,
|
|
4322
4324
|
createPaymentIntentCore: () => createPaymentIntentCore,
|
|
4323
4325
|
createSolvaPay: () => createSolvaPay,
|
|
4324
4326
|
createSolvaPayClient: () => createSolvaPayClient,
|
|
4327
|
+
createTopupPaymentIntentCore: () => createTopupPaymentIntentCore,
|
|
4325
4328
|
createVirtualTools: () => createVirtualTools,
|
|
4326
4329
|
decodeJwtPayload: () => decodeJwtPayload,
|
|
4327
4330
|
extractBearerToken: () => extractBearerToken,
|
|
4328
4331
|
getAuthenticatedUserCore: () => getAuthenticatedUserCore,
|
|
4332
|
+
getCustomerBalanceCore: () => getCustomerBalanceCore,
|
|
4329
4333
|
getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
|
|
4330
4334
|
getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
|
|
4331
4335
|
getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
|
|
@@ -4334,9 +4338,12 @@ __export(index_exports, {
|
|
|
4334
4338
|
isErrorResult: () => isErrorResult,
|
|
4335
4339
|
jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
|
|
4336
4340
|
listPlansCore: () => listPlansCore,
|
|
4341
|
+
paywallErrorToClientPayload: () => paywallErrorToClientPayload,
|
|
4337
4342
|
processPaymentIntentCore: () => processPaymentIntentCore,
|
|
4343
|
+
reactivatePurchaseCore: () => reactivatePurchaseCore,
|
|
4338
4344
|
registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
|
|
4339
4345
|
syncCustomerCore: () => syncCustomerCore,
|
|
4346
|
+
trackUsageCore: () => trackUsageCore,
|
|
4340
4347
|
verifyWebhook: () => verifyWebhook,
|
|
4341
4348
|
withRetry: () => withRetry
|
|
4342
4349
|
});
|
|
@@ -4379,12 +4386,10 @@ function createSolvaPayClient(opts) {
|
|
|
4379
4386
|
// POST: /v1/sdk/usages
|
|
4380
4387
|
async trackUsage(params) {
|
|
4381
4388
|
const url = `${base}/v1/sdk/usages`;
|
|
4382
|
-
const { customerRef, ...rest } = params;
|
|
4383
|
-
const body = { ...rest, customerId: customerRef };
|
|
4384
4389
|
const res = await fetch(url, {
|
|
4385
4390
|
method: "POST",
|
|
4386
4391
|
headers,
|
|
4387
|
-
body: JSON.stringify(
|
|
4392
|
+
body: JSON.stringify(params)
|
|
4388
4393
|
});
|
|
4389
4394
|
if (!res.ok) {
|
|
4390
4395
|
const error = await res.text();
|
|
@@ -4629,7 +4634,7 @@ function createSolvaPayClient(opts) {
|
|
|
4629
4634
|
body: JSON.stringify({
|
|
4630
4635
|
productRef: params.productRef,
|
|
4631
4636
|
planRef: params.planRef,
|
|
4632
|
-
|
|
4637
|
+
customerRef: params.customerRef
|
|
4633
4638
|
})
|
|
4634
4639
|
});
|
|
4635
4640
|
if (!res.ok) {
|
|
@@ -4637,8 +4642,32 @@ function createSolvaPayClient(opts) {
|
|
|
4637
4642
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4638
4643
|
throw new import_core.SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
|
|
4639
4644
|
}
|
|
4640
|
-
|
|
4641
|
-
|
|
4645
|
+
return await res.json();
|
|
4646
|
+
},
|
|
4647
|
+
// POST: /v1/sdk/payment-intents (purpose: credit_topup)
|
|
4648
|
+
async createTopupPaymentIntent(params) {
|
|
4649
|
+
const idempotencyKey = params.idempotencyKey || `topup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
4650
|
+
const url = `${base}/v1/sdk/payment-intents`;
|
|
4651
|
+
const res = await fetch(url, {
|
|
4652
|
+
method: "POST",
|
|
4653
|
+
headers: {
|
|
4654
|
+
...headers,
|
|
4655
|
+
"Idempotency-Key": idempotencyKey
|
|
4656
|
+
},
|
|
4657
|
+
body: JSON.stringify({
|
|
4658
|
+
customerRef: params.customerRef,
|
|
4659
|
+
purpose: "credit_topup",
|
|
4660
|
+
amount: params.amount,
|
|
4661
|
+
currency: params.currency,
|
|
4662
|
+
description: params.description
|
|
4663
|
+
})
|
|
4664
|
+
});
|
|
4665
|
+
if (!res.ok) {
|
|
4666
|
+
const error = await res.text();
|
|
4667
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4668
|
+
throw new import_core.SolvaPayError(`Create topup payment intent failed (${res.status}): ${error}`);
|
|
4669
|
+
}
|
|
4670
|
+
return await res.json();
|
|
4642
4671
|
},
|
|
4643
4672
|
// POST: /v1/sdk/payment-intents/{paymentIntentId}/process
|
|
4644
4673
|
async processPaymentIntent(params) {
|
|
@@ -4715,6 +4744,54 @@ function createSolvaPayClient(opts) {
|
|
|
4715
4744
|
}
|
|
4716
4745
|
return result;
|
|
4717
4746
|
},
|
|
4747
|
+
// POST: /v1/sdk/purchases/{purchaseRef}/reactivate
|
|
4748
|
+
async reactivatePurchase(params) {
|
|
4749
|
+
const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/reactivate`;
|
|
4750
|
+
const res = await fetch(url, {
|
|
4751
|
+
method: "POST",
|
|
4752
|
+
headers
|
|
4753
|
+
});
|
|
4754
|
+
if (!res.ok) {
|
|
4755
|
+
const error = await res.text();
|
|
4756
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4757
|
+
if (res.status === 404) {
|
|
4758
|
+
throw new import_core.SolvaPayError(`Purchase not found: ${error}`);
|
|
4759
|
+
}
|
|
4760
|
+
if (res.status === 400) {
|
|
4761
|
+
throw new import_core.SolvaPayError(
|
|
4762
|
+
`Purchase cannot be reactivated: ${error}`
|
|
4763
|
+
);
|
|
4764
|
+
}
|
|
4765
|
+
throw new import_core.SolvaPayError(`Reactivate purchase failed (${res.status}): ${error}`);
|
|
4766
|
+
}
|
|
4767
|
+
const responseText = await res.text();
|
|
4768
|
+
let responseData;
|
|
4769
|
+
try {
|
|
4770
|
+
responseData = JSON.parse(responseText);
|
|
4771
|
+
} catch (parseError) {
|
|
4772
|
+
log(`\u274C Failed to parse response as JSON: ${parseError}`);
|
|
4773
|
+
throw new import_core.SolvaPayError(
|
|
4774
|
+
`Invalid JSON response from reactivate purchase endpoint: ${responseText.substring(0, 200)}`
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
if (!responseData || typeof responseData !== "object") {
|
|
4778
|
+
log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
|
|
4779
|
+
throw new import_core.SolvaPayError(`Invalid response structure from reactivate purchase endpoint`);
|
|
4780
|
+
}
|
|
4781
|
+
let result;
|
|
4782
|
+
if (responseData.purchase && typeof responseData.purchase === "object") {
|
|
4783
|
+
result = responseData.purchase;
|
|
4784
|
+
} else if (responseData.reference) {
|
|
4785
|
+
result = responseData;
|
|
4786
|
+
} else {
|
|
4787
|
+
result = responseData.purchase || responseData;
|
|
4788
|
+
}
|
|
4789
|
+
if (!result || typeof result !== "object") {
|
|
4790
|
+
log(`\u274C Invalid purchase data in response. Full response:`, responseData);
|
|
4791
|
+
throw new import_core.SolvaPayError(`Invalid purchase data in reactivate purchase response`);
|
|
4792
|
+
}
|
|
4793
|
+
return result;
|
|
4794
|
+
},
|
|
4718
4795
|
// POST: /v1/sdk/user-info
|
|
4719
4796
|
async getUserInfo(params) {
|
|
4720
4797
|
const url = `${base}/v1/sdk/user-info`;
|
|
@@ -4730,6 +4807,20 @@ function createSolvaPayClient(opts) {
|
|
|
4730
4807
|
}
|
|
4731
4808
|
return await res.json();
|
|
4732
4809
|
},
|
|
4810
|
+
// GET: /v1/sdk/customers/:customerRef/balance
|
|
4811
|
+
async getCustomerBalance(params) {
|
|
4812
|
+
const url = `${base}/v1/sdk/customers/${params.customerRef}/balance`;
|
|
4813
|
+
const res = await fetch(url, {
|
|
4814
|
+
method: "GET",
|
|
4815
|
+
headers
|
|
4816
|
+
});
|
|
4817
|
+
if (!res.ok) {
|
|
4818
|
+
const error = await res.text();
|
|
4819
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4820
|
+
throw new import_core.SolvaPayError(`Get customer balance failed (${res.status}): ${error}`);
|
|
4821
|
+
}
|
|
4822
|
+
return await res.json();
|
|
4823
|
+
},
|
|
4733
4824
|
// POST: /v1/sdk/checkout-sessions
|
|
4734
4825
|
async createCheckoutSession(params) {
|
|
4735
4826
|
const url = `${base}/v1/sdk/checkout-sessions`;
|
|
@@ -4761,6 +4852,21 @@ function createSolvaPayClient(opts) {
|
|
|
4761
4852
|
}
|
|
4762
4853
|
const result = await res.json();
|
|
4763
4854
|
return result;
|
|
4855
|
+
},
|
|
4856
|
+
// POST: /v1/sdk/activate
|
|
4857
|
+
async activatePlan(params) {
|
|
4858
|
+
const url = `${base}/v1/sdk/activate`;
|
|
4859
|
+
const res = await fetch(url, {
|
|
4860
|
+
method: "POST",
|
|
4861
|
+
headers,
|
|
4862
|
+
body: JSON.stringify(params)
|
|
4863
|
+
});
|
|
4864
|
+
if (!res.ok) {
|
|
4865
|
+
const error = await res.text();
|
|
4866
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4867
|
+
throw new import_core.SolvaPayError(`Activate plan failed (${res.status}): ${error}`);
|
|
4868
|
+
}
|
|
4869
|
+
return await res.json();
|
|
4764
4870
|
}
|
|
4765
4871
|
};
|
|
4766
4872
|
}
|
|
@@ -4916,6 +5022,24 @@ var PaywallError = class extends Error {
|
|
|
4916
5022
|
this.name = "PaywallError";
|
|
4917
5023
|
}
|
|
4918
5024
|
};
|
|
5025
|
+
function paywallErrorToClientPayload(error) {
|
|
5026
|
+
const sc = error.structuredContent;
|
|
5027
|
+
const base = {
|
|
5028
|
+
success: false,
|
|
5029
|
+
error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
|
|
5030
|
+
product: sc.product,
|
|
5031
|
+
checkoutUrl: sc.checkoutUrl,
|
|
5032
|
+
message: sc.message
|
|
5033
|
+
};
|
|
5034
|
+
if (sc.kind === "activation_required") {
|
|
5035
|
+
base.kind = "activation_required";
|
|
5036
|
+
if (sc.plans !== void 0) base.plans = sc.plans;
|
|
5037
|
+
if (sc.balance !== void 0) base.balance = sc.balance;
|
|
5038
|
+
if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
|
|
5039
|
+
if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
|
|
5040
|
+
}
|
|
5041
|
+
return base;
|
|
5042
|
+
}
|
|
4919
5043
|
var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
|
|
4920
5044
|
cacheTTL: 6e4,
|
|
4921
5045
|
// Cache results for 60 seconds (reduces API calls significantly)
|
|
@@ -4954,8 +5078,6 @@ var SolvaPayPaywall = class {
|
|
|
4954
5078
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4955
5079
|
async protect(handler, metadata = {}, getCustomerRef) {
|
|
4956
5080
|
const product = this.resolveProduct(metadata);
|
|
4957
|
-
const configuredPlanRef = metadata.plan?.trim();
|
|
4958
|
-
const usagePlanRef = configuredPlanRef || "unspecified";
|
|
4959
5081
|
const usageType = metadata.usageType || "requests";
|
|
4960
5082
|
return async (args) => {
|
|
4961
5083
|
const startTime = Date.now();
|
|
@@ -4969,12 +5091,13 @@ var SolvaPayPaywall = class {
|
|
|
4969
5091
|
}
|
|
4970
5092
|
let resolvedMeterName;
|
|
4971
5093
|
try {
|
|
4972
|
-
const limitsCacheKey = `${backendCustomerRef}:${product}:${
|
|
5094
|
+
const limitsCacheKey = `${backendCustomerRef}:${product}:${usageType}`;
|
|
4973
5095
|
const cachedLimits = this.limitsCache.get(limitsCacheKey);
|
|
4974
5096
|
const now = Date.now();
|
|
4975
5097
|
let withinLimits;
|
|
4976
5098
|
let remaining;
|
|
4977
5099
|
let checkoutUrl;
|
|
5100
|
+
let lastLimitsCheck;
|
|
4978
5101
|
const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
|
|
4979
5102
|
if (hasFreshCachedLimits) {
|
|
4980
5103
|
checkoutUrl = cachedLimits.checkoutUrl;
|
|
@@ -4998,9 +5121,9 @@ var SolvaPayPaywall = class {
|
|
|
4998
5121
|
const limitsCheck = await this.apiClient.checkLimits({
|
|
4999
5122
|
customerRef: backendCustomerRef,
|
|
5000
5123
|
productRef: product,
|
|
5001
|
-
...configuredPlanRef ? { planRef: configuredPlanRef } : {},
|
|
5002
5124
|
meterName: usageType
|
|
5003
5125
|
});
|
|
5126
|
+
lastLimitsCheck = limitsCheck;
|
|
5004
5127
|
withinLimits = limitsCheck.withinLimits;
|
|
5005
5128
|
remaining = limitsCheck.remaining;
|
|
5006
5129
|
checkoutUrl = limitsCheck.checkoutUrl;
|
|
@@ -5023,12 +5146,25 @@ var SolvaPayPaywall = class {
|
|
|
5023
5146
|
this.trackUsage(
|
|
5024
5147
|
backendCustomerRef,
|
|
5025
5148
|
product,
|
|
5026
|
-
usagePlanRef,
|
|
5027
5149
|
resolvedMeterName || usageType,
|
|
5028
5150
|
"paywall",
|
|
5029
5151
|
requestId,
|
|
5030
5152
|
latencyMs2
|
|
5031
5153
|
);
|
|
5154
|
+
if (lastLimitsCheck?.activationRequired) {
|
|
5155
|
+
const confirmationUrl = lastLimitsCheck.confirmationUrl;
|
|
5156
|
+
const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
|
|
5157
|
+
throw new PaywallError("Activation required", {
|
|
5158
|
+
kind: "activation_required",
|
|
5159
|
+
product,
|
|
5160
|
+
message: "Product activation is required before this tool can be used.",
|
|
5161
|
+
checkoutUrl: payCheckoutUrl,
|
|
5162
|
+
confirmationUrl,
|
|
5163
|
+
plans: lastLimitsCheck.plans,
|
|
5164
|
+
balance: lastLimitsCheck.balance,
|
|
5165
|
+
productDetails: lastLimitsCheck.product
|
|
5166
|
+
});
|
|
5167
|
+
}
|
|
5032
5168
|
throw new PaywallError("Payment required", {
|
|
5033
5169
|
kind: "payment_required",
|
|
5034
5170
|
product,
|
|
@@ -5041,7 +5177,6 @@ var SolvaPayPaywall = class {
|
|
|
5041
5177
|
this.trackUsage(
|
|
5042
5178
|
backendCustomerRef,
|
|
5043
5179
|
product,
|
|
5044
|
-
usagePlanRef,
|
|
5045
5180
|
resolvedMeterName || usageType,
|
|
5046
5181
|
"success",
|
|
5047
5182
|
requestId,
|
|
@@ -5060,7 +5195,6 @@ var SolvaPayPaywall = class {
|
|
|
5060
5195
|
this.trackUsage(
|
|
5061
5196
|
backendCustomerRef,
|
|
5062
5197
|
product,
|
|
5063
|
-
usagePlanRef,
|
|
5064
5198
|
resolvedMeterName || usageType,
|
|
5065
5199
|
"fail",
|
|
5066
5200
|
requestId,
|
|
@@ -5131,7 +5265,8 @@ var SolvaPayPaywall = class {
|
|
|
5131
5265
|
this.customerCreationAttempts.add(customerRef);
|
|
5132
5266
|
try {
|
|
5133
5267
|
const createParams = {
|
|
5134
|
-
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local
|
|
5268
|
+
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5269
|
+
metadata: {}
|
|
5135
5270
|
};
|
|
5136
5271
|
if (options?.name) {
|
|
5137
5272
|
createParams.name = options.name;
|
|
@@ -5155,7 +5290,10 @@ var SolvaPayPaywall = class {
|
|
|
5155
5290
|
return searchResult.customerRef;
|
|
5156
5291
|
}
|
|
5157
5292
|
} catch (lookupError) {
|
|
5158
|
-
this.log(
|
|
5293
|
+
this.log(
|
|
5294
|
+
`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
|
|
5295
|
+
lookupError instanceof Error ? lookupError.message : lookupError
|
|
5296
|
+
);
|
|
5159
5297
|
}
|
|
5160
5298
|
}
|
|
5161
5299
|
const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
|
|
@@ -5178,7 +5316,8 @@ var SolvaPayPaywall = class {
|
|
|
5178
5316
|
try {
|
|
5179
5317
|
const retryParams = {
|
|
5180
5318
|
email: `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5181
|
-
externalRef
|
|
5319
|
+
externalRef,
|
|
5320
|
+
metadata: {}
|
|
5182
5321
|
};
|
|
5183
5322
|
if (options?.name) {
|
|
5184
5323
|
retryParams.name = options.name;
|
|
@@ -5215,14 +5354,14 @@ var SolvaPayPaywall = class {
|
|
|
5215
5354
|
}
|
|
5216
5355
|
return backendRef;
|
|
5217
5356
|
}
|
|
5218
|
-
async trackUsage(customerRef,
|
|
5357
|
+
async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration) {
|
|
5219
5358
|
await withRetry(
|
|
5220
5359
|
() => this.apiClient.trackUsage({
|
|
5221
5360
|
customerRef,
|
|
5222
5361
|
actionType: "api_call",
|
|
5223
5362
|
units: 1,
|
|
5224
5363
|
outcome,
|
|
5225
|
-
|
|
5364
|
+
productRef,
|
|
5226
5365
|
duration: actionDuration,
|
|
5227
5366
|
metadata: { action: action || "api_requests", requestId },
|
|
5228
5367
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5474,8 +5613,10 @@ var McpAdapter = class {
|
|
|
5474
5613
|
const ref = await this.options.getCustomerRef(args, extra);
|
|
5475
5614
|
return AdapterUtils.ensureCustomerRef(ref);
|
|
5476
5615
|
}
|
|
5477
|
-
const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
|
|
5478
|
-
const
|
|
5616
|
+
const customerRefFromExtra = typeof extra?.authInfo?.extra?.customer_ref === "string" ? String(extra.authInfo.extra.customer_ref) : void 0;
|
|
5617
|
+
const customerRefFromArgs = typeof args.auth === "object" && args.auth !== null && typeof args.auth.customer_ref === "string" ? String(args.auth.customer_ref) : void 0;
|
|
5618
|
+
const directCustomerRef = typeof args.customer_ref === "string" ? args.customer_ref : void 0;
|
|
5619
|
+
const customerRef = (customerRefFromExtra || customerRefFromArgs || directCustomerRef || "anonymous").trim();
|
|
5479
5620
|
return AdapterUtils.ensureCustomerRef(customerRef);
|
|
5480
5621
|
}
|
|
5481
5622
|
formatResponse(result, _context) {
|
|
@@ -5499,17 +5640,7 @@ var McpAdapter = class {
|
|
|
5499
5640
|
content: [
|
|
5500
5641
|
{
|
|
5501
5642
|
type: "text",
|
|
5502
|
-
text: JSON.stringify(
|
|
5503
|
-
{
|
|
5504
|
-
success: false,
|
|
5505
|
-
error: "Payment required",
|
|
5506
|
-
product: error.structuredContent.product,
|
|
5507
|
-
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
5508
|
-
message: error.structuredContent.message
|
|
5509
|
-
},
|
|
5510
|
-
null,
|
|
5511
|
-
2
|
|
5512
|
-
)
|
|
5643
|
+
text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
|
|
5513
5644
|
}
|
|
5514
5645
|
],
|
|
5515
5646
|
isError: true,
|
|
@@ -5796,6 +5927,12 @@ function createSolvaPay(config) {
|
|
|
5796
5927
|
}
|
|
5797
5928
|
return apiClient.createPaymentIntent(params);
|
|
5798
5929
|
},
|
|
5930
|
+
createTopupPaymentIntent(params) {
|
|
5931
|
+
if (!apiClient.createTopupPaymentIntent) {
|
|
5932
|
+
throw new import_core2.SolvaPayError("createTopupPaymentIntent is not available on this API client");
|
|
5933
|
+
}
|
|
5934
|
+
return apiClient.createTopupPaymentIntent(params);
|
|
5935
|
+
},
|
|
5799
5936
|
processPaymentIntent(params) {
|
|
5800
5937
|
if (!apiClient.processPaymentIntent) {
|
|
5801
5938
|
throw new import_core2.SolvaPayError("processPaymentIntent is not available on this API client");
|
|
@@ -5812,11 +5949,17 @@ function createSolvaPay(config) {
|
|
|
5812
5949
|
if (!apiClient.createCustomer) {
|
|
5813
5950
|
throw new import_core2.SolvaPayError("createCustomer is not available on this API client");
|
|
5814
5951
|
}
|
|
5815
|
-
return apiClient.createCustomer(params);
|
|
5952
|
+
return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
|
|
5816
5953
|
},
|
|
5817
5954
|
getCustomer(params) {
|
|
5818
5955
|
return apiClient.getCustomer(params);
|
|
5819
5956
|
},
|
|
5957
|
+
getCustomerBalance(params) {
|
|
5958
|
+
if (!apiClient.getCustomerBalance) {
|
|
5959
|
+
throw new import_core2.SolvaPayError("getCustomerBalance is not available on this API client");
|
|
5960
|
+
}
|
|
5961
|
+
return apiClient.getCustomerBalance(params);
|
|
5962
|
+
},
|
|
5820
5963
|
createCheckoutSession(params) {
|
|
5821
5964
|
return apiClient.createCheckoutSession({
|
|
5822
5965
|
customerRef: params.customerRef,
|
|
@@ -5827,6 +5970,12 @@ function createSolvaPay(config) {
|
|
|
5827
5970
|
createCustomerSession(params) {
|
|
5828
5971
|
return apiClient.createCustomerSession(params);
|
|
5829
5972
|
},
|
|
5973
|
+
activatePlan(params) {
|
|
5974
|
+
if (!apiClient.activatePlan) {
|
|
5975
|
+
throw new import_core2.SolvaPayError("activatePlan is not available on this API client");
|
|
5976
|
+
}
|
|
5977
|
+
return apiClient.activatePlan(params);
|
|
5978
|
+
},
|
|
5830
5979
|
bootstrapMcpProduct(params) {
|
|
5831
5980
|
if (!apiClient.bootstrapMcpProduct) {
|
|
5832
5981
|
throw new import_core2.SolvaPayError("bootstrapMcpProduct is not available on this API client");
|
|
@@ -5848,9 +5997,8 @@ function createSolvaPay(config) {
|
|
|
5848
5997
|
// Payable API for framework-specific handlers
|
|
5849
5998
|
payable(options = {}) {
|
|
5850
5999
|
const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
5851
|
-
const plan = options.planRef || options.plan;
|
|
5852
6000
|
const usageType = options.usageType || "requests";
|
|
5853
|
-
const metadata = { product,
|
|
6001
|
+
const metadata = { product, usageType };
|
|
5854
6002
|
return {
|
|
5855
6003
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5856
6004
|
http(businessLogic, adapterOptions) {
|
|
@@ -5914,7 +6062,8 @@ var McpBearerAuthError = class extends Error {
|
|
|
5914
6062
|
function base64UrlDecode(input) {
|
|
5915
6063
|
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
5916
6064
|
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
5917
|
-
|
|
6065
|
+
const bytes = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
6066
|
+
return new TextDecoder().decode(bytes);
|
|
5918
6067
|
}
|
|
5919
6068
|
function extractBearerToken(authorization) {
|
|
5920
6069
|
if (!authorization) return null;
|
|
@@ -6188,6 +6337,24 @@ async function syncCustomerCore(request, options = {}) {
|
|
|
6188
6337
|
return handleRouteError(error, "Sync customer", "Failed to sync customer");
|
|
6189
6338
|
}
|
|
6190
6339
|
}
|
|
6340
|
+
async function getCustomerBalanceCore(request, options = {}) {
|
|
6341
|
+
try {
|
|
6342
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
6343
|
+
if (isErrorResult(userResult)) {
|
|
6344
|
+
return userResult;
|
|
6345
|
+
}
|
|
6346
|
+
const { userId, email, name } = userResult;
|
|
6347
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6348
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6349
|
+
email: email || void 0,
|
|
6350
|
+
name: name || void 0
|
|
6351
|
+
});
|
|
6352
|
+
const result = await solvaPay.getCustomerBalance({ customerRef });
|
|
6353
|
+
return result;
|
|
6354
|
+
} catch (error) {
|
|
6355
|
+
return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
|
|
6356
|
+
}
|
|
6357
|
+
}
|
|
6191
6358
|
|
|
6192
6359
|
// src/helpers/payment.ts
|
|
6193
6360
|
async function createPaymentIntentCore(request, body, options = {}) {
|
|
@@ -6214,7 +6381,7 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
6214
6381
|
customerRef
|
|
6215
6382
|
});
|
|
6216
6383
|
return {
|
|
6217
|
-
|
|
6384
|
+
processorPaymentId: paymentIntent.processorPaymentId,
|
|
6218
6385
|
clientSecret: paymentIntent.clientSecret,
|
|
6219
6386
|
publishableKey: paymentIntent.publishableKey,
|
|
6220
6387
|
accountId: paymentIntent.accountId,
|
|
@@ -6224,6 +6391,57 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
6224
6391
|
return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
|
|
6225
6392
|
}
|
|
6226
6393
|
}
|
|
6394
|
+
async function createTopupPaymentIntentCore(request, body, options = {}) {
|
|
6395
|
+
try {
|
|
6396
|
+
if (!body.amount || body.amount <= 0) {
|
|
6397
|
+
return {
|
|
6398
|
+
error: "Missing or invalid amount: must be a positive number",
|
|
6399
|
+
status: 400
|
|
6400
|
+
};
|
|
6401
|
+
}
|
|
6402
|
+
if (!body.currency) {
|
|
6403
|
+
return {
|
|
6404
|
+
error: "Missing required parameter: currency",
|
|
6405
|
+
status: 400
|
|
6406
|
+
};
|
|
6407
|
+
}
|
|
6408
|
+
if (body.currency !== body.currency.toUpperCase()) {
|
|
6409
|
+
return {
|
|
6410
|
+
error: `Invalid currency "${body.currency}": must be an uppercase ISO 4217 code (e.g. "USD", "EUR")`,
|
|
6411
|
+
status: 400
|
|
6412
|
+
};
|
|
6413
|
+
}
|
|
6414
|
+
const customerResult = await syncCustomerCore(request, {
|
|
6415
|
+
solvaPay: options.solvaPay,
|
|
6416
|
+
includeEmail: options.includeEmail,
|
|
6417
|
+
includeName: options.includeName
|
|
6418
|
+
});
|
|
6419
|
+
if (isErrorResult(customerResult)) {
|
|
6420
|
+
return customerResult;
|
|
6421
|
+
}
|
|
6422
|
+
const customerRef = customerResult;
|
|
6423
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6424
|
+
const paymentIntent = await solvaPay.createTopupPaymentIntent({
|
|
6425
|
+
customerRef,
|
|
6426
|
+
amount: body.amount,
|
|
6427
|
+
currency: body.currency,
|
|
6428
|
+
description: body.description
|
|
6429
|
+
});
|
|
6430
|
+
return {
|
|
6431
|
+
processorPaymentId: paymentIntent.processorPaymentId,
|
|
6432
|
+
clientSecret: paymentIntent.clientSecret,
|
|
6433
|
+
publishableKey: paymentIntent.publishableKey,
|
|
6434
|
+
accountId: paymentIntent.accountId,
|
|
6435
|
+
customerRef
|
|
6436
|
+
};
|
|
6437
|
+
} catch (error) {
|
|
6438
|
+
return handleRouteError(
|
|
6439
|
+
error,
|
|
6440
|
+
"Create topup payment intent",
|
|
6441
|
+
"Topup payment intent creation failed"
|
|
6442
|
+
);
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6227
6445
|
async function processPaymentIntentCore(request, body, options = {}) {
|
|
6228
6446
|
try {
|
|
6229
6447
|
if (!body.paymentIntentId || !body.productRef) {
|
|
@@ -6386,6 +6604,103 @@ async function cancelPurchaseCore(request, body, options = {}) {
|
|
|
6386
6604
|
return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
|
|
6387
6605
|
}
|
|
6388
6606
|
}
|
|
6607
|
+
async function reactivatePurchaseCore(request, body, options = {}) {
|
|
6608
|
+
try {
|
|
6609
|
+
if (!body.purchaseRef) {
|
|
6610
|
+
return {
|
|
6611
|
+
error: "Missing required parameter: purchaseRef is required",
|
|
6612
|
+
status: 400
|
|
6613
|
+
};
|
|
6614
|
+
}
|
|
6615
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6616
|
+
if (!solvaPay.apiClient.reactivatePurchase) {
|
|
6617
|
+
return {
|
|
6618
|
+
error: "Reactivate purchase method not available on SDK client",
|
|
6619
|
+
status: 500
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
|
|
6623
|
+
purchaseRef: body.purchaseRef
|
|
6624
|
+
});
|
|
6625
|
+
if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
|
|
6626
|
+
return {
|
|
6627
|
+
error: "Invalid response from reactivate purchase endpoint",
|
|
6628
|
+
status: 500
|
|
6629
|
+
};
|
|
6630
|
+
}
|
|
6631
|
+
const responseObj = reactivatedPurchase;
|
|
6632
|
+
if (responseObj.purchase && typeof responseObj.purchase === "object") {
|
|
6633
|
+
reactivatedPurchase = responseObj.purchase;
|
|
6634
|
+
}
|
|
6635
|
+
if (!reactivatedPurchase.reference) {
|
|
6636
|
+
return {
|
|
6637
|
+
error: "Reactivate purchase response missing required fields",
|
|
6638
|
+
status: 500
|
|
6639
|
+
};
|
|
6640
|
+
}
|
|
6641
|
+
if (reactivatedPurchase.cancelledAt) {
|
|
6642
|
+
return {
|
|
6643
|
+
error: `Purchase reactivation failed: cancelledAt is still set`,
|
|
6644
|
+
status: 500
|
|
6645
|
+
};
|
|
6646
|
+
}
|
|
6647
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
6648
|
+
return reactivatedPurchase;
|
|
6649
|
+
} catch (error) {
|
|
6650
|
+
if (error instanceof import_core4.SolvaPayError) {
|
|
6651
|
+
const errorMessage = error.message;
|
|
6652
|
+
if (errorMessage.includes("not found")) {
|
|
6653
|
+
return {
|
|
6654
|
+
error: "Purchase not found",
|
|
6655
|
+
status: 404,
|
|
6656
|
+
details: errorMessage
|
|
6657
|
+
};
|
|
6658
|
+
}
|
|
6659
|
+
if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
|
|
6660
|
+
return {
|
|
6661
|
+
error: "Purchase cannot be reactivated",
|
|
6662
|
+
status: 400,
|
|
6663
|
+
details: errorMessage
|
|
6664
|
+
};
|
|
6665
|
+
}
|
|
6666
|
+
return {
|
|
6667
|
+
error: errorMessage,
|
|
6668
|
+
status: 500,
|
|
6669
|
+
details: errorMessage
|
|
6670
|
+
};
|
|
6671
|
+
}
|
|
6672
|
+
return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6675
|
+
|
|
6676
|
+
// src/helpers/activation.ts
|
|
6677
|
+
async function activatePlanCore(request, body, options = {}) {
|
|
6678
|
+
try {
|
|
6679
|
+
if (!body.productRef || !body.planRef) {
|
|
6680
|
+
return {
|
|
6681
|
+
error: "Missing required parameters: productRef and planRef are required",
|
|
6682
|
+
status: 400
|
|
6683
|
+
};
|
|
6684
|
+
}
|
|
6685
|
+
const customerResult = await syncCustomerCore(request, {
|
|
6686
|
+
solvaPay: options.solvaPay,
|
|
6687
|
+
includeEmail: options.includeEmail,
|
|
6688
|
+
includeName: options.includeName
|
|
6689
|
+
});
|
|
6690
|
+
if (isErrorResult(customerResult)) {
|
|
6691
|
+
return customerResult;
|
|
6692
|
+
}
|
|
6693
|
+
const customerRef = customerResult;
|
|
6694
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6695
|
+
return await solvaPay.activatePlan({
|
|
6696
|
+
customerRef,
|
|
6697
|
+
productRef: body.productRef,
|
|
6698
|
+
planRef: body.planRef
|
|
6699
|
+
});
|
|
6700
|
+
} catch (error) {
|
|
6701
|
+
return handleRouteError(error, "Activate plan", "Plan activation failed");
|
|
6702
|
+
}
|
|
6703
|
+
}
|
|
6389
6704
|
|
|
6390
6705
|
// src/helpers/plans.ts
|
|
6391
6706
|
var import_core5 = require("@solvapay/core");
|
|
@@ -6428,6 +6743,89 @@ async function listPlansCore(request) {
|
|
|
6428
6743
|
}
|
|
6429
6744
|
}
|
|
6430
6745
|
|
|
6746
|
+
// src/helpers/purchase.ts
|
|
6747
|
+
async function checkPurchaseCore(request, options = {}) {
|
|
6748
|
+
try {
|
|
6749
|
+
const userResult = await getAuthenticatedUserCore(request, {
|
|
6750
|
+
includeEmail: options.includeEmail,
|
|
6751
|
+
includeName: options.includeName
|
|
6752
|
+
});
|
|
6753
|
+
if (isErrorResult(userResult)) {
|
|
6754
|
+
return userResult;
|
|
6755
|
+
}
|
|
6756
|
+
const { userId, email, name } = userResult;
|
|
6757
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6758
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
6759
|
+
if (cachedCustomerRef) {
|
|
6760
|
+
try {
|
|
6761
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
6762
|
+
if (customer && customer.customerRef) {
|
|
6763
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
6764
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
6765
|
+
(p) => p.status === "active"
|
|
6766
|
+
);
|
|
6767
|
+
return {
|
|
6768
|
+
customerRef: customer.customerRef,
|
|
6769
|
+
email: customer.email,
|
|
6770
|
+
name: customer.name,
|
|
6771
|
+
purchases: filteredPurchases
|
|
6772
|
+
};
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
} catch {
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
try {
|
|
6779
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6780
|
+
email: email || void 0,
|
|
6781
|
+
name: name || void 0
|
|
6782
|
+
});
|
|
6783
|
+
const customer = await solvaPay.getCustomer({ customerRef });
|
|
6784
|
+
const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
|
|
6785
|
+
return {
|
|
6786
|
+
customerRef: customer.customerRef || userId,
|
|
6787
|
+
email: customer.email,
|
|
6788
|
+
name: customer.name,
|
|
6789
|
+
purchases: filteredPurchases
|
|
6790
|
+
};
|
|
6791
|
+
} catch {
|
|
6792
|
+
return {
|
|
6793
|
+
customerRef: userId,
|
|
6794
|
+
purchases: []
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
} catch (error) {
|
|
6798
|
+
return handleRouteError(error, "Check purchase", "Failed to check purchase");
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
6801
|
+
|
|
6802
|
+
// src/helpers/usage.ts
|
|
6803
|
+
async function trackUsageCore(request, body, options = {}) {
|
|
6804
|
+
try {
|
|
6805
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
6806
|
+
if (isErrorResult(userResult)) {
|
|
6807
|
+
return userResult;
|
|
6808
|
+
}
|
|
6809
|
+
const { userId, email, name } = userResult;
|
|
6810
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6811
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6812
|
+
email: email || void 0,
|
|
6813
|
+
name: name || void 0
|
|
6814
|
+
});
|
|
6815
|
+
await solvaPay.trackUsage({
|
|
6816
|
+
customerRef,
|
|
6817
|
+
actionType: body.actionType,
|
|
6818
|
+
units: body.units,
|
|
6819
|
+
productRef: body.productRef,
|
|
6820
|
+
description: body.description,
|
|
6821
|
+
metadata: body.metadata
|
|
6822
|
+
});
|
|
6823
|
+
return { success: true };
|
|
6824
|
+
} catch (error) {
|
|
6825
|
+
return handleRouteError(error, "Track usage", "Track usage failed");
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6431
6829
|
// src/index.ts
|
|
6432
6830
|
function verifyWebhook({
|
|
6433
6831
|
body,
|
|
@@ -6470,18 +6868,22 @@ function verifyWebhook({
|
|
|
6470
6868
|
McpBearerAuthError,
|
|
6471
6869
|
PaywallError,
|
|
6472
6870
|
VIRTUAL_TOOL_DEFINITIONS,
|
|
6871
|
+
activatePlanCore,
|
|
6473
6872
|
buildAuthInfoFromBearer,
|
|
6474
6873
|
cancelPurchaseCore,
|
|
6874
|
+
checkPurchaseCore,
|
|
6475
6875
|
createCheckoutSessionCore,
|
|
6476
6876
|
createCustomerSessionCore,
|
|
6477
6877
|
createMcpOAuthBridge,
|
|
6478
6878
|
createPaymentIntentCore,
|
|
6479
6879
|
createSolvaPay,
|
|
6480
6880
|
createSolvaPayClient,
|
|
6881
|
+
createTopupPaymentIntentCore,
|
|
6481
6882
|
createVirtualTools,
|
|
6482
6883
|
decodeJwtPayload,
|
|
6483
6884
|
extractBearerToken,
|
|
6484
6885
|
getAuthenticatedUserCore,
|
|
6886
|
+
getCustomerBalanceCore,
|
|
6485
6887
|
getCustomerRefFromBearerAuthHeader,
|
|
6486
6888
|
getCustomerRefFromJwtPayload,
|
|
6487
6889
|
getOAuthAuthorizationServerResponse,
|
|
@@ -6490,9 +6892,12 @@ function verifyWebhook({
|
|
|
6490
6892
|
isErrorResult,
|
|
6491
6893
|
jsonSchemaToZodRawShape,
|
|
6492
6894
|
listPlansCore,
|
|
6895
|
+
paywallErrorToClientPayload,
|
|
6493
6896
|
processPaymentIntentCore,
|
|
6897
|
+
reactivatePurchaseCore,
|
|
6494
6898
|
registerVirtualToolsMcpImpl,
|
|
6495
6899
|
syncCustomerCore,
|
|
6900
|
+
trackUsageCore,
|
|
6496
6901
|
verifyWebhook,
|
|
6497
6902
|
withRetry
|
|
6498
6903
|
});
|