[ic] TrustCommerce payment module working

Dan Helfman interchange-users@interchange.redhat.com
Mon May 13 17:12:45 2002


--=-TiSkJIZINS4+adqOhFHT
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Thanks for the help everyone. I finally got the TrustCommerce payment
module playing nicely with Interchange. The problem was that Interchange
wasn't setting MV_PAYMENT_MODE to "trustcommerce", even though it was
defined in many configuration files. Also, for some reason my modified
Admin Wizard doesn't set TRUSTCOMMERCE_ID, although it does set
TRUSTCOMMERCE_SECRET. Anyway, I went into the Tables, setting those
values manually, and now the Trustcommerce module works.

I'm attaching the first version of the module to this message with the
hopes that it will be integrated into Interchange proper. If there is a
preferred method of submitting new code, please let me know. This
version of the module assumes a sale transaction (as opposed to an
auth/capture pair), and assumes AVS should be enabled. I will make these
options configurable as soon as I figure out how to get the Admin Wizard
to set values properly, rather than requiring the admin to modify the
tables manually.

I would appreciate any and all feedback.

-- 
Dan Helfman
Software Engineer, TrustCommerce
dan@trustcommerce.com
http://www.trustcommerce.com

--=-TiSkJIZINS4+adqOhFHT
Content-Disposition: attachment; filename=TCLink.pm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; name=TCLink.pm; charset=ANSI_X3.4-1968

# Vend::Payment::TCLink - Interchange TrustCommerce TCLink support
#
# Copyright (C) 2002 TrustCommerce <developer@trustcommerce.com>
#
# by Dan Helfman <dan@trustcommerce.com> with code reused and inspired by
#       Mark Stosberg <mark@summersault.com>
#	Mike Heins <mheins@redhat.com>
#	webmaster@nameastar.net
#	Jeff Nappi <brage@cyberhighway.net>
#	Paul Delys <paul@gi.alaska.edu>

# 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::TCLink;

=3Dhead1 Interchange TrustCommerce Support

Vend::Payment::TCLink $Revision: 1.0.0.0 $

=3Dhead1 SYNOPSIS

    &charge=3Dtrustcommerce
=20
        or
=20
    [charge mode=3Dtrustcommerce param1=3Dvalue1 param2=3Dvalue2]

=3Dhead1 PREREQUISITES

    Net::TCLink

http://www.trustcommerce.com/tclink.html and CPAN both have this module,
which actually does the bulk of the work. All that Vend::Payment::TCLink
does is to massage the payment data between Interchange and Net::TCLink.

=3Dhead1 DESCRIPTION

The Vend::Payment::TCLink module implements the trustcommerce() 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 TrustCommerce with a few configuration=20
file changes.

To enable this module, place this directive in C<interchange.cfg>:

    Require module Vend::Payment::TCLink

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<trustcommerce>. 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 trustcommerce

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 option=
s,
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=3Dtrustcommerce id=3DYourTrustCommerceID]

or

    Route trustcommerce id YourTrustCommerceID

or=20

    Variable MV_PAYMENT_ID YourTrustCommerceID

The active settings are:

=3Dover 4

=3Ditem id

Your TrustCommerce customer ID, supplied by TrustCommerce when you sign up.
Global parameter is MV_PAYMENT_ID.

=3Ditem secret

Your TrustCommerce customer password, supplied by TrustCommerce when you
sign up. Global parameter is MV_PAYMENT_SECRET.

=3Ditem transaction

The type of transaction to be run. Valid values are:

    Interchange         TrustCommerce
    ----------------    -----------------
        auth            preauth
        return          credit
        reverse         chargeback
        sale            sale
        settle          postauth

=3Ditem remap=20

This remaps the form variable names to the ones needed by TrustCommerce. Se=
e
the C<Payment Settings> heading in the Interchange documentation for use.

=3Ditem test

Set this to C<TRUE> if you wish to operate in test mode, i.e. set the
TrustCommerce C<demo> query paramter to TRUE.

Examples:=20

    Route trustcommerce test TRUE
        or
    Variable MV_PAYMENT_TEST TRUE
        or=20
    [charge mode=3Dtrustcommerce test=3DTRUE]

=3Dback

=3Dhead2 Troubleshooting

Try the instructions above, then enable test mode. A test order should comp=
lete.

Disable test mode, then test in various TrustCommerce 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 shou=
ld
be in [data session payment_error].

If nothing works:

=3Dover 4

=3Ditem *

Make sure you "Require"d the module in interchange.cfg:

    Require module Vend::Payment::TrustCommerce

=3Ditem *

Make sure Net::TCLink is installed and working. You can test to see
whether your Perl thinks they are:

    perl -MNet::TCLink -e 'print "It works.\n"'

If it "It works." and returns to the prompt you should be OK (presuming
they are in working order otherwise).

=3Ditem *

Check the error logs, both catalog and global.

=3Ditem *

Make sure you set your payment parameters properly. =20

=3Ditem *

