Imports System.Net.Http Imports System.Text.Json Imports System.Text.Json.Serialization Imports System.Text.RegularExpressions Imports System.Threading.Tasks ' ============================================================ ' CLASES DE MODELO PARA EL JSON DEVUELTO POR VIES (REST) ' ============================================================ Public Class ViesApproximate Public Property MatchName As Integer Public Property MatchStreet As Integer Public Property MatchPostalCode As Integer Public Property MatchCity As Integer Public Property MatchCompanyType As Integer End Class Public Class ViesRestResponse Public Property IsValid As Boolean Public Property RequestDate As Date Public Property UserError As String Public Property Name As String Public Property Address As String Public Property RequestIdentifier As String Public Property OriginalVatNumber As String Public Property VatNumber As String Public Property ViesApproximate As ViesApproximate ' Campo adicional para errores locales Public Property LocalError As String End Class ' ============================================================ ' VALIDADOR COMPLETO VAT + VIES REST ' ============================================================ Public Class ValidacionDocumentosIdentidadVIES ' ----------------------------- ' 1) Validación de formato VAT ' ----------------------------- Private Shared ReadOnly Patterns As New Dictionary(Of String, String) From { {"AT", "^[A-Z]U[0-9]{8}$"}, {"BE", "^[0-9]{10}$"}, {"BG", "^[0-9]{9,10}$"}, {"CY", "^[0-9]{8}[A-Z]$"}, {"CZ", "^[0-9]{8,10}$"}, {"DE", "^[0-9]{9}$"}, {"DK", "^[0-9]{8}$"}, {"EE", "^[0-9]{9}$"}, {"EL", "^[0-9]{9}$"}, {"ES", "^[A-Z0-9][0-9]{7}[A-Z0-9]$"}, {"FI", "^[0-9]{8}$"}, {"FR", "^[A-Z0-9]{2}[0-9]{9}$"}, {"HR", "^[0-9]{11}$"}, {"HU", "^[0-9]{8}$"}, {"IE", "^[0-9]{7}[A-W][A-I]?$"}, {"IT", "^[0-9]{11}$"}, {"LT", "^[0-9]{9}|[0-9]{12}$"}, {"LU", "^[0-9]{8}$"}, {"LV", "^[0-9]{11}$"}, {"MT", "^[0-9]{8}$"}, {"NL", "^[0-9]{9}B[0-9]{2}$"}, {"PL", "^[0-9]{10}$"}, {"PT", "^[0-9]{9}$"}, {"RO", "^[0-9]{2,10}$"}, {"SE", "^[0-9]{12}$"}, {"SI", "^[0-9]{8}$"}, {"SK", "^[0-9]{10}$"}, {"XI", "^[0-9]{9}$"} } Public Shared Function IsValidVatFormat(countryCode As String, vatNumber As String) As Boolean countryCode = countryCode.ToUpper().Trim() vatNumber = vatNumber.ToUpper().Trim() If Not Patterns.ContainsKey(countryCode) Then Return False End If Return Regex.IsMatch(vatNumber, Patterns(countryCode)) End Function ' ----------------------------- ' 2) Cliente HTTP compartido ' ----------------------------- Private Shared ReadOnly _http As New HttpClient() With { .Timeout = TimeSpan.FromSeconds(10) } ' ----------------------------- ' 3) Validación VIES vía REST (ASÍNCRONA) ' ----------------------------- Public Shared Async Function ValidateVatAsync(countryCode As String, vatNumber As String) As Task(Of ViesRestResponse) Dim result As New ViesRestResponse() Try ' Normalización countryCode = countryCode.Trim().ToUpper() vatNumber = vatNumber.Trim().ToUpper() ' Validación de formato previa If Not IsValidVatFormat(countryCode, vatNumber) Then result.LocalError = $"Formato VAT inválido para {countryCode}" Return result End If Dim url = $"https://ec.europa.eu/taxation_customs/vies/rest-api/ms/{countryCode}/vat/{vatNumber}" Dim json As String = Await _http.GetStringAsync(url).ConfigureAwait(False) result = JsonSerializer.Deserialize(Of ViesRestResponse)(json) Catch ex As HttpRequestException result.LocalError = "Error HTTP: " & ex.Message Catch ex As TaskCanceledException result.LocalError = "Timeout al consultar VIES" Catch ex As Exception result.LocalError = "Error inesperado: " & ex.Message End Try Return result End Function ' ----------------------------- ' 4) Validación VIES vía REST (SINCRONA SEGURA PARA WPF) ' ----------------------------- Public Shared Function ValidateVat(countryCode As String, vatNumber As String) As ViesRestResponse Return Task.Run( Async Function() Return Await ValidateVatAsync(countryCode, vatNumber).ConfigureAwait(False) End Function ).Result End Function End Class