[interchange-cvs] interchange - danb modified 2 files

interchange-core@icdevgroup.org interchange-core@icdevgroup.org
Fri Aug 23 13:41:00 2002


User:      danb
Date:      2002-08-23 17:40:00 GMT
Modified:  .        WHATSNEW
Added:     lib/Vend/Payment ECHO.pm
Log:
* Support for ECHO added. Written, tested, and donated by
  Michael Lehmkuhl <michael@electricpulp.com>.  Ported from globalsub to
  Vend::Payment by Dan Browning <db@kavod.com>.  Thanks Michael!

Revision  Changes    Path
2.36      +3 -0      interchange/WHATSNEW


rev 2.36, prev_rev 2.35
Index: WHATSNEW
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /var/cvs/interchange/WHATSNEW,v
retrieving revision 2.35
retrieving revision 2.36
diff -u -r2.35 -r2.36
--- WHATSNEW	14 Aug 2002 19:58:00 -0000	2.35
+++ WHATSNEW	23 Aug 2002 17:39:59 -0000	2.36
@@ -169,6 +169,9 @@
   Written, tested, and donated by Tom Friedel <tom@readyink.com>.
   Thanks, Tom!
=20=20
+* Support for ECHO added. Written, tested, and donated by
+  Michael Lehmkuhl <michael@electricpulp.com>.  Ported from globalsub to=
=20
+  Vend::Payment by Dan Browning <db@kavod.com>.  Thanks Michael!
=20
 UI
 --



1.1                  interchange/lib/Vend/Payment/ECHO.pm


rev 1.1, prev_rev 1.0
Index: ECHO.pm
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
# Vend::Payment::ECHO - Interchange ECHO support
#
# $Id: ECHO.pm,v 1.1 2002/08/23 17:40:00 danb Exp $
#
# Copyright (C) 2002=20
#      Electric Pulp. <info@electricpulp.com>=20
#    & Kavod Technologies <info@kavod.com>
#
# VERSION HISTORY
# + v1.1 08/06/2002 Fixed a problem with handling the return status from the
#   OpenECHO module.
# + v1.2 08/17/2002 General clean up
# + v1.3 08/22/2002 Ported from globalsub to Vend::Payment
#
#	http://www.openecho.com/
#	http://www.echo-inc.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::ECHO;

=3Dhead1 Interchange ECHO Support

Vend::Payment::ECHO $Revision: 1.1 $

=3Dhead1 AUTHOR

Michael Lehmkuhl <michael@electricpulp.com>.

Ported to Vend::Payment by Dan Browning <db@kavod.com>.  Code reused and=20
inspired by Mike Heins <mike@perusion.com>.

=3Dhead1 SPECIAL THANKS

Jim Darden <support@openecho.com>, Dan Browning <db@kavod.com>

=3Dhead1 SYNOPSIS

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

=3Dhead1 PREREQUISITES

If you have not done so already, you will need to sign up for an ECHO accou=
nt.
You will be provided an ID and a PIN (also known as 'secret').  You may also
sign up for a test account at the following URL:

	http://www.echo-inc.com/echotestapp.php

This subroutine uses the OpenECHO module.  Make sure OpenECHO.pm is in your=
 @INC
array.  For your convenience, version 1.2 has been included with Interchang=
e,=20
but you can get updated versions from:
=20=20
  http://www.openecho.com/
  http://www.echo-inc.com/
=20=20
The OpenECHO.pm module itself has some additional prerequisites:

  Net::SSLeay
=20
    or
=20=20
  LWP::UserAgent and Crypt::SSLeay

Only one of these need be present and working.  Net::SSLeay is preferred as=
 some
have reported problems using LWP::UserAgent and Crypt::SSLeay.

  URL::Escape

This module is used to write some of the URLs used by the OpenECHO module. =
 It
is recommended that you read the documention for the OpenECHO module itself=
 in
addition to this document.

=3Dhead1 DESCRIPTION

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

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

    Require module Vend::Payment::ECHO

This I<must> be in interchange.cfg or a file included from it.

NOTE: 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<echo>. 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  echo

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:

    Route echo id Your_ECHO_ID

or  (with only ECHO as a payment provider)
=20=20=20=20
	 Variable MV_PAYMENT_ID	Your_ECHO_ID
=09=20
or

	 Variable ECHO_PAYMENT_ID	Your_ECHO_ID

or
=20
	 [charge mode=3Decho id=3DYour_ECHO_ID]

The active settings are:

=3Dover 4

=3Ditem id

