create-x402-app 0.1.2 → 0.1.3

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.
Files changed (60) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +2 -2
  3. package/templates/backend-express/README.md +56 -0
  4. package/templates/backend-express/env.example +8 -0
  5. package/templates/backend-express/gitignore +4 -0
  6. package/templates/backend-express/package.json +23 -0
  7. package/templates/backend-express/src/index.ts +79 -0
  8. package/templates/backend-express/tsconfig.json +18 -0
  9. package/templates/backend-hono/README.md +57 -0
  10. package/templates/backend-hono/env.example +8 -0
  11. package/templates/backend-hono/gitignore +4 -0
  12. package/templates/backend-hono/package.json +21 -0
  13. package/templates/backend-hono/src/index.ts +75 -0
  14. package/templates/backend-hono/tsconfig.json +18 -0
  15. package/templates/fullstack-express/README.md +308 -0
  16. package/templates/fullstack-express/_env.example +11 -0
  17. package/templates/fullstack-express/_gitignore +46 -0
  18. package/templates/fullstack-express/eslint.config.mjs +18 -0
  19. package/templates/fullstack-express/next-env.d.ts +6 -0
  20. package/templates/fullstack-express/next.config.ts +7 -0
  21. package/templates/fullstack-express/package.json +33 -0
  22. package/templates/fullstack-express/postcss.config.mjs +7 -0
  23. package/templates/fullstack-express/public/X402.png +0 -0
  24. package/templates/fullstack-express/src/app/api/info/route.ts +14 -0
  25. package/templates/fullstack-express/src/app/api/premium/route.ts +52 -0
  26. package/templates/fullstack-express/src/app/api/weather/route.ts +103 -0
  27. package/templates/fullstack-express/src/app/favicon.ico +0 -0
  28. package/templates/fullstack-express/src/app/globals.css +82 -0
  29. package/templates/fullstack-express/src/app/layout.tsx +42 -0
  30. package/templates/fullstack-express/src/app/page.tsx +511 -0
  31. package/templates/fullstack-express/src/components/grain-overlay.tsx +11 -0
  32. package/templates/fullstack-express/src/components/magnetic-button.tsx +90 -0
  33. package/templates/fullstack-express/src/components/ui/blur-fade.tsx +81 -0
  34. package/templates/fullstack-express/src/components/ui/magic-card.tsx +103 -0
  35. package/templates/fullstack-express/src/lib/utils.ts +6 -0
  36. package/templates/fullstack-express/src/types/ethereum.d.ts +11 -0
  37. package/templates/fullstack-express/tsconfig.json +34 -0
  38. package/templates/fullstack-hono/README.md +308 -0
  39. package/templates/fullstack-hono/_env.example +11 -0
  40. package/templates/fullstack-hono/_gitignore +46 -0
  41. package/templates/fullstack-hono/eslint.config.mjs +18 -0
  42. package/templates/fullstack-hono/next-env.d.ts +6 -0
  43. package/templates/fullstack-hono/next.config.ts +7 -0
  44. package/templates/fullstack-hono/package.json +33 -0
  45. package/templates/fullstack-hono/postcss.config.mjs +7 -0
  46. package/templates/fullstack-hono/public/X402.png +0 -0
  47. package/templates/fullstack-hono/src/app/api/info/route.ts +14 -0
  48. package/templates/fullstack-hono/src/app/api/premium/route.ts +52 -0
  49. package/templates/fullstack-hono/src/app/api/weather/route.ts +103 -0
  50. package/templates/fullstack-hono/src/app/favicon.ico +0 -0
  51. package/templates/fullstack-hono/src/app/globals.css +82 -0
  52. package/templates/fullstack-hono/src/app/layout.tsx +42 -0
  53. package/templates/fullstack-hono/src/app/page.tsx +511 -0
  54. package/templates/fullstack-hono/src/components/grain-overlay.tsx +11 -0
  55. package/templates/fullstack-hono/src/components/magnetic-button.tsx +90 -0
  56. package/templates/fullstack-hono/src/components/ui/blur-fade.tsx +81 -0
  57. package/templates/fullstack-hono/src/components/ui/magic-card.tsx +103 -0
  58. package/templates/fullstack-hono/src/lib/utils.ts +6 -0
  59. package/templates/fullstack-hono/src/types/ethereum.d.ts +11 -0
  60. package/templates/fullstack-hono/tsconfig.json +34 -0
