1- import { NextResponse } from "next/server" ;
21import { Environment , Para as ParaServer } from "@getpara/server-sdk" ;
3- import { eq , and } from "drizzle-orm" ;
2+ import { and , eq } from "drizzle-orm" ;
3+ import { NextResponse } from "next/server" ;
44import { auth } from "@/lib/auth" ;
55import { db } from "@/lib/db" ;
6- import { paraWallets , integrations } from "@/lib/db/schema" ;
6+ import { createIntegration } from "@/lib/db/integrations" ;
7+ import { integrations , paraWallets } from "@/lib/db/schema" ;
78import { encryptUserShare } from "@/lib/encryption" ;
89import { getUserWallet , userHasWallet } from "@/lib/para/wallet-helpers" ;
9- import { createIntegration } from "@/lib/db/integrations" ;
1010
11- const PARA_API_KEY = process . env . PARA_API_KEY ! ;
11+ const PARA_API_KEY = process . env . PARA_API_KEY || "" ;
1212const PARA_ENV = process . env . PARA_ENVIRONMENT || "beta" ;
1313
14+ // Helper: Validate user authentication and email
15+ async function validateUser ( request : Request ) {
16+ const session = await auth . api . getSession ( {
17+ headers : request . headers ,
18+ } ) ;
19+
20+ if ( ! session ?. user ) {
21+ return { error : "Unauthorized" , status : 401 } ;
22+ }
23+
24+ const user = session . user ;
25+
26+ if ( ! user . email ) {
27+ return { error : "Email required to create wallet" , status : 400 } ;
28+ }
29+
30+ // Check if user is anonymous
31+ if (
32+ user . email . includes ( "@http://" ) ||
33+ user . email . includes ( "@https://" ) ||
34+ user . email . startsWith ( "temp-" )
35+ ) {
36+ return {
37+ error :
38+ "Anonymous users cannot create wallets. Please sign in with a real account." ,
39+ status : 400 ,
40+ } ;
41+ }
42+
43+ return { user } ;
44+ }
45+
46+ // Helper: Check if wallet or integration already exists
47+ async function checkExistingWallet ( userId : string ) {
48+ const hasWallet = await userHasWallet ( userId ) ;
49+ if ( hasWallet ) {
50+ return { error : "Wallet already exists for this user" , status : 400 } ;
51+ }
52+
53+ const existingIntegration = await db
54+ . select ( )
55+ . from ( integrations )
56+ . where ( and ( eq ( integrations . userId , userId ) , eq ( integrations . type , "web3" ) ) )
57+ . limit ( 1 ) ;
58+
59+ if ( existingIntegration . length > 0 ) {
60+ return {
61+ error : "Web3 integration already exists for this user" ,
62+ status : 400 ,
63+ } ;
64+ }
65+
66+ return { valid : true } ;
67+ }
68+
69+ // Helper: Create wallet via Para SDK
70+ async function createParaWallet ( email : string ) {
71+ if ( ! PARA_API_KEY ) {
72+ console . error ( "[Para] PARA_API_KEY not configured" ) ;
73+ throw new Error ( "Para API key not configured" ) ;
74+ }
75+
76+ const environment = PARA_ENV === "prod" ? Environment . PROD : Environment . BETA ;
77+ console . log (
78+ `[Para] Initializing SDK with environment: ${ PARA_ENV } (${ environment } )`
79+ ) ;
80+ console . log ( `[Para] API key: ${ PARA_API_KEY . slice ( 0 , 8 ) } ...` ) ;
81+
82+ const paraClient = new ParaServer ( environment , PARA_API_KEY ) ;
83+
84+ console . log ( `[Para] Creating wallet for email: ${ email } ` ) ;
85+
86+ const wallet = await paraClient . createPregenWallet ( {
87+ type : "EVM" ,
88+ pregenId : { email } ,
89+ } ) ;
90+
91+ const userShare = await paraClient . getUserShare ( ) ;
92+
93+ if ( ! userShare ) {
94+ throw new Error ( "Failed to get user share from Para" ) ;
95+ }
96+
97+ if ( ! ( wallet . id && wallet . address ) ) {
98+ throw new Error ( "Invalid wallet data from Para" ) ;
99+ }
100+
101+ return { wallet, userShare } ;
102+ }
103+
104+ // Helper: Get user-friendly error response for wallet creation failures
105+ function getErrorResponse ( error : unknown ) {
106+ console . error ( "[Para] Wallet creation failed:" , error ) ;
107+
108+ let errorMessage = "Failed to create wallet" ;
109+ let statusCode = 500 ;
110+
111+ if ( error instanceof Error ) {
112+ const message = error . message . toLowerCase ( ) ;
113+
114+ if ( message . includes ( "already exists" ) ) {
115+ errorMessage = "A wallet already exists for this email address" ;
116+ statusCode = 409 ;
117+ } else if ( message . includes ( "invalid email" ) ) {
118+ errorMessage = "Invalid email format" ;
119+ statusCode = 400 ;
120+ } else if ( message . includes ( "forbidden" ) || message . includes ( "403" ) ) {
121+ errorMessage = "API key authentication failed. Please contact support." ;
122+ statusCode = 403 ;
123+ } else {
124+ errorMessage = error . message ;
125+ }
126+ }
127+
128+ return NextResponse . json ( { error : errorMessage } , { status : statusCode } ) ;
129+ }
130+
131+ // Helper: Store wallet in database and create integration
132+ async function storeWalletAndIntegration ( options : {
133+ userId : string ;
134+ email : string ;
135+ walletId : string ;
136+ walletAddress : string ;
137+ userShare : string ;
138+ } ) {
139+ const { userId, email, walletId, walletAddress, userShare } = options ;
140+
141+ // Store wallet in para_wallets table
142+ await db . insert ( paraWallets ) . values ( {
143+ userId,
144+ email,
145+ walletId,
146+ walletAddress,
147+ userShare : encryptUserShare ( userShare ) ,
148+ } ) ;
149+
150+ console . log ( `[Para] ✓ Wallet created: ${ walletAddress } ` ) ;
151+
152+ // Create Web3 integration record with truncated address as name
153+ const truncatedAddress = `${ walletAddress . slice ( 0 , 6 ) } ...${ walletAddress . slice ( - 4 ) } ` ;
154+
155+ await createIntegration ( userId , truncatedAddress , "web3" , { } ) ;
156+
157+ console . log ( `[Para] ✓ Web3 integration created: ${ truncatedAddress } ` ) ;
158+
159+ return { walletAddress, walletId, truncatedAddress } ;
160+ }
161+
14162export async function GET ( request : Request ) {
15163 try {
16164 const session = await auth . api . getSession ( {
@@ -52,123 +200,38 @@ export async function GET(request: Request) {
52200
53201export async function POST ( request : Request ) {
54202 try {
55- // 1. Authenticate user
56- const session = await auth . api . getSession ( {
57- headers : request . headers ,
58- } ) ;
59-
60- if ( ! session ?. user ) {
61- return NextResponse . json ( { error : "Unauthorized" } , { status : 401 } ) ;
62- }
63-
64- const user = session . user ;
65-
66- // 2. Check user has valid email (not anonymous)
67- if ( ! user . email ) {
203+ // 1. Validate user
204+ const userValidation = await validateUser ( request ) ;
205+ if ( "error" in userValidation ) {
68206 return NextResponse . json (
69- { error : "Email required to create wallet" } ,
70- { status : 400 }
207+ { error : userValidation . error } ,
208+ { status : userValidation . status }
71209 ) ;
72210 }
211+ const { user } = userValidation ;
73212
74- // Check if user is anonymous (has email like temp-xxx@http://localhost:3000)
75- if ( user . email . includes ( "@http://" ) || user . email . includes ( "@https://" ) || user . email . startsWith ( "temp-" ) ) {
213+ // 2. Check if wallet/integration already exists
214+ const existingCheck = await checkExistingWallet ( user . id ) ;
215+ if ( "error" in existingCheck ) {
76216 return NextResponse . json (
77- { error : "Anonymous users cannot create wallets. Please sign in with a real account." } ,
78- { status : 400 }
217+ { error : existingCheck . error } ,
218+ { status : existingCheck . status }
79219 ) ;
80220 }
81221
82- // 3. Check if wallet already exists
83- const hasWallet = await userHasWallet ( user . id ) ;
84- if ( hasWallet ) {
85- return NextResponse . json (
86- { error : "Wallet already exists for this user" } ,
87- { status : 400 }
88- ) ;
89- }
90-
91- // 4. Check if Web3 integration already exists (additional safety check)
92- const existingIntegration = await db
93- . select ( )
94- . from ( integrations )
95- . where (
96- and (
97- eq ( integrations . userId , user . id ) ,
98- eq ( integrations . type , "web3" )
99- )
100- )
101- . limit ( 1 ) ;
102-
103- if ( existingIntegration . length > 0 ) {
104- return NextResponse . json (
105- { error : "Web3 integration already exists for this user" } ,
106- { status : 400 }
107- ) ;
108- }
109-
110- // 5. Validate Para API key
111- if ( ! PARA_API_KEY ) {
112- console . error ( "[Para] PARA_API_KEY not configured" ) ;
113- return NextResponse . json (
114- { error : "Para API key not configured" } ,
115- { status : 500 }
116- ) ;
117- }
222+ // 3. Create wallet via Para SDK
223+ const { wallet, userShare } = await createParaWallet ( user . email ) ;
118224
119- // 6. Initialize Para SDK
120- const environment = PARA_ENV === "prod" ? Environment . PROD : Environment . BETA ;
121- console . log ( `[Para] Initializing SDK with environment: ${ PARA_ENV } (${ environment } )` ) ;
122- console . log ( `[Para] API key: ${ PARA_API_KEY . slice ( 0 , 8 ) } ...` ) ;
123-
124- const paraClient = new ParaServer ( environment , PARA_API_KEY ) ;
125-
126- // 7. Skip wallet existence check - might be causing 403
127- // Note: createPregenWallet should be idempotent anyway
128-
129- // 8. Create wallet via Para SDK
130- console . log ( `[Para] Creating wallet for user ${ user . id } (${ user . email } )` ) ;
131-
132- const wallet = await paraClient . createPregenWallet ( {
133- type : "EVM" ,
134- pregenId : { email : user . email } ,
135- } ) ;
136-
137- // 9. Get user share (cryptographic key for signing)
138- const userShare = await paraClient . getUserShare ( ) ;
139-
140- if ( ! userShare ) {
141- throw new Error ( "Failed to get user share from Para" ) ;
142- }
143-
144- if ( ! ( wallet . id && wallet . address ) ) {
145- throw new Error ( "Invalid wallet data from Para" ) ;
146- }
147-
148- // 10. Store wallet in para_wallets table
149- await db . insert ( paraWallets ) . values ( {
225+ // 4. Store wallet and create integration
226+ await storeWalletAndIntegration ( {
150227 userId : user . id ,
151228 email : user . email ,
152229 walletId : wallet . id ,
153230 walletAddress : wallet . address ,
154- userShare : encryptUserShare ( userShare ) , // Encrypted!
231+ userShare,
155232 } ) ;
156233
157- console . log ( `[Para] ✓ Wallet created: ${ wallet . address } ` ) ;
158-
159- // 11. Create Web3 integration record with truncated address as name
160- const truncatedAddress = `${ wallet . address . slice ( 0 , 6 ) } ...${ wallet . address . slice ( - 4 ) } ` ;
161-
162- await createIntegration (
163- user . id ,
164- truncatedAddress ,
165- "web3" ,
166- { } // Empty config for web3
167- ) ;
168-
169- console . log ( `[Para] ✓ Web3 integration created: ${ truncatedAddress } ` ) ;
170-
171- // 12. Return success
234+ // 5. Return success
172235 return NextResponse . json ( {
173236 success : true ,
174237 wallet : {
@@ -178,37 +241,7 @@ export async function POST(request: Request) {
178241 } ,
179242 } ) ;
180243 } catch ( error ) {
181- console . error ( "[Para] Wallet creation failed:" , error ) ;
182-
183- // Extract user-friendly error message
184- let errorMessage = "Failed to create wallet" ;
185- let statusCode = 500 ;
186-
187- if ( error instanceof Error ) {
188- const message = error . message . toLowerCase ( ) ;
189-
190- // Check for specific Para API errors
191- if ( message . includes ( "already exists" ) ) {
192- errorMessage = "A wallet already exists for this email address" ;
193- statusCode = 409 ;
194- } else if ( message . includes ( "invalid email" ) ) {
195- errorMessage = "Invalid email format" ;
196- statusCode = 400 ;
197- } else if ( message . includes ( "forbidden" ) || message . includes ( "403" ) ) {
198- errorMessage = "API key authentication failed. Please contact support." ;
199- statusCode = 403 ;
200- } else {
201- // Include the actual error message for other errors
202- errorMessage = error . message ;
203- }
204- }
205-
206- return NextResponse . json (
207- {
208- error : errorMessage ,
209- } ,
210- { status : statusCode }
211- ) ;
244+ return getErrorResponse ( error ) ;
212245 }
213246}
214247
@@ -246,10 +279,7 @@ export async function DELETE(request: Request) {
246279 await db
247280 . delete ( integrations )
248281 . where (
249- and (
250- eq ( integrations . userId , user . id ) ,
251- eq ( integrations . type , "web3" )
252- )
282+ and ( eq ( integrations . userId , user . id ) , eq ( integrations . type , "web3" ) )
253283 ) ;
254284
255285 console . log ( `[Para] Web3 integration deleted for user ${ user . id } ` ) ;
0 commit comments