initial import
[Business-OnlinePayment-eSelectPlus.git] / eSelectPlus.pm
1 package Business::OnlinePayment::eSelectPlus;\r
2 \r
3 use strict;\r
4 use Carp;\r
5 use Tie::IxHash;\r
6 use Business::OnlinePayment 3;\r
7 use Business::OnlinePayment::HTTPS;\r
8 use vars qw($VERSION $DEBUG @ISA);\r
9 \r
10 @ISA = qw(Business::OnlinePayment::HTTPS);\r
11 $VERSION = '0.01';\r
12 $DEBUG = 0;\r
13 \r
14 sub set_defaults {\r
15     my $self = shift;\r
16 \r
17     $self->server('esqa.moneris.com');\r
18     $self->port('443');\r
19     $self->path('/gateway2/servlet/MpgRequest');\r
20 \r
21     $self->build_subs(qw( order_number ));\r
22     # avs_code order_type md5 cvv2_response cavv_response\r
23 }\r
24 \r
25 sub submit {\r
26     my($self) = @_;\r
27 \r
28     #$self->map_fields();\r
29     $self->remap_fields(\r
30         #                => 'order_type',\r
31         #                => 'transaction_type',\r
32         #login            => 'store_id',\r
33         #password         => 'api_token',\r
34         #authorization   => \r
35         #customer_ip     =>\r
36         #name            =>\r
37         #first_name      =>\r
38         #last_name       =>\r
39         #company         =>\r
40         #address         => \r
41         #city            => \r
42         #state           => \r
43         #zip             => \r
44         #country         =>\r
45         phone            => \r
46         #fax             =>\r
47         email            =>\r
48         card_number      => 'pan',\r
49         #expiration        =>\r
50         #                => 'expdate',\r
51 \r
52         'amount'         => 'amount',\r
53         #invoice_number  =>\r
54         #customer_id     =>\r
55         order_number     => 'order_id',\r
56         authorization    => 'txn_number'\r
57 \r
58         #cvv2              =>\r
59     );\r
60 \r
61     my $action = $self->{_content}{'action'};\r
62     if ( $self->{_content}{'action'} =~ /^\s*normal\s*authorization\s*$/i ) {\r
63       $action = 'purchase';\r
64     } elsif ( $self->{_content}{'action'} =~ /^\s*authorization\s*only\s*$/i ) {\r
65       $action = 'preauth';\r
66     } elsif ( $self->{_content}{'action'} =~ /^\s*post\s*authorization\s*$/i ) {\r
67       $action = 'completion';\r
68     } elsif ( $self->{_content}{'action'} =~ /^\s*void\s*$/i ) {\r
69       $action = 'void';\r
70     } elsif ( $self->{_content}{'action'} =~ /^\s*credit\s*$/i ) {\r
71       if ( $self->{_content}{'authorization'} ) {\r
72         $action = 'refund';\r
73       } else {\r
74         $action = 'ind_refund';\r
75       }\r
76     }\r
77 \r
78     if ( $action =~ /^(purchase|preauth|ind_refund)$/ ) {\r
79 \r
80       $self->required_fields(\r
81         qw( login password amount card_number expiration )\r
82       );\r
83 \r
84       #cardexpiremonth & cardexpireyear\r
85       $self->{_content}{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/\r
86         or croak "unparsable expiration ". $self->{_content}{expiration};\r
87       my( $month, $year ) = ( $1, $2 );\r
88       $month = '0'. $month if $month =~ /^\d$/;\r
89       $self->{_content}{expdate} = $year.$month;\r
90 \r
91       $self->generate_order_id;\r
92 \r
93       $self->{_content}{amount} = sprintf('%.2f', $self->{_content}{amount} );\r
94 \r
95     } elsif ( $action eq 'completion' || $action eq 'void' ) {\r
96 \r
97       $self->required_fields( qw( login password order_number authorization ) );\r
98 \r
99     } elsif ( $action eq 'refund' ) {\r
100 \r
101       $self->required_fields(\r
102         qw( login passowrd order_number authorization )\r
103       );\r
104 \r
105     }\r
106 \r
107     $self->{_content}{'crypt_type'} ||= 7;\r
108 \r
109     #no, values aren't escaped for XML.  their "mpgClasses.pl" example doesn't\r
110     #appear to do so, i dunno\r
111     tie my %fields, 'Tie::IxHash', $self->get_fields( $self->fields );\r
112     my $post_data =\r
113       '<?xml version="1.0"?>'.\r
114       '<request>'.\r
115       '<store_id>'.  $self->{_content}{'login'}. '</store_id>'.\r
116       '<api_token>'. $self->{_content}{'password'}. '</api_token>'.\r
117       "<$action>".\r
118       join('', map "<$_>$fields{$_}</$_>", keys %fields ).\r
119       "</$action>".\r
120       '</request>';\r
121 \r
122     warn $post_data if $DEBUG > 1;\r
123 \r
124     my( $page, $response, @reply_headers) = $self->https_post( $post_data );\r
125 \r
126     #my %reply_headers = @reply_headers;\r
127     #warn join('', map { "  $_ => $reply_headers{$_}\n" } keys %reply_headers )\r
128     #  if $DEBUG;\r
129 \r
130     #XXX check $response and die if not 200?\r
131 \r
132     #   avs_code\r
133     #   is_success\r
134     #   result_code\r
135     #   authorization\r
136     #md5 cvv2_response cavv_response ...?\r
137 \r
138     $self->server_response($page);\r
139 \r
140     my $result = $self->GetXMLProp($page, 'ResponseCode');\r
141 \r
142     die "gateway error: ". $self->GetXMLProp( $page, 'Message' )\r
143       if $result =~ /^null$/i;\r
144 \r
145     if ( $result =~ /^\d+$/ && $result < 50 ) {\r
146       $self->is_success(1);\r
147       $self->result_code( $self->GetXMLProp( $page, 'ISO' ) );\r
148       $self->authorization( $self->GetXMLProp( $page, 'Txn_number' ) );\r
149       $self->order_number( $self->GetXMLProp( $page, 'order_id') );\r
150     } elsif ( $result =~ /^\d+$/ ) {\r
151       $self->is_success(0);\r
152       $self->error_message( $self->GetXMLProp( $page, 'Message' ) );\r
153     } else {\r
154       die "unparsable response received from gateway (response $result)".\r
155           ( $DEBUG ? ": $page" : '' );\r
156     }\r
157 \r
158 }\r
159 \r
160 use vars qw(@oidset);\r
161 @oidset = ( 'A'..'Z', '0'..'9' );\r
162 sub generate_order_id {\r
163     my $self = shift;\r
164     #generate an order_id if order_number not passed\r
165     unless (    exists ($self->{_content}{order_id})\r
166              && defined($self->{_content}{order_id})\r
167              && length ($self->{_content}{order_id})\r
168            ) {\r
169       $self->{_content}{'order_id'} =\r
170         join('', map { $oidset[int(rand(scalar(@oidset)))] } (1..23) );\r
171     }\r
172 }\r
173 \r
174 sub fields {\r
175         my $self = shift;\r
176 \r
177         #order is important to this processor\r
178         qw(\r
179           order_id\r
180           cust_id\r
181           amount\r
182           comp_amount\r
183           txn_number\r
184           pan\r
185           expdate\r
186           crypt_type\r
187           cavv\r
188         );\r
189 }\r
190 \r
191 sub GetXMLProp {\r
192         my( $self, $raw, $prop ) = @_;\r
193         local $^W=0;\r
194 \r
195         my $data;\r
196         ($data) = $raw =~ m"<$prop>(.*?)</$prop>"gsi;\r
197         #$data =~ s/<.*?>/ /gs;\r
198         chomp $data;\r
199         return $data;\r
200 }\r
201 \r
202 1;\r
203 \r
204 __END__\r
205 \r
206 =head1 NAME\r
207 \r
208 Business::OnlinePayment::eSelectPlus - Moneris eSelect Plus backend module for Business::OnlinePayment\r
209 \r
210 =head1 SYNOPSIS\r
211 \r
212   use Business::OnlinePayment;\r
213 \r
214   ####\r
215   # One step transaction, the simple case.\r
216   ####\r
217 \r
218   my $tx = new Business::OnlinePayment("eSelectPlus");\r
219   $tx->content(\r
220       type           => 'VISA',\r
221       login          => 'eSelect Store ID,\r
222       password       => 'eSelect API Token',\r
223       action         => 'Normal Authorization',\r
224       description    => 'Business::OnlinePayment test',\r
225       amount         => '49.95',\r
226       name           => 'Tofu Beast',\r
227       address        => '123 Anystreet',\r
228       city           => 'Anywhere',\r
229       state          => 'UT',\r
230       zip            => '84058',\r
231       phone          => '420-867-5309',\r
232       email          => 'tofu.beast@example.com',\r
233       card_number    => '4005550000000019',\r
234       expiration     => '08/06',\r
235       cvv2           => '1234', #optional\r
236   );\r
237   $tx->submit();\r
238 \r
239   if($tx->is_success()) {\r
240       print "Card processed successfully: ".$tx->authorization."\n";\r
241   } else {\r
242       print "Card was rejected: ".$tx->error_message."\n";\r
243   }\r
244 \r
245 =head1 SUPPORTED TRANSACTION TYPES\r
246 \r
247 =head2 CC, Visa, MasterCard, American Express, Discover\r
248 \r
249 Content required: type, login, password, action, amount, card_number, expiration.\r
250 \r
251 =head1 PREREQUISITES\r
252 \r
253   URI::Escape\r
254   Tie::IxHash\r
255 \r
256   Net::SSLeay _or_ ( Crypt::SSLeay and LWP )\r
257 \r
258 =head1 DESCRIPTION\r
259 \r
260 For detailed information see L<Business::OnlinePayment>.\r
261 \r
262 =head1 NOTE\r
263 \r
264 =head1 AUTHOR\r
265 \r
266 Ivan Kohler <ivan-eselectplus@420.am>\r
267 \r
268 =head1 SEE ALSO\r
269 \r
270 perl(1). L<Business::OnlinePayment>.\r
271 \r
272 =cut\r
273 \r