@solvapay/server 1.0.7 → 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 CHANGED
@@ -2488,6 +2488,26 @@ interface ProcessPaymentResult {
2488
2488
  status: 'completed';
2489
2489
  }
2490
2490
  type ActivatePlanResult = components['schemas']['ActivatePlanResponseDto'];
2491
+ /**
2492
+ * SDK-facing merchant identity (source: GET /v1/sdk/merchant).
2493
+ *
2494
+ * Hand-typed here until the OpenAPI generator picks up the new endpoint.
2495
+ * Fields match `SdkMerchantResponseDto` on the backend.
2496
+ */
2497
+ type SdkMerchantResponse = {
2498
+ displayName: string;
2499
+ legalName: string;
2500
+ supportEmail?: string;
2501
+ supportUrl?: string;
2502
+ termsUrl?: string;
2503
+ privacyUrl?: string;
2504
+ country?: string;
2505
+ defaultCurrency?: string;
2506
+ statementDescriptor?: string;
2507
+ logoUrl?: string;
2508
+ };
2509
+ /** SDK-facing product projection. Sourced from the existing OpenAPI spec. */
2510
+ type SdkProductResponse = components['schemas']['SdkProductResponse'];
2491
2511
  type McpBootstrapRequest = components['schemas']['McpBootstrapDto'];
2492
2512
  interface McpBootstrapResponse {
2493
2513
  product: components['schemas']['SdkProductResponse'];
@@ -2553,6 +2573,13 @@ interface SolvaPayClient {
2553
2573
  externalRef?: string;
2554
2574
  email?: string;
2555
2575
  }): Promise<CustomerResponseMapped>;
