@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/LICENSE.md +21 -0
- package/README.md +271 -20
- package/dist/index.cjs +303 -76
- package/dist/index.d.cts +458 -53
- package/dist/index.d.ts +458 -53
- package/dist/index.js +299 -71
- package/package.json +10 -9
package/dist/index.cjs
CHANGED
|
@@ -30,47 +30,59 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
cancelRenewal: () => cancelRenewal,
|
|
34
|
+
checkPurchase: () => checkPurchase,
|
|
35
|
+
clearAllPurchaseCache: () => clearAllPurchaseCache,
|
|
36
|
+
clearPurchaseCache: () => clearPurchaseCache,
|
|
37
|
+
createAuthMiddleware: () => createAuthMiddleware,
|
|
38
|
+
createCheckoutSession: () => createCheckoutSession,
|
|
39
|
+
createCustomerSession: () => createCustomerSession,
|
|
40
|
+
createPaymentIntent: () => createPaymentIntent,
|
|
41
|
+
createSupabaseAuthMiddleware: () => createSupabaseAuthMiddleware,
|
|
42
|
+
getAuthenticatedUser: () => getAuthenticatedUser,
|
|
43
|
+
getPurchaseCacheStats: () => getPurchaseCacheStats,
|
|
44
|
+
listPlans: () => listPlans,
|
|
45
|
+
processPaymentIntent: () => processPaymentIntent,
|
|
46
|
+
syncCustomer: () => syncCustomer
|
|
37
47
|
});
|
|
38
48
|
module.exports = __toCommonJS(index_exports);
|
|
39
|
-
var
|
|
40
|
-
var
|
|
49
|
+
var import_server16 = require("next/server");
|
|
50
|
+
var import_server17 = require("@solvapay/server");
|
|
41
51
|
var import_core = require("@solvapay/core");
|
|
52
|
+
|
|
53
|
+
// src/cache.ts
|
|
42
54
|
function createRequestDeduplicator(options = {}) {
|
|
43
|
-
const {
|
|
44
|
-
cacheTTL = 2e3,
|
|
45
|
-
maxCacheSize = 1e3,
|
|
46
|
-
cacheErrors = true
|
|
47
|
-
} = options;
|
|
55
|
+
const { cacheTTL = 2e3, maxCacheSize = 1e3, cacheErrors = true } = options;
|
|
48
56
|
const inFlightRequests = /* @__PURE__ */ new Map();
|
|
49
57
|
const resultCache = /* @__PURE__ */ new Map();
|
|
50
58
|
const cacheInvalidatedAt = /* @__PURE__ */ new Map();
|
|
51
|
-
let cleanupInterval = null;
|
|
52
59
|
if (cacheTTL > 0) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
setInterval(
|
|
61
|
+
() => {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const entriesToDelete = [];
|
|
64
|
+
for (const [key, cached] of resultCache.entries()) {
|
|
65
|
+
if (now - cached.timestamp >= cacheTTL) {
|
|
66
|
+
entriesToDelete.push(key);
|
|
67
|
+
}
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
for (const key of entriesToDelete) {
|
|
62
|
-
resultCache.delete(key);
|
|
63
|
-
cacheInvalidatedAt.delete(key);
|
|
64
|
-
}
|
|
65
|
-
if (resultCache.size > maxCacheSize) {
|
|
66
|
-
const sortedEntries = Array.from(resultCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
67
|
-
const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
|
|
68
|
-
for (const [key] of toRemove) {
|
|
69
|
+
for (const key of entriesToDelete) {
|
|
69
70
|
resultCache.delete(key);
|
|
70
71
|
cacheInvalidatedAt.delete(key);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
if (resultCache.size > maxCacheSize) {
|
|
74
|
+
const sortedEntries = Array.from(resultCache.entries()).sort(
|
|
75
|
+
(a, b) => a[1].timestamp - b[1].timestamp
|
|
76
|
+
);
|
|
77
|
+
const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
|
|
78
|
+
for (const [key] of toRemove) {
|
|
79
|
+
resultCache.delete(key);
|
|
80
|
+
cacheInvalidatedAt.delete(key);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
Math.min(cacheTTL, 1e3)
|
|
85
|
+
);
|
|
74
86
|
}
|
|
75
87
|
const deduplicate = async (key, fn) => {
|
|
76
88
|
if (cacheTTL > 0) {
|
|
@@ -140,10 +152,10 @@ function createRequestDeduplicator(options = {}) {
|
|
|
140
152
|
getStats
|
|
141
153
|
};
|
|
142
154
|
}
|
|
143
|
-
var
|
|
155
|
+
var sharedPurchaseDeduplicator = null;
|
|
144
156
|
function getSharedDeduplicator(options) {
|
|
145
|
-
if (!
|
|
146
|
-
|
|
157
|
+
if (!sharedPurchaseDeduplicator) {
|
|
158
|
+
sharedPurchaseDeduplicator = createRequestDeduplicator({
|
|
147
159
|
cacheTTL: 2e3,
|
|
148
160
|
// Cache results for 2 seconds
|
|
149
161
|
maxCacheSize: 1e3,
|
|
@@ -153,90 +165,305 @@ function getSharedDeduplicator(options) {
|
|
|
153
165
|
...options
|
|
154
166
|
});
|
|
155
167
|
}
|
|
156
|
-
return
|
|
168
|
+
return sharedPurchaseDeduplicator;
|
|
169
|
+
}
|
|
170
|
+
function clearPurchaseCache(userId) {
|
|
171
|
+
const deduplicator = getSharedDeduplicator();
|
|
172
|
+
deduplicator.clearCache(userId);
|
|
173
|
+
}
|
|
174
|
+
function clearAllPurchaseCache() {
|
|
175
|
+
const deduplicator = getSharedDeduplicator();
|
|
176
|
+
deduplicator.clearAllCache();
|
|
177
|
+
}
|
|
178
|
+
function getPurchaseCacheStats() {
|
|
179
|
+
const deduplicator = getSharedDeduplicator();
|
|
180
|
+
return deduplicator.getStats();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/helpers/auth.ts
|
|
184
|
+
var import_server = require("next/server");
|
|
185
|
+
var import_server2 = require("@solvapay/server");
|
|
186
|
+
async function getAuthenticatedUser(request, options = {}) {
|
|
187
|
+
const result = await (0, import_server2.getAuthenticatedUserCore)(request, options);
|
|
188
|
+
if ((0, import_server2.isErrorResult)(result)) {
|
|
189
|
+
return import_server.NextResponse.json(
|
|
190
|
+
{ error: result.error, details: result.details },
|
|
191
|
+
{ status: result.status }
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/helpers/customer.ts
|
|
198
|
+
var import_server3 = require("next/server");
|
|
199
|
+
var import_server4 = require("@solvapay/server");
|
|
200
|
+
async function syncCustomer(request, options = {}) {
|
|
201
|
+
const result = await (0, import_server4.syncCustomerCore)(request, options);
|
|
202
|
+
if ((0, import_server4.isErrorResult)(result)) {
|
|
203
|
+
return import_server3.NextResponse.json(
|
|
204
|
+
{ error: result.error, details: result.details },
|
|
205
|
+
{ status: result.status }
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/helpers/payment.ts
|
|
212
|
+
var import_server5 = require("next/server");
|
|
213
|
+
var import_server6 = require("@solvapay/server");
|
|
214
|
+
var import_server7 = require("@solvapay/server");
|
|
215
|
+
async function createPaymentIntent(request, body, options = {}) {
|
|
216
|
+
const result = await (0, import_server6.createPaymentIntentCore)(request, body, options);
|
|
217
|
+
if ((0, import_server6.isErrorResult)(result)) {
|
|
218
|
+
return import_server5.NextResponse.json(
|
|
219
|
+
{ error: result.error, details: result.details },
|
|
220
|
+
{ status: result.status }
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const userResult = await (0, import_server7.getAuthenticatedUserCore)(request);
|
|
225
|
+
if (!(0, import_server6.isErrorResult)(userResult)) {
|
|
226
|
+
clearPurchaseCache(userResult.userId);
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
async function processPaymentIntent(request, body, options = {}) {
|
|
233
|
+
const result = await (0, import_server6.processPaymentIntentCore)(request, body, options);
|
|
234
|
+
if ((0, import_server6.isErrorResult)(result)) {
|
|
235
|
+
return import_server5.NextResponse.json(
|
|
236
|
+
{ error: result.error, details: result.details },
|
|
237
|
+
{ status: result.status }
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const userResult = await (0, import_server7.getAuthenticatedUserCore)(request);
|
|
242
|
+
if (!(0, import_server6.isErrorResult)(userResult)) {
|
|
243
|
+
clearPurchaseCache(userResult.userId);
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/helpers/checkout.ts
|
|
251
|
+
var import_server8 = require("next/server");
|
|
252
|
+
var import_server9 = require("@solvapay/server");
|
|
253
|
+
async function createCheckoutSession(request, body, options = {}) {
|
|
254
|
+
const result = await (0, import_server9.createCheckoutSessionCore)(request, body, options);
|
|
255
|
+
if ((0, import_server9.isErrorResult)(result)) {
|
|
256
|
+
return import_server8.NextResponse.json(
|
|
257
|
+
{ error: result.error, details: result.details },
|
|
258
|
+
{ status: result.status }
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
async function createCustomerSession(request, options = {}) {
|
|
264
|
+
const result = await (0, import_server9.createCustomerSessionCore)(request, options);
|
|
265
|
+
if ((0, import_server9.isErrorResult)(result)) {
|
|
266
|
+
return import_server8.NextResponse.json(
|
|
267
|
+
{ error: result.error, details: result.details },
|
|
268
|
+
{ status: result.status }
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/helpers/renewal.ts
|
|
275
|
+
var import_server10 = require("next/server");
|
|
276
|
+
var import_server11 = require("@solvapay/server");
|
|
277
|
+
var import_server12 = require("@solvapay/server");
|
|
278
|
+
async function cancelRenewal(request, body, options = {}) {
|
|
279
|
+
const result = await (0, import_server11.cancelPurchaseCore)(request, body, options);
|
|
280
|
+
if ((0, import_server11.isErrorResult)(result)) {
|
|
281
|
+
return import_server10.NextResponse.json(
|
|
282
|
+
{ error: result.error, details: result.details },
|
|
283
|
+
{ status: result.status }
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const userResult = await (0, import_server12.getAuthenticatedUserCore)(request);
|
|
288
|
+
if (!(0, import_server11.isErrorResult)(userResult)) {
|
|
289
|
+
clearPurchaseCache(userResult.userId);
|
|
290
|
+
}
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/helpers/plans.ts
|
|
297
|
+
var import_server13 = require("next/server");
|
|
298
|
+
var import_server14 = require("@solvapay/server");
|
|
299
|
+
async function listPlans(request) {
|
|
300
|
+
const result = await (0, import_server14.listPlansCore)(request);
|
|
301
|
+
if ((0, import_server14.isErrorResult)(result)) {
|
|
302
|
+
return import_server13.NextResponse.json(
|
|
303
|
+
{ error: result.error, details: result.details },
|
|
304
|
+
{ status: result.status }
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/helpers/middleware.ts
|
|
311
|
+
var import_server15 = require("next/server");
|
|
312
|
+
function createAuthMiddleware(options) {
|
|
313
|
+
const { adapter, publicRoutes = [], userIdHeader = "x-user-id" } = options;
|
|
314
|
+
return async function middleware(request) {
|
|
315
|
+
const req = request;
|
|
316
|
+
const { pathname } = req.nextUrl;
|
|
317
|
+
if (!pathname.startsWith("/api")) {
|
|
318
|
+
return import_server15.NextResponse.next();
|
|
319
|
+
}
|
|
320
|
+
const isPublicRoute = publicRoutes.some((route) => pathname.startsWith(route));
|
|
321
|
+
const userId = await adapter.getUserIdFromRequest(req);
|
|
322
|
+
if (isPublicRoute) {
|
|
323
|
+
const requestHeaders2 = new Headers(req.headers);
|
|
324
|
+
if (userId) {
|
|
325
|
+
requestHeaders2.set(userIdHeader, userId);
|
|
326
|
+
}
|
|
327
|
+
return import_server15.NextResponse.next({
|
|
328
|
+
request: {
|
|
329
|
+
headers: requestHeaders2
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
if (!userId) {
|
|
334
|
+
return import_server15.NextResponse.json(
|
|
335
|
+
{ error: "Unauthorized", details: "Valid authentication required" },
|
|
336
|
+
{ status: 401 }
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const requestHeaders = new Headers(req.headers);
|
|
340
|
+
requestHeaders.set(userIdHeader, userId);
|
|
341
|
+
return import_server15.NextResponse.next({
|
|
342
|
+
request: {
|
|
343
|
+
headers: requestHeaders
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function createSupabaseAuthMiddleware(options = {}) {
|
|
349
|
+
const { jwtSecret, publicRoutes = [], userIdHeader = "x-user-id" } = options;
|
|
350
|
+
let authAdapter = null;
|
|
351
|
+
let adapterPromise = null;
|
|
352
|
+
const lazyAdapter = {
|
|
353
|
+
async getUserIdFromRequest(req) {
|
|
354
|
+
if (!authAdapter) {
|
|
355
|
+
if (!adapterPromise) {
|
|
356
|
+
adapterPromise = (async () => {
|
|
357
|
+
const secret = jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
358
|
+
if (!secret) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
"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"
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
const { SupabaseAuthAdapter } = await import("@solvapay/auth/supabase");
|
|
364
|
+
authAdapter = new SupabaseAuthAdapter({ jwtSecret: secret });
|
|
365
|
+
return authAdapter;
|
|
366
|
+
})();
|
|
367
|
+
}
|
|
368
|
+
authAdapter = await adapterPromise;
|
|
369
|
+
}
|
|
370
|
+
return authAdapter.getUserIdFromRequest(req);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
return createAuthMiddleware({
|
|
374
|
+
adapter: lazyAdapter,
|
|
375
|
+
publicRoutes,
|
|
376
|
+
userIdHeader
|
|
377
|
+
});
|
|
157
378
|
}
|
|
158
|
-
|
|
379
|
+
|
|
380
|
+
// src/index.ts
|
|
381
|
+
async function checkPurchase(request, options = {}) {
|
|
159
382
|
try {
|
|
160
383
|
const { requireUserId, getUserEmailFromRequest, getUserNameFromRequest } = await import("@solvapay/auth");
|
|
161
384
|
const userIdOrError = requireUserId(request);
|
|
162
385
|
if (userIdOrError instanceof Response) {
|
|
163
386
|
const clonedResponse = userIdOrError.clone();
|
|
164
387
|
const body = await clonedResponse.json().catch(() => ({ error: "Unauthorized" }));
|
|
165
|
-
return
|
|
388
|
+
return import_server16.NextResponse.json(body, { status: userIdOrError.status });
|
|
166
389
|
}
|
|
167
390
|
const userId = userIdOrError;
|
|
168
391
|
const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
|
|
169
392
|
const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
|
|
393
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
394
|
+
const solvaPay = options.solvaPay || (0, import_server17.createSolvaPay)();
|
|
395
|
+
if (cachedCustomerRef) {
|
|
396
|
+
try {
|
|
397
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
398
|
+
if (customer && customer.customerRef) {
|
|
399
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
400
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
401
|
+
(p) => p.status === "active"
|
|
402
|
+
);
|
|
403
|
+
return {
|
|
404
|
+
customerRef: customer.customerRef,
|
|
405
|
+
email: customer.email,
|
|
406
|
+
name: customer.name,
|
|
407
|
+
purchases: filteredPurchases
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
}
|
|
170
414
|
const deduplicator = getSharedDeduplicator(options.deduplication);
|
|
171
415
|
const response = await deduplicator.deduplicate(userId, async () => {
|
|
172
416
|
try {
|
|
173
|
-
const solvaPay = options.solvaPay || (0, import_server2.createSolvaPay)();
|
|
174
417
|
const ensuredCustomerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
175
418
|
email: email || void 0,
|
|
176
419
|
name: name || void 0
|
|
177
420
|
});
|
|
178
421
|
const customer = await solvaPay.getCustomer({ customerRef: ensuredCustomerRef });
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const isCancelled = sub.status === "cancelled" || subAny.cancelledAt;
|
|
183
|
-
if (!isCancelled) {
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
if (isCancelled && subAny.endDate) {
|
|
187
|
-
const endDate = new Date(subAny.endDate);
|
|
188
|
-
const isFuture = endDate > now;
|
|
189
|
-
return isFuture;
|
|
190
|
-
}
|
|
191
|
-
return false;
|
|
192
|
-
});
|
|
422
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
423
|
+
(p) => p.status === "active"
|
|
424
|
+
);
|
|
193
425
|
const result = {
|
|
194
426
|
customerRef: customer.customerRef || userId,
|
|
195
427
|
email: customer.email,
|
|
196
428
|
name: customer.name,
|
|
197
|
-
|
|
429
|
+
purchases: filteredPurchases
|
|
198
430
|
};
|
|
199
431
|
return result;
|
|
200
432
|
} catch (error) {
|
|
201
|
-
console.error("[
|
|
433
|
+
console.error("[checkPurchase] Error fetching customer:", error);
|
|
202
434
|
return {
|
|
203
435
|
customerRef: userId,
|
|
204
|
-
|
|
436
|
+
purchases: []
|
|
205
437
|
};
|
|
206
438
|
}
|
|
207
439
|
});
|
|
208
440
|
return response;
|
|
209
441
|
} catch (error) {
|
|
210
|
-
console.error("Check
|
|
442
|
+
console.error("Check purchase failed:", error);
|
|
211
443
|
if (error instanceof import_core.SolvaPayError) {
|
|
212
|
-
return
|
|
213
|
-
{ error: error.message },
|
|
214
|
-
{ status: 500 }
|
|
215
|
-
);
|
|
444
|
+
return import_server16.NextResponse.json({ error: error.message }, { status: 500 });
|
|
216
445
|
}
|
|
217
446
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
218
|
-
return
|
|
219
|
-
{ error: "Failed to check
|
|
447
|
+
return import_server16.NextResponse.json(
|
|
448
|
+
{ error: "Failed to check purchase", details: errorMessage },
|
|
220
449
|
{ status: 500 }
|
|
221
450
|
);
|
|
222
451
|
}
|
|
223
452
|
}
|
|
224
|
-
function clearSubscriptionCache(userId) {
|
|
225
|
-
const deduplicator = getSharedDeduplicator();
|
|
226
|
-
deduplicator.clearCache(userId);
|
|
227
|
-
}
|
|
228
|
-
function clearAllSubscriptionCache() {
|
|
229
|
-
const deduplicator = getSharedDeduplicator();
|
|
230
|
-
deduplicator.clearAllCache();
|
|
231
|
-
}
|
|
232
|
-
function getSubscriptionCacheStats() {
|
|
233
|
-
const deduplicator = getSharedDeduplicator();
|
|
234
|
-
return deduplicator.getStats();
|
|
235
|
-
}
|
|
236
453
|
// Annotate the CommonJS export names for ESM import in node:
|
|
237
454
|
0 && (module.exports = {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
455
|
+
cancelRenewal,
|
|
456
|
+
checkPurchase,
|
|
457
|
+
clearAllPurchaseCache,
|
|
458
|
+
clearPurchaseCache,
|
|
459
|
+
createAuthMiddleware,
|
|
460
|
+
createCheckoutSession,
|
|
461
|
+
createCustomerSession,
|
|
462
|
+
createPaymentIntent,
|
|
463
|
+
createSupabaseAuthMiddleware,
|
|
464
|
+
getAuthenticatedUser,
|
|
465
|
+
getPurchaseCacheStats,
|
|
466
|
+
listPlans,
|
|
467
|
+
processPaymentIntent,
|
|
468
|
+
syncCustomer
|
|
242
469
|
});
|