@solvapay/next 1.0.0-preview.18 → 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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 SolvaPay Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -14,28 +14,29 @@ npm install @solvapay/next @solvapay/server next
14
14
 
15
15
  All helpers return either a success result or a `NextResponse` error, making them easy to use in Next.js API routes.
16
16
 
17
- ### Check Subscription
17
+ ### Check Purchase
18
18
 
19
- Check user subscription status with built-in request deduplication and caching:
19
+ Check user purchase status with built-in request deduplication and caching:
20
20
 
21
21
  ```typescript
22
- import { NextRequest, NextResponse } from 'next/server';
23
- import { checkSubscription } from '@solvapay/next';
22
+ import { NextRequest, NextResponse } from 'next/server'
23
+ import { checkPurchase } from '@solvapay/next'
24
24
 
25
25
  export async function GET(request: NextRequest) {
26
- const result = await checkSubscription(request);
27
-
26
+ const result = await checkPurchase(request)
27
+
28
28
  // If result is a NextResponse, it's an error response - return it
29
29
  if (result instanceof NextResponse) {
30
- return result;
30
+ return result
31
31
  }
32
-
33
- // Otherwise, return the subscription data
34
- return NextResponse.json(result);
32
+
33
+ // Otherwise, return the purchase data
34
+ return NextResponse.json(result)
35
35
  }
36
36
  ```
37
37
 
38
38
  **Features:**
39
+
39
40
  - **Automatic Deduplication**: Prevents duplicate API calls by deduplicating concurrent requests
40
41
  - **Caching**: Caches results for 2 seconds to prevent duplicate sequential requests
41
42
  - **Automatic Cleanup**: Expired cache entries are automatically cleaned up
