File Coverage

blib/lib/Business/OnlinePayment/IPayment.pm
Criterion Covered Total %
statement 152 167 91.0
branch 40 62 64.5
condition 17 36 47.2
subroutine 30 30 100.0
pod 13 13 100.0
total 252 308 81.8


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::IPayment;
2              
3 7     7   1117767 use 5.010001;
  7         43  
4 7     7   36 use strict;
  7         12  
  7         163  
5 7     7   32 use warnings;
  7         10  
  7         209  
6              
7             # preparation
8 7     7   3174 use XML::Compile::WSDL11;
  7         1810769  
  7         291  
9 7     7   3135 use XML::Compile::SOAP11;
  7         101404  
  7         207  
10 7     7   3301 use XML::Compile::Transport::SOAPHTTP;
  7         52678  
  7         200  
11 7     7   3182 use Business::OnlinePayment::IPayment::Response;
  7         29  
  7         333  
12 7     7   3369 use Business::OnlinePayment::IPayment::Transaction;
  7         18  
  7         238  
13 7     7   2737 use Business::OnlinePayment::IPayment::Return;
  7         20  
  7         235  
14 7     7   46 use Digest::MD5 qw/md5_hex/;
  7         13  
  7         280  
15 7     7   38 use URI;
  7         13  
  7         191  
16              
17 7     7   3189 use Business::OnlinePayment 3;
  7         21348  
  7         165  
18 7     7   41 use Moo;
  7         14  
  7         39  
19              
20 7     7   1873 use base 'Business::OnlinePayment';
  7         15  
  7         14172  
