1 package Business::OnlinePayment::NMI;
5 use Business::OnlinePayment 3;
6 use Business::OnlinePayment::HTTPS;
7 use Digest::MD5 qw(md5_hex);
9 use vars qw($VERSION @ISA $DEBUG);
11 @ISA = qw(Business::OnlinePayment::HTTPS);
18 'info_compat' => '0.01',
19 'gateway_name' => 'Network Merchants',
20 'gateway_url' => 'https://www.nmi.com',
21 'module_version' => $VERSION,
22 'supported_types' => [ 'CC', 'ECHECK' ],
23 'supported_actions' => {
25 'Normal Authorization',
32 'Normal Authorization',
41 'normal authorization' => 'sale',
42 'authorization only' => 'auth',
43 'post authorization' => 'capture',
53 # NMI Direct Post API, June 2007
54 action => 'type', # special
56 password => 'password',
57 card_number => 'ccnumber',
58 expiration => 'ccexp',
60 routing_code => 'checkaba',
61 account_number => 'checkaccount',
62 account_holder_type => 'account_holder_type',
63 account_type => 'account_type',
66 payment => 'payment', # special
67 description => 'orderdescription',
68 invoice_number => 'orderid',
69 customer_ip => 'ipaddress',
71 freight => 'shipping',
72 po_number => 'ponumber',
73 first_name => 'firstname',
74 last_name => 'lastname',
76 address => 'address1',
81 order_number => 'transactionid', # used for capture/void/refund
84 $fields{"ship_$_"} = 'shipping_'.$fields{$_}
85 foreach(qw(first_name last_name company address city state zip country)) ;
88 'ALL' => [ qw( type username password payment ) ],
89 'sale' => [ 'amount' ],
90 'sale:creditcard' => [ 'ccnumber', 'ccexp' ],
91 'sale:check' => [ qw( checkname checkaba checkaccount account_holder_type account_type ) ],
92 'auth:creditcard' => [ qw( amount ccnumber ccexp ) ],
93 'capture' => [ 'amount', 'transactionid' ],
94 'refund' => [ 'amount', 'transactionid' ],
95 'void' => [ 'transactionid' ],
96 # not supported: update
101 'sale' => [ qw( orderdescription orderid ipaddress tax
102 shipping ponumber firstname lastname company
103 address1 city state zip country phone fax email
104 shipping_firstname shipping_lastname
105 shipping_company shipping_address1 shipping_city
106 shipping_state shipping_zip shipping_country
108 'sale:creditcard' => [ 'cvv' ],
110 'auth:creditcard' => [ qw( orderdescription orderid ipaddress tax
111 shipping ponumber firstname lastname company
112 address1 city state zip country phone fax email
113 shipping_firstname shipping_lastname
114 shipping_company shipping_address1 shipping_city
115 shipping_state shipping_zip shipping_country
117 'capture' => [ 'orderid' ],
118 'refund' => [ 'amount' ],
121 my %failure_status = (
129 # add others here as needed; very little code uses failure_status at present
134 $self->server('secure.networkmerchants.com');
136 $self->path('/api/transact.php');
137 $self->build_subs(qw(avs_code cvv2_response failure_status));
143 my %content = $self->content();
145 if($self->test_transaction) {
146 # Public test account.
147 $content{'login'} = 'demo';
148 $content{'password'} = 'password';
151 $content{'payment'} = $types{lc($content{'type'})} or die "Payment method '$content{type}' not supported.\n";
152 $content{'action'} = $actions{lc($content{'action'})} or die "Transaction type '$content{action}' not supported.\n";
154 $content{'expiration'} =~ s/\D//g if defined($content{'expiration'});
156 $content{'account_type'} ||= 'personal checking';
157 @content{'account_holder_type', 'account_type'} =
158 map {lc} split /\s/, $content{'account_type'};
159 $content{'ship_name'} = $content{'ship_first_name'} ?
160 ($content{'ship_first_name'}.' '.$content{'ship_last_name'}) : '';
161 $self->content(%content);
169 $self->remap_fields(%fields);
171 my %content = $self->content;
172 my $type = $content{'type'}; # what we call "action"
173 my $payment = $content{'payment'}; # what we call "type"
175 warn "content:$_ => $content{$_}\n" foreach keys %content;
178 my @required_fields = ( @{$required{'ALL'}} );
179 push @required_fields, @{$required{$type}} if exists($required{$type});
180 push @required_fields, @{$required{"$type:$payment"}} if exists($required{"$type:$payment"});
182 $self->required_fields(@required_fields);
184 my @allowed_fields = @required_fields;
185 push @allowed_fields, @{$optional{'ALL'}};
186 push @allowed_fields, @{$optional{$type}} if exists($optional{$type});
187 push @allowed_fields, @{$optional{"$type:$payment"}} if exists($required{"$type:$payment"});
189 my %post_data = $self->get_fields(@allowed_fields);
192 warn "post_data:$_ => $post_data{$_}\n" foreach keys %post_data;
195 my($page,$server_response) = $self->https_post(\%post_data);
197 warn "response page: $page\n";
201 if ($server_response =~ /200/){
202 $response = {map { split '=', $_, 2 } split '&', $page};
205 die "HTTPS error: '$server_response'\n";
208 $response->{$_} =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg
209 foreach keys %$response;
212 warn "response:$_ => $response->{$_}\n" foreach keys %$response;
215 $self->is_success(0);
217 if( $response->{response} == 1 ) {
218 $self->is_success(1);
220 elsif( $response->{response} == 2 ) {
221 $error = $response->{responsetext};
222 my $code = $response->{response_code};
223 $self->failure_status($failure_status{$code}) if exists($failure_status{$code});
225 elsif( $response->{response} == 3 ) {
226 $error = "Transaction error: '".$response->{responsetext}."'";
229 $error = "Could not interpret server response: '$page'";
231 $self->order_number($response->{transactionid});
232 $self->authorization($response->{authcode});
233 $self->avs_code($response->{avsresponse});
234 $self->cvv2_response($response->{cvvresponse});
235 $self->result_code($response->{response_code});
236 $self->error_message($error);
237 $self->server_response($response);
245 Business::OnlinePayment::NMI - Network Merchants backend for Business::OnlinePayment
249 use Business::OnlinePayment;
251 my $tx = new Business::OnlinePayment("NMI");
254 password => 'mypass',
255 action => 'Normal Authorization',
256 description => 'Business::OnlinePayment test',
258 invoice_number => '100100',
259 name => 'Tofu Beast',
260 card_number => '46464646464646',
261 expiration => '11/08',
262 address => '1234 Bean Curd Lane, San Francisco',
267 if($tx->is_success()) {
268 print "Card processed successfully: ".$tx->authorization."\n";
270 print "Card was rejected: ".$tx->error_message."\n";
275 For detailed information see L<Business::OnlinePayment>.
277 =head1 SUPPORTED TRANSACTION TYPES
281 Normal Authorization, Authorization Only, Post Authorization, Void, Credit.
285 Normal Authorization, Void, Credit.
289 Credit is handled using NMI's 'refund' action, which applies the credit against
292 Post Authorization, Void, and Credit require C<order_number> to be set with the
293 transaction ID of the previous authorization.
297 This module implements the NMI Direct Post API, June 2007 revision.
301 Mark Wells <mark@freeside.biz>
303 Based in part on Business::OnlinePayment::USAePay by Jeff Finucane
308 perl(1). L<Business::OnlinePayment>.