@@ -46,17 +47,17 @@ export async function GET(request: NextRequest) {
46
47
  Sync customer with SolvaPay backend (ensures customer exists and returns customer reference):
47
48
 
48
49
  ```typescript
49
- import { NextRequest, NextResponse } from 'next/server';
50
- import { syncCustomer } from '@solvapay/next';
50
+ import { NextRequest, NextResponse } from 'next/server'
51
+ import { syncCustomer } from '@solvapay/next'
51
52
 
52
53
  export async function POST(request: NextRequest) {
53
- const result = await syncCustomer(request);
54
-
54
+ const result = await syncCustomer(request)
55
+
55
56
  if (result instanceof NextResponse) {
56
- return result;
57
+ return result
57
58
  }
58
-
59
- return NextResponse.json({ customerRef: result });
59
+
60
+ return NextResponse.json({ customerRef: result })
60
61
  }
61
62
  ```
62
63
 
@@ -65,21 +66,18 @@ export async function POST(request: NextRequest) {
65
66
  Create a Stripe payment intent for checkout:
66
67
 
67
68
  ```typescript
68
- import { NextRequest, NextResponse } from 'next/server';
69
- import { createPaymentIntent } from '@solvapay/next';
69
+ import { NextRequest, NextResponse } from 'next/server'
70
+ import { createPaymentIntent } from '@solvapay/next'
70
71
 
71
72
  export async function POST(request: NextRequest) {
72
- const { planRef, agentRef } = await request.json();
73
-
73
+ const { planRef, agentRef } = await request.json()
74
+
74
75
  if (!planRef || !agentRef) {
75
- return NextResponse.json(
76
- { error: 'Missing required parameters' },
77
- { status: 400 }
78
- );
76
+ return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 })
79
77
  }
80
-
81
- const result = await createPaymentIntent(request, { planRef, agentRef });
82
- return result instanceof NextResponse ? result : NextResponse.json(result);
78
+
79
+ const result = await createPaymentIntent(request, { planRef, agentRef })
80
+ return result instanceof NextResponse ? result : NextResponse.json(result)
83
81
  }
84
82
  ```
85
83
 
@@ -88,21 +86,18 @@ export async function POST(request: NextRequest) {
88
86
  Process payment after Stripe confirmation:
89
87
 
90
88
  ```typescript
91
- import { NextRequest, NextResponse } from 'next/server';
92
- import { processPayment } from '@solvapay/next';
89
+ import { NextRequest, NextResponse } from 'next/server'
90
+ import { processPayment } from '@solvapay/next'
93
91
 
94
92
  export async function POST(request: NextRequest) {
95
- const { paymentIntentId, agentRef, planRef } = await request.json();
96
-
93
+ const { paymentIntentId, agentRef, planRef } = await request.json()
94
+
97
95
  if (!paymentIntentId || !agentRef) {
98
- return NextResponse.json(
99
- { error: 'Missing required parameters' },
100
- { status: 400 }
101
- );
96
+ return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 })
102
97
  }
103
-
104
- const result = await processPayment(request, { paymentIntentId, agentRef, planRef });
105
- return result instanceof NextResponse ? result : NextResponse.json(result);
98
+
99
+ const result = await processPayment(request, { paymentIntentId, agentRef, planRef })
100
+ return result instanceof NextResponse ? result : NextResponse.json(result)
106
101
  }
107
102
  ```
108
103
 
@@ -111,35 +106,32 @@ export async function POST(request: NextRequest) {
111
106
  List available plans (public route, no authentication required):
112
107
 
113
108
  ```typescript
114
- import { NextRequest, NextResponse } from 'next/server';
115
- import { listPlans } from '@solvapay/next';
109
+ import { NextRequest, NextResponse } from 'next/server'
110
+ import { listPlans } from '@solvapay/next'
116
111
 
117
112
  export async function GET(request: NextRequest) {
118
- const result = await listPlans(request);
119
- return result instanceof NextResponse ? result : NextResponse.json(result);
113
+ const result = await listPlans(request)
114
+ return result instanceof NextResponse ? result : NextResponse.json(result)
120
115
  }
121
116
  ```
122
117
 
123
- ### Cancel Subscription
118
+ ### Cancel Renewal
124
119
 
125
- Cancel a user's subscription:
120
+ Cancel renewal of a user's purchase:
126
121
 
127
122
  ```typescript
128
- import { NextRequest, NextResponse } from 'next/server';
129
- import { cancelSubscription } from '@solvapay/next';
123
+ import { NextRequest, NextResponse } from 'next/server'
124
+ import { cancelRenewal } from '@solvapay/next'
130
125
 
131
126
  export async function POST(request: NextRequest) {
132
- const { subscriptionRef, reason } = await request.json();
133
-
134
- if (!subscriptionRef) {
135
- return NextResponse.json(
136
- { error: 'Missing subscriptionRef' },
137
- { status: 400 }
138
- );
127
+ const { purchaseRef, reason } = await request.json()
128
+
129
+ if (!purchaseRef) {
130
+ return NextResponse.json({ error: 'Missing purchaseRef' }, { status: 400 })
139
131
  }
140
-
141
- const result = await cancelSubscription(request, { subscriptionRef, reason });
142
- return result instanceof NextResponse ? result : NextResponse.json(result);
132
+
133
+ const result = await cancelRenewal(request, { purchaseRef, reason })
134
+ return result instanceof NextResponse ? result : NextResponse.json(result)
143
135
  }
144
136
  ```
145
137
 
@@ -148,21 +140,18 @@ export async function POST(request: NextRequest) {
148
140
  Create a hosted checkout session:
149
141
 
150
142
  ```typescript
151
- import { NextRequest, NextResponse } from 'next/server';
152
- import { createCheckoutSession } from '@solvapay/next';
143
+ import { NextRequest, NextResponse } from 'next/server'
144
+ import { createCheckoutSession } from '@solvapay/next'
153
145
 
154
146
  export async function POST(request: NextRequest) {
155
- const { agentRef, planRef } = await request.json();
156
-
147
+ const { agentRef, planRef } = await request.json()
148
+
157
149
  if (!agentRef) {
158
- return NextResponse.json(
159
- { error: 'Missing agentRef' },
160
- { status: 400 }
161
- );
150
+ return NextResponse.json({ error: 'Missing agentRef' }, { status: 400 })
162
151
  }
163
-
164
- const result = await createCheckoutSession(request, { agentRef, planRef });
165
- return result instanceof NextResponse ? result : NextResponse.json(result);
152
+
153
+ const result = await createCheckoutSession(request, { agentRef, planRef })
154
+ return result instanceof NextResponse ? result : NextResponse.json(result)
166
155
  }
167
156
  ```
168
157
 
@@ -171,12 +160,12 @@ export async function POST(request: NextRequest) {
171
160
  Create a customer portal session:
172
161
 
173
162
  ```typescript
174
- import { NextRequest, NextResponse } from 'next/server';
175
- import { createCustomerSession } from '@solvapay/next';
163
+ import { NextRequest, NextResponse } from 'next/server'
164
+ import { createCustomerSession } from '@solvapay/next'
176
165
 
177
166
  export async function POST(request: NextRequest) {
178
- const result = await createCustomerSession(request);
179
- return result instanceof NextResponse ? result : NextResponse.json(result);
167
+ const result = await createCustomerSession(request)
168
+ return result instanceof NextResponse ? result : NextResponse.json(result)
180
169
  }
181
170
  ```
182
171
 
@@ -185,63 +174,66 @@ export async function POST(request: NextRequest) {
185
174
  Get authenticated user information (userId, email, name):
186
175
 
187
176
  ```typescript
188
- import { NextRequest, NextResponse } from 'next/server';
189
- import { getAuthenticatedUser } from '@solvapay/next';
177
+ import { NextRequest, NextResponse } from 'next/server'
178
+ import { getAuthenticatedUser } from '@solvapay/next'
190
179
 
191
180
  export async function GET(request: NextRequest) {
192
181
  const result = await getAuthenticatedUser(request, {
193
182
  includeEmail: true,
194
183
  includeName: true,
195
- });
196
-
184
+ })
185
+
197
186
  if (result instanceof NextResponse) {
198
- return result;
187
+ return result
199
188
  }
200
-
201
- return NextResponse.json(result);
189
+
190
+ return NextResponse.json(result)
202
191
  }
203
192
  ```
204
193
 
205
194
  ### Cache Management
206
195
 
207
196
  ```typescript
208
- import {
209
- clearSubscriptionCache,
210
- clearAllSubscriptionCache,
211
- getSubscriptionCacheStats
212
- } from '@solvapay/next';
197
+ import {
198
+ clearPurchaseCache,
199
+ clearAllPurchaseCache,
200
+ getPurchaseCacheStats,
201
+ } from '@solvapay/next'
213
202
 
214
203
  // Clear cache for a specific user
215
- clearSubscriptionCache(userId);
204
+ clearPurchaseCache(userId)
216
205
 
217
206
  // Clear all cache entries
218
- clearAllSubscriptionCache();
207
+ clearAllPurchaseCache()
219
208
 
220
209
  // Get cache statistics
221
- const stats = getSubscriptionCacheStats();
222
- console.log(`In-flight: ${stats.inFlight}, Cached: ${stats.cached}`);
210
+ const stats = getPurchaseCacheStats()
211
+ console.log(`In-flight: ${stats.inFlight}, Cached: ${stats.cached}`)
223
212
  ```
224
213
 
225
214
  ## Helper Functions Reference
226
215
 
227
216
  All helper functions follow the same pattern:
217
+
228
218
  - Take a `Request` or `NextRequest` as the first parameter
229
219
  - Return either the success result or a `NextResponse` error
230
220
  - Automatically extract user information from request headers (set by middleware)
231
221
  - Support optional configuration options
232
222
 
233
223
  **Available Helpers:**
234
- - `checkSubscription(request, options?)` - Check subscription with caching
224
+
225
+ - `checkPurchase(request, options?)` - Check purchase with caching
235
226
  - `syncCustomer(request, options?)` - Sync customer with backend
236
227
  - `createPaymentIntent(request, body, options?)` - Create payment intent
237
228
  - `processPayment(request, body, options?)` - Process payment
238
229
  - `listPlans(request)` - List available plans (public)
239
- - `cancelSubscription(request, body, options?)` - Cancel subscription
230
+ - `cancelRenewal(request, body, options?)` - Cancel renewal
240
231
  - `createCheckoutSession(request, body, options?)` - Create hosted checkout
241
232
  - `createCustomerSession(request, options?)` - Create customer portal
242
233
  - `getAuthenticatedUser(request, options?)` - Get user info
243
234
 
244
235
  **Common Options:**
236
+
245
237
  - `solvaPay?: SolvaPay` - Custom SolvaPay instance
246
238
  - `includeEmail?: boolean` - Include user email (default: true)
247
239
  - `includeName?: boolean` - Include user name (default: true)
@@ -264,35 +256,38 @@ These helpers expect the user ID to be set in the `x-user-id` header by your Nex
264
256
  The easiest way is to use `createSupabaseAuthMiddleware`:
265
257
 
266
258
  **For Next.js 15:**
259
+
267
260
  ```typescript
268
261
  // middleware.ts (at project root)
269
- import { createSupabaseAuthMiddleware } from '@solvapay/next';
262
+ import { createSupabaseAuthMiddleware } from '@solvapay/next'
270
263
 
271
264
  export const middleware = createSupabaseAuthMiddleware({
272
265
  publicRoutes: ['/api/list-plans'],
273
- });
266
+ })
274
267
 
275
268
  export const config = {
276
269
  matcher: ['/api/:path*'],
277
- };
270
+ }
278
271
  ```
279
272
 
280
273
  **For Next.js 16 with `src/` folder:**
274
+
281
275
  ```typescript
282
276
  // src/proxy.ts (in src/ folder, not project root)
283
- import { createSupabaseAuthMiddleware } from '@solvapay/next';
277
+ import { createSupabaseAuthMiddleware } from '@solvapay/next'
284
278
 
285
279
  // Use 'proxy' export for Next.js 16 (no deprecation warning)
286
280
  export const proxy = createSupabaseAuthMiddleware({
287
281
  publicRoutes: ['/api/list-plans'],
288
- });
282
+ })
289
283
 
290
284
  export const config = {
291
285
  matcher: ['/api/:path*'],
292
- };
286
+ }
293
287
  ```
294
288
 
295
289
  **File Location Notes:**
290
+
296
291
  - **Next.js 15**: Place `middleware.ts` at project root
297
292
  - **Next.js 16 without `src/` folder**: Place `middleware.ts` or `proxy.ts` at project root
298
293
  - **Next.js 16 with `src/` folder**: Place `src/proxy.ts` or `src/middleware.ts` (in `src/` folder, not root)
@@ -305,24 +300,23 @@ Alternatively, you can create your own middleware:
305
300
 
306
301
  ```typescript
307
302
  // middleware.ts (or src/proxy.ts for Next.js 16)
308
- import { NextResponse } from 'next/server';
309
- import type { NextRequest } from 'next/server';
303
+ import { NextResponse } from 'next/server'
304
+ import type { NextRequest } from 'next/server'
310
305
 
311
306
  export async function middleware(request: NextRequest) {
312
307
  // Extract user ID from your auth system
313
- const userId = await getUserIdFromAuth(request);
314
-
308
+ const userId = await getUserIdFromAuth(request)
309
+
315
310
  // Clone request and add user ID header
316
- const requestHeaders = new Headers(request.headers);
317
- requestHeaders.set('x-user-id', userId);
318
-
311
+ const requestHeaders = new Headers(request.headers)
312
+ requestHeaders.set('x-user-id', userId)
313
+
319
314
  return NextResponse.next({
320
315
  request: {
321
316
  headers: requestHeaders,
322
317
  },
323
- });
318
+ })
324
319
  }
325
320
  ```
326
321
 
327
322
  You can also use the `requireUserId` utility from `@solvapay/auth` in your middleware.
328
-