File Coverage

blib/lib/Business/OnlinePayment/AuthorizeNet/ARB.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::AuthorizeNet::ARB;
2              
3 1     1   5 use strict;
  1         2  
  1         42  
4 1     1   5 use Carp;
  1         2  
  1         82  
5 1     1   5 use Business::OnlinePayment::AuthorizeNet;
  1         1  
  1         27  
6 1     1   707 use Business::OnlinePayment::HTTPS;
  1         53483  
  1         36  
7 1     1   622 use XML::Simple;
  0            
  0            
8             use XML::Writer;
9             use Tie::IxHash;
10             use vars qw($VERSION $DEBUG @ISA $me);
11              
12             @ISA = qw(Business::OnlinePayment::AuthorizeNet Business::OnlinePayment::HTTPS);
13             $VERSION = '0.02';
14             $DEBUG = 0;
15             $me='Business::OnlinePayment::AuthorizeNet::ARB';
16              
17             sub set_defaults {
18             my $self = shift;
19              
20             $self->server('api.authorize.net') unless $self->server;
21             $self->port('443') unless $self->port;
22             $self->path('/xml/v1/request.api') unless $self->path;
23              
24             $self->build_subs(qw( order_number md5 avs_code cvv2_response
25             cavv_response
26             ));
27             }
28              
29             sub map_fields {
30             my($self) = @_;
31              
32             my %content = $self->content();
33              
34             # ACTION MAP
35             my %actions = ('recurring authorization'
36             => 'ARBCreateSubscriptionRequest',
37             'modify recurring authorization'
38             => 'ARBUpdateSubscriptionRequest',
39             'cancel recurring authorization'
40             => 'ARBCancelSubscriptionRequest',
41             );
42             $content{'action'} = $actions{lc($content{'action'} || '')} || $content{'action'};
43              
44             # TYPE MAP
45             my %types = ('visa' => 'CC',
46             'mastercard' => 'CC',
47             'american express' => 'CC',
48             'discover' => 'CC',
49             'check' => 'ECHECK',
50             );
51             $content{'type'} = $types{lc($content{'type'} || '')} || $content{'type'};
52             $self->transaction_type($content{'type'});
53              
54             # ACCOUNT TYPE MAP
55             my %account_types = ('personal checking' => 'checking',
56             'personal savings' => 'savings',
57             'business checking' => 'businessChecking',
58             'business savings' => 'savings',
59             );
60             $content{'account_type'} = $account_types{lc($content{'account_type'} || '')}
61             || $content{'account_type'};
62              
63             # MASSAGE EXPIRATION
64             $content{'expdate_yyyymm'} = $self->expdate_yyyymm($content{'expiration'});
65              
66             # stuff it back into %content
67             $self->content(%content);
68              
69             }
70              
71             sub revmap_fields {
72             my $self = shift;
73             tie my(%map), 'Tie::IxHash', @_;
74             my %content = $self->content();
75             map {
76             my $value;
77             if ( ref( $map{$_} ) eq 'HASH' ) {
78             $value = $map{$_} if ( keys %{ $map{$_} } );
79             }elsif( exists( $content{ $map{$_} } ) ) {
80             $value = $content{ $map{$_} };
81             }
82              
83             if (defined($value)) {
84             ($_ => $value);
85             }else{
86             ();
87             }
88             } (keys %map);
89             }
90              
91             sub expdate_yyyymm {
92             my $self = shift;
93             my $expiration = shift;
94             my $expdate_yyyymm;
95             if ( defined($expiration) and $expiration =~ /^(\d{1,2})\D+(\d{2})$/ ) {
96             my ( $month, $year ) = ( $1, $2 );
97             $expdate_yyyymm = sprintf( "20%02d-%02d", $year, $month );
98             }
99             return defined($expdate_yyyymm) ? $expdate_yyyymm : $expiration;
100             };
101              
102             sub _xmlwrite {
103             my ($self, $writer, $item, $value) = @_;
104             $writer->startTag($item);
105             if ( ref( $value ) eq 'HASH' ) {
106             foreach ( keys ( %$value ) ) {
107             $self->_xmlwrite($writer, $_, $value->{$_});
108             }
109             }else{
110             $writer->characters($value);
111             }
112             $writer->endTag($item);
113             }
114              
115             sub submit {
116             my($self) = @_;
117              
118             $self->map_fields();
119              
120             my @required_fields = qw(action login password);
121              
122             if ( $self->{_content}->{action} eq 'ARBCreateSubscriptionRequest' ) {
123             push @required_fields,
124             qw( type interval start periods amount first_name last_name );
125              
126             if ($self->transaction_type() eq "ECHECK") {
127             push @required_fields,
128             qw( amount routing_code account_number account_type account_name
129             check_type
130             );
131             } elsif ($self->transaction_type() eq 'CC' ) {
132             push @required_fields, qw( card_number expiration );
133             }
134             }elsif ( $self->{_content}->{action} eq 'ARBUpdateSubscriptionRequest' ) {
135             push @required_fields, qw( subscription );
136             }elsif ( $self->{_content}->{action} eq 'ARBCancelSubscriptionRequest' ) {
137             push @required_fields, qw( subscription );
138             }else{
139             croak "$me can't handle transaction type: ".
140             $self->{_content}->{action}. " for ".
141             $self->transaction_type();
142             }
143              
144             $self->required_fields(@required_fields);
145              
146             tie my %merchant, 'Tie::IxHash',
147             $self->revmap_fields(
148             name => 'login',
149             transactionKey => 'password',
150             );
151              
152             my ($length,$unit) =
153             ($self->{_content}->{interval} or '') =~ /^\s*(\d+)\s+(day|month)s?\s*$/;
154             tie my %interval, 'Tie::IxHash', (
155             ($length ? (length => $length) : () ),
156             ($unit ? (unit => $unit.'s') : () ),
157             );
158              
159             tie my %schedule, 'Tie::IxHash',
160             $self->revmap_fields(
161             interval => \%interval,
162             startDate => 'start',
163             totalOccurrences => 'periods',
164             trialOccurrences => 'trialperiods',
165             );
166              
167             tie my %account, 'Tie::IxHash', (
168             ( defined($self->transaction_type())
169             && $self->transaction_type() eq 'CC'
170             ) ? $self->revmap_fields(
171             cardNumber => 'card_number',
172             expirationDate => 'expdate_yyyymm',
173             )
174             : $self->revmap_fields(
175             accountType => 'account_type',
176             routingNumber => 'routing_code',
177             accountNumber => 'account_number',
178             nameOnAccount => 'account_name',
179             echeckType => 'check_type',
180             bankName => 'bank_name',
181             )
182             );
183              
184             tie my %payment, 'Tie::IxHash',
185             $self->revmap_fields(
186             ( ( defined($self->transaction_type()) && # require?
187             $self->transaction_type() eq 'CC'
188             ) ? 'creditCard'
189             : 'bankAccount'
190             ) => \%account,
191             );
192              
193             tie my %order, 'Tie::IxHash',
194             $self->revmap_fields(
195             invoiceNumber => 'invoice_number',
196             description => 'description',
197             );
198              
199             tie my %drivers, 'Tie::IxHash',
200             $self->revmap_fields(
201             number => 'license_num',
202             state => 'license_state',
203             dateOfBirth => 'license_dob',
204             );
205              
206             tie my %billto, 'Tie::IxHash',
207             $self->revmap_fields(
208             firstName => 'first_name',
209             lastName => 'last_name',
210             company => 'company',
211             address => 'address',
212             city => 'city',
213             state => 'state',
214             zip => 'zip',
215             country => 'country',
216             );
217              
218             tie my %shipto, 'Tie::IxHash',
219             $self->revmap_fields(
220             firstName => 'ship_first_name',
221             lastName => 'ship_last_name',
222             company => 'ship_company',
223             address => 'ship_address',
224             city => 'ship_city',
225             state => 'ship_state',
226             zip => 'ship_zip',
227             country => 'ship_country',
228             );
229              
230             tie my %customer, 'Tie::IxHash',
231             $self->revmap_fields(
232             type => 'customer_org',
233             id => 'customer_id',
234             email => 'email',
235             phoneNumber => 'phone',
236             faxNumber => 'fax',
237             driversLicense => \%drivers,
238             taxid => 'customer_ssn',
239             );
240              
241             tie my %sub, 'Tie::IxHash',
242             $self->revmap_fields(
243             name => 'subscription_name',
244             paymentSchedule => \%schedule,
245             amount => 'amount',
246             trialAmount => 'trialamount',
247             payment => \%payment,
248             order => \%order,
249             customer => \%customer,
250             billTo => \%billto,
251             shipTo => \%shipto,
252             );
253              
254              
255             tie my %req, 'Tie::IxHash',
256             $self->revmap_fields (
257             merchantAuthentication => \%merchant,
258             subscriptionId => 'subscription',
259             subscription => \%sub,
260             );
261              
262             my $ns = "AnetApi/xml/v1/schema/AnetApiSchema.xsd";
263             my $post_data;
264             my $writer = new XML::Writer( OUTPUT => \$post_data,
265             DATA_MODE => 1,
266             DATA_INDENT => 1,
267             ENCODING => 'utf-8',
268             );
269             $writer->xmlDecl();
270             $writer->startTag($self->{_content}->{action}, 'xmlns', $ns);
271             foreach ( keys ( %req ) ) {
272             $self->_xmlwrite($writer, $_, $req{$_});
273             }
274             $writer->endTag($self->{_content}->{action});
275             $writer->end();
276              
277             if ($self->test_transaction()) {
278             $self->server('apitest.authorize.net');
279             }
280              
281             warn $post_data if $DEBUG;
282             my($page,$server_response,%headers) =
283             $self->https_post( { 'Content-Type' => 'text/xml' }, $post_data);
284              
285             #trim leading (4?) characters of unknown origin not in spec
286             $page =~ s/^(.*?)
287             my $garbage=$1;
288             warn "Trimmed $garbage from response page.\n" if $DEBUG;
289              
290             warn $page if $DEBUG;
291              
292             my $response;
293             my $message;
294             if ($server_response =~ /200/){
295             $response = XMLin($page);
296             if (ref($response->{messages}->{message}) eq 'ARRAY') {
297             $message = $response->{messages}->{message}->[0];
298             }else{
299             $message = $response->{messages}->{message};
300             }
301             }else{
302             $response->{messages}->{resultCode} = "Server Failed";
303             $message->{code} = $server_response;
304             }
305              
306             $self->server_response($page);
307             $self->order_number($response->{subscriptionId});
308             $self->result_code($message->{code});
309             $self->error_message($message->{text});
310              
311             if($response->{messages}->{resultCode} eq "Ok" ) {
312             $self->is_success(1);
313             } else {
314             $self->is_success(0);
315             unless ( $self->error_message() ) { #additional logging information
316             $self->error_message(
317             "(HTTPS response: $server_response) ".
318             "(HTTPS headers: ".
319             join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
320             "(Raw HTTPS content: $page)"
321             );
322             }
323             }
324             }
325              
326             1;
327             __END__