test fixes
[Business-OnlinePayment-USAePay.git] / USAePay.pm
1 package Business::OnlinePayment::USAePay;
2
3 use strict;
4 use Carp;
5 use Business::OnlinePayment 3;
6 use Business::OnlinePayment::HTTPS;
7 use Digest::MD5 qw(md5_hex);
8 use URI::Escape;
9 use vars qw($VERSION @ISA $DEBUG);
10
11 @ISA = qw(Business::OnlinePayment::HTTPS);
12 $VERSION = '0.02';
13
14 $DEBUG = 0;
15
16 sub _info {
17   {
18     'info_compat'           => '0.02',
19     'gateway_name'          => 'USAePay',
20     'gateway_url'           => 'http://www.usaepay.com',
21     'module_version'        => $VERSION,
22     'supported_types'       => [ 'CC', 'ECHECK' ],
23     'supported_actions'     => {
24                                   CC => [
25                                     'Normal Authorization',
26                                     'Authorization Only',
27                                     'Post Authorization',
28                                     'Credit',
29                                     'Void',
30                                     ],
31                                   ECHECK => [
32                                     'Normal Authorization',
33                                     'Credit',
34                                     ],
35     },
36   };
37 }
38
39 my $default_path = '/gate.php';
40 my $default_cert_path = '/secure/gate.php';
41
42 sub set_defaults {
43     my $self = shift;
44     $self->server('www.usaepay.com');
45     $self->port('443');
46     $self->path($default_path);
47     $self->build_subs(qw(avs_code cvv2_response));
48
49 }
50
51 sub map_fields {
52   my($self) = shift;
53
54   my %content = $self->content();
55
56   my %types = ('visa'             => 'CC',
57                'mastercard'       => 'CC',
58                'american express' => 'CC',
59                'discover'         => 'CC',
60                'check'            => 'ECHECK',
61               );
62   $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
63   $self->transaction_type($content{'type'});
64
65   my %actions;
66   my %cc_actions = ('normal authorization' => 'sale',
67                     'authorization only'   => 'authonly',
68                     'post authorization'   => 'postauth',
69                     'credit'               => 'credit',
70                     'void'                 => 'void',
71                    );
72   my %ec_actions = ('normal authorization' => 'check',
73                     'credit'               => 'checkcredit',
74                    );
75   if ($content{'type'} eq 'CC') {
76     (%actions) = (%cc_actions);
77   }elsif ($content{'type'} eq 'ECHECK') {
78     (%actions) = (%ec_actions);
79   }
80   $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
81                  
82   $content{'expiration'} =~ s/\D//g if exists $content{'expiration'};
83
84   $content{'md5hash'} =
85       md5_hex( join(':', map { defined($content{$_}) ? $content{$_} : '' }
86                              qw(action password amount invoice_number md5key)))
87     if defined $content{'password'};
88
89   $self->content(%content);
90 }
91
92 sub submit {
93     my($self) = @_;
94
95     $self->map_fields();
96
97     $self->remap_fields(
98       login            => 'UMkey',
99       md5key           => 'UMmd5key',
100       md5hash          => 'UMmd5hash',
101       card_number      => 'UMcard',
102       expiration       => 'UMexpir',
103       amount           => 'UMamount',
104       invoice_number   => 'UMinvoice',
105       description      => 'UMdescription',
106       customer_id      => 'UMcustid',
107       cvv2             => 'UMcvv2',
108       email            => 'UMcustemail',
109       name             => 'UMname',
110       address          => 'UMstreet',
111       zip              => 'UMzip',
112       customer_ip      => 'UMip',
113       order_number     => 'UMrefNum',
114       authorization    => 'UMauthCode',
115       routing_code     => 'UMrouting',
116       account_number   => 'UMaccount',
117       customer_ssn     => 'UMssn',
118       action           => 'UMcommand',
119     );
120     my %content = $self->content;
121     if ( $DEBUG ) {
122       warn "content:$_ => $content{$_}\n" foreach keys %content;
123     }
124
125     my @required_fields = qw(type action login);
126
127     my $action = $self->{_content}->{action};
128     if ($self->transaction_type() eq 'CC' ) {
129       if ($action eq 'void' or $action eq 'capture') {
130         push @required_fields, qw/order_number/;
131       }
132       else {
133         # sale, authonly, credit, postauth
134         push @required_fields, qw/card_number expiration amount address zip/;
135         if ($action eq 'postauth') {
136           push @required_fields, qw/authorization/;
137         }
138       }
139     } 
140     elsif ($self->transaction_type() eq 'ECHECK' ) {
141       push @required_fields, qw/routing_code account_number amount name customer_ssn/;
142     } else {
143       croak("USAePay can't handle transaction type: ".
144             $self->transaction_type());
145     }
146
147     $self->required_fields(@required_fields);
148
149     my %post_data = $self->get_fields( map "$_", qw(
150       UMcommand UMkey UMmd5hash UMmd5key UMauthCode UMrefNum UMcard UMexpir
151       UMrouting UMaccount UMamount Umtax UMnontaxable UMtip UMshipping
152       UMdiscount UMsubtotal UMcustid UMinvoice UMorderid UMponum UMdescription
153       UMcvv2 UMcustemail UMcustreceipt UMname UMStreet UMzip UMssn UMdlnum
154       UMdlstate UMclerk UMterminal UMtable UMip UMsoftware UMredir
155       UMredirApproved UMredirDeclined UMechofields UMtestmode
156     ) );
157
158     # test_transaction(0): normal mode
159     #                  1 : test mode (validates formatting only)
160     #                  2 : use sandbox server
161     #                  3 : test mode on sandbox server
162     my $test = $self->test_transaction || 0;
163     $self->server('sandbox.usaepay.com') if ( $test & 2 );
164     $post_data{'UMtestmode'} = ($test & 1) ? 1 : 0;
165
166     $post_data{'UMsoftware'} = __PACKAGE__. " $VERSION";
167     if ( $DEBUG ) {
168       warn "post_data:$_ => $post_data{$_}\n" foreach keys %post_data;
169     }
170
171     my($page,$server_response) = $self->https_post(%post_data);
172     if ( $DEBUG ) {
173       warn "response page: $page\n";
174     }
175
176     my $response;
177     if ($server_response =~ /200/){
178       $response = {map { split '=', $_, 2 } split '&', $page};
179     }else{
180       $response->{UMstatus} = 'Error';
181       $response->{UMerror} = $server_response;
182     }
183
184     $response->{$_} =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg
185       foreach keys %$response;
186
187     if ( $DEBUG ) {
188       warn "response:$_ => $response->{$_}\n" foreach keys %$response;
189     }
190
191     if ( $response->{UMstatus} =~ /^Approved/ ) {
192       $self->is_success(1);
193       $self->authorization($response->{UMauthCode});
194     } else {
195       $self->is_success(0);
196     }
197     $self->order_number($response->{UMrefNum});
198     $self->avs_code($response->{UMavsResultCode});
199     $self->cvv2_response($response->{UMcvv2ResultCode});
200     $self->result_code($response->{UMresult});
201     $self->error_message($response->{UMerror});
202     $self->server_response($response);
203 }
204
205 1;
206 __END__
207
208 =head1 NAME
209
210 Business::OnlinePayment::USAePay - USA ePay backend for Business::OnlinePayment
211
212 =head1 SYNOPSIS
213
214   use Business::OnlinePayment;
215
216   my $tx = new Business::OnlinePayment("USAePay");
217   $tx->content(
218       login          => 'igztOatyqbpd1wsxijl4xnxjodldwdxR', #USA ePay source key
219       action         => 'Normal Authorization',
220       description    => 'Business::OnlinePayment test',
221       amount         => '49.95',
222       invoice_number => '100100',
223       name           => 'Tofu Beast',
224       card_number    => '46464646464646',
225       expiration     => '11/08',
226       address        => '1234 Bean Curd Lane, San Francisco',
227       zip            => '94102',
228   );
229   $tx->submit();
230
231   if($tx->is_success()) {
232       print "Card processed successfully: ".$tx->authorization."\n";
233   } else {
234       print "Card was rejected: ".$tx->error_message."\n";
235   }
236
237 =head1 DESCRIPTION
238
239 For detailed information see L<Business::OnlinePayment>.
240
241 =head1 NOTE
242
243 =head1 COMPATIBILITY
244
245 This module implements USAePay's CGI Gateway API v2.9.5.  See
246 http://www.usaepay.com/topics/api.html for details.
247
248 =head1 AUTHOR
249
250 Original author: Jeff Finucane <jeff@cmh.net>
251
252 0.02 update and current maintainer: Ivan Kohler <ivan-usaepay@freeside.biz>
253
254 =head1 SEE ALSO
255
256 perl(1). L<Business::OnlinePayment>.
257
258 =cut
259