initial commit
This commit is contained in:
285
app/api/services/hire-developer/route.ts
Normal file
285
app/api/services/hire-developer/route.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user