Codebase list libbusiness-tax-vat-validation-perl / 2a4f482
New upstream version 1.20 Florian Schlichting 2 years ago
11 changed file(s) with 177 addition(s) and 57 deletion(s). Raw diff Collapse all Expand all
(No changes)
00 Revision history for Business::Tax::VAT::Validation
11
2 1.12 20/03/20
2 1.20 01/04/2021 (Promoting 1.13 trial release to stable)
3 - Changes as per 1.13 below - supporting UK/XI numbers via HMRC API
4 courtesy of Dave Lambley (davel), GoDaddy
5
6 1.13 24/03/2021 (TRIAL release)
7 - Implement checking of UK VAT numbers via HMRC (davel)
8 - Some refactoring (davel)
9 - Add information returned by VIES/HMRC via new information() (davel)
10
11 Many thanks to Dave Lambley (davel) for the changse in this release!
12
13 This is going out as a trial release first, then will be promoted to
14 a full release as 1.20 soon.
15
16 1.12 20/03/2020
317 - Use https:// base_url to avoid a redirect (fgehring)
418
519 1.11 13/03/2017
(No changes)
00 {
1 "abstract" : "Validate EU VAT numbers against VIES",
1 "abstract" : "Validate EU VAT numbers against VIES/HMRC",
22 "author" : [
33 "David Precious <davidp@preshweb.co.uk>"
44 ],
3232 "runtime" : {
3333 "requires" : {
3434 "HTTP::Request::Common" : "1",
35 "LWP::UserAgent" : "1"
35 "JSON" : "0",
36 "LWP::UserAgent" : "1",
37 "perl" : "v5.8.9"
3638 }
3739 }
3840 },
4648 "url" : "https://github.com/bigpresh/Business-Tax-VAT-Validation"
4749 }
4850 },
49 "version" : "1.12",
51 "version" : "1.20",
5052 "x_serialization_backend" : "JSON::PP version 2.27300_01"
5153 }
00 ---
1 abstract: 'Validate EU VAT numbers against VIES'
1 abstract: 'Validate EU VAT numbers against VIES/HMRC'
22 author:
33 - 'David Precious <davidp@preshweb.co.uk>'
44 build_requires:
1818 - inc
1919 requires:
2020 HTTP::Request::Common: '1'
21 JSON: '0'
2122 LWP::UserAgent: '1'
23 perl: v5.8.9
2224 resources:
2325 bugtracker: https://github.com/bigpresh/Business-Tax-VAT-Validation/issues
2426 homepage: https://github.com/bigpresh/Business-Tax-VAT-Validation/
2527 repository: https://github.com/bigpresh/Business-Tax-VAT-Validation
26 version: '1.12'
28 version: '1.20'
2729 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
77 'PREREQ_PM' => {
88 HTTP::Request::Common => 1.0,
99 LWP::UserAgent => 1.0,
10 JSON => 0,
11 perl => 5.8.9,
1012 },
1113 ABSTRACT_FROM => 'lib/Business/Tax/VAT/Validation.pm',
1214 AUTHOR => 'David Precious <davidp@preshweb.co.uk>',
189189
190190 LICENSE
191191
192 GPL. Enjoy! See COPYING for further information on the GPL.
192 GPL3. Enjoy! See COPYING for further information on the GPL.
193193
194194 DISCLAIMER
195195
88 # COPYRIGHT NOTICE #
99 # Copyright 2003 Bernard Nauwelaerts All Rights Reserved. #
1010 # #
11 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence #
11 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence version 3 #
1212 # See COPYING for details #
1313 # #
1414 # This software is provided as is, WITHOUT ANY WARRANTY, without even the #
88 # COPYRIGHT NOTICE #
99 # Copyright 2003 Bernard Nauwelaerts All Rights Reserved. #
1010 # #
11 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence #
11 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence version 3 #
1212 # See COPYING for details #
1313 # #
1414 # This software is provided as is, WITHOUT ANY WARRANTY, without even the #
88 # Original author: #
99 # IT Development software #
1010 # European VAT number validator Version 1.0.2 #
11 # Created 06/08/2003 Last Modified 30/11/2012 #
11 # Created 06/08/2003 #
1212 # #
1313 # Maintainership kindly handed over to David Precious (BIGPRESH) in 2015 #
1414 ############################################################################
1616 # Copyright 2003 Bernard Nauwelaerts All Rights Reserved. #
1717 # Copyright 2015 David Precious All Rights Reserved. #
1818 # #
19 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence #
19 # THIS SOFTWARE IS RELEASED UNDER THE GNU Public Licence version 3 #
2020 # Please see COPYING for details #
2121 # #
2222 # DISCLAIMER #
2626 # #
2727 ############################################################################
2828 use strict;
29
30 BEGIN {
31 $Business::Tax::VAT::Validation::VERSION = '1.12';
32 use HTTP::Request::Common qw(POST);
33 use LWP::UserAgent;
34 }
29 use warnings;
30
31 our $VERSION = '1.20';
32
33 use HTTP::Request::Common qw(POST);
34 use LWP::UserAgent;
35 use JSON qw/ decode_json /;
3536
3637 =head1 NAME
3738
38 Business::Tax::VAT::Validation - Validate EU VAT numbers against VIES
39 Business::Tax::VAT::Validation - Validate EU VAT numbers against VIES/HMRC
3940
4041 =head1 SYNOPSIS
4142
6061 state are performed first, to avoid unnecessarily sending queries to VIES for
6162 input that could never be valid.
6263
64 It also supports looking up VAT codes from the United Kingdom by using the
65 REST API provided by their HMRC.
6366
6467 =head1 CONSTRUCTOR
6568
8285 my ( $class, %arg ) = @_;
8386 my $self = {
8487 baseurl => $arg{baseurl} || 'https://ec.europa.eu/taxation_customs/vies/services/checkVatService',
88 hmrc_baseurl => $arg{hmrc_baseurl} || 'https://api.service.hmrc.gov.uk/organisations/vat/check-vat-number/lookup/',
8589 error => '',
8690 error_code => 0,
8791 response => '',
115119 SE => '[0-9]{12}',
116120 SI => '[0-9]{8}',
117121 SK => '[0-9]{10}',
122 XI => '([0-9]{3} ?[0-9]{4} ?[0-9]{2}|[0-9]{3} ?[0-9]{4} ?[0-9]{2} ?[0-9]{3}|GD[0-9]{3}|HA[0-9]{3})',
118123 },
119124 proxy => $arg{-proxy},
120 informations => {}
125 information => {}
121126 };
122127 $self = bless $self, $class;
123128 $self->{members} = join( '|', keys %{ $self->{re} } );
130135
131136 =over 4
132137
133 =item B<member_states> Returns all member states 2-digit codes as array
138 =item B<member_states> Returns all supported country codes.
139
140 These are ISO 3166-1 alpha-2 country codes with two exceptions. This module
141 supports VAT codes from all current European Union member states and The United
142 Kingdom of Great Britain and Northern Ireland.
143
144 =over 4
145
146 =item C<EL> Greece
147
148 Must be used in place of Greece's proper code.
149
150 =item C<XI> Northern Ireland
151
152 May be used rather than C<GB> for checking a Northern Irish company.
153
154 =back
134155
135156 @ms=$hvatn->member_states;
136157
178199 Valid MS values are :
179200
180201 AT, BE, BG, CY, CZ, DE, DK, EE, EL, ES,
181 FI, FR, GB, HU, IE, IT, LU, LT, LV, MT,
182 NL, PL, PT, RO, SE, SI, SK
202 FI, FR, GB, HR, HU, IE, IT, LU, LT, LV,
203 MT, NL, PL, PT, RO, SE, SI, SK, XI
183204
184205 =cut
185206
186207 sub check {
187208 my ($self, $vatNumber, $countryCode, @other) = @_; # @other is here for backward compatibility purposes
209 $self->{information} = {};
188210 return $self->_set_error('You must provide a VAT number') unless $vatNumber;
189211 $countryCode ||= '';
190212 ( $vatNumber, $countryCode ) = $self->_format_vatn( $vatNumber, $countryCode );
191213 if ($vatNumber) {
192 my $ua = LWP::UserAgent->new;
193 if ( ref $self->{proxy} eq 'ARRAY' ) {
194 $ua->proxy( @{ $self->{proxy} } );
195 } else {
196 $ua->env_proxy;
214 if ($countryCode eq 'GB') {
215 return $self->_check_hmrc($vatNumber, $countryCode);
197216 }
198 $ua->agent( 'Business::Tax::VAT::Validation/'. $Business::Tax::VAT::Validation::VERSION );
199
200 my $request = HTTP::Request->new(POST => $self->{baseurl});
201 $request->header(SOAPAction => 'http://www.w3.org/2003/05/soap-envelope');
202 $request->content(_in_soap_envelope($vatNumber, $countryCode));
203 $request->content_type("Content-Type: application/soap+xml; charset=utf-8");
204
205 my $response = $ua->request($request);
206
207 return $countryCode . '-' . $vatNumber if $self->_is_res_ok( $response->code, $response->decoded_content );
217 return $self->_check_vies($vatNumber, $countryCode);
208218 }
209219 0;
210220 }
219229
220230 sub local_check {
221231 my ( $self, $vatn, $mscc, @other ) = @_; # @other is here for backward compatibility purposes
232 $self->{information} = {};
222233 return $self->_set_error('You must provide a VAT number') unless $vatn;
223234 $mscc ||= '';
224235 ( $vatn, $mscc ) = $self->_format_vatn( $vatn, $mscc );
230241 }
231242 }
232243
233 =item B<informations> - Returns informations related to the last validated VAT number
234
235 %infos=$hvatn->informations();
236
237
238 =cut
239
240 sub informations {
244 =item B<information> - Returns information related to the last checked VAT number
245
246 # Get all available information as a hashref:
247 my $info = $hvatn->information();
248
249 # Get a particular key:
250 my $address = $hvatn->information('address');
251
252 Which information is offered depends on the checker used - for UK VAT numbers,
253 checked via the HMRC API, C<address> is the only key which will be set.
254
255 For EU VAT numbers checked via VIES, you can expect C<name> and C<address>.
256 This hashref will be reset every time you call check() or local_check()
257
258 =cut
259
260 sub information {
241261 my ( $self, $key, @other ) = @_;
242262 if ($key) {
243 return $self->{informations}{$key}
263 return $self->{information}{$key}
244264 } else {
245 return ($self->{informations})
265 return ($self->{information})
246266 }
247267 }
248268
319339 }
320340
321341 ### PRIVATE FUNCTIONS ==========================================================
342 sub _get_ua {
343 my ($self) = @_;
344 my $ua = LWP::UserAgent->new;
345 if ( ref $self->{proxy} eq 'ARRAY' ) {
346 $ua->proxy( @{ $self->{proxy} } );
347 } else {
348 $ua->env_proxy;
349 }
350 $ua->agent( 'Business::Tax::VAT::Validation/'. $Business::Tax::VAT::Validation::VERSION );
351 return $ua;
352 }
353
354 sub _check_vies {
355 my ($self, $vatNumber, $countryCode) = @_;
356 my $ua = $self->_get_ua();
357 my $request = HTTP::Request->new(POST => $self->{baseurl});
358 $request->header(SOAPAction => 'http://www.w3.org/2003/05/soap-envelope');
359 $request->content(_in_soap_envelope($vatNumber, $countryCode));
360 $request->content_type("Content-Type: application/soap+xml; charset=utf-8");
361
362 my $response = $ua->request($request);
363
364 return $countryCode . '-' . $vatNumber if $self->_is_res_ok( $response->code, $response->decoded_content );
365 }
366
367 sub _check_hmrc {
368 my ($self, $vatNumber, $countryCode) = @_;
369 my $ua = $self->_get_ua();
370
371 my $request = HTTP::Request->new(GET => $self->{hmrc_baseurl}.$vatNumber);
372 $request->header(Accept => 'application/vnd.hmrc.1.0+json');
373 my $response = $ua->request($request);
374
375 $self->{res} = $response->decoded_content;
376 if ($response->code == 200) {
377 my $data = decode_json($self->{res});
378 $self->{information}->{name} = $data->{target}->{name};
379 my $line = 1;
380 my $address = "";
381 while (defined $data->{target}->{address}->{"line$line"}) {
382 $address .= $data->{target}->{address}->{"line$line"}."\n";
383 $line++;
384 }
385 $address .= $data->{target}->{address}->{postcode};
386 $address .= "\n".$data->{target}->{address}->{countryCode};
387 $self->{information}->{address} = $address;
388 $self->_set_error( -1, 'Valid VAT Number');
389 }
390 elsif ($response->code == 404) {
391 return $self->_set_error( 2, 'Invalid VAT Number ('.$vatNumber.')');
392 }
393 elsif ($response->code == 400) {
394 return $self->_set_error( 3, 'VAT number badly formed ('.$vatNumber.')');
395 }
396 else {
397 return $self->_set_error( 500, 'Could not contact HMRC: '.$response->status_line);
398 }
399
400 return $countryCode . '-' . $vatNumber;
401 }
402
322403 sub _format_vatn {
323404 my ( $self, $vatn, $mscc ) = @_;
324405 my $null = '';
356437
357438 sub _is_res_ok {
358439 my ( $self, $code, $res ) = @_;
359 $self->{informations}={};
440 $self->{information}={};
360441 $res=~s/[\r\n]/ /g;
361442 $self->{response} = $res;
362443 if ($code == 200) {
364445 my $v = $1;
365446 if ($v eq 'true' || $v eq '1') {
366447 if ($res=~m/<name> *(.*?) *<\/name>/) {
367 $self->{informations}{name} = $1
448 $self->{information}{name} = $1
368449 }
369450 if ($res=~m/<address> *(.*?) *<\/address>/) {
370 $self->{informations}{address} = $1
451 $self->{information}{address} = $1
371452 }
372453 $self->_set_error( -1, 'Valid VAT Number');
373454 return 1;
411492
412493 LWP::UserAgent
413494
414 I<http://ec.europa.eu/taxation_customs/vies/faqvies.do> for the FAQs related to the VIES service.
415
495 L<http://ec.europa.eu/taxation_customs/vies/faqvies.do> for the FAQs related to the VIES service.
496
497 L<https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api/1.0>
498 for details of the service provided by the UK's HMRC.
416499
417500 =head1 FEEDBACK
418501
445528 Martin H. Sluka, noris network AG, Germany.
446529
447530 =item *
448 Simon Williams, UK2 Limited, United Kingdom & Benoît Galy, Greenacres, France & Raluca Boboia, Evozon, Romania
531 Simon Williams, UK2 Limited, United Kingdom
532
533 =item *
534 Benoît Galy, Greenacres, France
535
536 =item *
537 Raluca Boboia, Evozon, Romania
449538
450539 =item *
451540 Dave O., POBox, U.S.A.
465554 =item *
466555 Torsten Mueller, Archesoft, Germany
467556
557 =item *
558 Dave Lambley (davel), GoDaddy, United Kingdom
559
468560 =back
469561
470562 =head1 LICENSE
471563
472 GPL. Enjoy! See COPYING for further information on the GPL.
564 GPL3. Enjoy! See COPYING for further information on the GPL.
473565
474566
475567 =head1 DISCLAIMER
476568
477 See I<http://ec.europa.eu/taxation_customs/vies/viesdisc.do> to known the limitations of the EU validation service.
569 See L<http://ec.europa.eu/taxation_customs/vies/viesdisc.do> to known the limitations of the EU validation service.
478570
479571 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
480572 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0 #!/usr/bin/env perl -w
0 #!/usr/bin/env perl
11 use strict;
2 use warnings;
23 use Test;
34
45 my %distant_checks = (
1516 FI => ['12345678'],
1617 FR => ['12345678901', '12 456789012', 'A2 456789012', '1B 456789012', 'AB 456789012'],
1718 GB => ['123456789', '123456789012', '123 5678 01', '123 5678 12 234','GD345', 'HA123'],
19 XI => ['123456789', '123456789012', '123 5678 01', '123 5678 12 234','GD345', 'HA123'],
1820 HU => ['12345678'],
1921 IE => ['1234567A', '1B34567C', '1+34567C', '1*34567C'],
2022 IT => ['12345678901'],
5658 } elsif ($err > 257) {
5759 warn("skipping $ms$t : ".$hvat->get_last_error."\n");
5860 skip(1);
61 } elsif ($err == 3 && $ms eq 'GB' && ($t eq 'GD345' || $t eq 'HA123')) {
62 # These two codes are only successfully looked up in VIES, HMRC
63 # throws a 400 error indicating that the code is malformed.
64 # This does not seem correct, but is observed behaviour.
65 warn("skipping $ms$t : ".$hvat->get_last_error."\n");
66 skip(1);
5967 } else {
6068 warn("Distant check for $ms$t failed: ".$err.' '.$hvat->get_last_error."\n");
6169 ok(0);
6573 sleep(1);
6674 }
6775 }
68 exit;
76 exit 0;
6977 __END__