User: jon Date: 2008-04-10 23:44:45 GMT Modified: lib/Vend/Payment Protx2.pm Log: Code cleanup after audit of Protx2 payment module. The logdir option was insecure, as it could be specified by an end-user, and could cause a new file to be touched. This is now only allowed if the developer wants it by setting this in catalog.cfg: Route protx logdir_from_user_allowed 1 It was also insecure in that it allowed absolute paths, disregarding NoAbsolute. That is now checked. Reworked external touch call with Perl open call to remove shell exploit possibility. This was probably the most dangerous part of accepting end-user logdir form input. Fix variable name typo that broke Diners Club detection. Made code work under strict pragma. Fixed errors and cleaned up POD documentation. Reformatted code with standard indenting, brace positioning, etc. Only tested against the Protx test environment; still needs live account testing by someone with an account. Revision Changes Path 1.2 interchange/lib/Vend/Payment/Protx2.pm rev 1.2, prev_rev 1.1 Index: Protx2.pm =================================================================== RCS file: /var/cvs/interchange/lib/Vend/Payment/Protx2.pm,v retrieving revision 1.1 retrieving revision 1.2 diff -u -u -r1.1 -r1.2 --- Protx2.pm 10 Apr 2008 23:35:40 -0000 1.1 +++ Protx2.pm 10 Apr 2008 23:44:45 -0000 1.2 @@ -1,7 +1,9 @@ -# Vend::Payment::Protx - Interchange Protx support +# Vend::Payment::Protx2 - Interchange Protx Direct payment support # -# Protx.pm, v 2.1.2, July 2007 +# $Id: Protx2.pm,v 1.2 2008-04-10 23:44:45 jon Exp $ +# Based on Protx2.pm, v 2.1.2, July 2007 # +# Copyright (C) 2008 Interchange Development Group # Copyright (C) 2007 Zolotek Resources Ltd. All rights reserved. # # Author: Lyn St George @@ -13,50 +15,40 @@ # # 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public Licence for more details. # # You should have received a copy of the GNU General Public -# Licence along with this program; if not, write to the Free -# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA. - - +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301 USA. package Vend::Payment::Protx2; -=head1 Interchange Protx Support - -Vend::Payment::Protx $Revision: 1.1 $ +=head1 NAME -http://kiwi.zolotek.net is the home page with the latest version. Also to be found on -Kevin Walsh's excellent Interchange site, http://interchange.rtfm.info. +Interchange Protx Direct payment system interface -=head1 This package is for the 'Protx Direct' payment system. +=head1 PREREQUISITES -Note that their 'Direct' system is the only one which leaves the customer on -your own site and takes payment in real time. Their other systems, eg Terminal -or Server, do not require this module. +Net::SSLeay + or +LWP::UserAgent and Crypt::SSLeay +wget - a recent version built with SSL and supporting the 'connect' timeout function. -Note also that Maestro cards can only be taken by the 3DSecure version of this module, not by this -version, as Mastercard have decreed that Maestro cards will no longer be accepted without 3DSecure. +=head1 QUICK START SUMMARY +1. Call this module in interchange.cfg with: -While PREAUTH is still in this module, it is scheduled to be dropped on the 1st August 2007 or shortly -thereafter, and is only here as a backup during the changeover to AUTHENTICATE. - + Require module Vend::Payment::Protx2 -=head1 Quick Start Summary +2. Add into products/variable.txt (tab separated): -1 Place this module in /lib/Vend/Payment/Protx210.pm + MV_PAYMENT_MODE protx -2 Call it in interchange.cfg with: - Require module Vend::Payment::Protx210 +3. Add a new route into catalog.cfg (options for the last entry in parentheses): -3 Add into variable.txt (tab separated): - MV_PAYMENT_MODE protx - Add a new route into catalog.cfg (options for the last entry in parentheses): Route protx id YourProtxID Route protx host ukvps.protx.com (ukvpstest.protx.com) Route protx currency GBP (USD, EUR, others, defaults to GBP) @@ -64,7 +56,7 @@ Route protx available yes (no, empty) Route protx logzero yes (no, empty) Route protx double_pay yes (no, empty) - Route protx logdir "/path/to/your/shop/tmp" + Route protx logdir "path/to/log/dir" Route protx protxlog yes (no, empty) Route protx applyavscv2 '0': if enabled then check, and if rules apply use. '1': force checks even if not enabled; if rules apply use. @@ -73,31 +65,22 @@ Route protx giftaidpayment 0 (1 to donate tax to Gift Aid) or put these vars into products/variable.txt instead: + MV_PAYMENT_ID YourProtxID Payment MV_PAYMENT_MODE protx Payment MV_PAYMENT_HOST ukvps.protx.com Payment MV_PAYMENT_CURRENCY GBP Payment + and the rest as above. -4 Create a new locale setting for en_UK as noted in "item currency" below, and copy the +4. Create a new locale setting for en_UK as noted in "item currency" below, and copy the public space interchange/en_US/ directory to a new interchange/en_UK/ one. Ensure that any other locales you might use have a correctly named directory as well. Ensure that this locale is found in your version of locale.txt (and set up UK as opposed to US language strings to taste). -5 Create entry boxes on your checkout page for: 'mv_credit_card_issue_number', 'mv_credit_card_start_month', +5. Create entry boxes on your checkout page for: 'mv_credit_card_issue_number', 'mv_credit_card_start_month', 'mv_credit_card_start_year', 'mv_credit_card_type', and optionally 'mv_credit_card_cvv2'. - - -=head1 PREREQUISITES - - Net::SSLeay - or - LWP::UserAgent and Crypt::SSLeay - - wget - a recent version built with SSL and supporting the 'connect' timeout function. - - =head1 DESCRIPTION The Vend::Payment::Protx module implements the Protx() routine for use with @@ -113,8 +96,17 @@ Make sure CreditCardAuto is off (default in Interchange demos). +Note that the Protx 'Direct' system is the only one which leaves the customer on +your own site and takes payment in real time. Their other systems, eg Terminal +or Server, do not require this module. + +Note also that Maestro cards can only be taken by the 3DSecure version of this module, not by this +version, as Mastercard have decreed that Maestro cards will no longer be accepted without 3DSecure. + +While PREAUTH is still in this module, it is scheduled to be dropped on the 1st August 2007 or shortly +thereafter, and is only here as a backup during the changeover to AUTHENTICATE. -=head1 The active settings. +=head2 The active settings The module uses several of the standard settings from the Interchange payment routes. Any such setting, as a general rule, is obtained first from the tag/call options on @@ -122,36 +114,41 @@ then a default global payment variable in products/variable.txt, and finally in some cases a default will be hard-coded into the module. +=over + =item Mode The mode can be named anything, but the C parameter must be set to C. To make it the default payment gateway for all credit card transactions in a specific catalog, you can set in C: - Variable MV_PAYMENT_MODE protx + Variable MV_PAYMENT_MODE protx + or in variable.txt: - MV_PAYMENT_MODE protx (tab separated) + MV_PAYMENT_MODE protx (tab separated) if you want this to cooperate with other payment systems, eg PaypalExpress or GoogleCheckout, then see the documentation that comes with that system - it should be fully explained there (essentially, you don't run the charge route from profiles.order but from log_transaction). - =item id Your Protx vendor ID, supplied by Protx when you sign up. Various ways to state this: + in variable.txt: + MV_PAYMENT_ID YourProtxID Payment + or in catalog.cfg either of: + Route protx id YourProtxID Variable MV_PAYMENT_ID YourProtxID - =item txtype -The transaction type is one of: PAYMENT, AUTHENTICATE or DEFERRED for an initial purchase -through the catalogue, and then can be one of: REFUND, RELEASE, REPEAT for payment +The transaction type is one of: PAYMENT, AUTHENTICATE or DEFERRED for an initial purchase +through the catalogue, and then can be one of: REFUND, RELEASE, REPEAT for payment operations through the virtual terminal. The transaction type is taken firstly from a dynamic variable in the page, meant @@ -164,7 +161,6 @@ the module and logged using the value returned from Protx, rather than a value from the page which possibly may not exist. - =item available If 'yes', then the module will check that the gateway is responding before sending the transaction. @@ -183,7 +179,6 @@ CV2 checking at run-time by default for such a transaction (logging the CV2 value breaks Visa/MC rules and so it can't be legally available for this process). - =item logzero If 'yes', then the module will log a transaction even if the amount sent is zero (which the @@ -193,19 +188,21 @@ by-pass the normal IC processes. IC will allow an item to be processed at zero total price but simply bypasses the gateway when doing so. - =item logempty + If 'yes, then if the response from Protx is read as empty (ie, zero bytes) the result is forced to 'success' and the transaction logged as though the customer has paid. There are two markers set to warn of this: + $Session->{payment_result}{TxType} will be NULL, + $Session->{payment_result}{StatusDetail} will be: 'UNKNOWN status - check with Protx before dispatching goods' -and you should include these into the report emailed to you. +and you should include these into the report emailed to you. =item card_type -Protx requires that the card type be sent. Valid types are: VISA, MC, AMEX, DELTA, SOLO, UKE, +Protx requires that the card type be sent. Valid types are: VISA, MC, AMEX, DELTA, SOLO, UKE, JCB, DINERS (UKE is Visa Electron issued in the UK). MAESTRO is no longer accepted without 3DSecure. This may optionally be determined by the module using regexes, or you may use a select box on the page. If there is an error in the regex match in this module due to a change in card ranges or some other @@ -215,24 +212,24 @@ You may display a select box on the checkout page like so: - - - + =item currency @@ -250,16 +247,14 @@ the module and logged using the value returned from Protx, rather than a value from the page which possibly may not exist. - =item cvv2 This is sent to Protx as mv_credit_card_cvv2. Put this on the checkout page: - CVV2:   + CVV2: but note that under PCI rules you must not log this value anywhere. - =item issue_number This is used for some debit cards, and taken from an input box on the checkout page: @@ -272,47 +267,66 @@ checkout page in a similar style to those for the card expiry date. The labels to be used are: 'mv_credit_card_start_month', 'mv_credit_card_start_year'. Eg: - - + =item Log directory To choose the directory used for logging both the Protx latency log and the double -payment safeguard record, set either: - Route protx logdir "/full/path/to/your/catalogue/tmp", or - -into your payment page. This allows an individual user to have his own logs in a shared -hosting environment. If you use this, ensure that you add a 'logdir' field to -transactions.txt. If not set, defaults to the system /tmp. +payment safeguard record, set in catalog.cfg: + + Route protx logdir "path/to/log/dir" + +It must be relative to the catalog root directory if you have +NoAbsolute set for this catalog in interchange.cfg. + +If logdir is not set, it defaults to the system /tmp. + +A somewhat dangerous option allows the payment page to specify the +logdir in a form variable, like this: + + + +This allows an individual user to have his own logs in a shared hosting +environment. However, it also allows a creative end-user to create +arbitrary empty files or update timestamps of existing files. + +Because of the potential for abuse, this option is not allowed unless you set +a special route variable indicating you want it: + + Route protx logdir_from_user_allowed 1 =item Protx API v2.22 extra functions + ApplyAVSCV2 set to: - 0 = If AVS/CV2 enabled then check them. If rules apply, use rules. (default) - 1 = Force AVS/CV2 checks even if not enabled for the account. If rules apply, use rules. - 2 = Force NO AVS/CV2 checks even if enabled on account. - 3 = Force AVS/CV2 checks even if not enabled for the account but DON'T apply any rules. - You may pass this value from the page as 'applyavscv2' to override the payment route setting. + + 0 = If AVS/CV2 enabled then check them. If rules apply, use rules. (default) + 1 = Force AVS/CV2 checks even if not enabled for the account. If rules apply, use rules. + 2 = Force NO AVS/CV2 checks even if enabled on account. + 3 = Force AVS/CV2 checks even if not enabled for the account but DON'T apply any rules. + You may pass this value from the page as 'applyavscv2' to override the payment route setting. CustomerName: optional, may be different to the cardholder name + ContactFax: optional + GiftAidPayment: set to - - 0 = This transaction is not a Gift Aid charitable donation(default) - 1 = This payment is a Gift Aid charitable donation and the customer has AGREED to donate the tax. - You may pass this value from the page as 'giftaidpayment' - + 0 = This transaction is not a Gift Aid charitable donation(default) + 1 = This payment is a Gift Aid charitable donation and the customer has AGREED to donate the tax. + You may pass this value from the page as 'giftaidpayment' + ClientIPAddress: will show in Protx reports, and they will attempt to Geo-locate the IP. =item Encrypted email with card info @@ -320,10 +334,7 @@ If you want to add the extra fields (issue no, start date) to the PGP message emailed back to you, then set the following in catalog.cfg: -VariableMV_CREDIT_CARD_INFO_TEMPLATE Card type: {MV_CREDIT_CARD_TYPE}; Card no: {MV_CREDIT_CARD_NUMBER}; Expiry: {MV_CREDIT_CARD_EXP_MONTH}/{MV_CREDIT_CARD_EXP_YEAR}; Issue no: {MV_CREDIT_CARD_ISSUE_NUMBER}; StartDate: {MV_CREDIT_CARD_START_MONTH}/{MV_CREDIT_CARD_START_YEAR} - - - + VariableMV_CREDIT_CARD_INFO_TEMPLATE Card type: {MV_CREDIT_CARD_TYPE}; Card no: {MV_CREDIT_CARD_NUMBER}; Expiry: {MV_CREDIT_CARD_EXP_MONTH}/{MV_CREDIT_CARD_EXP_YEAR}; Issue no: {MV_CREDIT_CARD_ISSUE_NUMBER}; StartDate: {MV_CREDIT_CARD_START_MONTH}/{MV_CREDIT_CARD_START_YEAR} =item testing @@ -332,7 +343,6 @@ (*without* any leading https://) or as 'Route protx host ukvpstest.protx.com' in catalog.cfg. - =item methods NB: Protx have removed PREAUTH from their protocol and replaced it with AUTHENTICATE/AUTHORISE. @@ -358,6 +368,7 @@ This does not need to refer to any previous transaction codes, and is useful if you need to make a refund but the customer's card has changed or the original purchase was not made by card. +=back =head2 Virtual Payment Terminal @@ -366,30 +377,28 @@ does refunds and repeats, directrefunds, and converts offline transactions to online ones. Being a plugin to the Interchange Admin Panel it integrates these operations into your database. - - -=head2 Troubleshooting +=head1 TROUBLESHOOTING Only the test card numbers given below will be successfully authorised (all other card numbers will be declined). -VISA 4929 0000 0000 6 -MASTERCARD 5404 0000 0000 0001 -DELTA 4462000000000003 -SOLO 6334900000000005 issue 1 -DOMESTIC MAESTRO 5641 8200 0000 0005 issue 01 (should be rejected now) -AMEX 3742 0000 0000 004 -ELECTRON 4917 3000 0000 0008 -JCB 3569 9900 0000 0009 -DINERS 3600 0000 0000 08 + VISA 4929 0000 0000 6 + MASTERCARD 5404 0000 0000 0001 + DELTA 4462000000000003 + SOLO 6334900000000005 issue 1 + DOMESTIC MAESTRO 5641 8200 0000 0005 issue 01 (should be rejected now) + AMEX 3742 0000 0000 004 + ELECTRON 4917 3000 0000 0008 + JCB 3569 9900 0000 0009 + DINERS 3600 0000 0000 08 You'll also need to supply the following values for CV2, Billing Address Numbers and Billing Post Code Numbers. These are the only values which will return as Matched on the test server. Any other values will return a Not Matched on the test server. -CV2 123 -Billing Address Numbers 88 -Billing Post Code Numbers 412 + CV2 123 + Billing Address Numbers 88 + Billing Post Code Numbers 412 If nothing works: @@ -418,21 +427,21 @@ Check the error logs, both catalogue and global. Make sure you set your payment parameters properly. Try an order, then put this code in a page: - - [calc] + <pre> + [calcn] my $string = $Tag->uneval( { ref => $Session->{payment_result} }); $string =~ s/{/{\n/; $string =~ s/,/,\n/g; return $string; - [/calc] - + [/calcn] + That should show what happened. =item * If you have unexplained and unlogged errors then check you have allowed the new database fields to -be NULL. If MySQL does not try to write to a field that is marked NOT NULL then it will fail silently. +be NULL. If MySQL tries to write to a field that is marked NOT NULL then it will fail silently. =item * @@ -446,7 +455,6 @@ If you get the same error message within the Virtual Terminal, then you haven't set the order route as noted above. - =item * If all else fails, Zolotek and other consultants are available to help @@ -454,25 +462,29 @@ =back -=head1 BUGS - -If using BDB/GDBM there is one known issue relating to SQL syntax - it is highly recommended that you -use an SQL database such as MySQL, Postgres, or DB2. This is now compulsory for recent versions of IC. +=head1 RESOURCES +http://kiwi.zolotek.net is the home page with the latest version. Also to be found on +Kevin Walsh's excellent Interchange site, http://interchange.rtfm.info. =head1 AUTHORS Lyn St George , based on original code by Mike Heins and others. -=head2 CREDITS +=head1 CREDITS + Hillary Corney (designersilversmiths.co.uk), Jamie Neil (versado.net), Andy Mayer (andymayer.net) for testing and suggestions. +=head1 LICENSE +GPLv2 =cut +use strict; + BEGIN { my $selected; @@ -503,7 +515,7 @@ die __PACKAGE__ . " requires Net::SSLeay or Crypt::SSLeay"; } -::logGlobal("%s v2.1.2 payment module initialised, using %s", __PACKAGE__, $selected) + ::logGlobal("%s v2.1.2 payment module initialised, using %s", __PACKAGE__, $selected) unless $Vend::Quiet; } @@ -512,426 +524,448 @@ sub protx { -my ($vendor, $amount, $actual, $opt); + my ($vendor, $amount, $actual, $opt); -# Amount sent to Protx, in 2 decimal places with any cruft removed. -# Defaults to 'amount' from the Accounts IPM or an invoicing system, falling back to IC input - $amount = $::Values->{amount} || Vend::Interpolate::total_cost(); - $amount =~ s/^\D*//g; - $amount =~ s/\,//g; - $amount = sprintf '%.2f', $amount; - -# Transaction type sent to Protx. -my $txtype = $::Values->{transtype} || charge_param('txtype') || $::Variable->{MV_PAYMENT_TRANSACTION} ||'PAYMENT'; -my $accountType = $::Values->{account_type} || charge_param('account_type') || 'E'; -my $payID = $::Values->{inv_no} || $::Session->{mv_transaction_id} || $::Session->{id}.$amount; -my $logdir = $::Values->{logdir} || charge_param('logdir') || '/tmp'; -my $logzero = charge_param('logzero') || 'no'; -my $available = charge_param('available') || 'no'; -my $logempty = $::Values->{logempty} || charge_param('logempty') || 'no'; -my $double_pay = $::Values->{double_pay} || charge_param('double_pay') || 'no'; -my $findcard = charge_param('find_card_type') || 'no'; # yes for auto, page for input, no for IC -my $description = charge_param('description') || $::Variable->{COMPANY}; - $description = substr($description,0,99); -my $applyAVSCV2 = $::Values->{applyavscv2} || charge_param('applyavscv2') || '0'; -my ($marker); - -my %result; - -# if payment is logged as made, raise an error message and exit -if ($txtype =~ /DEFERRED/i) { -$marker = "$logdir/pre-$payID" - } -else { -$marker = "$logdir/paid-$payID" -} + # Amount sent to Protx, in 2 decimal places with any cruft removed. + # Defaults to 'amount' from the Accounts IPM or an invoicing system, falling back to IC input + $amount = $::Values->{amount} || Vend::Interpolate::total_cost(); + $amount =~ s/^\D+//g; + $amount =~ s/,//g; + $amount = sprintf '%.2f', $amount; + + # Transaction type sent to Protx. + my $txtype = $::Values->{transtype} || charge_param('txtype') || $::Variable->{MV_PAYMENT_TRANSACTION} || 'PAYMENT'; + my $accountType = $::Values->{account_type} || charge_param('account_type') || 'E'; + my $payID = $::Values->{inv_no} || $::Session->{mv_transaction_id} || $::Session->{id}.$amount; + + my $logdir; + + # is logdir allowed to come from user? + if (charge_param('logdir_from_user_allowed')) { + $logdir = $::Values->{logdir}; + } + elsif ($::Values->{logdir}) { + ::logError("%s: user-specified logdir not allowed without route logdir_from_user 1", __PACKAGE__); + } + + # was logdir specified in route? + $logdir ||= charge_param('logdir'); + my $default_logdir = '/tmp'; + if (! $logdir) { + $logdir = $default_logdir; + } + # validate logdir is valid + elsif (! Vend::File::allowed_file("$logdir/TEST_FILE_NAME")) { + ::logError("%s: using logdir %s instead of disallowed %s", __PACKAGE__, $default_logdir, $logdir); + $logdir = $default_logdir; + } + $logdir = Vend::File::make_absolute_file($logdir); + + my $logzero = charge_param('logzero') || 'no'; + my $available = charge_param('available') || 'no'; + my $logempty = $::Values->{logempty} || charge_param('logempty') || 'no'; + my $double_pay = $::Values->{double_pay} || charge_param('double_pay') || 'no'; + my $findcard = charge_param('find_card_type') || 'no'; # yes for auto, page for input, no for IC + my $description = charge_param('description') || $::Variable->{COMPANY}; + $description = substr($description,0,99); + my $applyAVSCV2 = $::Values->{applyavscv2} || charge_param('applyavscv2') || '0'; + + # if payment is logged as made, raise an error message and exit + my $marker; + if ($txtype =~ /DEFERRED/i) { + $marker = "$logdir/pre-$payID"; + } + else { + $marker = "$logdir/paid-$payID"; + } -# check for double payment only if using the payment terminal or making an invoice payment. Allow the -# payment terminal to override this check to 'off' and allow identical amounts to be processed within -# the same session. -if (($::Values->{mv_order_route} =~ /ptipm_route|protx_vt_route/i) and (-e "$marker") and ($double_pay eq 'yes')) { -unless ($txtype =~ /REFUND|VOID|ABORT/) { - $result{MErrMsg} = "Payment for this transaction $marker has already been made - thank you"; - return %result; - } - } -# wrap around everything to bottom -else { - -my (%actual) = map_actual(); - $actual = \%actual; - $opt = {}; + my %result; + + # check for double payment only if using the payment terminal or making an invoice payment. Allow the + # payment terminal to override this check to 'off' and allow identical amounts to be processed within + # the same session. + if (($::Values->{mv_order_route} =~ /ptipm_route|protx_vt_route/i) and (-e $marker) and ($double_pay eq 'yes')) { + unless ($txtype =~ /REFUND|VOID|ABORT/) { + $result{MErrMsg} = "Payment for this transaction $marker has already been made - thank you"; + return %result; + } + } + # wrap around everything to bottom + else { + my %actual = map_actual(); + $actual = \%actual; + $opt = {}; #::logDebug("actual map result: " . ::uneval($actual)); - $vendor = $opt->{id} || charge_param('id') || $::Variable->{MV_PAYMENT_ID}; - $opt->{host} = charge_param('host') || $::Variable->{MV_PAYMENT_HOST} ||'ukvpstest.protx.com'; - $opt->{use_wget} = charge_param('use_wget') || '1'; - $opt->{port} = '443'; + $vendor = $opt->{id} || charge_param('id') || $::Variable->{MV_PAYMENT_ID}; + $opt->{host} = charge_param('host') || $::Variable->{MV_PAYMENT_HOST} || 'ukvpstest.protx.com'; + $opt->{use_wget} = charge_param('use_wget') || '1'; + $opt->{port} = '443'; -if ($txtype =~ /DEFERRED|PAYMENT|AUTHENTICATE|PREAUTH/i) { - $opt->{script} = '/vspgateway/service/vspdirect-register.vsp'; - } -elsif ($txtype =~ /RELEASE/i) { - $opt->{script} = '/vspgateway/service/release.vsp'; - } -elsif ($txtype =~ /DIRECTREFUND/i) { - $opt->{script} = '/vspgateway/service/directrefund.vsp'; - } -elsif ($txtype =~ /REFUND/i) { - $opt->{script} = '/vspgateway/service/refund.vsp'; - } -elsif ($txtype =~ /VOID/i) { - $opt->{script} = '/vspgateway/service/void.vsp'; - } -elsif ($txtype =~ /CANCEL/i) { - $opt->{script} = '/vspgateway/service/cancel.vsp'; - } -elsif ($txtype =~ /ABORT/i) { - $opt->{script} = '/vspgateway/service/abort.vsp'; - } -elsif ($txtype =~ /MANUAL/i) { - $opt->{script} = '/vspgateway/service/manualpayment.vsp'; - } -elsif ($txtype =~ /REPEAT|REPEATDEFERRED/i) { - $opt->{script} = '/vspgateway/service/repeat.vsp'; - } -elsif ($txtype =~ /AUTHORISE/i) { - $opt->{script} = '/vspgateway/service/authorise.vsp'; - } + if ($txtype =~ /DEFERRED|PAYMENT|AUTHENTICATE|PREAUTH/i) { + $opt->{script} = '/vspgateway/service/vspdirect-register.vsp'; + } + elsif ($txtype =~ /RELEASE/i) { + $opt->{script} = '/vspgateway/service/release.vsp'; + } + elsif ($txtype =~ /DIRECTREFUND/i) { + $opt->{script} = '/vspgateway/service/directrefund.vsp'; + } + elsif ($txtype =~ /REFUND/i) { + $opt->{script} = '/vspgateway/service/refund.vsp'; + } + elsif ($txtype =~ /VOID/i) { + $opt->{script} = '/vspgateway/service/void.vsp'; + } + elsif ($txtype =~ /CANCEL/i) { + $opt->{script} = '/vspgateway/service/cancel.vsp'; + } + elsif ($txtype =~ /ABORT/i) { + $opt->{script} = '/vspgateway/service/abort.vsp'; + } + elsif ($txtype =~ /MANUAL/i) { + $opt->{script} = '/vspgateway/service/manualpayment.vsp'; + } + elsif ($txtype =~ /REPEAT|REPEATDEFERRED/i) { + $opt->{script} = '/vspgateway/service/repeat.vsp'; + } + elsif ($txtype =~ /AUTHORISE/i) { + $opt->{script} = '/vspgateway/service/authorise.vsp'; + } - my @override = qw/ - order_id - mv_credit_card_exp_month - mv_credit_card_exp_year - mv_credit_card_start_month - mv_credit_card_start_year - mv_credit_card_issue_number - mv_credit_card_number - /; - for(@override) { - next unless defined $opt->{$_}; - $actual->{$_} = $opt->{$_}; - } + my @override = qw/ + order_id + mv_credit_card_exp_month + mv_credit_card_exp_year + mv_credit_card_start_month + mv_credit_card_start_year + mv_credit_card_issue_number + mv_credit_card_number + /; + for(@override) { + next unless defined $opt->{$_}; + $actual->{$_} = $opt->{$_}; + } -my $ccnum = $actual->{mv_credit_card_number}; - $ccnum =~ 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/; - -my $startDateMonth = $actual->{mv_credit_card_start_month} || $::Values->{mv_credit_card_start_month} || $::Values->{start_date_month} || 01; - $startDateMonth =~ s/\D//g; - -my $startDateYear = $actual->{mv_credit_card_start_year} || $::Values->{mv_credit_card_start_year} || $::Values->{start_date_year} || 06; - $startDateYear =~ s/\D//g; - $startDateYear =~ s/\d\d(\d\d)/$1/; - -my $issue = $actual->{mv_credit_card_issue_number} || $::Values->{mv_credit_card_issue_number} || $::Values->{card_issue_number}; - $issue =~ s/\D//g; - -my $cvv2 = $actual->{mv_credit_card_cvv2} || $::Values->{cvv2}; - $cvv2 =~ s/\D//g; - -# overide the configured AVSCV2 setting -if($txtype =~ /REPEAT|RELEASE|REFUND/i) { - $applyAVSCV2 = '2'; -} + my $ccnum = $actual->{mv_credit_card_number}; + $ccnum =~ 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/; + + my $startDateMonth = $actual->{mv_credit_card_start_month} || $::Values->{mv_credit_card_start_month} || $::Values->{start_date_month} || 01; + $startDateMonth =~ s/\D//g; + + my $startDateYear = $actual->{mv_credit_card_start_year} || $::Values->{mv_credit_card_start_year} || $::Values->{start_date_year} || 06; + $startDateYear =~ s/\D//g; + $startDateYear =~ s/\d\d(\d\d)/$1/; + + my $issue = $actual->{mv_credit_card_issue_number} || $::Values->{mv_credit_card_issue_number} || $::Values->{card_issue_number}; + $issue =~ s/\D//g; + + my $cvv2 = $actual->{mv_credit_card_cvv2} || $::Values->{cvv2}; + $cvv2 =~ s/\D//g; + + # overide the configured AVSCV2 setting + if($txtype =~ /REPEAT|RELEASE|REFUND/i) { + $applyAVSCV2 = '2'; + } -my $exp = sprintf '%02d%02d', - $actual->{mv_credit_card_exp_month}, $actual->{mv_credit_card_exp_year}; + my $exp = sprintf '%02d%02d', $actual->{mv_credit_card_exp_month}, $actual->{mv_credit_card_exp_year}; -my $startDate; -if (!$startDateMonth) { $startDate = ''; - } -else { $startDate = sprintf '%02d%02d', $startDateMonth, $startDateYear; -} + my $startDate; + if (!$startDateMonth) { + $startDate = ''; + } + else { + $startDate = sprintf '%02d%02d', $startDateMonth, $startDateYear; + } -my $cardType; + my $cardType; -if ($::Values->{mv_credit_card_type}) { - $cardType = $::Values->{mv_credit_card_type}; - } -else { - - if ($ccnum =~ /^4(?:5085[0-9]|91880)\d{10}$/) {$cardType = 'UKE'} - elsif ($ccnum =~ /^4917(?:3[0-3]|4(?:[0-2]|[9])|5[28])\d{10}$/) {$cardType = 'UKE'} - - elsif ($ccnum =~ /^4462[0-9][0-9]\d{10}$/) {$cardType = 'Delta'} - elsif ($ccnum =~ /^45(?:397[89]|4313|443[2-5])\d{10}$/) {$cardType = 'Delta'} - elsif ($ccnum =~ /^4547(?:[2][5-9]|[3][0-9]|[4][0-5])\d{10}$/) {$cardType = 'Delta'} - elsif ($ccnum =~ /^49(?:09[67][0-9]|218[12]|8824)\d{10}$/) {$cardType = 'Delta'} - - elsif ($ccnum =~ /^6011\d{12}$/) {$cardType = 'Discover'} - elsif ($ccnum =~ /^3(?:6\d{12}|0[0-5]\d{11})$/) {$cardtype = 'Dinersclub'} - elsif ($ccnum =~ /^38\d{12}$/) {$cardType = 'Carteblanche' } - elsif ($ccnum =~ /^2(?:014|149)\d{11}$/) {$cardType = 'Enroute'} - elsif ($ccnum =~ /^(?:3\d{15}|2131\d{11}|1800\d{11})$/) {$cardType = 'JCB'} - - elsif ($ccnum =~ /^490(?:30[2-9]|33[5-9]|340|52[5-9])\d{10,12}$/) {$cardType = 'MAESTRO'} - elsif ($ccnum =~ /^4911(?:0[0-2]|7[4-9]|8[0-2])\d{12,14}$/) {$cardType = 'MAESTRO'} - elsif ($ccnum =~ /^4936[0-9][0-9]\d{10,13}$/) {$cardType = 'MAESTRO'} - elsif ($ccnum =~ /^564182\d{10}$/) {$cardType = 'MAESTRO'} - elsif ($ccnum =~ /^633(?:110|3[0-9][0-9]|461)\d{10,12,13}$/) {$cardType = 'MAESTRO'} - elsif ($ccnum =~ /^6759\d{12,14,15}$/) {$cardType = 'MAESTRO'} - - elsif ($ccnum =~ /^49030[2-9]\d{12}$/) {$cardType = 'Solo'} - elsif ($ccnum =~ /^63345[0-9]\d{10}$/) {$cardType = 'Solo'} - elsif ($ccnum =~ /^63346([0]|[2-9])\d{10}$/) {$cardType = 'Solo'} - elsif ($ccnum =~ /^6334[7-9][0-9]\d{10,12,13}$/) {$cardType = 'Solo'} - elsif ($ccnum =~ /^6767[0-9][0-9]\d{10,12,13}$/) {$cardType = 'Solo'} - - elsif ($ccnum =~ /^4(?:\d{12}|\d{15})$/) {$cardType = 'Visa'} - elsif ($ccnum =~ /^5[1-5]\d{14}$/) {$cardType = 'MC'} - elsif ($ccnum =~ /^3[47]\d{13}$/) {$cardType = 'Amex'} + if ($::Values->{mv_credit_card_type}) { + $cardType = $::Values->{mv_credit_card_type}; + } + else { + if ($ccnum =~ /^4(?:5085[0-9]|91880)\d{10}$/) {$cardType = 'UKE'} + elsif ($ccnum =~ /^4917(?:3[0-3]|4(?:[0-2]|[9])|5[28])\d{10}$/) {$cardType = 'UKE'} + + elsif ($ccnum =~ /^4462[0-9][0-9]\d{10}$/) {$cardType = 'Delta'} + elsif ($ccnum =~ /^45(?:397[89]|4313|443[2-5])\d{10}$/) {$cardType = 'Delta'} + elsif ($ccnum =~ /^4547(?:[2][5-9]|[3][0-9]|[4][0-5])\d{10}$/) {$cardType = 'Delta'} + elsif ($ccnum =~ /^49(?:09[67][0-9]|218[12]|8824)\d{10}$/) {$cardType = 'Delta'} + + elsif ($ccnum =~ /^6011\d{12}$/) {$cardType = 'Discover'} + elsif ($ccnum =~ /^3(?:6\d{12}|0[0-5]\d{11})$/) {$cardType = 'Dinersclub'} + elsif ($ccnum =~ /^38\d{12}$/) {$cardType = 'Carteblanche'} + elsif ($ccnum =~ /^2(?:014|149)\d{11}$/) {$cardType = 'Enroute'} + elsif ($ccnum =~ /^(?:3\d{15}|2131\d{11}|1800\d{11})$/) {$cardType = 'JCB'} + + elsif ($ccnum =~ /^490(?:30[2-9]|33[5-9]|340|52[5-9])\d{10,12}$/) {$cardType = 'MAESTRO'} + elsif ($ccnum =~ /^4911(?:0[0-2]|7[4-9]|8[0-2])\d{12,14}$/) {$cardType = 'MAESTRO'} + elsif ($ccnum =~ /^4936[0-9][0-9]\d{10,13}$/) {$cardType = 'MAESTRO'} + elsif ($ccnum =~ /^564182\d{10}$/) {$cardType = 'MAESTRO'} + elsif ($ccnum =~ /^633(?:110|3[0-9][0-9]|461)\d{10,12,13}$/) {$cardType = 'MAESTRO'} + elsif ($ccnum =~ /^6759\d{12,14,15}$/) {$cardType = 'MAESTRO'} + + elsif ($ccnum =~ /^49030[2-9]\d{12}$/) {$cardType = 'Solo'} + elsif ($ccnum =~ /^63345[0-9]\d{10}$/) {$cardType = 'Solo'} + elsif ($ccnum =~ /^63346([0]|[2-9])\d{10}$/) {$cardType = 'Solo'} + elsif ($ccnum =~ /^6334[7-9][0-9]\d{10,12,13}$/) {$cardType = 'Solo'} + elsif ($ccnum =~ /^6767[0-9][0-9]\d{10,12,13}$/) {$cardType = 'Solo'} + + elsif ($ccnum =~ /^4(?:\d{12}|\d{15})$/) {$cardType = 'Visa'} + elsif ($ccnum =~ /^5[1-5]\d{14}$/) {$cardType = 'MC'} + elsif ($ccnum =~ /^3[47]\d{13}$/) {$cardType = 'Amex'} + } -} -# Mastercard require Maestro to use 3ds now. -if ($cardType =~ /Switch|Maestro/i) { - $result{MStatus} = $result{'pop.status'} = 'failed'; - $result{MErrMsg} = "Sorry, we do not accept Maestro cards"; - return %result; - } - - -my $cardRef = $actual->{mv_credit_card_number}; - $cardRef =~ s/^(\d\d).*(\d\d\d\d)$/$1****$2/; - -# Prefer billing values but fall back to shipping values. -my $billingAddress = sprintf '%s, %s, %s, %s', - $actual->{b_address} || $actual->{address}, - $actual->{b_city} || $actual->{city}, - $actual->{b_state} || $actual->{state}, - $actual->{b_country} || $actual->{country}; - -my $deliveryAddress = sprintf '%s, %s, %s, %s', - $actual->{address}, - $actual->{city}, - $actual->{state}, - $actual->{country}; - -my $cardHolder = $actual->{b_name} || '$actual->{b_fname} $actual->{b_lname}' || - $actual->{name} || '$actual->{fname} $actual->{lname}'; - -my $billingPostCode = $actual->{b_zip} || $actual->{zip}; -my $deliveryPostCode = $actual->{zip} || $actual->{b_zip}; -my $customerName = $actual->{name} || '$actual->{fname} $actual->{lname}' || $cardHolder; -my $contactNumber = $actual->{phone_day} || $actual->{phone_night}; -my $customerEmail = $actual->{email}; -my $contactFax = $::Values->{fax} || ''; -my $giftAidPayment = $::Values->{giftaidpayment} || charge_param('giftaidpayment') || '0'; -my $authCode = $::Values->{authcode} || ''; -my $clientIPAddress = $CGI::remote_addr; - -# VendorTxCode generated here. - $order_id = gen_order_id($opt); -if ($txtype =~ /RELEASE|VOID|ABORT/i) { - $vendorTxCode = $::Values->{OrigVendorTxCode} + # Mastercard require Maestro to use 3ds now. + if ($cardType =~ /Switch|Maestro/i) { + $result{MStatus} = $result{'pop.status'} = 'failed'; + $result{MErrMsg} = "Sorry, we do not accept Maestro cards"; + return %result; } -else { - $vendorTxCode = $order_id -} -# ISO currency code sent to Protx, from the page or fall back to config files. -my $currency = $::Values->{iso_currency_code} || $::Values->{currency_code} || $Vend::Cfg->{Locale}{iso_currency_code} || - charge_param('currency') || $::Variable->{MV_PAYMENT_CURRENCY} || 'GBP'; - -my $psp_host = $opt->{host}; - -# The string sent to Protx. -my %query = ( - TxType => $txtype, - VendorTxCode => $vendorTxCode, - Vendor => $vendor, - AccountType => $accountType, - VPSProtocol => '2.22', - Apply3DSecure => '2' - ); -if ($txtype =~ /REFUND|REPEAT|AUTHORISE/) { - $query{RelatedVPSTxID} = $::Values->{RelatedVPSTxID}; - $query{RelatedVendorTxCode} = $::Values->{RelatedVendorTxCode}; - $query{RelatedSecurityKey} = $::Values->{RelatedSecurityKey}; - $query{Description} = $description; - $query{Amount} = $amount; - } -if ($txtype =~ /REFUND|REPEAT/) { - $query{RelatedTxAuthNo} = $::Values->{RelatedTxAuthNo}; - $query{Currency} = $currency; - } -if ($txtype =~ /VOID|ABORT|CANCEL/i) { - $query{VPSTxID} = $::Values->{OrigVPSTxID}; - $query{SecurityKey} = $::Values->{OrigSecurityKey}; - } -if ($txtype =~ /VOID|ABORT/i) { - $query{TxAuthNo} = $::Values->{OrigTxAuthNo}; - } -if ($txtype =~ /DIRECTREFUND|DEFERRED|PAYMENT|PREAUTH|AUTHENTICATE|MANUAL/i) { - $query{CardType} = $cardType; - $query{CardNumber} = $ccnum; - $query{IssueNumber} = $issue; - $query{CardHolder} = $cardHolder; - $query{Description} = $description; - $query{Amount} = $amount; - $query{Currency} = $currency; - $query{StartDate} = $startDate; - $query{ExpiryDate} = $exp - } + my $cardRef = $actual->{mv_credit_card_number}; + $cardRef =~ s/^(\d\d).*(\d\d\d\d)$/$1****$2/; -if ($txtype =~ /PAYMENT|DEFERRED|PREAUTH|AUTHENTICATE|MANUAL/i) { - $query{BillingAddress} = $billingAddress; - $query{DeliveryAddress} = $deliveryAddress; - $query{BillingPostCode} = $billingPostCode; - $query{DeliveryPostCode} = $deliveryPostCode; - $query{CustomerName} = $customerName; - $query{ContactNumber} = $contactNumber; - $query{ContactFax} = $contactFax; - $query{CustomerEmail} = $customerEmail; - $query{GiftAidPayment} = $giftAidPayment; - $query{ClientIPAddress} = $clientIPAddress; - $query{AuthCode} = $authCode; - $query{CV2} = $cvv2; - } -if ($txtype =~ /PAYMENT|DEFERRED|PREAUTH|AUTHENTICATE|AUTHORISE/i) { - $query{ApplyAVSCV2} = $applyAVSCV2; - } - - foreach $key (sort keys(%query)) { -#::logDebug("Sent to Protx: $key=$query{$key}"); - } - -# Test for gateway availability, and if not available optionally go off-line and complete -# transaction for manual processing later. Also go off-line if amount is zero, so as to log the -# transaction and email a receipt for audit purposes (useful mainly for subscription billing). + # Prefer billing values but fall back to shipping values. + my $billingAddress = sprintf '%s, %s, %s, %s', + $actual->{b_address} || $actual->{address}, + $actual->{b_city} || $actual->{city}, + $actual->{b_state} || $actual->{state}, + $actual->{b_country} || $actual->{country}; + + my $deliveryAddress = sprintf '%s, %s, %s, %s', + $actual->{address}, + $actual->{city}, + $actual->{state}, + $actual->{country}; + + my $cardHolder = $actual->{b_name} || '$actual->{b_fname} $actual->{b_lname}' + || $actual->{name} || '$actual->{fname} $actual->{lname}'; + + my $billingPostCode = $actual->{b_zip} || $actual->{zip}; + my $deliveryPostCode = $actual->{zip} || $actual->{b_zip}; + my $customerName = $actual->{name} || '$actual->{fname} $actual->{lname}' || $cardHolder; + my $contactNumber = $actual->{phone_day} || $actual->{phone_night}; + my $customerEmail = $actual->{email}; + my $contactFax = $::Values->{fax} || ''; + my $giftAidPayment = $::Values->{giftaidpayment} || charge_param('giftaidpayment') || '0'; + my $authCode = $::Values->{authcode} || ''; + my $clientIPAddress = $CGI::remote_addr; + + # VendorTxCode generated here. + my $vendorTxCode; + my $order_id = gen_order_id($opt); + if ($txtype =~ /RELEASE|VOID|ABORT/i) { + $vendorTxCode = $::Values->{OrigVendorTxCode}; + } + else { + $vendorTxCode = $order_id; + } + + # ISO currency code sent to Protx, from the page or fall back to config files. + my $currency = $::Values->{iso_currency_code} || $::Values->{currency_code} || $Vend::Cfg->{Locale}{iso_currency_code} + || charge_param('currency') || $::Variable->{MV_PAYMENT_CURRENCY} || 'GBP'; + + my $psp_host = $opt->{host}; + + # The string sent to Protx. + my %query = ( + TxType => $txtype, + VendorTxCode => $vendorTxCode, + Vendor => $vendor, + AccountType => $accountType, + VPSProtocol => '2.22', + Apply3DSecure => '2', + ); + if ($txtype =~ /REFUND|REPEAT|AUTHORISE/) { + $query{RelatedVPSTxID} = $::Values->{RelatedVPSTxID}; + $query{RelatedVendorTxCode} = $::Values->{RelatedVendorTxCode}; + $query{RelatedSecurityKey} = $::Values->{RelatedSecurityKey}; + $query{Description} = $description; + $query{Amount} = $amount; + } + if ($txtype =~ /REFUND|REPEAT/) { + $query{RelatedTxAuthNo} = $::Values->{RelatedTxAuthNo}; + $query{Currency} = $currency; + } + if ($txtype =~ /VOID|ABORT|CANCEL/i) { + $query{VPSTxID} = $::Values->{OrigVPSTxID}; + $query{SecurityKey} = $::Values->{OrigSecurityKey}; + } + if ($txtype =~ /VOID|ABORT/i) { + $query{TxAuthNo} = $::Values->{OrigTxAuthNo}; + } + if ($txtype =~ /DIRECTREFUND|DEFERRED|PAYMENT|PREAUTH|AUTHENTICATE|MANUAL/i) { + $query{CardType} = $cardType; + $query{CardNumber} = $ccnum; + $query{IssueNumber} = $issue; + $query{CardHolder} = $cardHolder; + $query{Description} = $description; + $query{Amount} = $amount; + $query{Currency} = $currency; + $query{StartDate} = $startDate; + $query{ExpiryDate} = $exp + } + if ($txtype =~ /PAYMENT|DEFERRED|PREAUTH|AUTHENTICATE|MANUAL/i) { + $query{BillingAddress} = $billingAddress; + $query{DeliveryAddress} = $deliveryAddress; + $query{BillingPostCode} = $billingPostCode; + $query{DeliveryPostCode} = $deliveryPostCode; + $query{CustomerName} = $customerName; + $query{ContactNumber} = $contactNumber; + $query{ContactFax} = $contactFax; + $query{CustomerEmail} = $customerEmail; + $query{GiftAidPayment} = $giftAidPayment; + $query{ClientIPAddress} = $clientIPAddress; + $query{AuthCode} = $authCode; + $query{CV2} = $cvv2; + } + if ($txtype =~ /PAYMENT|DEFERRED|PREAUTH|AUTHENTICATE|AUTHORISE/i) { + $query{ApplyAVSCV2} = $applyAVSCV2; + } -my ($request, $in); +#::logDebug("Sent to Protx: " . ::uneval(\%query)); -#::logDebug("Protx809: available=$available, amount=$amount, order_route=$::Values->{mv_order_route}, logzero=$logzero\n"); + # Test for gateway availability, and if not available optionally go off-line and complete + # transaction for manual processing later. Also go off-line if amount is zero, so as to log the + # transaction and email a receipt for audit purposes (useful mainly for subscription billing). -if (($available eq 'yes') and ($amount > 0) and ($::Values->{mv_order_route} !~ /ptipm_route|protx_vt_route/i)) { - my $CMD = '/usr/bin/wget -nv --spider -T9 -t1'; - open (IN, "$CMD https://$psp_host 2>&1 |") || die "Could not open pipe to wget: $!\n"; - $in = ; - close(IN); - # $in = 'test'; ######################## testing only, will force offline mode - if ($in =~ /^200 OK$/) { $request = 'psp'; } - else { $request = 'offline'; } + my ($request, $in); - } -elsif (($::Values->{mv_order_route} =~ /ptipm_route|protx_vt_route/i) and ($amount > 0)) { - $request = 'psp'; - } -elsif (($available ne 'yes') and ($amount > 0)) { - $request = 'psp'; - } -elsif (($amount == 0) and ($logzero eq 'yes')) { - $request = 'log'; -} +#::logDebug("Protx809: available=$available, amount=$amount, order_route=$::Values->{mv_order_route}, logzero=$logzero\n"); + + if (($available eq 'yes') and ($amount > 0) and ($::Values->{mv_order_route} !~ /ptipm_route|protx_vt_route/i)) { + my $CMD = '/usr/bin/wget -nv --spider -T9 -t1'; + open (my $in, "$CMD https://$psp_host 2>&1 |") || die "Could not open pipe to wget: $!\n"; + $in = <$in>; + close $in; + # $in = 'test'; # testing only, will force offline mode + if ($in =~ /^200 OK$/) { + $request = 'psp'; + } + else { + $request = 'offline'; + } + } + elsif (($::Values->{mv_order_route} =~ /ptipm_route|protx_vt_route/i) and ($amount > 0)) { + $request = 'psp'; + } + elsif (($available ne 'yes') and ($amount > 0)) { + $request = 'psp'; + } + elsif (($amount == 0) and ($logzero eq 'yes')) { + $request = 'log'; + } -if ($request eq 'psp') { + if ($request eq 'psp') { - my $post = post_data($opt, \%query); - my $response = $post->{status_line}; - my $page = $post->{result_page}; + my $post = post_data($opt, \%query); + my $response = $post->{status_line}; + my $page = $post->{result_page}; #::logDebug("Response from Protx:\n$page \nend of response from Protx\n\n"); - $result{TxType} = $txtype; - $result{Currency} = $currency; - $result{CardRef} = $cardRef; - $result{CardType} = $cardType; - $result{ExpAll} = $exp; - $result{amount} = $amount; - $result{request} = $request; - $result{IP} = $clientIPAddress; - if ($cardType eq 'UKE') {$result{CardType} = 'Electron';} - $result{CardInfo} = "$result{CardType}, $cardRef, $actual->{mv_credit_card_exp_month}/$actual->{mv_credit_card_exp_year}"; - -for my $line (split /\r\n/, $page) { - if ($line =~ /VPSTxID=(.*)/i) { $result{VPSTxID} = $1; } - if ($line =~ /^Status=(.*)/i) { $result{Status} = $1; } - if ($line =~ /StatusDetail=(.*)/i) { $result{StatusDetail} = $1; } - if ($line =~ /TxAuthNo=(.*)/i) { $result{TxAuthNo} = $1;} - if ($line =~ /SecurityKey=(.*)/i) { $result{SecurityKey} = $1; } - if ($line =~ /AVSCV2=(.*)/i) { $result{AVSCV2} = $1; } - if ($line =~ /VPSProtocol=(.*)/i) { $result{VPSProtocol} = $1; } - if ($line =~ /AddressResult=(.*)/i) { $result{AddressResult} = $1; } - if ($line =~ /PostCodeResult=(.*)/i) { $result{PostCodeResult} = $1; } - if ($line =~ /CV2Result=(.*)/i) { $result{CV2Result} = $1; } -} + $result{TxType} = $txtype; + $result{Currency} = $currency; + $result{CardRef} = $cardRef; + $result{CardType} = $cardType; + $result{ExpAll} = $exp; + $result{amount} = $amount; + $result{request} = $request; + $result{IP} = $clientIPAddress; + if ($cardType eq 'UKE') { $result{CardType} = 'Electron'; } + $result{CardInfo} = "$result{CardType}, $cardRef, $actual->{mv_credit_card_exp_month}/$actual->{mv_credit_card_exp_year}"; + + for my $line (split /\r\n/, $page) { + if ($line =~ /VPSTxID=(.*)/i) { $result{VPSTxID} = $1; } + if ($line =~ /^Status=(.*)/i) { $result{Status} = $1; } + if ($line =~ /StatusDetail=(.*)/i) { $result{StatusDetail} = $1; } + if ($line =~ /TxAuthNo=(.*)/i) { $result{TxAuthNo} = $1;} + if ($line =~ /SecurityKey=(.*)/i) { $result{SecurityKey} = $1; } + if ($line =~ /AVSCV2=(.*)/i) { $result{AVSCV2} = $1; } + if ($line =~ /VPSProtocol=(.*)/i) { $result{VPSProtocol} = $1; } + if ($line =~ /AddressResult=(.*)/i) { $result{AddressResult} = $1; } + if ($line =~ /PostCodeResult=(.*)/i) { $result{PostCodeResult} = $1; } + if ($line =~ /CV2Result=(.*)/i) { $result{CV2Result} = $1; } + } -if ($txtype =~ /PAYMENT|DEFERRED|MANUAL|PREAUTH|AUTHENTICATE|AUTHORISE/i) { + if ($txtype =~ /PAYMENT|DEFERRED|MANUAL|PREAUTH|AUTHENTICATE|AUTHORISE/i) { - if ($result{Status} =~ /OK$|REGISTERED/i) { - $result{MStatus} = $result{'pop.status'} = 'success'; - $result{'order-id'} ||= $opt->{order_id}; - - } - - elsif ($result{Status} !~ /OK$|REGISTERED/i) { - $result{MStatus} = $result{'pop.status'} = 'failed'; - if ($result{StatusDetail} =~ /AVS/i) { - $result{MErrMsg} = "Data mismatch
Please ensure that your address matches the address on your credit-card statement, and that the card security number matches the code on the back of your card\n"; - } - else { - $result{MErrMsg} = "$result{StatusDetail} $result{Status}"; + if ($result{Status} =~ /OK$|REGISTERED/i) { + $result{MStatus} = $result{'pop.status'} = 'success'; + $result{'order-id'} ||= $opt->{order_id}; + } + elsif ($result{Status} !~ /OK$|REGISTERED/i) { + $result{MStatus} = $result{'pop.status'} = 'failed'; + if ($result{StatusDetail} =~ /AVS/i) { + $result{MErrMsg} = "Data mismatch
Please ensure that your address matches the address on your credit-card statement, and that the card security number matches the code on the back of your card\n"; + } + else { + $result{MErrMsg} = "$result{StatusDetail} $result{Status}"; + } + } + elsif (!$result{Status}) { + # If the status response is read as empty, the customer will try to repeat the order. This next + # will force the transaction to success in this case, thus preventing any attempt to repeat the + # order, but flag it so that the merchant knows to manually check with Protx to see if they have + # a valid transaction. There are different views on this approach - use with care. + if ($logempty eq 'yes') { + $result{MStatus} = $result{'pop.status'} = 'success'; + $result{'order-id'} ||= $opt->{order_id}; + $result{TxType} = 'NULL'; + $result{StatusDetail} = 'UNKNOWN status - check with Protx before dispatching goods'; + } + } + else { + $result{MStatus} = $result{'pop.status'} = 'failed'; + $result{MErrMsg} = "$result{StatusDetail}"; + } } - } - elsif (!$result{Status}) { - # If the status response is read as empty, the customer will try to repeat the order. This next - # will force the transaction to success in this case, thus preventing any attempt to repeat the - # order, but flag it so that the merchant knows to manually check with Protx to see if they have - # a valid transaction. There are different views on this approach - use with care. - if ($logempty eq 'yes') { - $result{MStatus} = $result{'pop.status'} = 'success'; - $result{'order-id'} ||= $opt->{order_id}; - $result{'TxType'} = 'NULL'; - $result{'StatusDetail'} = 'UNKNOWN status - check with Protx before dispatching goods'; - } - } - - else { - $result{MStatus} = $result{'pop.status'} = 'failed'; - $result{MErrMsg} = "$result{StatusDetail}"; - } - } - -# these next can only be done through a virtual terminal -elsif($txtype =~ /RELEASE|REPEAT|REFUND|DIRECTREFUND|VOID|ABORT/i ) { - if ($result{Status} =~ /OK|REGISTERED/i) { - $result{MStatus} = $result{'pop.status'} = 'success'; - $result{'order-id'} ||= $opt->{order_id}; - } - else { - $result{MStatus} = $result{'pop.status'} = 'failed'; - $result{MErrMsg} = "$result{Status} $result{StatusDetail}"; - } - } - } -elsif ($request eq 'offline') { -# end of PSP request, now for either OFFLINE or LOG - # force result to 'success' so that transaction completes off-line - $result{MStatus} = 'success'; - $result{'order-id'} ||= $opt->{order_id}; - $result{'TxType'} = 'OFFLINE'; - } -elsif ($request eq 'log') { - # force result to 'success' so that transaction completes off-line - $result{MStatus} = 'success'; - $result{'order-id'} ||= $opt->{order_id}; - $result{'TxType'} = 'LOG'; - } - -# if payment is confirmed as OK by Protx, log this for checking above -if ($result{Status} =~ /OK|REGISTERED/i) { -`touch $marker`; - } + # these next can only be done through a virtual terminal + elsif($txtype =~ /RELEASE|REPEAT|REFUND|DIRECTREFUND|VOID|ABORT/i ) { + if ($result{Status} =~ /OK|REGISTERED/i) { + $result{MStatus} = $result{'pop.status'} = 'success'; + $result{'order-id'} ||= $opt->{order_id}; + } + else { + $result{MStatus} = $result{'pop.status'} = 'failed'; + $result{MErrMsg} = "$result{Status} $result{StatusDetail}"; + } + } + } + elsif ($request eq 'offline') { + # end of PSP request, now for either OFFLINE or LOG + # force result to 'success' so that transaction completes off-line + $result{MStatus} = 'success'; + $result{'order-id'} ||= $opt->{order_id}; + $result{TxType} = 'OFFLINE'; + } + elsif ($request eq 'log') { + # force result to 'success' so that transaction completes off-line + $result{MStatus} = 'success'; + $result{'order-id'} ||= $opt->{order_id}; + $result{TxType} = 'LOG'; + } - } # close double payment marker + # if payment is confirmed as OK by Protx, log this for checking above +# if ($result{Status} =~ /OK|REGISTERED/i) { + if (open my $touch, '>>', $marker) { + close $touch; + } + else { + ::logError("%s: error updating timestamp of %s: %s", __PACKAGE__, $marker, $!); + } +# } - return (%result); + } # close double payment marker + + return %result; } package Vend::Payment::Protx2; 1; -