[ic] payment module authorizenet (modifications)
Philip S. Hempel
interchange-users@icdevgroup.org
Wed May 28 19:52:01 2003
This is so far what I have for the cvv2 support there are other
requirements to get this blasted thing going. If you have a two line
address it is will not work.
I do not know enough perl yet I need to under stand how to get use split
and my poor use of regex, since any field that gets extended needs to
bee encapsulated.
Then if you want the data that it kicks back to be usable it needs to be
parsed based on the delim char.
Hopefully I will get this done tomorrow, and then the tough part is
going to be getting IC to decide to use the card service to be used
during a E-Check order. I think once all the error codes get added in
and figuring out the environments that are willing to be passed to the
module things will get smoother.
I thought at first we (I) was using SIM and the response codes would be
in the data along with the name, guess what there not. AIM is being used
and I get the comma (or whatever you define) delimited list returned
back, YEA! Because of the way the list returns I had to predefine the
location of the fields, exactly as they are returned.
There will be at least another 10 or more fields that need to be defined
for the E-Check support as well, but I have not gotten there yet.
Later all, the code follows.
# Vend::Payment::AuthorizeNet - Interchange AuthorizeNet support
#
# Connection routine for AuthorizeNet version 3 using the 'ADC Direct
Response'
# method.
#
# $Id: AuthorizeNet.pm,v 2.8 2003/05/21 20:22:15 jon Exp $
#
# Copyright (C) 2003 Interchange Development Group,
http://www.icdevgroup.org/
# Copyright (C) 1999-2002 Red Hat, Inc.
#
# Authors:
# mark@summersault.com
# Mike Heins <mike@perusion.com>
# Jeff Nappi <brage@cyberhighway.net>
# Paul Delys <paul@gi.alaska.edu>
# webmaster@nameastar.net
# Ray Desjardins <ray@dfwmicrotech.com>
# Nelson H Ferrari <nferrari@ccsc.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA.
package Vend::Payment::AuthorizeNet;
=head1 Interchange AuthorizeNet Support
Vend::Payment::AuthorizeNet $Revision: 2.8 $
=head1 SYNOPSIS
&charge=authorizenet
or
[charge mode=authorizenet param1=value1 param2=value2]
=head1 PREREQUISITES
Net::SSLeay
or
LWP::UserAgent and Crypt::SSLeay
Only one of these need be present and working.
=head1 DESCRIPTION
The Vend::Payment::AuthorizeNet module implements the authorizenet()
routine
for use with Interchange. It is compatible on a call level with the
other
Interchange payment modules -- in theory (and even usually in practice)
you
could switch from CyberCash to Authorize.net with a few configuration
file changes.
To enable this module, place this directive in C<interchange.cfg>:
Require module Vend::Payment::AuthorizeNet
This I<must> be in interchange.cfg or a file included from it.
Make sure CreditCardAuto is off (default in Interchange demos).
The mode can be named anything, but the C<gateway> parameter must be set
to C<authorizenet>. To make it the default payment gateway for all
credit
card transactions in a specific catalog, you can set in C<catalog.cfg>:
Variable MV_PAYMENT_MODE authorizenet
It uses several of the standard settings from Interchange payment. Any
time
we speak of a setting, it is obtained either first from the tag/call
options,
then from an Interchange order Route named for the mode, then finally a
default global payment variable, For example, the C<id> parameter would
be specified by:
[charge mode=authorizenet id=YourAuthorizeNetID]
or
Route authorizenet id YourAuthorizeNetID
or
Variable MV_PAYMENT_ID YourAuthorizeNetID
The active settings are:
=over 4
=item id
Your Authorize.net account ID, supplied by Authorize.net when you sign
up.
Global parameter is MV_PAYMENT_ID.
=item secret
Your Authorize.net account password, supplied by Authorize.net when you
sign up.
Global parameter is MV_PAYMENT_SECRET. This may not be needed for
actual charges.
=item referer
A valid referering url (match this with your setting on
secure.authorize.net).
Global parameter is MV_PAYMENT_REFERER.
=item transaction
The type of transaction to be run. Valid values are:
Interchange AuthorizeNet
---------------- -----------------
auth AUTH_ONLY
return CREDIT
reverse PRIOR_AUTH_CAPTURE
sale AUTH_CAPTURE
settle CAPTURE_ONLY
void VOID
=item remap
This remaps the form variable names to the ones needed by Authorize.net.
See
the C<Payment Settings> heading in the Interchange documentation for
use.
Minimum needs to have mv_credit_card_cvv2 remapped to use cvv2
and field name in checkout needs to match above
<Variable MV_PAYMENT_REMAP>
mv_credit_card_cvv2 mv_credit_card_cvv2
mv_order_number mv_order_number
mv_username mv_username
</Variable>
=item test
Set this to C<TRUE> if you wish to operate in test mode, i.e. set the
Authorize.net
C<x_Test_Request> query paramter to TRUE.i
Examples:
Route authorizenet test TRUE
or
Variable MV_PAYMENT_TEST TRUE
or
[charge mode=authorizenet test=TRUE]
=back
=head2 Troubleshooting
Try the instructions above, then enable test mode. A test order should
complete.
Disable test mode, then test in various Authorize.net error modes by
using the credit card number 4222 2222 2222 2222.
Then try a sale with the card number C<4111 1111 1111 1111>
and a valid expiration date. The sale should be denied, and the reason
should
be in [data session payment_error].
If nothing works:
=over 4
=item *
Make sure you "Require"d the module in interchange.cfg:
Require module Vend::Payment::AuthorizeNet
=item *
Make sure either Net::SSLeay or Crypt::SSLeay and LWP::UserAgent are
installed
and working. You can test to see whether your Perl thinks they are:
perl -MNet::SSLeay -e 'print "It works\n"'
or
perl -MLWP::UserAgent -MCrypt::SSLeay -e 'print "It works\n"'
If either one prints "It works." and returns to the prompt you should be
OK
(presuming they are in working order otherwise).
=item *
Check the error logs, both catalog and global.
=item *
Make sure you set your payment parameters properly.
=item *
Try an order, then put this code in a page:
<XMP>
[calc]
my $string = $Tag->uneval( { ref => $Session->{payment_result}
});
$string =~ s/{/{\n/;
$string =~ s/,/,\n/g;
return $string;
[/calc]
</XMP>
That should show what happened.
=item *
If all else fails, consultants are available to help with integration
for a fee.
See http://www.icdevgroup.org/ for mailing lists and other information.
=back
=head1 BUGS
There is actually nothing *in* Vend::Payment::AuthorizeNet. It changes
packages
to Vend::Payment and places things there.
=head1 AUTHORS
Mark Stosberg <mark@summersault.com>.
Based on original code by Mike Heins <mike@perusion.com>.
=head1 CREDITS
Jeff Nappi <brage@cyberhighway.net>
Paul Delys <paul@gi.alaska.edu>
webmaster@nameastar.net
Ray Desjardins <ray@dfwmicrotech.com>
Nelson H. Ferrari <nferrari@ccsc.com>
=cut
BEGIN {
my $selected;
eval {
package Vend::Payment;
require Net::SSLeay;
import Net::SSLeay qw(post_https make_form make_headers);
$selected = "Net::SSLeay";
};
$Vend::Payment::Have_Net_SSLeay = 1 unless $@;
unless ($Vend::Payment::Have_Net_SSLeay) {
eval {
package Vend::Payment;
require LWP::UserAgent;
require HTTP::Request::Common;
require Crypt::SSLeay;
import HTTP::Request::Common qw(POST);
$selected = "LWP and Crypt::SSLeay";
};
$Vend::Payment::Have_LWP = 1 unless $@;
}
unless ($Vend::Payment::Have_Net_SSLeay or $Vend::Payment::Have_LWP) {
die __PACKAGE__ . " requires Net::SSLeay or Crypt::SSLeay";
}
::logGlobal("%s payment module initialized, using %s", __PACKAGE__,
$selected)
unless $Vend::Quiet;
}
package Vend::Payment;
sub authorizenet {
my ($user, $amount) = @_;
my $opt;
if(ref $user) {
$opt = $user;
$user = $opt->{id} || undef;
$secret = $opt->{secret} || undef;
}
else {
$opt = {};
}
my $actual;
if($opt->{actual}) {
$actual = $opt->{actual};
}
else {
my (%actual) = map_actual();
$actual = \%actual;
}
#::logDebug("actual map result: " . ::uneval($actual));
if (! $user ) {
$user = charge_param('id')
or return (
MStatus => 'failure-hard',
MErrMsg => errmsg('No account id'),
);
}
$secret = charge_param('secret') if ! $secret;
$opt->{host} ||= 'secure.authorize.net';
$opt->{script} ||= '/gateway/transact.dll';
$opt->{port} ||= 443;
my $precision = $opt->{precision}
|| 2;
my $referer = $opt->{referer}
|| charge_param('referer');
my $trankey = $opt->{trankey} || undef;
my @override = qw/
order_id
auth_code
mv_credit_card_exp_month
mv_credit_card_exp_year
mv_credit_card_number
/;
for(@override) {
next unless defined $opt->{$_};
$actual->{$_} = $opt->{$_};
}
## Authorizenet does things a bit different, ensure we are OK
$actual->{mv_credit_card_exp_month} =~ s/\D//g;
$actual->{mv_credit_card_exp_month} =~ s/^0+//;
$actual->{mv_credit_card_exp_year} =~ s/\D//g;
$actual->{mv_credit_card_exp_year} =~ s/\d\d(\d\d)/$1/;
$actual->{mv_credit_card_number} =~ s/\D//g;
my $exp = sprintf '%02d%02d',
$actual->{mv_credit_card_exp_month},
$actual->{mv_credit_card_exp_year};
# Using mv_payment_mode for compatibility with older versions, #
probably not
# necessary.
$opt->{transaction} ||= 'sale';
my $transtype = $opt->{transaction};
my %type_map = (
AUTH_ONLY => 'AUTH_ONLY',
CAPTURE_ONLY => 'CAPTURE_ONLY',
CREDIT => 'CREDIT',
PRIOR_AUTH_CAPTURE => 'PRIOR_AUTH_CAPTURE',
VOID => 'VOID',
auth => 'AUTH_ONLY',
authorize => 'AUTH_ONLY',
mauthcapture => 'AUTH_CAPTURE',
mauthonly => 'AUTH_ONLY',
return => 'CREDIT',
settle_prior => 'PRIOR_AUTH_CAPTURE',
sale => 'AUTH_CAPTURE',
settle => 'CAPTURE_ONLY',
void => 'VOID',
);
if (defined $type_map{$transtype}) {
$transtype = $type_map{$transtype};
}
$amount = $opt->{total_cost} if $opt->{total_cost};
if(! $amount) {
$amount = Vend::Interpolate::total_cost();
$amount = Vend::Util::round_to_frac_digits($amount,$precision);
}
$order_id = gen_order_id($opt);
# ip_address used for extended secure services
$ip_address=$Vend::Session->{ohost};
#::logDebug("auth_code=$actual->{auth_code} order_id=$opt->{order_id}");
my %query = (
x_Test_Request => $opt->{test} || charge_param('test'),
x_First_Name => $actual->{b_fname},
x_Last_Name => $actual->{b_lname},
x_Company => $actual->{b_company},
x_Address => $actual->{b_address},
x_City => $actual->{b_city},
x_State => $actual->{b_state},
x_Zip => $actual->{b_zip},
x_Country => $actual->{b_country},
x_Ship_To_First_Name => $actual->{fname},
x_Ship_To_Last_Name => $actual->{lname},
x_Ship_To_Company => $actual->{company},
x_Ship_To_Address => $actual->{address},
x_Ship_To_City => $actual->{city},
x_Ship_To_State => $actual->{state},
x_Ship_To_Zip => $actual->{zip},
x_Ship_To_Country => $actual->{country},
x_Email => $actual->{email},
x_Phone => $actual->{phone_day},
x_Type => $transtype,
x_Amount => $amount,
x_Method => 'CC',
x_Card_Num => $actual->{mv_credit_card_number},
x_Exp_Date => $exp,
x_Card_Code => $actual->{mv_credit_card_cvv2},
x_Trans_ID => $actual->{order_id},
x_Invoice_Num => $actual->{mv_order_number},
x_Cust_ID => $actual->{mv_username},
x_Customer_IP => $ip_address,
x_Auth_Code => $actual->{auth_code},
x_Tran_Key => $trankey,
# fix this x_Password => $secret,
x_Login => $user,
x_Version => '3.1',
x_ADC_URL => 'FALSE',
x_Delim_Data => 'TRUE',
x_Delim_Data_Char => ',',
x_Encap_Char => ' ',
);
my @query;
for (keys %query) {
my $key = $_;
my $val = $query{$key};
$val =~ s/["\$\n\r]//g;
$val =~ s/\$//g;
my $len = length($val);
if($val =~ /[&=]/) {
$key .= "[$len]";
}
push @query, "$key=$val";
}
my $string = join '&', @query;
#::logDebug("Authorizenet query: " . ::uneval(\%query));
$opt->{extra_headers} = { Referer => $referer };
my $thing = post_data($opt, \%query);
my $page = $thing->{result_page};
my $response = $thing->{status_line};
# Minivend names are on the left, Authorize.Net on the right
my %result_map = ( qw/
pop.status x_response_code
pop.error-message x_response_reason_text
order-id x_trans_id
pop.order-id x_trans_id
pop.invoice_num x_invoice_num
pop.auth-code x_auth_code
pop.avs_code x_avs_code
pop.avs_zip x_zip
pop.avs_addr x_address
pop.cvv2_resp_code x_cvv2_resp_code
/
);
#::logDebug(qq{\nauthorizenet page: $page response: $response\n});
my %result;
@result{
qw/
x_response_code
x_response_subcode
x_response_reason_code
x_response_reason_text
x_auth_code
x_avs_code
x_trans_id
x_invoice_num
x_description
x_amount
x_method
x_type
x_cust_id
x_first_name
x_last_name
x_company
x_address
x_city
x_state
x_zip
x_country
x_phone
x_fax
x_email
x_ship_to_first_name
x_ship_to_last_name
x_ship_to_company
x_ship_to_address
x_ship_to_city
x_ship_to_state
x_ship_to_country
x_tax
x_duty
x_freight
x_tax_exempt
x_po_num
x_md5_hash
x_cvv2_resp_code
/
}
= split (/,/,$page);
#::logDebug(qq{authorizenet
#response_reason_text=$result{x_response_reason_text} response_code:
#$result{x_response_code} x_cvv2_resp_code=$result{x_cvv2_resp_code}});
for (keys %result_map) {
$result{$_} = $result{$result_map{$_}}
if defined $result{$result_map{$_}};
}
if ($result{x_response_code} == 1) {
$result{MStatus} = 'success';
$result{'order-id'} ||= $opt->{order_id};
}
else {
$result{MStatus} = 'failure';
delete $result{'order-id'};
# NOTE: A lot more AVS codes could be checked for here.
if ($result{x_avs_code} eq 'N') {
my $msg = $opt->{message_avs} ||
q{You must enter the correct billing address of your credit card.
The bank returned the following error: %s};
$result{MErrMsg} = errmsg($msg, $result{x_response_reason_text});
}
else {
my $msg = $opt->{message_declined} ||
"Authorizenet error: %s. Please call in your order or try again.";
$result{MErrMsg} = errmsg($msg, $result{x_response_reason_text});
}
}
#::logDebug(qq{authorizenet result=} . uneval(\%result));
return (%result);
}
package Vend::Payment::AuthorizeNet;
1;
--
Philip S. Hempel
Give a man a fish and he will feed himself for a day.
Teach a man to fish and he will feed himself for a lifetime.
http://linuxhardcore.com/