@solvapay/next 1.0.0-preview.9 → 1.0.1-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,39 +1,41 @@
1
1
  // src/index.ts
2
- import { NextResponse } from "next/server";
2
+ import { NextResponse as NextResponse8 } from "next/server";
3
3
  import { createSolvaPay } from "@solvapay/server";
4
4
  import { SolvaPayError } from "@solvapay/core";
5
+
6
+ // src/cache.ts
5
7
  function createRequestDeduplicator(options = {}) {
6
- const {
7
- cacheTTL = 2e3,
8
- maxCacheSize = 1e3,
9
- cacheErrors = true
10
- } = options;
8
+ const { cacheTTL = 2e3, maxCacheSize = 1e3, cacheErrors = true } = options;
11
9
  const inFlightRequests = /* @__PURE__ */ new Map();
12
10
  const resultCache = /* @__PURE__ */ new Map();
13
11
  const cacheInvalidatedAt = /* @__PURE__ */ new Map();
14
- let cleanupInterval = null;
15
12
  if (cacheTTL > 0) {
16
- cleanupInterval = setInterval(() => {
17
- const now = Date.now();
18
- const entriesToDelete = [];
19
- for (const [key, cached] of resultCache.entries()) {
20
- if (now - cached.timestamp >= cacheTTL) {
21
- entriesToDelete.push(key);
13
+ setInterval(
14
+ () => {
15
+ const now = Date.now();
16
+ const entriesToDelete = [];
17
+ for (const [key, cached] of resultCache.entries()) {
18
+ if (now - cached.timestamp >= cacheTTL) {
19
+ entriesToDelete.push(key);
20
+ }
22
21
  }
23
- }
24
- for (const key of entriesToDelete) {
25
- resultCache.delete(key);
26
- cacheInvalidatedAt.delete(key);
27
- }
28
- if (resultCache.size > maxCacheSize) {
29
- const sortedEntries = Array.from(resultCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp);
30
- const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
31
- for (const [key] of toRemove) {
22
+ for (const key of entriesToDelete) {
32
23
  resultCache.delete(key);
33
24
  cacheInvalidatedAt.delete(key);
34
25
  }
35
- }
36
- }, Math.min(cacheTTL, 1e3));
26
+ if (resultCache.size > maxCacheSize) {
27
+ const sortedEntries = Array.from(resultCache.entries()).sort(
28
+ (a, b) => a[1].timestamp - b[1].timestamp
29
+ );
30
+ const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
31
+ for (const [key] of toRemove) {
32
+ resultCache.delete(key);
33
+ cacheInvalidatedAt.delete(key);
34
+ }
35
+ }
36
+ },
37
+ Math.min(cacheTTL, 1e3)
38
+ );
37
39
  }
38
40
  const deduplicate = async (key, fn) => {
39
41
  if (cacheTTL > 0) {
@@ -103,10 +105,10 @@ function createRequestDeduplicator(options = {}) {
103
105
  getStats
104
106
  };
105
107
  }
106
- var sharedSubscriptionDeduplicator = null;
108
+ var sharedPurchaseDeduplicator = null;
107
109
  function getSharedDeduplicator(options) {
108
- if (!sharedSubscriptionDeduplicator) {
109
- sharedSubscriptionDeduplicator = createRequestDeduplicator({
110
+ if (!sharedPurchaseDeduplicator) {
111
+ sharedPurchaseDeduplicator = createRequestDeduplicator({
110
112
  cacheTTL: 2e3,
111
113
  // Cache results for 2 seconds
112
114
  maxCacheSize: 1e3,
@@ -116,89 +118,315 @@ function getSharedDeduplicator(options) {
116
118
  ...options
117
119
  });
118
120
  }
119
- return sharedSubscriptionDeduplicator;
121
+ return sharedPurchaseDeduplicator;
122
+ }
123
+ function clearPurchaseCache(userId) {
124
+ const deduplicator = getSharedDeduplicator();
125
+ deduplicator.clearCache(userId);
126
+ }
127
+ function clearAllPurchaseCache() {
128
+ const deduplicator = getSharedDeduplicator();
129
+ deduplicator.clearAllCache();
130
+ }
131
+ function getPurchaseCacheStats() {
132
+ const deduplicator = getSharedDeduplicator();
133
+ return deduplicator.getStats();
134
+ }
135
+
136
+ // src/helpers/auth.ts
137
+ import { NextResponse } from "next/server";
138
+ import {
139
+ getAuthenticatedUserCore,
140
+ isErrorResult
141
+ } from "@solvapay/server";
142
+ async function getAuthenticatedUser(request, options = {}) {
143
+ const result = await getAuthenticatedUserCore(request, options);
144
+ if (isErrorResult(result)) {
145
+ return NextResponse.json(
146
+ { error: result.error, details: result.details },
147
+ { status: result.status }
148
+ );
149
+ }
150
+ return result;
120
151
  }
121
- async function checkSubscription(request, options = {}) {
152
+
153
+ // src/helpers/customer.ts
154
+ import { NextResponse as NextResponse2 } from "next/server";
155
+ import { syncCustomerCore, isErrorResult as isErrorResult2 } from "@solvapay/server";
156
+ async function syncCustomer(request, options = {}) {
157
+ const result = await syncCustomerCore(request, options);
158
+ if (isErrorResult2(result)) {
159
+ return NextResponse2.json(
160
+ { error: result.error, details: result.details },
161
+ { status: result.status }
162
+ );
163
+ }
164
+ return result;
165
+ }
166
+
167
+ // src/helpers/payment.ts
168
+ import { NextResponse as NextResponse3 } from "next/server";
169
+ import {
170
+ createPaymentIntentCore,
171
+ processPaymentIntentCore,
172
+ isErrorResult as isErrorResult3
173
+ } from "@solvapay/server";
174
+ import { getAuthenticatedUserCore as getAuthenticatedUserCore2 } from "@solvapay/server";
175
+ async function createPaymentIntent(request, body, options = {}) {
176
+ const result = await createPaymentIntentCore(request, body, options);
177
+ if (isErrorResult3(result)) {
178
+ return NextResponse3.json(
179
+ { error: result.error, details: result.details },
180
+ { status: result.status }
181
+ );
182
+ }
183
+ try {
184
+ const userResult = await getAuthenticatedUserCore2(request);
185
+ if (!isErrorResult3(userResult)) {
186
+ clearPurchaseCache(userResult.userId);
187
+ }
188
+ } catch {
189
+ }
190
+ return result;
191
+ }
192
+ async function processPaymentIntent(request, body, options = {}) {
193
+ const result = await processPaymentIntentCore(request, body, options);
194
+ if (isErrorResult3(result)) {
195
+ return NextResponse3.json(
196
+ { error: result.error, details: result.details },
197
+ { status: result.status }
198
+ );
199
+ }
200
+ try {
201
+ const userResult = await getAuthenticatedUserCore2(request);
202
+ if (!isErrorResult3(userResult)) {
203
+ clearPurchaseCache(userResult.userId);
204
+ }
205
+ } catch {
206
+ }
207
+ return result;
208
+ }
209
+
210
+ // src/helpers/checkout.ts
211
+ import { NextResponse as NextResponse4 } from "next/server";
212
+ import {
213
+ createCheckoutSessionCore,
214
+ createCustomerSessionCore,
215
+ isErrorResult as isErrorResult4
216
+ } from "@solvapay/server";
217
+ async function createCheckoutSession(request, body, options = {}) {
218
+ const result = await createCheckoutSessionCore(request, body, options);
219
+ if (isErrorResult4(result)) {
220
+ return NextResponse4.json(
221
+ { error: result.error, details: result.details },
222
+ { status: result.status }
223
+ );
224
+ }
225
+ return result;
226
+ }
227
+ async function createCustomerSession(request, options = {}) {
228
+ const result = await createCustomerSessionCore(request, options);
229
+ if (isErrorResult4(result)) {
230
+ return NextResponse4.json(
231
+ { error: result.error, details: result.details },
232
+ { status: result.status }
233
+ );
234
+ }
235
+ return result;
236
+ }
237
+
238
+ // src/helpers/renewal.ts
239
+ import { NextResponse as NextResponse5 } from "next/server";
240
+ import { cancelPurchaseCore, isErrorResult as isErrorResult5 } from "@solvapay/server";
241
+ import { getAuthenticatedUserCore as getAuthenticatedUserCore3 } from "@solvapay/server";
242
+ async function cancelRenewal(request, body, options = {}) {
243
+ const result = await cancelPurchaseCore(request, body, options);
244
+ if (isErrorResult5(result)) {
245
+ return NextResponse5.json(
246
+ { error: result.error, details: result.details },
247
+ { status: result.status }
248
+ );
249
+ }
250
+ try {
251
+ const userResult = await getAuthenticatedUserCore3(request);
252
+ if (!isErrorResult5(userResult)) {
253
+ clearPurchaseCache(userResult.userId);
254
+ }
255
+ } catch {
256
+ }
257
+ return result;
258
+ }
259
+
260
+ // src/helpers/plans.ts
261
+ import { NextResponse as NextResponse6 } from "next/server";
262
+ import { listPlansCore, isErrorResult as isErrorResult6 } from "@solvapay/server";
263
+ async function listPlans(request) {
264
+ const result = await listPlansCore(request);
265
+ if (isErrorResult6(result)) {
266
+ return NextResponse6.json(
267
+ { error: result.error, details: result.details },
268
+ { status: result.status }
269
+ );
270
+ }
271
+ return result;
272
+ }
273
+
274
+ // src/helpers/middleware.ts
275
+ import { NextResponse as NextResponse7 } from "next/server";
276
+ function createAuthMiddleware(options) {
277
+ const { adapter, publicRoutes = [], userIdHeader = "x-user-id" } = options;
278
+ return async function middleware(request) {
279
+ const req = request;
280
+ const { pathname } = req.nextUrl;
281
+ if (!pathname.startsWith("/api")) {
282
+ return NextResponse7.next();
283
+ }
284
+ const isPublicRoute = publicRoutes.some((route) => pathname.startsWith(route));
285
+ const userId = await adapter.getUserIdFromRequest(req);
286
+ if (isPublicRoute) {
287
+ const requestHeaders2 = new Headers(req.headers);
288
+ if (userId) {
289
+ requestHeaders2.set(userIdHeader, userId);
290
+ }
291
+ return NextResponse7.next({
292
+ request: {
293
+ headers: requestHeaders2
294
+ }
295
+ });
296
+ }
297
+ if (!userId) {
298
+ return NextResponse7.json(
299
+ { error: "Unauthorized", details: "Valid authentication required" },
300
+ { status: 401 }
301
+ );
302
+ }
303
+ const requestHeaders = new Headers(req.headers);
304
+ requestHeaders.set(userIdHeader, userId);
305
+ return NextResponse7.next({
306
+ request: {
307
+ headers: requestHeaders
308
+ }
309
+ });
310
+ };
311
+ }
312
+ function createSupabaseAuthMiddleware(options = {}) {
313
+ const { jwtSecret, publicRoutes = [], userIdHeader = "x-user-id" } = options;
314
+ let authAdapter = null;
315
+ let adapterPromise = null;
316
+ const lazyAdapter = {
317
+ async getUserIdFromRequest(req) {
318
+ if (!authAdapter) {
319
+ if (!adapterPromise) {
320
+ adapterPromise = (async () => {
321
+ const secret = jwtSecret || process.env.SUPABASE_JWT_SECRET;
322
+ if (!secret) {
323
+ throw new Error(
324
+ "SUPABASE_JWT_SECRET environment variable is required. Please set it in your .env.local file. Get it from: Supabase Dashboard \u2192 Settings \u2192 API \u2192 JWT Secret"
325
+ );
326
+ }
327
+ const { SupabaseAuthAdapter } = await import("@solvapay/auth/supabase");
328
+ authAdapter = new SupabaseAuthAdapter({ jwtSecret: secret });
329
+ return authAdapter;
330
+ })();
331
+ }
332
+ authAdapter = await adapterPromise;
333
+ }
334
+ return authAdapter.getUserIdFromRequest(req);
335
+ }
336
+ };
337
+ return createAuthMiddleware({
338
+ adapter: lazyAdapter,
339
+ publicRoutes,
340
+ userIdHeader
341
+ });
342
+ }
343
+
344
+ // src/index.ts
345
+ async function checkPurchase(request, options = {}) {
122
346
  try {
123
347
  const { requireUserId, getUserEmailFromRequest, getUserNameFromRequest } = await import("@solvapay/auth");
124
348
  const userIdOrError = requireUserId(request);
125
349
  if (userIdOrError instanceof Response) {
126
350
  const clonedResponse = userIdOrError.clone();
127
351
  const body = await clonedResponse.json().catch(() => ({ error: "Unauthorized" }));
128
- return NextResponse.json(body, { status: userIdOrError.status });
352
+ return NextResponse8.json(body, { status: userIdOrError.status });
129
353
  }
130
354
  const userId = userIdOrError;
131
355
  const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
132
356
  const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
357
+ const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
358
+ const solvaPay = options.solvaPay || createSolvaPay();
359
+ if (cachedCustomerRef) {
360
+ try {
361
+ const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
362
+ if (customer && customer.customerRef) {
363
+ if (customer.externalRef && customer.externalRef === userId) {
364
+ const filteredPurchases = (customer.purchases || []).filter(
365
+ (p) => p.status === "active"
366
+ );
367
+ return {
368
+ customerRef: customer.customerRef,
369
+ email: customer.email,
370
+ name: customer.name,
371
+ purchases: filteredPurchases
372
+ };
373
+ }
374
+ }
375
+ } catch {
376
+ }
377
+ }
133
378
  const deduplicator = getSharedDeduplicator(options.deduplication);
134
379
  const response = await deduplicator.deduplicate(userId, async () => {
135
380
  try {
136
- const solvaPay = options.solvaPay || createSolvaPay();
137
381
  const ensuredCustomerRef = await solvaPay.ensureCustomer(userId, userId, {
138
382
  email: email || void 0,
139
383
  name: name || void 0
140
384
  });
141
385
  const customer = await solvaPay.getCustomer({ customerRef: ensuredCustomerRef });
142
- const now = /* @__PURE__ */ new Date();
143
- const filteredSubscriptions = (customer.subscriptions || []).filter((sub) => {
144
- const subAny = sub;
145
- const isCancelled = sub.status === "cancelled" || subAny.cancelledAt;
146
- if (!isCancelled) {
147
- return true;
148
- }
149
- if (isCancelled && subAny.endDate) {
150
- const endDate = new Date(subAny.endDate);
151
- const isFuture = endDate > now;
152
- return isFuture;
153
- }
154
- return false;
155
- });
386
+ const filteredPurchases = (customer.purchases || []).filter(
387
+ (p) => p.status === "active"
388
+ );
156
389
  const result = {
157
390
  customerRef: customer.customerRef || userId,
158
391
  email: customer.email,
159
392
  name: customer.name,
160
- subscriptions: filteredSubscriptions
393
+ purchases: filteredPurchases
161
394
  };
162
395
  return result;
163
396
  } catch (error) {
164
- console.error("[checkSubscription] Error fetching customer:", error);
397
+ console.error("[checkPurchase] Error fetching customer:", error);
165
398
  return {
166
399
  customerRef: userId,
167
- subscriptions: []
400
+ purchases: []
168
401
  };
169
402
  }
170
403
  });
171
404
  return response;
172
405
  } catch (error) {
173
- console.error("Check subscription failed:", error);
406
+ console.error("Check purchase failed:", error);
174
407
  if (error instanceof SolvaPayError) {
175
- return NextResponse.json(
176
- { error: error.message },
177
- { status: 500 }
178
- );
408
+ return NextResponse8.json({ error: error.message }, { status: 500 });
179
409
  }
180
410
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
181
- return NextResponse.json(
182
- { error: "Failed to check subscription", details: errorMessage },
411
+ return NextResponse8.json(
412
+ { error: "Failed to check purchase", details: errorMessage },
183
413
  { status: 500 }
184
414
  );
185
415
  }
186
416
  }
187
- function clearSubscriptionCache(userId) {
188
- const deduplicator = getSharedDeduplicator();
189
- deduplicator.clearCache(userId);
190
- }
191
- function clearAllSubscriptionCache() {
192
- const deduplicator = getSharedDeduplicator();
193
- deduplicator.clearAllCache();
194
- }
195
- function getSubscriptionCacheStats() {
196
- const deduplicator = getSharedDeduplicator();
197
- return deduplicator.getStats();
198
- }
199
417
  export {
200
- checkSubscription,
201
- clearAllSubscriptionCache,
202
- clearSubscriptionCache,
203
- getSubscriptionCacheStats
418
+ cancelRenewal,
419
+ checkPurchase,
420
+ clearAllPurchaseCache,
421
+ clearPurchaseCache,
422
+ createAuthMiddleware,
423
+ createCheckoutSession,
424
+ createCustomerSession,
425
+ createPaymentIntent,
426
+ createSupabaseAuthMiddleware,
427
+ getAuthenticatedUser,
428
+ getPurchaseCacheStats,
429
+ listPlans,
430
+ processPaymentIntent,
431
+ syncCustomer
204
432
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solvapay/next",
3
- "version": "1.0.0-preview.9",
3
+ "version": "1.0.1-preview.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -29,24 +29,25 @@
29
29
  },
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
- "@solvapay/auth": "1.0.0-preview.9",
33
- "@solvapay/core": "1.0.0-preview.9",
34
- "@solvapay/server": "1.0.0-preview.9"
32
+ "@solvapay/server": "1.0.1-preview.1",
33
+ "@solvapay/auth": "1.0.1-preview.1",
34
+ "@solvapay/core": "1.0.1-preview.1"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "next": ">=13.0.0"
38
38
  },
39
39
  "devDependencies": {
40
- "next": "^14.0.0",
41
- "tsup": "^8.0.1",
42
- "typescript": "^5.5.4",
43
- "vitest": "^2.0.5",
40
+ "next": "^16.2.1",
41
+ "tsup": "^8.5.1",
42
+ "typescript": "^5.9.3",
43
+ "vitest": "^4.1.2",
44
44
  "@solvapay/test-utils": "0.0.0"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json --external next",
48
48
  "test": "vitest run || exit 0",
49
49
  "test:watch": "vitest",
50
- "lint": "echo 'no lint yet'"
50
+ "lint": "eslint src",
51
+ "lint:fix": "eslint src --fix"
51
52
  }
52
53
  }