0.02 fixes for postauth/refund/void
[Business-OnlinePayment-Capstone.git] / lib / Business / OnlinePayment / Capstone.pm
1 package Business::OnlinePayment::Capstone;\r
2 \r
3 use strict;\r
4 use Carp;\r
5 #use Tie::IxHash;\r
6 use URI::Escape;\r
7 use Business::OnlinePayment 3;\r
8 use Business::OnlinePayment::HTTPS 0.03;\r
9 use vars qw($VERSION $DEBUG @ISA);\r
10 \r
11 @ISA = qw(Business::OnlinePayment::HTTPS);\r
12 $VERSION = '0.02';\r
13 $DEBUG = 0;\r
14 \r
15 sub set_defaults {\r
16     my $self = shift;\r
17 \r
18     $self->server('www.capstonepay.com');\r
19     $self->port('443');\r
20     $self->path('/cgi-bin/client/transaction.cgi');\r
21 \r
22     $self->build_subs(qw( order_number avs_code cvv2_response ));\r
23 }\r
24 \r
25 sub submit {\r
26     my($self) = @_;\r
27 \r
28     my $action = $self->{_content}{'action'};\r
29     if ( $self->{_content}{'action'} =~ /^\s*normal\s*authorization\s*$/i ) {\r
30       $action = 'authpostauth';\r
31     } elsif ( $self->{_content}{'action'} =~ /^\s*authorization\s*only\s*$/i ) {\r
32       $action = 'auth';\r
33     } elsif ( $self->{_content}{'action'} =~ /^\s*post\s*authorization\s*$/i ) {\r
34       $action = 'postauth';\r
35     } elsif ( $self->{_content}{'action'} =~ /^\s*void\s*$/i ) {\r
36       $action = 'void';\r
37     } elsif ( $self->{_content}{'action'} =~ /^\s*credit\s*$/i ) {\r
38       $action = 'return';\r
39     }\r
40 \r
41    #$self->map_fields();\r
42     $self->revmap_fields(\r
43         merchantid          =>'login',\r
44         account_password    => 'password',\r
45         action              => \$action,\r
46         amount              => 'amount',\r
47         name                => 'name',\r
48         address1            => 'address',\r
49         #address2\r
50         city                => 'city',\r
51         state               => 'state',\r
52         postal              => 'zip',\r
53         country             => 'country',\r
54         currency            => \'USD', #XXX fix me\r
55         email               => 'email',\r
56         ipaddress           => 'customer_ip',\r
57         card_num            => 'card_number',\r
58         #card_exp            => 'expiration', #strip /\r
59         card_cvv            => 'cvv2',\r
60         #start_date          => 'card_start', #strip /\r
61         issue_num           => 'issue_number',\r
62         #bank_name   #XXX fix to support ACH\r
63         #bank_phone  #XXX fix to support ACH\r
64         orderid             => 'order_number',\r
65         custom0             => 'description',\r
66       );\r
67 \r
68         #                => 'order_type',\r
69         #                => 'transaction_type',\r
70 \r
71         #authorization   => \r
72 \r
73         #company         =>\r
74         #phone            => \r
75         #fax             =>\r
76 \r
77         #invoice_number  =>\r
78         #customer_id     =>\r
79         #authorization    => 'txn_number'\r
80 \r
81     if ( $action =~ /^auth(postauth)?$/ ) {\r
82 \r
83       $self->required_fields(qw(\r
84                                  login password action amount\r
85                                  name address city state zip\r
86                                  email\r
87                                  card_number expiration cvv2\r
88                             ));\r
89 \r
90       $self->{_content}{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/\r
91         or croak "unparsable expiration: ". $self->{_content}{expiration};\r
92       my( $month, $year ) = ( $1, $2 );\r
93       $month = '0'. $month if $month =~ /^\d$/;\r
94       $self->{_content}{card_exp} = $month.$year;\r
95 \r
96       if ( $self->{_content}{'card_start'} ) {\r
97         $self->{_content}{'card_start'} =~ /^(\d+)\D+\d*(\d{2})$/\r
98           or croak "unparsable card_start ". $self->{_content}{card_start};\r
99         my( $month, $year ) = ( $1, $2 );\r
100         $month = '0'. $month if $month =~ /^\d$/;\r
101         $self->{_content}{start_date} = $month.$year;\r
102       }\r
103 \r
104 #      $self->{_content}{amount} = sprintf('%.2f', $self->{_content}{amount} );\r
105 \r
106     } elsif ( $action =~ /^(postauth|void|return)$/ ) {\r
107 \r
108       $self->required_fields(qw(\r
109                                  login password action order_number\r
110                             ));\r
111 \r
112     } else {\r
113       die "unknown action $action";\r
114     }\r
115 \r
116     $self->{'_content'}{country} ||= 'US';\r
117 \r
118     #tie my %post_data, 'Tie::IxHash', $self->get_fields(qw(\r
119     my %post_data = $self->get_fields(qw(\r
120       merchantid\r
121       account_password\r
122       action\r
123       amount\r
124       name\r
125       address1\r
126       city\r
127       state\r
128       postal\r
129       country\r
130       currency\r
131       email\r
132       ipaddress\r
133       card_num\r
134       card_exp\r
135       card_cvv\r
136       state_date\r
137       issue_num\r
138       bank_name\r
139       bank_phone\r
140       orderid\r
141       custom0\r
142     ));\r
143 \r
144     warn join("\n", map { "$_: ". $post_data{$_} } keys %post_data )\r
145       if $DEBUG;\r
146 \r
147     #my( $page, $response, @reply_headers) = $self->https_post( \%post_data );\r
148     my( $page, $response, @reply_headers) = $self->https_post( %post_data );\r
149 \r
150     #my %reply_headers = @reply_headers;\r
151     #warn join('', map { "  $_ => $reply_headers{$_}\n" } keys %reply_headers )\r
152     #  if $DEBUG;\r
153 \r
154     #XXX check $response and die if not 200?\r
155 \r
156     $self->server_response($page);\r
157 \r
158     #warn "****** $page *******";\r
159 \r
160     $page =~ s/^\n+//;\r
161 \r
162     my %result = map { \r
163                        /^(\w+)=(.*)$/ or die "can't parse response: $_";\r
164                        ($1, uri_unescape($2));\r
165                      }\r
166                  split(/\&/, $page);\r
167 \r
168     $self->result_code(   $result{'status_code'} );\r
169     $self->avs_code(      $result{'avs_resp'} );\r
170     $self->cvv2_response( $result{'cvv_resp'} );\r
171 \r
172     if ( $result{'status'} eq 'good' ) {\r
173       $self->is_success(1);\r
174       $self->authorization( $result{'auth_code'}   );\r
175       $self->order_number(  $result{'orderid'}     );\r
176     } elsif ( $result{'status'} =~ /^(bad|error|fraud)$/ ) {\r
177       $self->is_success(0);\r
178       $self->error_message("$1: ". $result{'status_msg'});\r
179     } else {\r
180       die "unparsable response received from gateway".\r
181           ( $DEBUG ? ": $page" : '' );\r
182     }\r
183 \r
184 }\r
185 \r
186 sub revmap_fields {\r
187     my($self, %map) = @_;\r
188     my %content = $self->content();\r
189     foreach(keys %map) {\r
190 #    warn "$_ = ". ( ref($map{$_})\r
191 #                         ? ${ $map{$_} }\r
192 #                         : $content{$map{$_}} ). "\n";\r
193         $content{$_} = ref($map{$_})\r
194                          ? ${ $map{$_} }\r
195                          : $content{$map{$_}};\r
196     }\r
197     $self->content(%content);\r
198 }\r
199 \r
200 1;\r
201 \r
202 __END__\r
203 \r
204 =head1 NAME\r
205 \r
206 Business::OnlinePayment::Capstone - CapstonePay backend module for Business::OnlinePayment\r
207 \r
208 =head1 SYNOPSIS\r
209 \r
210   use Business::OnlinePayment;\r
211 \r
212   ####\r
213   # One step transaction, the simple case.\r
214   ####\r
215 \r
216   my $tx = new Business::OnlinePayment("Capstone");\r
217   $tx->content(\r
218       type           => 'VISA',\r
219       login          => 'Merchant ID',\r
220       password       => 'API password',\r
221       action         => 'Normal Authorization',\r
222       description    => 'Business::OnlinePayment test',\r
223       amount         => '49.95',\r
224       name           => 'Tofu Beast',\r
225       address        => '123 Anystreet',\r
226       city           => 'Anywhere',\r
227       state          => 'UT',\r
228       zip            => '84058',\r
229       phone          => '420-867-5309',\r
230       email          => 'tofu.beast@example.com',\r
231       card_number    => '4005550000000019',\r
232       expiration     => '08/06',\r
233       card_start     => '05/04', #switch/solo \r
234       issue_number   => '5678',  #\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-capstone@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