create-x402-app 0.1.7 → 0.1.9
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 +1 -1
- package/package.json +1 -1
- package/templates/fullstack-express/src/app/api/premium/route.ts +87 -39
- package/templates/fullstack-express/src/app/page.tsx +63 -4
- package/templates/fullstack-hono/src/app/api/premium/route.ts +87 -39
- package/templates/fullstack-hono/src/app/page.tsx +63 -4
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,52 +1,100 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
-
import { processPaymentMiddleware, initializePlatform } from 'x402-mantle-sdk/server'
|
|
2
|
+
import { processPaymentMiddleware, initializePlatform, clearCache } from 'x402-mantle-sdk/server'
|
|
3
|
+
|
|
4
|
+
// Configuration - Set these in your .env file
|
|
5
|
+
const appId = process.env.X402_APP_ID || ''
|
|
6
|
+
const platformUrl = process.env.X402_PLATFORM_URL || 'https://mantle-x402.vercel.app'
|
|
7
|
+
|
|
8
|
+
if (!process.env.X402_APP_ID) {
|
|
9
|
+
process.env.X402_APP_ID = appId
|
|
10
|
+
}
|
|
11
|
+
if (!process.env.X402_PLATFORM_URL) {
|
|
12
|
+
process.env.X402_PLATFORM_URL = platformUrl
|
|
13
|
+
}
|
|
3
14
|
|
|
4
15
|
let initialized = false
|
|
16
|
+
let initError: Error | null = null
|
|
5
17
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!initialized) {
|
|
18
|
+
const initPlatform = async () => {
|
|
19
|
+
try {
|
|
9
20
|
await initializePlatform()
|
|
10
21
|
initialized = true
|
|
22
|
+
initError = null
|
|
23
|
+
} catch (error) {
|
|
24
|
+
initError = error as Error
|
|
25
|
+
console.error('Platform initialization failed:', error)
|
|
11
26
|
}
|
|
27
|
+
}
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Payment required - return 402 with payment details
|
|
26
|
-
if (result.paymentRequired) {
|
|
27
|
-
const response = NextResponse.json(result.paymentRequired.body, { status: 402 })
|
|
28
|
-
Object.entries(result.paymentRequired.headers).forEach(([key, value]) => {
|
|
29
|
-
response.headers.set(key, value)
|
|
30
|
-
})
|
|
31
|
-
return response
|
|
32
|
-
}
|
|
29
|
+
export async function GET(request: NextRequest) {
|
|
30
|
+
try {
|
|
31
|
+
// Initialize platform on first request
|
|
32
|
+
if (!initialized && !initError) {
|
|
33
|
+
await initPlatform()
|
|
34
|
+
} else if (initError) {
|
|
35
|
+
try { clearCache() } catch {}
|
|
36
|
+
initError = null
|
|
37
|
+
await initPlatform()
|
|
38
|
+
}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if (initError) {
|
|
41
|
+
return NextResponse.json(
|
|
42
|
+
{ error: 'Platform initialization failed', details: (initError as Error).message },
|
|
43
|
+
{ status: 500 }
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Payment options
|
|
48
|
+
const paymentOptions = {
|
|
49
|
+
price: '0.001',
|
|
50
|
+
token: 'MNT',
|
|
51
|
+
testnet: true,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Convert headers to plain object
|
|
55
|
+
const headers: Record<string, string> = {}
|
|
56
|
+
request.headers.forEach((value, key) => {
|
|
57
|
+
headers[key] = value
|
|
43
58
|
})
|
|
44
|
-
}
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return NextResponse.json({ error: result.error.message }, { status: result.error.status })
|
|
49
|
-
}
|
|
60
|
+
// Process payment middleware
|
|
61
|
+
const result = await processPaymentMiddleware(paymentOptions, headers)
|
|
50
62
|
|
|
51
|
-
|
|
63
|
+
// Payment required - return 402 with payment details
|
|
64
|
+
if (result.paymentRequired) {
|
|
65
|
+
const response = NextResponse.json(result.paymentRequired.body, { status: 402 })
|
|
66
|
+
Object.entries(result.paymentRequired.headers).forEach(([key, value]) => {
|
|
67
|
+
response.headers.set(key, value)
|
|
68
|
+
})
|
|
69
|
+
return response
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Error
|
|
73
|
+
if (result.error) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: result.error.message },
|
|
76
|
+
{ status: result.error.status }
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Payment verified - return premium content
|
|
81
|
+
if (result.allowed) {
|
|
82
|
+
return NextResponse.json({
|
|
83
|
+
success: true,
|
|
84
|
+
message: 'Premium content unlocked!',
|
|
85
|
+
data: {
|
|
86
|
+
secret: 'This is premium data that required payment.',
|
|
87
|
+
timestamp: new Date().toISOString(),
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({ error: 'Unexpected error' }, { status: 500 })
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error in premium route:', error)
|
|
95
|
+
return NextResponse.json(
|
|
96
|
+
{ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' },
|
|
97
|
+
{ status: 500 }
|
|
98
|
+
)
|
|
99
|
+
}
|
|
52
100
|
}
|
|
@@ -144,7 +144,18 @@ export default function Home() {
|
|
|
144
144
|
const res = await fetch(`${API_BASE}${endpoint}`)
|
|
145
145
|
|
|
146
146
|
if (res.status === 402) {
|
|
147
|
-
|
|
147
|
+
let body = {}
|
|
148
|
+
const contentType = res.headers.get("content-type")
|
|
149
|
+
if (contentType && contentType.includes("application/json")) {
|
|
150
|
+
try {
|
|
151
|
+
const text = await res.text()
|
|
152
|
+
if (text.trim()) {
|
|
153
|
+
body = JSON.parse(text)
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
// Ignore JSON parse errors, use empty object
|
|
157
|
+
}
|
|
158
|
+
}
|
|
148
159
|
|
|
149
160
|
const amount = res.headers.get("x-402-amount") || body.amount || "0"
|
|
150
161
|
const token = res.headers.get("x-402-token") || body.token || "MNT"
|
|
@@ -165,7 +176,22 @@ export default function Home() {
|
|
|
165
176
|
return
|
|
166
177
|
}
|
|
167
178
|
|
|
168
|
-
|
|
179
|
+
let data = {}
|
|
180
|
+
const contentType = res.headers.get("content-type")
|
|
181
|
+
if (contentType && contentType.includes("application/json")) {
|
|
182
|
+
try {
|
|
183
|
+
const text = await res.text()
|
|
184
|
+
if (text.trim()) {
|
|
185
|
+
data = JSON.parse(text)
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
setError("Failed to parse response as JSON")
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
const text = await res.text()
|
|
193
|
+
data = { message: text || "Request successful" }
|
|
194
|
+
}
|
|
169
195
|
setResponse(data)
|
|
170
196
|
} catch (err) {
|
|
171
197
|
setError(err instanceof Error ? err.message : "Request failed")
|
|
@@ -193,7 +219,23 @@ export default function Home() {
|
|
|
193
219
|
"X-402-Transaction-Hash": payment.transactionHash,
|
|
194
220
|
},
|
|
195
221
|
})
|
|
196
|
-
|
|
222
|
+
|
|
223
|
+
let data = {}
|
|
224
|
+
const contentType = res.headers.get("content-type")
|
|
225
|
+
if (contentType && contentType.includes("application/json")) {
|
|
226
|
+
try {
|
|
227
|
+
const text = await res.text()
|
|
228
|
+
if (text.trim()) {
|
|
229
|
+
data = JSON.parse(text)
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
lastError = "Failed to parse response"
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
const text = await res.text()
|
|
237
|
+
data = { message: text || "Request successful" }
|
|
238
|
+
}
|
|
197
239
|
|
|
198
240
|
if (res.ok) {
|
|
199
241
|
setResponse(data)
|
|
@@ -231,7 +273,24 @@ export default function Home() {
|
|
|
231
273
|
|
|
232
274
|
try {
|
|
233
275
|
const res = await fetch(`${API_BASE}${endpoint}`)
|
|
234
|
-
|
|
276
|
+
|
|
277
|
+
let data = {}
|
|
278
|
+
const contentType = res.headers.get("content-type")
|
|
279
|
+
if (contentType && contentType.includes("application/json")) {
|
|
280
|
+
try {
|
|
281
|
+
const text = await res.text()
|
|
282
|
+
if (text.trim()) {
|
|
283
|
+
data = JSON.parse(text)
|
|
284
|
+
}
|
|
285
|
+
} catch (err) {
|
|
286
|
+
setError("Failed to parse response as JSON")
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
const text = await res.text()
|
|
291
|
+
data = { message: text || "Request successful" }
|
|
292
|
+
}
|
|
293
|
+
|
|
235
294
|
setResponse(data)
|
|
236
295
|
} catch (err) {
|
|
237
296
|
setError(err instanceof Error ? err.message : "Request failed")
|
|
@@ -1,52 +1,100 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
-
import { processPaymentMiddleware, initializePlatform } from 'x402-mantle-sdk/server'
|
|
2
|
+
import { processPaymentMiddleware, initializePlatform, clearCache } from 'x402-mantle-sdk/server'
|
|
3
|
+
|
|
4
|
+
// Configuration - Set these in your .env file
|
|
5
|
+
const appId = process.env.X402_APP_ID || ''
|
|
6
|
+
const platformUrl = process.env.X402_PLATFORM_URL || 'https://mantle-x402.vercel.app'
|
|
7
|
+
|
|
8
|
+
if (!process.env.X402_APP_ID) {
|
|
9
|
+
process.env.X402_APP_ID = appId
|
|
10
|
+
}
|
|
11
|
+
if (!process.env.X402_PLATFORM_URL) {
|
|
12
|
+
process.env.X402_PLATFORM_URL = platformUrl
|
|
13
|
+
}
|
|
3
14
|
|
|
4
15
|
let initialized = false
|
|
16
|
+
let initError: Error | null = null
|
|
5
17
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!initialized) {
|
|
18
|
+
const initPlatform = async () => {
|
|
19
|
+
try {
|
|
9
20
|
await initializePlatform()
|
|
10
21
|
initialized = true
|
|
22
|
+
initError = null
|
|
23
|
+
} catch (error) {
|
|
24
|
+
initError = error as Error
|
|
25
|
+
console.error('Platform initialization failed:', error)
|
|
11
26
|
}
|
|
27
|
+
}
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Payment required - return 402 with payment details
|
|
26
|
-
if (result.paymentRequired) {
|
|
27
|
-
const response = NextResponse.json(result.paymentRequired.body, { status: 402 })
|
|
28
|
-
Object.entries(result.paymentRequired.headers).forEach(([key, value]) => {
|
|
29
|
-
response.headers.set(key, value)
|
|
30
|
-
})
|
|
31
|
-
return response
|
|
32
|
-
}
|
|
29
|
+
export async function GET(request: NextRequest) {
|
|
30
|
+
try {
|
|
31
|
+
// Initialize platform on first request
|
|
32
|
+
if (!initialized && !initError) {
|
|
33
|
+
await initPlatform()
|
|
34
|
+
} else if (initError) {
|
|
35
|
+
try { clearCache() } catch {}
|
|
36
|
+
initError = null
|
|
37
|
+
await initPlatform()
|
|
38
|
+
}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if (initError) {
|
|
41
|
+
return NextResponse.json(
|
|
42
|
+
{ error: 'Platform initialization failed', details: (initError as Error).message },
|
|
43
|
+
{ status: 500 }
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Payment options
|
|
48
|
+
const paymentOptions = {
|
|
49
|
+
price: '0.001',
|
|
50
|
+
token: 'MNT',
|
|
51
|
+
testnet: true,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Convert headers to plain object
|
|
55
|
+
const headers: Record<string, string> = {}
|
|
56
|
+
request.headers.forEach((value, key) => {
|
|
57
|
+
headers[key] = value
|
|
43
58
|
})
|
|
44
|
-
}
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return NextResponse.json({ error: result.error.message }, { status: result.error.status })
|
|
49
|
-
}
|
|
60
|
+
// Process payment middleware
|
|
61
|
+
const result = await processPaymentMiddleware(paymentOptions, headers)
|
|
50
62
|
|
|
51
|
-
|
|
63
|
+
// Payment required - return 402 with payment details
|
|
64
|
+
if (result.paymentRequired) {
|
|
65
|
+
const response = NextResponse.json(result.paymentRequired.body, { status: 402 })
|
|
66
|
+
Object.entries(result.paymentRequired.headers).forEach(([key, value]) => {
|
|
67
|
+
response.headers.set(key, value)
|
|
68
|
+
})
|
|
69
|
+
return response
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Error
|
|
73
|
+
if (result.error) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: result.error.message },
|
|
76
|
+
{ status: result.error.status }
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Payment verified - return premium content
|
|
81
|
+
if (result.allowed) {
|
|
82
|
+
return NextResponse.json({
|
|
83
|
+
success: true,
|
|
84
|
+
message: 'Premium content unlocked!',
|
|
85
|
+
data: {
|
|
86
|
+
secret: 'This is premium data that required payment.',
|
|
87
|
+
timestamp: new Date().toISOString(),
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({ error: 'Unexpected error' }, { status: 500 })
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error in premium route:', error)
|
|
95
|
+
return NextResponse.json(
|
|
96
|
+
{ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' },
|
|
97
|
+
{ status: 500 }
|
|
98
|
+
)
|
|
99
|
+
}
|
|
52
100
|
}
|
|
@@ -144,7 +144,18 @@ export default function Home() {
|
|
|
144
144
|
const res = await fetch(`${API_BASE}${endpoint}`)
|
|
145
145
|
|
|
146
146
|
if (res.status === 402) {
|
|
147
|
-
|
|
147
|
+
let body = {}
|
|
148
|
+
const contentType = res.headers.get("content-type")
|
|
149
|
+
if (contentType && contentType.includes("application/json")) {
|
|
150
|
+
try {
|
|
151
|
+
const text = await res.text()
|
|
152
|
+
if (text.trim()) {
|
|
153
|
+
body = JSON.parse(text)
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
// Ignore JSON parse errors, use empty object
|
|
157
|
+
}
|
|
158
|
+
}
|
|
148
159
|
|
|
149
160
|
const amount = res.headers.get("x-402-amount") || body.amount || "0"
|
|
150
161
|
const token = res.headers.get("x-402-token") || body.token || "MNT"
|
|
@@ -165,7 +176,22 @@ export default function Home() {
|
|
|
165
176
|
return
|
|
166
177
|
}
|
|
167
178
|
|
|
168
|
-
|
|
179
|
+
let data = {}
|
|
180
|
+
const contentType = res.headers.get("content-type")
|
|
181
|
+
if (contentType && contentType.includes("application/json")) {
|
|
182
|
+
try {
|
|
183
|
+
const text = await res.text()
|
|
184
|
+
if (text.trim()) {
|
|
185
|
+
data = JSON.parse(text)
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
setError("Failed to parse response as JSON")
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
const text = await res.text()
|
|
193
|
+
data = { message: text || "Request successful" }
|
|
194
|
+
}
|
|
169
195
|
setResponse(data)
|
|
170
196
|
} catch (err) {
|
|
171
197
|
setError(err instanceof Error ? err.message : "Request failed")
|
|
@@ -193,7 +219,23 @@ export default function Home() {
|
|
|
193
219
|
"X-402-Transaction-Hash": payment.transactionHash,
|
|
194
220
|
},
|
|
195
221
|
})
|
|
196
|
-
|
|
222
|
+
|
|
223
|
+
let data = {}
|
|
224
|
+
const contentType = res.headers.get("content-type")
|
|
225
|
+
if (contentType && contentType.includes("application/json")) {
|
|
226
|
+
try {
|
|
227
|
+
const text = await res.text()
|
|
228
|
+
if (text.trim()) {
|
|
229
|
+
data = JSON.parse(text)
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
lastError = "Failed to parse response"
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
const text = await res.text()
|
|
237
|
+
data = { message: text || "Request successful" }
|
|
238
|
+
}
|
|
197
239
|
|
|
198
240
|
if (res.ok) {
|
|
199
241
|
setResponse(data)
|
|
@@ -231,7 +273,24 @@ export default function Home() {
|
|
|
231
273
|
|
|
232
274
|
try {
|
|
233
275
|
const res = await fetch(`${API_BASE}${endpoint}`)
|
|
234
|
-
|
|
276
|
+
|
|
277
|
+
let data = {}
|
|
278
|
+
const contentType = res.headers.get("content-type")
|
|
279
|
+
if (contentType && contentType.includes("application/json")) {
|
|
280
|
+
try {
|
|
281
|
+
const text = await res.text()
|
|
282
|
+
if (text.trim()) {
|
|
283
|
+
data = JSON.parse(text)
|
|
284
|
+
}
|
|
285
|
+
} catch (err) {
|
|
286
|
+
setError("Failed to parse response as JSON")
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
const text = await res.text()
|
|
291
|
+
data = { message: text || "Request successful" }
|
|
292
|
+
}
|
|
293
|
+
|
|
235
294
|
setResponse(data)
|
|
236
295
|
} catch (err) {
|
|
237
296
|
setError(err instanceof Error ? err.message : "Request failed")
|