File Coverage

blib/lib/Business/OnlinePayment/IATSPayments.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::IATSPayments;
2 1     1   34144 use base qw( Business::OnlinePayment );
  1         3  
  1         970  
3              
4 1     1   5075 use warnings;
  1         3  
  1         31  
5 1     1   5 use strict;
  1         8  
  1         29  
6 1     1   1931 use Data::Dumper;
  1         15422  
  1         187  
7 1     1   416 use Business::CreditCard;
  0            
  0            
8             use SOAP::Lite;
9             #SOAP::Lite->import(+trace=>'debug');
10              
11             our $VERSION = '0.02';
12             $VERSION = eval $VERSION; # modperlstyle: convert the string into a number
13              
14             sub _info {
15             {
16             'info_compat' => '0.01',
17             'gateway_name' => 'IATS Payments',
18             'gateway_url' => 'http://home.iatspayments.com/',
19             'module_version' => $VERSION,
20             'supported_types' => [ 'CC', 'ECHECK' ],
21             #'token_support' => 1,
22             'test_transaction' => 1,
23              
24             'supported_actions' => [ 'Normal Authorization',
25             'Credit',
26             ],
27             };
28             }
29              
30             sub set_defaults {
31             my $self = shift;
32             #my %opts = @_;
33              
34             #$self->build_subs(qw( order_number avs_code cvv2_response
35             # response_page response_code response_headers
36             # ));
37              
38             $self->build_subs(qw( avs_code ));
39              
40             }
41              
42             sub map_fields {
43             my($self) = @_;
44              
45             my %content = $self->content();
46              
47             # TYPE MAP
48             my %types = ( 'visa' => 'CC',
49             'mastercard' => 'CC',
50             'american express' => 'CC',
51             'discover' => 'CC',
52             'check' => 'ECHECK',
53             );
54             $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
55             $self->transaction_type($content{'type'});
56            
57             # ACTION MAP
58             my $action = lc($content{'action'});
59             my %actions =
60             ( 'normal authorization' => 'ProcessCreditCardV1',
61             'credit' => 'ProcessCreditCardRefundWithTransactionIdV1',
62             );
63             my %check_actions =
64             ( 'normal authorization' => 'ProcessACHEFTV1',
65             'credit' => 'ProcessACHEFTRefundWithTransactionIdV1',
66             );
67              
68             if ($self->transaction_type eq 'CC') {
69             $content{'action'} = $actions{$action} || $action;
70             } elsif ($self->transaction_type eq 'ECHECK') {
71              
72             $content{'action'} = $check_actions{$action} || $action;
73              
74             # ACCOUNT TYPE MAP
75             my %account_types = ('personal checking' => 'CHECKING',
76             'personal savings' => 'SAVINGS',
77             'business checking' => 'CHECKING',
78             'business savings' => 'SAVINGS',
79             #not technically B:OP valid i guess?
80             'checking' => 'CHECKING',
81             'savings' => 'SAVINGS',
82             );
83             $content{'account_type'} = $account_types{lc($content{'account_type'})}
84             || $content{'account_type'};
85             }
86              
87             # stuff it back into %content
88             $self->content(%content);
89              
90             }
91              
92             sub remap_fields {
93             my($self,%map) = @_;
94              
95             my %content = $self->content();
96             foreach(keys %map) {
97             $content{$map{$_}} = $content{$_};
98             }
99             $self->content(%content);
100             }
101              
102             # NA: VISA, MC, AMX, DSC
103             # UK: VISA, MC, AMX, MAESTR
104             our %mop = (
105             'VISA card' => 'VISA',
106             'MasterCard' => 'MC',
107             'Discover card' => 'DSC',
108             'American Express card' => 'AMEX',
109             'Switch' => 'MAESTR',
110             'Solo' => 'MAESTR',
111             );
112              
113             #https://www.iatspayments.com/english/help/rejects.html
114             our %reject = (
115             '1' => 'Agent code has not been set up on the authorization system. Please call iATS at 1-888-955-5455.',
116             '2' => 'Unable to process transaction. Verify and re-enter credit card information.',
117             '3' => 'Invalid Customer Code.',
118             '4' => 'Incorrect expiration date.',
119             '5' => 'Invalid transaction. Verify and re-enter credit card information.',
120             '6' => 'Please have cardholder call the number on the back of the card.',
121             '7' => 'Lost or stolen card.',
122             '8' => 'Invalid card status.',
123             '9' => 'Restricted card status. Usually on corporate cards restricted to specific sales.',
124             '10' => 'Error. Please verify and re-enter credit card information.',
125             '11' => 'General decline code. Please have client call the number on the back of credit card',
126             '12' => 'Incorrect CVV2 or Expiry date',
127             '14' => 'The card is over the limit.',
128             '15' => 'General decline code. Please have client call the number on the back of credit card',
129             '16' => 'Invalid charge card number. Verify and re-enter credit card information.',
130             '17' => 'Unable to authorize transaction. Authorizer needs more information for approval.',
131             '18' => 'Card not supported by institution.',
132             '19' => 'Incorrect CVV2 security code',
133             '22' => 'Bank timeout. Bank lines may be down or busy. Re-try transaction later.',
134             '23' => 'System error. Re-try transaction later.',
135             '24' => 'Charge card expired.',
136             '25' => 'Capture card. Reported lost or stolen.',
137             '26' => 'Invalid transaction, invalid expiry date. Please confirm and retry transaction.',
138             '27' => 'Please have cardholder call the number on the back of the card.',
139             '32' => 'Invalid charge card number.',
140             '39' => 'Contact IATS 1-888-955-5455.',
141             '40' => 'Invalid card number. Card not supported by IATS.',
142             '41' => 'Invalid Expiry date.',
143             '42' => 'CVV2 required.',
144             '43' => 'Incorrect AVS.',
145             '45' => 'Credit card name blocked. Call iATS at 1-888-955-5455.',
146             '46' => 'Card tumbling. Call iATS at 1-888-955-5455.',
147             '47' => 'Name tumbling. Call iATS at 1-888-955-5455.',
148             '48' => 'IP blocked. Call iATS at 1-888-955-5455.',
149             '49' => 'Velocity 1 – IP block. Call iATS at 1-888-955-5455.',
150             '50' => 'Velocity 2 – IP block. Call iATS at 1-888-955-5455.',
151             '51' => 'Velocity 3 – IP block. Call iATS at 1-888-955-5455.',
152             '52' => 'Credit card BIN country blocked. Call iATS at 1-888-955-5455.',
153             '100' => 'DO NOT REPROCESS. Call iATS at 1-888-955-5455.',
154             #Timeout The system has not responded in the time allotted. Call iATS at 1-888-955-5455.
155             );
156              
157             our %failure_status = (
158             '7' => 'stolen',
159             '8' => 'inactive',
160             '9' => 'inactive',
161             '14' => 'nsf',
162             '24' => 'expired',
163             '25' => 'stolen',
164             '45' => 'blacklisted',
165             '48' => 'blacklisted',
166             '49' => 'blacklisted',
167             '50' => 'blacklisted',
168             '51' => 'blacklisted',
169             '52' => 'blacklisted',
170             #'100' => # it sounds serious. but why? it says nothing specific
171             );
172              
173             sub submit {
174             my($self) = @_;
175              
176             $self->map_fields;
177              
178             $self->remap_fields(
179             login => 'agentCode',
180             password => 'password',
181              
182             description => 'comment',
183             amount => 'total',
184             invoice_number => 'invoiceNum',
185             customer_ip => 'customerIPAddress',
186              
187             last_name => 'lastName',
188             first_name => 'firstName',
189             address => 'address',
190             city => 'city',
191             state => 'state',
192             zip => 'zipCode',
193             #country => 'x_Country',
194              
195             card_number => 'creditCardNum',
196             expiration => 'creditCardExpiry',
197             cvv2 => 'cvv2',
198              
199             authorization => 'transactionId',
200              
201             account_type => 'accountType',
202              
203             );
204              
205             my %content = $self->content();
206              
207             $content{'mop'} = $mop{ cardtype($content{creditCardNum}) }
208             if $content{'type'} eq 'CC';
209              
210             if ( $self->test_transaction ) {
211             $content{agentCode} = 'TEST88';
212             $content{password} = 'TEST88';
213             }
214              
215             my $base_uri =
216             ( ! $content{currency} || $content{currency} =~ /^(USD|CAD)$/i )
217             ? 'https://www.iatspayments.com/NetGate/'
218             : 'https://www.uk.iatspayments.com/NetGate/';
219              
220             my $action = $content{action};
221              
222             my $uri = $base_uri. "ProcessLink.asmx?op=$action";
223              
224             my %data = map { $_ => $content{$_} } (qw(
225             agentCode
226             password
227             comment
228             total
229             customerIPAddress
230             ));
231              
232             if ( $action =~ /RefundWithTransacdtionIdV[\d\.]+$/ ) {
233              
234             $data{ $_ } = $content{$_} for qw(
235             transactionId
236             );
237              
238             } else {
239              
240             $data{ $_ } = $content{$_} for qw(
241             invoiceNum
242             lastName
243             firstName
244             address
245             city
246             state
247             zipCode
248             );
249              
250             if ( $content{'type'} eq 'CC' ) {
251              
252             $data{$_} = $content{$_}
253             for qw( creditCardNum creditCardExpiry cvv2 mop );
254              
255             } elsif ( $content{'type'} eq 'ECHECK' ) {
256              
257             $data{'accountNum'}= $content{'routing_code'}. $content{'account_number'};
258              
259             $data{$_} = $content{$_}
260             for qw( accountType );
261              
262             }
263              
264             }
265              
266             my @opts = map { SOAP::Data->name($_)->value( $data{$_} ) }
267             keys %data;
268              
269             my $result = SOAP::Lite
270             ->proxy($uri)
271             ->default_ns($base_uri)
272             #->on_action( sub { join '/', @_ } )
273             ->on_action( sub { join '', @_ } )
274             ->autotype(0)
275              
276             ->$action( @opts )
277              
278             ->result();
279              
280             my $iatsresponse = $result->{IATSRESPONSE};
281              
282             if ( $iatsresponse->{STATUS} eq 'Failure' && $iatsresponse->{ERRORS} ) {
283             die 'iATS Payments error: '. $iatsresponse->{ERRORS}. "\n";
284             } elsif ( $iatsresponse->{STATUS} ne 'Success' ) {
285             die "Couldn't parse iATS Payments response: ". Dumper($result);
286             }
287              
288             my $processresult = $iatsresponse->{PROCESSRESULT};
289              
290             if ( defined( $processresult->{TRANSACTIONID} ) ) {
291             $processresult->{TRANSACTIONID} =~ s/^\s+//;
292             $processresult->{TRANSACTIONID} =~ s/\s+$//;
293             }
294             $self->authorization($processresult->{TRANSACTIONID} || '');
295              
296             if ( $processresult->{AUTHORIZATIONRESULT} =~ /^\s*OK(:\s*\d+:)?(\w)?\s*$/i ) {
297             $self->is_success(1);
298             $self->avs_code($2); #avs_code? sure looks like one
299              
300             } elsif ( $processresult->{AUTHORIZATIONRESULT} =~ /^\s*Timeout\s*$/i ) {
301             $self->is_success(0);
302             $self->error_message('The system has not responded in the time allotted. '.
303             'Call iATS at 1-888-955-5455.');
304              
305             } elsif ( $processresult->{AUTHORIZATIONRESULT}
306             =~ /^\s*REJ(ECT)?:\s*(\d+)\s*$/i
307             )
308             {
309             $self->is_success(0);
310             $self->result_code($2);
311             $self->error_message( $reject{$2} || $processresult->{AUTHORIZATIONRESULT});
312             $self->failure_status( $failure_status{$2} || 'decline' );
313              
314             } else {
315             die "No/Unknown AUTHORIZATIONRESULT iATS Payments response: ".
316             Dumper($processresult);
317             }
318              
319             }
320              
321             1;
322              
323             __END__