File Coverage

blib/lib/Business/OnlinePayment/AuthorizeNet/ARB.pm
Criterion Covered Total %
statement 116 126 92.0
branch 41 54 75.9
condition 12 20 60.0
subroutine 14 14 100.0
pod 1 5 20.0
total 184 219 84.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         25  
4 1     1   5 use Carp;
  1         2  
  1         57  
5 1     1   15 use Business::OnlinePayment::AuthorizeNet;
  1         2  
  1         23  
6 1     1   744 use Business::OnlinePayment::HTTPS;
  1         26357  
  1         108  
7 1     1   1389 use XML::Simple;
  1         9549  
  1         8  
8 1     1   1127 use XML::Writer;
  1         15155  
  1         31  
9 1     1   8 use Tie::IxHash;
  1         3  
  1         26  
10 1     1   5 use vars qw($VERSION $DEBUG @ISA $me);
  1         2  
  1         1874  
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 2     2 0 4 my $self = shift;
19              
20 2 100       56 $self->server('api.authorize.net') unless $self->server;
21 2 100       101 $self->port('443') unless $self->port;
22 2 100       91 $self->path('/xml/v1/request.api') unless $self->path;
23              
24 2         52 $self->build_subs(qw( order_number md5 avs_code cvv2_response
25             cavv_response
26             ));
27             }
28              
29             sub map_fields {
30 2     2 0 4 my($self) = @_;
31              
32 2         12 my %content = $self->content();
33              
34             # ACTION MAP
35 2         40 my %actions = ('recurring authorization'
36             => 'ARBCreateSubscriptionRequest',
37             'modify recurring authorization'
38             => 'ARBUpdateSubscriptionRequest',
39             'cancel recurring authorization'
40             => 'ARBCancelSubscriptionRequest',
41             );
42 2   33     14 $content{'action'} = $actions{lc($content{'action'} || '')} || $content{'action'};
43              
44             # TYPE MAP
45 2         9 my %types = ('visa' => 'CC',
46             'mastercard' => 'CC',
47             'american express' => 'CC',
48             'discover' => 'CC',
49             'check' => 'ECHECK',
50             );
51 2   66     18 $content{'type'} = $types{lc($content{'type'} || '')} || $content{'type'};
52 2         55 $self->transaction_type($content{'type'});
53              
54             # ACCOUNT TYPE MAP
55 2         21 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 2   33     17 || $content{'account_type'};
62              
63             # MASSAGE EXPIRATION
64 2         7 $content{'expdate_yyyymm'} = $self->expdate_yyyymm($content{'expiration'});
65              
66             # stuff it back into %content
67 2         11 $self->content(%content);
68              
69             }
70              
71             sub revmap_fields {
72 22     22 0 117 my $self = shift;
73 22         81 tie my(%map), 'Tie::IxHash', @_;
74 22         1225 my %content = $self->content();
75             map {
76 22         334 my $value;
  102         635  
77 102 100       310 if ( ref( $map{$_} ) eq 'HASH' ) {
    100          
78 22 100       133 $value = $map{$_} if ( keys %{ $map{$_} } );
  22         71  
79             }elsif( exists( $content{ $map{$_} } ) ) {
80 14         211 $value = $content{ $map{$_} };
81             }
82              
83 102 100       1429 if (defined($value)) {
84 22         90 ($_ => $value);
85             }else{
86 80         205 ();
87             }
88             } (keys %map);
89             }
90              
91             sub expdate_yyyymm {
92 2     2 0 3 my $self = shift;
93 2         4 my $expiration = shift;
94 2         3 my $expdate_yyyymm;
95 2 100 66     13 if ( defined($expiration) and $expiration =~ /^(\d{1,2})\D+(\d{2})$/ ) {
96 1         4 my ( $month, $year ) = ( $1, $2 );
97 1         7 $expdate_yyyymm = sprintf( "20%02d-%02d", $year, $month );
98             }
99 2 100       8 return defined($expdate_yyyymm) ? $expdate_yyyymm : $expiration;
100             };
101              
102             sub _xmlwrite {
103 24     24   67 my ($self, $writer, $item, $value) = @_;
104 24         168 $writer->startTag($item);
105 24 100       956 if ( ref( $value ) eq 'HASH' ) {
106 9         33 foreach ( keys ( %$value ) ) {
107 20         416 $self->_xmlwrite($writer, $_, $value->{$_});
108             }
109             }else{
110 15         37 $writer->characters($value);
111             }
112 24         478 $writer->endTag($item);
113             }
114              
115             sub submit {
116 2     2 1 4 my($self) = @_;
117              
118 2         5 $self->map_fields();
119              
120 2         92 my @required_fields = qw(action login password);
121              
122 2 100       13 if ( $self->{_content}->{action} eq 'ARBCreateSubscriptionRequest' ) {
    50          
    50          
123 1         4 push @required_fields,
124             qw( type interval start periods amount first_name last_name );
125              
126 1 50       23 if ($self->transaction_type() eq "ECHECK") {
    50          
127 0         0 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 1         34 push @required_fields, qw( card_number expiration );
133             }
134             }elsif ( $self->{_content}->{action} eq 'ARBUpdateSubscriptionRequest' ) {
135 0         0 push @required_fields, qw( subscription );
136             }elsif ( $self->{_content}->{action} eq 'ARBCancelSubscriptionRequest' ) {
137 1         2 push @required_fields, qw( subscription );
138             }else{
139             croak "$me can't handle transaction type: ".
140 0         0 $self->{_content}->{action}. " for ".
141             $self->transaction_type();
142             }
143              
144 2         14 $self->required_fields(@required_fields);
145              
146 2         74 tie my %merchant, 'Tie::IxHash',
147             $self->revmap_fields(
148             name => 'login',
149             transactionKey => 'password',
150             );
151              
152             my ($length,$unit) =
153 2   100     81 ($self->{_content}->{interval} or '') =~ /^\s*(\d+)\s+(day|month)s?\s*$/;
154 2 100       17 tie my %interval, 'Tie::IxHash', (
    100          
155             ($length ? (length => $length) : () ),
156             ($unit ? (unit => $unit.'s') : () ),
157             );
158              
159 2         48 tie my %schedule, 'Tie::IxHash',
160             $self->revmap_fields(
161             interval => \%interval,
162             startDate => 'start',
163             totalOccurrences => 'periods',
164             trialOccurrences => 'trialperiods',
165             );
166              
167 2 100 66     104 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 2 100 66     91 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 2         38 tie my %order, 'Tie::IxHash',
194             $self->revmap_fields(
195             invoiceNumber => 'invoice_number',
196             description => 'description',
197             );
198              
199 2         37 tie my %drivers, 'Tie::IxHash',
200             $self->revmap_fields(
201             number => 'license_num',
202             state => 'license_state',
203             dateOfBirth => 'license_dob',
204             );
205              
206 2         28 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 2         51 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 2         29 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 2         31 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 2         77 tie my %req, 'Tie::IxHash',
256             $self->revmap_fields (
257             merchantAuthentication => \%merchant,
258             subscriptionId => 'subscription',
259             subscription => \%sub,
260             );
261              
262 2         67 my $ns = "AnetApi/xml/v1/schema/AnetApiSchema.xsd";
263 2         4 my $post_data;
264 2         12 my $writer = new XML::Writer( OUTPUT => \$post_data,
265             DATA_MODE => 1,
266             DATA_INDENT => 1,
267             ENCODING => 'utf-8',
268             );
269 2         332 $writer->xmlDecl();
270 2         106 $writer->startTag($self->{_content}->{action}, 'xmlns', $ns);
271 2         157 foreach ( keys ( %req ) ) {
272 4         94 $self->_xmlwrite($writer, $_, $req{$_});
273             }
274 2         53 $writer->endTag($self->{_content}->{action});
275 2         55 $writer->end();
276              
277 2 50       88 if ($self->test_transaction()) {
278 2         58 $self->server('apitest.authorize.net');
279             }
280              
281 2 50       17 warn $post_data if $DEBUG;
282 2         28 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 2         847211 $page =~ s/^(.*?)
287 2         12 my $garbage=$1;
288 2 50       15 warn "Trimmed $garbage from response page.\n" if $DEBUG;
289              
290 2 50       11 warn $page if $DEBUG;
291              
292 2         7 my $response;
293             my $message;
294 2 50       17 if ($server_response =~ /200/){
295 2         17 $response = XMLin($page);
296 2 50       102011 if (ref($response->{messages}->{message}) eq 'ARRAY') {
297 0         0 $message = $response->{messages}->{message}->[0];
298             }else{
299 2         7 $message = $response->{messages}->{message};
300             }
301             }else{
302 0         0 $response->{messages}->{resultCode} = "Server Failed";
303 0         0 $message->{code} = $server_response;
304             }
305              
306 2         85 $self->server_response($page);
307 2         69 $self->order_number($response->{subscriptionId});
308 2         70 $self->result_code($message->{code});
309 2         64 $self->error_message($message->{text});
310              
311 2 50       20 if($response->{messages}->{resultCode} eq "Ok" ) {
312 2         53 $self->is_success(1);
313             } else {
314 0           $self->is_success(0);
315 0 0         unless ( $self->error_message() ) { #additional logging information
316             $self->error_message(
317             "(HTTPS response: $server_response) ".
318             "(HTTPS headers: ".
319 0           join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
  0            
320             "(Raw HTTPS content: $page)"
321             );
322             }
323             }
324             }
325              
326             1;
327             __END__