ai-wpa/app/api/services/hire-developer/route.ts

286 lines
8.3 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { authMiddleware } from '@/lib/auth-middleware'
import connectDB from '@/lib/mongodb'
import { User as UserModel } from '@/models/user'
import { DeveloperRequest, IDeveloperRequest } from '@/models/developer-request'
import { Transaction } from '@/models/transaction'
import BillingService from '@/lib/billing-service'
import { checkServiceAvailability } from '@/lib/system-settings'
// Schema for developer hire request
const HireDeveloperSchema = z.object({
planId: z.enum(['hourly', 'daily', 'monthly']),
planName: z.string().min(1),
planPrice: z.number().positive(),
requirements: z.string().min(10, 'Requirements must be at least 10 characters').max(5000),
contactInfo: z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Valid email is required'),
phone: z.string().optional(),
}),
})
// Schema for response
const HireDeveloperResponseSchema = z.object({
success: z.boolean(),
data: z
.object({
requestId: z.string(),
transactionId: z.string(),
status: z.string(),
message: z.string(),
estimatedResponse: z.string(),
})
.optional(),
error: z
.object({
message: z.string(),
code: z.string(),
})
.optional(),
})
export async function POST(request: NextRequest) {
try {
// Check if developer hire service is enabled
if (!(await checkServiceAvailability('developer'))) {
return NextResponse.json(
{
success: false,
error: {
message: 'Developer hire service is currently disabled by administrator',
code: 'SERVICE_DISABLED',
},
},
{ status: 503 }
)
}
// Authenticate user
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required', code: 'UNAUTHORIZED' },
},
{ status: 401 }
)
}
await connectDB()
// Parse and validate request body
const body = await request.json()
const validatedData = HireDeveloperSchema.parse(body)
// Get user's current balance from database
const userData = await UserModel.findOne({ email: user.email })
if (!userData) {
return NextResponse.json(
{
success: false,
error: { message: 'User not found', code: 'USER_NOT_FOUND' },
},
{ status: 404 }
)
}
const currentBalance = userData.balance || 0
// Check if user has sufficient balance (minimum deposit required)
const minimumDeposit = Math.min(validatedData.planPrice * 0.5, 10000) // 50% or max ₹10,000
if (currentBalance < minimumDeposit) {
return NextResponse.json(
{
success: false,
error: {
message: `Insufficient balance. Minimum deposit required: ₹${minimumDeposit}, Available: ₹${currentBalance}`,
code: 'INSUFFICIENT_BALANCE',
},
},
{ status: 400 }
)
}
// Create developer request first
const developerRequest = new DeveloperRequest({
userId: userData._id,
planId: validatedData.planId,
planName: validatedData.planName,
planPrice: validatedData.planPrice,
requirements: validatedData.requirements,
contactInfo: validatedData.contactInfo,
status: 'pending',
paymentStatus: 'pending', // Will be updated after billing
transactionId: null, // Will be set after transaction is created
})
// Save developer request first
const savedRequest = await developerRequest.save()
// Process billing using the new comprehensive billing service
try {
const billingResult = await BillingService.processServiceDeployment({
user: {
id: user.id,
email: user.email,
siliconId: userData.siliconId,
},
service: {
name: `Developer Hire - ${validatedData.planName}`,
type: 'developer_hire',
id: savedRequest._id.toString(),
config: {
planId: validatedData.planId,
planName: validatedData.planName,
planPrice: validatedData.planPrice,
requirements: validatedData.requirements,
contactInfo: validatedData.contactInfo,
},
},
amount: minimumDeposit,
currency: 'INR',
cycle: 'onetime',
deploymentSuccess: true, // Developer hire request is always successful
deploymentResponse: { requestId: savedRequest._id.toString() },
metadata: {
userAgent: request.headers.get('user-agent'),
ip: request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip'),
depositAmount: minimumDeposit,
fullPlanPrice: validatedData.planPrice,
},
})
// Update developer request with billing info
await (DeveloperRequest as any).updateOne(
{ _id: savedRequest._id },
{
$set: {
paymentStatus: 'paid',
transactionId: billingResult.transaction?._id,
},
}
)
console.log('Developer hire billing processed:', {
requestId: savedRequest._id,
billingId: billingResult.billing.billing_id,
transactionId: billingResult.transaction?.transactionId,
})
} catch (billingError) {
console.error('Billing processing failed:', billingError)
// Rollback developer request if billing fails
await (DeveloperRequest as any).deleteOne({ _id: savedRequest._id })
if (billingError instanceof Error && billingError.message.includes('Insufficient balance')) {
return NextResponse.json(
{
success: false,
error: {
message: billingError.message,
code: 'INSUFFICIENT_BALANCE',
},
},
{ status: 400 }
)
}
throw new Error('Failed to process payment')
}
const responseData = {
success: true,
data: {
requestId: savedRequest._id.toString(),
transactionId: 'processed_via_billing',
status: 'pending',
message:
'Your developer hire request has been submitted successfully! Our team will review your requirements and contact you within 24 hours.',
estimatedResponse: '24 hours',
},
}
const validatedResponse = HireDeveloperResponseSchema.parse(responseData)
return NextResponse.json(validatedResponse, { status: 200 })
} catch (error) {
console.error('Developer hire error:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: {
message: 'Invalid request data',
code: 'VALIDATION_ERROR',
details: error.issues,
},
},
{ status: 400 }
)
}
return NextResponse.json(
{
success: false,
error: { message: 'Failed to process developer hire request', code: 'INTERNAL_ERROR' },
},
{ status: 500 }
)
}
}
// GET endpoint to fetch user's developer requests
export async function GET(request: NextRequest) {
try {
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required', code: 'UNAUTHORIZED' },
},
{ status: 401 }
)
}
await connectDB()
const userData = await UserModel.findOne({ email: user.email })
if (!userData) {
return NextResponse.json(
{
success: false,
error: { message: 'User not found', code: 'USER_NOT_FOUND' },
},
{ status: 404 }
)
}
// Get user's developer requests
const requests = await (DeveloperRequest as any)
.find({ userId: userData._id })
.sort({ createdAt: -1 })
.populate('transactionId')
.lean()
return NextResponse.json({
success: true,
data: {
requests,
total: requests.length,
},
})
} catch (error) {
console.error('Failed to fetch developer requests:', error)
return NextResponse.json(
{
success: false,
error: { message: 'Failed to fetch requests', code: 'INTERNAL_ERROR' },
},
{ status: 500 }
)
}
}