# frozen_string_literal: true require_relative 'base' require 'date' require 'net/http' require 'erb' require 'rexml' class Valvat class Lookup class VIES < Base ENDPOINT_URI = URI('https://ec.europa.eu/taxation_customs/vies/services/checkVatService').freeze HEADERS = { 'Accept' => 'text/xml;charset=UTF-8', 'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => '' }.freeze BODY = <<-XML.gsub(/^\s+/, '') > <%= @vat.vat_country_code %> <%= @vat.to_s_wo_country %> <% if @requester %> <%= @vat.vat_country_code %> <%= @vat.to_s_wo_country %> <% end %> > XML BODY_TEMPLATE = ERB.new(BODY).freeze private def endpoint_uri ENDPOINT_URI end def build_request(uri) request = Net::HTTP::Post.new(uri.request_uri, HEADERS) request.body = BODY_TEMPLATE.result(binding) request end def parse(body) doc = REXML::Document.new(body) elements = doc.get_elements('/env:Envelope/env:Body').first.first convert_values(elements.each_with_object({}) do |el, hash| hash[convert_key(el.name)] = convert_value(el.text) end) end def convert_key(key) key.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .tr('-', '_') .sub(/\Atrader_/, '') .downcase.to_sym end def convert_value(value) value == '---' ? nil : value end def convert_values(hash) return build_fault(hash) if hash[:faultstring] hash[:valid] = hash[:valid] == 'true' || (hash[:valid] == 'false' ? false : nil) if hash.key?(:valid) hash[:request_date] = Date.parse(hash[:request_date]) if hash.key?(:request_date) hash end FAULTS = { 'SERVICE_UNAVAILABLE' => ServiceUnavailable, 'MS_UNAVAILABLE' => MemberStateUnavailable, 'INVALID_REQUESTER_INFO' => InvalidRequester, 'TIMEOUT' => Timeout, 'VAT_BLOCKED' => BlockedError, 'IP_BLOCKED' => BlockedError, 'GLOBAL_MAX_CONCURRENT_REQ' => RateLimitError, 'GLOBAL_MAX_CONCURRENT_REQ_TIME' => RateLimitError, 'MS_MAX_CONCURRENT_REQ' => RateLimitError, 'MS_MAX_CONCURRENT_REQ_TIME' => RateLimitError }.freeze def build_fault(hash) fault = hash[:faultstring] return hash.merge({ valid: false }) if fault == 'INVALID_INPUT' hash.merge({ error: (FAULTS[fault] || UnknownLookupError).new(fault, self.class) }) end end end end