Fix required fields in USAePay refunds, RT#8819
[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.01',
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;
83
84   $content{'md5hash'} = md5_hex(join(':', map "$content{$_}", qw(action password amount invoice_number md5key))) if defined $content{'password'};
85
86   $self->content(%content);
87 }
88
89 sub submit {
90     my($self) = @_;
91
92     $self->map_fields();
93
94     $self->remap_fields(
95       login            => 'UMkey',
96       md5key           => 'UMmd5key',
97       md5hash          => 'UMmd5hash',
98       card_number      => 'UMcard',
99       expiration       => 'UMexpir',
100       amount           => 'UMamount',
101       invoice_number   => 'UMinvoice',
102       description      => 'UMdescription',
103       customer_id      => 'UMcustid',
104       cvv2             => 'UMcvv2',
105       email            => 'UMcustemail',
106       name             => 'UMname',
107       address          => 'UMstreet',
108       zip              => 'UMzip',
109       customer_ip      => 'UMip',
110       order_number     => 'UMrefNum',
111       authorization    => 'UMauthCode',
112       routing_code     => 'UMrouting',
113       account_number   => 'UMaccount',
114       customer_ssn     => 'UMssn',
115       action           => 'UMcommand',
116     );
117     my %content = $self->content;
118     if ( $DEBUG ) {
119       warn "content:$_ => $content{$_}\n" foreach keys %content;
120     }
121
122     my @required_fields = qw(type action login);
123
124     my $action = $self->{_content}->{action};
125     if ($self->transaction_type() eq 'CC' ) {
126       if ($action eq 'void' or $action eq 'capture') {
127         push @required_fields, qw/order_number/;
128       }
129       else {
130         # sale, authonly, credit, postauth
131         push @required_fields, qw/card_number expiration amount address zip/;
132         if ($action eq 'postauth') {
133           push @required_fields, qw/authorization/;
134         }
135       }
136     } 
137     elsif ($self->transaction_type() eq 'ECHECK' ) {
138       push @required_fields, qw/routing_code account_number amount name customer_ssn/;
139     } else {
140       croak("USAePay can't handle transaction type: ".
141             $self->transaction_type());
142     }
143
144     $self->required_fields(@required_fields);
145
146     my %post_data = $self->get_fields( map "$_", qw(
147       UMcommand UMkey UMmd5hash UMmd5key UMauthCode UMrefNum UMcard UMexpir
148       UMrouting UMaccount UMamount Umtax UMnontaxable UMtip UMshipping
149       UMdiscount UMsubtotal UMcustid UMinvoice UMorderid UMponum UMdescription
150       UMcvv2 UMcustemail UMcustreceipt UMname UMStreet UMzip UMssn UMdlnum
151       UMdlstate UMclerk UMterminal UMtable UMip UMsoftware UMredir
152       UMredirApproved UMredirDeclined UMechofields UMtestmode
153     ) );
154     # test_transaction(0): normal mode
155     #                  1 : test mode (validates formatting only)
156     #                  2 : use sandbox server
157     #                  3 : test mode on sandbox server
158     $self->server('sandbox.usaepay.com') if ( $self->test_transaction & 2 );
159     $post_data{'UMtestmode'} = ($self->test_transaction() & 1) ? 1 : 0;
160     $post_data{'UMsoftware'} = __PACKAGE__. " $VERSION";
161     if ( $DEBUG ) {
162       warn "post_data:$_ => $post_data{$_}\n" foreach keys %post_data;
163     }
164
165     my($page,$server_response) = $self->https_post(%post_data);
166     if ( $DEBUG ) {
167       warn "response page: $page\n";
168     }
169
170     my $response;
171     if ($server_response =~ /200/){
172       $response = {map { split '=', $_, 2 } split '&', $page};
173     }else{
174       $response->{UMstatus} = 'Error';
175       $response->{UMerror} = $server_response;
176     }
177
178     $response->{$_} =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg
179       foreach keys %$response;
180
181     if ( $DEBUG ) {
182       warn "response:$_ => $response->{$_}\n" foreach keys %$response;
183     }
184
185     if ( $response->{UMstatus} =~ /^Approved/ ) {
186       $self->is_success(1);
187       $self->authorization($response->{UMauthCode});
188     } else {
189       $self->is_success(0);
190     }
191     $self->order_number($response->{UMrefNum});
192     $self->avs_code($response->{UMavsResultCode});
193     $self->cvv2_response($response->{UMcvv2ResultCode});
194     $self->result_code($response->{UMresult});
195     $self->error_message($response->{UMerror});
196     $self->server_response($response);
197 }
198
199 1;
200 __END__
201
202 =head1 NAME
203
204 Business::OnlinePayment::USAePay - USA ePay backend for Business::OnlinePayment
205
206 =head1 SYNOPSIS
207
208   use Business::OnlinePayment;
209
210   my $tx = new Business::OnlinePayment("USAePay");
211   $tx->content(
212       login          => 'igztOatyqbpd1wsxijl4xnxjodldwdxR', #USA ePay source key
213       action         => 'Normal Authorization',
214       description    => 'Business::OnlinePayment test',
215       amount         => '49.95',
216       invoice_number => '100100',
217       name           => 'Tofu Beast',
218       card_number    => '46464646464646',
219       expiration     => '11/08',
220       address        => '1234 Bean Curd Lane, San Francisco',
221       zip            => '94102',
222   );
223   $tx->submit();
224
225   if($tx->is_success()) {
226       print "Card processed successfully: ".$tx->authorization."\n";
227   } else {
228       print "Card was rejected: ".$tx->error_message."\n";
229   }
230
231 =head1 DESCRIPTION
232
233 For detailed information see L<Business::OnlinePayment>.
234
235 =head1 NOTE
236
237 =head1 COMPATIBILITY
238
239 This module implements USAePay's CGI Gateway API v2.9.5.  See
240 http://www.usaepay.com/topics/api.html for details.
241
242 =head1 AUTHOR
243
244 Jeff Finucane <jeff@cmh.net>
245
246 =head1 SEE ALSO
247
248 perl(1). L<Business::OnlinePayment>.
249
250 =cut
251