Your account ID, supplied by ECHO when you sign up.
Global parameter is MV_PAYMENT_ID or ECHO_PAYMENT_ID.

=3Ditem secret

Your account password, selected by you or provided by ECHO when you sign up.
Global parameter is MV_PAYMENT_SECRET or ECHO_PAYMENT_SECRET.

=3Ditem others...

If planning to do AUTH_ONLY or other with special admin page
Variable MV_PAYMENT_REMAP order_id=3Dmv_order_id auth_code=3Dmv_auth_code

Variable ECHO_PAYMENT_ORDER_TYPE         S
	# S for "self-service" orders
	# F for hosted or ISP orders
Variable ECHO_PAYMENT_ISP_ECHO_ID        123<4567890
Variable ECHO_PAYMENT_ISP_PIN            12345608
Variable ECHO_PAYMENT_MERCHANT_EMAIL     merchant@merchant.com
Variable ECHO_PAYMENT_DEBUG              F
	# C causes ECHO to return a statement of conformity
	# T or TRUE causes ECHO to return additional debug information
	# Any other value turns off ECHO debugging

=3Dback=20

=3Dhead2 Example Configuration

This is an example configuration that one would add to catalog.cfg:=20

Variable MV_PAYMENT_ID		Your_ECHO_ID
Variable MV_PAYMENT_SECRET	Your_ECHO_secret
Variable MV_PAYMENT_MODE	echo

=3Dhead2 Troubleshooting

Try a sale with the card number C<4111 1111 1111 1111> and a valid expirati=
on=20
date. The sale should be denied, and the reason should be in=20
[data session payment_error].

If nothing works:

=3Dover 4

=3Ditem *

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

    Require module Vend::Payment::ECHO

=3Ditem *

Make sure the ECHO C<OpenECHO.pm> module is available either in your
path or in /path_to_interchange/lib.

=3Ditem *

Check the error logs, both catalog and global.

=3Ditem *

Make sure you set your account ID and secret properly.=20=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, Interchange consultants are available to help
with integration for a fee.

=3Dback

=3Dhead1 SECURITY CONSIDERATIONS

Because this library calls an executable, you should ensure that no
untrusted users have write permission on any of the system directories
or Interchange software directories.

=3Dhead1 NOTES

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

=3Dcut

BEGIN {

	my $selected;
	eval {
		package Vend::Payment;
		require Net::SSLeay;
		import Net::SSLeay qw(post_https make_form make_headers);
		$selected =3D "Net::SSLeay";
	};

	$Vend::Payment::Have_Net_SSLeay =3D 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 =3D "LWP and Crypt::SSLeay";
		};

		$Vend::Payment::Have_LWP =3D 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__, $select=
ed)
		unless $Vend::Quiet;

}

package Vend::Payment;

use OpenECHO;