21              
22             # use Log::Report mode => 'DEBUG';
23              
24             =head1 NAME
25              
26             Business::OnlinePayment::IPayment - Checkout via Ipayment Silent Mode
27              
28             =head1 VERSION
29              
30             Version 0.09
31              
32             =cut
33              
34             our $VERSION = '0.09';
35              
36             =head1 DESCRIPTION
37              
38             This module provides an interface for online payments via gateway, using the
39             IPayment silent mode (L).
40              
41             It supports payments, capture and reverse operations, and vault-related
42             functions.
43              
44             =head1 SYNOPSIS
45              
46             use Business::OnlinePayment::IPayment;
47             my %account = (
48             accountid => 99999,
49             trxuserid => 99998,
50             trxpassword => 0,
51             adminactionpassword => '5cfgRT34xsdedtFLdfHxj7tfwx24fe',
52             app_security_key => 'testtest',
53             wsdl_file => $wsdl_file,
54             success_url => 'http://example.net/checkout-payment',
55             failure_url => 'http://example.net/checkout-success',
56             hidden_trigger_rul => 'http://example.net/trigger',
57             );
58            
59            
60             my $secbopi = Business::OnlinePayment::IPayment->new(%account);
61             $secbopi->transaction(transactionType => 'preauth',
62             trxAmount => 5000);
63             # see Business::OnlinePayment::IPayment::Transaction for available options
64              
65             $response = $ua->post('https://ipayment.de/merchant/99999/processor/2.0/',
66             { ipayment_session_id => $secbopi->session_id,
67             addr_name => "Mario Pegula",
68             silent => 1,
69             cc_number => "4111111111111111",
70             cc_checkcode => "",
71             cc_expdate_month => "02",
72             trx_securityhash => $secbopi->trx_securityhash,
73             cc_expdate_year => "2014" });
74            
75            
76             =head2 ACCESSORS
77              
78             =head3 Fixed values (accountData and processorUrls)
79              
80             The following attributes should and can be set only in the
81             constructor, as they are pretty much fixed values.
82              
83             =over 4
84              
85             =item wsdl_file
86              
87             The name of the WSDL file. It should be a local file.
88              
89             =cut
90              
91             has wsdl_file => (is => 'rw');
92              
93             =item accountid
94              
95             The Ipayment account id (the one put into the CGI url). Integer.
96              
97             =cut
98              
99             has accountid => (is => 'rw',
100             isa => sub {
101             die "Not an integer" unless $_[0] =~ m/^[0-9]+$/s
102             });
103              
104             =item trxuserid
105              
106             The application ID, you can in your ipayment configuration menu read
107             using Anwendung > Details. Integer
108              
109             =cut
110              
111             has trxuserid => (is => 'rw',
112             isa => sub {
113             die "Not an integer" unless $_[0] =~ m/^[0-9]+$/s
114             });
115              
116             =item trxpassword
117              
118             For each application, there is an application password which
119             automatically ipayment System is presented. The password consists of
120             numbers. You will find the application password in your ipayment
121             Anwendungen > Details
122              
123             B
124              
125             =cut
126              
127             has trxpassword => (is => 'rw');
128              
129             =item adminactionpassword
130              
131             The admin password.
132              
133             B
134              
135             =cut
136              
137             has adminactionpassword => (is => 'rw');
138              
139              
140             =item app_security_key
141              
142             If this attribute is set, we will (and shall) send a checksum for the
143             parameters.
144              
145             B
146              
147             =cut
148              
149             has app_security_key => (is => 'rw');
150              
151              
152             =item accountData
153              
154             Accessor to retrieve the hash with the account data details. The
155             output will look like this:
156              
157             accountData => {
158             accountid => 99999,
159             trxuserid => 99999,
160             trxpassword =>0,
161             adminactionpassword => '5cfgRT34xsdedtFLdfHxj7tfwx24fe'}
162              
163              
164             =cut
165              
166             sub accountData {
167 46     46 1 123 my $self = shift;
168 46         1253 my %account_data = ( # mandatory
169             accountId => $self->accountid,
170             trxuserId => $self->trxuserid,
171             trxpassword => $self->trxpassword
172             );
173 46         2110 my $adminpass = $self->adminactionpassword;
174 46 100       158 if (defined $adminpass) {
175 43         112 $account_data{adminactionpassword} = $adminpass;
176             }
177 46         242 return \%account_data;
178             }
179              
180             =item success_url
181              
182             Mandatory (for us) field, where to redirect the user in case of success.
183              
184             CGI-Name: C
185              
186             I
187             script.> (no need to C)
188              
189             =cut
190              
191             has success_url => (is => 'rw',
192             isa => sub { die "Missing success url" unless $_[0] },
193             default => sub { die "Missing success url" },
194             );
195              
196             =item failure_url
197              
198             Mandatory (for us) field, where to redirect the user in case of failure.
199              
200             CGI Name: C
201             Data type: String
202              
203             This URL is more in case of failure of ipayment system with the error information and parameters B. This URL must point to a CGI script that can handle the paramaters.
204              
205             =cut
206              
207             has failure_url => (is => 'rw',
208             isa => sub { die "Missing failure url" unless $_[0] },
209             default => sub { die "Missing success url" },
210             );
211              
212              
213             =item hidden_trigger_url
214              
215             Optional url for the hidden trigger.
216              
217             =cut
218              
219             has hidden_trigger_url => (is => 'rw');
220              
221              
222             =item processorUrls
223              
224             Return the hashref with the defined urls
225              
226             =back
227              
228             =cut
229              
230             sub processorUrls {
231 21     21 1 1025 my $self = shift;
232             return {
233 21         338 redirectUrl => $self->success_url,
234             silentErrorUrl => $self->failure_url,
235             hiddenTriggerUrl => $self->hidden_trigger_url
236             };
237             }
238              
239              
240              
241              
242              
243             =head3 error
244              
245             This accessors point to a XML::Compile::SOAP backtrace. The object is
246             quite large and deeply nested, but it's there just in case we need it.
247              
248             =cut
249              
250             has error => (is => 'rwp');
251              
252             =head3 debug
253              
254             Every call to session id stores the trace into this attribute.
255              
256             =cut
257              
258             has debug => (is => 'rwp');
259              
260             =head3 trx_obj
261              
262             Attribute to hold a L object
263              
264             =cut
265              
266             has trx_obj => (is => 'rwp');
267              
268             =head3 transaction
269              
270             Constructor for the object above. All the argument are passed verbatim
271             to the L constructor,
272             then the object is stored.
273              
274             =cut
275              
276             sub transaction {
277 16     16 1 2813 my $self = shift;
278 16         93 my %trx = @_;
279 16         360 my $trxdata = Business::OnlinePayment::IPayment::Transaction->new(%trx);
280 16         256 $self->_set_trx_obj($trxdata);
281             }
282              
283              
284             =head2 METHODS
285              
286             =over 4
287              
288             =item session_id
289              
290             This is the main method to call. The session is not stored in the object, because it can used only B. So calling session_id will send the data to the SOAP service and retrieve the session key.
291              
292             =cut
293              
294             sub session_id {
295 18     18 1 169 my $self = shift;
296             # clean eventually stale data
297 18         72 $self->_set_error(undef);
298              
299 18         68 my %args = (
300             # fixed values
301             accountData => $self->accountData,
302             processorUrls => $self->processorUrls,
303             # then the transaction
304             transactionType => $self->trx_obj->transactionType,
305             paymentType => $self->trx_obj->paymentType,
306             transactionData => $self->trx_obj->transactionData,
307             );
308             # and the options, if needed
309 15 100       68 if ($self->trx_obj->options) {
310 3         11 $args{options} = $self->trx_obj->options;
311             }
312              
313             # do the request passing the accountData
314 15         87 my ($res, $trace) = $self->_get_soap_object('createSession')->(%args);
315 15         6216141 $self->_set_debug($trace);
316              
317             # check if we got something valuable
318 15 50 33     178 unless ($res and
      33        
319             ref($res) eq 'HASH' and
320             exists $res->{createSessionResponse}->{sessionId}) {
321             # ok, we got an error. Save the trace to the error and return
322 0         0 $self->_set_error($trace);
323 0         0 return undef;
324             }
325              
326 15         219 return $res->{createSessionResponse}->{sessionId};
327             # please note that we don't store the sessionId. It's a fire and forget.
328             }
329              
330              
331             =item raw_response_hash
332              
333             Debug for the arguments passed to IPayment::Return;
334              
335             =cut
336              
337             has raw_response_hash => (is => 'rwp');
338              
339              
340              
341             =item capture($ret_trx_number, $amount, $currency, $opts)
342              
343             Charge an amount previously preauth'ed. C<$amount> and C<$currency>
344             are optional and may be used to charge partial amounts. C<$amount> and
345             C<$currency> follow the same rules of C and C
346             of L (no decimal,
347             usually multiply by 100).
348              
349             The last optional argument should be a hashref with additional
350             parameters to pass to transactionData (notably shopperId).
351              
352             =cut
353              
354             sub _do_post_payment_op {
355 21     21   115 my ($self, $op, $number, $amount, $currency, $trxdetails) = @_;
356 21 50       308 unless (defined $number) {
357 0         0 $self->_set_error("Missing transaction number");
358 0         0 return undef;
359             }
360 21 50       66 die "Wrong usage, missing operation" unless $op;
361 21         90 my %args = (
362             accountData => $self->accountData,
363             origTrxNumber => $number,
364             );
365             # amount is always mandatory for transactionData
366 21 100       75 if ($amount) {
367 13         24 my %trxdata;
368 13 50       74 die "Wrong amount $amount!\n" unless ($amount =~ m/^[1-9][0-9]*$/s);
369              
370 13 100       34 unless ($currency) {
371 2         5 $currency = 'EUR'
372             }
373 13 50       52 unless ($currency =~ m/^[A-Z]{3}$/s) {
374 0         0 die "Wrong currency name!\n";
375             }
376            
377             %trxdata = (
378 13         60 trxAmount => $amount,
379             trxCurrency => $currency,
380             );
381 13 50 66     98 if ($trxdetails and
      66        
382             (ref($trxdetails) eq 'HASH')
383             and %$trxdetails) {
384 9         32 foreach my $k (keys %$trxdetails) {
385 9 50       32 unless ($trxdata{$k}) {
386 9         27 $trxdata{$k} = $trxdetails->{$k}
387             }
388             }
389             }
390 13         44 $args{transactionData} = \%trxdata;
391             }
392              
393 21 50 100     142 die "Wrong operation" unless ($op eq 'capture' or
      66        
394             $op eq 'refund' or
395             $op eq 'reverse');
396              
397 21         96 my ($res, $trace) = $self->_get_soap_object($op)->(%args);
398 21         24065168 $self->_set_debug($trace);
399 21         147 $self->_set_raw_response_hash($res);
400 21 50 33     276 if ($res and ref($res) eq 'HASH' and
      33        
401             exists $res->{"${op}Response"}->{ipaymentReturn}) {
402             return Business::OnlinePayment::IPayment::Return
403 21         645 ->new($res->{"${op}Response"}->{ipaymentReturn});
404             }
405             else {
406 0         0 $self->_set_error($trace);
407 0         0 return undef;
408             }
409             }
410              
411             =item datastorage_op($datastorage_id)
412              
413             After calling C, if you have a valid datastorage id, you
414             may want to use that instead of creating a session and use the form.
415              
416             This method will do a SOAP request to the Ipayment server, using the
417             transaction details provided in the call to C, and do the
418             requested operation. So far it's supported preauth and auth. The
419             capture and other operations should be done via its own method (which
420             don't require the datastorage, but simply the previous transaction's
421             id).
422              
423             =cut
424              
425             sub datastorage_op {
426 3     3 1 17 my ($self, $id) = @_;
427 3 50       10 return unless $id;
428            
429 3         13 $self->_set_error(undef);
430             # this should be fully populated by now
431 3         14 my %args = (
432             accountData => $self->accountData,
433             paymentData => {
434             storageData => {
435             fromDatastorageId => $id,
436             },
437             },
438             transactionData => $self->trx_obj->transactionData,
439             );
440 3         12 my $operation = $self->trx_obj->transactionType;
441             # append the options if needed
442 3 50       12 if ($self->trx_obj->options) {
443 0         0 $args{options} = $self->trx_obj->options;
444             }
445 3         14 my ($res, $trace) = $self->_get_soap_object($operation)->(%args);
446 3         3656687 $self->_set_debug($trace);
447 3         20 $self->_set_raw_response_hash($res);
448              
449             # in the trasaction object the call is defined as in CGI, but we
450             # need the SOAP one
451 3         12 my $op = $self->_translate_to_soap_call($operation);
452              
453 3 50 33     42 if ($res and ref($res) eq 'HASH' and
      33        
454             exists $res->{"${op}Response"}->{ipaymentReturn}) {
455             return Business::OnlinePayment::IPayment::Return
456 3         66 ->new($res->{"${op}Response"}->{ipaymentReturn});
457             }
458             else {
459 0         0 $self->_set_error($trace);
460 0         0 return undef;
461             }
462             }
463              
464             =item expire_datastorage($id)
465              
466             Given the storage id passed as argument, expire it. Keep in mind that
467             expiring it multiple times returns always true, so the return code is
468             not really interesting.
469              
470             It returns 0 if the storage didn't exist.
471              
472             =cut
473              
474             sub expire_datastorage {
475 3     3 1 1549 my ($self, $id) = @_;
476 3 50       14 return unless $id;
477 3         6 my $op = 'expireDatastorage';
478 3         13 my %args = (
479             accountData => $self->accountData,
480             datastorageId => $id,
481             );
482 3         16 my ($res, $trace) = $self->_get_soap_object($op)->(%args);
483 3         231411 $self->_set_debug($trace);
484 3         61 $self->_set_raw_response_hash($res);
485 3 50 33     48 if ($res and ref($res) eq 'HASH' and
      33        
486             exists $res->{"${op}Response"}->{expireDatastorageReturn}) {
487 3         36 return $res->{"${op}Response"}->{expireDatastorageReturn};
488             }
489 0         0 return;
490             }
491              
492              
493             sub capture {
494 8     8 1 1000704 my ($self, $number, $amount, $currency, $opts) = @_;
495             # init the soap, if not already
496 8         38 return $self->_do_post_payment_op(capture => $number,
497             $amount, $currency, $opts);
498             }
499              
500             =item reverse($ret_trx_number)
501              
502             Release the amount previously preauth'ed, passing the original
503             transaction number. No partial amount can be released, and will
504             succeed only if no charging has been done.
505              
506             =cut
507              
508              
509             sub reverse {
510 3     3 1 237 my ($self, $number) = @_;
511             # we don't pass $amount and $currency
512 3         14 return $self->_do_post_payment_op(reverse => $number);
513             }
514              
515             =item refund($ret_trx_number, $amount, $currency, $opts)
516              
517             Refund the given amount. Please note that we have to pass the
518             transaction number B, not the C one.
519              
520             The last optional argument should be a hashref with additional
521             parameters to pass to transactionData (notably shopperId).
522              
523             =cut
524              
525             sub refund {
526 10     10 1 643 my ($self, $number, $amount, $currency, $opts) = @_;
527 10         43 return $self->_do_post_payment_op(refund => $number,
528             $amount, $currency, $opts);
529             }
530              
531             # accessors to soap objects
532              
533             has _soap_createSession => (is => 'rw');
534             has _soap_capture => (is => 'rw');
535             has _soap_reverse => (is => 'rw');
536             has _soap_refund => (is => 'rw');
537             has _soap_preAuthorize => (is => 'rw');
538             has _soap_authorize => (is => 'rw');
539             has _soap_expireDatastorage => (is => 'rw');
540              
541             sub _get_soap_object {
542 42     42   328 my ($self, $op) = @_;
543 42         147 my $call = $self->_translate_to_soap_call($op);
544 42         124 my $accessor = "_soap_" . $call;
545 42         171 my $obj = $self->$accessor;
546 42 100       188 return $obj if $obj;
547 16         217 my $wsdl = XML::Compile::WSDL11->new($self->wsdl_file);
548 16         3846490 my $client = $wsdl->compileClient($call);
549             # set the object
550 16         1268587 $self->$accessor($client);
551 16         97 return $self->$accessor;
552             }
553              
554             # this method may be used for to do a sanity check, as it will die on
555             # undef/wrong values.
556              
557             sub _translate_to_soap_call {
558 45     45   158 my ($self, $op) = @_;
559 45 50       130 die "No operation provided!" unless $op;
560 45         431 my %hash = (capture => 'capture',
561             reverse => 'reverse',
562             refund => 'refund',
563             preauth => 'preAuthorize',
564             auth => 'authorize',
565             authorize => 'authorize',
566             preAuthorize => 'preAuthorize',
567             createSession => 'createSession',
568             expireDatastorage => 'expireDatastorage',
569             );
570 45 50       168 die "Wrong call $op!" unless $hash{$op};
571 45         180 return $hash{$op};
572             }
573              
574             =back
575              
576             =head2 SOAP specification
577              
578             Name: createSession
579             Binding: ipaymentBinding
580             Endpoint: https://ipayment.de/service/3.0/
581             SoapAction: createSession
582             Input:
583             use: literal
584             namespace: https://ipayment.de/service_v3/binding
585             message: createSessionRequest
586             parts:
587             accountData: https://ipayment.de/service_v3/extern:AccountData
588             transactionData: https://ipayment.de/service_v3/extern:TransactionData
589             transactionType: https://ipayment.de/service_v3/extern:TransactionType
590             paymentType: https://ipayment.de/service_v3/extern:PaymentType
591             options: https://ipayment.de/service_v3/extern:OptionData
592             processorUrls: https://ipayment.de/service_v3/extern:ProcessorUrlData
593             Output:
594             use: literal
595             namespace: https://ipayment.de/service_v3/binding
596             message: createSessionResponse
597             parts:
598             sessionId: http://www.w3.org/2001/XMLSchema:string
599             Style: rpc
600             Transport: http://schemas.xmlsoap.org/soap/http
601            
602              
603             =head2 SECURITY
604              
605             =over 4
606              
607             =item trx_securityhash
608              
609             If we have a security key, we trigger the hash generation, so we can
610             double check the result.
611              
612             CGI Name: C
613             Data type: string, maximum 32 characters
614              
615             Security hash of CGI command concatenating Id, amount, currency,
616             password, Transaction Security Key (should be set in the configuration
617             menu using ipayment). The hash is C, C,
618             C, C and the I.
619              
620             md5_hex($trxuser_id . $trx_amount . $trx_currency . $trxpassword . $sec_key);
621              
622             perl -e 'use Digest::MD5 qw/md5_hex/;
623             print md5_hex("99998" . 5000 . "EUR" . 0 . "testtest"), "\n";'
624             # => then in the form
625            
626             value="db4812171baef817dec0cd56c0f5c8cd">
627              
628             =cut
629              
630             sub trx_securityhash {
631 12     12 1 120 my $self = shift;
632 12 50       73 unless ($self->app_security_key) {
633 0         0 warn "hash requested, but app_security_key wasn't provided!\n";
634 0         0 return;
635             }
636 12         301 return md5_hex($self->trxuserid .
637             $self->trx_obj->trxAmount .
638             $self->trx_obj->trxCurrency .
639             $self->trxpassword .
640             $self->app_security_key);
641             }
642              
643             =back
644              
645             =head2 UTILITIES
646              
647             =head3 get_response_obj($rawuri) or get_response_obj(%params)
648              
649             To be sure the transaction happened as aspected, we have to check this back.
650             Expected hash:
651              
652             Success:
653              
654             'ret_transtime' => '08:42:05', 'ret_transtime' => '08:42:03',
655             'ret_errorcode' => '0', 'ret_errorcode' => '0',
656             'redirect_needed' => '0', 'redirect_needed' => '0',
657             'ret_transdate' => '14.03.13', 'ret_transdate' => '14.03.13',
658             'addr_name' => 'Mario Pegula', 'addr_name' => 'Mario Rossi',
659             'trx_paymentmethod' => 'VisaCard', 'trx_paymentmethod' => 'AmexCard',
660             'ret_authcode' => '', 'ret_authcode' => '',
661             'trx_currency' => 'EUR', 'trx_currency' => 'EUR',
662             'ret_url_checksum' => 'md5sum',
663             'ret_param_checksum' => 'md5sum',
664             'ret_ip' => '88.198.37.147', 'ret_ip' => '88.198.37.147',
665             'trx_typ' => 'preauth', 'trx_typ' => 'preauth',
666             'ret_trx_number' => '1-83443831', 'ret_trx_number' => '1-83443830',
667             'ret_status' => 'SUCCESS', 'ret_status' => 'SUCCESS',
668             'trx_paymenttyp' => 'cc', 'trx_paymenttyp' => 'cc',
669             'trx_paymentdata_country' => 'US',
670             'trx_amount' => '5000', 'trx_amount' => '1000',
671             'ret_booknr' => '1-83443831', 'ret_booknr' => '1-83443830',
672             'trxuser_id' => '99998', 'trxuser_id' => '99999',
673             'trx_remoteip_country' => 'DE' 'trx_remoteip_country' => 'DE'
674              
675             Returns a L object, so you
676             can call ->is_success on it.
677              
678             This is just a shortcut for
679              
680             Business::OnlinePayment::IPayment::Response->new(%params);
681              
682             with C and C inherited from the fixed
683             values of this class.
684              
685             =cut
686              
687             sub get_response_obj {
688 11     11 1 16619738 my ($self, @args) = @_;
689 11         28 my %details;
690             # only one argument: we have an URI
691 11 50       45 if (@args == 1) {
    0          
692 11         28 my $raw_url = shift(@args);
693 11         75 my $uri = URI->new($raw_url);
694 11         1218 %details = $uri->query_form;
695 11         5438 $details{raw_url} = $raw_url;
696             }
697             elsif ((@args % 2) == 0) {
698 0         0 %details = @args;
699             }
700             else {
701 0         0 die "Arguments to validate the response not provided "
702             . "(paramaters or raw url";
703             }
704 11 50       52 unless (exists $details{my_security_key}) {
705 11         75 $details{my_security_key} = $self->app_security_key;
706             }
707 11 50       56 unless (exists $details{my_userid}) {
708 11         551 $details{my_userid} = $self->trxuserid;
709             }
710 11         317 return Business::OnlinePayment::IPayment::Response->new(%details);
711             }
712              
713             =head3 ipayment_cgi_location
714              
715             Returns the correct url where the customer posts the CC data, which is simply:
716             L/processor/2.0/>
717              
718             =cut
719              
720             sub ipayment_cgi_location {
721 13     13 1 9153 my $self = shift;
722 13         240 return 'https://ipayment.de/merchant/' . $self->accountid
723             . '/processor/2.0/';
724             }
725              
726              
727             =head2 Additional information
728              
729             =head3 country
730              
731             Country code of the cardholder of the current
732             L object
733              
734             Being these information transaction specific, if a transaction has not
735             been initiated, the method will not do anything nor will return
736             anything.
737              
738             UK will be translated to GB, and EI to IE.
739              
740              
741             =cut
742              
743             sub country {
744 10     10 1 1019519 my $self = shift;
745             #
746 10 100       37 return unless $self->trx_obj;
747 8 100       18 if (@_ == 1) {
748 4         15 $self->trx_obj->addr_info->{country} = shift;
749             }
750 8         20 my $country = uc($self->trx_obj->addr_info->{country});
751 8 100       30 return unless $country =~ m/^[A-Z]{2,3}$/s;
752 6 100       17 if ($country eq 'UK') {
    100          
753 2         5 return 'GB';
754             }
755             elsif ($country eq 'EI') {
756 2         6 return 'IE';
757             }
758             else {
759 2         7 return $country;
760             }
761             }
762              
763             =head1 TESTING
764              
765             Test credit card numbers can be found here: L.
766              
767             =head1 AUTHOR
768              
769             Marco Pessotto, C<< >>
770              
771             =head1 BUGS
772              
773             Please report any bugs or feature requests to C, or through
774             the web interface at L. I will be notified, and then you'll
775             automatically be notified of progress on your bug as I make changes.
776              
777              
778              
779              
780             =head1 SUPPORT
781              
782             You can find documentation for this module with the perldoc command.
783              
784             perldoc Business::OnlinePayment::IPayment
785              
786              
787             You can also look for information at:
788              
789             =over 4
790              
791             =item * RT: CPAN's request tracker (report bugs here)
792              
793             L
794              
795             =item * AnnoCPAN: Annotated CPAN documentation
796              
797             L
798              
799             =item * CPAN Ratings
800              
801             L
802              
803             =item * Search CPAN
804              
805             L
806              
807             =back
808              
809              
810             =head1 ACKNOWLEDGEMENTS
811              
812             Thanks to Stefan Hornburg (Racke) C for the initial
813             code, ideas and support.
814              
815             =head1 LICENSE AND COPYRIGHT
816              
817             Copyright 2013-2014 Marco Pessotto.
818              
819             This program is free software; you can redistribute it and/or modify it
820             under the terms of the the Artistic License (2.0). You may obtain a
821             copy of the full license at:
822              
823             L
824              
825             Any use, modification, and distribution of the Standard or Modified
826             Versions is governed by this Artistic License. By using, modifying or
827             distributing the Package, you accept this license. Do not use, modify,
828             or distribute the Package, if you do not accept this license.
829              
830             If your Modified Version has been derived from a Modified Version made
831             by someone other than you, you are nevertheless required to ensure that
832             your Modified Version complies with the requirements of this license.
833              
834             This license does not grant you the right to use any trademark, service
835             mark, tradename, or logo of the Copyright Holder.
836              
837             This license includes the non-exclusive, worldwide, free-of-charge
838             patent license to make, have made, use, offer to sell, sell, import and
839             otherwise transfer the Package with respect to any patent claims
840             licensable by the Copyright Holder that are necessarily infringed by the
841             Package. If you institute patent litigation (including a cross-claim or
842             counterclaim) against any party alleging that the Package constitutes
843             direct or contributory patent infringement, then this Artistic License
844             to you shall terminate on the date that such litigation is filed.
845              
846             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
847             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
848             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
849             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
850             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
851             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
852             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
853             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
854              
855              
856             =cut
857              
858             1; # End of Business::OnlinePayment::IPayment