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;
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'}.' '.$content{'ship_last_name'};
160 $self->content(%content);
168 $self->remap_fields(%fields);
170 my %content = $self->content;
171 my $type = $content{'type'}; # what we call "action"
172 my $payment = $content{'payment'}; # what we call "type"
174 warn "content:$_ => $content{$_}\n" foreach keys %content;
177 my @required_fields = ( @{$required{'ALL'}} );
178 push @required_fields, @{$required{$type}} if exists($required{$type});
179 push @required_fields, @{$required{"$type:$payment"}} if exists($required{"$type:$payment"});
181 $self->required_fields(@required_fields);
183 my @allowed_fields = @required_fields;
184 push @allowed_fields, @{$optional{'ALL'}};
185 push @allowed_fields, @{$optional{$type}} if exists($optional{$type});
186 push @allowed_fields, @{$optional{"$type:$payment"}} if exists($required{"$type:$payment"});
188 my %post_data = $self->get_fields(@allowed_fields);
191 warn "post_data:$_ => $post_data{$_}\n" foreach keys %post_data;
194 my($page,$server_response) = $self->https_post(\%post_data);
196 warn "response page: $page\n";
200 if ($server_response =~ /200/){
201 $response = {map { split '=', $_, 2 } split '&', $page};
204 die "HTTPS error: '$server_response'\n";
207 $response->{$_} =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg
208 foreach keys %$response;
211 warn "response:$_ => $response->{$_}\n" foreach keys %$response;
214 $self->is_success(0);
216 if( $response->{response} == 1 ) {
217 $self->is_success(1);
219 elsif( $response->{response} == 2 ) {
220 $error = $response->{responsetext};
221 my $code = $response->{response_code};
222 $self->failure_status($failure_status{$code}) if exists($failure_status{$code});
224 elsif( $response->{response} == 3 ) {
225 $error = "Transaction error: '".$response->{responsetext};
228 $error = "Could not interpret server response: '$page'";
230 $self->order_number($response->{transactionid});
231 $self->authorization($response->{authcode});
232 $self->avs_code($response->{avsresponse});
233 $self->cvv2_response($response->{cvvresponse});
234 $self->result_code($response->{response_code});
235 $self->error_message($error);
236 $self->server_response($response);
244 Business::OnlinePayment::NMI - Network Merchants backend for Business::OnlinePayment
248 use Business::OnlinePayment;
250 my $tx = new Business::OnlinePayment("NMI");
253 password => 'mypass',
254 action => 'Normal Authorization',
255 description => 'Business::OnlinePayment test',
257 invoice_number => '100100',
258 name => 'Tofu Beast',
259 card_number => '46464646464646',
260 expiration => '11/08',
261 address => '1234 Bean Curd Lane, San Francisco',
266 if($tx->is_success()) {
267 print "Card processed successfully: ".$tx->authorization."\n";
269 print "Card was rejected: ".$tx->error_message."\n";
274 For detailed information see L<Business::OnlinePayment>.
276 =head1 SUPPORTED TRANSACTION TYPES
280 Normal Authorization, Authorization Only, Post Authorization, Void, Credit.
284 Normal Authorization, Void, Credit.
288 Credit is handled using NMI's 'refund' action, which applies the credit against
291 Post Authorization, Void, and Credit require C<order_number> to be set with the
292 transaction ID of the previous authorization.
296 This module implements the NMI Direct Post API, June 2007 revision.
300 Mark Wells <mark@freeside.biz>
302 Based in part on Business::OnlinePayment::USAePay by Jeff Finucane
307 perl(1). L<Business::OnlinePayment>.