sub echo {
	my ($opt) =3D @_;

#::logDebug("echo called, args=3D" . ::uneval(\@_));
=09
	my (%actual) =3D map_actual();
=09
	my @errMsgs =3D ();
	# Required for validation
	if (! $user) {
		$user      =3D $opt->{id} ||=20
		             charge_param('id') ||=20=20
		             $::Variable->{ECHO_PAYMENT_ID} ||
		             $::Variable->{MV_PAYMENT_ID} ||
	                $::Variable->{CYBER_ID}
	                or push @errMsgs, "No payment ID found.";
	}
=09
	# Required for validation
	if (! $secret) {
		$secret    =3D $opt->{secret} ||
		             charge_param('secret') ||
						 $::Variable->{ECHO_PAYMENT_SECRET} ||
						 $::Variable->{MV_PAYMENT_SECRET} ||
		             $::Variable->{CYBER_SECRET}
		             or push @errMsgs, "No payment secret found.";
	}

	if (scalar @errMsgs) {
		for (@errMsgs) {
			::logError($_);
		}
		return 0;
	}
	@errMsgs =3D ();

   my $server     =3D $opt->{server} ||
                     charge_param('server') ||
							$::Variable->{ECHO_PAYMENT_SERVER} ||
							$::Variable->{MV_PAYMENT_SERVER} ||
                     $::Variable->{CYBER_SERVER} ||
                     'https://wwws.echo-inc.com/scripts/INR200.EXE';

	my $precision  =3D  $opt->{precision} ||
                     charge_param('precision') ||
							$::Variable->{ECHO_PAYMENT_PRECISION} ||
							$::Variable->{MV_PAYMENT_PRECISION} ||
                     $::Variable->{CYBER_PRECISION} ||
                     2;

	##### ECHO SPECIFIC VARIABLES #####

	my $order_type =3D $::Variable->{ECHO_PAYMENT_ORDER_TYPE} || 'S';
	my $isp_echo_id =3D $::Variable->{ECHO_PAYMENT_ISP_ECHO_ID};
	my $isp_pin =3D $::Variable->{ECHO_PAYMENT_ISP_PIN};
	my $merchant_email =3D $::Variable->{ECHO_PAYMENT_MERCHANT_EMAIL};

	# Set to 'C' for Certify mode to check compliance with the ECHO spec on a
	# transaction-by-transaction basis.  'T' or 'TRUE' for full ECHO debugging.
	my $debug =3D $::Variable->{ECHO_PAYMENT_DEBUG};

	##########################

	$actual{mv_credit_card_exp_month} =3D~ s/\D//g;
	$actual{mv_credit_card_exp_month} =3D~ s/^0+//;
	$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;

	my $exp =3D 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.
	$actual{cyber_mode} =3D $actual{mv_payment_mode} || 'ES'
        unless $actual{cyber_mode};

	# Credit Card Transactions=20
	# *	AD (Address Verification)=20
	# *	AS (Authorization)=20
	# *	AV (Authorization with Address Verification)=20
	# *	CR (Credit)=20
	# *	DS (Deposit)=20
	# *	ES (Authorization and Deposit)=20
	# *	EV (Authorization and Deposit with Address Verification)=20
	# *	CK (System check)=20
	# Credit Card Transactions Enhanced by CyberSource=20
	# *	CI (AV Transaction with CyberSource Internet Fraud Screen)=20
	# *	CE (AV Transaction with CyberSource Export Compliance)=20
	# *	CB (AV Transaction with CyberSource Internet Fraud Screen and Export C=
ompliance)=20
	# Electronic Check Transactions=20
	# *	DV (Electronic Check Verification)=20
	# *	DD (Electronic Check Debit)=20
	# *	DC (Electronic Check Credit)
	my %type_map =3D (
		mauth_capture 			=3D>	'ES',
		mauthonly				=3D>	'AS',
		CAPTURE_ONLY			=3D>  'DS',
		CREDIT					=3D>	'CR',
		AUTH_ONLY				=3D>	'AS',
		PRIOR_AUTH_CAPTURE		=3D>	'DS',
	);
=09
	if (defined $type_map{$actual{cyber_mode}}) {
        $actual{cyber_mode} =3D $type_map{$actual{cyber_mode}};
    }
    else {
        $actual{cyber_mode} =3D 'ES';
    }

    if(! $amount) {
        $amount =3D Vend::Interpolate::total_cost();
        $amount =3D sprintf("%.${precision}f", $amount);
    }

    my($orderID);
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =3D gmtime(time=
());

    ### Make an order ID based on date, time, and Interchange session
    # $mon is the month index where Jan=3D0 and Dec=3D11, so we use
    # $mon+1 to get the more familiar Jan=3D1 and Dec=3D12
    #$orderID =3D sprintf("%04d%02d%02d%02d%02d%05d%s",
    #        $year + 1900,$mon + 1,$mday,$hour,$min,$$,$Vend::SessionName);
	$orderID =3D Vend::Payment::gen_order_id();

	### Set up the OpenECHO instance
	use OpenECHO;
	my $openecho =3D new OpenECHO or push @errMsgs, "Couldn't make instance of=
 OpenECHO.";
	if (scalar @errMsgs) {
		for (@errMsgs) {
			::logError($_);
		}
		return 0;
	}
	@errMsgs =3D ();

	### Connection info
	$openecho->set_EchoServer("https://wwws.echo-inc.com/scripts/INR200.EXE");
	$openecho->set_transaction_type($actual{cyber_mode});
	$openecho->set_order_type($order_type);

	### Merchant/ISP info
	$openecho->set_merchant_echo_id($user);
	$openecho->set_merchant_pin($secret);
	$openecho->set_isp_echo_id($isp_echo_id);
	$openecho->set_isp_pin($isp_pin);
	$openecho->set_merchant_email($merchant_email);

	### Billing info
	my $billing_first_name =3D $actual{b_fname} || $actual{fname};
	my $billing_last_name =3D $actual{b_lname} || $actual{lname};
	my $billing_address1 =3D $actual{b_address1} || $actual{address1};
	my $billing_address2 =3D $actual{b_address2} || $actual{address2};
	my $billing_city =3D $actual{b_city} || $actual{city};
	my $billing_state =3D $actual{b_state} || $actual{state};
	my $billing_zip =3D $actual{b_zip} || $actual{zip};
	my $billing_country =3D $actual{b_country} || $actual{country};
	my $billing_phone =3D $actual{phone_day} || $actual{phone_night};
	$openecho->set_billing_ip_address($Vend::Session->{ohost});		# aka [data s=