@@ -0,0 +1,81 @@
1
+ "use client"
2
+
3
+ import { useRef } from "react"
4
+ import {
5
+ AnimatePresence,
6
+ motion,
7
+ MotionProps,
8
+ useInView,
9
+ UseInViewOptions,
10
+ Variants,
11
+ } from "motion/react"
12
+
13
+ type MarginType = UseInViewOptions["margin"]
14
+
15
+ interface BlurFadeProps extends MotionProps {
16
+ children: React.ReactNode
17
+ className?: string
18
+ variant?: {
19
+ hidden: { y: number }
20
+ visible: { y: number }
21
+ }
22
+ duration?: number
23
+ delay?: number
24
+ offset?: number
25
+ direction?: "up" | "down" | "left" | "right"
26
+ inView?: boolean
27
+ inViewMargin?: MarginType
28
+ blur?: string
29
+ }
30
+
31
+ export function BlurFade({
32
+ children,
33
+ className,
34
+ variant,
35
+ duration = 0.4,
36
+ delay = 0,
37
+ offset = 6,
38
+ direction = "down",
39
+ inView = false,
40
+ inViewMargin = "-50px",
41
+ blur = "6px",
42
+ ...props
43
+ }: BlurFadeProps) {
44
+ const ref = useRef(null)
45
+ const inViewResult = useInView(ref, { once: true, margin: inViewMargin })
46
+ const isInView = !inView || inViewResult
47
+ const defaultVariants: Variants = {
48
+ hidden: {
49
+ [direction === "left" || direction === "right" ? "x" : "y"]:
50
+ direction === "right" || direction === "down" ? -offset : offset,
51
+ opacity: 0,
52
+ filter: `blur(${blur})`,
53
+ },
54
+ visible: {
55
+ [direction === "left" || direction === "right" ? "x" : "y"]: 0,
56
+ opacity: 1,
57
+ filter: `blur(0px)`,
58
+ },
59
+ }
60
+ const combinedVariants = variant || defaultVariants
61
+ return (
62
+ <AnimatePresence>
63
+ <motion.div
64
+ ref={ref}
65
+ initial="hidden"
66
+ animate={isInView ? "visible" : "hidden"}
67
+ exit="hidden"
68
+ variants={combinedVariants}
69
+ transition={{
70
+ delay: 0.04 + delay,
71
+ duration,
72
+ ease: "easeOut",
73
+ }}
74
+ className={className}
75
+ {...props}
76
+ >
77
+ {children}
78
+ </motion.div>
79
+ </AnimatePresence>
80
+ )
81
+ }
@@ -0,0 +1,103 @@
1
+ "use client"
2
+
3
+ import React, { useCallback, useEffect } from "react"
4
+ import { motion, useMotionTemplate, useMotionValue } from "motion/react"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ interface MagicCardProps {
9
+ children?: React.ReactNode
10
+ className?: string
11
+ gradientSize?: number
12
+ gradientColor?: string
13
+ gradientOpacity?: number
14
+ gradientFrom?: string
15
+ gradientTo?: string
16
+ }
17
+
18
+ export function MagicCard({
19
+ children,
20
+ className,
21
+ gradientSize = 200,
22
+ gradientColor = "#262626",
23
+ gradientOpacity = 0.8,
24
+ gradientFrom = "#9E7AFF",
25
+ gradientTo = "#FE8BBB",
26
+ }: MagicCardProps) {
27
+ const mouseX = useMotionValue(-gradientSize)
28
+ const mouseY = useMotionValue(-gradientSize)
29
+ const reset = useCallback(() => {
30
+ mouseX.set(-gradientSize)
31
+ mouseY.set(-gradientSize)
32
+ }, [gradientSize, mouseX, mouseY])
33
+
34
+ const handlePointerMove = useCallback(
35
+ (e: React.PointerEvent<HTMLDivElement>) => {
36
+ const rect = e.currentTarget.getBoundingClientRect()
37
+ mouseX.set(e.clientX - rect.left)
38
+ mouseY.set(e.clientY - rect.top)
39
+ },
40
+ [mouseX, mouseY]
41
+ )
42
+
43
+ useEffect(() => {
44
+ reset()
45
+ }, [reset])
46
+
47
+ useEffect(() => {
48
+ const handleGlobalPointerOut = (e: PointerEvent) => {
49
+ if (!e.relatedTarget) {
50
+ reset()
51
+ }
52
+ }
53
+
54
+ const handleVisibility = () => {
55
+ if (document.visibilityState !== "visible") {
56
+ reset()
57
+ }
58
+ }
59
+
60
+ window.addEventListener("pointerout", handleGlobalPointerOut)
61
+ window.addEventListener("blur", reset)
62
+ document.addEventListener("visibilitychange", handleVisibility)
63
+
64
+ return () => {
65
+ window.removeEventListener("pointerout", handleGlobalPointerOut)
66
+ window.removeEventListener("blur", reset)
67
+ document.removeEventListener("visibilitychange", handleVisibility)
68
+ }
69
+ }, [reset])
70
+
71
+ return (
72
+ <div
73
+ className={cn("group relative rounded-[inherit]", className)}
74
+ onPointerMove={handlePointerMove}
75
+ onPointerLeave={reset}
76
+ onPointerEnter={reset}
77
+ >
78
+ <motion.div
79
+ className="bg-border pointer-events-none absolute inset-0 rounded-[inherit] duration-300 group-hover:opacity-100"
80
+ style={{
81
+ background: useMotionTemplate`
82
+ radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,
83
+ ${gradientFrom},
84
+ ${gradientTo},
85
+ var(--border) 100%
86
+ )
87
+ `,
88
+ }}
89
+ />
90
+ <div className="bg-background absolute inset-px rounded-[inherit]" />
91
+ <motion.div
92
+ className="pointer-events-none absolute inset-px rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100"
93
+ style={{
94
+ background: useMotionTemplate`
95
+ radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px, ${gradientColor}, transparent 100%)
96
+ `,
97
+ opacity: gradientOpacity,
98
+ }}
99
+ />
100
+ <div className="relative">{children}</div>
101
+ </div>
102
+ )
103
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,11 @@
1
+ interface EthereumProvider {
2
+ request(args: { method: string; params?: unknown[] }): Promise<unknown>
3
+ isMetaMask?: boolean
4
+ on(event: string, handler: (...args: unknown[]) => void): void
5
+ removeListener(event: string, handler: (...args: unknown[]) => void): void
6
+ }
7
+
8
+ interface Window {
9
+ ethereum?: EthereumProvider
10
+ }
11
+
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./src/*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules", "server.ts"]
34
+ }
@@ -0,0 +1,308 @@
1
+ # x402 Starter Template
2
+
3
+ A production-ready Next.js boilerplate for building **pay-per-request APIs** using the HTTP 402 protocol on Mantle Network.
4
+
5
+ ## What is x402?
6
+
7
+ x402 implements the [HTTP 402 Payment Required](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402) status code to enable native payments for API access. When a client requests a paid endpoint without payment, the server returns a 402 response with payment details. After payment, the client retries with the transaction hash to access the content.
8
+
9
+ ## Quick Start
10
+
11
+ ### 1. Clone and Install
12
+
13
+ ```bash
14
+ git clone https://github.com/your-repo/x402-starter.git
15
+ cd x402-starter
16
+ npm install
17
+ ```
18
+
19
+ ### 2. Get Your App ID
20
+
21
+ 1. Visit the [x402 Dashboard](https://mantle-x402.vercel.app) or [Documentation](https://mantle-x402.vercel.app/dashboard?tab=docs)
22
+ 2. Connect your wallet
23
+ 3. Create a new project
24
+ 4. Copy your **App ID**
25
+
26
+ ### 3. Configure Environment
27
+
28
+ ```bash
29
+ cp .env.example .env
30
+ ```
31
+
32
+ Edit `.env`:
33
+
34
+ ```env
35
+ X402_APP_ID=your_app_id_here
36
+ X402_PLATFORM_URL=https://mantle-x402.vercel.app
37
+ ```
38
+
39
+ ### 4. Run Development Server
40
+
41
+ ```bash
42
+ npm run dev
43
+ ```
44
+
45
+ Open [http://localhost:3000](http://localhost:3000)
46
+
47
+ ## Project Structure
48
+
49
+ ```
50
+ x402-starter/
51
+ ├── src/
52
+ │ ├── app/
53
+ │ │ ├── api/
54
+ │ │ │ ├── info/route.ts # Free endpoint (no payment)
55
+ │ │ │ ├── premium/route.ts # Paid endpoint (0.001 MNT)
56
+ │ │ │ └── weather/route.ts # Paid endpoint (0.0005 MNT)
57
+ │ │ ├── page.tsx # Demo frontend
58
+ │ │ └── layout.tsx # Root layout
59
+ │ ├── components/ # UI components
60
+ │ └── lib/ # Utilities
61
+ ├── .env.example # Environment template
62
+ └── package.json
63
+ ```
64
+
65
+ ## API Endpoints
66
+
67
+ | Endpoint | Price | Description |
68
+ |----------|-------|-------------|
69
+ | `GET /api/info` | Free | API information |
70
+ | `GET /api/premium` | 0.001 MNT | Premium content |
71
+ | `GET /api/weather` | 0.0005 MNT | Weather data |
72
+
73
+ ## Creating Paid Endpoints
74
+
75
+ ### Basic Example
76
+
77
+ ```typescript
78
+ // src/app/api/my-endpoint/route.ts
79
+ import { NextRequest, NextResponse } from 'next/server'
80
+ import { processPaymentMiddleware, initializePlatform } from 'x402-mantle-sdk/server'
81
+
82
+ let initialized = false
83
+
84
+ export async function GET(request: NextRequest) {
85
+ // Initialize once
86
+ if (!initialized) {
87
+ await initializePlatform()
88
+ initialized = true
89
+ }
90
+
91
+ // Convert headers
92
+ const headers: Record<string, string> = {}
93
+ request.headers.forEach((value, key) => {
94
+ headers[key] = value
95
+ })
96
+
97
+ // Check payment
98
+ const result = await processPaymentMiddleware(
99
+ { price: '0.01', token: 'MNT', testnet: true },
100
+ headers
101
+ )
102
+
103
+ // Return 402 if payment required
104
+ if (result.paymentRequired) {
105
+ const response = NextResponse.json(result.paymentRequired.body, { status: 402 })
106
+ Object.entries(result.paymentRequired.headers).forEach(([key, value]) => {
107
+ response.headers.set(key, value)
108
+ })
109
+ return response
110
+ }
111
+
112
+ // Return content if payment verified
113
+ if (result.allowed) {
114
+ return NextResponse.json({
115
+ success: true,
116
+ data: 'Your premium content here'
117
+ })
118
+ }
119
+
120
+ return NextResponse.json({ error: 'Error' }, { status: 500 })
121
+ }
122
+ ```
123
+
124
+ ### Payment Options
125
+
126
+ ```typescript
127
+ const options = {
128
+ price: '0.001', // Amount to charge
129
+ token: 'MNT', // Token (MNT, USDC, USDT)
130
+ testnet: true, // Use Mantle Sepolia (false for mainnet)
131
+ network: 'mantle-sepolia' // Or 'mantle' for mainnet
132
+ }
133
+ ```
134
+
135
+ ## Client-Side Integration
136
+
137
+ ### Using the PaymentModal Component
138
+
139
+ ```tsx
140
+ import { PaymentModal } from 'x402-mantle-sdk/client/react'
141
+
142
+ function MyComponent() {
143
+ const [showModal, setShowModal] = useState(false)
144
+ const [paymentRequest, setPaymentRequest] = useState(null)
145
+
146
+ const fetchPaidEndpoint = async () => {
147
+ const res = await fetch('/api/premium')
148
+
149
+ if (res.status === 402) {
150
+ // Extract payment details from response
151
+ const body = await res.json()
152
+ setPaymentRequest({
153
+ amount: body.amount,
154
+ token: body.token,
155
+ network: body.network,
156
+ recipient: body.recipient,
157
+ chainId: body.chainId,
158
+ })
159
+ setShowModal(true)
160
+ }
161
+ }
162
+
163
+ const handlePaymentComplete = async (payment) => {
164
+ // Retry with transaction hash
165
+ const res = await fetch('/api/premium', {
166
+ headers: {
167
+ 'X-402-Transaction-Hash': payment.transactionHash
168
+ }
169
+ })
170
+ const data = await res.json()
171
+ console.log('Premium data:', data)
172
+ }
173
+
174
+ return (
175
+ <>
176
+ <button onClick={fetchPaidEndpoint}>Get Premium Data</button>
177
+
178
+ {paymentRequest && (
179
+ <PaymentModal
180
+ request={paymentRequest}
181
+ isOpen={showModal}
182
+ onComplete={handlePaymentComplete}
183
+ onCancel={() => setShowModal(false)}
184
+ />
185
+ )}
186
+ </>
187
+ )
188
+ }
189
+ ```
190
+
191
+ ### Using x402Fetch (Auto-handles payments)
192
+
193
+ ```typescript
194
+ import { x402Fetch } from 'x402-mantle-sdk/client'
195
+
196
+ // Automatically shows payment modal when 402 is returned
197
+ const response = await x402Fetch('/api/premium')
198
+ const data = await response.json()
199
+ ```
200
+
201
+ ## HTTP 402 Protocol
202
+
203
+ ### Request Flow
204
+
205
+ ```
206
+ 1. Client requests paid endpoint
207
+ GET /api/premium
208
+
209
+ 2. Server returns 402 with payment details
210
+ HTTP/1.1 402 Payment Required
211
+ X-402-Amount: 0.001
212
+ X-402-Token: MNT
213
+ X-402-Network: mantle-sepolia
214
+ X-402-Recipient: 0x...
215
+
216
+ 3. Client makes payment on-chain
217
+
218
+ 4. Client retries with transaction hash
219
+ GET /api/premium
220
+ X-402-Transaction-Hash: 0x...
221
+
222
+ 5. Server verifies payment and returns content
223
+ HTTP/1.1 200 OK
224
+ { "data": "Premium content" }
225
+ ```
226
+
227
+ ## Supported Networks
228
+
229
+ | Network | Chain ID | Environment |
230
+ |---------|----------|-------------|
231
+ | Mantle | 5000 | Production |
232
+ | Mantle Sepolia | 5003 | Testnet |
233
+
234
+ ## Supported Tokens
235
+
236
+ | Token | Mainnet | Testnet |
237
+ |-------|---------|---------|
238
+ | MNT | Native | Native |
239
+ | USDC | 0x09Bc4E... | 0x09Bc4E... |
240
+ | USDT | 0x201EBa... | - |
241
+
242
+ ## Testing
243
+
244
+ 1. Install [MetaMask](https://metamask.io/)
245
+ 2. Add Mantle Sepolia network:
246
+ - RPC: `https://rpc.sepolia.mantle.xyz`
247
+ - Chain ID: `5003`
248
+ - Symbol: `MNT`
249
+ 3. Get test MNT from [Mantle Faucet](https://faucet.sepolia.mantle.xyz/)
250
+ 4. Connect wallet and try paid endpoints
251
+
252
+ ## Deployment
253
+
254
+ ### Vercel (Recommended)
255
+
256
+ ```bash
257
+ npm run build
258
+ vercel deploy
259
+ ```
260
+
261
+ Set environment variables in Vercel dashboard:
262
+ - `X402_APP_ID`
263
+ - `X402_PLATFORM_URL`
264
+
265
+ ### Other Platforms
266
+
267
+ Works with any Node.js hosting:
268
+ - Railway
269
+ - Render
270
+ - AWS Lambda
271
+ - Google Cloud Run
272
+
273
+ ## Configuration
274
+
275
+ ### Environment Variables
276
+
277
+ | Variable | Required | Description |
278
+ |----------|----------|-------------|
279
+ | `X402_APP_ID` | Yes | Your project App ID from the dashboard |
280
+ | `X402_PLATFORM_URL` | Yes | Platform URL (default: https://mantle-x402.vercel.app) |
281
+
282
+ ## Troubleshooting
283
+
284
+ ### "X402_APP_ID is required"
285
+
286
+ Make sure your `.env` file exists and contains a valid App ID.
287
+
288
+ ### Payment not verifying
289
+
290
+ 1. Wait for transaction confirmation (~3 seconds)
291
+ 2. Check that the transaction was successful on block explorer
292
+ 3. Ensure the amount matches exactly
293
+
294
+ ### Network mismatch
295
+
296
+ Make sure your wallet is on the same network as configured in the endpoint (testnet vs mainnet).
297
+
298
+ ## Resources
299
+
300
+ - [x402 Documentation](https://mantle-x402.vercel.app/dashboard?tab=docs)
301
+ - [x402 Dashboard](https://mantle-x402.vercel.app)
302
+ - [x402-mantle-sdk on npm](https://www.npmjs.com/package/x402-mantle-sdk)
303
+ - [Mantle Network](https://www.mantle.xyz/)
304
+ - [Mantle Faucet](https://faucet.sepolia.mantle.xyz/)
305
+
306
+ ## License
307
+
308
+ MIT
@@ -0,0 +1,11 @@
1
+ # x402 Configuration
2
+ # Get your App ID from: https://mantle-x402.vercel.app
3
+ #
4
+ # Steps:
5
+ # 1. Visit the dashboard URL above
6
+ # 2. Connect your wallet
7
+ # 3. Create a new project
8
+ # 4. Copy your App ID and paste it below
9
+
10
+ X402_APP_ID=your_app_id_here
11
+ X402_PLATFORM_URL=https://mantle-x402.vercel.app
@@ -0,0 +1,46 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files
34
+ .env
35
+ .env.local
36
+ .env.*.local
37
+
38
+ # keep example
39
+ !.env.example
40
+
41
+ # vercel
42
+ .vercel
43
+
44
+ # typescript
45
+ *.tsbuildinfo
46
+ next-env.d.ts
@@ -0,0 +1,18 @@
1
+ import { defineConfig, globalIgnores } from "eslint/config";
2
+ import nextVitals from "eslint-config-next/core-web-vitals";
3
+ import nextTs from "eslint-config-next/typescript";
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ ".next/**",
12
+ "out/**",
13
+ "build/**",
14
+ "next-env.d.ts",
15
+ ]),
16
+ ]);
17
+
18
+ export default eslintConfig;
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "x402-starter",
3
+ "version": "1.0.0",
4
+ "description": "A Next.js starter template for building pay-per-request APIs with x402 on Mantle Network",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint"
11
+ },
12
+ "dependencies": {
13
+ "clsx": "^2.1.1",
14
+ "ethers": "^6.16.0",
15
+ "motion": "^12.23.26",
16
+ "next": "16.1.0",
17
+ "react": "19.2.3",
18
+ "react-dom": "19.2.3",
19
+ "tailwind-merge": "^3.4.0",
20
+ "tw-animate-css": "^1.4.0",
21
+ "x402-mantle-sdk": "^0.2.3"
22
+ },
23
+ "devDependencies": {
24
+ "@tailwindcss/postcss": "^4",
25
+ "@types/node": "^20",
26
+ "@types/react": "^19",
27
+ "@types/react-dom": "^19",
28
+ "eslint": "^9",
29
+ "eslint-config-next": "16.1.0",
30
+ "tailwindcss": "^4",
31
+ "typescript": "^5"
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,14 @@
1
+ import { NextResponse } from 'next/server'
2
+
3
+ export async function GET() {
4
+ return NextResponse.json({
5
+ message: 'Welcome to x402 API',
6
+ endpoints: {
7
+ free: 'GET /api/info',
8
+ paid: 'GET /api/premium (0.001 MNT)',
9
+ paidWeather: 'GET /api/weather (0.0005 MNT)',
10
+ },
11
+ network: 'mantle-sepolia',
12
+ documentation: 'https://mantle-x402.vercel.app',
13
+ })
14
+ }