2576
+ /**
2577
+ * SDK-facing merchant identity (GET /v1/sdk/merchant).
2578
+ * Returns the subset of provider fields safe for browser consumption —
2579
+ * used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
2580
+ */
2581
+ getMerchant?(): Promise<SdkMerchantResponse>;
2582
+ getProduct?(productRef: string): Promise<SdkProductResponse>;
2556
2583
  listProducts?(): Promise<Array<{
2557
2584
  reference: string;
2558
2585
  name: string;
@@ -3733,6 +3760,13 @@ declare function getAuthenticatedUserCore(request: Request, options?: {
3733
3760
  * Works with standard Web API Request (works everywhere).
3734
3761
  */
3735
3762
 
3763
+ type CustomerBalanceResult = {
3764
+ customerRef: string;
3765
+ credits: number;
3766
+ displayCurrency: string;
3767
+ creditsPerMinorUnit: number;
3768
+ displayExchangeRate: number;
3769
+ };
3736
3770
  /**
3737
3771
  * Sync customer with SolvaPay backend (ensure customer exists).
3738
3772
  *
@@ -3775,6 +3809,18 @@ declare function syncCustomerCore(request: Request, options?: {
3775
3809
  includeEmail?: boolean;
3776
3810
  includeName?: boolean;
3777
3811
  }): Promise<string | ErrorResult>;
3812
+ /**
3813
+ * Get credits for the authenticated customer.
3814
+ *
3815
+ * Authenticates the request, syncs the customer, then fetches credits.
3816
+ *
3817
+ * @param request - Standard Web API Request object
3818
+ * @param options - Configuration options
3819
+ * @returns Customer credits result or error result
3820
+ */
3821
+ declare function getCustomerBalanceCore(request: Request, options?: {
3822
+ solvaPay?: SolvaPay;
3823
+ }): Promise<CustomerBalanceResult | ErrorResult>;
3778
3824
 
3779
3825
  /**
3780
3826
  * Create a payment intent for a customer to purchase a plan.
@@ -3969,6 +4015,31 @@ declare function cancelPurchaseCore(request: Request, body: {
3969
4015
  }, options?: {
3970
4016
  solvaPay?: SolvaPay;
3971
4017
  }): Promise<Record<string, unknown> | ErrorResult>;
4018
+ /**
4019
+ * Reactivate purchase - core implementation
4020
+ *
4021
+ * Undoes a pending cancellation, restoring auto-renewal and clearing cancellation fields.
4022
+ * Only works while the purchase is still active and the end date hasn't passed.
4023
+ *
4024
+ * @param request - Standard Web API Request
4025
+ * @param body - Reactivation parameters
4026
+ * @param options - Configuration options
4027
+ * @returns Reactivated purchase response or error result
4028
+ */
4029
+ declare function reactivatePurchaseCore(request: Request, body: {
4030
+ purchaseRef: string;
4031
+ }, options?: {
4032
+ solvaPay?: SolvaPay;
4033
+ }): Promise<Record<string, unknown> | ErrorResult>;
4034
+
4035
+ declare function activatePlanCore(request: Request, body: {
4036
+ productRef: string;
4037
+ planRef: string;
4038
+ }, options?: {
4039
+ solvaPay?: SolvaPay;
4040
+ includeEmail?: boolean;
4041
+ includeName?: boolean;
4042
+ }): Promise<ActivatePlanResult | ErrorResult>;
3972
4043
 
3973
4044
  /**
3974
4045
  * Plans Helper (Core)
@@ -3980,13 +4051,62 @@ declare function cancelPurchaseCore(request: Request, body: {
3980
4051
 
3981
4052
  type Plan = components['schemas']['Plan'];
3982
4053
  /**
3983
- * List plans - core implementation
4054
+ * List plans - core implementation.
4055
+ *
4056
+ * Pass `options.solvaPay` to route through a pre-configured SolvaPay instance
4057
+ * (e.g. stub-backed in examples). When omitted, the helper reads
4058
+ * `SOLVAPAY_SECRET_KEY` from environment and constructs a real API client.
3984
4059
  */
3985
- declare function listPlansCore(request: Request): Promise<{
4060
+ declare function listPlansCore(request: Request, options?: {
4061
+ solvaPay?: SolvaPay;
4062
+ }): Promise<{
3986
4063
  plans: Plan[];
3987
4064
  productRef: string;
3988
4065
  } | ErrorResult>;
3989
4066
 
4067
+ interface PurchaseCheckResult {
4068
+ customerRef: string;
4069
+ email?: string;
4070
+ name?: string;
4071
+ purchases: Array<{
4072
+ reference: string;
4073
+ productName?: string;
4074
+ productRef?: string;
4075
+ status?: string;
4076
+ startDate?: string;
4077
+ planSnapshot?: {
4078
+ meterId?: string;
4079
+ limit?: number;
4080
+ freeUnits?: number;
4081
+ };
4082
+ usage?: {
4083
+ used?: number;
4084
+ overageUnits?: number;
4085
+ overageCost?: number;
4086
+ periodStart?: string;
4087
+ periodEnd?: string;
4088
+ };
4089
+ [key: string]: unknown;
4090
+ }>;
4091
+ }
4092
+ declare function checkPurchaseCore(request: Request, options?: {
4093
+ solvaPay?: SolvaPay;
4094
+ includeEmail?: boolean;
4095
+ includeName?: boolean;
4096
+ }): Promise<PurchaseCheckResult | ErrorResult>;
4097
+
4098
+ declare function trackUsageCore(request: Request, body: {
4099
+ actionType?: 'transaction' | 'api_call' | 'hour' | 'email' | 'storage' | 'custom';
4100
+ units?: number;
4101
+ productRef?: string;
4102
+ description?: string;
4103
+ metadata?: Record<string, unknown>;
4104
+ }, options?: {
4105
+ solvaPay?: SolvaPay;
4106
+ }): Promise<{
4107
+ success: true;
4108
+ } | ErrorResult>;
4109
+
3990
4110
  /**
3991
4111
  * SolvaPay Server SDK - Edge Runtime Entry Point
3992
4112
  *
@@ -4000,4 +4120,4 @@ declare function verifyWebhook({ body, signature, secret, }: {
4000
4120
  secret: string;
4001
4121
  }): Promise<WebhookEvent>;
4002
4122
 
4003
- export { type AuthenticatedUser, type CreateSolvaPayConfig, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, type NextAdapterOptions, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type RetryOptions, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, cancelPurchaseCore, createCheckoutSessionCore, createCustomerSessionCore, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, getAuthenticatedUserCore, handleRouteError, isErrorResult, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, syncCustomerCore, verifyWebhook, withRetry };
4123
+ export { type AuthenticatedUser, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, type NextAdapterOptions, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type PurchaseCheckResult, type RetryOptions, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, cancelPurchaseCore, checkPurchaseCore, createCheckoutSessionCore, createCustomerSessionCore, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, getAuthenticatedUserCore, getCustomerBalanceCore, handleRouteError, isErrorResult, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };
package/dist/edge.js CHANGED
@@ -109,6 +109,36 @@ function createSolvaPayClient(opts) {
109
109
  purchases: customer.purchases || []
110
110
  };
111
111
  },
112
+ // GET: /v1/sdk/merchant
113
+ async getMerchant() {
114
+ const url = `${base}/v1/sdk/merchant`;
115
+ const res = await fetch(url, {
116
+ method: "GET",
117
+ headers
118
+ });
119
+ if (!res.ok) {
120
+ const error = await res.text();
121
+ log(`\u274C API Error: ${res.status} - ${error}`);
122
+ throw new SolvaPayError(`Get merchant failed (${res.status}): ${error}`);
123
+ }
124
+ return res.json();
125
+ },
126
+ // GET: /v1/sdk/products/{productRef}
127
+ async getProduct(productRef) {
128
+ const url = `${base}/v1/sdk/products/${encodeURIComponent(productRef)}`;
129
+ const res = await fetch(url, {
130
+ method: "GET",
131
+ headers
132
+ });
133
+ if (!res.ok) {
134
+ const error = await res.text();
135
+ log(`\u274C API Error: ${res.status} - ${error}`);
136
+ throw new SolvaPayError(`Get product failed (${res.status}): ${error}`);
137
+ }
138
+ const result = await res.json();
139
+ const data = result.data || {};
140
+ return { ...data, ...result };
141
+ },
112
142
  // Product management methods (primarily for integration tests)
113
143
  // GET: /v1/sdk/products
114
144
  async listProducts() {
@@ -1775,6 +1805,24 @@ async function syncCustomerCore(request, options = {}) {
1775
1805
  return handleRouteError(error, "Sync customer", "Failed to sync customer");
1776
1806
  }
1777
1807
  }
1808
+ async function getCustomerBalanceCore(request, options = {}) {
1809
+ try {
1810
+ const userResult = await getAuthenticatedUserCore(request);
1811
+ if (isErrorResult(userResult)) {
1812
+ return userResult;
1813
+ }
1814
+ const { userId, email, name } = userResult;
1815
+ const solvaPay = options.solvaPay || createSolvaPay();
1816
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
1817
+ email: email || void 0,
1818
+ name: name || void 0
1819
+ });
1820
+ const result = await solvaPay.getCustomerBalance({ customerRef });
1821
+ return result;
1822
+ } catch (error) {
1823
+ return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
1824
+ }
1825
+ }
1778
1826
 
1779
1827
  // src/helpers/payment.ts
1780
1828
  async function createPaymentIntentCore(request, body, options = {}) {
@@ -2024,10 +2072,107 @@ async function cancelPurchaseCore(request, body, options = {}) {
2024
2072
  return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
2025
2073
  }
2026
2074
  }
2075
+ async function reactivatePurchaseCore(request, body, options = {}) {
2076
+ try {
2077
+ if (!body.purchaseRef) {
2078
+ return {
2079
+ error: "Missing required parameter: purchaseRef is required",
2080
+ status: 400
2081
+ };
2082
+ }
2083
+ const solvaPay = options.solvaPay || createSolvaPay();
2084
+ if (!solvaPay.apiClient.reactivatePurchase) {
2085
+ return {
2086
+ error: "Reactivate purchase method not available on SDK client",
2087
+ status: 500
2088
+ };
2089
+ }
2090
+ let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
2091
+ purchaseRef: body.purchaseRef
2092
+ });
2093
+ if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
2094
+ return {
2095
+ error: "Invalid response from reactivate purchase endpoint",
2096
+ status: 500
2097
+ };
2098
+ }
2099
+ const responseObj = reactivatedPurchase;
2100
+ if (responseObj.purchase && typeof responseObj.purchase === "object") {
2101
+ reactivatedPurchase = responseObj.purchase;
2102
+ }
2103
+ if (!reactivatedPurchase.reference) {
2104
+ return {
2105
+ error: "Reactivate purchase response missing required fields",
2106
+ status: 500
2107
+ };
2108
+ }
2109
+ if (reactivatedPurchase.cancelledAt) {
2110
+ return {
2111
+ error: `Purchase reactivation failed: cancelledAt is still set`,
2112
+ status: 500
2113
+ };
2114
+ }
2115
+ await new Promise((resolve) => setTimeout(resolve, 500));
2116
+ return reactivatedPurchase;
2117
+ } catch (error) {
2118
+ if (error instanceof SolvaPayError4) {
2119
+ const errorMessage = error.message;
2120
+ if (errorMessage.includes("not found")) {
2121
+ return {
2122
+ error: "Purchase not found",
2123
+ status: 404,
2124
+ details: errorMessage
2125
+ };
2126
+ }
2127
+ if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
2128
+ return {
2129
+ error: "Purchase cannot be reactivated",
2130
+ status: 400,
2131
+ details: errorMessage
2132
+ };
2133
+ }
2134
+ return {
2135
+ error: errorMessage,
2136
+ status: 500,
2137
+ details: errorMessage
2138
+ };
2139
+ }
2140
+ return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
2141
+ }
2142
+ }
2143
+
2144
+ // src/helpers/activation.ts
2145
+ async function activatePlanCore(request, body, options = {}) {
2146
+ try {
2147
+ if (!body.productRef || !body.planRef) {
2148
+ return {
2149
+ error: "Missing required parameters: productRef and planRef are required",
2150
+ status: 400
2151
+ };
2152
+ }
2153
+ const customerResult = await syncCustomerCore(request, {
2154
+ solvaPay: options.solvaPay,
2155
+ includeEmail: options.includeEmail,
2156
+ includeName: options.includeName
2157
+ });
2158
+ if (isErrorResult(customerResult)) {
2159
+ return customerResult;
2160
+ }
2161
+ const customerRef = customerResult;
2162
+ const solvaPay = options.solvaPay || createSolvaPay();
2163
+ return await solvaPay.activatePlan({
2164
+ customerRef,
2165
+ productRef: body.productRef,
2166
+ planRef: body.planRef
2167
+ });
2168
+ } catch (error) {
2169
+ return handleRouteError(error, "Activate plan", "Plan activation failed");
2170
+ }
2171
+ }
2027
2172
 
2028
2173
  // src/helpers/plans.ts
2029
2174
  import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
2030
- async function listPlansCore(request) {
2175
+ async function listPlansCore(request, options = {}) {
2031
2176
  try {
2032
2177
  const url = new URL(request.url);
2033
2178
  const productRef = url.searchParams.get("productRef");
@@ -2037,19 +2182,20 @@ async function listPlansCore(request) {
2037
2182
  status: 400
2038
2183
  };
2039
2184
  }
2040
- const config = getSolvaPayConfig2();
2041
- const solvapaySecretKey = config.apiKey;
2042
- const solvapayApiBaseUrl = config.apiBaseUrl;
2043
- if (!solvapaySecretKey) {
2185
+ const apiClient = options.solvaPay?.apiClient ?? (() => {
2186
+ const config = getSolvaPayConfig2();
2187
+ if (!config.apiKey) return null;
2188
+ return createSolvaPayClient({
2189
+ apiKey: config.apiKey,
2190
+ apiBaseUrl: config.apiBaseUrl
2191
+ });
2192
+ })();
2193
+ if (!apiClient) {
2044
2194
  return {
2045
2195
  error: "Server configuration error: SolvaPay secret key not configured",
2046
2196
  status: 500
2047
2197
  };
2048
2198
  }
2049
- const apiClient = createSolvaPayClient({
2050
- apiKey: solvapaySecretKey,
2051
- apiBaseUrl: solvapayApiBaseUrl
2052
- });
2053
2199
  if (!apiClient.listPlans) {
2054
2200
  return {
2055
2201
  error: "List plans method not available",
@@ -2066,6 +2212,89 @@ async function listPlansCore(request) {
2066
2212
  }
2067
2213
  }
2068
2214
 
2215
+ // src/helpers/purchase.ts
2216
+ async function checkPurchaseCore(request, options = {}) {
2217
+ try {
2218
+ const userResult = await getAuthenticatedUserCore(request, {
2219
+ includeEmail: options.includeEmail,
2220
+ includeName: options.includeName
2221
+ });
2222
+ if (isErrorResult(userResult)) {
2223
+ return userResult;
2224
+ }
2225
+ const { userId, email, name } = userResult;
2226
+ const solvaPay = options.solvaPay || createSolvaPay();
2227
+ const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
2228
+ if (cachedCustomerRef) {
2229
+ try {
2230
+ const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
2231
+ if (customer && customer.customerRef) {
2232
+ if (customer.externalRef && customer.externalRef === userId) {
2233
+ const filteredPurchases = (customer.purchases || []).filter(
2234
+ (p) => p.status === "active"
2235
+ );
2236
+ return {
2237
+ customerRef: customer.customerRef,
2238
+ email: customer.email,
2239
+ name: customer.name,
2240
+ purchases: filteredPurchases
2241
+ };
2242
+ }
2243
+ }
2244
+ } catch {
2245
+ }
2246
+ }
2247
+ try {
2248
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
2249
+ email: email || void 0,
2250
+ name: name || void 0
2251
+ });
2252
+ const customer = await solvaPay.getCustomer({ customerRef });
2253
+ const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
2254
+ return {
2255
+ customerRef: customer.customerRef || userId,
2256
+ email: customer.email,
2257
+ name: customer.name,
2258
+ purchases: filteredPurchases
2259
+ };
2260
+ } catch {
2261
+ return {
2262
+ customerRef: userId,
2263
+ purchases: []
2264
+ };
2265
+ }
2266
+ } catch (error) {
2267
+ return handleRouteError(error, "Check purchase", "Failed to check purchase");
2268
+ }
2269
+ }
2270
+
2271
+ // src/helpers/usage.ts
2272
+ async function trackUsageCore(request, body, options = {}) {
2273
+ try {
2274
+ const userResult = await getAuthenticatedUserCore(request);
2275
+ if (isErrorResult(userResult)) {
2276
+ return userResult;
2277
+ }
2278
+ const { userId, email, name } = userResult;
2279
+ const solvaPay = options.solvaPay || createSolvaPay();
2280
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
2281
+ email: email || void 0,
2282
+ name: name || void 0
2283
+ });
2284
+ await solvaPay.trackUsage({
2285
+ customerRef,
2286
+ actionType: body.actionType,
2287
+ units: body.units,
2288
+ productRef: body.productRef,
2289
+ description: body.description,
2290
+ metadata: body.metadata
2291
+ });
2292
+ return { success: true };
2293
+ } catch (error) {
2294
+ return handleRouteError(error, "Track usage", "Track usage failed");
2295
+ }
2296
+ }
2297
+
2069
2298
  // src/edge.ts
2070
2299
  function timingSafeEqual(a, b) {
2071
2300
  if (a.length !== b.length) return false;
@@ -2120,7 +2349,9 @@ async function verifyWebhook({
2120
2349
  }
2121
2350
  export {
2122
2351
  PaywallError,
2352
+ activatePlanCore,
2123
2353
  cancelPurchaseCore,
2354
+ checkPurchaseCore,
2124
2355
  createCheckoutSessionCore,
2125
2356
  createCustomerSessionCore,
2126
2357
  createPaymentIntentCore,
@@ -2128,12 +2359,15 @@ export {
2128
2359
  createSolvaPayClient,
2129
2360
  createTopupPaymentIntentCore,
2130
2361
  getAuthenticatedUserCore,
2362
+ getCustomerBalanceCore,
2131
2363
  handleRouteError,
2132
2364
  isErrorResult,
2133
2365
  listPlansCore,
2134
2366
  paywallErrorToClientPayload,
2135
2367
  processPaymentIntentCore,
2368
+ reactivatePurchaseCore,
2136
2369
  syncCustomerCore,
2370
+ trackUsageCore,
2137
2371
  verifyWebhook,
2138
2372
  withRetry
2139
2373
  };