ession ohost] aka REMOTE_HOST
	#$openecho->set_billing_prefix($actual{prefix});
	$openecho->set_billing_first_name($billing_first_name);
	$openecho->set_billing_last_name($billing_last_name);
	#$openecho->set_billing_company_name($actual{company_name});
	$openecho->set_billing_address1($billing_address1);
	$openecho->set_billing_address2($billing_address2);
	$openecho->set_billing_city($billing_city);
	$openecho->set_billing_state($billing_state);
	$openecho->set_billing_zip($billing_zip);
	$openecho->set_billing_country($billing_country);
	$openecho->set_billing_phone($billing_phone);
	#$openecho->set_billing_fax($actual{fax});
	$openecho->set_billing_email($actual{email});

	### Electronic check payment info if supplied...
	#$openecho->set_ec_bank_name($ec_bank_name);
	#$openecho->set_ec_first_name($billing_first_name);
	#$openecho->set_ec_last_name($billing_last_name);
	#$openecho->set_ec_address1($billing_address1);
	#$openecho->set_ec_address2($billing_address2);
	#$openecho->set_ec_city($billing_city);
	#$openecho->set_ec_state($billing_state);
	#$openecho->set_ec_zip($billing_zip);
	#$openecho->set_ec_rt($ec_rt);
	#$openecho->set_ec_account($ec_account);
	#$openecho->set_ec_serial_number($ec_serial_number);
	#$openecho->set_ec_payee($ec_payee);
	#$openecho->set_ec_id_state($ec_id_state);
	#$openecho->set_ec_id_number($ec_id_number);
	#$openecho->set_ec_id_type($ec_id_type);

	### Debug on/off
	$openecho->set_debug($debug);
=09
	### Payment details
	$openecho->set_cc_number($actual{mv_credit_card_number});
	$openecho->set_grand_total($amount);
	$openecho->set_ccexp_month($actual{mv_credit_card_exp_month});
	$openecho->set_ccexp_year($actual{mv_credit_card_exp_year});
	$openecho->set_counter($openecho->getRandomCounter());
	$openecho->set_merchant_trace_nbr($orderID);
=09
	### Send payment request
	#print($openecho->get_version() . "<BR>");
#::logDebug("openecho submitting <urldata>%s</urldata>", $openecho->getURLD=
ata());
	$openecho->Submit();

#::logDebug("The ECHO response is <echo_response>%s</echo_response>", $open=
echo->{'EchoResponse'});

#::logDebug("The ECHO type 2 response is <echotype2>%s</echotype2>", $opene=
cho->{'echotype2'});

#::logDebug("The avs_result field is <avs_result>%s</avs_result>", $openech=
o->{avs_result});

	my %result;
	if ($openecho->{EchoSuccess} !=3D 0) {
		$result{'MStatus'} =3D 'success';
		$result{'pop.status'} =3D 'success';
		$result{'MErrMsg'} =3D $openecho->{'echotype2'};
		$result{'pop.error-message'} =3D $openecho->{'echotype2'};
		$result{'order-id'} =3D $openecho->{order_number} || 1;
		$result{'pop.order-id'} =3D $openecho->{order_number} || 1;
		$result{'auth_code'} =3D $openecho->{auth_code};
		$result{'pop.auth_code'} =3D $openecho->{auth_code};
		$result{'avs_code'} =3D $openecho->{avs_result};
		$result{'pop.avs_code'} =3D $openecho->{avs_result};
	}
	else {
		$result{MStatus} =3D 'failure';
		$Vend::Session->{MStatus} =3D 'failure';
=09=09
		# NOTE: A lot more AVS codes could be checked for here.
		if ($result{avs_code} eq 'N') {
			$result{MErrMsg} =3D "You must enter the correct billing address of your=
 credit card. The bank returned the following error: " . $openecho->{'avs_r=
esult'};
		}
		else {
			$result{MErrMsg} =3D $openecho->{'echotype2'};
		}
		$Vend::Session->{payment_error} =3D $result{MErrMsg};
#::logDebug("openecho oops: ".$Vend::Session->{payment_error});
	}

    return (%result);
}

package Vend::Payment::ECHO;

return 1;