@solvapay/server 1.0.6 → 1.0.8-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 +1065 -1746
- package/dist/edge.js +472 -44
- package/dist/index.cjs +563 -53
- package/dist/index.d.cts +1137 -1792
- package/dist/index.d.ts +1137 -1792
- package/dist/index.js +546 -45
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -4314,35 +4314,44 @@ __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,
|
|
4335
|
+
getMerchantCore: () => getMerchantCore,
|
|
4331
4336
|
getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
|
|
4332
4337
|
getOAuthProtectedResourceResponse: () => getOAuthProtectedResourceResponse,
|
|
4338
|
+
getProductCore: () => getProductCore,
|
|
4333
4339
|
handleRouteError: () => handleRouteError,
|
|
4334
4340
|
isErrorResult: () => isErrorResult,
|
|
4335
4341
|
jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
|
|
4336
4342
|
listPlansCore: () => listPlansCore,
|
|
4343
|
+
paywallErrorToClientPayload: () => paywallErrorToClientPayload,
|
|
4337
4344
|
processPaymentIntentCore: () => processPaymentIntentCore,
|
|
4345
|
+
reactivatePurchaseCore: () => reactivatePurchaseCore,
|
|
4338
4346
|
registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
|
|
4339
4347
|
syncCustomerCore: () => syncCustomerCore,
|
|
4348
|
+
trackUsageCore: () => trackUsageCore,
|
|
4340
4349
|
verifyWebhook: () => verifyWebhook,
|
|
4341
4350
|
withRetry: () => withRetry
|
|
4342
4351
|
});
|
|
4343
4352
|
module.exports = __toCommonJS(index_exports);
|
|
4344
4353
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
4345
|
-
var
|
|
4354
|
+
var import_core8 = require("@solvapay/core");
|
|
4346
4355
|
|
|
4347
4356
|
// src/client.ts
|
|
4348
4357
|
var import_core = require("@solvapay/core");
|
|
@@ -4379,12 +4388,10 @@ function createSolvaPayClient(opts) {
|
|
|
4379
4388
|
// POST: /v1/sdk/usages
|
|
4380
4389
|
async trackUsage(params) {
|
|
4381
4390
|
const url = `${base}/v1/sdk/usages`;
|
|
4382
|
-
const { customerRef, ...rest } = params;
|
|
4383
|
-
const body = { ...rest, customerId: customerRef };
|
|
4384
4391
|
const res = await fetch(url, {
|
|
4385
4392
|
method: "POST",
|
|
4386
4393
|
headers,
|
|
4387
|
-
body: JSON.stringify(
|
|
4394
|
+
body: JSON.stringify(params)
|
|
4388
4395
|
});
|
|
4389
4396
|
if (!res.ok) {
|
|
4390
4397
|
const error = await res.text();
|
|
@@ -4452,6 +4459,36 @@ function createSolvaPayClient(opts) {
|
|
|
4452
4459
|
purchases: customer.purchases || []
|
|
4453
4460
|
};
|
|
4454
4461
|
},
|
|
4462
|
+
// GET: /v1/sdk/merchant
|
|
4463
|
+
async getMerchant() {
|
|
4464
|
+
const url = `${base}/v1/sdk/merchant`;
|
|
4465
|
+
const res = await fetch(url, {
|
|
4466
|
+
method: "GET",
|
|
4467
|
+
headers
|
|
4468
|
+
});
|
|
4469
|
+
if (!res.ok) {
|
|
4470
|
+
const error = await res.text();
|
|
4471
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4472
|
+
throw new import_core.SolvaPayError(`Get merchant failed (${res.status}): ${error}`);
|
|
4473
|
+
}
|
|
4474
|
+
return res.json();
|
|
4475
|
+
},
|
|
4476
|
+
// GET: /v1/sdk/products/{productRef}
|
|
4477
|
+
async getProduct(productRef) {
|
|
4478
|
+
const url = `${base}/v1/sdk/products/${encodeURIComponent(productRef)}`;
|
|
4479
|
+
const res = await fetch(url, {
|
|
4480
|
+
method: "GET",
|
|
4481
|
+
headers
|
|
4482
|
+
});
|
|
4483
|
+
if (!res.ok) {
|
|
4484
|
+
const error = await res.text();
|
|
4485
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4486
|
+
throw new import_core.SolvaPayError(`Get product failed (${res.status}): ${error}`);
|
|
4487
|
+
}
|
|
4488
|
+
const result = await res.json();
|
|
4489
|
+
const data = result.data || {};
|
|
4490
|
+
return { ...data, ...result };
|
|
4491
|
+
},
|
|
4455
4492
|
// Product management methods (primarily for integration tests)
|
|
4456
4493
|
// GET: /v1/sdk/products
|
|
4457
4494
|
async listProducts() {
|
|
@@ -4629,7 +4666,7 @@ function createSolvaPayClient(opts) {
|
|
|
4629
4666
|
body: JSON.stringify({
|
|
4630
4667
|
productRef: params.productRef,
|
|
4631
4668
|
planRef: params.planRef,
|
|
4632
|
-
|
|
4669
|
+
customerRef: params.customerRef
|
|
4633
4670
|
})
|
|
4634
4671
|
});
|
|
4635
4672
|
if (!res.ok) {
|
|
@@ -4637,8 +4674,32 @@ function createSolvaPayClient(opts) {
|
|
|
4637
4674
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4638
4675
|
throw new import_core.SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
|
|
4639
4676
|
}
|
|
4640
|
-
|
|
4641
|
-
|
|
4677
|
+
return await res.json();
|
|
4678
|
+
},
|
|
4679
|
+
// POST: /v1/sdk/payment-intents (purpose: credit_topup)
|
|
4680
|
+
async createTopupPaymentIntent(params) {
|
|
4681
|
+
const idempotencyKey = params.idempotencyKey || `topup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
4682
|
+
const url = `${base}/v1/sdk/payment-intents`;
|
|
4683
|
+
const res = await fetch(url, {
|
|
4684
|
+
method: "POST",
|
|
4685
|
+
headers: {
|
|
4686
|
+
...headers,
|
|
4687
|
+
"Idempotency-Key": idempotencyKey
|
|
4688
|
+
},
|
|
4689
|
+
body: JSON.stringify({
|
|
4690
|
+
customerRef: params.customerRef,
|
|
4691
|
+
purpose: "credit_topup",
|
|
4692
|
+
amount: params.amount,
|
|
4693
|
+
currency: params.currency,
|
|
4694
|
+
description: params.description
|
|
4695
|
+
})
|
|
4696
|
+
});
|
|
4697
|
+
if (!res.ok) {
|
|
4698
|
+
const error = await res.text();
|
|
4699
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4700
|
+
throw new import_core.SolvaPayError(`Create topup payment intent failed (${res.status}): ${error}`);
|
|
4701
|
+
}
|
|
4702
|
+
return await res.json();
|
|
4642
4703
|
},
|
|
4643
4704
|
// POST: /v1/sdk/payment-intents/{paymentIntentId}/process
|
|
4644
4705
|
async processPaymentIntent(params) {
|
|
@@ -4715,6 +4776,54 @@ function createSolvaPayClient(opts) {
|
|
|
4715
4776
|
}
|
|
4716
4777
|
return result;
|
|
4717
4778
|
},
|
|
4779
|
+
// POST: /v1/sdk/purchases/{purchaseRef}/reactivate
|
|
4780
|
+
async reactivatePurchase(params) {
|
|
4781
|
+
const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/reactivate`;
|
|
4782
|
+
const res = await fetch(url, {
|
|
4783
|
+
method: "POST",
|
|
4784
|
+
headers
|
|
4785
|
+
});
|
|
4786
|
+
if (!res.ok) {
|
|
4787
|
+
const error = await res.text();
|
|
4788
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4789
|
+
if (res.status === 404) {
|
|
4790
|
+
throw new import_core.SolvaPayError(`Purchase not found: ${error}`);
|
|
4791
|
+
}
|
|
4792
|
+
if (res.status === 400) {
|
|
4793
|
+
throw new import_core.SolvaPayError(
|
|
4794
|
+
`Purchase cannot be reactivated: ${error}`
|
|
4795
|
+
);
|
|
4796
|
+
}
|
|
4797
|
+
throw new import_core.SolvaPayError(`Reactivate purchase failed (${res.status}): ${error}`);
|
|
4798
|
+
}
|
|
4799
|
+
const responseText = await res.text();
|
|
4800
|
+
let responseData;
|
|
4801
|
+
try {
|
|
4802
|
+
responseData = JSON.parse(responseText);
|
|
4803
|
+
} catch (parseError) {
|
|
4804
|
+
log(`\u274C Failed to parse response as JSON: ${parseError}`);
|
|
4805
|
+
throw new import_core.SolvaPayError(
|
|
4806
|
+
`Invalid JSON response from reactivate purchase endpoint: ${responseText.substring(0, 200)}`
|
|
4807
|
+
);
|
|
4808
|
+
}
|
|
4809
|
+
if (!responseData || typeof responseData !== "object") {
|
|
4810
|
+
log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
|
|
4811
|
+
throw new import_core.SolvaPayError(`Invalid response structure from reactivate purchase endpoint`);
|
|
4812
|
+
}
|
|
4813
|
+
let result;
|
|
4814
|
+
if (responseData.purchase && typeof responseData.purchase === "object") {
|
|
4815
|
+
result = responseData.purchase;
|
|
4816
|
+
} else if (responseData.reference) {
|
|
4817
|
+
result = responseData;
|
|
4818
|
+
} else {
|
|
4819
|
+
result = responseData.purchase || responseData;
|
|
4820
|
+
}
|
|
4821
|
+
if (!result || typeof result !== "object") {
|
|
4822
|
+
log(`\u274C Invalid purchase data in response. Full response:`, responseData);
|
|
4823
|
+
throw new import_core.SolvaPayError(`Invalid purchase data in reactivate purchase response`);
|
|
4824
|
+
}
|
|
4825
|
+
return result;
|
|
4826
|
+
},
|
|
4718
4827
|
// POST: /v1/sdk/user-info
|
|
4719
4828
|
async getUserInfo(params) {
|
|
4720
4829
|
const url = `${base}/v1/sdk/user-info`;
|
|
@@ -4730,6 +4839,20 @@ function createSolvaPayClient(opts) {
|
|
|
4730
4839
|
}
|
|
4731
4840
|
return await res.json();
|
|
4732
4841
|
},
|
|
4842
|
+
// GET: /v1/sdk/customers/:customerRef/balance
|
|
4843
|
+
async getCustomerBalance(params) {
|
|
4844
|
+
const url = `${base}/v1/sdk/customers/${params.customerRef}/balance`;
|
|
4845
|
+
const res = await fetch(url, {
|
|
4846
|
+
method: "GET",
|
|
4847
|
+
headers
|
|
4848
|
+
});
|
|
4849
|
+
if (!res.ok) {
|
|
4850
|
+
const error = await res.text();
|
|
4851
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4852
|
+
throw new import_core.SolvaPayError(`Get customer balance failed (${res.status}): ${error}`);
|
|
4853
|
+
}
|
|
4854
|
+
return await res.json();
|
|
4855
|
+
},
|
|
4733
4856
|
// POST: /v1/sdk/checkout-sessions
|
|
4734
4857
|
async createCheckoutSession(params) {
|
|
4735
4858
|
const url = `${base}/v1/sdk/checkout-sessions`;
|
|
@@ -4761,6 +4884,21 @@ function createSolvaPayClient(opts) {
|
|
|
4761
4884
|
}
|
|
4762
4885
|
const result = await res.json();
|
|
4763
4886
|
return result;
|
|
4887
|
+
},
|
|
4888
|
+
// POST: /v1/sdk/activate
|
|
4889
|
+
async activatePlan(params) {
|
|
4890
|
+
const url = `${base}/v1/sdk/activate`;
|
|
4891
|
+
const res = await fetch(url, {
|
|
4892
|
+
method: "POST",
|
|
4893
|
+
headers,
|
|
4894
|
+
body: JSON.stringify(params)
|
|
4895
|
+
});
|
|
4896
|
+
if (!res.ok) {
|
|
4897
|
+
const error = await res.text();
|
|
4898
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4899
|
+
throw new import_core.SolvaPayError(`Activate plan failed (${res.status}): ${error}`);
|
|
4900
|
+
}
|
|
4901
|
+
return await res.json();
|
|
4764
4902
|
}
|
|
4765
4903
|
};
|
|
4766
4904
|
}
|
|
@@ -4916,6 +5054,24 @@ var PaywallError = class extends Error {
|
|
|
4916
5054
|
this.name = "PaywallError";
|
|
4917
5055
|
}
|
|
4918
5056
|
};
|
|
5057
|
+
function paywallErrorToClientPayload(error) {
|
|
5058
|
+
const sc = error.structuredContent;
|
|
5059
|
+
const base = {
|
|
5060
|
+
success: false,
|
|
5061
|
+
error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
|
|
5062
|
+
product: sc.product,
|
|
5063
|
+
checkoutUrl: sc.checkoutUrl,
|
|
5064
|
+
message: sc.message
|
|
5065
|
+
};
|
|
5066
|
+
if (sc.kind === "activation_required") {
|
|
5067
|
+
base.kind = "activation_required";
|
|
5068
|
+
if (sc.plans !== void 0) base.plans = sc.plans;
|
|
5069
|
+
if (sc.balance !== void 0) base.balance = sc.balance;
|
|
5070
|
+
if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
|
|
5071
|
+
if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
|
|
5072
|
+
}
|
|
5073
|
+
return base;
|
|
5074
|
+
}
|
|
4919
5075
|
var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
|
|
4920
5076
|
cacheTTL: 6e4,
|
|
4921
5077
|
// Cache results for 60 seconds (reduces API calls significantly)
|
|
@@ -4954,8 +5110,6 @@ var SolvaPayPaywall = class {
|
|
|
4954
5110
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4955
5111
|
async protect(handler, metadata = {}, getCustomerRef) {
|
|
4956
5112
|
const product = this.resolveProduct(metadata);
|
|
4957
|
-
const configuredPlanRef = metadata.plan?.trim();
|
|
4958
|
-
const usagePlanRef = configuredPlanRef || "unspecified";
|
|
4959
5113
|
const usageType = metadata.usageType || "requests";
|
|
4960
5114
|
return async (args) => {
|
|
4961
5115
|
const startTime = Date.now();
|
|
@@ -4969,12 +5123,13 @@ var SolvaPayPaywall = class {
|
|
|
4969
5123
|
}
|
|
4970
5124
|
let resolvedMeterName;
|
|
4971
5125
|
try {
|
|
4972
|
-
const limitsCacheKey = `${backendCustomerRef}:${product}:${
|
|
5126
|
+
const limitsCacheKey = `${backendCustomerRef}:${product}:${usageType}`;
|
|
4973
5127
|
const cachedLimits = this.limitsCache.get(limitsCacheKey);
|
|
4974
5128
|
const now = Date.now();
|
|
4975
5129
|
let withinLimits;
|
|
4976
5130
|
let remaining;
|
|
4977
5131
|
let checkoutUrl;
|
|
5132
|
+
let lastLimitsCheck;
|
|
4978
5133
|
const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
|
|
4979
5134
|
if (hasFreshCachedLimits) {
|
|
4980
5135
|
checkoutUrl = cachedLimits.checkoutUrl;
|
|
@@ -4998,9 +5153,9 @@ var SolvaPayPaywall = class {
|
|
|
4998
5153
|
const limitsCheck = await this.apiClient.checkLimits({
|
|
4999
5154
|
customerRef: backendCustomerRef,
|
|
5000
5155
|
productRef: product,
|
|
5001
|
-
...configuredPlanRef ? { planRef: configuredPlanRef } : {},
|
|
5002
5156
|
meterName: usageType
|
|
5003
5157
|
});
|
|
5158
|
+
lastLimitsCheck = limitsCheck;
|
|
5004
5159
|
withinLimits = limitsCheck.withinLimits;
|
|
5005
5160
|
remaining = limitsCheck.remaining;
|
|
5006
5161
|
checkoutUrl = limitsCheck.checkoutUrl;
|
|
@@ -5023,12 +5178,25 @@ var SolvaPayPaywall = class {
|
|
|
5023
5178
|
this.trackUsage(
|
|
5024
5179
|
backendCustomerRef,
|
|
5025
5180
|
product,
|
|
5026
|
-
usagePlanRef,
|
|
5027
5181
|
resolvedMeterName || usageType,
|
|
5028
5182
|
"paywall",
|
|
5029
5183
|
requestId,
|
|
5030
5184
|
latencyMs2
|
|
5031
5185
|
);
|
|
5186
|
+
if (lastLimitsCheck?.activationRequired) {
|
|
5187
|
+
const confirmationUrl = lastLimitsCheck.confirmationUrl;
|
|
5188
|
+
const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
|
|
5189
|
+
throw new PaywallError("Activation required", {
|
|
5190
|
+
kind: "activation_required",
|
|
5191
|
+
product,
|
|
5192
|
+
message: "Product activation is required before this tool can be used.",
|
|
5193
|
+
checkoutUrl: payCheckoutUrl,
|
|
5194
|
+
confirmationUrl,
|
|
5195
|
+
plans: lastLimitsCheck.plans,
|
|
5196
|
+
balance: lastLimitsCheck.balance,
|
|
5197
|
+
productDetails: lastLimitsCheck.product
|
|
5198
|
+
});
|
|
5199
|
+
}
|
|
5032
5200
|
throw new PaywallError("Payment required", {
|
|
5033
5201
|
kind: "payment_required",
|
|
5034
5202
|
product,
|
|
@@ -5041,7 +5209,6 @@ var SolvaPayPaywall = class {
|
|
|
5041
5209
|
this.trackUsage(
|
|
5042
5210
|
backendCustomerRef,
|
|
5043
5211
|
product,
|
|
5044
|
-
usagePlanRef,
|
|
5045
5212
|
resolvedMeterName || usageType,
|
|
5046
5213
|
"success",
|
|
5047
5214
|
requestId,
|
|
@@ -5060,7 +5227,6 @@ var SolvaPayPaywall = class {
|
|
|
5060
5227
|
this.trackUsage(
|
|
5061
5228
|
backendCustomerRef,
|
|
5062
5229
|
product,
|
|
5063
|
-
usagePlanRef,
|
|
5064
5230
|
resolvedMeterName || usageType,
|
|
5065
5231
|
"fail",
|
|
5066
5232
|
requestId,
|
|
@@ -5131,7 +5297,8 @@ var SolvaPayPaywall = class {
|
|
|
5131
5297
|
this.customerCreationAttempts.add(customerRef);
|
|
5132
5298
|
try {
|
|
5133
5299
|
const createParams = {
|
|
5134
|
-
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local
|
|
5300
|
+
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5301
|
+
metadata: {}
|
|
5135
5302
|
};
|
|
5136
5303
|
if (options?.name) {
|
|
5137
5304
|
createParams.name = options.name;
|
|
@@ -5155,7 +5322,10 @@ var SolvaPayPaywall = class {
|
|
|
5155
5322
|
return searchResult.customerRef;
|
|
5156
5323
|
}
|
|
5157
5324
|
} catch (lookupError) {
|
|
5158
|
-
this.log(
|
|
5325
|
+
this.log(
|
|
5326
|
+
`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
|
|
5327
|
+
lookupError instanceof Error ? lookupError.message : lookupError
|
|
5328
|
+
);
|
|
5159
5329
|
}
|
|
5160
5330
|
}
|
|
5161
5331
|
const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
|
|
@@ -5178,7 +5348,8 @@ var SolvaPayPaywall = class {
|
|
|
5178
5348
|
try {
|
|
5179
5349
|
const retryParams = {
|
|
5180
5350
|
email: `${customerRef}-${Date.now()}@auto-created.local`,
|
|
5181
|
-
externalRef
|
|
5351
|
+
externalRef,
|
|
5352
|
+
metadata: {}
|
|
5182
5353
|
};
|
|
5183
5354
|
if (options?.name) {
|
|
5184
5355
|
retryParams.name = options.name;
|
|
@@ -5215,14 +5386,14 @@ var SolvaPayPaywall = class {
|
|
|
5215
5386
|
}
|
|
5216
5387
|
return backendRef;
|
|
5217
5388
|
}
|
|
5218
|
-
async trackUsage(customerRef,
|
|
5389
|
+
async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration) {
|
|
5219
5390
|
await withRetry(
|
|
5220
5391
|
() => this.apiClient.trackUsage({
|
|
5221
5392
|
customerRef,
|
|
5222
5393
|
actionType: "api_call",
|
|
5223
5394
|
units: 1,
|
|
5224
5395
|
outcome,
|
|
5225
|
-
|
|
5396
|
+
productRef,
|
|
5226
5397
|
duration: actionDuration,
|
|
5227
5398
|
metadata: { action: action || "api_requests", requestId },
|
|
5228
5399
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5474,8 +5645,10 @@ var McpAdapter = class {
|
|
|
5474
5645
|
const ref = await this.options.getCustomerRef(args, extra);
|
|
5475
5646
|
return AdapterUtils.ensureCustomerRef(ref);
|
|
5476
5647
|
}
|
|
5477
|
-
const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
|
|
5478
|
-
const
|
|
5648
|
+
const customerRefFromExtra = typeof extra?.authInfo?.extra?.customer_ref === "string" ? String(extra.authInfo.extra.customer_ref) : void 0;
|
|
5649
|
+
const customerRefFromArgs = typeof args.auth === "object" && args.auth !== null && typeof args.auth.customer_ref === "string" ? String(args.auth.customer_ref) : void 0;
|
|
5650
|
+
const directCustomerRef = typeof args.customer_ref === "string" ? args.customer_ref : void 0;
|
|
5651
|
+
const customerRef = (customerRefFromExtra || customerRefFromArgs || directCustomerRef || "anonymous").trim();
|
|
5479
5652
|
return AdapterUtils.ensureCustomerRef(customerRef);
|
|
5480
5653
|
}
|
|
5481
5654
|
formatResponse(result, _context) {
|
|
@@ -5499,17 +5672,7 @@ var McpAdapter = class {
|
|
|
5499
5672
|
content: [
|
|
5500
5673
|
{
|
|
5501
5674
|
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
|
-
)
|
|
5675
|
+
text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
|
|
5513
5676
|
}
|
|
5514
5677
|
],
|
|
5515
5678
|
isError: true,
|
|
@@ -5796,6 +5959,12 @@ function createSolvaPay(config) {
|
|
|
5796
5959
|
}
|
|
5797
5960
|
return apiClient.createPaymentIntent(params);
|
|
5798
5961
|
},
|
|
5962
|
+
createTopupPaymentIntent(params) {
|
|
5963
|
+
if (!apiClient.createTopupPaymentIntent) {
|
|
5964
|
+
throw new import_core2.SolvaPayError("createTopupPaymentIntent is not available on this API client");
|
|
5965
|
+
}
|
|
5966
|
+
return apiClient.createTopupPaymentIntent(params);
|
|
5967
|
+
},
|
|
5799
5968
|
processPaymentIntent(params) {
|
|
5800
5969
|
if (!apiClient.processPaymentIntent) {
|
|
5801
5970
|
throw new import_core2.SolvaPayError("processPaymentIntent is not available on this API client");
|
|
@@ -5812,11 +5981,17 @@ function createSolvaPay(config) {
|
|
|
5812
5981
|
if (!apiClient.createCustomer) {
|
|
5813
5982
|
throw new import_core2.SolvaPayError("createCustomer is not available on this API client");
|
|
5814
5983
|
}
|
|
5815
|
-
return apiClient.createCustomer(params);
|
|
5984
|
+
return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
|
|
5816
5985
|
},
|
|
5817
5986
|
getCustomer(params) {
|
|
5818
5987
|
return apiClient.getCustomer(params);
|
|
5819
5988
|
},
|
|
5989
|
+
getCustomerBalance(params) {
|
|
5990
|
+
if (!apiClient.getCustomerBalance) {
|
|
5991
|
+
throw new import_core2.SolvaPayError("getCustomerBalance is not available on this API client");
|
|
5992
|
+
}
|
|
5993
|
+
return apiClient.getCustomerBalance(params);
|
|
5994
|
+
},
|
|
5820
5995
|
createCheckoutSession(params) {
|
|
5821
5996
|
return apiClient.createCheckoutSession({
|
|
5822
5997
|
customerRef: params.customerRef,
|
|
@@ -5827,6 +6002,12 @@ function createSolvaPay(config) {
|
|
|
5827
6002
|
createCustomerSession(params) {
|
|
5828
6003
|
return apiClient.createCustomerSession(params);
|
|
5829
6004
|
},
|
|
6005
|
+
activatePlan(params) {
|
|
6006
|
+
if (!apiClient.activatePlan) {
|
|
6007
|
+
throw new import_core2.SolvaPayError("activatePlan is not available on this API client");
|
|
6008
|
+
}
|
|
6009
|
+
return apiClient.activatePlan(params);
|
|
6010
|
+
},
|
|
5830
6011
|
bootstrapMcpProduct(params) {
|
|
5831
6012
|
if (!apiClient.bootstrapMcpProduct) {
|
|
5832
6013
|
throw new import_core2.SolvaPayError("bootstrapMcpProduct is not available on this API client");
|
|
@@ -5848,9 +6029,8 @@ function createSolvaPay(config) {
|
|
|
5848
6029
|
// Payable API for framework-specific handlers
|
|
5849
6030
|
payable(options = {}) {
|
|
5850
6031
|
const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
5851
|
-
const plan = options.planRef || options.plan;
|
|
5852
6032
|
const usageType = options.usageType || "requests";
|
|
5853
|
-
const metadata = { product,
|
|
6033
|
+
const metadata = { product, usageType };
|
|
5854
6034
|
return {
|
|
5855
6035
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5856
6036
|
http(businessLogic, adapterOptions) {
|
|
@@ -5914,7 +6094,8 @@ var McpBearerAuthError = class extends Error {
|
|
|
5914
6094
|
function base64UrlDecode(input) {
|
|
5915
6095
|
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
5916
6096
|
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
5917
|
-
|
|
6097
|
+
const bytes = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
6098
|
+
return new TextDecoder().decode(bytes);
|
|
5918
6099
|
}
|
|
5919
6100
|
function extractBearerToken(authorization) {
|
|
5920
6101
|
if (!authorization) return null;
|
|
@@ -6188,6 +6369,24 @@ async function syncCustomerCore(request, options = {}) {
|
|
|
6188
6369
|
return handleRouteError(error, "Sync customer", "Failed to sync customer");
|
|
6189
6370
|
}
|
|
6190
6371
|
}
|
|
6372
|
+
async function getCustomerBalanceCore(request, options = {}) {
|
|
6373
|
+
try {
|
|
6374
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
6375
|
+
if (isErrorResult(userResult)) {
|
|
6376
|
+
return userResult;
|
|
6377
|
+
}
|
|
6378
|
+
const { userId, email, name } = userResult;
|
|
6379
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6380
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6381
|
+
email: email || void 0,
|
|
6382
|
+
name: name || void 0
|
|
6383
|
+
});
|
|
6384
|
+
const result = await solvaPay.getCustomerBalance({ customerRef });
|
|
6385
|
+
return result;
|
|
6386
|
+
} catch (error) {
|
|
6387
|
+
return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
|
|
6388
|
+
}
|
|
6389
|
+
}
|
|
6191
6390
|
|
|
6192
6391
|
// src/helpers/payment.ts
|
|
6193
6392
|
async function createPaymentIntentCore(request, body, options = {}) {
|
|
@@ -6214,7 +6413,7 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
6214
6413
|
customerRef
|
|
6215
6414
|
});
|
|
6216
6415
|
return {
|
|
6217
|
-
|
|
6416
|
+
processorPaymentId: paymentIntent.processorPaymentId,
|
|
6218
6417
|
clientSecret: paymentIntent.clientSecret,
|
|
6219
6418
|
publishableKey: paymentIntent.publishableKey,
|
|
6220
6419
|
accountId: paymentIntent.accountId,
|
|
@@ -6224,6 +6423,57 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
6224
6423
|
return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
|
|
6225
6424
|
}
|
|
6226
6425
|
}
|
|
6426
|
+
async function createTopupPaymentIntentCore(request, body, options = {}) {
|
|
6427
|
+
try {
|
|
6428
|
+
if (!body.amount || body.amount <= 0) {
|
|
6429
|
+
return {
|
|
6430
|
+
error: "Missing or invalid amount: must be a positive number",
|
|
6431
|
+
status: 400
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
if (!body.currency) {
|
|
6435
|
+
return {
|
|
6436
|
+
error: "Missing required parameter: currency",
|
|
6437
|
+
status: 400
|
|
6438
|
+
};
|
|
6439
|
+
}
|
|
6440
|
+
if (body.currency !== body.currency.toUpperCase()) {
|
|
6441
|
+
return {
|
|
6442
|
+
error: `Invalid currency "${body.currency}": must be an uppercase ISO 4217 code (e.g. "USD", "EUR")`,
|
|
6443
|
+
status: 400
|
|
6444
|
+
};
|
|
6445
|
+
}
|
|
6446
|
+
const customerResult = await syncCustomerCore(request, {
|
|
6447
|
+
solvaPay: options.solvaPay,
|
|
6448
|
+
includeEmail: options.includeEmail,
|
|
6449
|
+
includeName: options.includeName
|
|
6450
|
+
});
|
|
6451
|
+
if (isErrorResult(customerResult)) {
|
|
6452
|
+
return customerResult;
|
|
6453
|
+
}
|
|
6454
|
+
const customerRef = customerResult;
|
|
6455
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6456
|
+
const paymentIntent = await solvaPay.createTopupPaymentIntent({
|
|
6457
|
+
customerRef,
|
|
6458
|
+
amount: body.amount,
|
|
6459
|
+
currency: body.currency,
|
|
6460
|
+
description: body.description
|
|
6461
|
+
});
|
|
6462
|
+
return {
|
|
6463
|
+
processorPaymentId: paymentIntent.processorPaymentId,
|
|
6464
|
+
clientSecret: paymentIntent.clientSecret,
|
|
6465
|
+
publishableKey: paymentIntent.publishableKey,
|
|
6466
|
+
accountId: paymentIntent.accountId,
|
|
6467
|
+
customerRef
|
|
6468
|
+
};
|
|
6469
|
+
} catch (error) {
|
|
6470
|
+
return handleRouteError(
|
|
6471
|
+
error,
|
|
6472
|
+
"Create topup payment intent",
|
|
6473
|
+
"Topup payment intent creation failed"
|
|
6474
|
+
);
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6227
6477
|
async function processPaymentIntentCore(request, body, options = {}) {
|
|
6228
6478
|
try {
|
|
6229
6479
|
if (!body.paymentIntentId || !body.productRef) {
|
|
@@ -6386,10 +6636,107 @@ async function cancelPurchaseCore(request, body, options = {}) {
|
|
|
6386
6636
|
return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
|
|
6387
6637
|
}
|
|
6388
6638
|
}
|
|
6639
|
+
async function reactivatePurchaseCore(request, body, options = {}) {
|
|
6640
|
+
try {
|
|
6641
|
+
if (!body.purchaseRef) {
|
|
6642
|
+
return {
|
|
6643
|
+
error: "Missing required parameter: purchaseRef is required",
|
|
6644
|
+
status: 400
|
|
6645
|
+
};
|
|
6646
|
+
}
|
|
6647
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6648
|
+
if (!solvaPay.apiClient.reactivatePurchase) {
|
|
6649
|
+
return {
|
|
6650
|
+
error: "Reactivate purchase method not available on SDK client",
|
|
6651
|
+
status: 500
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
|
|
6655
|
+
purchaseRef: body.purchaseRef
|
|
6656
|
+
});
|
|
6657
|
+
if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
|
|
6658
|
+
return {
|
|
6659
|
+
error: "Invalid response from reactivate purchase endpoint",
|
|
6660
|
+
status: 500
|
|
6661
|
+
};
|
|
6662
|
+
}
|
|
6663
|
+
const responseObj = reactivatedPurchase;
|
|
6664
|
+
if (responseObj.purchase && typeof responseObj.purchase === "object") {
|
|
6665
|
+
reactivatedPurchase = responseObj.purchase;
|
|
6666
|
+
}
|
|
6667
|
+
if (!reactivatedPurchase.reference) {
|
|
6668
|
+
return {
|
|
6669
|
+
error: "Reactivate purchase response missing required fields",
|
|
6670
|
+
status: 500
|
|
6671
|
+
};
|
|
6672
|
+
}
|
|
6673
|
+
if (reactivatedPurchase.cancelledAt) {
|
|
6674
|
+
return {
|
|
6675
|
+
error: `Purchase reactivation failed: cancelledAt is still set`,
|
|
6676
|
+
status: 500
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
6680
|
+
return reactivatedPurchase;
|
|
6681
|
+
} catch (error) {
|
|
6682
|
+
if (error instanceof import_core4.SolvaPayError) {
|
|
6683
|
+
const errorMessage = error.message;
|
|
6684
|
+
if (errorMessage.includes("not found")) {
|
|
6685
|
+
return {
|
|
6686
|
+
error: "Purchase not found",
|
|
6687
|
+
status: 404,
|
|
6688
|
+
details: errorMessage
|
|
6689
|
+
};
|
|
6690
|
+
}
|
|
6691
|
+
if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
|
|
6692
|
+
return {
|
|
6693
|
+
error: "Purchase cannot be reactivated",
|
|
6694
|
+
status: 400,
|
|
6695
|
+
details: errorMessage
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6698
|
+
return {
|
|
6699
|
+
error: errorMessage,
|
|
6700
|
+
status: 500,
|
|
6701
|
+
details: errorMessage
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
|
|
6708
|
+
// src/helpers/activation.ts
|
|
6709
|
+
async function activatePlanCore(request, body, options = {}) {
|
|
6710
|
+
try {
|
|
6711
|
+
if (!body.productRef || !body.planRef) {
|
|
6712
|
+
return {
|
|
6713
|
+
error: "Missing required parameters: productRef and planRef are required",
|
|
6714
|
+
status: 400
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6717
|
+
const customerResult = await syncCustomerCore(request, {
|
|
6718
|
+
solvaPay: options.solvaPay,
|
|
6719
|
+
includeEmail: options.includeEmail,
|
|
6720
|
+
includeName: options.includeName
|
|
6721
|
+
});
|
|
6722
|
+
if (isErrorResult(customerResult)) {
|
|
6723
|
+
return customerResult;
|
|
6724
|
+
}
|
|
6725
|
+
const customerRef = customerResult;
|
|
6726
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6727
|
+
return await solvaPay.activatePlan({
|
|
6728
|
+
customerRef,
|
|
6729
|
+
productRef: body.productRef,
|
|
6730
|
+
planRef: body.planRef
|
|
6731
|
+
});
|
|
6732
|
+
} catch (error) {
|
|
6733
|
+
return handleRouteError(error, "Activate plan", "Plan activation failed");
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6389
6736
|
|
|
6390
6737
|
// src/helpers/plans.ts
|
|
6391
6738
|
var import_core5 = require("@solvapay/core");
|
|
6392
|
-
async function listPlansCore(request) {
|
|
6739
|
+
async function listPlansCore(request, options = {}) {
|
|
6393
6740
|
try {
|
|
6394
6741
|
const url = new URL(request.url);
|
|
6395
6742
|
const productRef = url.searchParams.get("productRef");
|
|
@@ -6399,19 +6746,20 @@ async function listPlansCore(request) {
|
|
|
6399
6746
|
status: 400
|
|
6400
6747
|
};
|
|
6401
6748
|
}
|
|
6402
|
-
const
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6749
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6750
|
+
const config = (0, import_core5.getSolvaPayConfig)();
|
|
6751
|
+
if (!config.apiKey) return null;
|
|
6752
|
+
return createSolvaPayClient({
|
|
6753
|
+
apiKey: config.apiKey,
|
|
6754
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6755
|
+
});
|
|
6756
|
+
})();
|
|
6757
|
+
if (!apiClient) {
|
|
6406
6758
|
return {
|
|
6407
6759
|
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6408
6760
|
status: 500
|
|
6409
6761
|
};
|
|
6410
6762
|
}
|
|
6411
|
-
const apiClient = createSolvaPayClient({
|
|
6412
|
-
apiKey: solvapaySecretKey,
|
|
6413
|
-
apiBaseUrl: solvapayApiBaseUrl
|
|
6414
|
-
});
|
|
6415
6763
|
if (!apiClient.listPlans) {
|
|
6416
6764
|
return {
|
|
6417
6765
|
error: "List plans method not available",
|
|
@@ -6428,6 +6776,159 @@ async function listPlansCore(request) {
|
|
|
6428
6776
|
}
|
|
6429
6777
|
}
|
|
6430
6778
|
|
|
6779
|
+
// src/helpers/merchant.ts
|
|
6780
|
+
var import_core6 = require("@solvapay/core");
|
|
6781
|
+
async function getMerchantCore(_request, options = {}) {
|
|
6782
|
+
try {
|
|
6783
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6784
|
+
const config = (0, import_core6.getSolvaPayConfig)();
|
|
6785
|
+
if (!config.apiKey) return null;
|
|
6786
|
+
return createSolvaPayClient({
|
|
6787
|
+
apiKey: config.apiKey,
|
|
6788
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6789
|
+
});
|
|
6790
|
+
})();
|
|
6791
|
+
if (!apiClient) {
|
|
6792
|
+
return {
|
|
6793
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6794
|
+
status: 500
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
if (!apiClient.getMerchant) {
|
|
6798
|
+
return {
|
|
6799
|
+
error: "Get merchant method not available",
|
|
6800
|
+
status: 500
|
|
6801
|
+
};
|
|
6802
|
+
}
|
|
6803
|
+
const merchant = await apiClient.getMerchant();
|
|
6804
|
+
return merchant;
|
|
6805
|
+
} catch (error) {
|
|
6806
|
+
return handleRouteError(error, "Get merchant", "Failed to fetch merchant");
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
|
|
6810
|
+
// src/helpers/product.ts
|
|
6811
|
+
var import_core7 = require("@solvapay/core");
|
|
6812
|
+
async function getProductCore(request, options = {}) {
|
|
6813
|
+
try {
|
|
6814
|
+
const url = new URL(request.url);
|
|
6815
|
+
const productRef = url.searchParams.get("productRef");
|
|
6816
|
+
if (!productRef) {
|
|
6817
|
+
return {
|
|
6818
|
+
error: "Missing required parameter: productRef",
|
|
6819
|
+
status: 400
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6823
|
+
const config = (0, import_core7.getSolvaPayConfig)();
|
|
6824
|
+
if (!config.apiKey) return null;
|
|
6825
|
+
return createSolvaPayClient({
|
|
6826
|
+
apiKey: config.apiKey,
|
|
6827
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6828
|
+
});
|
|
6829
|
+
})();
|
|
6830
|
+
if (!apiClient) {
|
|
6831
|
+
return {
|
|
6832
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6833
|
+
status: 500
|
|
6834
|
+
};
|
|
6835
|
+
}
|
|
6836
|
+
if (!apiClient.getProduct) {
|
|
6837
|
+
return {
|
|
6838
|
+
error: "Get product method not available",
|
|
6839
|
+
status: 500
|
|
6840
|
+
};
|
|
6841
|
+
}
|
|
6842
|
+
const product = await apiClient.getProduct(productRef);
|
|
6843
|
+
return product;
|
|
6844
|
+
} catch (error) {
|
|
6845
|
+
return handleRouteError(error, "Get product", "Failed to fetch product");
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
|
|
6849
|
+
// src/helpers/purchase.ts
|
|
6850
|
+
async function checkPurchaseCore(request, options = {}) {
|
|
6851
|
+
try {
|
|
6852
|
+
const userResult = await getAuthenticatedUserCore(request, {
|
|
6853
|
+
includeEmail: options.includeEmail,
|
|
6854
|
+
includeName: options.includeName
|
|
6855
|
+
});
|
|
6856
|
+
if (isErrorResult(userResult)) {
|
|
6857
|
+
return userResult;
|
|
6858
|
+
}
|
|
6859
|
+
const { userId, email, name } = userResult;
|
|
6860
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6861
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
6862
|
+
if (cachedCustomerRef) {
|
|
6863
|
+
try {
|
|
6864
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
6865
|
+
if (customer && customer.customerRef) {
|
|
6866
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
6867
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
6868
|
+
(p) => p.status === "active"
|
|
6869
|
+
);
|
|
6870
|
+
return {
|
|
6871
|
+
customerRef: customer.customerRef,
|
|
6872
|
+
email: customer.email,
|
|
6873
|
+
name: customer.name,
|
|
6874
|
+
purchases: filteredPurchases
|
|
6875
|
+
};
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
} catch {
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
try {
|
|
6882
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6883
|
+
email: email || void 0,
|
|
6884
|
+
name: name || void 0
|
|
6885
|
+
});
|
|
6886
|
+
const customer = await solvaPay.getCustomer({ customerRef });
|
|
6887
|
+
const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
|
|
6888
|
+
return {
|
|
6889
|
+
customerRef: customer.customerRef || userId,
|
|
6890
|
+
email: customer.email,
|
|
6891
|
+
name: customer.name,
|
|
6892
|
+
purchases: filteredPurchases
|
|
6893
|
+
};
|
|
6894
|
+
} catch {
|
|
6895
|
+
return {
|
|
6896
|
+
customerRef: userId,
|
|
6897
|
+
purchases: []
|
|
6898
|
+
};
|
|
6899
|
+
}
|
|
6900
|
+
} catch (error) {
|
|
6901
|
+
return handleRouteError(error, "Check purchase", "Failed to check purchase");
|
|
6902
|
+
}
|
|
6903
|
+
}
|
|
6904
|
+
|
|
6905
|
+
// src/helpers/usage.ts
|
|
6906
|
+
async function trackUsageCore(request, body, options = {}) {
|
|
6907
|
+
try {
|
|
6908
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
6909
|
+
if (isErrorResult(userResult)) {
|
|
6910
|
+
return userResult;
|
|
6911
|
+
}
|
|
6912
|
+
const { userId, email, name } = userResult;
|
|
6913
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6914
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6915
|
+
email: email || void 0,
|
|
6916
|
+
name: name || void 0
|
|
6917
|
+
});
|
|
6918
|
+
await solvaPay.trackUsage({
|
|
6919
|
+
customerRef,
|
|
6920
|
+
actionType: body.actionType,
|
|
6921
|
+
units: body.units,
|
|
6922
|
+
productRef: body.productRef,
|
|
6923
|
+
description: body.description,
|
|
6924
|
+
metadata: body.metadata
|
|
6925
|
+
});
|
|
6926
|
+
return { success: true };
|
|
6927
|
+
} catch (error) {
|
|
6928
|
+
return handleRouteError(error, "Track usage", "Track usage failed");
|
|
6929
|
+
}
|
|
6930
|
+
}
|
|
6931
|
+
|
|
6431
6932
|
// src/index.ts
|
|
6432
6933
|
function verifyWebhook({
|
|
6433
6934
|
body,
|
|
@@ -6435,34 +6936,34 @@ function verifyWebhook({
|
|
|
6435
6936
|
secret
|
|
6436
6937
|
}) {
|
|
6437
6938
|
const toleranceSec = 300;
|
|
6438
|
-
if (!signature) throw new
|
|
6939
|
+
if (!signature) throw new import_core8.SolvaPayError("Missing webhook signature");
|
|
6439
6940
|
const parts = signature.split(",");
|
|
6440
6941
|
const tPart = parts.find((p) => p.startsWith("t="));
|
|
6441
6942
|
const v1Part = parts.find((p) => p.startsWith("v1="));
|
|
6442
6943
|
if (!tPart || !v1Part) {
|
|
6443
|
-
throw new
|
|
6944
|
+
throw new import_core8.SolvaPayError("Malformed webhook signature");
|
|
6444
6945
|
}
|
|
6445
6946
|
const timestamp = parseInt(tPart.slice(2), 10);
|
|
6446
6947
|
const receivedHmac = v1Part.slice(3);
|
|
6447
6948
|
if (Number.isNaN(timestamp) || !receivedHmac) {
|
|
6448
|
-
throw new
|
|
6949
|
+
throw new import_core8.SolvaPayError("Malformed webhook signature");
|
|
6449
6950
|
}
|
|
6450
6951
|
if (toleranceSec > 0) {
|
|
6451
6952
|
const age = Math.abs(Math.floor(Date.now() / 1e3) - timestamp);
|
|
6452
6953
|
if (age > toleranceSec) {
|
|
6453
|
-
throw new
|
|
6954
|
+
throw new import_core8.SolvaPayError("Webhook signature timestamp too old");
|
|
6454
6955
|
}
|
|
6455
6956
|
}
|
|
6456
6957
|
const expectedHmac = import_node_crypto.default.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
|
|
6457
6958
|
if (receivedHmac.length !== expectedHmac.length) {
|
|
6458
|
-
throw new
|
|
6959
|
+
throw new import_core8.SolvaPayError("Invalid webhook signature");
|
|
6459
6960
|
}
|
|
6460
6961
|
const ok = import_node_crypto.default.timingSafeEqual(Buffer.from(expectedHmac), Buffer.from(receivedHmac));
|
|
6461
|
-
if (!ok) throw new
|
|
6962
|
+
if (!ok) throw new import_core8.SolvaPayError("Invalid webhook signature");
|
|
6462
6963
|
try {
|
|
6463
6964
|
return JSON.parse(body);
|
|
6464
6965
|
} catch {
|
|
6465
|
-
throw new
|
|
6966
|
+
throw new import_core8.SolvaPayError("Invalid webhook payload: body is not valid JSON");
|
|
6466
6967
|
}
|
|
6467
6968
|
}
|
|
6468
6969
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -6470,29 +6971,38 @@ function verifyWebhook({
|
|
|
6470
6971
|
McpBearerAuthError,
|
|
6471
6972
|
PaywallError,
|
|
6472
6973
|
VIRTUAL_TOOL_DEFINITIONS,
|
|
6974
|
+
activatePlanCore,
|
|
6473
6975
|
buildAuthInfoFromBearer,
|
|
6474
6976
|
cancelPurchaseCore,
|
|
6977
|
+
checkPurchaseCore,
|
|
6475
6978
|
createCheckoutSessionCore,
|
|
6476
6979
|
createCustomerSessionCore,
|
|
6477
6980
|
createMcpOAuthBridge,
|
|
6478
6981
|
createPaymentIntentCore,
|
|
6479
6982
|
createSolvaPay,
|
|
6480
6983
|
createSolvaPayClient,
|
|
6984
|
+
createTopupPaymentIntentCore,
|
|
6481
6985
|
createVirtualTools,
|
|
6482
6986
|
decodeJwtPayload,
|
|
6483
6987
|
extractBearerToken,
|
|
6484
6988
|
getAuthenticatedUserCore,
|
|
6989
|
+
getCustomerBalanceCore,
|
|
6485
6990
|
getCustomerRefFromBearerAuthHeader,
|
|
6486
6991
|
getCustomerRefFromJwtPayload,
|
|
6992
|
+
getMerchantCore,
|
|
6487
6993
|
getOAuthAuthorizationServerResponse,
|
|
6488
6994
|
getOAuthProtectedResourceResponse,
|
|
6995
|
+
getProductCore,
|
|
6489
6996
|
handleRouteError,
|
|
6490
6997
|
isErrorResult,
|
|
6491
6998
|
jsonSchemaToZodRawShape,
|
|
6492
6999
|
listPlansCore,
|
|
7000
|
+
paywallErrorToClientPayload,
|
|
6493
7001
|
processPaymentIntentCore,
|
|
7002
|
+
reactivatePurchaseCore,
|
|
6494
7003
|
registerVirtualToolsMcpImpl,
|
|
6495
7004
|
syncCustomerCore,
|
|
7005
|
+
trackUsageCore,
|
|
6496
7006
|
verifyWebhook,
|
|
6497
7007
|
withRetry
|
|
6498
7008
|
});
|