[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;