File Coverage

blib/lib/Business/OnlinePayment/eSelectPlus.pm
Criterion Covered Total %
statement 38 109 34.8
branch 1 50 2.0
condition 2 27 7.4
subroutine 11 12 91.6
pod 1 5 20.0
total 53 203 26.1


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::eSelectPlus;
2              
3 10     10   1596498 use strict;
  10         24  
  10         372  
4 10     10   58 use Carp;
  10         18  
  10         706  
5 10     10   11587 use Tie::IxHash;
  10         2537229  
  10         372  
6 10     10   956 use Business::OnlinePayment 3;
  10         3365  
  10         298  
7 10     10   9607 use Business::OnlinePayment::HTTPS 0.03;
  10         99344  
  10         493  
8 10     10   94 use vars qw($VERSION $DEBUG @ISA);
  10         20  
  10         15450  
9              
10             @ISA = qw(Business::OnlinePayment::HTTPS);
11             $VERSION = '0.07';
12             $DEBUG = 0;
13              
14             sub set_defaults {
15 13     13 0 6748 my $self = shift;
16              
17             #USD
18             #$self->server('esplusqa.moneris.com'); # development
19 13         478 $self->server('esplus.moneris.com'); # production
20 13         549 $self->path('/gateway_us/servlet/MpgRequest');
21              
22             ##CAD
23             ##$self->server('esqa.moneris.com'); # development
24             #$self->server('www3.moneris.com'); # production
25             #$self->path('/gateway2/servlet/MpgRequest');
26              
27 13         476 $self->port('443');
28              
29 13         195 $self->build_subs(qw( order_number avs_code ));
30             # avs_code order_type md5 cvv2_response cavv_response
31             }
32              
33             sub submit {
34 0     0 1   my($self) = @_;
35              
36 0 0 0       if ( defined( $self->{_content}{'currency'} )
37             && $self->{_content}{'currency'} eq 'CAD' ) {
38 0           $self->server('www3.moneris.com');
39 0           $self->path('/gateway2/servlet/MpgRequest');
40             } else { #sorry, default to USD
41 0           $self->server('esplus.moneris.com');
42 0           $self->path('/gateway_us/servlet/MpgRequest');
43             }
44              
45 0 0         if ($self->test_transaction) {
46 0 0 0       if ( defined( $self->{_content}{'currency'} )
47             && $self->{_content}{'currency'} eq 'CAD' ) {
48 0           $self->server('esqa.moneris.com');
49 0           $self->{_content}{'login'} = 'store2'; # store[123]
50 0           $self->{_content}{'password'} = 'yesguy';
51             } else { #sorry, default to USD
52 0           $self->server('esplusqa.moneris.com');
53 0           $self->{_content}{'login'} = 'monusqa002'; # monusqa00[123]
54 0           $self->{_content}{'password'} = 'qatoken';
55             }
56             }
57              
58 0           my %cust_id = ( 'invoice_number' => 'cust_id' );
59              
60 0           my $invoice_number = $self->{_content}{invoice_number};
61              
62             # BOP field => eSelectPlus field
63             #$self->map_fields();
64 0           $self->remap_fields(
65             # => 'order_type',
66             # => 'transaction_type',
67             #login => 'store_id',
68             #password => 'api_token',
69             #authorization =>
70             #customer_ip =>
71             #name =>
72             #first_name =>
73             #last_name =>
74             #company =>
75             #address =>
76             #city =>
77             #state =>
78             #zip =>
79             #country =>
80             phone =>
81             #fax =>
82             email =>
83             card_number => 'pan',
84             #expiration =>
85             # => 'expdate',
86              
87             'amount' => 'amount',
88             customer_id => 'cust_id',
89             order_number => 'order_id', # must be unique number
90             authorization => 'txn_number' # reference to previous trans
91              
92             #cvv2 =>
93             );
94              
95 0           my $action = $self->{_content}{'action'};
96 0 0         if ( $self->{_content}{'action'} =~ /^\s*normal\s*authorization\s*$/i ) {
    0          
    0          
    0          
    0          
97 0           $action = 'purchase';
98             } elsif ( $self->{_content}{'action'} =~ /^\s*authorization\s*only\s*$/i ) {
99 0           $action = 'preauth';
100             } elsif ( $self->{_content}{'action'} =~ /^\s*post\s*authorization\s*$/i ) {
101 0           $action = 'completion';
102             } elsif ( $self->{_content}{'action'} =~ /^\s*void\s*$/i ) {
103 0           $action = 'purchasecorrection';
104             } elsif ( $self->{_content}{'action'} =~ /^\s*credit\s*$/i ) {
105 0 0         if ( $self->{_content}{'authorization'} ) {
106 0           $action = 'refund';
107             } else {
108 0           $action = 'ind_refund';
109             }
110             }
111              
112 0 0         if ( $action =~ /^(purchase|preauth|ind_refund)$/ ) {
    0          
113              
114 0           $self->required_fields(qw(
115             login password amount card_number expiration
116             ));
117              
118             #cardexpiremonth & cardexpireyear
119 0 0         $self->{_content}{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
120             or croak "unparsable expiration ". $self->{_content}{expiration};
121 0           my( $month, $year ) = ( $1, $2 );
122 0 0         $month = '0'. $month if $month =~ /^\d$/;
123 0           $self->{_content}{expdate} = $year.$month;
124              
125 0           $self->generate_order_id;
126              
127 0   0       $self->{_content}{order_id} .= '-'. ($invoice_number || 0);
128              
129 0           $self->{_content}{amount} = sprintf('%.2f', $self->{_content}{amount} );
130              
131             } elsif ( $action =~ /^(completion|purchasecorrection|refund)$/ ) {
132              
133 0           $self->required_fields(qw(
134             login password order_number authorization
135             ));
136              
137 0 0         if ( $action eq 'completion' ) {
    0          
138 0           $self->{_content}{comp_amount} = delete $self->{_content}{amount};
139             } elsif ( $action eq 'purchasecorrection' ) {
140 0           delete $self->{_content}{amount};
141             #} elsif ( $action eq 'refund' ) {
142             }
143              
144             }
145              
146             # E-Commerce Indicator (see eSelectPlus docs)
147 0   0       $self->{_content}{'crypt_type'} ||= 7;
148              
149 0 0 0       $action = "us_$action"
150             unless defined( $self->{_content}{'currency'} )
151             && $self->{_content}{'currency'} eq 'CAD';
152              
153             #no, values aren't escaped for XML. their "mpgClasses.pl" example doesn't
154             #appear to do so, i dunno
155 0           tie my %fields, 'Tie::IxHash', $self->get_fields( $self->fields );
156 0           my $post_data =
157             ''.
158             ''.
159             ''. $self->{_content}{'login'}. ''.
160             ''. $self->{_content}{'password'}. ''.
161             "<$action>".
162             join('', map "<$_>$fields{$_}", keys %fields ).
163             "".
164             '';
165              
166 0 0         warn "POSTING: ".$post_data if $DEBUG > 1;
167              
168 0           my( $page, $response, @reply_headers) = $self->https_post( $post_data );
169              
170 0 0         if ($DEBUG > 1) {
171 0           my %reply_headers = @reply_headers;
172 0           warn join('', map { " $_ => $reply_headers{$_}\n" } keys %reply_headers)
  0            
173             }
174              
175 0 0         if ($response !~ /^200/) {
176             # Connection error
177 0           $response =~ s/[\r\n]+/ /g; # ensure single line
178 0           $self->is_success(0);
179 0   0       my $diag_message = $response || "connection error";
180 0           die $diag_message;
181             }
182              
183             # avs_code - eSELECTplus_Perl_IG.pdf Appendix F
184 0           my %avsTable = ('A' => 'A',
185             'B' => 'A',
186             'C' => 'E',
187             'D' => 'Y',
188             'G' => '',
189             'I' => '',
190             'M' => 'Y',
191             'N' => 'N',
192             'P' => 'Z',
193             'R' => 'R',
194             'S' => '',
195             'U' => 'E',
196             'W' => 'Z',
197             'X' => 'Y',
198             'Y' => 'Y',
199             'Z' => 'Z',
200             );
201 0           my $AvsResultCode = $self->GetXMLProp($page, 'AvsResultCode');
202 0 0 0       $self->avs_code( defined($AvsResultCode) && exists $avsTable{$AvsResultCode}
203             ? $avsTable{$AvsResultCode}
204             : $AvsResultCode
205             );
206              
207             #md5 cvv2_response cavv_response ...?
208              
209 0           $self->server_response($page);
210              
211 0           my $result = $self->GetXMLProp($page, 'ResponseCode');
212              
213 0 0         die "gateway error: ". $self->GetXMLProp( $page, 'Message' )
214             if $result =~ /^null$/i;
215              
216             # Original order_id supplied to the gateway
217 0           $self->order_number($self->GetXMLProp($page, 'ReceiptId'));
218              
219             # We (Whizman & DonorWare) do not have enough info about "ISO"
220             # response codes to make use of them.
221             # There may be good reasons why the ISO codes could be preferable,
222             # but we would need more information. For now, the ResponseCode.
223             # $self->result_code( $self->GetXMLProp( $page, 'ISO' ) );
224 0           $self->result_code( $result );
225              
226 0 0 0       if ( $result =~ /^\d+$/ && $result < 50 ) {
    0          
227 0           $self->is_success(1);
228 0           $self->authorization($self->GetXMLProp($page, 'TransID'));
229             } elsif ( $result =~ /^\d+$/ ) {
230 0           $self->is_success(0);
231 0           my $tmp_msg = $self->GetXMLProp( $page, 'Message' );
232 0           $tmp_msg =~ s/\s{2,}//g;
233 0           $tmp_msg =~ s/[\*\=]//g;
234 0           $self->error_message( $tmp_msg );
235             } else {
236 0 0         die "unparsable response received from gateway (response $result)".
237             ( $DEBUG ? ": $page" : '' );
238             }
239              
240             }
241              
242 10     10   74 use vars qw(@oidset);
  10         20  
  10         3078  
243             @oidset = ( 'A'..'Z', '0'..'9' );
244             sub generate_order_id {
245 9     9 0 15 my $self = shift;
246             #generate an order_id if order_number not passed
247 9 50 33     98 unless ( exists ($self->{_content}{order_id})
      33        
248             && defined($self->{_content}{order_id})
249             && length ($self->{_content}{order_id})
250             ) {
251 207         905 $self->{_content}{'order_id'} =
252 9         23 join('', map { $oidset[int(rand(scalar(@oidset)))] } (1..23) );
253             }
254             }
255              
256             sub fields {
257 13     13 0 682 my $self = shift;
258              
259             #order is important to this processor
260 13         189 qw(
261             order_id
262             cust_id
263             amount
264             comp_amount
265             txn_number
266             pan
267             expdate
268             crypt_type
269             cavv
270             );
271             }
272              
273             sub GetXMLProp {
274 52     52 0 124 my( $self, $raw, $prop ) = @_;
275 52         197 local $^W=0;
276              
277 52         91 my $data;
278 52         1240 ($data) = $raw =~ m"<$prop>(.*?)"gsi;
279             #$data =~ s/<.*?>/ /gs;
280 52         207 chomp $data;
281 52         1194 return $data;
282             }
283              
284             1;
285              
286             __END__