Add src/app/api/payment/process/route.ts

This commit is contained in:
2026-03-14 16:55:23 +00:00
parent c5c06f9f26
commit e8eafad650

View File

@@ -0,0 +1,204 @@
import { NextRequest, NextResponse } from 'next/server';
interface PaymentRequest {
paymentMethod: string;
cardName: string;
cardNumber: string;
expiryDate: string;
cvv: string;
email: string;
phone: string;
amount: number;
currency: string;
items: Array<{
id: string;
name: string;
price: string;
quantity: number;
}>;
orderDetails: {
subtotal: string;
tax: string;
total: string;
};
}
interface PaymentResponse {
success: boolean;
transactionId?: string;
message: string;
order?: {
id: string;
amount: number;
currency: string;
status: string;
};
}
function validatePaymentData(data: PaymentRequest): { valid: boolean; error?: string } {
if (!data.cardName?.trim()) {
return { valid: false, error: 'Cardholder name is required' };
}
const cardNumberDigits = data.cardNumber.replace(/\D/g, '');
if (!/^\d{13,19}$/.test(cardNumberDigits)) {
return { valid: false, error: 'Invalid card number' };
}
if (!/^\d{2}\/\d{2}$/.test(data.expiryDate)) {
return { valid: false, error: 'Invalid expiry date format' };
}
if (!/^\d{3,4}$/.test(data.cvv)) {
return { valid: false, error: 'Invalid CVV' };
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
return { valid: false, error: 'Invalid email address' };
}
if (!data.phone?.trim()) {
return { valid: false, error: 'Phone number is required' };
}
if (data.amount <= 0) {
return { valid: false, error: 'Invalid payment amount' };
}
return { valid: true };
}
function luhnCheck(cardNumber: string): boolean {
const digits = cardNumber.replace(/\D/g, '');
let sum = 0;
let isEven = false;
for (let i = digits.length - 1; i >= 0; i--) {
let digit = parseInt(digits[i], 10);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
function generateTransactionId(): string {
return `TXN-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
}
export async function POST(request: NextRequest): Promise<NextResponse<PaymentResponse>> {
try {
const data: PaymentRequest = await request.json();
// Validate payment data
const validation = validatePaymentData(data);
if (!validation.valid) {
return NextResponse.json(
{
success: false,
message: validation.error || 'Validation failed'
},
{ status: 400 }
);
}
// Validate card using Luhn algorithm
if (!luhnCheck(data.cardNumber)) {
return NextResponse.json(
{
success: false,
message: 'Invalid card number'
},
{ status: 400 }
);
}
// Validate expiry date
const [month, year] = data.expiryDate.split('/');
const expiryDate = new Date(2000 + parseInt(year), parseInt(month) - 1);
if (expiryDate < new Date()) {
return NextResponse.json(
{
success: false,
message: 'Card has expired'
},
{ status: 400 }
);
}
// Validate items and amount
if (!data.items || data.items.length === 0) {
return NextResponse.json(
{
success: false,
message: 'No items in order'
},
{ status: 400 }
);
}
// In production, integrate with actual payment gateway here
// This is a mock processing example
const transactionId = generateTransactionId();
// Simulate payment processing delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Mock fraud check - reject cards ending in 0002
const lastFourDigits = data.cardNumber.replace(/\D/g, '').slice(-4);
if (lastFourDigits === '0002') {
return NextResponse.json(
{
success: false,
message: 'Payment declined. Please try another card.'
},
{ status: 402 }
);
}
// Log order for email notification
console.log('Payment Processing Log:', {
transactionId,
amount: data.amount,
currency: data.currency,
cardLast4: lastFourDigits,
email: data.email,
phone: data.phone,
itemCount: data.items.length,
timestamp: new Date().toISOString()
});
return NextResponse.json(
{
success: true,
transactionId,
message: 'Payment processed successfully',
order: {
id: transactionId,
amount: data.amount,
currency: data.currency,
status: 'completed'
}
},
{ status: 200 }
);
} catch (error) {
console.error('Payment processing error:', error);
return NextResponse.json(
{
success: false,
message: 'Payment processing failed. Please try again.'
},
{ status: 500 }
);
}
}