@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 +21 -0
- package/README.md +99 -105
- package/dist/index.cjs +170 -178
- package/dist/index.d.cts +197 -137
- package/dist/index.d.ts +197 -137
- package/dist/index.js +168 -185
- package/package.json +7 -6
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
|
|
17
|
+
### Check Purchase
|
|
18
18
|
|
|
19
|
-
Check user
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
118
|
+
### Cancel Renewal
|
|
124
119
|
|
|
125
|
-
Cancel a user's
|
|
120
|
+
Cancel renewal of a user's purchase:
|
|
126
121
|
|
|
127
122
|
```typescript
|
|
128
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
129
|
-
import {
|
|
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 {
|
|
133
|
-
|
|
134
|
-
if (!
|
|
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
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
204
|
+
clearPurchaseCache(userId)
|
|
216
205
|
|
|
217
206
|
// Clear all cache entries
|
|
218
|
-
|
|
207
|
+
clearAllPurchaseCache()
|
|
219
208
|
|
|
220
209
|
// Get cache statistics
|
|
221
|
-
const stats =
|
|
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
|
-
|
|
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
|
-
- `
|
|
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
|
-
|