Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Interfacing to Authorize.Net

by THRAK (Monk)
on Nov 08, 2001 at 23:52 UTC ( [id://124154]=perlquestion: print w/replies, xml ) Need Help??

THRAK has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a script to interface to Authorize.Net and seem to be getting nowhere fast. I've never used Net::SSLeay which is what the outdated example script uses, although I'm using the newer V1.09. I've searched here and the net at large and haven't found anything (that's not $200 anyway!) about interfacing to Authorize.Net. If I can get this working I figure not only do I benefit, but other Perlers who may need to tread this road may do so with less effort. I talked to AuthNet technical support that gave me a different address that will echo back the form values that are sent, and it would appear that things are not being properly sent. The person I talked to didn't quite know how to proceed and I'm currently awaiting a return call from someone else. Incidentally I went back and ran the example script and it fails in the same fashion as I describe below. My script is based heavily upon the example script, but has been modified to run under strict. I've found a couple of problems/concerns when I set the SSLeay trace to 3...

The first being Odd number of elements in hash assignment at ./an_pm.pl line 65. The script then attempts to connect to the server...

Opening connection to server105.hypermart.net:443 (63.251.5.33)

Followed by ...

Argument "server105.hypermart.net" isn't numeric in subroutine entry at /usr/local/lib/perl5/5.6.1/i686-linux/Socket.pm line 442.

Things then seem to proceed as normal. When accessing AuthNet I get an "Invalid Merchant Login or Account Inactive" reply. When accessing the test server, it sends back one field named "1/8" with no value assigned (obviously because it's not a field that was sent). I'm guessing my problems are related to the two errors above, but I don't know what is causing them. It seems to establish a connection, but then is sending wrong/corrupted data. The code...
#!/usr/bin/perl -w # Based upon the work of Doc Web... # Copyright 08.07.1999 by Doc Webb, webmaster@e-business-hosting.com # All rights reserved. # USAGE: # authnet.pl <first_name> <last_name> <CC_number> <expiration_date> + <charge_amount> ################ ### includes ### ################ use strict; use Net::SSLeay qw(post_https make_headers make_form); $Net::SSLeay::ssl_version = 3; # force version in OpenSSL (autodetect +broken?) $Net::SSLeay::trace = 3; ############################ ### Script Configuration ### ############################ $|++; #unbuffer output my $debug = 1; ################################### ### Authorize.Net Configuration ### ################################### # These all related to setting in the Authorize.Net Developer's Guide my $login = 'testdrive'; #Authorize.Net login name my $test_mode = 'TRUE'; # TRUE=test, FALSE=live!!! #Generally, these shouldn't need to be changed... my $adc_delim_data = 'TRUE'; my $adc_url = 'FALSE'; my $authnet_ver = '3.0'; #my $host = 'secure.authorize.net'; #my $script = '/gateway/transact.dll'; my $host = 'server105.hypermart.net'; my $script = '/testan/getpost.cgi'; my $port = '443'; ### Charge Form Data ### my $first_name = shift || 'John'; my $last_name = shift || 'Doe'; my $card_num = shift || '4222222222222'; my $expiration_date = shift || '1102'; my $charge_amount = shift || '1'; my $authorization_type = "AUTH_ONLY"; my $auth_type = 'AUTH_ONLY'; my $payment_method = 'CC'; ### Reply Data ### my ($reply_data, $reply_type, %reply_headers); ###### DO NOT CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOUR DOI +NG! ###### ################################# ### Authorize.Net Interaction ### ################################# #Build form data... my %form_data = make_form + ( + 'x_Login' => $login ,'x_Version' => $authnet_ver ,'x_ADC_Delim_Data' => $adc_delim_data ,'x_ADC_URL' => $adc_url ,'x_Test_Request' => $test_mode ,'x_Type' => $auth_type ,'x_Method' => $payment_method ,'x_First_Name' => $first_name ,'x_Last_Name' => $last_name ,'x_Amount' => $charge_amount ,'x_Card_Num' => $card_num ,'x_Exp_Date' => $expiration_date ); # Send data via $form_data{'payment_method'}he same encrypted channel. + ($reply_data, $reply_type, %reply_headers) = post_https($host, $port, +$script, '', %form_data); ### SHOULD PUT A COMM FAILURE CHECK HERE ### if ($debug) { # print "Content-type: text/html\n\n"; print "Sent the following string:<br><br>\n"; print "https://${host}${script}?x_Login=$login&x_Version=$authnet_ +ver&x_ADC_Delim_Data=$adc_delim_data&x_ADC_URL=$adc_url&x_Type=$auth_ +type&x_Test_Request=$test_mode&x_Method=$payment_method&x_First_Name= +$first_name&x_Last_Name=$last_name&x_Amount=$charge_amount&x_Card_Num +=$card_num&x_Exp_Date=$expiration_date<BR><BR>\n"; # Display the reply headers. print "Reply headers received from v3.0 transact.dll:<BR>\n"; foreach my $key (keys %reply_headers) { print "$key: $reply_headers{$key}<BR>\n"; } print "<BR>Reply type specification received from v3.0 transact.dl +l:<BR> $reply_type<BR>\n"; # split out the returned fields my @data = split (/\,/, $reply_data); # Print the unparsed reply_data. print STDOUT "<BR>Unparsed reply data:<BR> $reply_data<BR>\n"; print "<BR>Data returned by v3.0 transact.dll (see v3.0 transact.d +ll documentation for a descriptions of each data item ret +urned):<BR>\n"; my $data_item = 1; foreach(@data) { print "Item $data_item: $_<BR>\n"; $data_item++; } } __END__


-THRAK
www.polarlava.com

Replies are listed 'Best First'.
Re: Interfacing to Authorize.Net
by traveler (Parson) on Nov 09, 2001 at 00:01 UTC
    Just for fun, instead of printing the fields directly in the if($debug), try calling Data::Dumper to show what is actually in %form_data as that is what gets sent via post_https.

    Also, is there a way you can contact their server unencrypted? If so, you could send the test data and use a tool like ethereal (win32 or *nix) or tcpdump to see what is actually going over the wire.

    HTH, --traveler

      I don't believe you can access AuthNet unencrypted. If so, I don't know how and they don't seem to publicize it. Tried Data::Dumper as you suggested:

      $VAR1 = 'x_Login=testdrive&x_Version=3.0&x_ADC_Delim_Data=TRUE&x_ADC_U +RL=FALSE&x_Test_Request=TRUE&x_Type=AUTH_ONLY&x_Method=CC&x_First_Nam +e=John&x_Last_Name=Doe&x_Amount=1&x_Card_Num=4222222222222&x_Exp_Date +=1102'; $VAR2 = undef;
      I would guess that $VAR2 = undef might be part of the problem.

      -THRAK
      www.polarlava.com
        What did your call to Dumper look like? Did you send anything besides %form_data? If not, it looks as though make_form is not really very special...

        --traveler

Re: Interfacing to Authorize.Net
by THRAK (Monk) on Nov 09, 2001 at 02:20 UTC
    I went back looking at the Net::SSLeay docs and I changed the "Authorize.Net Interaction" section to make it more like the example shown (3b) and now the server is spitting things back. Once I get things more organized I will write it up and post it, so further suggestions are welcome. Also if somebody sees the problem and can explain what is wrong with the way the original code uses the hash I'd appreciate the insight.
    ################################# ### Authorize.Net Interaction ### ################################# ($reply_data, $reply_type, %reply_headers) = post_https($host, $port, +$script, '', make_form ( + 'x_Login' => $login ,'x_Version' => $authnet_ver ,'x_ADC_Delim_Data' => $adc_delim_data ,'x_ADC_URL' => $adc_url ,'x_Test_Request' => $test_mode ,'x_Type' => $auth_type ,'x_Method' => $payment_method ,'x_First_Name' => $first_name ,'x_Last_Name' => $last_name ,'x_Amount' => $charge_amount ,'x_Card_Num' => $card_num ,'x_Exp_Date' => $expiration_date ) );


    -THRAK
    www.polarlava.com
      Have you posted the code for Interfacing to Authorize.Net? I really want to have the script to test the authorize.net aim method.
Re: Interfacing to Authorize.Net
by monecky (Initiate) on Nov 09, 2001 at 05:47 UTC
    Have you tried Business::OnlinePayment? It gives you a generic interface to several credit card processors. Business::OnlinePayment::AuthorizeNet on CPAN seems to be broken with the newest version of AuthorizeNet's EDI. I fixed AuthorizeNet.pm according to their Appendix last month, but I haven't yet sent back the changes to the maintainer. Here is my modified AuthorizeNet.pm (ugly)

    Here

    I have this working production, so I could give you a hand if it doesn't work out-o-box for you.

    Paul
      DOH! I went to AuthNet and found their less than adequate answers and then took off with Net::SSLeay. I searched high and low for info on interfacing to them, but I never searched CPAN for A.Net! What was I thinking? Anyhow, the Business::OnlinePayment stuff looks pretty good, but I'm going to stick with the road I'm currently traveling. If I was implementing this completely from scratch in Perl I would scrap it and go with Business::OnlinePayment. But the site I'm working is all in PHP and has been using the WebLink process. They want to switch to ADC, but the (virtually hosted) server doesn't have PHP configured to do this directly, so I'm going to drop this chunk of Perl inline to do the processing and pass the results back to the PHP code. Not ideal, but it will fit into what is already there without too much trouble.

      Thanks

      -THRAK
      www.polarlava.com
Re: Interfacing to Authorize.Net
by lil sister (Initiate) on Nov 18, 2001 at 04:53 UTC
    THRAK - don't know if you came up with the code to interface with authorize.net yet - but here is some code that I worked out recently for version 3.0 of the authorize.net interface. Hope it helps...
    #!/usr/bin/perl -wT ###################################################################### +################# # Copyright 2001. General Public License # # You can use, modify and redistribute this software under the terms o +f the GNU # General Public License as published by the Free Software Foundation. + And provided # that this header appear on all copies of the software. This program +is distributed # without warranty. # # References: http://www.perldoc.com/cpan/Net/SSLeay.html # http://remus.prakinf.tu-ilmenau.de/ssl-users/arc +hive29/0042.html # ###################################################################### +################# use strict; use CGI qw(:standard); use Text::CSV; use Net::SSLeay qw(post_https make_headers make_form); my $DEBUG = 1; ############################### Credit Card Data ############ +################### # only address number and zip code required for AVS my $first_name = 'Firstname'; my $last_name = 'Lastname'; my $address = 'Address'; my $city = 'City'; my $state = 'State'; my $zip = 'Zip'; my $email = 'Email'; my $cc_type = 'VISA'; my $cc_number = '4222222222222'; my $exp_date = '11/2002'; my $cc_amount = '1.00'; my $description = 'Description'; ############################### Authorize.Net Configuration # +############################## my $login = 'login'; # Authorize.Net login name my $test_mode; if ($DEBUG) { $test_mode = 'TRUE'; # TRUE=test } else { $test_mode = 'FALSE'; # FALSE=live } # for the most part these should remain the same my $adc_delim_data = 'TRUE'; # return results in machine read +able format my $adc_url = 'FALSE'; # no return url my $auth_version = '3.0'; # version of authorize.net gatew +ay code # version 3.0 defaults my $server = 'secure.authorize.net'; my $path = '/gateway/transact.dll'; my $port = '443'; # if other authorization types will be accepted, may need to make +it an argument # password is not needed for auth_capture type my $auth_type = 'AUTH_CAPTURE'; my $password = ''; ### check for required data for this auth type such as credit card, et +c. # setup ADC form fields my %submit_data = (x_Login => "$login", x_Password => "$password", x_Version => "$auth_version", x_ADC_Delim_Data => "$adc_delim_data", x_ADC_URL => "$adc_url", x_Test_Request => "$test_mode", x_Type => "$auth_type", x_Description => "$description", x_First_Name => "$first_name", x_Last_Name => "$last_name", x_Address => "$address", x_City => "$city", x_State => "$state", x_Zip => "$zip", x_Email => "$email", x_Card_Num => "$cc_number", x_Exp_Date => "$exp_date", x_Amount => "$cc_amount"); ############################### Process the Transaction ##### +########################## # make the form data my $post_data = &make_form(%submit_data); # Send data via SSL (encrypted channel) and wait for reply # $page is the content as defined by http (usually your HTML page) # $response is the first line sent back on hte SSL connection. # %headers is a hash of HTTP headers/response codes sent back my($page,$response,%headers) = &post_https($server,$port,$path,'',$p +ost_data); ############################### See What Was Returned ######## +####################### print header(), start_html(); # if $page is defined, success! if (defined $page) { if ($DEBUG) { print h3('Sent the following string:'), "https://${server}${path}?$post_data<BR><BR>\n"; # Display the reply headers. print h4('Headers received from v3.0 transact.dll:'); foreach my $key (keys %headers) { print "$key: $headers{$key}<BR>\n"; } print h4('Server response received from v3.0 transact.dll:'), "$response<br>\n"; #parse the page my $csv = new Text::CSV(); $csv->parse($page); my @col = $csv->fields(); print h4("see Developer's Guide - Appendix C - Response Codes"); print "Response Code (0): $col[0].<br>"; print "Response Subcode (1): $col[1]<br>\n"; print "Response Reason Code (2): $col[2]<br>\n"; print "Response Reason Text (3): $col[3]<br>\n"; print "Authorization Code (4): $col[4]<br>\n"; print "AVS Code (5): $col[5]<br>\n"; print "Trans ID (6): $col[6]<br>\n", p; my $elements = scalar(@col); my $fields = $elements - 7; print "<b>Total Number of Fields: $fields</b><br>"; my ($i, $item); for ($i=7; $i<$elements; $i++) { $item = $i - 6; print "$item. ($i) $col[$i]<BR>\n"; } } } else { # return empty list (or was it undef) if they fail to connection +(or any internal errors) print "Failed to open tcp connection, SSL connection negotiation f +ailed or there was an internal error." } print end_html;
      I am having a problem interfacing with Authorize.net as well. I am creating a perl module using Net::SSLeay in a similay way as listed in this thread and other sources I have found.

      The problem comes when I perform a post_https operation. Every time I attempt to post, I receive the error message "Invalid Merchant Login or Account Inactive". However, I turn right around and perform a get_https operation using the same information and achieve desired results (i.e. a delimited response string).

      Am I doing something wrong? Will using the get_https method produce the desired results? I can provide more info on what I am doing, if needed. Please someone help!!!

      Below is my module code:

      package Authnet; my($VERSION) = '1.00'; use strict; no strict 'refs'; use Net::SSLeay qw(get_https post_https sslcat make_headers make_form) +; #--------------------------------------------------------------------- +--------------------------- ## BEGIN SUBS HERE #--------------------------------------------------------------------- +--------------------------- sub new { my $self = bless({},shift); my $parm = shift; $self->{_prog_name} = $parm->{prog_name} or croak("No prog_name pa +rm"); $self->{_conf_path} = $parm->{conf_path} or croak("No conf_path pa +rm"); $self->initialize(); return($self); } #--------------------------------------------------------------------- +-------------- sub AUTOLOAD { # Handle the case where an undefined subroutine is called return(''); } #--------------------------------------------------------------------- +-------------- sub DESTROY { my($self) = shift; $self->finish(); } #--------------------------------------------------------------------- +-------------- sub finish { my($self) = shift; } #--------------------------------------------------------------------- +-------------- sub initialize { my($self) = shift; require $self->{_conf_path}; if (defined(%conf::data)) { $self->{_conf} = \%conf::data; } $self->{_error} = undef; $self->{_err_txt} = undef; } #--------------------------------------------------------------------- +-------------- #this sub processes the payment transaction via authorize.net #--------------------------------------------------------------------- +-------------- sub auth_trans{ my ($self) = shift; my ($ref_url) = shift; my ($sub_id) = shift; my ($f_name) = shift; my ($l_name) = shift; my ($addr) = shift; my ($city) = shift; my ($state) = shift; my ($zip) = shift; my ($card_no) = shift; my ($cvv2) = shift; my ($exp_date) = shift; my ($amt) = shift; my ($DEBUG) = 1; if (!$sub_id){ $self->{_error} = 97; $self->system_notify(); return(0); } if (!$f_name || !$l_name || !$card_no || !$exp_date || !$amt){ $self->{_error} = 33; $self->{_err_txt} = 'First name, Last Name, Card Number, Exp D +ate, or Amount'; return(0); } my (@auth_array) = (); my($host) = $self->{_conf}{'Authnet_host'}; my($script) = $self->{_conf}{'Authen_script'}; my ($url) = $self->{_conf}{'Authnet_URL'}; my($headers) = make_headers('Connection' => 'close', 'Referer' => $ref_url, 'Accept-Language' => 'en-us', 'User-Agent' => 'Mozilla/4.0 (compatib +le; AIT 1.1)'); my ($form) = make_form('x_Login' => $self->{_conf}{'x_Login'}, 'x_Password' => $self->{_conf}{'x_Password' +}, 'x_ADC_Delim_Character' => $self->{_conf}{' +x_ADC_Delim_Character'}, 'x_ADC_Delim_Data' => $self->{_conf}{'x_ADC +_Delim_Data'}, 'x_Encapsulate_Character' => $self->{_conf} +{'x_Encapsulte_Character'}, 'x_ADC_URL' => $self->{_conf}{'x_ADC_URL'}, 'x_Method' => $self->{_conf}{'x_Method'}, 'x_Type' => $self->{_conf}{'x_Type'}, 'x_Version' => $self->{_conf}{'x_Version'}, 'x_Description' => $self->{_conf}{'x_Descri +ption'}, 'x_Cust_id' => $sub_id, 'x_First_Name' => $f_name, 'x_Last_Name' => $l_name, 'x_Address' => $addr, 'x_City' => $city, 'x_State' => $state, 'x_Zip' => $zip, 'x_Card_Num' => $card_no, 'x_Card_Code' => $cvv2, 'x_Exp_Date' => $exp_date, 'x_Amount' => $amt); if ($DEBUG){ print "\nFORM> $form\n"; print "\nHEADERS> $headers\n"; } my($page1,$response,%reply_headers1) = post_https($host,'443',$scr +ipt,'',$form); print "\nPAGE1> $page1...$response\n"; if ($DEBUG){ my($key) = undef; foreach $key (sort keys %reply_headers1){ print "$key -> $reply_headers1{$key}\n"; } } my($x) = undef; ############attempt to contact transact.dll at least 5 times, exi +t loop if sucessfull, else generate error####### for($x=0;$x<5;$x++){ my ($page2,$result,%reply_headers2) = get_https($host,'443',"$ +script?$form"); print "\nPAGE2> $page2...$result\n"; if($result =~ m/OK/){ @auth_array = split(/,/,$page2); $self->{_error} = $auth_array[2]; last; }else{ $self->{_error} = 99; } if ($DEBUG){ $key = undef; foreach $key (sort keys %reply_headers2){ print "$key -> $reply_headers2{$key}\n"; } } } if($self->{_error} > 1){ #-- if error --# ###### if the error is a system error, then notify system +contact ###### if ($self->{_conf}{'x_response_reason_code'}{$self->{_erro +r}}{'System'} == 1){ $self->system_notify($sub_id); } ###### if omitted field error, then get field name for cus +tom message ###### if ($self->{_error} == 33){ $auth_array[3] =~ /^(.+)\scannot/; $self->{_err_txt} = $1; } return(0); }else{ ##### if success, return x_trans_id ##### return(1,$auth_array[6]); } } #--------------------------------------------------------------------- +------------- #this sub returns error text #--------------------------------------------------------------------- +-------------- sub error{ my($self) = shift; my($t) = $self->{_conf}{'x_response_reason_code'}{$self->{_error}} +{'Text'}; if ($self->{_err_txt}){ $t .= " $self->{_err_txt}"; } undef($self->{_error}); return($t); } #--------------------------------------------------------------------- +-------------- #this sub emails system contact in the event of a system error #--------------------------------------------------------------------- +-------------- sub system_notify{ my($self) = shift; my($sub_id) = shift; my($error) = $self->{_conf}{'x_response_reason_code'}{$self->{_err +or}}{'Text'}; if ($DEBUG){print "SUB_ID => $sub_id\nERROR => $error\n";} my($mailto) = 'ccgateway@cinergycom.com'; my($smail) = '/usr/lib/sendmail'; open (MAIL, "|$smail -f $mailto gmorris\@evansville.net") or die " +Error sending email:$!"; print MAIL <<EOM; From: Authorize.net Processing Gateway <$mailto> To: Authorize.net Processing Gateway <$mailto> Subject: A system error has occurred The following error has occurred while processing a credit card transaction for customer $sub_id: Error $self->{_error}: $error Calling Program: $self->{_prog_name} EOM close MAIL; $self->{_error} = 98; } 1;
        Hi All,

        Apologies for bringing up an old thread. I would like to use 'lil sisers' code snippet to process cc transactions on my site. But when reading the cc numbers into the program and storing them in a variable the number would go into the system memory, is this something I should be concerned about? or anyway to prevent it?

        Thanks a lot

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://124154]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (3)
As of 2024-04-19 17:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found