@solvapay/server 1.0.6 → 1.0.9-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/edge.js CHANGED
@@ -38,12 +38,10 @@ function createSolvaPayClient(opts) {
38
38
  // POST: /v1/sdk/usages
39
39
  async trackUsage(params) {
40
40
  const url = `${base}/v1/sdk/usages`;
41
- const { customerRef, ...rest } = params;
42
- const body = { ...rest, customerId: customerRef };
43
41
  const res = await fetch(url, {
44
42
  method: "POST",
45
43
  headers,
46
- body: JSON.stringify(body)
44
+ body: JSON.stringify(params)
47
45
  });
48
46
  if (!res.ok) {
49
47
  const error = await res.text();
@@ -288,7 +286,7 @@ function createSolvaPayClient(opts) {
288
286
  body: JSON.stringify({
289
287
  productRef: params.productRef,
290
288
  planRef: params.planRef,
291
- customerReference: params.customerRef
289
+ customerRef: params.customerRef
292
290
  })
293
291
  });
294
292
  if (!res.ok) {
@@ -296,8 +294,32 @@ function createSolvaPayClient(opts) {
296
294
  log(`\u274C API Error: ${res.status} - ${error}`);
297
295
  throw new SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
298
296
  }
299
- const result = await res.json();
300
- return result;
297
+ return await res.json();
298
+ },
299
+ // POST: /v1/sdk/payment-intents (purpose: credit_topup)
300
+ async createTopupPaymentIntent(params) {
301
+ const idempotencyKey = params.idempotencyKey || `topup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
302
+ const url = `${base}/v1/sdk/payment-intents`;
303
+ const res = await fetch(url, {
304
+ method: "POST",
305
+ headers: {
306
+ ...headers,
307
+ "Idempotency-Key": idempotencyKey
308
+ },
309
+ body: JSON.stringify({
310
+ customerRef: params.customerRef,
311
+ purpose: "credit_topup",
312
+ amount: params.amount,
313
+ currency: params.currency,
314
+ description: params.description
315
+ })
316
+ });
317
+ if (!res.ok) {
318
+ const error = await res.text();
319
+ log(`\u274C API Error: ${res.status} - ${error}`);
320
+ throw new SolvaPayError(`Create topup payment intent failed (${res.status}): ${error}`);
321
+ }
322
+ return await res.json();
301
323
  },
302
324
  // POST: /v1/sdk/payment-intents/{paymentIntentId}/process
303
325
  async processPaymentIntent(params) {
@@ -374,6 +396,54 @@ function createSolvaPayClient(opts) {
374
396
  }
375
397
  return result;
376
398
  },
399
+ // POST: /v1/sdk/purchases/{purchaseRef}/reactivate
400
+ async reactivatePurchase(params) {
401
+ const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/reactivate`;
402
+ const res = await fetch(url, {
403
+ method: "POST",
404
+ headers
405
+ });
406
+ if (!res.ok) {
407
+ const error = await res.text();
408
+ log(`\u274C API Error: ${res.status} - ${error}`);
409
+ if (res.status === 404) {
410
+ throw new SolvaPayError(`Purchase not found: ${error}`);
411
+ }
412
+ if (res.status === 400) {
413
+ throw new SolvaPayError(
414
+ `Purchase cannot be reactivated: ${error}`
415
+ );
416
+ }
417
+ throw new SolvaPayError(`Reactivate purchase failed (${res.status}): ${error}`);
418
+ }
419
+ const responseText = await res.text();
420
+ let responseData;
421
+ try {
422
+ responseData = JSON.parse(responseText);
423
+ } catch (parseError) {
424
+ log(`\u274C Failed to parse response as JSON: ${parseError}`);
425
+ throw new SolvaPayError(
426
+ `Invalid JSON response from reactivate purchase endpoint: ${responseText.substring(0, 200)}`
427
+ );
428
+ }
429
+ if (!responseData || typeof responseData !== "object") {
430
+ log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
431
+ throw new SolvaPayError(`Invalid response structure from reactivate purchase endpoint`);
432
+ }
433
+ let result;
434
+ if (responseData.purchase && typeof responseData.purchase === "object") {
435
+ result = responseData.purchase;
436
+ } else if (responseData.reference) {
437
+ result = responseData;
438
+ } else {
439
+ result = responseData.purchase || responseData;
440
+ }
441
+ if (!result || typeof result !== "object") {
442
+ log(`\u274C Invalid purchase data in response. Full response:`, responseData);
443
+ throw new SolvaPayError(`Invalid purchase data in reactivate purchase response`);
444
+ }
445
+ return result;
446
+ },
377
447
  // POST: /v1/sdk/user-info
378
448
  async getUserInfo(params) {
379
449
  const url = `${base}/v1/sdk/user-info`;
@@ -389,6 +459,20 @@ function createSolvaPayClient(opts) {
389
459
  }
390
460
  return await res.json();
391
461
  },
462
+ // GET: /v1/sdk/customers/:customerRef/balance
463
+ async getCustomerBalance(params) {
464
+ const url = `${base}/v1/sdk/customers/${params.customerRef}/balance`;
465
+ const res = await fetch(url, {
466
+ method: "GET",
467
+ headers
468
+ });
469
+ if (!res.ok) {
470
+ const error = await res.text();
471
+ log(`\u274C API Error: ${res.status} - ${error}`);
472
+ throw new SolvaPayError(`Get customer balance failed (${res.status}): ${error}`);
473
+ }
474
+ return await res.json();
475
+ },
392
476
  // POST: /v1/sdk/checkout-sessions
393
477
  async createCheckoutSession(params) {
394
478
  const url = `${base}/v1/sdk/checkout-sessions`;
@@ -420,6 +504,21 @@ function createSolvaPayClient(opts) {
420
504
  }
421
505
  const result = await res.json();
422
506
  return result;
507
+ },
508
+ // POST: /v1/sdk/activate
509
+ async activatePlan(params) {
510
+ const url = `${base}/v1/sdk/activate`;
511
+ const res = await fetch(url, {
512
+ method: "POST",
513
+ headers,
514
+ body: JSON.stringify(params)
515
+ });
516
+ if (!res.ok) {
517
+ const error = await res.text();
518
+ log(`\u274C API Error: ${res.status} - ${error}`);
519
+ throw new SolvaPayError(`Activate plan failed (${res.status}): ${error}`);
520
+ }
521
+ return await res.json();
423
522
  }
424
523
  };
425
524
  }
