Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
162 lines
4.2 KiB
TypeScript
162 lines
4.2 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { auth } from '@clerk/nextjs/server'
|
|
|
|
// Order types
|
|
interface OrderItem {
|
|
productId: string
|
|
productName: string
|
|
price: number
|
|
quantity: number
|
|
priceId: string
|
|
}
|
|
|
|
interface Order {
|
|
id: string
|
|
customerId: string | null
|
|
customerEmail: string
|
|
items: OrderItem[]
|
|
totalAmount: number
|
|
currency: string
|
|
status: 'pending' | 'paid' | 'fulfilled' | 'cancelled' | 'refunded'
|
|
paymentIntentId: string | null
|
|
stripeSessionId: string | null
|
|
createdAt: string
|
|
updatedAt: string
|
|
shippingAddress?: {
|
|
name: string
|
|
address: string
|
|
city: string
|
|
state: string
|
|
zipCode: string
|
|
country: string
|
|
}
|
|
}
|
|
|
|
// In-memory storage for demo purposes
|
|
// In a real application, you would use a database
|
|
let orders: Order[] = []
|
|
|
|
// Generate a simple ID for demo
|
|
function generateOrderId(): string {
|
|
return 'ORD-' + Date.now() + '-' + Math.floor(Math.random() * 1000)
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
// Check authentication - only authenticated users can view orders
|
|
const { userId } = await auth()
|
|
if (!userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
try {
|
|
const { searchParams } = new URL(request.url)
|
|
const page = parseInt(searchParams.get('page') || '1')
|
|
const limit = parseInt(searchParams.get('limit') || '10')
|
|
const status = searchParams.get('status') || undefined
|
|
const customerEmail = searchParams.get('customerEmail') || undefined
|
|
|
|
// Filter orders
|
|
let filteredOrders = [...orders]
|
|
|
|
if (status) {
|
|
filteredOrders = filteredOrders.filter(order => order.status === status)
|
|
}
|
|
|
|
if (customerEmail) {
|
|
filteredOrders = filteredOrders.filter(order =>
|
|
order.customerEmail.toLowerCase().includes(customerEmail.toLowerCase())
|
|
)
|
|
}
|
|
|
|
// Sort by creation date (newest first)
|
|
filteredOrders.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
|
|
// Pagination
|
|
const startIndex = (page - 1) * limit
|
|
const endIndex = startIndex + limit
|
|
const paginatedOrders = filteredOrders.slice(startIndex, endIndex)
|
|
|
|
return NextResponse.json({
|
|
orders: paginatedOrders,
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
total: filteredOrders.length,
|
|
totalPages: Math.ceil(filteredOrders.length / limit)
|
|
}
|
|
})
|
|
} catch (error) {
|
|
console.error('Error fetching orders:', error)
|
|
return NextResponse.json(
|
|
{ error: 'Failed to fetch orders' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json()
|
|
const { items, customerEmail, paymentIntentId, stripeSessionId, shippingAddress } = body
|
|
|
|
// Validate required fields
|
|
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
return NextResponse.json(
|
|
{ error: 'Items are required' },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (!customerEmail) {
|
|
return NextResponse.json(
|
|
{ error: 'Customer email is required' },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (!paymentIntentId) {
|
|
return NextResponse.json(
|
|
{ error: 'Payment intent ID is required' },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (!stripeSessionId) {
|
|
return NextResponse.json(
|
|
{ error: 'Stripe session ID is required' },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
// Calculate total
|
|
const totalAmount = items.reduce((total: number, item: OrderItem) => {
|
|
return total + (item.price * item.quantity)
|
|
}, 0)
|
|
|
|
// Create order
|
|
const newOrder: Order = {
|
|
id: generateOrderId(),
|
|
customerId: null, // TODO: Implement customer accounts
|
|
customerEmail,
|
|
items,
|
|
totalAmount,
|
|
currency: 'usd',
|
|
status: 'paid', // Assume payment was successful since webhook was triggered
|
|
paymentIntentId,
|
|
stripeSessionId,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
shippingAddress
|
|
}
|
|
|
|
orders.unshift(newOrder) // Add to beginning of array
|
|
|
|
return NextResponse.json(newOrder, { status: 201 })
|
|
} catch (error) {
|
|
console.error('Error creating order:', error)
|
|
return NextResponse.json(
|
|
{ error: 'Failed to create order' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|