[interchange] Add HSBC payment module.
Stefan Hornburg <[email protected]>
| Newsgroups | gmane.comp.web.interchange.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit e6dbb16719c9d419969e4c95a28c965f4a458c4e Author: Lyn St George <[email protected]> Date: Thu May 30 20:22:54 2013 +0200 Add HSBC payment module. MANIFEST | 1 + lib/Vend/Payment/HSBC.pm | 829 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 830 insertions(+), 0 deletions(-) --- diff --git a/MANIFEST b/MANIFEST index 631603e..4a98e7c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1147,6 +1147,7 @@ lib/Vend/Payment/EFSNet.pm lib/Vend/Payment/Ezic.pm lib/Vend/Payment/Getitcard.pm lib/Vend/Payment/GoogleCheckout.pm +lib/Vend/Payment/HSBC.pm lib/Vend/Payment/ICS.pm lib/Vend/Payment/iTransact.pm lib/Vend/Payment/Linkpoint.pm diff --git a/lib/Vend/Payment/HSBC.pm b/lib/Vend/Payment/HSBC.pm new file mode 100644 index 0000000..34fb08a --- /dev/null +++ b/lib/Vend/Payment/HSBC.pm @@ -0,0 +1,829 @@ +# Vend::Payment::HSBC - Interchange HSBC Payment module +# +# Copyright (C) 2013 Zolotek Resources Ltd +# All Rights Reserved. +# +# Author: Lyn St George <[email protected]> +# +# 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::HSBC; + +=head1 NAME + +Vend::Payment::HSBC - Interchange HSBC Payments Module + +=head1 PREREQUISITES + + XML::Simple + URI + libwww-perl + Net::SSLeay + HTTP::Request + +Test for current installations, eg: perl -MXML::Simple -e 'print "It works\n"' +To install perl modules do: "emerge dev-perl/XML-Simple" or so on Gentoo, or on other systems do +"perl -MCPAN -e 'install XML::Simple'" + +=head1 DESCRIPTION + +The Vend::Payment::HSBC module implements the HSBC payment routine for use with Interchange. + +#========================= + +=head1 SYNOPSIS + +Quick start: + +Place this module in <ic_root>/lib/Vend/Payment, and call it in <ic_root>/interchange.cfg with +Require module Vend::Payment::HSBC. Ensure that your perl installation contains the modules +listed above and their pre-requisites. + +Add a new payment route into catalog.cfg as follows: +Route hsbc gwhost https://www.secure-epayments.apixml.hsbc.com +Route hsbc tdshost https://www.ccpa.hsbc.com/ccpa +Route hsbc returnurl http://__SERVER_NAME__/ord/hsbctdsreturn +Route hsbc username XXnnnnnn +Route hsbc currency GBP (default if not otherwise specified) +Route hsbc lcusername "XXX XXX" (currency codes, if they give you some usernames in lowercase and others in uppercase) +Route hsbc clientidGBP nnnnn (5 digit number for that currency) +Route hsbc clientidEUR nnnnn +Route hsbc clientidXXX nnnnn +Route hsbc clientalias XXnnnnnnnn (ie country code plus your 8-digit merchant a/c id) +Route hsbc password xxx +Route hsbc txmode P (see below for others) +Route hsbc txtype Auth (PreAuth, Auth, PostAuth, Void, Credit, ForceInsertPreAuth,ForceInsertAuth, ReAuth, RePreAuth, ReviewPendingUpdate) +Route hsbc payment_type PaymentNoFraud (or Payment to use their fraud routines) +Route hsbc finalcheckoutpage ord/final (defaults to ord/checkout) +Route hsbc payment_type PaymentNoFraud (bypasses fraud checks, default is 'Payment' to run checks) + +Route hsbc mail_txn_approved 1 (email you messages, a la Paypal, of payment made) +Route hsbc mail_txn_declined 1 (email you messages of payments declined, for monitoring fraud attempts) +Route hsbc mail_txn_to (merchant's email address, defaults to EMAIL_SERVICE or ORDERS_TO) +Route hsbc hsbcrequest gwpost (to bypass 3DSecure, defaults to tdspost) + +Add the following block of fraud screening rules, including comments as reminders. +# Default fraud rules: mark '1' to accept the order conditional upon further processing, '2' to display the message to the +# customer. Mark '0' to reject the order, or to not display a message. Eg '0 2' will reject with the message. +# '1 0' will accept and complete the order but not display a message, '1 2' will accept and display a message. +# All of these cases will be 'approved' by default but marked as 'review before capturing funds' by the bank. +Route hsbc fraud_4 '1 2' # The customer's billing address is in the UK but they + # are using an overseas issued card. +Route hsbc fraud_5 '1 2' # The customer is using a card issued in a different + # country to the billing address. +Route hsbc fraud_6 '0 2' # Failed AVS check. Both the first line of the address + # and post code do not match the address held by the card issuer. +Route hsbc fraud_16 '0 2' # Failed AVS check. Only the first line of the address matches + # the address held by the card issuer. +Route hsbc fraud_17 '1 2' # Failed AVS check. Only the post code matches the address held + # by the card issuer. +Route hsbc fraud_7 '1 0' # The card issuer has not responded to the AVS request. +Route hsbc fraud_8 '0 2' # CVM does not match the card issuer's value. +Route hsbc fraud_9 '0 2' # <BillToName> appears to be an invalid name. +Route hsbc fraud_10 '0 2' # <BillToStreet1> appears to be an invalid address. +Route hsbc fraud_11 '0 2' # <BillToCity> appears to be an invalid address. +Route hsbc fraud_12 '0 0' # <BillToPostalCode> appears to be an invalid address. +Route hsbc fraud_13 '1 0' # The AVS check cannot be performed on a British Forces Postal Office address. +Route hsbc fraud_14 '0 0' # HSBC Merchant Services has recently identified a number of fraudulent + # orders originating from this location. +Route hsbc fraud_15 '0 0' # HSBC Merchant Services has recently identified a number of fraudulent + # orders originating from this location. +Route hsbc fraud_20 '0 0' # HSBC Merchant Services has recently observed a high incidence of + # fraudulent transactions using the name '<BillToName>'. +Route hsbc fraud_22 '0 0' # HSBC Merchant Services has recently identified a number of fraudulent + # orders using this range of card numbers with a UK billing address. +Route hsbc fraud_23 '0 0' # HSBC Merchant Services has recently identified a number of fraudulent + # orders using this range of card numbers with Irish billing addresses. + +If these rules are triggered, [scratch pspfraudmsg] will hold the message to display +to your customer, and should be included into the receipt, mail_receipt and report. In all +cases where a rule is triggered and payment needs manual review and acceptance an email will +be sent to the merchant with details. + +TxMode: P = production, + Y = test 'yes' response + N = test, 'no' response + R = test, random 'yes|no' response; + FY = test, FraudShield 'yes' response + FN = test, FraudShield 'no' response + +Alter etc/log_transaction to wrap the following code around the "[charge route...]" call +found in ln 172 (or nearby): + [if scratchd mstatus eq success] + [tmp name="charge_succeed"][scratch order_id][/tmp] + [else] + [tmp name="charge_succeed"][charge route="[var MV_PAYMENT_MODE]" amount="[scratch tmp_remaining]" order_id="[value mv_transaction_id]"][/tmp] + [/else] + [/if] +and change [var MV_PAYMENT_MODE] above to [value mv_payment_route] if you want to use Paypal or similar in conjunction with this + +Also add this line just after '&final = yes' near the end of the credit_card section of etc/profiles.order: + &set=mv_payment_route hsbc if you change [var MV_PAYMENT_MODE] as above + + +If run from some sort of terminal this will also make refunds or send funds to a specified +credit card. + + + +=head1 Changelog +v 1.0.1, February 2013, change of ownership from eSecurePayments to GlobalIris and + consequent change of URLs for gateway. +v 1.0.0, October 2011. + first public release + +=head1 AUTHORS + +Lyn St George <[email protected]> + +=cut + +BEGIN { + eval { + package Vend::Payment; + require Net::SSLeay; + require XML::Simple; + require XML::Parser; + require LWP; + require MIME::Base64; + require HTTP::Request::Common; + import HTTP::Request::Common qw(POST); + use HTTP::Request (); + use MIME::Base64; + import Net::SSLeay qw(post_https make_form make_headers); + }; + + $Vend::Payment::Have_Net_SSLeay = 1 unless $@; + + if ($@) { + $msg = __PACKAGE__ . ' requires XML::Simple, HTTP::Request, Net::SSLeay and LWP ' . $@; + ::logGlobal ($msg); + die $msg; + } + + ::logGlobal("%s v1.0.1 20130222 payment module loaded",__PACKAGE__) + unless $Vend::Quiet or ! $Global::VendRoot; +} + +package Vend::Payment; + +use strict; + + my $gwhost; + +sub hsbc { + my ($method, $response, $in, $opt, $actual, %result, $db, $dbh, $sth, $transactionid, $id, $md, $x); + my ($passoutdata,$telephone, $md, $cardref, $mailreview, $mailtxn, $display, $pass); + my (%actual) = map_actual(); + $actual = \%actual; + $gwhost = charge_param('gwhost') || 'https://apixml.globaliris.com'; + my $tdshost = charge_param('tdshost') || 'https://mpi.globaliris.com/ccpa'; # 'https://www.ccpa.hsbc.com/ccpa'; # only live .. + my $bypass3ds = $::Values->{'bypass3ds'} || charge_param('bypass3ds') || ''; + my $hsbcrequest = $::Values->{'hsbcrequest'} || charge_param('hsbcrequest') || 'gwpost'; + $::Values->{'hsbcrequest'} = ''; + my $tdsreturn = charge_param('tdsreturn') || ''; + $hsbcrequest = 'gwpost' if length $tdsreturn; + my $mailto = charge_param('mail_txn_to') || $::Variable->{'EMAIL_SERVICE'} || $::Variable->{'ORDERS_TO'}; + my $currency = $::Values->{'iso_currency_code'} || $::Scratch->{'iso_currency_code'} || charge_param('currency') || 'GBP'; + $::Values->{'iso_currency_code'} = ''; + my $clientID = 'clientid' . $currency; + my $clientid = charge_param($clientID) or die "No client ID\n"; + my $clientalias = charge_param('clientalias') or warn "No client Alias\n"; + $clientalias .= $currency; + my $ccpaclientid = $clientalias . "01"; + my $username = charge_param('username') or die "No username id\n"; + $username = lc($username) if charge_param('lcusername') =~ /$currency/; + my $password = charge_param('password') or die "No password\n"; + my $txtype = $::Values->{'txtype'} || charge_param('txtype') || 'Auth'; + $::Values->{'txtype'} = ''; + my $paymenttype = $::Values->{'payment_type'} || charge_param('payment_type') || 'Payment'; # PaymentNoFraud bypasses HSBC's fraud tests + $::Values->{'payment_type'} = ''; + my $txmode = $::Values->{'txmode'} || charge_param('txmode') || 'P'; # P = production, others listed above + $::Values->{'txmode'} = ''; + my $authcode = $::Values->{'authcode'} || ''; # Obtained by voice authorisation for ForceInsertPreauth and the like + my $hsbctdspage = charge_param('hsbctdspage') || 'ord/hsbctds'; + my $returnurl = charge_param('returnurl') || "$::Variable->{SECURE_SERVER}$::Variable->{CGI_URL}/ord/hsbctdsreturn"; + my $finalcheckout = charge_param('finalcheckoutpage') || 'ord/checkout'; + my $currencyshort = $currency; + $currencyshort =~ /(\w\w)/i; + +#::logDebug("HSBC".__LINE__.": req=$hsbcrequest; type=$txtype; mode=$txmode; clientID=$clientid; clientalias=$clientalias, currency=$currency, username=$username"); + + my $amount = charge_param('amount') || Vend::Interpolate::total_cost() || $::Values->{'amount'}; + $amount =~ s/^\D*//g; + $amount =~ s/\s*//g; + my $purchaseamount = $amount; + $amount =~ s/,//g; + $amount = sprintf '%.2f', $amount; + $amount =~ s/\.//g; # £10.00 becomes 1000 for HSBC; 'purchaseamount' is for display to customer at PAS +#::logDebug("HSBC".__LINE__.":cp-amnt: ".charge_param('amount')."; v-amnt: $::Values->{'amount'}; ic-total_cost=" . Vend::Interpolate::total_cost()); + my $usebill = ''; + $usebill = '1' if ($::Values->{'mv_same_billing'} == '0' || length $::Values->{'b_lname'}); + my $name = $usebill ? "$::Values->{'b_fname'} $::Values->{'b_lname'}" || '' : "$::Values->{'fname'} $::Values->{'lname'}" || ''; + my $address1 = $usebill ? $::Values->{'b_address1'} : $::Values->{'address1'}; + my $address2 = $usebill ? $::Values->{'b_address2'} : $::Values->{'address2'}; + my $address3 = $usebill ? $::Values->{'b_address3'} : $::Values->{'address3'}; + my $phone = $::Values->{'phone_day'} || $::Values->{'phone_night'}; + my $address = "$address1, $address2, $address3"; + $address =~ s/,\s+$//g; + $address =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $city = $usebill ? $::Values->{'b_city'} : $::Values->{'city'}; + $city =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $state = $usebill ? $::Values->{'b_state'} : $::Values->{'state'}; + $state =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $zip = $usebill ? $::Values->{'b_zip'} : $::Values->{'zip'}; + $zip =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $country = $usebill ? $::Values->{'b_country'} : $::Values->{'country'}; + $country = 'GB' if ($country eq 'UK'); + my $email = $actual->{'email'}; + $email =~ s/[^a-zA-Z0-9.\@\-_]//gi; + my $phone = $actual->{'phone_day'} || $actual->{'phone_night'}; + $phone =~ s/[\(\)]/ /g; + $phone =~ s/[^0-9-+ ]//g; + my $ipaddress = $CGI::remote_addr if $CGI::remote_addr; + $ipaddress =~ /(\d*)\.(\d*)\.(\d*)\.(\d*)/; + my $t1 = sprintf '%03d', $1; + my $t2 = sprintf '%03d', $2; + my $t3 = sprintf '%03d', $3; + my $t4 = sprintf '%03d', $4; + $ipaddress = "$t1.$t2.$t3.$t4" if $CGI::remote_addr; + + my $pan = $actual->{'mv_credit_card_number'}; + $pan =~ s/\D//g; + $actual->{'mv_credit_card_exp_month'} =~ s/\D//g; + $actual->{'mv_credit_card_exp_year'} =~ s/\D//g; + $actual->{'mv_credit_card_exp_year'} =~ s/\d\d(\d\d)/$1/; + + if (length $pan) { + $cardref = $pan; + $cardref =~ s/^(\d\d).*(\d\d\d\d)$/$1**$2/; + $::Session->{'CardRef'} = $cardref; + } +#::logDebug("HSBC".__LINE__.": cardref=$cardref; $::Session->{CardRef}"); + + my $fname = $::Values->{'fname'} || $actual->{'b_fname'} || $actual->{'fname'}; + $fname =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $lname = $::Values->{'lname'} || $actual->{'b_lname'} || $actual->{'lname'}; + $lname =~ s/[^a-zA-Z0-9,.\- ]//gi; + my $cardholder = $::Values->{'cardholdername'} || "$fname $lname"; + $cardholder =~ s/[^a-zA-Z0-9,.\- ]//gi; +#::logDebug("HSBC".__LINE__.": name=$cardholder, $::Values->{cardholdername}; lname=$lname, $::Values->{lname}"); + my $mvccexpmonth = sprintf '%02d', $actual->{'mv_credit_card_exp_month'}; + my $mvccexpyear = sprintf '%02d', $actual->{'mv_credit_card_exp_year'}; + + my $cardexpiration = $mvccexpmonth . "/" . $mvccexpyear; + my $cardexpirationtds = $mvccexpyear . $mvccexpmonth; + + my $mvccstartmonth = sprintf '%02d', $actual->{'mv_credit_card_start_month'} || $::Values->{'mv_credit_card_start_month'} || $::Values->{'start_date_month'}; + $mvccstartmonth =~ s/\D//g; + + my $mvccstartyear = $actual->{'mv_credit_card_start_year'} || $::Values->{'mv_credit_card_start_year'} || $::Values->{'start_date_year'}; + $mvccstartyear =~ s/\D//g; + $mvccstartyear =~ s/\d\d(\d\d)/$1/; + + my $startdate = $mvccstartmonth . "/" . $mvccstartyear; + $startdate = '' unless $mvccstartmonth > '0'; + + my $issuenumber = $actual->{'mv_credit_card_issue_number'} || $::Values->{'mv_credit_card_issue_number'} || $::Values->{'card_issue_number'}; + $issuenumber =~ s/\D//g; + + my $cv2 = $actual->{'mv_credit_card_cvv2'} || $::Values->{'cvv2'}; + $cv2 =~ s/\D//g; + + my $cv2indicator = $::Values->{'cv2indicator'} || '2'; # 1=cv2 present, 2=cv2 customer claims not present on card, 5=intentionally not entered + $cv2indicator = '1' if length $cv2; + $::Session->{'cv2indicator'} = $cv2indicator if length $cv2; +#::logDebug("HSBC".__LINE__.": amt=$amount; currency=$currency; pan=$pan; cardholder=$cardholder; expm=$mvccexpmonth; expy=$mvccexpyear; issue=$issuenumber; cv2=$cv2; cv2indi=$cv2indicator; address=$address"); + + +# Lookup unlisted iso codes from country.txt - major country and currency codes identical + my ($iso_country_code_numeric, $iso_currency_code_numeric, $iso_currency_symbol, $currencyexponent); + + if ($currency =~ /GBP/i) { + $iso_currency_code_numeric = '826'; + $iso_currency_symbol = '£'; + $currencyexponent = '2'; + } + elsif ($currency =~ /EUR/i) { + $iso_currency_code_numeric = '978'; + $iso_currency_symbol = '€'; + $currencyexponent = '2'; + } + elsif ($currency =~ /USD/i) { + $iso_currency_code_numeric = '840'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /AUD/i) { + $iso_currency_code_numeric = '036'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /CAD/i) { + $iso_currency_code_numeric = '124'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /DKK/i) { + $iso_currency_code_numeric = '206'; + $iso_currency_symbol = 'kr'; + $currencyexponent = '2'; + } + elsif ($currency =~ /HKD/i) { + $iso_currency_code_numeric = '344'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /JPY/i) { + $iso_currency_code_numeric = '392'; + $iso_currency_symbol = 'Y'; + $currencyexponent = '0'; + } + elsif ($currency =~ /NZD/i) { + $iso_currency_code_numeric = '554'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /NOK/i) { + $iso_currency_code_numeric = '578'; + $iso_currency_symbol = 'kr'; + $currencyexponent = '2'; + } + elsif ($currency =~ /SGD/i) { + $iso_currency_code_numeric = '702'; + $iso_currency_symbol = '$'; + $currencyexponent = '2'; + } + elsif ($currency =~ /SEK/i) { + $iso_currency_code_numeric = '752'; + $iso_currency_symbol = 'kr'; + $currencyexponent = '2'; + } + elsif ($currency =~ /CHF/i) { + $iso_currency_code_numeric = '756'; + $iso_currency_symbol = 'CHF'; + $currencyexponent = '2'; + } + + if ($country =~ /GB|UK/i) { + $iso_country_code_numeric = '826'; + } + elsif ($country =~ /US/i) { + $iso_country_code_numeric = '840'; + } + else { + $db = dbref('country') || die ::errmsg("cannot open country table"); + $dbh = $db->dbh() || die ::errmsg("cannot get handle for tbl 'country'"); + $sth = $dbh->prepare("SELECT isonum FROM country WHERE code = '$country'"); + $sth->execute(); + $iso_country_code_numeric = $sth->fetchrow(); + }; + +#::logDebug("HSBC".__LINE__.": country=$country; currency=$currency; currencycode=$iso_currency_code_numeric; countrycode=$iso_country_code_numeric"); + $iso_currency_code_numeric = '826' unless defined $iso_currency_code_numeric; + $iso_country_code_numeric = '826' unless defined $iso_country_code_numeric; +#::logDebug("HSBC".__LINE__.": cardref=$cardref; req=$hsbcrequest; country=$country; currency=$currency; currencycode=$iso_currency_code_numeric"); +#::logDebug("HSBC".__LINE__.": usebill=$usebill; country: $::Values->{b_country}, $actual->{b_country}; $::Values->{country}, $actual->{country}"); + +#------------------------------------------------------------------------------------------------ +# Go to PaymentAuthenticationServer first, before posting to bank, if doing 3DSecure +# + if ($hsbcrequest eq 'tdspost') { +#::logDebug("HSBC".__LINE__." started 3DS post to PAS $tdshost"); +# now create encoded string to have returned with data for next post to gateway + $md = encode_base64("$pan:$cv2:$cv2indicator:$cardexpiration:$issuenumber:$amount:$x"); + + $::Session->{'tdshost'} = $::Scratch->{'tdshost'} = $tdshost; + $::Scratch->{'cardexpiration'} = $cardexpirationtds; + $::Scratch->{'cardholderpan'} = $pan; + $::Scratch->{'ccpaclientid'} = $ccpaclientid; + $::Scratch->{'currencyexponent'} = $currencyexponent; + $::Scratch->{'md'} = $md; + $::Scratch->{'purchaseamount'} = $iso_currency_symbol . $purchaseamount; + $::Scratch->{'purchaseamountraw'} = $amount; + $::Scratch->{'purchasecurrency'} = $iso_currency_code_numeric; + $::Scratch->{'resulturl'} = $returnurl; + + $::Scratch->{'issuenumber'} = $issuenumber; + + $::Scratch->{'tdsreturned'} = $::Scratch->{'tdsbounced'} = ''; + + $::Scratch->{'tdsrun'} = $::Scratch->{'tdspause'} = '1'; +#::logDebug("HSBC".__LINE__.": cursymbol=$iso_currency_symbol; pamnt=$purchaseamount; curcode=$iso_currency_code_numeric"); + my $hsbctds = $Tag->area({ href => "$hsbctdspage" }); +#::logDebug("HSBC".__LINE__." tdshost=$::Scratch->{'tdshost'}, $tdshost; returnurl=$returnurl; tdspage=$hsbctdspage; tds=$hsbctds"); +$Tag->tag({ op => 'header', body => <<EOB }); +Status: 302 moved +Location: $hsbctds +EOB + + } + +#---------------------------------------------------------------------------------------------- +# Header for use in gwpost or other ops +# + + my $header = <<EOX; +<?xml version="1.0" encoding="UTF-8" ?> + <EngineDocList> + <DocVersion DataType="String">1.0</DocVersion> + <EngineDoc> + <ContentType DataType="String">OrderFormDoc</ContentType> + <User> + <ClientId DataType="S32">$clientid</ClientId> + <Name DataType="String">$username</Name> + <Password DataType="String">$password</Password> + </User> +EOX + + +#---------------------------------------------------------------------------------------------------------- +# Returned from the PAS - read results from PAS and action accordingly +# + + if ($hsbcrequest eq 'gwpost') { +#::logDebug("HSBC".__LINE__." started $hsbcrequest; tdsreturn=$tdsreturn"); + +# Result from PAS is posted as key=val pairs in URL. Run this only if doing 3DS + if ($tdsreturn) { + my $tdsdata = ::http()->{entity} ; + my @tdsresult = split(/&/, $$tdsdata); +#::logDebug("HSBC".__LINE__.": tdsresult=@tdsresult; back=$$tdsdata"); + foreach (@tdsresult) { + my($key,$val) = split(/=/,$_); + $result{$key} = $val; +#::logDebug("HSBC".__LINE__.": tdsresult: $key = $val"); + } +#::logDebug("HSBC".__LINE__." result from PAS:" .::uneval(\%result)); + + undef $result{'MErrMsg'}; + +# If PAS 3DSecure result is OK then post to gateway +# Simply post directly to 'gwpost' and not 'tdspost' if you want to bypass 3DSecure +# Set various values according to the CcpaResultsCode + if ($result{'CcpaResultsCode'} =~ /5|6/) { +#::logDebug("HSBC".__LINE__.": code=$result{'CcpaResultsCode'}"); + $result{'MErrMsg'} = "Authentication failed, please try again or use a different card"; + return(%result); + } + elsif ($result{'CcpaResultsCode'} == '10') { + $result{'MErrMsg'} = "Payment error, please try again or use a different method"; + return(%result); + } + + elsif ($result{'CcpaResultsCode'} == '0'){ + $result{'PayerSecurityLevel'} = '2'; + $result{'PayerAuthenticationCode'} = $result{'CAVV'}; + $result{'PayerTxnId'} = $result{'XID'}; + $result{'CardholderPresentCode'} = '13'; + } + elsif ($result{'CcpaResultsCode'} == '1') { + $result{'PayerSecurityLevel'} = '5'; + $result{'CardholderPresentCode'} = '13'; + + } + elsif ($result{'CcpaResultsCode'} == '2') { + $result{'PayerSecurityLevel'} = '1'; + $result{'CardholderPresentCode'} = '13'; + } + elsif ($result{'CcpaResultsCode'} == '3') { + $result{'PayerSecurityLevel'} = '6'; + $result{'PayerAuthenticationCode'} = $result{'CAVV'}; + $result{'PayerTxnId'} = $result{'XID'}; + $result{'CardholderPresentCode'} = '13'; + } + elsif ($result{'CcpaResultsCode'} == '14') { + $result{'CardholderPresentCode'} = '7'; + } + else { + $result{'PayerSecurityLevel'} = '4'; + } +# end after PAS results parsed + $md = decode_base64($result{'MD'}); + ($pan,$cv2,$cv2indicator,$cardexpiration,$issuenumber,$amount,$x) = split /:/, $md; + +#::logDebug("HSBC".__LINE__." pan=$pan, cv2=$cv2, cv2ind=$cv2indicator, sid=$::Session->{id}; scpan=$::Scratch->{'cardholderpan'}"); + $pan ||= $::Scratch->{'cardholderpan'}; + + + } # if tdsreturn + + undef %result unless length $tdsreturn; + +#::logDebug("HSBC".__LINE__." result from PAS after massage:" .::uneval(\%result)); + + my $cardholderpresentcode = $result{'CardholderPresentCode'} || '7'; # 7 for initial setup without 3DS $paymenttype = 'PaymentNoFraud' if $cardholderpresentcode =~ /8|10/; + my $txid = $Tag->time({ body => "%Y%m%d%H%M%S" }) . $::Session->{id}; + $txid = $::Values->{'txid'} if ($txtype =~ /^PostAuth|Void|^RePreAuth|^ReAuth|Credit/i and !$pan); + $::Values->{'txid'} = ''; + my $grouptxid = "rp-" . $txid unless $::Values->{'grouptxid'}; # Pull from db on repeats, else generate for first instance of periodic billing + $::Values->{'grouptxid'} = ''; + # $order_id .= $::Session->{id}; + $::Session->{'order_id'} = $txid; + $::Session->{'HSBCHost'} = ''; + $::Scratch->{'tdspause'} = ''; +#::logDebug("HSBC".__LINE__.": pan=$pan, cv2=$cv2; exp=$cardexpiration, amnt=$amount; txid=$txid; rpordernumer=$::Values->{rpordernumber}; chpcode=$cardholderpresentcode"); + + $cv2 ||= $::Scratch->{'cv2'}; + $issuenumber ||= $::Scratch->{'issuenumber'}; +#::logDebug("HSBC".__LINE__.": amt=$amount; pan=$pan; cardholder=$cardholder; fname=$fname, lname=$lname, cv2=$cv2; cv2indi=$cv2indicator; address=$address"); + + my $xmlOut = $header; + + $xmlOut .= <<EOX; + <Instructions> + <Pipeline DataType="String">$paymenttype</Pipeline> + </Instructions> + <OrderFormDoc> + <Id DataType="String">$txid</Id> + <Mode DataType="String">$txmode</Mode> +EOX + + $xmlOut .= <<EOX unless $txtype =~ /void|credit|postauth/i; + <Consumer> + <BillTo> + <Location> + <TelVoice>$telephone</TelVoice> + <Email>$email</Email> + <Address> + <Title></Title> + <FirstName>$fname</FirstName> + <LastName>$lname</LastName> + <Name>$cardholder</Name> +EOX + + $xmlOut .= <<EOX if $paymenttype eq 'Payment'; + <Street1>$address1</Street1> + <Street2>$address2</Street2> + <Street3>$address3</Street3> + <City>$city</City> + <StateProv>$state</StateProv> + <PostalCode>$zip</PostalCode> + <Country>$iso_country_code_numeric</Country> +EOX + + $xmlOut .= <<EOX unless $txtype =~ /void|credit|refund|postauth/i; + </Address> + </Location> + </BillTo> + <PaymentMech> + <Type DataType="String">CreditCard</Type> + <CreditCard> + <Number DataType="String">$pan</Number> + <Expires DataType="ExpirationDate" Locale="$iso_country_code_numeric">$cardexpiration</Expires> +EOX + + $xmlOut .= <<EOX if $startdate; + <StartDate DataType="StartDate" Locale="$iso_country_code_numeric">$startdate</StartDate> +EOX +# for 'credit' as payment to card, rather than 'credit' as refund to txid +# <Type DataType="String">CreditCard</Type> + $xmlOut .= <<EOX if ($txtype =~ /credit/i and length $pan); + <Consumer> + <PaymentMech> + <CreditCard> + <Number DataType="String">$pan</Number> + <Expires DataType="ExpirationDate" Locale="$iso_country_code_numeric">$cardexpiration</Expires> + </CreditCard> + </PaymentMech> + </Consumer> +EOX + + $xmlOut .= <<EOX unless $txtype =~ /void|credit|refund|postauth/i; + <Cvv2Indicator>$cv2indicator</Cvv2Indicator> + <Cvv2Val>$cv2</Cvv2Val> + <IssueNum>$issuenumber</IssueNum> + </CreditCard> + </PaymentMech> + </Consumer> +EOX + $xmlOut .= <<EOX; + <Transaction> + <Type DataType="String">$txtype</Type> +EOX + + $xmlOut .= <<EOX unless $txtype =~ /void|credit|postauth/i; + <CardholderPresentCode>$cardholderpresentcode</CardholderPresentCode> +EOX + + $xmlOut .= <<EOX if $result{'XID'}; + <PayerAuthenticationCode>$result{'CAVV'}</PayerAuthenticationCode> + <PayerTxnId>$result{'XID'}</PayerTxnId> + <PayerSecurityLevel>$result{'PayerSecurityLevel'}</PayerSecurityLevel> +EOX + + $xmlOut .= <<EOX if $authcode; + <AuthCode>$authcode</AuthCode> +EOX + + + $xmlOut .= <<EOX unless $txtype =~ /void|postauth/i; + <CurrentTotals> + <Totals> + <Total DataType="Money" Currency="$iso_currency_code_numeric">$amount</Total> + </Totals> + </CurrentTotals> +EOX + + $xmlOut .= <<EOX; + </Transaction> +EOX + + $xmlOut .= <<EOX; + </OrderFormDoc> + </EngineDoc> + </EngineDocList> +EOX + +#::logDebug("HSBC".__LINE__.": xmlOut=$xmlOut"); + my $msg = postHSBC($xmlOut); +#::logDebug("HSBC".__LINE__.": msg returned=$msg"); + my $xml = new XML::Simple(Keyattr => 'EngineDocList'); + my $data = $xml->XMLin("$msg"); + $data = $data->{'EngineDoc'}{'OrderFormDoc'}; +#::logDebug("HSBC".__LINE__.": xmlback=".::uneval($data)); + +#::logDebug("HSBC".__LINE__.": MErrMsg=$result{'MErrMsg'}"); +#::logGlobal("HSBC: msg returned=$msg") if $result{'CcErrCode'} != '1'; + $result{'TxStatus'} = $data->{'Overview'}{'TransactionStatus'}; + $result{'TxType'} = $txtype; + $result{'CardRef'} = $::Session->{'CardRef'}; + $result{'Currency'} = $currency if length $currency; + + $result{'HSBCTxId'} = $data->{'Transaction'}{'Id'}{'content'}; + $result{'SecurityIndicator'} = $data->{'Transaction'}{'SecurityIndicator'}{'content'}; + $result{'AuthCode'} = $data->{'Transaction'}{'AuthCode'}{'content'}; + $result{'Type'} = $data->{'Transaction'}{'Auth'}{'content'}; + $result{'SendRPvsData'} = $data->{'Transaction'}{'SendPbAvsData'}{'content'}; + $result{'ProcReturnMsg'} = $data->{'Transaction'}{'CardProcResp'}{'ProcReturnMsg'}{'content'}; + $result{'ProcReturnCode'} = $data->{'Transaction'}{'CardProcResp'}{'ProcReturnCode'}{'content'}; + $result{'Status'} = $data->{'Transaction'}{'CardProcResp'}{'Status'}{'content'}; + $result{'CcErrCode'} = $data->{'Transaction'}{'CardProcResp'}{'CcErrCode'}{'content'}; + $result{'CcReturnMsg'} = $data->{'Transaction'}{'CardProcResp'}{'CcReturnMsg'}{'content'}; + $result{'ProcAvsRespCode'} = $data->{'Transaction'}{'CardProcResp'}{'ProcAvsRespCode'}{'content'}; + $result{'AvsDisplay'} = $data->{'Transaction'}{'CardProcResp'}{'AvsDisplay'}{'content'}; + $result{'CcReturnMsg'} = $data->{'Transaction'}{'CardProcResp'}{'CcReturnMsg'}{'content'}; + $result{'CommercialCardType'} = $data->{'Transaction'}{'CardProcResp'}{'CommercialCardType'}{'content'}; + $result{'Cvv2Resp'} = $data->{'Transaction'}{'CardProcResp'}{'Cvv2Resp'}{'content'}; + $result{'FraudStatus'} = $data->{'Overview'}{'FraudStatus'}{'content'}; + $result{'FraudResult'} = $data->{'FraudInfo'}{'FraudResult'}{'content'}; + $result{'FraudOrderScore'} = $data->{'FraudInfo'}{'OrderScore'}{'content'}; + + $pass = '1' if $result{'CcErrCode'} == '1'; # no errors, full pass + +# Multiple triggers of fraud rules: fail if one rule fails + if ($pass != '1') { + if ($data->{'FraudInfo'}{'Alerts'} =~ /ARRAY/i) { + for my $i (0 .. 3) { + $result{'FraudRuleId'} = $data->{'FraudInfo'}{'Alerts'}[$i]{'RuleId'}{'content'}; + $result{'FraudMsg'} = $data->{'FraudInfo'}{'Alerts'}[$i]{'Message'}{'content'}; + $pass = ''; + $pass = '1' if charge_param('fraud_' . $result{'FraudRuleId'}) =~ /1/; + $display .= " \"$result{'FraudMsg'}\" <br>" if charge_param('fraud_' . $result{'FraudRuleId'}) =~ /2/; + last if $pass != '1'; + } + } + else { + $result{'FraudRuleId'} = $data->{'FraudInfo'}{'Alerts'}{'RuleId'}{'content'}; + $result{'FraudMsg'} = $data->{'FraudInfo'}{'Alerts'}{'Message'}{'content'}; + $pass = '1' if charge_param('fraud_' . $result{'FraudRuleId'}) =~ /1/; # list of all rule IDs plus descriptions + $display = " \"$result{'FraudMsg'}\" " if charge_param('fraud_' .$result{'FraudRuleId'}) =~ /2/; # display the message + } + } + + $result{'FraudTotalScore'} = $data->{'FraudInfo'}{'TotalScore'}{'content'}; + $result{'FraudResultCode'} = $data->{'FraudInfo'}{'FraudResultCode'}{'content'}; + $result{'CardholderPresentCode'} = $data->{'Transaction'}{'CardholderPresentCode'}{'content'}; +#::logDebug("HSBC".__LINE__.": status=$result{'TxStatus'}; authcode=$result{'AuthCode'}; errorcode=$result{CcErrCode}"); + + my $hsbcdate = $Tag->time({ body => "%A %d %B %Y, %k:%M:%S, %Z" }); + my $amountshow = $amount/'100'; + +# +#------------------------------------------------------------------------------------------------- +# Now to complete things +# + +#::logDebug("HSBC".__LINE__.": rule=$result{FraudRuleId}; ReturnMsg=$result{'CcReturnMsg'}"); + $display =~ s/Select \'Accept\' to proceed with the order\.//gi; + $display =~ s/It is recommended that caution is exercised\.//gi; + $::Scratch->{'pspfraudmsg'} = ''; +#::logDebug("HSBC".__LINE__.": ErrCode=$result{CcErrCode}; MStatus=$result{MStatus}; SecureStatus=$result{'SecureStatus'}"); + if (($pass == '1') and ($hsbcrequest eq 'gwpost')) { + $result{'MStatus'} = 'success'; + $result{'pop.status'} = 'success'; + $::Scratch->{'mstatus'} = 'success'; + $result{'order-id'} ||= $::Session->{'order_id'}; + $::Values->{'mv_payment'} = "Real-time card $::Session->{'CardInfo'}"; + if (length $tdsreturn) { + undef $tdsreturn; + $::Values->{'psp'} = charge_param('psp') || 'HSBC'; + $::Session->{'payment_id'} = $result{'order-id'}; + $::Scratch->{'order_id'} = $result{'order-id'}; + $::CGI::values{'mv_todo'} = 'submit'; + $::Scratch->{'tdsrun'} = '1'; +#::logDebug("HSBC".__LINE__.": result=".::uneval(\%result)); + $::Scratch->{'pspfraudmsg'} = "Our bank has flagged this transaction, " + . $display . + " and we are obligated to review this order prior to accepting payment + and making despatch. Our apologies for any inconvenience." + if $result{'CcErrCode'} != '1'; + + $Vend::Session->{'payment_result'} = \%result; + Vend::Dispatch::do_process(); + + } + } + else { + $result{'MStatus'} = $result{'pop.status'} = $::Scratch->{'mstatus'} = 'fail'; + $result{'order-id'} = $result{'pop.order-id'} = ''; + $result{'MErrMsg'} = 'Order not taken, card problem. ' . $display; +#::logDebug("HSBC".__LINE__.": mstatus=$result{'MStatus'}; MErrMsg=$result{'MErrMsg'}"); + } + + # my $review = $result{'OverView'}{'FraudStatus'} || $result{'OverView'}{'FraudStatus'} +#::logDebug("HSBC".__LINE__.": fraudstatus=$result{'FraudStatus'}"); + if ($result{'FraudStatus'} =~ /Review/i) { + $mailreview = <<EOM; +At $hsbcdate, HSBC TxID $result{HSBCTxId}, for +$result{Currency} $amountshow has been marked both "$result{ProcReturnMsg}" and +"$result{CcReturnMsg}" with ErrorCode "$result{CcErrCode}". +EOM + + $::Tag->email({ to => $mailto, from => $mailto, reply => $mailto, + subject => "HSBC txn: blocked for fraud review", + body => "$mailreview\n\n", + }); +#::logDebug("HSBC".__LINE__.": blocked, now to mail: $mailreview"); + }; + + if (($result{'FraudStatus'} =~ /Decline/i) and (charge_param('mail_txn_declined') == '1')) { + $::Tag->email({ to => $mailto, from => $mailto, reply => $mailto, + subject => "HSBC txn: declined", + body => "$mailreview\n\n", + }); +#::logDebug("HSBC".__LINE__.": declined, now to mail: $mailreview"); + }; + + $mailtxn = "At $hsbcdate you received payment from $cardholder of +$result{Currency}$amountshow for OrderID $::Session->{'payment_id'}, HSBC txn ID $result{HSBCTxId}, + AuthCode $result{AuthCode}, from IP $ipaddress with card $result{CardBrand} $result{CardRef}"; + + if (($result{'CcReturnMsg'} =~ /Approved/i) and (charge_param('mail_txn_approved') == '1')) { + $::Tag->email({ to => $mailto, from => $mailto, reply => $mailto, + subject => "HSBC txn: approved", + body => "$mailtxn\n\n", + }); +#::logDebug("HSBC".__LINE__.": approved, now to mailto $mailto: $mailtxn"); + }; + +#::logDebug("HSBC".__LINE__.": result=".::uneval(\%result)); + return (%result); + + } # if gwpost + +} + +# +#-------------------------------------------------------------------------------------------------- +# End of main routine +#-------------------------------------------------------------------------------------------------- +# + +sub postHSBC { + my $self = shift; +#::logDebug("HSBC".__LINE__."\n###############\nxmlout=$self\n================\n"); + my $ua = LWP::UserAgent->new; + $ua->timeout(30); + my $req = HTTP::Request->new('POST' => $gwhost); + $req->content_type('text/xml'); + $req->content_length( length($self) ); + $req->content($self); + my $res = $ua->request($req); + my $respcode = $res->status_line; + + if ($res->is_success && $res->content){ + return ($res->content()); + } + else { + $::Session->{'errors'}{'HSBC'} = "No response from the HSBC payment gateway"; + return $res->status_line; + } +} + +package Vend::Payment::HSBC; + +1; _______________________________________________ interchange-cvs mailing list [email protected] http://www.icdevgroup.org/mailman/listinfo/interchange-cvs