[interchange-cvs] interchange - kwalsh modified lib/Vend/Payment/MCVE.pm

interchange-core@icdevgroup.org interchange-core@icdevgroup.org
Wed Jul 24 11:31:04 2002


User:      kwalsh
Date:      2002-07-24 15:30:59 GMT
Added:     lib/Vend/Payment MCVE.pm
Log:
	* Payment module to interface with the "Mainstreet Credit
	  Verification Engine" (www.mcve.com).  The module was written,
	  tested and kindly donated by Tom Friedel (tom@readyink.com).
	  Thanks tom!

Revision  Changes    Path
1.1                  interchange/lib/Vend/Payment/MCVE.pm


rev 1.1, prev_rev 1.0
Index: MCVE.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::MCVE - Interchange MCVE support
#
# $Id: MCVE.pm,v 1.1 2002/07/24 15:30:58 kwalsh Exp $
#
# Author: Tom Friedel (tom@readyink.com) for Carlc Internet Services (http:=
//www.carlc.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::MCVE;

use vars qw($VERSION);
$VERSION =3D substr(q$Revision: 1.1 $, 10);

=3Dhead1 Interchange MCVE support

Vend::Payment::MCVE $Revision: 1.1 $

=3Dhead1 SYNOPSIS

    &charge=3Dmcve

	or

    [charge mode=3Dmcve param1=3Dvalue1 param2=3Dvalue2]

	or

    mcve($mode, $opt);

=3Dhead1 PREREQUISITES

    MCVE.pm

The MCVE libraries are available free at http://www.mcve.com/

=3Dhead1 DESCRIPTION

MCVE, Mainstreet Credit Verification Engine is a high performance
software application designed to provide quality, in-house, Credit Card
processing FROM Linux, FreeBSD, OpenBSD, IBM AIX, Sun Solaris, SCO
OpenServer/UnixWare, and Mac OS X platforms to established clearing
houses.  The MCVE C & Perl library software can be downloaded free of charge
from http://mcve.com.  This module was developed and tested with the server=
=20
software installed on HotConnect.net (http://www.hotconnect.net).=20
Hot Connect, Inc. is an Interchange friendly Web Hosting, E-Commerce, and
Internet Services company.

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

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

    Require module Vend::Payment::MCVE

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

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=3Dmcve name=3Dmcve_configname]

or

    Route mcve name mcve_configname

or=20

    Variable MV_PAYMENT_NAME      mcve_configname

The active settings are:

    Variable   MV_PAYMENT_MODE mcve
    Variable   MV_PAYMENT_NAME mcve_username
    Variable   MV_PAYMENT_PASSWD mcve_password
    Variable   MV_PAYMENT_HOST sv1.carlc.com
    Variable   MV_PAYMENT_PORT 8333
    Variable   MV_PAYMENT_COUNTER etc/mcve_id.counter
    Variable   MV_PAYMENT_COUNTER_START 100
    Variable   MV_PAYMENT_SALE_ON_AUTH 1
    Variable   MV_PAYMENT_NO_SALE_BAD_AVS 0
    Variable   MV_PAYMENT_NO_SALE_BAD_CVV2 0
    Variable   MV_PAYMENT_SUCCESS_ON_ANY 0

=3Dover 4

=3Ditem name

Your MCVE configuration username, set up when MCVE was configured/installed=
 on
the machine. Global parameter is MV_PAYMENT_NAME.

=3Ditem no_sale_bad_avs

Normally Interchange charge modules do an authorization, and if successful,=
 do a sale.
This module is configurable for a different models, where transactions are =
not
automatically saled.=20=20

=3Ditem sale_on_auth

The storekeeper may not wish to sale a transaction if the AVS is bad.=20=20

=3Ditem success_on_any

