@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/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(body)
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
- customerReference: params.customerRef
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
- const result = await res.json();
4641
- return result;
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}:${configuredPlanRef || ""}:${usageType}`;
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(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
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, _productRef, _planRef, action, outcome, requestId, actionDuration) {
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
- productReference: _productRef,
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 customerRef = typeof customerRefFromExtra === "string" && customerRefFromExtra.trim() ? customerRefFromExtra.trim() : "anonymous";
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, plan, usageType };
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
- return Buffer.from(padded, "base64").toString("utf8");
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
- id: paymentIntent.id,
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
  });