Try an order, then put this code in a page:

    <XMP>
    [calc]
        my $string =3D $Tag->uneval( { ref =3D> $Session->{payment_result} =
});
        $string =3D~ s/{/{\n/;
        $string =3D~ s/,/,\n/g;
        return $string;
    [/calc]
    </XMP>

That should show what happened.

=3Ditem *

If all else fails, TrustCommerce consultants are available to help you out.

See http://www.trustcommerce.com/contact.html for more information.

=3Dback

=3Dhead1 BUGS

There is actually nothing *in* Vend::Payment::TrustCommerce. It changes
packages to Vend::Payment and places things there.

=3Dhead1 AUTHORS

Dan Helfman <dan@trustcommerce.com>, based on code by Mark Stosberg
<mark@summersault.com>, which was based on original code by Mike Heins
<mheins@redhat.com>.

=3Dhead1 CREDITS

    webmaster@nameastar.net
    Jeff Nappi <brage@cyberhighway.net>
    Paul Delys <paul@gi.alaska.edu>

=3Dcut

BEGIN {

	eval {
		package Vend::Payment;
        	require Net::TCLink or die __PACKAGE__ . " requires Net::TCLink";
	};

	::logGlobal("%s payment module initialized, using %s", __PACKAGE__)
		unless $Vend::Quiet;
}

package Vend::Payment;
sub trustcommerce {
	my ($user, $amount) =3D @_;

	my $opt;
	if(ref $user) {
		$opt =3D $user;
		$user =3D $opt->{id} || undef;
		$secret =3D $opt->{secret} || undef;
	}
	else {
		$opt =3D {};
	}
=09
	my $actual;
	if($opt->{actual}) {
		$actual =3D $opt->{actual};
	}
	else {
		my (%actual) =3D map_actual();
		$actual =3D \%actual;
	}

#::logGlobal("actual map result: " . ::uneval($actual));
	if (! $user ) {
		$user    =3D  charge_param('id')
						or return (
							MStatus =3D> 'failure-hard',
							MErrMsg =3D> errmsg('No customer id'),
							);
	}
=09
	$secret =3D  charge_param('secret') if ! $secret;

	my $precision =3D $opt->{precision} || 2;

	my $referer   =3D  $opt->{referer}
					|| charge_param('referer');

	$actual->{mv_credit_card_exp_month} =3D~ s/\D//g;
	$actual->{mv_credit_card_exp_year} =3D~ s/\D//g;
	$actual->{mv_credit_card_exp_year} =3D~ s/\d\d(\d\d)/$1/;
	$actual->{mv_credit_card_number} =3D~ s/\D//g;
	$actual->{b_zip} =3D~ s/\D//g;

	my $exp =3D sprintf '%02d%02d', $actual->{mv_credit_card_exp_month},
		$actual->{mv_credit_card_exp_year};

	$opt->{transaction} ||=3D 'sale';
	my $transtype =3D $opt->{transaction};

	my %type_map =3D (
		auth		 	=3D>	'preauth',
		authorize		=3D>	'preauth',
		mauthcapture 		=3D>	'sale',
		mauthonly		=3D>	'preauth',
		return			=3D>	'credit',
		reverse          	=3D>	'chargeback',
		sale	 		=3D>	'sale',
		settle 			=3D>	'postauth',
	);
=09
	if (defined $type_map{$transtype}) {
		$transtype =3D $type_map{$transtype};
	}

	$amount =3D $opt->{total_cost} if $opt->{total_cost};
=09
	if(! $amount) {
		$amount =3D Vend::Interpolate::total_cost();
		$amount =3D Vend::Util::round_to_frac_digits($amount,$precision);
	}
        $amount =3D~ s/\D//g;

	$order_id =3D gen_order_id($opt);

	$name =3D $actual->{b_fname} . ' ' . $actual->{b_lname};

	my %query =3D (
		amount		    	=3D> $amount,
		cc			=3D> $actual->{mv_credit_card_number},
		exp			=3D> $exp,
		demo			=3D> $opt->{test} || charge_param('test'),
		name			=3D> $name,
		address1		=3D> $actual->{b_address},
		city			=3D> $actual->{b_city},
		state			=3D> $actual->{b_state},
		zip			=3D> $actual->{b_zip},
		country			=3D> $actual->{b_country},
		action			=3D> $transtype,
		offlineauthcode		=3D> $actual->{auth_code},
		transid			=3D> $actual->{order_id},
		email			=3D> $actual->{email},
		phone			=3D> $actual->{phone_day},
		password	  	=3D> $secret,
		custid	  	   	=3D> $user,
		avs			=3D> 'y'
	);

        # delete query keys with undefined values
	for (keys %query) {
        	delete $query{$_} unless $query{$_};
	}
        delete $query{country} if $query{country} eq 'US';

::logGlobal("trustcommerce query: " . ::uneval(\%query));

	my %result =3D Net::TCLink::send(\%query);
=09
	# Interchange names are on the left, TCLink on the right
	my %result_map =3D ( qw/
		pop.status		status
		order-id		transid
		pop.order-id		transid
		pop.avs_code		avs
	/
	);

::logGlobal("trustcommerce response: " . ::uneval(\%result));

	for (keys %result_map) {
		$result{$_} =3D $result{$result_map{$_}}
			if defined $result{$result_map{$_}};
	}

	if ($result{status} =3D=3D 'approved') {
		$result{MStatus} =3D 'success';
	}
	else {
		$result{MStatus} =3D 'failure';
		delete $result{'order-id'};

		# NOTE: A lot more AVS codes could be checked for here.
	    	if ($result{avs} eq 'N') {
			my $msg =3D q{You must enter the correct billing address of your credit =
card.};
			$result{MErrMsg} =3D errmsg($msg);
	    	}
		else {
			my $msg =3D "TrustCommerce error: %s. Please call in your order or try a=
gain.";
	   		$result{MErrMsg} =3D errmsg($msg);
	    	}
	}

::logGlobal("result given to interchange " . ::uneval(\%result));

	return (%result);
}

package Vend::Payment::TCLink;

1;

--=-TiSkJIZINS4+adqOhFHT--