Fix VIES check treating "member state unavailable" as "invalid"
VIES answers HTTP 200 even when it couldn't actually perform the check
(actionSucceed: false, e.g. MS_UNAVAILABLE — Germany's own national
gateway does this fairly regularly). checkVatIdViaVies() only ever read
data.valid, which is absent on that response shape, so it silently
read as valid: false — a real, currently-registered German VAT ID
(reported: DE351362947) looked rejected. Worse, /api/checkout/validate-
vat then wrapped even a correctly-returned ok:false as { ok: true,
valid: false }, which the client reads as "invalid" rather than
"unavailable" — the actual bug the user hit, compounding the vies.ts
gap. Both are fixed now: an unconfirmable check surfaces to the client
as ok:false, which CheckoutContent.tsx's handleVatIdBlur already
correctly renders as "USt-IdNr.-Prüfung derzeit nicht möglich"
instead of a rejection. Same fix applied to the payload backend's own
copy of vies.ts (company-settings' VAT check).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,16 @@ export async function POST(request: Request) {
|
||||
|
||||
const result = await checkVatIdViaVies(normalized);
|
||||
if (!result.ok) {
|
||||
return NextResponse.json({ ok: true, valid: false, reason: `USt-IdNr.-Prüfung derzeit nicht möglich (${result.reason}).` });
|
||||
// `ok: false` here means "VIES couldn't confirm this one way or the
|
||||
// other" (unreachable, or the member state's own gateway is briefly
|
||||
// down — `MS_UNAVAILABLE`, which VIES itself answers 200 for, not an
|
||||
// error status) — NOT "confirmed invalid". Previously this branch
|
||||
// still answered `{ ok: true, valid: false }`, which the client reads
|
||||
// as a rejected VAT ID (`vatIdViesStatus = "invalid"`) instead of
|
||||
// "couldn't check right now" (`"unavailable"`) — a real, currently
|
||||
// registered VAT ID looked wrong to the customer whenever VIES (or
|
||||
// just Germany's own national gateway) had a hiccup.
|
||||
return NextResponse.json({ ok: false, reason: result.reason });
|
||||
}
|
||||
return NextResponse.json({ ok: true, valid: result.valid, name: result.name });
|
||||
}
|
||||
|
||||
+13
-1
@@ -33,7 +33,19 @@ export async function checkVatIdViaVies(vatId: string): Promise<ViesCheckResult>
|
||||
signal: AbortSignal.timeout(8000),
|
||||
});
|
||||
if (!res.ok) return { ok: false, reason: `VIES antwortete mit ${res.status}` };
|
||||
const data: { valid?: boolean; name?: string; address?: string } = await res.json();
|
||||
const data: { actionSucceed?: boolean; valid?: boolean; name?: string; address?: string; errorWrappers?: { error?: string }[] } = await res.json();
|
||||
// VIES answers 200 even when it couldn't actually perform the check —
|
||||
// `actionSucceed: false` (e.g. `MS_UNAVAILABLE`, the member state's own
|
||||
// national gateway being temporarily down — Germany's in particular is
|
||||
// known to do this) means "couldn't confirm", not "confirmed invalid".
|
||||
// Without this check a `MS_UNAVAILABLE` response fell through to
|
||||
// `Boolean(data.valid)` on a body that has no `valid` field at all,
|
||||
// silently reading as `valid: false` — a real, currently-registered VAT
|
||||
// ID would then look rejected instead of "VIES unavailable, try again".
|
||||
if (data.actionSucceed === false) {
|
||||
const reason = data.errorWrappers?.[0]?.error ?? "VIES konnte die Anfrage nicht bearbeiten.";
|
||||
return { ok: false, reason: `VIES: ${reason}` };
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
valid: Boolean(data.valid),
|
||||
|
||||
Reference in New Issue
Block a user