X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=AuthorizeNet.pm;h=316b4814f9006107d6bf8041d0953d692ee7932b;hb=f62baef5adfb6d0e4f70255ca71fc40f5b057c5d;hp=569d88dff8d0a7afd62cf2d8c00e3ed122be7b5e;hpb=1a21aa040d676e76c602ab256106b61dee0bf907;p=Business-OnlinePayment-AuthorizeNet.git diff --git a/AuthorizeNet.pm b/AuthorizeNet.pm index 569d88d..316b481 100644 --- a/AuthorizeNet.pm +++ b/AuthorizeNet.pm @@ -1,170 +1,45 @@ package Business::OnlinePayment::AuthorizeNet; -# $Id: AuthorizeNet.pm,v 1.9 2002-04-24 02:39:38 ivan Exp $ - use strict; +use Carp; use Business::OnlinePayment; -use Net::SSLeay qw/make_form post_https/; -use Text::CSV_XS; -use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); - -require Exporter; +use vars qw($VERSION @ISA $me); -@ISA = qw(Exporter AutoLoader Business::OnlinePayment); -@EXPORT = qw(); -@EXPORT_OK = qw(); -$VERSION = '3.11'; +@ISA = qw(Business::OnlinePayment); +$VERSION = '3.19'; +$me = 'Business::OnlinePayment::AuthorizeNet'; sub set_defaults { my $self = shift; - $self->server('secure.authorize.net'); - $self->port('443'); - $self->path('/gateway/transact.dll'); - - $self->build_subs('order_number'); #no idea how it worked for jason w/o this + $self->build_subs(qw( order_number md5 avs_code cvv2_response + cavv_response + )); } -sub map_fields { +sub _map_processor { my($self) = @_; my %content = $self->content(); - - # ACTION MAP - my %actions = ('normal authorization' => 'AUTH_CAPTURE', - 'authorization only' => 'AUTH_ONLY', - 'credit' => 'CREDIT', - 'post authorization' => 'PRIOR_AUTH_CAPTURE', - ); - $content{'action'} = $actions{lc($content{'action'})} || $content{'action'}; - - # TYPE MAP - my %types = ('visa' => 'CC', - 'mastercard' => 'CC', - 'american express' => 'CC', - 'discover' => 'CC', - 'check' => 'ECHECK', - ); - $content{'type'} = $types{lc($content{'type'})} || $content{'type'}; - $self->transaction_type($content{'type'}); - - # stuff it back into %content - $self->content(%content); -} - -sub remap_fields { - my($self,%map) = @_; - - my %content = $self->content(); - foreach(keys %map) { - $content{$map{$_}} = $content{$_}; - } - $self->content(%content); -} - -sub get_fields { - my($self,@fields) = @_; - - my %content = $self->content(); - my %new = (); - foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; } - return %new; + my %processors = ('recurring authorization' => 'ARB', + 'modify recurring authorization' => 'ARB', + 'cancel recurring authorization' => 'ARB', + ); + $processors{lc($content{'action'})} || 'AIM'; } sub submit { my($self) = @_; - $self->map_fields(); - $self->remap_fields( - type => 'x_Method', - login => 'x_Login', - password => 'x_Password', - action => 'x_Type', - description => 'x_Description', - amount => 'x_Amount', - invoice_number => 'x_Invoice_Num', - customer_id => 'x_Cust_ID', - last_name => 'x_Last_Name', - first_name => 'x_First_Name', - address => 'x_Address', - city => 'x_City', - state => 'x_State', - zip => 'x_Zip', - card_number => 'x_Card_Num', - expiration => 'x_Exp_Date', - account_number => 'x_Bank_Acct_Num', - routing_code => 'x_Bank_ABA_Code', - bank_name => 'x_Bank_Name', - country => 'x_Country', - phone => 'x_Phone', - fax => 'x_Fax', - email => 'x_Email', - company => 'x_Company', - order_number => 'x_Trans_ID', - ); - - if($self->transaction_type() eq "ECHECK") { - $self->required_fields(qw/type login password action amount last_name - first_name account_number routing_code - bank_name/); - } elsif($self->transaction_type() eq 'CC' ) { - if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) { - $self->required_fields(qw/type login password action amount - card_number expiration/); - } else { - $self->required_fields(qw/type login password action amount last_name - first_name card_number expiration/); - } - } else { - Carp::croak("AuthorizeNet can't handle transaction type: ". - $self->transaction_type()); - } - - my %post_data = $self->get_fields(qw/x_Login x_Password x_Invoice_Num - x_Description x_Amount x_Cust_ID - x_Method x_Type x_Card_Num x_Exp_Date - x_Auth_Code x_Bank_Acct_Num - x_Bank_ABA_Code x_Bank_Name - x_Last_Name x_First_Name x_Address - x_City x_State x_Zip x_Country x_Phone - x_Fax x_Email x_Email_Customer - x_Company x_Country x_Trans_ID/); - $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE"; - $post_data{'x_ADC_Delim_Data'} = 'TRUE'; - $post_data{'x_ADC_URL'} = 'FALSE'; - $post_data{'x_Version'} = '3.1'; - - my $pd = make_form(%post_data); - my $s = $self->server(); - my $p = $self->port(); - my $t = $self->path(); - my($page,$server_response,%headers) = post_https($s,$p,$t,'',$pd); - - my $csv = new Text::CSV_XS(); - $csv->parse($page); - my @col = $csv->fields(); - - $self->server_response($page); - if($col[0] eq "1" ) { # Authorized/Pending/Test - $self->is_success(1); - $self->result_code($col[0]); - $self->authorization($col[4]); - $self->order_number($col[6]); - } else { - $self->is_success(0); - $self->result_code($col[2]); - $self->error_message($col[3]); - unless ( $self->result_code() ) { #additional logging information - $page =~ s/\x00/\^0/g; - $self->error_message($col[3]. - " DEBUG: No x_response_code from server, ". - "(HTTPS response: $server_response) ". - "(HTTPS headers: ". - join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ". - "(Raw HTTPS content: $page)" - ); - } - } + my $processor = $me. "::". $self->_map_processor(); + + eval "use $processor"; + croak("unknown processor $processor ($@)") if $@; + + my $object = bless $self, $processor; + $object->set_defaults(); + $object->submit(); + bless $self, $me; } 1; @@ -178,11 +53,15 @@ Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::Onlin use Business::OnlinePayment; + #### + # One step transaction, the simple case. + #### + my $tx = new Business::OnlinePayment("AuthorizeNet"); $tx->content( type => 'VISA', login => 'testdrive', - password => '', + password => '', #password or transaction key action => 'Normal Authorization', description => 'Business::OnlinePayment test', amount => '49.95', @@ -196,6 +75,8 @@ Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::Onlin zip => '84058', card_number => '4007000000027', expiration => '09/02', + cvv2 => '1234', #optional + referer => 'http://valid.referer.url/', ); $tx->submit(); @@ -205,26 +86,291 @@ Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::Onlin print "Card was rejected: ".$tx->error_message."\n"; } + #### + # Two step transaction, authorization and capture. + # If you don't need to review order before capture, you can + # process in one step as above. + #### + + my $tx = new Business::OnlinePayment("AuthorizeNet"); + $tx->content( + type => 'VISA', + login => 'testdrive', + password => '', #password or transaction key + action => 'Authorization Only', + description => 'Business::OnlinePayment test', + amount => '49.95', + invoice_number => '100100', + customer_id => 'jsk', + first_name => 'Jason', + last_name => 'Kohles', + address => '123 Anystreet', + city => 'Anywhere', + state => 'UT', + zip => '84058', + card_number => '4007000000027', + expiration => '09/02', + cvv2 => '1234', #optional + referer => 'http://valid.referer.url/', + ); + $tx->submit(); + + if($tx->is_success()) { + # get information about authorization + $authorization = $tx->authorization + $ordernum = $tx->order_number; + $avs_code = $tx->avs_code; # AVS Response Code + $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code + $cavv_response = $tx->cavv_response; # Cardholder Authentication + # Verification Value (CAVV) Response + # Code + + # now capture transaction + my $capture = new Business::OnlinePayment("AuthorizeNet"); + + $capture->content( + type => 'CC', + action => 'Post Authorization', + login => 'YOURLOGIN + password => 'YOURPASSWORD', + order_number => $ordernum, + amount => '49.95', + ); + + $capture->submit(); + + if($capture->is_success()) { + print "Card captured successfully: ".$capture->authorization."\n"; + } else { + print "Card was rejected: ".$capture->error_message."\n"; + } + + } else { + print "Card was rejected: ".$tx->error_message."\n"; + } + + #### + # One step subscription, the simple case. + #### + + my $tx = new Business::OnlinePayment("AuthorizeNet::ARB"); + $tx->content( + type => 'CC', + login => 'testdrive', + password => 'testpass', + action => 'Recurring Authorization', + interval => '7 days', + start => '2008-3-10', + periods => '16', + amount => '99.95', + trialperiods => '4', + trialamount => '0', + description => 'Business::OnlinePayment test', + invoice_number => '1153B33F', + customer_id => 'vip', + first_name => 'Tofu', + last_name => 'Beast', + address => '123 Anystreet', + city => 'Anywhere', + state => 'GA', + zip => '84058', + card_number => '4111111111111111', + expiration => '09/02', + ); + $tx->submit(); + + if($tx->is_success()) { + print "Card processed successfully: ".$tx->order_number."\n"; + } else { + print "Card was rejected: ".$tx->error_message."\n"; + } + my $subscription = $tx->order_number + + + #### + # Subscription change. Modestly more complicated. + #### + + $tx->content( + type => 'CC', + subscription => '99W2C', + login => 'testdrive', + password => 'testpass', + action => 'Modify Recurring Authorization', + interval => '7 days', + start => '2008-3-10', + periods => '16', + amount => '29.95', + trialperiods => '4', + trialamount => '0', + description => 'Business::OnlinePayment test', + invoice_number => '1153B340', + customer_id => 'vip', + first_name => 'Tofu', + last_name => 'Beast', + address => '123 Anystreet', + city => 'Anywhere', + state => 'GA', + zip => '84058', + card_number => '4111111111111111', + expiration => '09/02', + ); + $tx->submit(); + + if($tx->is_success()) { + print "Update processed successfully."\n"; + } else { + print "Update was rejected: ".$tx->error_message."\n"; + } + $tx->content( + subscription => '99W2D', + login => 'testdrive', + password => 'testpass', + action => 'Cancel Recurring Authorization', + ); + $tx->submit(); + + #### + # Subscription cancellation. It happens. + #### + + if($tx->is_success()) { + print "Cancellation processed successfully."\n"; + } else { + print "Cancellation was rejected: ".$tx->error_message."\n"; + } + + =head1 SUPPORTED TRANSACTION TYPES -=head2 Visa, MasterCard, American Express, Discover +=head2 CC, Visa, MasterCard, American Express, Discover -Content required: type, login, password, action, amount, first_name, last_name, card_number, expiration. +Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration. =head2 Check -Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, bank_name. +Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name (non-subscription), account_type (subscription), check_type (subscription). + +=head2 Subscriptions + +Additional content required: interval, start, periods. =head1 DESCRIPTION For detailed information see L. +=head1 METHODS AND FUNCTIONS + +See L for the complete list. The following methods either override the methods in L or provide additional functions. + +=head2 result_code + +Returns the response reason code (from the message.code field for subscriptions). + +=head2 error_message + +Returns the response reason text (from the message.text field for subscriptions. + +=head2 server_response + +Returns the complete response from the server. + +=head1 Handling of content(%content) data: + +=head2 action + +The following actions are valid + + normal authorization + authorization only + credit + post authorization + void + recurring authorization + modify recurring authorization + cancel recurring authorization + +=head2 interval + + Interval contains a number of digits, whitespace, and the units of days or months in either singular or plural form. + + +=head1 Setting AuthorizeNet ARB parameters from content(%content) + +The following rules are applied to map data to AuthorizeNet ARB parameters +from content(%content): + + # ARB param => $content{} + merchantAuthentication + name => 'login', + transactionKey => 'password', + subscription + paymentSchedule + interval + length => \( the digits in 'interval' ), + unit => \( days or months gleaned from 'interval' ), startDate => 'start', + totalOccurrences => 'periods', + trialOccurrences => 'trialperiods', + amount => 'amount', + trialAmount => 'trialamount', + payment + creditCard + cardNumber => 'card_number', + expiration => \( $year.'-'.$month ), # YYYY-MM from 'expiration' + bankAccount + accountType => 'account_type', + routingNumber => 'routing_code', + accountNumber => 'account_number, + nameOnAccount => 'name', + bankName => 'bank_name', + echeckType => 'check_type', + order + invoiceNumber => 'invoice_number', + description => 'description', + customer + type => 'customer_org', + id => 'customer_id', + email => 'email', + phoneNumber => 'phone', + faxNumber => 'fax', + driversLicense + number => 'license_num', + state => 'license_state', + dateOfBirth => 'license_dob', + taxid => 'customer_ssn', + billTo + firstName => 'first_name', + lastName => 'last_name', + company => 'company', + address => 'address', + city => 'city', + state => 'state', + zip => 'zip', + country => 'country', + shipTo + firstName => 'ship_first_name', + lastName => 'ship_last_name', + company => 'ship_company', + address => 'ship_address', + city => 'ship_city', + state => 'ship_state', + zip => 'ship_zip', + country => 'ship_country', + =head1 NOTE Unlike Business::OnlinePayment or pre-3.0 verisons of Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and last_name fields. +Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced +Integration Method (AIM) (formerly known as ADC direct response)" and +"Automatic Recurring Billing (ARB)", sending a username and transaction_key +or password with every transaction. Therefore, Authorize.Net's +referrer "security" is not necessary. In your Authorize.Net interface at +https://secure.authorize.net/ make sure the list of allowable referers is +blank. Alternatively, set the B field in the transaction content. + To settle an authorization-only transaction (where you set action to 'Authorization Only'), submit the nine-digit transaction id code in the field "order_number" with the action set to "Post Authorization". @@ -233,6 +379,10 @@ order_number method on the object returned from the authorization. You must also submit the amount field with a value less than or equal to the amount specified in the original authorization. +For the subscription actions an authorization code is never returned by +the module. Instead it returns the value of subscriptionId in order_number. +This is the value to use for changing or cancelling subscriptions. + Recently (February 2002), Authorize.Net has turned address verification on by default for all merchants. If you do not have valid address information for your customer (such as in an IVR @@ -242,21 +392,42 @@ aren't denied due to a lack of address information. =head1 COMPATIBILITY -This module implements Authorize.Net's API verison 3.1 using the ADC -Direct Response method. See -https://secure.authorize.net/docs/developersguide.pml for details. +This module implements Authorize.Net's API using the Advanced Integration +Method (AIM) version 3.1, formerly known as ADC Direct Response and the +Automatic Recurring Billing version 1.0 using the XML interface. See +http://www.authorize.net/support/AIM_guide.pdf and http://www.authorize.net/support/ARB_guide.pdf for details. =head1 AUTHOR Jason Kohles, jason@mediabang.com Ivan Kohler updated it for Authorize.Net protocol -3.0/3.1 and is the current maintainer. +3.0/3.1 and is the current maintainer. Please send patches as unified diffs +(diff -u). Jason Spence contributed support for separate Authorization Only and Post Authorization steps and wrote some docs. OST paid for it. +Jeff Finucane added the ARB support. +ARB support sponsored by Plus Three, LP. L. + +T.J. Mather sent a number of CVV2 patches. + +Mike Barry sent in a patch for the referer field. + +Yuri V. Mkrtumyan sent in a patch to add the void action. + +Paul Zimmer sent in a patch for +card-less post authorizations. + +Daemmon Hughes sent in a patch for "transaction +key" authentication as well support for the recurring_billing flag and the md5 +method that returns the MD5 hash which is returned by the gateway. + +Steve Simitzis contributed a patch for better compatibility with +eProcessingNetwork's AuthorizeNet compatability mode. + =head1 SEE ALSO perl(1). L.