129 lines
3.3 KiB
TypeScript
129 lines
3.3 KiB
TypeScript
import { createClient } from '@/lib/supabase/server'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
/**
|
|
* Validate EU VAT number using VIES (VAT Information Exchange System)
|
|
*
|
|
* The EU provides a SOAP-based API, but we'll use a REST wrapper
|
|
* In production, you might want to use the official SOAP API or a dedicated service
|
|
*/
|
|
export async function POST(request: Request) {
|
|
const supabase = await createClient()
|
|
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
|
|
if (!user) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
const { vat_number, customer_id } = await request.json()
|
|
|
|
if (!vat_number) {
|
|
return NextResponse.json({ error: 'VAT number is required' }, { status: 400 })
|
|
}
|
|
|
|
// Extract country code and number
|
|
const countryCode = vat_number.substring(0, 2).toUpperCase()
|
|
const vatNumber = vat_number.substring(2).replace(/\s/g, '')
|
|
|
|
try {
|
|
// Use the EU VIES validation API
|
|
// Note: In production, you should use the official SOAP API or a reliable service
|
|
const response = await fetch(
|
|
`https://ec.europa.eu/taxation_customs/vies/rest-api/ms/${countryCode}/vat/${vatNumber}`,
|
|
{
|
|
method: 'GET',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
},
|
|
}
|
|
)
|
|
|
|
if (!response.ok) {
|
|
// If VIES is unavailable, return a soft error
|
|
return NextResponse.json({
|
|
valid: false,
|
|
error: 'VAT validation service unavailable. Please try again later.',
|
|
})
|
|
}
|
|
|
|
const data = await response.json()
|
|
|
|
const isValid = data.isValid === true
|
|
|
|
// Update customer if customer_id provided
|
|
if (customer_id && isValid) {
|
|
await supabase
|
|
.from('customers')
|
|
.update({
|
|
vat_number: vat_number.toUpperCase(),
|
|
vat_number_validated: true,
|
|
vat_number_validated_at: new Date().toISOString(),
|
|
})
|
|
.eq('id', customer_id)
|
|
.eq('user_id', user.id)
|
|
}
|
|
|
|
return NextResponse.json({
|
|
valid: isValid,
|
|
name: data.name || null,
|
|
address: data.address || null,
|
|
country_code: countryCode,
|
|
vat_number: vat_number.toUpperCase(),
|
|
})
|
|
} catch (error) {
|
|
console.error('VAT validation error:', error)
|
|
|
|
// Fallback: basic format validation
|
|
const isValidFormat = validateVatNumberFormat(countryCode, vatNumber)
|
|
|
|
return NextResponse.json({
|
|
valid: false,
|
|
error: 'Could not verify VAT number. Service temporarily unavailable.',
|
|
format_valid: isValidFormat,
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Basic VAT number format validation by country
|
|
*/
|
|
function validateVatNumberFormat(countryCode: string, vatNumber: string): boolean {
|
|
const patterns: Record<string, RegExp> = {
|
|
AT: /^U\d{8}$/,
|
|
BE: /^0\d{9}$/,
|
|
BG: /^\d{9,10}$/,
|
|
CY: /^\d{8}[A-Z]$/,
|
|
CZ: /^\d{8,10}$/,
|
|
DE: /^\d{9}$/,
|
|
DK: /^\d{8}$/,
|
|
EE: /^\d{9}$/,
|
|
EL: /^\d{9}$/, // Greece
|
|
ES: /^[A-Z0-9]\d{7}[A-Z0-9]$/,
|
|
FI: /^\d{8}$/,
|
|
FR: /^[A-Z0-9]{2}\d{9}$/,
|
|
HR: /^\d{11}$/,
|
|
HU: /^\d{8}$/,
|
|
IE: /^[0-9A-Z]{8,9}$/,
|
|
IT: /^\d{11}$/,
|
|
LT: /^\d{9,12}$/,
|
|
LU: /^\d{8}$/,
|
|
LV: /^\d{11}$/,
|
|
MT: /^\d{8}$/,
|
|
NL: /^\d{9}B\d{2}$/,
|
|
PL: /^\d{10}$/,
|
|
PT: /^\d{9}$/,
|
|
RO: /^\d{2,10}$/,
|
|
SE: /^\d{12}$/,
|
|
SI: /^\d{8}$/,
|
|
SK: /^\d{10}$/,
|
|
}
|
|
|
|
const pattern = patterns[countryCode]
|
|
if (!pattern) {
|
|
return false
|
|
}
|
|
|
|
return pattern.test(vatNumber)
|
|
}
|