File Coverage

blib/lib/Business/OnlinePayment/IPPay.pm
Criterion Covered Total %
statement 87 191 45.5
branch 28 104 26.9
condition 11 57 19.3
subroutine 16 18 88.8
pod 1 6 16.6
total 143 376 38.0


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::IPPay;
2              
3 4     4   16770 use strict;
  4         7  
  4         128  
4 4     4   19 use Carp;
  4         8  
  4         303  
5 4     4   2019 use Tie::IxHash;
  4         17295  
  4         129  
6 4     4   2988 use XML::Simple;
  4         32133  
  4         34  
7 4     4   3504 use XML::Writer;
  4         55432  
  4         153  
8 4     4   2769 use Locale::Country;
  4         135677  
  4         373  
9 4     4   756 use Business::OnlinePayment;
  4         3308  
  4         128  
10 4     4   2786 use Business::OnlinePayment::HTTPS;
  4         87307  
  4         204  
11 4     4   43 use vars qw($VERSION $DEBUG @ISA $me);
  4         9  
  4         11804  
12              
13             @ISA = qw(Business::OnlinePayment::HTTPS);
14             $VERSION = '0.05_02';
15             $VERSION = eval $VERSION; # modperlstyle: convert the string into a number
16              
17             $DEBUG = 0;
18             $me = 'Business::OnlinePayment::IPPay';
19              
20             sub _info {
21             {
22 1     1   172 'info_version' => '0.01',
23             'module_version' => $VERSION,
24             'supported_types' => [ qw( CC ECHECK ) ],
25             'supported_actions' => { 'CC' => [
26             'Normal Authorization',
27             'Authorization Only',
28             'Post Authorization',
29             'Void',
30             'Credit',
31             ],
32             'ECHECK' => [
33             'Normal Authorization',
34             'Void',
35             'Credit',
36             ],
37             },
38             'CC_void_requires_card' => 1,
39             'ECHECK_void_requires_account' => 1,
40             };
41             }
42              
43             sub set_defaults {
44 10     10 0 43127 my $self = shift;
45 10         39 my %opts = @_;
46              
47             # standard B::OP methods/data
48 10 50       528 $self->server('gateway17.jetpay.com') unless $self->server;
49 10 50       832 $self->port('443') unless $self->port;
50 10 50       672 $self->path('/jetpay') unless $self->path;
51              
52 10         430 $self->build_subs(qw( order_number avs_code cvv2_response
53             response_page response_code response_headers
54             ));
55              
56             # module specific data
57 10 50       611 if ( $opts{debug} ) {
58 0         0 $self->debug( $opts{debug} );
59 0         0 delete $opts{debug};
60             }
61              
62 10         29 my %_defaults = ();
63 10         39 foreach my $key (keys %opts) {
64 7 100       44 $key =~ /^default_(\w*)$/ or next;
65 4         18 $_defaults{$1} = $opts{$key};
66 4         12 delete $opts{$key};
67             }
68 10         53 $self->{_defaults} = \%_defaults;
69             }
70              
71             sub map_fields {
72 9     9 0 16 my($self) = @_;
73              
74 9         36 my %content = $self->content();
75              
76             # TYPE MAP
77 9         315 my %types = ( 'visa' => 'CC',
78             'mastercard' => 'CC',
79             'american express' => 'CC',
80             'discover' => 'CC',
81             'check' => 'ECHECK',
82             );
83 9   33     55 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
84 9         254 $self->transaction_type($content{'type'});
85            
86             # ACTION MAP
87 9         64 my $action = lc($content{'action'});
88 9         65 my %actions =
89             ( 'normal authorization' => 'SALE',
90             'authorization only' => 'AUTHONLY',
91             'post authorization' => 'CAPT',
92             'void' => 'VOID',
93             'credit' => 'CREDIT',
94             );
95 9         40 my %check_actions =
96             ( 'normal authorization' => 'CHECK',
97             'void' => 'VOIDACH',
98             'credit' => 'REVERSAL',
99             );
100 9 100       233 if ($self->transaction_type eq 'CC') {
    50          
101 6   33     63 $content{'TransactionType'} = $actions{$action} || $action;
102             }elsif ($self->transaction_type eq 'ECHECK') {
103 3   33     163 $content{'TransactionType'} = $check_actions{$action} || $action;
104             }
105              
106              
107             # ACCOUNT TYPE MAP
108 9         70 my %account_types = ('personal checking' => 'Checking',
109             'personal savings' => 'Savings',
110             'business checking' => 'BusinessCk',
111             );
112             $content{'account_type'} = $account_types{lc($content{'account_type'})}
113 9   33     1056 || $content{'account_type'};
114              
115             $content{Origin} = 'RECURRING'
116 9 50 33     44 if ($content{recurring_billing} &&$content{recurring_billing} eq 'YES' );
117              
118             # stuff it back into %content
119 9         78 $self->content(%content);
120              
121             }
122              
123             sub expdate_month {
124 18     18 0 50 my ($self, $exp) = (shift, shift);
125 18         26 my $month;
126 18 100 66     183 if ( defined($exp) and $exp =~ /^(\d+)\D+\d*\d{2}$/ ) {
    50 33        
127 6         52 $month = sprintf( "%02d", $1 );
128             }elsif ( defined($exp) and $exp =~ /^(\d{2})\d{2}$/ ) {
129 0         0 $month = sprintf( "%02d", $1 );
130             }
131 18         107 return $month;
132             }
133              
134             sub expdate_year {
135 18     18 0 38 my ($self, $exp) = (shift, shift);
136 18         25 my $year;
137 18 100 66     137 if ( defined($exp) and $exp =~ /^\d+\D+\d*(\d{2})$/ ) {
    50 33        
138 6         31 $year = sprintf( "%02d", $1 );
139             }elsif ( defined($exp) and $exp =~ /^\d{2}(\d{2})$/ ) {
140 0         0 $year = sprintf( "%02d", $1 );
141             }
142 18         37 return $year;
143             }
144              
145             sub revmap_fields {
146 51     51 0 64 my $self = shift;
147 51         208 tie my(%map), 'Tie::IxHash', @_;
148 51         5351 my %content = $self->content();
149             map {
150 51         1450 my $value;
  498         2169  
151 498 100       1260 if ( ref( $map{$_} ) eq 'HASH' ) {
    100          
    100          
152 36 100       178 $value = $map{$_} if ( keys %{ $map{$_} } );
  36         99  
153             }elsif( ref( $map{$_} ) ) {
154 87         917 $value = ${ $map{$_} };
  87         212  
155             }elsif( exists( $content{ $map{$_} } ) ) {
156 106         1890 $value = $content{ $map{$_} };
157             }
158              
159 498 100       6575 if (defined($value)) {
160 166         542 ($_ => $value);
161             }else{
162 332         691 ();
163             }
164             } (keys %map);
165             }
166              
167             sub submit {
168 0     0 1 0 my($self) = @_;
169              
170 0         0 $self->is_success(0);
171 0         0 $self->map_fields();
172              
173 0         0 my @required_fields = qw(action login type);
174              
175 0         0 my $action = lc($self->{_content}->{action});
176 0         0 my $type = $self->transaction_type();
177 0 0 0     0 if ( $action eq 'normal authorization'
    0 0        
    0 0        
      0        
178             || $action eq 'credit'
179             || $action eq 'authorization only' && $type eq 'CC')
180             {
181 0         0 push @required_fields, qw( amount );
182              
183 0 0       0 push @required_fields, qw( card_number expiration )
184             if ($type eq "CC");
185            
186 0 0       0 push @required_fields,
187             qw( routing_code account_number name ) # account_type
188             if ($type eq "ECHECK");
189            
190             }elsif ( $action eq 'post authorization' && $type eq 'CC') {
191 0         0 push @required_fields, qw( order_number );
192             }elsif ( $action eq 'void') {
193 0         0 push @required_fields, qw( order_number amount );
194              
195 0 0       0 push @required_fields, qw( authorization card_number )
196             if ($type eq "CC");
197              
198 0 0       0 push @required_fields,
199             qw( routing_code account_number name ) # account_type
200             if ($type eq "ECHECK");
201              
202             }else{
203             croak "$me can't handle transaction type: ".
204 0         0 $self->{_content}->{action}. " for ".
205             $self->transaction_type();
206             }
207              
208 0         0 my %content = $self->content();
209 0         0 foreach ( keys ( %{($self->{_defaults})} ) ) {
  0         0  
210 0 0       0 $content{$_} = $self->{_defaults}->{$_} unless exists($content{$_});
211             }
212 0         0 $self->content(%content);
213              
214 0         0 $self->required_fields(@required_fields);
215              
216             #quick validation because ippay dumps an error indecipherable to the end user
217 0 0       0 if (grep { /^routing_code$/ } @required_fields) {
  0         0  
218 0 0       0 unless( $content{routing_code} =~ /^\d{9}$/ ) {
219 0         0 $self->_error_response('Invalid routing code');
220 0         0 return;
221             }
222             }
223              
224 0 0       0 if ($self->test_transaction()) {
225 0         0 $self->server('test1.jetpay.com');
226 0         0 $self->port('443');
227 0         0 $self->path('/jetpay');
228             }
229              
230 0         0 my $transaction_id = $content{'order_number'};
231 0 0       0 unless ($transaction_id) {
232 0         0 my ($page, $server_response, %headers) = $self->https_get('dummy' => 1);
233             warn "fetched transaction id: (HTTPS response: $server_response) ".
234             "(HTTPS headers: ".
235 0 0       0 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
  0         0  
236             "(Raw HTTPS content: $page)"
237             if $DEBUG;
238 0 0       0 return unless $server_response=~ /^200/;
239 0         0 $transaction_id = $page;
240             }
241              
242 0         0 my $cardexpmonth = $self->expdate_month($content{expiration});
243 0         0 my $cardexpyear = $self->expdate_year($content{expiration});
244 0         0 my $cardstartmonth = $self->expdate_month($content{card_start});
245 0         0 my $cardstartyear = $self->expdate_year($content{card_start});
246            
247 0         0 my $amount;
248 0 0       0 if (defined($content{amount})) {
249 0         0 $amount = sprintf("%.2f", $content{amount});
250 0         0 $amount =~ s/\.//;
251             }
252              
253             my $check_number = $content{check_number} || "100" # make one up
254 0 0 0     0 if($content{account_number});
255              
256 0 0       0 my $terminalid = $content{login} if $type eq 'CC';
257 0 0       0 my $merchantid = $content{login} if $type eq 'ECHECK';
258              
259 0         0 my $country = country2code( $content{country}, LOCALE_CODE_ALPHA_3 );
260             $country = country_code2code( $content{country},
261 0 0       0 LOCALE_CODE_ALPHA_2,
262             LOCALE_CODE_ALPHA_3
263             )
264             unless $country;
265             $country = $content{country}
266 0 0       0 unless $country;
267 0 0       0 $country = uc($country) if $country;
268              
269             my $ship_country =
270 0         0 country2code( $content{ship_country}, LOCALE_CODE_ALPHA_3 );
271             $ship_country = country_code2code( $content{ship_country},
272 0 0       0 LOCALE_CODE_ALPHA_2,
273             LOCALE_CODE_ALPHA_3
274             )
275             unless $ship_country;
276             $ship_country = $content{ship_country}
277 0 0       0 unless $ship_country;
278 0 0       0 $ship_country = uc($ship_country) if $ship_country;
279              
280 0         0 tie my %ach, 'Tie::IxHash',
281             $self->revmap_fields(
282             #AccountType => 'account_type',
283             AccountNumber => 'account_number',
284             ABA => 'routing_code',
285             CheckNumber => \$check_number,
286             );
287              
288 0         0 tie my %industryinfo, 'Tie::IxHash',
289             $self->revmap_fields(
290             Type => 'IndustryInfo',
291             );
292              
293 0         0 tie my %shippingaddr, 'Tie::IxHash',
294             $self->revmap_fields(
295             Address => 'ship_address',
296             City => 'ship_city',
297             StateProv => 'ship_state',
298             Country => \$ship_country,
299             Phone => 'ship_phone',
300             );
301              
302 0 0 0     0 unless ( $type ne 'CC' || keys %shippingaddr ) {
303 0         0 tie %shippingaddr, 'Tie::IxHash',
304             $self->revmap_fields(
305             Address => 'address',
306             City => 'city',
307             StateProv => 'state',
308             Country => \$country,
309             Phone => 'phone',
310             );
311             }
312 0 0       0 delete $shippingaddr{Country} unless $shippingaddr{Country};
313              
314 0         0 tie my %shippinginfo, 'Tie::IxHash',
315             $self->revmap_fields(
316             CustomerPO => 'CustomerPO',
317             ShippingMethod => 'ShippingMethod',
318             ShippingName => 'ship_name',
319             ShippingAddr => \%shippingaddr,
320             );
321              
322 0         0 tie my %req, 'Tie::IxHash',
323             $self->revmap_fields(
324             TransactionType => 'TransactionType',
325             TerminalID => 'login',
326             # TerminalID => \$terminalid,
327             # MerchantID => \$merchantid,
328             TransactionID => \$transaction_id,
329             RoutingCode => 'RoutingCode',
330             Approval => 'authorization',
331             BatchID => 'BatchID',
332             Origin => 'Origin',
333             Password => 'password',
334             OrderNumber => 'invoice_number',
335             CardNum => 'card_number',
336             CVV2 => 'cvv2',
337             Issue => 'issue_number',
338             CardExpMonth => \$cardexpmonth,
339             CardExpYear => \$cardexpyear,
340             CardStartMonth => \$cardstartmonth,
341             CardStartYear => \$cardstartyear,
342             Track1 => 'track1',
343             Track2 => 'track2',
344             ACH => \%ach,
345             CardName => 'name',
346             DispositionType => 'DispositionType',
347             TotalAmount => \$amount,
348             FeeAmount => 'FeeAmount',
349             TaxAmount => 'TaxAmount',
350             BillingAddress => 'address',
351             BillingCity => 'city',
352             BillingStateProv => 'state',
353             BillingPostalCode => 'zip',
354             BillingCountry => \$country,
355             BillingPhone => 'phone',
356             Email => 'email',
357             UserIPAddr => 'customer_ip',
358             UserHost => 'UserHost',
359             UDField1 => 'UDField1',
360             UDField2 => 'UDField2',
361             UDField3 => 'UDField3',
362             ActionCode => 'ActionCode',
363             IndustryInfo => \%industryinfo,
364             ShippingInfo => \%shippinginfo,
365             );
366 0 0       0 delete $req{BillingCountry} unless $req{BillingCountry};
367              
368 0         0 my $post_data;
369 0         0 my $writer = new XML::Writer( OUTPUT => \$post_data,
370             DATA_MODE => 1,
371             DATA_INDENT => 1,
372             ENCODING => 'us-ascii',
373             );
374 0         0 $writer->xmlDecl();
375 0         0 $writer->startTag('JetPay');
376 0         0 foreach ( keys ( %req ) ) {
377 0         0 $self->_xmlwrite($writer, $_, $req{$_});
378             }
379 0         0 $writer->endTag('JetPay');
380 0         0 $writer->end();
381              
382 0 0       0 warn "$post_data\n" if $DEBUG;
383              
384 0         0 my ($page,$server_response,%headers) = $self->https_post($post_data);
385              
386 0 0       0 warn "$page\n" if $DEBUG;
387              
388 0         0 my $response = {};
389 0 0       0 if ($server_response =~ /^200/){
390 0         0 $response = XMLin($page);
391 0 0 0     0 if ( exists($response->{ActionCode}) && !exists($response->{ErrMsg})) {
392 0         0 $self->error_message($response->{ResponseText});
393             }else{
394 0         0 $self->error_message($response->{Errmsg});
395             }
396             # }else{
397             # $self->error_message("Server Failed");
398             }
399              
400 0   0     0 $self->result_code($response->{ActionCode} || '');
401 0   0     0 $self->order_number($response->{TransactionID} || '');
402 0   0     0 $self->authorization($response->{Approval} || '');
403 0   0     0 $self->cvv2_response($response->{CVV2} || '');
404 0   0     0 $self->avs_code($response->{AVS} || '');
405              
406 0 0       0 $self->is_success($self->result_code() eq '000' ? 1 : 0);
407              
408 0 0       0 unless ($self->is_success()) {
409 0 0       0 unless ( $self->error_message() ) { #additional logging information
410             $self->error_message(
411             "(HTTPS response: $server_response) ".
412             "(HTTPS headers: ".
413 0         0 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
  0         0  
414             "(Raw HTTPS content: $page)"
415             );
416             }
417             }
418              
419             }
420              
421             sub _error_response {
422 0     0   0 my ($self, $error_message) = (shift, shift);
423 0         0 $self->result_code('');
424 0         0 $self->order_number('');
425 0         0 $self->authorization('');
426 0         0 $self->cvv2_response('');
427 0         0 $self->avs_code('');
428 0         0 $self->is_success( 0);
429 0         0 $self->error_message($error_message);
430             }
431              
432             sub _xmlwrite {
433 166     166   461 my ($self, $writer, $item, $value) = @_;
434 166         1297 $writer->startTag($item);
435 166 100       6435 if ( ref( $value ) eq 'HASH' ) {
436 15         66 foreach ( keys ( %$value ) ) {
437 39         910 $self->_xmlwrite($writer, $_, $value->{$_});
438             }
439             }else{
440 151         315 $writer->characters($value);
441             }
442 166         3403 $writer->endTag($item);
443             }
444              
445             1;
446              
447             __END__