Alternatively the storekeeper may wish to cause any transaction to appear t=
o be a
successful sale.  The storekeeper would have to contact buyers with bad cre=
dit card
information and manually redo the sale.  The motivation is to make sure no =
one
attempts a sale and gives up for any reason.   This mode of operation, set =
with
MV_PAYMENT_SUCCESS_ON_ANY is not commonly used.

=3Ditem transaction

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

    Interchange mode    MCVE mode
    ----------------    -----------------
    auth                auth
    sale                sale

Not supported yet:

    return              return
    reverse             reverse
    void                void

=3Ditem counter, counter_start

Currently this is not being used, and Interchange is generating id's.

    Route   mcve  counter        etc/mcve_id.counter
    Route   mcve  counter_start  100

=3Dback

=3Dhead2 Troubleshooting

Try the instructions above, with a test credit card number from your paymen=
t processor.

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:

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

    Require module Vend::Payment::MCVE

Make sure MCVE is installed and working.

Check the error logs, both catalog and global.

Make sure you set your payment parameters properly.=20=20

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

    [calc]
	$Tag->uneval( { ref =3D> $Session->{payment_result} );
    [/calc]

That should show what happened.

=3Dhead1 BUGS

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

=3Dhead1 AUTHORS

MCVE modifications by tom@readyink.com for Carlc Internet Services

=3Dhead1 CREDITS

Derived from CCVS.pm template, and others.

=3Dcut

BEGIN {
	eval {
		package Vend::Payment;
        	require MVCE or die __PACKAGE__ . " requires MVCE";
		::logGlobal({}, "MCVE module found version %s", $MCVE::VERSION)
			unless $Vend::Quiet;
	};

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

package Vend::Payment;

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

    ::logDebug("mcve called, args=3D" . ::uneval(\@_));

    my $sess;
    my %result;

    my $mcve_die =3D sub {
	my ($msg, @args) =3D @_;
	$msg =3D "MCVE: $msg" unless $msg =3D~ /^MCVE/;
	$msg =3D ::errmsg($msg, @args);
	&MCVE::done($sess) if $sess;
	::logDebug("mcve erred, result=3D$msg");
	die("$msg\n");
    };

    my $actual =3D $opt->{actual};
    if(! $actual) {
	my %actual =3D map_actual();
	$actual =3D \%actual;
    }

    if(! defined $opt->{precision} ) {
        $opt->{precision} =3D charge_param('precision');
    }

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

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

    my %type_map =3D (
	mauthcapture	=3D>	'sale',
	mauthonly	=3D>	'auth',
	mauthreturn	=3D>	'return',
	S		=3D>	'sale',
	C		=3D>	'auth',
	V		=3D>	'void',
	sale		=3D>	'sale',
	auth		=3D>	'auth',
	authorize	=3D>	'auth',
	void		=3D>	'void',
	delete		=3D>	'delete',
    );

    if (defined $type_map{$op}) {
        $op =3D $type_map{$op};
    }

    if(! $amount) {
        $amount =3D $opt->{total_cost} ||=20
	  Vend::Util::round_to_frac_digits(
					 Vend::Interpolate::total_cost(),
					   $opt->{precision},
					   );
    }

    my $invoice;

    unless ($invoice =3D $opt->{order_id}) {

	if($op ne 'auth' and $op ne 'sale') {
	    return $mcve_die->("must supply order ID for transaction type %s", $op=
);
	}

	my $file =3D  $opt->{"counter"} || charge_param('counter');
	$file =3D "etc/mcve_order.counter" if ! $file ;

	if ( open( FILE, $file  )) {
	    $orderID =3D <FILE> ;
	    close( FILE ) ;
	    chomp $orderID ;
	    $orderID ++ ;
	}
	else {
	    $orderID =3D $opt->{"counter_start"} || charge_param('counter_start') =
|| 100 ;
	}

	if ( open( FILE, ">$file" )) {
	    print FILE "$orderID\n" ;
	    close( FILE ) ;
	}
	else {
	    return $mcve_die->("Could not create OrderID file $file");
	}
	$invoice =3D $orderID ;
    }

    $cvv2 =3D "" ;

    $configname =3D  $opt->{"name"} || charge_param('name');
    ::logDebug("mcve configuration name '$configname'");

    $mcve_id =3D $configname ;
    $mcve_pw =3D $opt->{"passwd"} || charge_param('passwd');
    ::logDebug("mcve configuration pw '$mcve_pw'");

    $host =3D $opt->{"host"} || charge_param('host');
    ::logDebug("mcve configuration host '$host'");

    $port =3D $opt->{"port"} || charge_param('port');
    ::logDebug("mcve configuration port '$port'");

    $host =3D "sv1.carlc.com" if ! $host ;
    $port =3D 8333 if ! $port ;

    $mcve_sale_on_auth =3D 1 ;
    $mcve_sale_on_auth =3D $opt{"sale_on_auth"} || charge_param('sale_on_au=
th') ;

    $mcve_no_sale_bad_avs =3D 0 ;
    $mcve_no_sale_bad_avs =3D $opt{"no_sale_bad_avs"} || charge_param('no_s=
ale_bad_avs') ;

    $mcve_no_sale_bad_cvv2 =3D 0 ;
    $mcve_no_sale_bad_cvv2 =3D $opt{"no_sale_bad_cvv2"} || charge_param('no=
_sale_bad_cvv2') ;

    $mcve_success_on_any =3D 0 ;
    $mcve_success_on_any =3D $opt{"success_on_any"} || charge_param('succes=
s_on_any') ;

    $conn=3DMCVE::MCVE_InitConn();

    ::logDebug("Connected\n");

    &MCVE::MCVE_SetIP($conn, $host, $port);
    ::logDebug("Set IP\n");

    if ( &MCVE::MCVE_Connect($conn) =3D=3D 0) {

	::logDebug("Count not Connect\n");

	&MCVE::MCVE_DestroyConn($conn);
	&MCVE::MCVE_DestroyEngine();
	return $mcve_die->("MCVE Connection Failed");
    }

    ::logDebug("Op =3D $op\n");

    if($op eq 'auth' or $op eq 'sale') {

	$cvv2 =3D "" ;
	$zip =3D $actual->{b_zip} ;
	$address =3D $actual->{"b_address"} ;

## These is specific to HotConnect MCVE implementation
	$comments =3D $actual->{"b_fname"} . " " . $actual->{"b_lname"} . "|" . $a=
ctual->{"b_address"} ;
	$clerkid =3D "" ;
	$stationid =3D 0 ;

	$text =3D "" ;
	$trackdata =3D "" ;
	$cardno =3D $actual->{"mv_credit_card_number"} ;

	::logDebug("Before PreAuth\n");

	$identifier=3D&MCVE::MCVE_PreAuth($conn, $mcve_id, $mcve_pw, $trackdata, $=
cardno, $exp, $amount, $address, $zip, $cvv2, $comments, $clerkid, $station=
id, '');

	if ($identifier =3D=3D MCVE::MCVE_ERROR()) {

	    ::logDebug("PreAuth Error\n");

	    &MCVE::MCVE_DestroyConn($conn);
	    &MCVE::MCVE_DestroyEngine();
	    return $mcve_die->("Error Making Transaction %s", $configname);
	}

	my $cnt =3D 0 ;
	while (&MCVE::MCVE_CheckStatus($conn, $identifier) !=3D &MCVE::MCVE_DONE()=
) { # Wait until this transaction is done....
	    &MCVE::MCVE_Monitor($conn); # MCVE_Monitor does the actual background =
communication with the MCVE daemon via IP or SSL
	    sleep(1);		# Don't loop too fast :)
	    $cnt ++ ;
	    if ( $cnt =3D=3D 100 ) {
		return $mcve_die->("CheckStatus Time Out %s", $configname);
	    }
	}

	$decline =3D 0 ;
	if (&MCVE::MCVE_ReturnStatus($conn, $identifier) =3D=3D &MCVE::MCVE_SUCCES=
S()) {
	    $acode =3D MCVE::MCVE_TransactionAuth($conn, $identifier); #  Authoriz=
ation Number
	    $sid =3D MCVE::MCVE_TransactionID($conn, $identifier); #  Authorizatio=
n Number
	    $result{"pop.error_message"} =3D $result{"result_text"} =3D "success" ;
	    $result{"pop.auth-code"} =3D $acode ;
	} else {
	    $decline =3D 1 ;
	    $text =3D MCVE::MCVE_TransactionText($conn, $identifier);
	    $text =3D "Your card was declined ($text)" ;
	    $result{"pop.error_message"} =3D $result{"result_text"} =3D $text ;

	    my $msg =3D errmsg(
			     "MCVE error: %s %s. Please call in your order or try again.",
			     $result{MStatus},
			     $result{result_text},
			     );
	    $Vend::Session->{errors}{mv_credit_card_valid} =3D $msg;
	}


	$avs =3D MCVE::MCVE_TransactionAVS($conn, $identifier);
	$result{"pop.avs_code"} =3D $avs ;

	$cv =3D MCVE::MCVE_TransactionCV($conn, $identifier);

	if ( ! $decline && ( $op eq "sale" ) && $mcve_sale_on_auth ) {

## cv =3D -1 if not entered, 0 if bad, 1 if good
## avs =3D -1 if not entered, 0 if bad, 1 or 2 or 3 if address/zip match
	    if ((( $avs > 0 ) || ! $mcve_no_sale_bad_avs ) && (($cvv2 && ( $cv > 0=
 )) || ! $mcve_no_sale_bad_cvv2 )) {

		$identifier=3D&MCVE::MCVE_PreAuthCompletion($conn, $mcve_id, $mcve_pw, $a=
mount, $sid, '' ) ;

		if ($identifier =3D=3D MCVE::MCVE_ERROR()) {
		    &MCVE::MCVE_DestroyConn($conn);
		    &MCVE::MCVE_DestroyEngine();
		    return $mcve_die->("Error Forcing Transaction ($acode)");
		}

		while (&MCVE::MCVE_CheckStatus($conn, $identifier) !=3D &MCVE::MCVE_DONE(=
)) { # Wait until this transaction is done....
		    &MCVE::MCVE_Monitor($conn); # MCVE_Monitor does the actual background=
 communication with the MCVE daemon via IP or SSL
		    sleep(1);	# Don't loop too fast :)
		}

		if (&MCVE::MCVE_ReturnStatus($conn, $identifier) =3D=3D &MCVE::MCVE_SUCCE=
SS()) {
#		$text .=3D "AVS Response =3D $avs - CV Response =3D $cv" ;
		    $decline =3D 0 ;
		} else {
		    $decline =3D 1 ;
		    $text=3D "Force: " . &MCVE::MCVE_TransactionText($conn, $identifier);
		}
	    }

	}

	$result{"order-id"} =3D  $result{"pop.order-id"} =3D $invoice ;

	if ( $mcve_success_on_any ) {
	    $decline =3D 0 ;
	}

	# If everything was succesful, push through the sale.
	if (! $decline) {
	    $result{'pop.status'} =3D	    $result{MStatus} =3D 'success';
	    $result{'invoice'} =3D $invoice;
	}
	else  {			#decline
	    $result{MStatus} =3D 'failed';
	    my $msg =3D errmsg(
			     "MCVE error: %s %s. Please call in your order or try again.",
			     $result{MStatus},
			     $result{result_text},
			     );
	    $Vend::Session->{errors}{mv_credit_card_valid} =3D $msg;
	}
    }

    &MCVE::MCVE_DestroyConn($conn);
    &MCVE::MCVE_DestroyEngine();

    ::logDebug("mcve returns, result=3D" . ::uneval(\%result));
    return %result;
}

package Vend::Payment::MCVE;

1;