@@ -575,6 +674,24 @@ var PaywallError = class extends Error {
575
674
  this.name = "PaywallError";
576
675
  }
577
676
  };
677
+ function paywallErrorToClientPayload(error) {
678
+ const sc = error.structuredContent;
679
+ const base = {
680
+ success: false,
681
+ error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
682
+ product: sc.product,
683
+ checkoutUrl: sc.checkoutUrl,
684
+ message: sc.message
685
+ };
686
+ if (sc.kind === "activation_required") {
687
+ base.kind = "activation_required";
688
+ if (sc.plans !== void 0) base.plans = sc.plans;
689
+ if (sc.balance !== void 0) base.balance = sc.balance;
690
+ if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
691
+ if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
692
+ }
693
+ return base;
694
+ }
578
695
  var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
579
696
  cacheTTL: 6e4,
580
697
  // Cache results for 60 seconds (reduces API calls significantly)
@@ -613,8 +730,6 @@ var SolvaPayPaywall = class {
613
730
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
614
731
  async protect(handler, metadata = {}, getCustomerRef) {
615
732
  const product = this.resolveProduct(metadata);
616
- const configuredPlanRef = metadata.plan?.trim();
617
- const usagePlanRef = configuredPlanRef || "unspecified";
618
733
  const usageType = metadata.usageType || "requests";
619
734
  return async (args) => {
620
735
  const startTime = Date.now();
@@ -628,12 +743,13 @@ var SolvaPayPaywall = class {
628
743
  }
629
744
  let resolvedMeterName;
630
745
  try {
631
- const limitsCacheKey = `${backendCustomerRef}:${product}:${configuredPlanRef || ""}:${usageType}`;
746
+ const limitsCacheKey = `${backendCustomerRef}:${product}:${usageType}`;
632
747
  const cachedLimits = this.limitsCache.get(limitsCacheKey);
633
748
  const now = Date.now();
634
749
  let withinLimits;
635
750
  let remaining;
636
751
  let checkoutUrl;
752
+ let lastLimitsCheck;
637
753
  const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
638
754
  if (hasFreshCachedLimits) {
639
755
  checkoutUrl = cachedLimits.checkoutUrl;
@@ -657,9 +773,9 @@ var SolvaPayPaywall = class {
657
773
  const limitsCheck = await this.apiClient.checkLimits({
658
774
  customerRef: backendCustomerRef,
659
775
  productRef: product,
660
- ...configuredPlanRef ? { planRef: configuredPlanRef } : {},
661
776
  meterName: usageType
662
777
  });
778
+ lastLimitsCheck = limitsCheck;
663
779
  withinLimits = limitsCheck.withinLimits;
664
780
  remaining = limitsCheck.remaining;
665
781
  checkoutUrl = limitsCheck.checkoutUrl;
@@ -682,12 +798,25 @@ var SolvaPayPaywall = class {
682
798
  this.trackUsage(
683
799
  backendCustomerRef,
684
800
  product,
685
- usagePlanRef,
686
801
  resolvedMeterName || usageType,
687
802
  "paywall",
688
803
  requestId,
689
804
  latencyMs2
690
805
  );
806
+ if (lastLimitsCheck?.activationRequired) {
807
+ const confirmationUrl = lastLimitsCheck.confirmationUrl;
808
+ const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
809
+ throw new PaywallError("Activation required", {
810
+ kind: "activation_required",
811
+ product,
812
+ message: "Product activation is required before this tool can be used.",
813
+ checkoutUrl: payCheckoutUrl,
814
+ confirmationUrl,
815
+ plans: lastLimitsCheck.plans,
816
+ balance: lastLimitsCheck.balance,
817
+ productDetails: lastLimitsCheck.product
818
+ });
819
+ }
691
820
  throw new PaywallError("Payment required", {
692
821
  kind: "payment_required",
693
822
  product,
@@ -700,7 +829,6 @@ var SolvaPayPaywall = class {
700
829
  this.trackUsage(
701
830
  backendCustomerRef,
702
831
  product,
703
- usagePlanRef,
704
832
  resolvedMeterName || usageType,
705
833
  "success",
706
834
  requestId,
@@ -719,7 +847,6 @@ var SolvaPayPaywall = class {
719
847
  this.trackUsage(
720
848
  backendCustomerRef,
721
849
  product,
722
- usagePlanRef,
723
850
  resolvedMeterName || usageType,
724
851
  "fail",
725
852
  requestId,
@@ -790,7 +917,8 @@ var SolvaPayPaywall = class {
790
917
  this.customerCreationAttempts.add(customerRef);
791
918
  try {
792
919
  const createParams = {
793
- email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
920
+ email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
921
+ metadata: {}
794
922
  };
795
923
  if (options?.name) {
796
924
  createParams.name = options.name;
@@ -814,7 +942,10 @@ var SolvaPayPaywall = class {
814
942
  return searchResult.customerRef;
815
943
  }
816
944
  } catch (lookupError) {
817
- this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
945
+ this.log(
946
+ `\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
947
+ lookupError instanceof Error ? lookupError.message : lookupError
948
+ );
818
949
  }
819
950
  }
820
951
  const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
@@ -837,7 +968,8 @@ var SolvaPayPaywall = class {
837
968
  try {
838
969
  const retryParams = {
839
970
  email: `${customerRef}-${Date.now()}@auto-created.local`,
840
- externalRef
971
+ externalRef,
972
+ metadata: {}
841
973
  };
842
974
  if (options?.name) {
843
975
  retryParams.name = options.name;
@@ -874,14 +1006,14 @@ var SolvaPayPaywall = class {
874
1006
  }
875
1007
  return backendRef;
876
1008
  }
877
- async trackUsage(customerRef, _productRef, _planRef, action, outcome, requestId, actionDuration) {
1009
+ async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration) {
878
1010
  await withRetry(
879
1011
  () => this.apiClient.trackUsage({
880
1012
  customerRef,
881
1013
  actionType: "api_call",
882
1014
  units: 1,
883
1015
  outcome,
884
- productReference: _productRef,
1016
+ productRef,
885
1017
  duration: actionDuration,
886
1018
  metadata: { action: action || "api_requests", requestId },
887
1019
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -1133,8 +1265,10 @@ var McpAdapter = class {
1133
1265
  const ref = await this.options.getCustomerRef(args, extra);
1134
1266
  return AdapterUtils.ensureCustomerRef(ref);
1135
1267
  }
1136
- const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
1137
- const customerRef = typeof customerRefFromExtra === "string" && customerRefFromExtra.trim() ? customerRefFromExtra.trim() : "anonymous";
1268
+ const customerRefFromExtra = typeof extra?.authInfo?.extra?.customer_ref === "string" ? String(extra.authInfo.extra.customer_ref) : void 0;
1269
+ const customerRefFromArgs = typeof args.auth === "object" && args.auth !== null && typeof args.auth.customer_ref === "string" ? String(args.auth.customer_ref) : void 0;
1270
+ const directCustomerRef = typeof args.customer_ref === "string" ? args.customer_ref : void 0;
1271
+ const customerRef = (customerRefFromExtra || customerRefFromArgs || directCustomerRef || "anonymous").trim();
1138
1272
  return AdapterUtils.ensureCustomerRef(customerRef);
1139
1273
  }
1140
1274
  formatResponse(result, _context) {
@@ -1158,17 +1292,7 @@ var McpAdapter = class {
1158
1292
  content: [
1159
1293
  {
1160
1294
  type: "text",
1161
- text: JSON.stringify(
1162
- {
1163
- success: false,
1164
- error: "Payment required",
1165
- product: error.structuredContent.product,
1166
- checkoutUrl: error.structuredContent.checkoutUrl,
1167
- message: error.structuredContent.message
1168
- },
1169
- null,
1170
- 2
1171
- )
1295
+ text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
1172
1296
  }
1173
1297
  ],
1174
1298
  isError: true,
@@ -1454,6 +1578,12 @@ function createSolvaPay(config) {
1454
1578
  }
1455
1579
  return apiClient.createPaymentIntent(params);
1456
1580
  },
1581
+ createTopupPaymentIntent(params) {
1582
+ if (!apiClient.createTopupPaymentIntent) {
1583
+ throw new SolvaPayError2("createTopupPaymentIntent is not available on this API client");
1584
+ }
1585
+ return apiClient.createTopupPaymentIntent(params);
1586
+ },
1457
1587
  processPaymentIntent(params) {
1458
1588
  if (!apiClient.processPaymentIntent) {
1459
1589
  throw new SolvaPayError2("processPaymentIntent is not available on this API client");
@@ -1470,11 +1600,17 @@ function createSolvaPay(config) {
1470
1600
  if (!apiClient.createCustomer) {
1471
1601
  throw new SolvaPayError2("createCustomer is not available on this API client");
1472
1602
  }
1473
- return apiClient.createCustomer(params);
1603
+ return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
1474
1604
  },
1475
1605
  getCustomer(params) {
1476
1606
  return apiClient.getCustomer(params);
1477
1607
  },
1608
+ getCustomerBalance(params) {
1609
+ if (!apiClient.getCustomerBalance) {
1610
+ throw new SolvaPayError2("getCustomerBalance is not available on this API client");
1611
+ }
1612
+ return apiClient.getCustomerBalance(params);
1613
+ },
1478
1614
  createCheckoutSession(params) {
1479
1615
  return apiClient.createCheckoutSession({
1480
1616
  customerRef: params.customerRef,
@@ -1485,6 +1621,12 @@ function createSolvaPay(config) {
1485
1621
  createCustomerSession(params) {
1486
1622
  return apiClient.createCustomerSession(params);
1487
1623
  },
1624
+ activatePlan(params) {
1625
+ if (!apiClient.activatePlan) {
1626
+ throw new SolvaPayError2("activatePlan is not available on this API client");
1627
+ }
1628
+ return apiClient.activatePlan(params);
1629
+ },
1488
1630
  bootstrapMcpProduct(params) {
1489
1631
  if (!apiClient.bootstrapMcpProduct) {
1490
1632
  throw new SolvaPayError2("bootstrapMcpProduct is not available on this API client");
@@ -1506,9 +1648,8 @@ function createSolvaPay(config) {
1506
1648
  // Payable API for framework-specific handlers
1507
1649
  payable(options = {}) {
1508
1650
  const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
1509
- const plan = options.planRef || options.plan;
1510
1651
  const usageType = options.usageType || "requests";
1511
- const metadata = { product, plan, usageType };
1652
+ const metadata = { product, usageType };
1512
1653
  return {
1513
1654
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1514
1655
  http(businessLogic, adapterOptions) {
@@ -1634,6 +1775,24 @@ async function syncCustomerCore(request, options = {}) {
1634
1775
  return handleRouteError(error, "Sync customer", "Failed to sync customer");
1635
1776
  }
1636
1777
  }
1778
+ async function getCustomerBalanceCore(request, options = {}) {
1779
+ try {
1780
+ const userResult = await getAuthenticatedUserCore(request);
1781
+ if (isErrorResult(userResult)) {
1782
+ return userResult;
1783
+ }
1784
+ const { userId, email, name } = userResult;
1785
+ const solvaPay = options.solvaPay || createSolvaPay();
1786
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
1787
+ email: email || void 0,
1788
+ name: name || void 0
1789
+ });
1790
+ const result = await solvaPay.getCustomerBalance({ customerRef });
1791
+ return result;
1792
+ } catch (error) {
1793
+ return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
1794
+ }
1795
+ }
1637
1796
 
1638
1797
  // src/helpers/payment.ts
1639
1798
  async function createPaymentIntentCore(request, body, options = {}) {
@@ -1660,7 +1819,7 @@ async function createPaymentIntentCore(request, body, options = {}) {
1660
1819
  customerRef
1661
1820
  });
1662
1821
  return {
1663
- id: paymentIntent.id,
1822
+ processorPaymentId: paymentIntent.processorPaymentId,
1664
1823
  clientSecret: paymentIntent.clientSecret,
1665
1824
  publishableKey: paymentIntent.publishableKey,
1666
1825
  accountId: paymentIntent.accountId,
@@ -1670,6 +1829,57 @@ async function createPaymentIntentCore(request, body, options = {}) {
1670
1829
  return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
1671
1830
  }
1672
1831
  }
1832
+ async function createTopupPaymentIntentCore(request, body, options = {}) {
1833
+ try {
1834
+ if (!body.amount || body.amount <= 0) {
1835
+ return {
1836
+ error: "Missing or invalid amount: must be a positive number",
1837
+ status: 400
1838
+ };
1839
+ }
1840
+ if (!body.currency) {
1841
+ return {
1842
+ error: "Missing required parameter: currency",
1843
+ status: 400
1844
+ };
1845
+ }
1846
+ if (body.currency !== body.currency.toUpperCase()) {
1847
+ return {
1848
+ error: `Invalid currency "${body.currency}": must be an uppercase ISO 4217 code (e.g. "USD", "EUR")`,
1849
+ status: 400
1850
+ };
1851
+ }
1852
+ const customerResult = await syncCustomerCore(request, {
1853
+ solvaPay: options.solvaPay,
1854
+ includeEmail: options.includeEmail,
1855
+ includeName: options.includeName
1856
+ });
1857
+ if (isErrorResult(customerResult)) {
1858
+ return customerResult;
1859
+ }
1860
+ const customerRef = customerResult;
1861
+ const solvaPay = options.solvaPay || createSolvaPay();
1862
+ const paymentIntent = await solvaPay.createTopupPaymentIntent({
1863
+ customerRef,
1864
+ amount: body.amount,
1865
+ currency: body.currency,
1866
+ description: body.description
1867
+ });
1868
+ return {
1869
+ processorPaymentId: paymentIntent.processorPaymentId,
1870
+ clientSecret: paymentIntent.clientSecret,
1871
+ publishableKey: paymentIntent.publishableKey,
1872
+ accountId: paymentIntent.accountId,
1873
+ customerRef
1874
+ };
1875
+ } catch (error) {
1876
+ return handleRouteError(
1877
+ error,
1878
+ "Create topup payment intent",
1879
+ "Topup payment intent creation failed"
1880
+ );
1881
+ }
1882
+ }
1673
1883
  async function processPaymentIntentCore(request, body, options = {}) {
1674
1884
  try {
1675
1885
  if (!body.paymentIntentId || !body.productRef) {
@@ -1832,6 +2042,103 @@ async function cancelPurchaseCore(request, body, options = {}) {
1832
2042
  return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
1833
2043
  }
1834
2044
  }
2045
+ async function reactivatePurchaseCore(request, body, options = {}) {
2046
+ try {
2047
+ if (!body.purchaseRef) {
2048
+ return {
2049
+ error: "Missing required parameter: purchaseRef is required",
2050
+ status: 400
2051
+ };
2052
+ }
2053
+ const solvaPay = options.solvaPay || createSolvaPay();
2054
+ if (!solvaPay.apiClient.reactivatePurchase) {
2055
+ return {
2056
+ error: "Reactivate purchase method not available on SDK client",
2057
+ status: 500
2058
+ };
2059
+ }
2060
+ let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
2061
+ purchaseRef: body.purchaseRef
2062
+ });
2063
+ if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
2064
+ return {
2065
+ error: "Invalid response from reactivate purchase endpoint",
2066
+ status: 500
2067
+ };
2068
+ }
2069
+ const responseObj = reactivatedPurchase;
2070
+ if (responseObj.purchase && typeof responseObj.purchase === "object") {
2071
+ reactivatedPurchase = responseObj.purchase;
2072
+ }
2073
+ if (!reactivatedPurchase.reference) {
2074
+ return {
2075
+ error: "Reactivate purchase response missing required fields",
2076
+ status: 500
2077
+ };
2078
+ }
2079
+ if (reactivatedPurchase.cancelledAt) {
2080
+ return {
2081
+ error: `Purchase reactivation failed: cancelledAt is still set`,
2082
+ status: 500
2083
+ };
2084
+ }
2085
+ await new Promise((resolve) => setTimeout(resolve, 500));
2086
+ return reactivatedPurchase;
2087
+ } catch (error) {
2088
+ if (error instanceof SolvaPayError4) {
2089
+ const errorMessage = error.message;
2090
+ if (errorMessage.includes("not found")) {
2091
+ return {
2092
+ error: "Purchase not found",
2093
+ status: 404,
2094
+ details: errorMessage
2095
+ };
2096
+ }
2097
+ if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
2098
+ return {
2099
+ error: "Purchase cannot be reactivated",
2100
+ status: 400,
2101
+ details: errorMessage
2102
+ };
2103
+ }
2104
+ return {
2105
+ error: errorMessage,
2106
+ status: 500,
2107
+ details: errorMessage
2108
+ };
2109
+ }
2110
+ return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
2111
+ }
2112
+ }
2113
+
2114
+ // src/helpers/activation.ts
2115
+ async function activatePlanCore(request, body, options = {}) {
2116
+ try {
2117
+ if (!body.productRef || !body.planRef) {
2118
+ return {
2119
+ error: "Missing required parameters: productRef and planRef are required",
2120
+ status: 400
2121
+ };
2122
+ }
2123
+ const customerResult = await syncCustomerCore(request, {
2124
+ solvaPay: options.solvaPay,
2125
+ includeEmail: options.includeEmail,
2126
+ includeName: options.includeName
2127
+ });
2128
+ if (isErrorResult(customerResult)) {
2129
+ return customerResult;
2130
+ }
2131
+ const customerRef = customerResult;
2132
+ const solvaPay = options.solvaPay || createSolvaPay();
2133
+ return await solvaPay.activatePlan({
2134
+ customerRef,
2135
+ productRef: body.productRef,
2136
+ planRef: body.planRef
2137
+ });
2138
+ } catch (error) {
2139
+ return handleRouteError(error, "Activate plan", "Plan activation failed");
2140
+ }
2141
+ }
1835
2142
 
1836
2143
  // src/helpers/plans.ts
1837
2144
  import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
@@ -1874,6 +2181,89 @@ async function listPlansCore(request) {
1874
2181
  }
1875
2182
  }
1876
2183
 
2184
+ // src/helpers/purchase.ts
2185
+ async function checkPurchaseCore(request, options = {}) {
2186
+ try {
2187
+ const userResult = await getAuthenticatedUserCore(request, {
2188
+ includeEmail: options.includeEmail,
2189
+ includeName: options.includeName
2190
+ });
2191
+ if (isErrorResult(userResult)) {
2192
+ return userResult;
2193
+ }
2194
+ const { userId, email, name } = userResult;
2195
+ const solvaPay = options.solvaPay || createSolvaPay();
2196
+ const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
2197
+ if (cachedCustomerRef) {
2198
+ try {
2199
+ const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
2200
+ if (customer && customer.customerRef) {
2201
+ if (customer.externalRef && customer.externalRef === userId) {
2202
+ const filteredPurchases = (customer.purchases || []).filter(
2203
+ (p) => p.status === "active"
2204
+ );
2205
+ return {
2206
+ customerRef: customer.customerRef,
2207
+ email: customer.email,
2208
+ name: customer.name,
2209
+ purchases: filteredPurchases
2210
+ };
2211
+ }
2212
+ }
2213
+ } catch {
2214
+ }
2215
+ }
2216
+ try {
2217
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
2218
+ email: email || void 0,
2219
+ name: name || void 0
2220
+ });
2221
+ const customer = await solvaPay.getCustomer({ customerRef });
2222
+ const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
2223
+ return {
2224
+ customerRef: customer.customerRef || userId,
2225
+ email: customer.email,
2226
+ name: customer.name,
2227
+ purchases: filteredPurchases
2228
+ };
2229
+ } catch {
2230
+ return {
2231
+ customerRef: userId,
2232
+ purchases: []
2233
+ };
2234
+ }
2235
+ } catch (error) {
2236
+ return handleRouteError(error, "Check purchase", "Failed to check purchase");
2237
+ }
2238
+ }
2239
+
2240
+ // src/helpers/usage.ts
2241
+ async function trackUsageCore(request, body, options = {}) {
2242
+ try {
2243
+ const userResult = await getAuthenticatedUserCore(request);
2244
+ if (isErrorResult(userResult)) {
2245
+ return userResult;
2246
+ }
2247
+ const { userId, email, name } = userResult;
2248
+ const solvaPay = options.solvaPay || createSolvaPay();
2249
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
2250
+ email: email || void 0,
2251
+ name: name || void 0
2252
+ });
2253
+ await solvaPay.trackUsage({
2254
+ customerRef,
2255
+ actionType: body.actionType,
2256
+ units: body.units,
2257
+ productRef: body.productRef,
2258
+ description: body.description,
2259
+ metadata: body.metadata
2260
+ });
2261
+ return { success: true };
2262
+ } catch (error) {
2263
+ return handleRouteError(error, "Track usage", "Track usage failed");
2264
+ }
2265
+ }
2266
+
1877
2267
  // src/edge.ts
1878
2268
  function timingSafeEqual(a, b) {
1879
2269
  if (a.length !== b.length) return false;
@@ -1928,18 +2318,25 @@ async function verifyWebhook({
1928
2318
  }
1929
2319
  export {
1930
2320
  PaywallError,
2321
+ activatePlanCore,
1931
2322
  cancelPurchaseCore,
2323
+ checkPurchaseCore,
1932
2324
  createCheckoutSessionCore,
1933
2325
  createCustomerSessionCore,
1934
2326
  createPaymentIntentCore,
1935
2327
  createSolvaPay,
1936
2328
  createSolvaPayClient,
2329
+ createTopupPaymentIntentCore,
1937
2330
  getAuthenticatedUserCore,
2331
+ getCustomerBalanceCore,
1938
2332
  handleRouteError,
1939
2333
  isErrorResult,
1940
2334
  listPlansCore,
2335
+ paywallErrorToClientPayload,
1941
2336
  processPaymentIntentCore,
2337
+ reactivatePurchaseCore,
1942
2338
  syncCustomerCore,
2339
+ trackUsageCore,
1943
2340
  verifyWebhook,
1944
2341
  withRetry
1945
2342
  };