@solvapay/server 1.0.5 → 1.0.7

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/index.cjs CHANGED
@@ -4314,6 +4314,7 @@ __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,
4319
4320
  createCheckoutSessionCore: () => createCheckoutSessionCore,
@@ -4322,10 +4323,12 @@ __export(index_exports, {
4322
4323
  createPaymentIntentCore: () => createPaymentIntentCore,
4323
4324
  createSolvaPay: () => createSolvaPay,
4324
4325
  createSolvaPayClient: () => createSolvaPayClient,
4326
+ createTopupPaymentIntentCore: () => createTopupPaymentIntentCore,
4325
4327
  createVirtualTools: () => createVirtualTools,
4326
4328
  decodeJwtPayload: () => decodeJwtPayload,
4327
4329
  extractBearerToken: () => extractBearerToken,
4328
4330
  getAuthenticatedUserCore: () => getAuthenticatedUserCore,
4331
+ getCustomerBalanceCore: () => getCustomerBalanceCore,
4329
4332
  getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
4330
4333
  getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
4331
4334
  getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
@@ -4334,7 +4337,9 @@ __export(index_exports, {
4334
4337
  isErrorResult: () => isErrorResult,
4335
4338
  jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
4336
4339
  listPlansCore: () => listPlansCore,
4340
+ paywallErrorToClientPayload: () => paywallErrorToClientPayload,
4337
4341
  processPaymentIntentCore: () => processPaymentIntentCore,
4342
+ reactivatePurchaseCore: () => reactivatePurchaseCore,
4338
4343
  registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
4339
4344
  syncCustomerCore: () => syncCustomerCore,
4340
4345
  verifyWebhook: () => verifyWebhook,
@@ -4379,12 +4384,10 @@ function createSolvaPayClient(opts) {
4379
4384
  // POST: /v1/sdk/usages
4380
4385
  async trackUsage(params) {
4381
4386
  const url = `${base}/v1/sdk/usages`;
4382
- const { customerRef, ...rest } = params;
4383
- const body = { ...rest, customerId: customerRef };
4384
4387
  const res = await fetch(url, {
4385
4388
  method: "POST",
4386
4389
  headers,
4387
- body: JSON.stringify(body)
4390
+ body: JSON.stringify(params)
4388
4391
  });
4389
4392
  if (!res.ok) {
4390
4393
  const error = await res.text();
@@ -4629,7 +4632,7 @@ function createSolvaPayClient(opts) {
4629
4632
  body: JSON.stringify({
4630
4633
  productRef: params.productRef,
4631
4634
  planRef: params.planRef,
4632
- customerReference: params.customerRef
4635
+ customerRef: params.customerRef
4633
4636
  })
4634
4637
  });
4635
4638
  if (!res.ok) {
@@ -4637,8 +4640,32 @@ function createSolvaPayClient(opts) {
4637
4640
  log(`\u274C API Error: ${res.status} - ${error}`);
4638
4641
  throw new import_core.SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
4639
4642
  }
4640
- const result = await res.json();
4641
- return result;
4643
+ return await res.json();
4644
+ },
4645
+ // POST: /v1/sdk/payment-intents (purpose: credit_topup)
4646
+ async createTopupPaymentIntent(params) {
4647
+ const idempotencyKey = params.idempotencyKey || `topup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
4648
+ const url = `${base}/v1/sdk/payment-intents`;
4649
+ const res = await fetch(url, {
4650
+ method: "POST",
4651
+ headers: {
4652
+ ...headers,
4653
+ "Idempotency-Key": idempotencyKey
4654
+ },
4655
+ body: JSON.stringify({
4656
+ customerRef: params.customerRef,
4657
+ purpose: "credit_topup",
4658
+ amount: params.amount,
4659
+ currency: params.currency,
4660
+ description: params.description
4661
+ })
4662
+ });
4663
+ if (!res.ok) {
4664
+ const error = await res.text();
4665
+ log(`\u274C API Error: ${res.status} - ${error}`);
4666
+ throw new import_core.SolvaPayError(`Create topup payment intent failed (${res.status}): ${error}`);
4667
+ }
4668
+ return await res.json();
4642
4669
  },
4643
4670
  // POST: /v1/sdk/payment-intents/{paymentIntentId}/process
4644
4671
  async processPaymentIntent(params) {
@@ -4715,6 +4742,54 @@ function createSolvaPayClient(opts) {
4715
4742
  }
4716
4743
  return result;
4717
4744
  },
4745
+ // POST: /v1/sdk/purchases/{purchaseRef}/reactivate
4746
+ async reactivatePurchase(params) {
4747
+ const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/reactivate`;
4748
+ const res = await fetch(url, {
4749
+ method: "POST",
4750
+ headers
4751
+ });
4752
+ if (!res.ok) {
4753
+ const error = await res.text();
4754
+ log(`\u274C API Error: ${res.status} - ${error}`);
4755
+ if (res.status === 404) {
4756
+ throw new import_core.SolvaPayError(`Purchase not found: ${error}`);
4757
+ }
4758
+ if (res.status === 400) {
4759
+ throw new import_core.SolvaPayError(
4760
+ `Purchase cannot be reactivated: ${error}`
4761
+ );
4762
+ }
4763
+ throw new import_core.SolvaPayError(`Reactivate purchase failed (${res.status}): ${error}`);
4764
+ }
4765
+ const responseText = await res.text();
4766
+ let responseData;
4767
+ try {
4768
+ responseData = JSON.parse(responseText);
4769
+ } catch (parseError) {
4770
+ log(`\u274C Failed to parse response as JSON: ${parseError}`);
4771
+ throw new import_core.SolvaPayError(
4772
+ `Invalid JSON response from reactivate purchase endpoint: ${responseText.substring(0, 200)}`
4773
+ );
4774
+ }
4775
+ if (!responseData || typeof responseData !== "object") {
4776
+ log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
4777
+ throw new import_core.SolvaPayError(`Invalid response structure from reactivate purchase endpoint`);
4778
+ }
4779
+ let result;
4780
+ if (responseData.purchase && typeof responseData.purchase === "object") {
4781
+ result = responseData.purchase;
4782
+ } else if (responseData.reference) {
4783
+ result = responseData;
4784
+ } else {
4785
+ result = responseData.purchase || responseData;
4786
+ }
4787
+ if (!result || typeof result !== "object") {
4788
+ log(`\u274C Invalid purchase data in response. Full response:`, responseData);
4789
+ throw new import_core.SolvaPayError(`Invalid purchase data in reactivate purchase response`);
4790
+ }
4791
+ return result;
4792
+ },
4718
4793
  // POST: /v1/sdk/user-info
4719
4794
  async getUserInfo(params) {
4720
4795
  const url = `${base}/v1/sdk/user-info`;
@@ -4730,6 +4805,20 @@ function createSolvaPayClient(opts) {
4730
4805
  }
4731
4806
  return await res.json();
4732
4807
  },
4808
+ // GET: /v1/sdk/customers/:customerRef/balance
4809
+ async getCustomerBalance(params) {
4810
+ const url = `${base}/v1/sdk/customers/${params.customerRef}/balance`;
4811
+ const res = await fetch(url, {
4812
+ method: "GET",
4813
+ headers
4814
+ });
4815
+ if (!res.ok) {
4816
+ const error = await res.text();
4817
+ log(`\u274C API Error: ${res.status} - ${error}`);
4818
+ throw new import_core.SolvaPayError(`Get customer balance failed (${res.status}): ${error}`);
4819
+ }
4820
+ return await res.json();
4821
+ },
4733
4822
  // POST: /v1/sdk/checkout-sessions
4734
4823
  async createCheckoutSession(params) {
4735
4824
  const url = `${base}/v1/sdk/checkout-sessions`;
@@ -4761,6 +4850,21 @@ function createSolvaPayClient(opts) {
4761
4850
  }
4762
4851
  const result = await res.json();
4763
4852
  return result;
4853
+ },
4854
+ // POST: /v1/sdk/activate
4855
+ async activatePlan(params) {
4856
+ const url = `${base}/v1/sdk/activate`;
4857
+ const res = await fetch(url, {
4858
+ method: "POST",
4859
+ headers,
4860
+ body: JSON.stringify(params)
4861
+ });
4862
+ if (!res.ok) {
4863
+ const error = await res.text();
4864
+ log(`\u274C API Error: ${res.status} - ${error}`);
4865
+ throw new import_core.SolvaPayError(`Activate plan failed (${res.status}): ${error}`);
4866
+ }
4867
+ return await res.json();
4764
4868
  }
4765
4869
  };
4766
4870
  }
@@ -4916,6 +5020,24 @@ var PaywallError = class extends Error {
4916
5020
  this.name = "PaywallError";
4917
5021
  }
4918
5022
  };
5023
+ function paywallErrorToClientPayload(error) {
5024
+ const sc = error.structuredContent;
5025
+ const base = {
5026
+ success: false,
5027
+ error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
5028
+ product: sc.product,
5029
+ checkoutUrl: sc.checkoutUrl,
5030
+ message: sc.message
5031
+ };
5032
+ if (sc.kind === "activation_required") {
5033
+ base.kind = "activation_required";
5034
+ if (sc.plans !== void 0) base.plans = sc.plans;
5035
+ if (sc.balance !== void 0) base.balance = sc.balance;
5036
+ if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
5037
+ if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
5038
+ }
5039
+ return base;
5040
+ }
4919
5041
  var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
4920
5042
  cacheTTL: 6e4,
4921
5043
  // Cache results for 60 seconds (reduces API calls significantly)
@@ -4954,8 +5076,6 @@ var SolvaPayPaywall = class {
4954
5076
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4955
5077
  async protect(handler, metadata = {}, getCustomerRef) {
4956
5078
  const product = this.resolveProduct(metadata);
4957
- const configuredPlanRef = metadata.plan?.trim();
4958
- const usagePlanRef = configuredPlanRef || "unspecified";
4959
5079
  const usageType = metadata.usageType || "requests";
4960
5080
  return async (args) => {
4961
5081
  const startTime = Date.now();
@@ -4969,12 +5089,13 @@ var SolvaPayPaywall = class {
4969
5089
  }
4970
5090
  let resolvedMeterName;
4971
5091
  try {
4972
- const limitsCacheKey = `${backendCustomerRef}:${product}:${configuredPlanRef || ""}:${usageType}`;
5092
+ const limitsCacheKey = `${backendCustomerRef}:${product}:${usageType}`;
4973
5093
  const cachedLimits = this.limitsCache.get(limitsCacheKey);
4974
5094
  const now = Date.now();
4975
5095
  let withinLimits;
4976
5096
  let remaining;
4977
5097
  let checkoutUrl;
5098
+ let lastLimitsCheck;
4978
5099
  const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
4979
5100
  if (hasFreshCachedLimits) {
4980
5101
  checkoutUrl = cachedLimits.checkoutUrl;
@@ -4998,9 +5119,9 @@ var SolvaPayPaywall = class {
4998
5119
  const limitsCheck = await this.apiClient.checkLimits({
4999
5120
  customerRef: backendCustomerRef,
5000
5121
  productRef: product,
5001
- ...configuredPlanRef ? { planRef: configuredPlanRef } : {},
5002
5122
  meterName: usageType
5003
5123
  });
5124
+ lastLimitsCheck = limitsCheck;
5004
5125
  withinLimits = limitsCheck.withinLimits;
5005
5126
  remaining = limitsCheck.remaining;
5006
5127
  checkoutUrl = limitsCheck.checkoutUrl;
@@ -5023,12 +5144,25 @@ var SolvaPayPaywall = class {
5023
5144
  this.trackUsage(
5024
5145
  backendCustomerRef,
5025
5146
  product,
5026
- usagePlanRef,
5027
5147
  resolvedMeterName || usageType,
5028
5148
  "paywall",
5029
5149
  requestId,
5030
5150
  latencyMs2
5031
5151
  );
5152
+ if (lastLimitsCheck?.activationRequired) {
5153
+ const confirmationUrl = lastLimitsCheck.confirmationUrl;
5154
+ const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
5155
+ throw new PaywallError("Activation required", {
5156
+ kind: "activation_required",
5157
+ product,
5158
+ message: "Product activation is required before this tool can be used.",
5159
+ checkoutUrl: payCheckoutUrl,
5160
+ confirmationUrl,
5161
+ plans: lastLimitsCheck.plans,
5162
+ balance: lastLimitsCheck.balance,
5163
+ productDetails: lastLimitsCheck.product
5164
+ });
5165
+ }
5032
5166
  throw new PaywallError("Payment required", {
5033
5167
  kind: "payment_required",
5034
5168
  product,
@@ -5041,7 +5175,6 @@ var SolvaPayPaywall = class {
5041
5175
  this.trackUsage(
5042
5176
  backendCustomerRef,
5043
5177
  product,
5044
- usagePlanRef,
5045
5178
  resolvedMeterName || usageType,
5046
5179
  "success",
5047
5180
  requestId,
@@ -5060,7 +5193,6 @@ var SolvaPayPaywall = class {
5060
5193
  this.trackUsage(
5061
5194
  backendCustomerRef,
5062
5195
  product,
5063
- usagePlanRef,
5064
5196
  resolvedMeterName || usageType,
5065
5197
  "fail",
5066
5198
  requestId,
@@ -5131,7 +5263,8 @@ var SolvaPayPaywall = class {
5131
5263
  this.customerCreationAttempts.add(customerRef);
5132
5264
  try {
5133
5265
  const createParams = {
5134
- email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
5266
+ email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
5267
+ metadata: {}
5135
5268
  };
5136
5269
  if (options?.name) {
5137
5270
  createParams.name = options.name;
@@ -5155,7 +5288,10 @@ var SolvaPayPaywall = class {
5155
5288
  return searchResult.customerRef;
5156
5289
  }
5157
5290
  } catch (lookupError) {
5158
- this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
5291
+ this.log(
5292
+ `\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
5293
+ lookupError instanceof Error ? lookupError.message : lookupError
5294
+ );
5159
5295
  }
5160
5296
  }
5161
5297
  const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
@@ -5178,7 +5314,8 @@ var SolvaPayPaywall = class {
5178
5314
  try {
5179
5315
  const retryParams = {
5180
5316
  email: `${customerRef}-${Date.now()}@auto-created.local`,
5181
- externalRef
5317
+ externalRef,
5318
+ metadata: {}
5182
5319
  };
5183
5320
  if (options?.name) {
5184
5321
  retryParams.name = options.name;
@@ -5215,14 +5352,14 @@ var SolvaPayPaywall = class {
5215
5352
  }
5216
5353
  return backendRef;
5217
5354
  }
5218
- async trackUsage(customerRef, _productRef, _planRef, action, outcome, requestId, actionDuration) {
5355
+ async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration) {
5219
5356
  await withRetry(
5220
5357
  () => this.apiClient.trackUsage({
5221
5358
  customerRef,
5222
5359
  actionType: "api_call",
5223
5360
  units: 1,
5224
5361
  outcome,
5225
- productReference: _productRef,
5362
+ productRef,
5226
5363
  duration: actionDuration,
5227
5364
  metadata: { action: action || "api_requests", requestId },
5228
5365
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -5474,8 +5611,10 @@ var McpAdapter = class {
5474
5611
  const ref = await this.options.getCustomerRef(args, extra);
5475
5612
  return AdapterUtils.ensureCustomerRef(ref);
5476
5613
  }
5477
- const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
5478
- const customerRef = typeof customerRefFromExtra === "string" && customerRefFromExtra.trim() ? customerRefFromExtra.trim() : "anonymous";
5614
+ const customerRefFromExtra = typeof extra?.authInfo?.extra?.customer_ref === "string" ? String(extra.authInfo.extra.customer_ref) : void 0;
5615
+ const customerRefFromArgs = typeof args.auth === "object" && args.auth !== null && typeof args.auth.customer_ref === "string" ? String(args.auth.customer_ref) : void 0;
5616
+ const directCustomerRef = typeof args.customer_ref === "string" ? args.customer_ref : void 0;
5617
+ const customerRef = (customerRefFromExtra || customerRefFromArgs || directCustomerRef || "anonymous").trim();
5479
5618
  return AdapterUtils.ensureCustomerRef(customerRef);
5480
5619
  }
5481
5620
  formatResponse(result, _context) {
@@ -5499,17 +5638,7 @@ var McpAdapter = class {
5499
5638
  content: [
5500
5639
  {
5501
5640
  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
- )
5641
+ text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
5513
5642
  }
5514
5643
  ],
5515
5644
  isError: true,
@@ -5796,6 +5925,12 @@ function createSolvaPay(config) {
5796
5925
  }
5797
5926
  return apiClient.createPaymentIntent(params);
5798
5927
  },
5928
+ createTopupPaymentIntent(params) {
5929
+ if (!apiClient.createTopupPaymentIntent) {
5930
+ throw new import_core2.SolvaPayError("createTopupPaymentIntent is not available on this API client");
5931
+ }
5932
+ return apiClient.createTopupPaymentIntent(params);
5933
+ },
5799
5934
  processPaymentIntent(params) {
5800
5935
  if (!apiClient.processPaymentIntent) {
5801
5936
  throw new import_core2.SolvaPayError("processPaymentIntent is not available on this API client");
@@ -5812,11 +5947,17 @@ function createSolvaPay(config) {
5812
5947
  if (!apiClient.createCustomer) {
5813
5948
  throw new import_core2.SolvaPayError("createCustomer is not available on this API client");
5814
5949
  }
5815
- return apiClient.createCustomer(params);
5950
+ return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
5816
5951
  },
5817
5952
  getCustomer(params) {
5818
5953
  return apiClient.getCustomer(params);
5819
5954
  },
5955
+ getCustomerBalance(params) {
5956
+ if (!apiClient.getCustomerBalance) {
5957
+ throw new import_core2.SolvaPayError("getCustomerBalance is not available on this API client");
5958
+ }
5959
+ return apiClient.getCustomerBalance(params);
5960
+ },
5820
5961
  createCheckoutSession(params) {
5821
5962
  return apiClient.createCheckoutSession({
5822
5963
  customerRef: params.customerRef,
@@ -5827,6 +5968,12 @@ function createSolvaPay(config) {
5827
5968
  createCustomerSession(params) {
5828
5969
  return apiClient.createCustomerSession(params);
5829
5970
  },
5971
+ activatePlan(params) {
5972
+ if (!apiClient.activatePlan) {
5973
+ throw new import_core2.SolvaPayError("activatePlan is not available on this API client");
5974
+ }
5975
+ return apiClient.activatePlan(params);
5976
+ },
5830
5977
  bootstrapMcpProduct(params) {
5831
5978
  if (!apiClient.bootstrapMcpProduct) {
5832
5979
  throw new import_core2.SolvaPayError("bootstrapMcpProduct is not available on this API client");
@@ -5848,9 +5995,8 @@ function createSolvaPay(config) {
5848
5995
  // Payable API for framework-specific handlers
5849
5996
  payable(options = {}) {
5850
5997
  const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
5851
- const plan = options.planRef || options.plan;
5852
5998
  const usageType = options.usageType || "requests";
5853
- const metadata = { product, plan, usageType };
5999
+ const metadata = { product, usageType };
5854
6000
  return {
5855
6001
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5856
6002
  http(businessLogic, adapterOptions) {
@@ -6188,6 +6334,24 @@ async function syncCustomerCore(request, options = {}) {
6188
6334
  return handleRouteError(error, "Sync customer", "Failed to sync customer");
6189
6335
  }
6190
6336
  }
6337
+ async function getCustomerBalanceCore(request, options = {}) {
6338
+ try {
6339
+ const userResult = await getAuthenticatedUserCore(request);
6340
+ if (isErrorResult(userResult)) {
6341
+ return userResult;
6342
+ }
6343
+ const { userId, email, name } = userResult;
6344
+ const solvaPay = options.solvaPay || createSolvaPay();
6345
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
6346
+ email: email || void 0,
6347
+ name: name || void 0
6348
+ });
6349
+ const result = await solvaPay.getCustomerBalance({ customerRef });
6350
+ return result;
6351
+ } catch (error) {
6352
+ return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
6353
+ }
6354
+ }
6191
6355
 
6192
6356
  // src/helpers/payment.ts
6193
6357
  async function createPaymentIntentCore(request, body, options = {}) {
@@ -6214,7 +6378,7 @@ async function createPaymentIntentCore(request, body, options = {}) {
6214
6378
  customerRef
6215
6379
  });
6216
6380
  return {
6217
- id: paymentIntent.id,
6381
+ processorPaymentId: paymentIntent.processorPaymentId,
6218
6382
  clientSecret: paymentIntent.clientSecret,
6219
6383
  publishableKey: paymentIntent.publishableKey,
6220
6384
  accountId: paymentIntent.accountId,
@@ -6224,6 +6388,57 @@ async function createPaymentIntentCore(request, body, options = {}) {
6224
6388
  return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
6225
6389
  }
6226
6390
  }
6391
+ async function createTopupPaymentIntentCore(request, body, options = {}) {
6392
+ try {
6393
+ if (!body.amount || body.amount <= 0) {
6394
+ return {
6395
+ error: "Missing or invalid amount: must be a positive number",
6396
+ status: 400
6397
+ };
6398
+ }
6399
+ if (!body.currency) {
6400
+ return {
6401
+ error: "Missing required parameter: currency",
6402
+ status: 400
6403
+ };
6404
+ }
6405
+ if (body.currency !== body.currency.toUpperCase()) {
6406
+ return {
6407
+ error: `Invalid currency "${body.currency}": must be an uppercase ISO 4217 code (e.g. "USD", "EUR")`,
6408
+ status: 400
6409
+ };
6410
+ }
6411
+ const customerResult = await syncCustomerCore(request, {
6412
+ solvaPay: options.solvaPay,
6413
+ includeEmail: options.includeEmail,
6414
+ includeName: options.includeName
6415
+ });
6416
+ if (isErrorResult(customerResult)) {
6417
+ return customerResult;
6418
+ }
6419
+ const customerRef = customerResult;
6420
+ const solvaPay = options.solvaPay || createSolvaPay();
6421
+ const paymentIntent = await solvaPay.createTopupPaymentIntent({
6422
+ customerRef,
6423
+ amount: body.amount,
6424
+ currency: body.currency,
6425
+ description: body.description
6426
+ });
6427
+ return {
6428
+ processorPaymentId: paymentIntent.processorPaymentId,
6429
+ clientSecret: paymentIntent.clientSecret,
6430
+ publishableKey: paymentIntent.publishableKey,
6431
+ accountId: paymentIntent.accountId,
6432
+ customerRef
6433
+ };
6434
+ } catch (error) {
6435
+ return handleRouteError(
6436
+ error,
6437
+ "Create topup payment intent",
6438
+ "Topup payment intent creation failed"
6439
+ );
6440
+ }
6441
+ }
6227
6442
  async function processPaymentIntentCore(request, body, options = {}) {
6228
6443
  try {
6229
6444
  if (!body.paymentIntentId || !body.productRef) {
@@ -6386,6 +6601,103 @@ async function cancelPurchaseCore(request, body, options = {}) {
6386
6601
  return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
6387
6602
  }
6388
6603
  }
6604
+ async function reactivatePurchaseCore(request, body, options = {}) {
6605
+ try {
6606
+ if (!body.purchaseRef) {
6607
+ return {
6608
+ error: "Missing required parameter: purchaseRef is required",
6609
+ status: 400
6610
+ };
6611
+ }
6612
+ const solvaPay = options.solvaPay || createSolvaPay();
6613
+ if (!solvaPay.apiClient.reactivatePurchase) {
6614
+ return {
6615
+ error: "Reactivate purchase method not available on SDK client",
6616
+ status: 500
6617
+ };
6618
+ }
6619
+ let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
6620
+ purchaseRef: body.purchaseRef
6621
+ });
6622
+ if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
6623
+ return {
6624
+ error: "Invalid response from reactivate purchase endpoint",
6625
+ status: 500
6626
+ };
6627
+ }
6628
+ const responseObj = reactivatedPurchase;
6629
+ if (responseObj.purchase && typeof responseObj.purchase === "object") {
6630
+ reactivatedPurchase = responseObj.purchase;
6631
+ }
6632
+ if (!reactivatedPurchase.reference) {
6633
+ return {
6634
+ error: "Reactivate purchase response missing required fields",
6635
+ status: 500
6636
+ };
6637
+ }
6638
+ if (reactivatedPurchase.cancelledAt) {
6639
+ return {
6640
+ error: `Purchase reactivation failed: cancelledAt is still set`,
6641
+ status: 500
6642
+ };
6643
+ }
6644
+ await new Promise((resolve) => setTimeout(resolve, 500));
6645
+ return reactivatedPurchase;
6646
+ } catch (error) {
6647
+ if (error instanceof import_core4.SolvaPayError) {
6648
+ const errorMessage = error.message;
6649
+ if (errorMessage.includes("not found")) {
6650
+ return {
6651
+ error: "Purchase not found",
6652
+ status: 404,
6653
+ details: errorMessage
6654
+ };
6655
+ }
6656
+ if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
6657
+ return {
6658
+ error: "Purchase cannot be reactivated",
6659
+ status: 400,
6660
+ details: errorMessage
6661
+ };
6662
+ }
6663
+ return {
6664
+ error: errorMessage,
6665
+ status: 500,
6666
+ details: errorMessage
6667
+ };
6668
+ }
6669
+ return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
6670
+ }
6671
+ }
6672
+
6673
+ // src/helpers/activation.ts
6674
+ async function activatePlanCore(request, body, options = {}) {
6675
+ try {
6676
+ if (!body.productRef || !body.planRef) {
6677
+ return {
6678
+ error: "Missing required parameters: productRef and planRef are required",
6679
+ status: 400
6680
+ };
6681
+ }
6682
+ const customerResult = await syncCustomerCore(request, {
6683
+ solvaPay: options.solvaPay,
6684
+ includeEmail: options.includeEmail,
6685
+ includeName: options.includeName
6686
+ });
6687
+ if (isErrorResult(customerResult)) {
6688
+ return customerResult;
6689
+ }
6690
+ const customerRef = customerResult;
6691
+ const solvaPay = options.solvaPay || createSolvaPay();
6692
+ return await solvaPay.activatePlan({
6693
+ customerRef,
6694
+ productRef: body.productRef,
6695
+ planRef: body.planRef
6696
+ });
6697
+ } catch (error) {
6698
+ return handleRouteError(error, "Activate plan", "Plan activation failed");
6699
+ }
6700
+ }
6389
6701
 
6390
6702
  // src/helpers/plans.ts
6391
6703
  var import_core5 = require("@solvapay/core");
@@ -6470,6 +6782,7 @@ function verifyWebhook({
6470
6782
  McpBearerAuthError,
6471
6783
  PaywallError,
6472
6784
  VIRTUAL_TOOL_DEFINITIONS,
6785
+ activatePlanCore,
6473
6786
  buildAuthInfoFromBearer,
6474
6787
  cancelPurchaseCore,
6475
6788
  createCheckoutSessionCore,
@@ -6478,10 +6791,12 @@ function verifyWebhook({
6478
6791
  createPaymentIntentCore,
6479
6792
  createSolvaPay,
6480
6793
  createSolvaPayClient,
6794
+ createTopupPaymentIntentCore,
6481
6795
  createVirtualTools,
6482
6796
  decodeJwtPayload,
6483
6797
  extractBearerToken,
6484
6798
  getAuthenticatedUserCore,
6799
+ getCustomerBalanceCore,
6485
6800
  getCustomerRefFromBearerAuthHeader,
6486
6801
  getCustomerRefFromJwtPayload,
6487
6802
  getOAuthAuthorizationServerResponse,
@@ -6490,7 +6805,9 @@ function verifyWebhook({
6490
6805
  isErrorResult,
6491
6806
  jsonSchemaToZodRawShape,
6492
6807
  listPlansCore,
6808
+ paywallErrorToClientPayload,
6493
6809
  processPaymentIntentCore,
6810
+ reactivatePurchaseCore,
6494
6811
  registerVirtualToolsMcpImpl,
6495
6812
  syncCustomerCore,
6496
6813
  verifyWebhook,