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