@solvapay/server 1.0.0-preview.2 → 1.0.0-preview.20

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
@@ -1,18 +1,16 @@
1
- import {
2
- __require
3
- } from "./chunk-R5U7XKVJ.js";
1
+ import "./chunk-MLKGABMK.js";
4
2
 
5
3
  // src/edge.ts
6
- import { SolvaPayError as SolvaPayError3 } from "@solvapay/core";
4
+ import { SolvaPayError as SolvaPayError5 } from "@solvapay/core";
7
5
 
8
6
  // src/client.ts
9
7
  import { SolvaPayError } from "@solvapay/core";
10
8
  function createSolvaPayClient(opts) {
11
- const base = opts.apiBaseUrl ?? "https://api-dev.solvapay.com";
9
+ const base = opts.apiBaseUrl ?? "https://api.solvapay.com";
12
10
  if (!opts.apiKey) throw new SolvaPayError("Missing apiKey");
13
11
  const headers = {
14
12
  "Content-Type": "application/json",
15
- "Authorization": `Bearer ${opts.apiKey}`
13
+ Authorization: `Bearer ${opts.apiKey}`
16
14
  };
17
15
  const debug = process.env.SOLVAPAY_DEBUG === "true";
18
16
  const log = (...args) => {
@@ -20,15 +18,10 @@ function createSolvaPayClient(opts) {
20
18
  console.log(...args);
21
19
  }
22
20
  };
23
- log(`\u{1F50C} SolvaPay API Client initialized`);
24
- log(` Backend URL: ${base}`);
25
- log(` API Key: ${opts.apiKey.substring(0, 10)}...`);
26
21
  return {
27
22
  // POST: /v1/sdk/limits
28
23
  async checkLimits(params) {
29
24
  const url = `${base}/v1/sdk/limits`;
30
- log(`\u{1F4E1} API Request: POST ${url}`);
31
- log(` Params:`, JSON.stringify(params, null, 2));
32
25
  const res = await fetch(url, {
33
26
  method: "POST",
34
27
  headers,
@@ -40,20 +33,11 @@ function createSolvaPayClient(opts) {
40
33
  throw new SolvaPayError(`Check limits failed (${res.status}): ${error}`);
41
34
  }
42
35
  const result = await res.json();
43
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
44
- log(`\u{1F50D} DEBUG - checkLimits breakdown:`);
45
- log(` - withinLimits: ${result.withinLimits}`);
46
- log(` - remaining: ${result.remaining}`);
47
- log(` - plan: ${result.plan || "N/A"}`);
48
- log(` - checkoutUrl: ${result.checkoutUrl || "N/A"}`);
49
- log(` - Full response keys:`, Object.keys(result));
50
36
  return result;
51
37
  },
52
38
  // POST: /v1/sdk/usages
53
39
  async trackUsage(params) {
54
40
  const url = `${base}/v1/sdk/usages`;
55
- log(`\u{1F4E1} API Request: POST ${url}`);
56
- log(` Params:`, JSON.stringify(params, null, 2));
57
41
  const res = await fetch(url, {
58
42
  method: "POST",
59
43
  headers,
@@ -64,13 +48,10 @@ function createSolvaPayClient(opts) {
64
48
  log(`\u274C API Error: ${res.status} - ${error}`);
65
49
  throw new SolvaPayError(`Track usage failed (${res.status}): ${error}`);
66
50
  }
67
- log(`\u2705 Usage tracked successfully`);
68
51
  },
69
52
  // POST: /v1/sdk/customers
70
53
  async createCustomer(params) {
71
54
  const url = `${base}/v1/sdk/customers`;
72
- log(`\u{1F4E1} API Request: POST ${url}`);
73
- log(` Params:`, JSON.stringify(params, null, 2));
74
55
  const res = await fetch(url, {
75
56
  method: "POST",
76
57
  headers,
@@ -82,18 +63,24 @@ function createSolvaPayClient(opts) {
82
63
  throw new SolvaPayError(`Create customer failed (${res.status}): ${error}`);
83
64
  }
84
65
  const result = await res.json();
85
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
86
- log(`\u{1F50D} DEBUG - createCustomer response:`);
87
- log(` - reference/customerRef: ${result.reference || result.customerRef}`);
88
- log(` - Has plan info: ${result.plan ? "YES" : "NO"}`);
89
- log(` - Has subscription info: ${result.subscription ? "YES" : "NO"}`);
90
- log(` - Full response keys:`, Object.keys(result));
91
66
  return result;
92
67
  },
93
- // GET: /v1/sdk/customers/{reference}
68
+ // GET: /v1/sdk/customers/{reference} or /v1/sdk/customers?externalRef={externalRef}|email={email}
94
69
  async getCustomer(params) {
95
- const url = `${base}/v1/sdk/customers/${params.customerRef}`;
96
- log(`\u{1F4E1} API Request: GET ${url}`);
70
+ let url;
71
+ let isByExternalRef = false;
72
+ let isByEmail = false;
73
+ if (params.externalRef) {
74
+ url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
75
+ isByExternalRef = true;
76
+ } else if (params.email) {
77
+ url = `${base}/v1/sdk/customers?email=${encodeURIComponent(params.email)}`;
78
+ isByEmail = true;
79
+ } else if (params.customerRef) {
80
+ url = `${base}/v1/sdk/customers/${params.customerRef}`;
81
+ } else {
82
+ throw new SolvaPayError("One of customerRef, externalRef, or email must be provided");
83
+ }
97
84
  const res = await fetch(url, {
98
85
  method: "GET",
99
86
  headers
@@ -104,14 +91,28 @@ function createSolvaPayClient(opts) {
104
91
  throw new SolvaPayError(`Get customer failed (${res.status}): ${error}`);
105
92
  }
106
93
  const result = await res.json();
107
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
108
- return result;
94
+ let customer = result;
95
+ if (isByExternalRef || isByEmail) {
96
+ const directCustomer = result && typeof result === "object" && (result.reference || result.customerRef || result.externalRef) ? result : void 0;
97
+ const wrappedCustomer = result && typeof result === "object" && result.customer ? result.customer : void 0;
98
+ const customers = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.customers) ? result.customers : [];
99
+ customer = directCustomer || wrappedCustomer || customers[0];
100
+ if (!customer) {
101
+ throw new SolvaPayError(`No customer found with externalRef: ${params.externalRef}`);
102
+ }
103
+ }
104
+ return {
105
+ customerRef: customer.reference || customer.customerRef,
106
+ email: customer.email,
107
+ name: customer.name,
108
+ externalRef: customer.externalRef,
109
+ purchases: customer.purchases || []
110
+ };
109
111
  },
110
- // Management methods (primarily for integration tests)
111
- // GET: /v1/sdk/agents
112
- async listAgents() {
113
- const url = `${base}/v1/sdk/agents`;
114
- log(`\u{1F4E1} API Request: GET ${url}`);
112
+ // Product management methods (primarily for integration tests)
113
+ // GET: /v1/sdk/products
114
+ async listProducts() {
115
+ const url = `${base}/v1/sdk/products`;
115
116
  const res = await fetch(url, {
116
117
  method: "GET",
117
118
  headers
@@ -119,21 +120,18 @@ function createSolvaPayClient(opts) {
119
120
  if (!res.ok) {
120
121
  const error = await res.text();
121
122
  log(`\u274C API Error: ${res.status} - ${error}`);
122
- throw new SolvaPayError(`List agents failed (${res.status}): ${error}`);
123
+ throw new SolvaPayError(`List products failed (${res.status}): ${error}`);
123
124
  }
124
125
  const result = await res.json();
125
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
126
- const agents = Array.isArray(result) ? result : result.agents || [];
127
- return agents.map((agent) => ({
128
- ...agent,
129
- ...agent.data || {}
126
+ const products = Array.isArray(result) ? result : result.products || [];
127
+ return products.map((product) => ({
128
+ ...product,
129
+ ...product.data || {}
130
130
  }));
131
131
  },
132
- // POST: /v1/sdk/agents
133
- async createAgent(params) {
134
- const url = `${base}/v1/sdk/agents`;
135
- log(`\u{1F4E1} API Request: POST ${url}`);
136
- log(` Params:`, JSON.stringify(params, null, 2));
132
+ // POST: /v1/sdk/products
133
+ async createProduct(params) {
134
+ const url = `${base}/v1/sdk/products`;
137
135
  const res = await fetch(url, {
138
136
  method: "POST",
139
137
  headers,
@@ -142,16 +140,14 @@ function createSolvaPayClient(opts) {
142
140
  if (!res.ok) {
143
141
  const error = await res.text();
144
142
  log(`\u274C API Error: ${res.status} - ${error}`);
145
- throw new SolvaPayError(`Create agent failed (${res.status}): ${error}`);
143
+ throw new SolvaPayError(`Create product failed (${res.status}): ${error}`);
146
144
  }
147
145
  const result = await res.json();
148
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
149
146
  return result;
150
147
  },
151
- // DELETE: /v1/sdk/agents/{agentRef}
152
- async deleteAgent(agentRef) {
153
- const url = `${base}/v1/sdk/agents/${agentRef}`;
154
- log(`\u{1F4E1} API Request: DELETE ${url}`);
148
+ // DELETE: /v1/sdk/products/{productRef}
149
+ async deleteProduct(productRef) {
150
+ const url = `${base}/v1/sdk/products/${productRef}`;
155
151
  const res = await fetch(url, {
156
152
  method: "DELETE",
157
153
  headers
@@ -159,14 +155,12 @@ function createSolvaPayClient(opts) {
159
155
  if (!res.ok && res.status !== 404) {
160
156
  const error = await res.text();
161
157
  log(`\u274C API Error: ${res.status} - ${error}`);
162
- throw new SolvaPayError(`Delete agent failed (${res.status}): ${error}`);
158
+ throw new SolvaPayError(`Delete product failed (${res.status}): ${error}`);
163
159
  }
164
- log(`\u2705 Agent deleted successfully`);
165
160
  },
166
- // GET: /v1/sdk/agents/{agentRef}/plans
167
- async listPlans(agentRef) {
168
- const url = `${base}/v1/sdk/agents/${agentRef}/plans`;
169
- log(`\u{1F4E1} API Request: GET ${url}`);
161
+ // GET: /v1/sdk/products/{productRef}/plans
162
+ async listPlans(productRef) {
163
+ const url = `${base}/v1/sdk/products/${productRef}/plans`;
170
164
  const res = await fetch(url, {
171
165
  method: "GET",
172
166
  headers
@@ -177,18 +171,22 @@ function createSolvaPayClient(opts) {
177
171
  throw new SolvaPayError(`List plans failed (${res.status}): ${error}`);
178
172
  }
179
173
  const result = await res.json();
180
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
181
174
  const plans = Array.isArray(result) ? result : result.plans || [];
182
- return plans.map((plan) => ({
183
- ...plan,
184
- ...plan.data || {}
185
- }));
175
+ return plans.map((plan) => {
176
+ const data = plan.data || {};
177
+ const price = plan.price ?? data.price;
178
+ const unwrapped = {
179
+ ...data,
180
+ ...plan,
181
+ ...price !== void 0 && { price }
182
+ };
183
+ delete unwrapped.data;
184
+ return unwrapped;
185
+ });
186
186
  },
187
- // POST: /v1/sdk/agents/{agentRef}/plans
187
+ // POST: /v1/sdk/products/{productRef}/plans
188
188
  async createPlan(params) {
189
- const url = `${base}/v1/sdk/agents/${params.agentRef}/plans`;
190
- log(`\u{1F4E1} API Request: POST ${url}`);
191
- log(` Params:`, JSON.stringify(params, null, 2));
189
+ const url = `${base}/v1/sdk/products/${params.productRef}/plans`;
192
190
  const res = await fetch(url, {
193
191
  method: "POST",
194
192
  headers,
@@ -200,13 +198,11 @@ function createSolvaPayClient(opts) {
200
198
  throw new SolvaPayError(`Create plan failed (${res.status}): ${error}`);
201
199
  }
202
200
  const result = await res.json();
203
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
204
201
  return result;
205
202
  },
206
- // DELETE: /v1/sdk/agents/{agentRef}/plans/{planRef}
207
- async deletePlan(agentRef, planRef) {
208
- const url = `${base}/v1/sdk/agents/${agentRef}/plans/${planRef}`;
209
- log(`\u{1F4E1} API Request: DELETE ${url}`);
203
+ // DELETE: /v1/sdk/products/{productRef}/plans/{planRef}
204
+ async deletePlan(productRef, planRef) {
205
+ const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
210
206
  const res = await fetch(url, {
211
207
  method: "DELETE",
212
208
  headers
@@ -216,17 +212,11 @@ function createSolvaPayClient(opts) {
216
212
  log(`\u274C API Error: ${res.status} - ${error}`);
217
213
  throw new SolvaPayError(`Delete plan failed (${res.status}): ${error}`);
218
214
  }
219
- log(`\u2705 Plan deleted successfully`);
220
215
  },
221
216
  // POST: /payment-intents
222
217
  async createPaymentIntent(params) {
223
218
  const idempotencyKey = params.idempotencyKey || `payment-${params.planRef}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
224
219
  const url = `${base}/v1/sdk/payment-intents`;
225
- log(`\u{1F4E1} API Request: POST ${url}`);
226
- log(` Agent Ref: ${params.agentRef}`);
227
- log(` Plan Ref: ${params.planRef}`);
228
- log(` Customer Ref: ${params.customerRef}`);
229
- log(` Idempotency Key: ${idempotencyKey}`);
230
220
  const res = await fetch(url, {
231
221
  method: "POST",
232
222
  headers: {
@@ -234,7 +224,7 @@ function createSolvaPayClient(opts) {
234
224
  "Idempotency-Key": idempotencyKey
235
225
  },
236
226
  body: JSON.stringify({
237
- agentRef: params.agentRef,
227
+ productRef: params.productRef,
238
228
  planRef: params.planRef,
239
229
  customerReference: params.customerRef
240
230
  })
@@ -245,12 +235,113 @@ function createSolvaPayClient(opts) {
245
235
  throw new SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
246
236
  }
247
237
  const result = await res.json();
248
- log(`\u2705 Payment intent created:`, {
249
- id: result.id,
250
- hasClientSecret: !!result.clientSecret,
251
- hasPublishableKey: !!result.publishableKey,
252
- accountId: result.accountId
238
+ return result;
239
+ },
240
+ // POST: /v1/sdk/payment-intents/{paymentIntentId}/process
241
+ async processPaymentIntent(params) {
242
+ const url = `${base}/v1/sdk/payment-intents/${params.paymentIntentId}/process`;
243
+ const res = await fetch(url, {
244
+ method: "POST",
245
+ headers: {
246
+ ...headers,
247
+ "Content-Type": "application/json"
248
+ },
249
+ body: JSON.stringify({
250
+ productRef: params.productRef,
251
+ customerRef: params.customerRef,
252
+ planRef: params.planRef
253
+ })
253
254
  });
255
+ if (!res.ok) {
256
+ const error = await res.text();
257
+ log(`\u274C API Error: ${res.status} - ${error}`);
258
+ throw new SolvaPayError(`Process payment failed (${res.status}): ${error}`);
259
+ }
260
+ const result = await res.json();
261
+ return result;
262
+ },
263
+ // POST: /v1/sdk/purchases/{purchaseRef}/cancel
264
+ async cancelPurchase(params) {
265
+ const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/cancel`;
266
+ const requestOptions = {
267
+ method: "POST",
268
+ headers
269
+ };
270
+ if (params.reason) {
271
+ requestOptions.body = JSON.stringify({ reason: params.reason });
272
+ }
273
+ const res = await fetch(url, requestOptions);
274
+ if (!res.ok) {
275
+ const error = await res.text();
276
+ log(`\u274C API Error: ${res.status} - ${error}`);
277
+ if (res.status === 404) {
278
+ throw new SolvaPayError(`Purchase not found: ${error}`);
279
+ }
280
+ if (res.status === 400) {
281
+ throw new SolvaPayError(
282
+ `Purchase cannot be cancelled or does not belong to provider: ${error}`
283
+ );
284
+ }
285
+ throw new SolvaPayError(`Cancel purchase failed (${res.status}): ${error}`);
286
+ }
287
+ const responseText = await res.text();
288
+ let responseData;
289
+ try {
290
+ responseData = JSON.parse(responseText);
291
+ } catch (parseError) {
292
+ log(`\u274C Failed to parse response as JSON: ${parseError}`);
293
+ throw new SolvaPayError(
294
+ `Invalid JSON response from cancel purchase endpoint: ${responseText.substring(0, 200)}`
295
+ );
296
+ }
297
+ if (!responseData || typeof responseData !== "object") {
298
+ log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
299
+ throw new SolvaPayError(`Invalid response structure from cancel purchase endpoint`);
300
+ }
301
+ let result;
302
+ if (responseData.purchase && typeof responseData.purchase === "object") {
303
+ result = responseData.purchase;
304
+ } else if (responseData.reference) {
305
+ result = responseData;
306
+ } else {
307
+ result = responseData.purchase || responseData;
308
+ }
309
+ if (!result || typeof result !== "object") {
310
+ log(`\u274C Invalid purchase data in response. Full response:`, responseData);
311
+ throw new SolvaPayError(`Invalid purchase data in cancel purchase response`);
312
+ }
313
+ return result;
314
+ },
315
+ // POST: /v1/sdk/checkout-sessions
316
+ async createCheckoutSession(params) {
317
+ const url = `${base}/v1/sdk/checkout-sessions`;
318
+ const res = await fetch(url, {
319
+ method: "POST",
320
+ headers,
321
+ body: JSON.stringify(params)
322
+ });
323
+ if (!res.ok) {
324
+ const error = await res.text();
325
+ log(`\u274C API Error: ${res.status} - ${error}`);
326
+ throw new SolvaPayError(`Create checkout session failed (${res.status}): ${error}`);
327
+ }
328
+ const result = await res.json();
329
+ return result;
330
+ },
331
+ // POST: /v1/sdk/customers/customer-sessions
332
+ async createCustomerSession(params) {
333
+ const url = `${base}/v1/sdk/customers/customer-sessions`;
334
+ const res = await fetch(url, {
335
+ method: "POST",
336
+ headers,
337
+ body: JSON.stringify(params)
338
+ });
339
+ if (!res.ok) {
340
+ const error = await res.text();
341
+ log(`\u274C API Error: ${res.status} - ${error}`);
342
+ throw new SolvaPayError(`Create customer session failed (${res.status}): ${error}`);
343
+ }
344
+ const result = await res.json();
254
345
  return result;
255
346
  }
256
347
  };
@@ -302,19 +393,123 @@ function calculateDelay(initialDelay, attempt, strategy) {
302
393
  function sleep(ms) {
303
394
  return new Promise((resolve) => setTimeout(resolve, ms));
304
395
  }
396
+ function createRequestDeduplicator(options = {}) {
397
+ const { cacheTTL = 2e3, maxCacheSize = 1e3, cacheErrors = true } = options;
398
+ const inFlightRequests = /* @__PURE__ */ new Map();
399
+ const resultCache = /* @__PURE__ */ new Map();
400
+ let _cleanupInterval = null;
401
+ if (cacheTTL > 0) {
402
+ _cleanupInterval = setInterval(
403
+ () => {
404
+ const now = Date.now();
405
+ const entriesToDelete = [];
406
+ for (const [key, cached] of resultCache.entries()) {
407
+ if (now - cached.timestamp >= cacheTTL) {
408
+ entriesToDelete.push(key);
409
+ }
410
+ }
411
+ for (const key of entriesToDelete) {
412
+ resultCache.delete(key);
413
+ }
414
+ if (resultCache.size > maxCacheSize) {
415
+ const sortedEntries = Array.from(resultCache.entries()).sort(
416
+ (a, b) => a[1].timestamp - b[1].timestamp
417
+ );
418
+ const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
419
+ for (const [key] of toRemove) {
420
+ resultCache.delete(key);
421
+ }
422
+ }
423
+ },
424
+ Math.min(cacheTTL, 1e3)
425
+ );
426
+ }
427
+ const deduplicate = async (key, fn) => {
428
+ if (cacheTTL > 0) {
429
+ const cached = resultCache.get(key);
430
+ if (cached && Date.now() - cached.timestamp < cacheTTL) {
431
+ return cached.data;
432
+ } else if (cached) {
433
+ resultCache.delete(key);
434
+ }
435
+ }
436
+ let requestPromise = inFlightRequests.get(key);
437
+ if (!requestPromise) {
438
+ requestPromise = (async () => {
439
+ try {
440
+ const result = await fn();
441
+ if (cacheTTL > 0) {
442
+ resultCache.set(key, {
443
+ data: result,
444
+ timestamp: Date.now()
445
+ });
446
+ }
447
+ return result;
448
+ } catch (error) {
449
+ if (cacheTTL > 0 && cacheErrors) {
450
+ resultCache.set(key, {
451
+ data: error,
452
+ timestamp: Date.now()
453
+ });
454
+ }
455
+ throw error;
456
+ } finally {
457
+ inFlightRequests.delete(key);
458
+ }
459
+ })();
460
+ const existingPromise = inFlightRequests.get(key);
461
+ if (existingPromise) {
462
+ requestPromise = existingPromise;
463
+ } else {
464
+ inFlightRequests.set(key, requestPromise);
465
+ }
466
+ }
467
+ return requestPromise;
468
+ };
469
+ const clearCache = (key) => {
470
+ resultCache.delete(key);
471
+ };
472
+ const clearAllCache = () => {
473
+ resultCache.clear();
474
+ };
475
+ const getStats = () => ({
476
+ inFlight: inFlightRequests.size,
477
+ cached: resultCache.size
478
+ });
479
+ return {
480
+ deduplicate,
481
+ clearCache,
482
+ clearAllCache,
483
+ getStats
484
+ };
485
+ }
305
486
 
306
487
  // src/paywall.ts
307
488
  var PaywallError = class extends Error {
489
+ /**
490
+ * Creates a new PaywallError instance.
491
+ *
492
+ * @param message - Error message
493
+ * @param structuredContent - Structured content with checkout URLs and metadata
494
+ */
308
495
  constructor(message, structuredContent) {
309
496
  super(message);
310
497
  this.structuredContent = structuredContent;
311
498
  this.name = "PaywallError";
312
499
  }
313
500
  };
501
+ var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
502
+ cacheTTL: 6e4,
503
+ // Cache results for 60 seconds (reduces API calls significantly)
504
+ maxCacheSize: 1e3,
505
+ // Maximum cache entries
506
+ cacheErrors: false
507
+ // Don't cache errors - retry on next request
508
+ });
314
509
  var SolvaPayPaywall = class {
315
510
  constructor(apiClient, options = {}) {
316
511
  this.apiClient = apiClient;
317
- this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG !== "false";
512
+ this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG === "true";
318
513
  }
319
514
  customerCreationAttempts = /* @__PURE__ */ new Set();
320
515
  customerRefMapping = /* @__PURE__ */ new Map();
@@ -325,16 +520,8 @@ var SolvaPayPaywall = class {
325
520
  console.log(...args);
326
521
  }
327
522
  }
328
- resolveAgent(metadata) {
329
- return metadata.agent || process.env.SOLVAPAY_AGENT || this.getPackageJsonName() || "default-agent";
330
- }
331
- getPackageJsonName() {
332
- try {
333
- const pkg = __require(process.cwd() + "/package.json");
334
- return pkg.name;
335
- } catch {
336
- return void 0;
337
- }
523
+ resolveProduct(metadata) {
524
+ return metadata.product || process.env.SOLVAPAY_PRODUCT || "default-product";
338
525
  }
339
526
  generateRequestId() {
340
527
  const timestamp = Date.now();
@@ -344,47 +531,75 @@ var SolvaPayPaywall = class {
344
531
  /**
345
532
  * Core protection method - works for both MCP and HTTP
346
533
  */
534
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
347
535
  async protect(handler, metadata = {}, getCustomerRef) {
348
- const agent = this.resolveAgent(metadata);
536
+ const product = this.resolveProduct(metadata);
349
537
  const toolName = handler.name || "anonymous";
350
538
  return async (args) => {
351
539
  const startTime = Date.now();
352
540
  const requestId = this.generateRequestId();
353
541
  const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
354
- const backendCustomerRef = await this.ensureCustomer(inputCustomerRef);
542
+ let backendCustomerRef;
543
+ if (inputCustomerRef.startsWith("cus_")) {
544
+ backendCustomerRef = inputCustomerRef;
545
+ } else {
546
+ backendCustomerRef = await this.ensureCustomer(inputCustomerRef, inputCustomerRef);
547
+ }
355
548
  try {
356
549
  const planRef = metadata.plan || toolName;
357
- this.log(`\u{1F50D} Checking limits for customer: ${backendCustomerRef}, agent: ${agent}, plan: ${planRef}`);
358
550
  const limitsCheck = await this.apiClient.checkLimits({
359
551
  customerRef: backendCustomerRef,
360
- agentRef: agent
552
+ productRef: product
361
553
  });
362
- this.log(`\u2713 Limits check passed:`, limitsCheck);
363
554
  if (!limitsCheck.withinLimits) {
364
555
  const latencyMs2 = Date.now() - startTime;
365
- this.log(`\u{1F6AB} Paywall triggered - tracking usage`);
366
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "paywall", requestId, latencyMs2);
556
+ await this.trackUsage(
557
+ backendCustomerRef,
558
+ product,
559
+ planRef,
560
+ toolName,
561
+ "paywall",
562
+ requestId,
563
+ latencyMs2
564
+ );
367
565
  throw new PaywallError("Payment required", {
368
566
  kind: "payment_required",
369
- agent,
567
+ product,
370
568
  checkoutUrl: limitsCheck.checkoutUrl || "",
371
- message: `Plan subscription required. Remaining: ${limitsCheck.remaining}`
569
+ message: `Plan purchase required. Remaining: ${limitsCheck.remaining}`
372
570
  });
373
571
  }
374
- this.log(`\u26A1 Executing handler: ${toolName}`);
375
572
  const result = await handler(args);
376
- this.log(`\u2713 Handler completed successfully`);
377
573
  const latencyMs = Date.now() - startTime;
378
- this.log(`\u{1F4CA} Tracking successful usage`);
379
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "success", requestId, latencyMs);
380
- this.log(`\u2705 Request completed successfully`);
574
+ await this.trackUsage(
575
+ backendCustomerRef,
576
+ product,
577
+ planRef,
578
+ toolName,
579
+ "success",
580
+ requestId,
581
+ latencyMs
582
+ );
381
583
  return result;
382
584
  } catch (error) {
383
- this.log(`\u274C Error in paywall:`, error);
585
+ if (error instanceof Error) {
586
+ const errorType = error instanceof PaywallError ? "PaywallError" : "API Error";
587
+ this.log(`\u274C Error in paywall [${errorType}]: ${error.message}`);
588
+ } else {
589
+ this.log(`\u274C Error in paywall:`, error);
590
+ }
384
591
  const latencyMs = Date.now() - startTime;
385
592
  const outcome = error instanceof PaywallError ? "paywall" : "fail";
386
593
  const planRef = metadata.plan || toolName;
387
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, outcome, requestId, latencyMs);
594
+ await this.trackUsage(
595
+ backendCustomerRef,
596
+ product,
597
+ planRef,
598
+ toolName,
599
+ outcome,
600
+ requestId,
601
+ latencyMs
602
+ );
388
603
  throw error;
389
604
  }
390
605
  };
@@ -394,47 +609,150 @@ var SolvaPayPaywall = class {
394
609
  * This is a public helper for testing, pre-creating customers, and internal use.
395
610
  * Only attempts creation once per customer (idempotent).
396
611
  * Returns the backend customer reference to use in API calls.
612
+ *
613
+ * @param customerRef - The customer reference used as a cache key (e.g., Supabase user ID)
614
+ * @param externalRef - Optional external reference for backend lookup (e.g., Supabase user ID)
615
+ * If provided, will lookup existing customer by externalRef before creating new one.
616
+ * The externalRef is stored on the SolvaPay backend for customer lookup.
617
+ * @param options - Optional customer details (email, name) for customer creation
397
618
  */
398
- async ensureCustomer(customerRef) {
619
+ async ensureCustomer(customerRef, externalRef, options) {
399
620
  if (this.customerRefMapping.has(customerRef)) {
400
621
  return this.customerRefMapping.get(customerRef);
401
622
  }
402
623
  if (customerRef === "anonymous") {
403
624
  return customerRef;
404
625
  }
405
- if (this.customerCreationAttempts.has(customerRef)) {
626
+ if (customerRef.startsWith("cus_")) {
406
627
  return customerRef;
407
628
  }
408
- if (!this.apiClient.createCustomer) {
409
- console.warn(`\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`);
410
- return customerRef;
629
+ const cacheKey = externalRef || customerRef;
630
+ if (this.customerRefMapping.has(customerRef)) {
631
+ const cached = this.customerRefMapping.get(customerRef);
632
+ return cached;
411
633
  }
412
- this.customerCreationAttempts.add(customerRef);
413
- try {
414
- this.log(`\u{1F527} Auto-creating customer: ${customerRef}`);
415
- const result = await this.apiClient.createCustomer({
416
- email: `${customerRef}@auto-created.local`,
417
- name: customerRef
418
- });
419
- const backendRef = result.customerRef || result.reference || customerRef;
420
- this.log(`\u2705 Successfully created customer: ${customerRef} -> ${backendRef}`, result);
421
- this.log(`\u{1F50D} DEBUG - ensureCustomer analysis:`);
422
- this.log(` - Input customerRef: ${customerRef}`);
423
- this.log(` - Backend customerRef: ${backendRef}`);
424
- this.log(` - Has plan in response: ${result.plan ? "YES - " + result.plan : "NO"}`);
425
- this.log(` - Has subscription in response: ${result.subscription ? "YES" : "NO"}`);
634
+ const backendRef = await sharedCustomerLookupDeduplicator.deduplicate(cacheKey, async () => {
635
+ if (externalRef) {
636
+ try {
637
+ const existingCustomer = await this.apiClient.getCustomer({ externalRef });
638
+ if (existingCustomer && existingCustomer.customerRef) {
639
+ const ref = existingCustomer.customerRef;
640
+ this.customerRefMapping.set(customerRef, ref);
641
+ this.customerCreationAttempts.add(customerRef);
642
+ if (externalRef !== customerRef) {
643
+ this.customerCreationAttempts.add(externalRef);
644
+ }
645
+ return ref;
646
+ }
647
+ } catch (error) {
648
+ const errorMessage = error instanceof Error ? error.message : String(error);
649
+ if (!errorMessage.includes("404") && !errorMessage.includes("not found")) {
650
+ this.log(`\u26A0\uFE0F Error looking up customer by externalRef: ${errorMessage}`);
651
+ }
652
+ }
653
+ }
654
+ if (this.customerCreationAttempts.has(customerRef) || externalRef && this.customerCreationAttempts.has(externalRef)) {
655
+ const mappedRef = this.customerRefMapping.get(customerRef);
656
+ return mappedRef || customerRef;
657
+ }
658
+ if (!this.apiClient.createCustomer) {
659
+ console.warn(
660
+ `\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`
661
+ );
662
+ return customerRef;
663
+ }
664
+ this.customerCreationAttempts.add(customerRef);
665
+ try {
666
+ const createParams = {
667
+ email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
668
+ };
669
+ if (options?.name) {
670
+ createParams.name = options.name;
671
+ }
672
+ if (externalRef) {
673
+ createParams.externalRef = externalRef;
674
+ }
675
+ const result = await this.apiClient.createCustomer(createParams);
676
+ const resultObj = result;
677
+ const ref = resultObj.customerRef || resultObj.reference || customerRef;
678
+ this.customerRefMapping.set(customerRef, ref);
679
+ return ref;
680
+ } catch (error) {
681
+ const errorMessage = error instanceof Error ? error.message : String(error);
682
+ if (errorMessage.includes("409") || errorMessage.includes("already exists")) {
683
+ if (externalRef) {
684
+ try {
685
+ const searchResult = await this.apiClient.getCustomer({ externalRef });
686
+ if (searchResult && searchResult.customerRef) {
687
+ this.customerRefMapping.set(customerRef, searchResult.customerRef);
688
+ return searchResult.customerRef;
689
+ }
690
+ } catch (lookupError) {
691
+ this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
692
+ }
693
+ }
694
+ const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
695
+ if (externalRef && isEmailConflict && options?.email) {
696
+ try {
697
+ const byEmail = await this.apiClient.getCustomer({ email: options.email });
698
+ if (byEmail && byEmail.customerRef) {
699
+ this.customerRefMapping.set(customerRef, byEmail.customerRef);
700
+ this.log(
701
+ `\u26A0\uFE0F Resolved customer ${customerRef} by email after conflict; using existing customer ${byEmail.customerRef}`
702
+ );
703
+ return byEmail.customerRef;
704
+ }
705
+ } catch (emailLookupError) {
706
+ this.log(
707
+ `\u26A0\uFE0F Email lookup failed after customer conflict for ${customerRef}:`,
708
+ emailLookupError instanceof Error ? emailLookupError.message : emailLookupError
709
+ );
710
+ }
711
+ try {
712
+ const retryParams = {
713
+ email: `${customerRef}-${Date.now()}@auto-created.local`,
714
+ externalRef
715
+ };
716
+ if (options?.name) {
717
+ retryParams.name = options.name;
718
+ }
719
+ const retryResult = await this.apiClient.createCustomer(retryParams);
720
+ const retryObj = retryResult;
721
+ const retryRef = retryObj.customerRef || retryObj.reference || customerRef;
722
+ this.customerRefMapping.set(customerRef, retryRef);
723
+ this.log(
724
+ `\u26A0\uFE0F Retried customer creation for ${customerRef} with generated email after email conflict`
725
+ );
726
+ return retryRef;
727
+ } catch (retryError) {
728
+ this.log(
729
+ `\u26A0\uFE0F Retry create customer with generated email failed for ${customerRef}:`,
730
+ retryError instanceof Error ? retryError.message : retryError
731
+ );
732
+ }
733
+ }
734
+ const unresolvedMessage = errorMessage || "Customer already exists but could not be resolved";
735
+ throw new Error(
736
+ `Failed to resolve existing customer for ${customerRef} after conflict: ${unresolvedMessage}. Ensure the existing customer is linked to this externalRef.`
737
+ );
738
+ }
739
+ this.log(
740
+ `\u274C Failed to auto-create customer ${customerRef}:`,
741
+ error instanceof Error ? error.message : error
742
+ );
743
+ throw error;
744
+ }
745
+ });
746
+ if (backendRef !== customerRef) {
426
747
  this.customerRefMapping.set(customerRef, backendRef);
427
- return backendRef;
428
- } catch (error) {
429
- this.log(`\u274C Failed to auto-create customer ${customerRef}:`, error instanceof Error ? error.message : error);
430
- return customerRef;
431
748
  }
749
+ return backendRef;
432
750
  }
433
- async trackUsage(customerRef, agentRef, planRef, toolName, outcome, requestId, actionDuration) {
751
+ async trackUsage(customerRef, productRef, planRef, toolName, outcome, requestId, actionDuration) {
434
752
  await withRetry(
435
753
  () => this.apiClient.trackUsage({
436
754
  customerRef,
437
- agentRef,
755
+ productRef,
438
756
  planRef,
439
757
  outcome,
440
758
  action: toolName,
@@ -446,7 +764,7 @@ var SolvaPayPaywall = class {
446
764
  maxRetries: 2,
447
765
  initialDelay: 500,
448
766
  shouldRetry: (error) => error.message.includes("Customer not found"),
449
- // TODO: review if this is needed and what to check for
767
+ // TODO: review if this is needed and what to check for
450
768
  onRetry: (error, attempt) => {
451
769
  console.warn(`\u26A0\uFE0F Customer not found (attempt ${attempt + 1}/3), retrying in 500ms...`);
452
770
  }
@@ -466,9 +784,6 @@ var AdapterUtils = class {
466
784
  if (!customerRef || customerRef === "anonymous") {
467
785
  return "anonymous";
468
786
  }
469
- if (!customerRef.startsWith("customer_") && !customerRef.startsWith("demo_")) {
470
- return `customer_${customerRef.replace(/[^a-zA-Z0-9]/g, "_")}`;
471
- }
472
787
  return customerRef;
473
788
  }
474
789
  /**
@@ -476,7 +791,7 @@ var AdapterUtils = class {
476
791
  */
477
792
  static async extractFromJWT(token, options) {
478
793
  try {
479
- const { jwtVerify } = await import("./esm-5GYCIXIY.js");
794
+ const { jwtVerify } = await import("./esm-UW7WCMEK.js");
480
795
  const jwtSecret = new TextEncoder().encode(
481
796
  options?.secret || process.env.OAUTH_JWKS_SECRET || "test-jwt-secret"
482
797
  );
@@ -485,7 +800,7 @@ var AdapterUtils = class {
485
800
  audience: options?.audience || process.env.OAUTH_CLIENT_ID || "test-client-id"
486
801
  });
487
802
  return payload.sub || null;
488
- } catch (error) {
803
+ } catch {
489
804
  return null;
490
805
  }
491
806
  }
@@ -496,7 +811,7 @@ async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
496
811
  const args = await adapter.extractArgs(context);
497
812
  const customerRef = await adapter.getCustomerRef(context);
498
813
  args.auth = { customer_ref: customerRef };
499
- const getCustomerRef = (args2) => args2.auth.customer_ref;
814
+ const getCustomerRef = (args2) => args2.auth?.customer_ref || "anonymous";
500
815
  const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
501
816
  const result = await protectedHandler(args);
502
817
  return adapter.formatResponse(result, context);
@@ -555,7 +870,7 @@ var HttpAdapter = class {
555
870
  const errorResponse2 = {
556
871
  success: false,
557
872
  error: "Payment required",
558
- agent: error.structuredContent.agent,
873
+ product: error.structuredContent.product,
559
874
  checkoutUrl: error.structuredContent.checkoutUrl,
560
875
  message: error.structuredContent.message
561
876
  };
@@ -599,7 +914,7 @@ var NextAdapter = class {
599
914
  if (request.method !== "GET" && request.headers.get("content-type")?.includes("application/json")) {
600
915
  body = await request.json();
601
916
  }
602
- } catch (error) {
917
+ } catch {
603
918
  }
604
919
  let routeParams = {};
605
920
  if (context?.params) {
@@ -628,6 +943,10 @@ var NextAdapter = class {
628
943
  return AdapterUtils.ensureCustomerRef(jwtSub);
629
944
  }
630
945
  }
946
+ const userId = request.headers.get("x-user-id");
947
+ if (userId) {
948
+ return AdapterUtils.ensureCustomerRef(userId);
949
+ }
631
950
  const headerRef = request.headers.get("x-customer-ref");
632
951
  if (headerRef) {
633
952
  return AdapterUtils.ensureCustomerRef(headerRef);
@@ -643,24 +962,30 @@ var NextAdapter = class {
643
962
  }
644
963
  formatError(error, _context) {
645
964
  if (error instanceof PaywallError) {
646
- return new Response(JSON.stringify({
965
+ return new Response(
966
+ JSON.stringify({
967
+ success: false,
968
+ error: "Payment required",
969
+ product: error.structuredContent.product,
970
+ checkoutUrl: error.structuredContent.checkoutUrl,
971
+ message: error.structuredContent.message
972
+ }),
973
+ {
974
+ status: 402,
975
+ headers: { "Content-Type": "application/json" }
976
+ }
977
+ );
978
+ }
979
+ return new Response(
980
+ JSON.stringify({
647
981
  success: false,
648
- error: "Payment required",
649
- agent: error.structuredContent.agent,
650
- checkoutUrl: error.structuredContent.checkoutUrl,
651
- message: error.structuredContent.message
652
- }), {
653
- status: 402,
982
+ error: error instanceof Error ? error.message : "Internal server error"
983
+ }),
984
+ {
985
+ status: 500,
654
986
  headers: { "Content-Type": "application/json" }
655
- });
656
- }
657
- return new Response(JSON.stringify({
658
- success: false,
659
- error: error instanceof Error ? error.message : "Internal server error"
660
- }), {
661
- status: 500,
662
- headers: { "Content-Type": "application/json" }
663
- });
987
+ }
988
+ );
664
989
  }
665
990
  };
666
991
 
@@ -677,54 +1002,79 @@ var McpAdapter = class {
677
1002
  const ref = await this.options.getCustomerRef(args);
678
1003
  return AdapterUtils.ensureCustomerRef(ref);
679
1004
  }
680
- const customerRef = args?.auth?.customer_ref || "anonymous";
1005
+ const auth = args?.auth;
1006
+ const customerRef = auth?.customer_ref || "anonymous";
681
1007
  return AdapterUtils.ensureCustomerRef(customerRef);
682
1008
  }
683
1009
  formatResponse(result, _context) {
684
1010
  const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
685
1011
  return {
686
- content: [{
687
- type: "text",
688
- text: JSON.stringify(transformed, null, 2)
689
- }]
1012
+ content: [
1013
+ {
1014
+ type: "text",
1015
+ text: JSON.stringify(transformed, null, 2)
1016
+ }
1017
+ ]
690
1018
  };
691
1019
  }
692
1020
  formatError(error, _context) {
693
1021
  if (error instanceof PaywallError) {
694
1022
  return {
695
- content: [{
696
- type: "text",
697
- text: JSON.stringify({
698
- success: false,
699
- error: "Payment required",
700
- agent: error.structuredContent.agent,
701
- checkoutUrl: error.structuredContent.checkoutUrl,
702
- message: error.structuredContent.message
703
- }, null, 2)
704
- }],
1023
+ content: [
1024
+ {
1025
+ type: "text",
1026
+ text: JSON.stringify(
1027
+ {
1028
+ success: false,
1029
+ error: "Payment required",
1030
+ product: error.structuredContent.product,
1031
+ checkoutUrl: error.structuredContent.checkoutUrl,
1032
+ message: error.structuredContent.message
1033
+ },
1034
+ null,
1035
+ 2
1036
+ )
1037
+ }
1038
+ ],
705
1039
  isError: true,
706
1040
  structuredContent: error.structuredContent
707
1041
  };
708
1042
  }
709
1043
  return {
710
- content: [{
711
- type: "text",
712
- text: JSON.stringify({
713
- success: false,
714
- error: error instanceof Error ? error.message : "Unknown error occurred"
715
- }, null, 2)
716
- }],
1044
+ content: [
1045
+ {
1046
+ type: "text",
1047
+ text: JSON.stringify(
1048
+ {
1049
+ success: false,
1050
+ error: error instanceof Error ? error.message : "Unknown error occurred"
1051
+ },
1052
+ null,
1053
+ 2
1054
+ )
1055
+ }
1056
+ ],
717
1057
  isError: true
718
1058
  };
719
1059
  }
720
1060
  };
721
1061
 
722
1062
  // src/factory.ts
723
- import { SolvaPayError as SolvaPayError2 } from "@solvapay/core";
1063
+ import { SolvaPayError as SolvaPayError2, getSolvaPayConfig } from "@solvapay/core";
724
1064
  function createSolvaPay(config) {
725
- const apiClient = config.apiClient || createSolvaPayClient({
726
- apiKey: config.apiKey,
727
- apiBaseUrl: config.apiBaseUrl
1065
+ let resolvedConfig;
1066
+ if (!config) {
1067
+ const envConfig = getSolvaPayConfig();
1068
+ resolvedConfig = {
1069
+ apiKey: envConfig.apiKey,
1070
+ apiBaseUrl: envConfig.apiBaseUrl
1071
+ };
1072
+ } else {
1073
+ resolvedConfig = config;
1074
+ }
1075
+ const apiClient = resolvedConfig.apiClient || createSolvaPayClient({
1076
+ apiKey: resolvedConfig.apiKey,
1077
+ apiBaseUrl: resolvedConfig.apiBaseUrl
728
1078
  });
729
1079
  const paywall = new SolvaPayPaywall(apiClient, {
730
1080
  debug: process.env.SOLVAPAY_DEBUG !== "false"
@@ -733,8 +1083,8 @@ function createSolvaPay(config) {
733
1083
  // Direct access to API client for advanced operations
734
1084
  apiClient,
735
1085
  // Common API methods exposed directly for convenience
736
- ensureCustomer(customerRef) {
737
- return paywall.ensureCustomer(customerRef);
1086
+ ensureCustomer(customerRef, externalRef, options) {
1087
+ return paywall.ensureCustomer(customerRef, externalRef, options);
738
1088
  },
739
1089
  createPaymentIntent(params) {
740
1090
  if (!apiClient.createPaymentIntent) {
@@ -742,6 +1092,12 @@ function createSolvaPay(config) {
742
1092
  }
743
1093
  return apiClient.createPaymentIntent(params);
744
1094
  },
1095
+ processPaymentIntent(params) {
1096
+ if (!apiClient.processPaymentIntent) {
1097
+ throw new SolvaPayError2("processPaymentIntent is not available on this API client");
1098
+ }
1099
+ return apiClient.processPaymentIntent(params);
1100
+ },
745
1101
  checkLimits(params) {
746
1102
  return apiClient.checkLimits(params);
747
1103
  },
@@ -755,57 +1111,49 @@ function createSolvaPay(config) {
755
1111
  return apiClient.createCustomer(params);
756
1112
  },
757
1113
  getCustomer(params) {
758
- if (!apiClient.getCustomer) {
759
- throw new SolvaPayError2("getCustomer is not available on this API client");
760
- }
761
1114
  return apiClient.getCustomer(params);
762
1115
  },
1116
+ createCheckoutSession(params) {
1117
+ return apiClient.createCheckoutSession({
1118
+ customerReference: params.customerRef,
1119
+ productRef: params.productRef,
1120
+ planRef: params.planRef
1121
+ });
1122
+ },
1123
+ createCustomerSession(params) {
1124
+ return apiClient.createCustomerSession(params);
1125
+ },
763
1126
  // Payable API for framework-specific handlers
764
1127
  payable(options = {}) {
765
- const agent = options.agentRef || options.agent || process.env.SOLVAPAY_AGENT || getPackageJsonName() || "default-agent";
766
- const plan = options.planRef || options.plan || agent;
767
- const metadata = { agent, plan };
1128
+ const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
1129
+ const plan = options.planRef || options.plan || product;
1130
+ const metadata = { product, plan };
768
1131
  return {
769
- // HTTP adapter for Express/Fastify
1132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
770
1133
  http(businessLogic, adapterOptions) {
771
1134
  const adapter = new HttpAdapter(adapterOptions);
772
1135
  return async (req, reply) => {
773
- const handler = await createAdapterHandler(
774
- adapter,
775
- paywall,
776
- metadata,
777
- businessLogic
778
- );
1136
+ const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
779
1137
  return handler([req, reply]);
780
1138
  };
781
1139
  },
782
- // Next.js adapter for App Router
1140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
783
1141
  next(businessLogic, adapterOptions) {
784
1142
  const adapter = new NextAdapter(adapterOptions);
785
1143
  return async (request, context) => {
786
- const handler = await createAdapterHandler(
787
- adapter,
788
- paywall,
789
- metadata,
790
- businessLogic
791
- );
1144
+ const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
792
1145
  return handler([request, context]);
793
1146
  };
794
1147
  },
795
- // MCP adapter for Model Context Protocol
1148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
796
1149
  mcp(businessLogic, adapterOptions) {
797
1150
  const adapter = new McpAdapter(adapterOptions);
798
1151
  return async (args) => {
799
- const handler = await createAdapterHandler(
800
- adapter,
801
- paywall,
802
- metadata,
803
- businessLogic
804
- );
1152
+ const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
805
1153
  return handler(args);
806
1154
  };
807
1155
  },
808
- // Pure function adapter for direct protection
1156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
809
1157
  async function(businessLogic) {
810
1158
  const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
811
1159
  return paywall.protect(businessLogic, metadata, getCustomerRef);
@@ -814,12 +1162,316 @@ function createSolvaPay(config) {
814
1162
  }
815
1163
  };
816
1164
  }
817
- function getPackageJsonName() {
1165
+
1166
+ // src/helpers/error.ts
1167
+ import { SolvaPayError as SolvaPayError3 } from "@solvapay/core";
1168
+ function isErrorResult(result) {
1169
+ return typeof result === "object" && result !== null && "error" in result && "status" in result;
1170
+ }
1171
+ function handleRouteError(error, operationName, defaultMessage) {
1172
+ console.error(`[${operationName}] Error:`, error);
1173
+ if (error instanceof SolvaPayError3) {
1174
+ const errorMessage2 = error.message;
1175
+ return {
1176
+ error: errorMessage2,
1177
+ status: 500,
1178
+ details: errorMessage2
1179
+ };
1180
+ }
1181
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1182
+ const message = defaultMessage || `${operationName} failed`;
1183
+ return {
1184
+ error: message,
1185
+ status: 500,
1186
+ details: errorMessage
1187
+ };
1188
+ }
1189
+
1190
+ // src/helpers/auth.ts
1191
+ async function getAuthenticatedUserCore(request, options = {}) {
1192
+ try {
1193
+ const { requireUserId, getUserEmailFromRequest, getUserNameFromRequest } = await import("@solvapay/auth");
1194
+ const userIdOrError = requireUserId(request);
1195
+ if (userIdOrError instanceof Response) {
1196
+ const clonedResponse = userIdOrError.clone();
1197
+ const body = await clonedResponse.json().catch(() => ({ error: "Unauthorized" }));
1198
+ return {
1199
+ error: body.error || "Unauthorized",
1200
+ status: userIdOrError.status,
1201
+ details: body.error || "Unauthorized"
1202
+ };
1203
+ }
1204
+ const userId = userIdOrError;
1205
+ const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
1206
+ const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
1207
+ return {
1208
+ userId,
1209
+ email,
1210
+ name
1211
+ };
1212
+ } catch (error) {
1213
+ return handleRouteError(error, "Get authenticated user", "Authentication failed");
1214
+ }
1215
+ }
1216
+
1217
+ // src/helpers/customer.ts
1218
+ async function syncCustomerCore(request, options = {}) {
1219
+ try {
1220
+ const userResult = await getAuthenticatedUserCore(request, {
1221
+ includeEmail: options.includeEmail,
1222
+ includeName: options.includeName
1223
+ });
1224
+ if (isErrorResult(userResult)) {
1225
+ return userResult;
1226
+ }
1227
+ const { userId, email, name } = userResult;
1228
+ const solvaPay = options.solvaPay || createSolvaPay();
1229
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
1230
+ email: email || void 0,
1231
+ name: name || void 0
1232
+ });
1233
+ return customerRef;
1234
+ } catch (error) {
1235
+ return handleRouteError(error, "Sync customer", "Failed to sync customer");
1236
+ }
1237
+ }
1238
+
1239
+ // src/helpers/payment.ts
1240
+ async function createPaymentIntentCore(request, body, options = {}) {
1241
+ try {
1242
+ if (!body.planRef || !body.productRef) {
1243
+ return {
1244
+ error: "Missing required parameters: planRef and productRef are required",
1245
+ status: 400
1246
+ };
1247
+ }
1248
+ const customerResult = await syncCustomerCore(request, {
1249
+ solvaPay: options.solvaPay,
1250
+ includeEmail: options.includeEmail,
1251
+ includeName: options.includeName
1252
+ });
1253
+ if (isErrorResult(customerResult)) {
1254
+ return customerResult;
1255
+ }
1256
+ const customerRef = customerResult;
1257
+ const solvaPay = options.solvaPay || createSolvaPay();
1258
+ const paymentIntent = await solvaPay.createPaymentIntent({
1259
+ productRef: body.productRef,
1260
+ planRef: body.planRef,
1261
+ customerRef
1262
+ });
1263
+ return {
1264
+ id: paymentIntent.id,
1265
+ clientSecret: paymentIntent.clientSecret,
1266
+ publishableKey: paymentIntent.publishableKey,
1267
+ accountId: paymentIntent.accountId,
1268
+ customerRef
1269
+ };
1270
+ } catch (error) {
1271
+ return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
1272
+ }
1273
+ }
1274
+ async function processPaymentIntentCore(request, body, options = {}) {
1275
+ try {
1276
+ if (!body.paymentIntentId || !body.productRef) {
1277
+ return {
1278
+ error: "paymentIntentId and productRef are required",
1279
+ status: 400
1280
+ };
1281
+ }
1282
+ const customerResult = await syncCustomerCore(request, {
1283
+ solvaPay: options.solvaPay
1284
+ });
1285
+ if (isErrorResult(customerResult)) {
1286
+ return customerResult;
1287
+ }
1288
+ const customerRef = customerResult;
1289
+ const solvaPay = options.solvaPay || createSolvaPay();
1290
+ const result = await solvaPay.processPaymentIntent({
1291
+ paymentIntentId: body.paymentIntentId,
1292
+ productRef: body.productRef,
1293
+ customerRef,
1294
+ planRef: body.planRef
1295
+ });
1296
+ return result;
1297
+ } catch (error) {
1298
+ return handleRouteError(error, "Process payment intent", "Payment processing failed");
1299
+ }
1300
+ }
1301
+
1302
+ // src/helpers/checkout.ts
1303
+ async function createCheckoutSessionCore(request, body, options = {}) {
1304
+ try {
1305
+ if (!body.productRef) {
1306
+ return {
1307
+ error: "Missing required parameter: productRef is required",
1308
+ status: 400
1309
+ };
1310
+ }
1311
+ const customerResult = await syncCustomerCore(request, {
1312
+ solvaPay: options.solvaPay,
1313
+ includeEmail: options.includeEmail,
1314
+ includeName: options.includeName
1315
+ });
1316
+ if (isErrorResult(customerResult)) {
1317
+ return customerResult;
1318
+ }
1319
+ const customerRef = customerResult;
1320
+ let returnUrl = body.returnUrl || options.returnUrl;
1321
+ if (!returnUrl) {
1322
+ try {
1323
+ const url = new URL(request.url);
1324
+ returnUrl = url.origin;
1325
+ } catch {
1326
+ }
1327
+ }
1328
+ const solvaPay = options.solvaPay || createSolvaPay();
1329
+ const session = await solvaPay.createCheckoutSession({
1330
+ productRef: body.productRef,
1331
+ customerRef,
1332
+ planRef: body.planRef || void 0,
1333
+ returnUrl
1334
+ });
1335
+ return {
1336
+ sessionId: session.sessionId,
1337
+ checkoutUrl: session.checkoutUrl
1338
+ };
1339
+ } catch (error) {
1340
+ return handleRouteError(error, "Create checkout session", "Checkout session creation failed");
1341
+ }
1342
+ }
1343
+ async function createCustomerSessionCore(request, options = {}) {
1344
+ try {
1345
+ const customerResult = await syncCustomerCore(request, {
1346
+ solvaPay: options.solvaPay,
1347
+ includeEmail: options.includeEmail,
1348
+ includeName: options.includeName
1349
+ });
1350
+ if (isErrorResult(customerResult)) {
1351
+ return customerResult;
1352
+ }
1353
+ const customerRef = customerResult;
1354
+ const solvaPay = options.solvaPay || createSolvaPay();
1355
+ const session = await solvaPay.createCustomerSession({
1356
+ customerRef
1357
+ });
1358
+ return session;
1359
+ } catch (error) {
1360
+ return handleRouteError(error, "Create customer session", "Customer session creation failed");
1361
+ }
1362
+ }
1363
+
1364
+ // src/helpers/renewal.ts
1365
+ import { SolvaPayError as SolvaPayError4 } from "@solvapay/core";
1366
+ async function cancelPurchaseCore(request, body, options = {}) {
1367
+ try {
1368
+ if (!body.purchaseRef) {
1369
+ return {
1370
+ error: "Missing required parameter: purchaseRef is required",
1371
+ status: 400
1372
+ };
1373
+ }
1374
+ const solvaPay = options.solvaPay || createSolvaPay();
1375
+ if (!solvaPay.apiClient.cancelPurchase) {
1376
+ return {
1377
+ error: "Cancel purchase method not available on SDK client",
1378
+ status: 500
1379
+ };
1380
+ }
1381
+ let cancelledPurchase = await solvaPay.apiClient.cancelPurchase({
1382
+ purchaseRef: body.purchaseRef,
1383
+ reason: body.reason
1384
+ });
1385
+ if (!cancelledPurchase || typeof cancelledPurchase !== "object") {
1386
+ return {
1387
+ error: "Invalid response from cancel purchase endpoint",
1388
+ status: 500
1389
+ };
1390
+ }
1391
+ const responseObj = cancelledPurchase;
1392
+ if (responseObj.purchase && typeof responseObj.purchase === "object") {
1393
+ cancelledPurchase = responseObj.purchase;
1394
+ }
1395
+ if (!cancelledPurchase.reference) {
1396
+ return {
1397
+ error: "Cancel purchase response missing required fields",
1398
+ status: 500
1399
+ };
1400
+ }
1401
+ const isCancelled = cancelledPurchase.status === "cancelled" || cancelledPurchase.cancelledAt;
1402
+ if (!isCancelled) {
1403
+ return {
1404
+ error: `Purchase cancellation failed: backend returned status '${cancelledPurchase.status}' without cancelledAt timestamp`,
1405
+ status: 500
1406
+ };
1407
+ }
1408
+ await new Promise((resolve) => setTimeout(resolve, 500));
1409
+ return cancelledPurchase;
1410
+ } catch (error) {
1411
+ if (error instanceof SolvaPayError4) {
1412
+ const errorMessage = error.message;
1413
+ if (errorMessage.includes("not found")) {
1414
+ return {
1415
+ error: "Purchase not found",
1416
+ status: 404,
1417
+ details: errorMessage
1418
+ };
1419
+ }
1420
+ if (errorMessage.includes("cannot be cancelled") || errorMessage.includes("does not belong to provider")) {
1421
+ return {
1422
+ error: "Purchase cannot be cancelled or does not belong to provider",
1423
+ status: 400,
1424
+ details: errorMessage
1425
+ };
1426
+ }
1427
+ return {
1428
+ error: errorMessage,
1429
+ status: 500,
1430
+ details: errorMessage
1431
+ };
1432
+ }
1433
+ return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
1434
+ }
1435
+ }
1436
+
1437
+ // src/helpers/plans.ts
1438
+ import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
1439
+ async function listPlansCore(request) {
818
1440
  try {
819
- const pkg = __require(process.cwd() + "/package.json");
820
- return pkg.name;
821
- } catch {
822
- return void 0;
1441
+ const url = new URL(request.url);
1442
+ const productRef = url.searchParams.get("productRef");
1443
+ if (!productRef) {
1444
+ return {
1445
+ error: "Missing required parameter: productRef",
1446
+ status: 400
1447
+ };
1448
+ }
1449
+ const config = getSolvaPayConfig2();
1450
+ const solvapaySecretKey = config.apiKey;
1451
+ const solvapayApiBaseUrl = config.apiBaseUrl;
1452
+ if (!solvapaySecretKey) {
1453
+ return {
1454
+ error: "Server configuration error: SolvaPay secret key not configured",
1455
+ status: 500
1456
+ };
1457
+ }
1458
+ const apiClient = createSolvaPayClient({
1459
+ apiKey: solvapaySecretKey,
1460
+ apiBaseUrl: solvapayApiBaseUrl
1461
+ });
1462
+ if (!apiClient.listPlans) {
1463
+ return {
1464
+ error: "List plans method not available",
1465
+ status: 500
1466
+ };
1467
+ }
1468
+ const plans = await apiClient.listPlans(productRef);
1469
+ return {
1470
+ plans: plans || [],
1471
+ productRef
1472
+ };
1473
+ } catch (error) {
1474
+ return handleRouteError(error, "List plans", "Failed to fetch plans");
823
1475
  }
824
1476
  }
825
1477
 
@@ -840,14 +1492,24 @@ async function verifyWebhook({
840
1492
  const sigBuf = await crypto.subtle.sign("HMAC", key, enc.encode(body));
841
1493
  const hex = Array.from(new Uint8Array(sigBuf)).map((b) => b.toString(16).padStart(2, "0")).join("");
842
1494
  if (hex !== signature) {
843
- throw new SolvaPayError3("Invalid webhook signature");
1495
+ throw new SolvaPayError5("Invalid webhook signature");
844
1496
  }
845
1497
  return JSON.parse(body);
846
1498
  }
847
1499
  export {
848
1500
  PaywallError,
1501
+ cancelPurchaseCore,
1502
+ createCheckoutSessionCore,
1503
+ createCustomerSessionCore,
1504
+ createPaymentIntentCore,
849
1505
  createSolvaPay,
850
1506
  createSolvaPayClient,
1507
+ getAuthenticatedUserCore,
1508
+ handleRouteError,
1509
+ isErrorResult,
1510
+ listPlansCore,
1511
+ processPaymentIntentCore,
1512
+ syncCustomerCore,
851
1513
  verifyWebhook,
852
1514
  withRetry
853
1515
  };