Provided by: interchange_5.7.7-2_amd64 bug

Interchange Sagepay Support

       Vend::Payment::SagePay $Revision: 0.8.7 $ is the home page with the latest version.

This package is for the 'SagePay Direct' payment system.

       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.

Quick Start Summary

       1 Place this module in <IC_root>/lib/Vend/Payment/

       2 Call it in interchange.cfg with:
           Require module Vend::Payment::SagePay

       3 Add into variable.txt (tab separated):
           MV_PAYMENT_MODE   sagepay
         Add a new route into catalog.cfg (options for the last entry in parentheses):
           Route sagepay id YourSagePayID
           Route sagepay host (
           Route sagepay currency GBP (USD, EUR, others, defaults to GBP)
           Route sagepay txtype PAYMENT (AUTHENTICATE, DEFERRED)
           Route sagepay available yes (no, empty)
           Route sagepay logzero yes (no, empty)
           Route sagepay logorder yes (no, empty)
           Route sagepay logsagepay yes (no, empty)
           Route sagepay applyavscv2 '0': if enabled then check, and if rules apply use.
                           '1': force checks even if not enabled; if rules apply use.
                           '2': force NO checks even if enabled on account.
                           '3': force checks even if not enabled; do NOT apply rules.
           Route sagepay giftaidpayment 0 (1 to donate tax to Gift Aid)

       4 Create a new locale setting for en_GB as noted in "item currency" below, and copy the
       public space interchange/en_US/ directory to a new interchange/en_GB/ 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 GB 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', 'mv_credit_card_start_year', 'mv_credit_card_type' and

       6 The new fields in API 2.23 are: BillingAddress, BillingPostCode, DeliveryAddress,
       DeliveryPostCode, BillingFirstnames, BillingSurname, DeliveryFirstnames, DeliverySurname,
       ContactNumber,ContactFax,CustomerEmail.  CustomerName has been removed. Billing and
       Delivery State must be sent if the destination country is the US, otherwise they are not
       required. State must be only 2 letters if sent. Other fields may default to a space if
       there is no proper value to send, though this may conflict with your AVS checking rules.
       SagePay currently accept a space as of time of writing - if they change this without
       changing the API version then send either a series of '0' or '-' characters to stop their
       error messages.

       7. Add a page in pages/ord/, tdsfinal.html, being a minimal page with only the header and
       side bar, and in the middle of the page put: [if scratch acsurl]        <tr>           <td
       align=center height=600 valign=middle colspan=2>             <iframe
       src="__CGI_URL__/ord/tdsauth.html" frameborder=0 width=600 height=600></iframe>
                 </td>        </tr> [/if]

       Add a page in pages/ord/, tdsauth.html, consisting of this: <body
       onload="document.form.submit();"> <FORM name="form" action="[scratchd acsurl]"
       method="POST" /> <input type="hidden" name="PaReq" value="[scratch pareq]" /> <input
       type="hidden" name="TermUrl" value="[scratch termurl]" /> <input type="hidden" name="MD"
       value="[scratch md]" /> </form> </body> along with whatever <noscript> equivalent you
       want. This will retrieve the bank's page within the iframe.

       Add a page in pages/ord/, tdsreturn.html, consisting of this:      [charge route="sagepay"
       sagepayrequest="3dsreturn"]      <p>         <blockquote>
               <font color="__CONTRAST__">
                       [error all=1 keep=1 show_error=1 show_label=1 joiner="<br>"]

       The iframe in 'tdsfinal' will be populated with the contents of 'tdsauth', and the
       javascript will automatically display the bank's authentication page. When the customer
       clicks 'Submit' at the bank's page, the iframe contents will be replaced with the
       'tdsreturn' page, which will complete the route and display the receipt inside the iframe.
       If the customer clicks 'cancel' at the bank, then this 'tdsreturn' page will stay put and
       display whatever message you have put there along with the error message.  The value of
       [scratch tds] is set true for a 3DSecure transaction only, so can be used for messages etc
       on the receipt page.

       8. When running a card through 3DSecure, the route is run twice: firstly to Sagepay who
       check whether or not the card is part of 3DSecure - if it is they send the customer to the
       bank's authentication page and upon returning from that the route must be run a second
       time to send the authentication results to Sagepay. The second run is initiated from the
       'ord/tdsreturn' page, not from etc/log_transaction as it normally would be. To handle this
       change to the normal system flow you need to alter log_transaction to make the call to the
       payment module conditional,ie, 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] If the
       first call to Sagepay returns a request to send the customer to the 3DSecure server, then
       IC will write a payment route error to the error log prior to sending the customer there.
       This error stops the route completing and lets the 3DSecure process proceed as it should.
       This error is not raised if the card is not part of 3DSecure, and instead the route
       completes as it normally would.

       Also add this line just after '&final = yes' near the end of the credit_card section of
       etc/profiles.order:      &set=mv_payment_route sagepay

       9. Add these new fields into log_transaction, to record the values returned from Sagepay
       (these will be key in identifying transactions and problems in any dispute with them):

       mv_credit_card_type: [calc]$Session->{payment_result}{CardType}[/calc]
       mv_credit_card_issue_number: [value mv_credit_card_issue_number] txtype:
       [calc]$Session->{payment_result}{TxType};[/calc] vpstxid:
       [calc]$Session->{payment_result}{VPSTxID};[/calc] txauthno:
       [calc]$Session->{payment_result}{TxAuthNo};[/calc] securitykey:
       [calc]$Session->{payment_result}{SecurityKey};[/calc] vendortxcode:
       [calc]$Session->{payment_result}{VendorTxCode};[/calc] avscv2:
       addressresult:[calc]$Session->{payment_result}{AddressResult};[/calc] postcoderesult:
       [calc]$Session->{payment_result}{PostCodeResult};[/calc] cv2result:
       securestatus:[calc]$Session->{payment_result}{SecureStatus};[/calc] pares:
       [calc]$Session->{payment_result}{PaRes};[/calc] md:
       [calc]$Session->{payment_result}{MD};[/calc] cavv:
       [calc]$Session->{payment_result}{CAVV};[/calc] and add these into your MySQL or Postgres
       transactions table, as type varchar(128) except for 'pares' which should be type 'text'.

       Note that there is no 'TxAuthNo' returned for a successful AUTHENTICATE.


         LWP::UserAgent and Crypt::SSLeay

         wget - a recent version built with SSL and supporting the 'connect' timeout function.


       The Vend::Payment::SagePay module implements the SagePay() routine for use with
       Interchange. It is _not_ compatible on a call level with the other Interchange payment
       modules - SagePay does things rather differently.

       To enable this module, place this directive in "interchange.cfg":

           Require module Vend::Payment::SagePay

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

       Make sure CreditCardAuto is off (default in Interchange demos).

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 a page,
       then from an Interchange order Route named for the mode in catalog.cfg, then a default
       global payment variable in products/variable.txt, and finally in some cases a default will
       be hard-coded into the module.

           The mode can be named anything, but the "gateway" parameter must be set to "sagepay".
           To make it the default payment gateway for all credit card transactions in a specific
           catalog, you can set in "catalog.cfg":

               Variable   MV_PAYMENT_MODE  sagepay
           or in variable.txt:
               MV_PAYMENT_MODE sagepay (tab separated)

           if you want this to cooperate with other payment systems, eg PaypalExpress, then see
           the documentation that comes with that system - it should be fully explained there.

       id  Your SagePay vendor ID, supplied by SagePay when you sign up. Various ways to state
           this: in variable.txt:
               MV_PAYMENT_ID   YourSagePayID Payment or in catalog.cfg either of:
               Route sagepay id YourSagePayID
               Variable MV_PAYMENT_ID      YourSagePayID or on the page
               [charge route=sagepay id=YourSagePayID]

           The transaction type is one of: PAYMENT, AUTHENTICATE, DEFERRED for an initial
           purchase through the catalogue, and then can be one of: AUTHORISE, REFUND, RELEASE,
           VOID, ABORT for payment operations through the virtual terminal.

           The transaction type is taken firstly from a dynamic variable in the page, meant
           primarily for use with the 'virtual payment terminal', viz: 'transtype' in a select
           box though this could usefully be taken from a possible entry in the products database
           if you have different products to be sold on different terms; then falling back to a
           'Route txtype PAYMENT' entry in catalog.cfg; then falling back to a global variable in
           variable.txt, eg 'MV_PAYMENT_TXTYPE PAYMENT Payment'; and finally defaulting to
           'PAYMENT' hard-coded into the module. This variable is returned to the module and
           logged using the value returned from SagePay, rather than a value from the page which
           possibly may not exist.

           If 'yes', then the module will check that the gateway is responding before sending the
           transaction.  If it fails to respond within 9 seconds, then the module will go 'off
           line' and log the transaction as though this module had not been called. It will also
           log the txtype as 'OFFLINE' so that you know you have to put the transaction through
           manually later (you will need to capture the card number to do this). The point of
           this is that your customer has the transaction done and dusted, rather than being told
           to 'try again later' and leaving for ever. If not explicitly 'yes', defaults to 'no'.
           NB: if you set this to 'yes', then add into the etc/report that is sent to you: Txtype
           = [calc]$Session->{payment_result}{TxType};[/calc]. Note that you need to have a
           recent version of wget which supports '--connect-timeout' to run this check. Note also
           that, as this transaction has not been logged anywhere on the SagePay server, you
           cannot use their terminal to process it. You must use a virtual terminal which
           includes a function for this purpose, and updates the existing order number with the
           new payment information returned from SagePay. Note further that if you have SagePay
           set up to require the CV2 value, then virtual terminal should disable 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).

           If 'yes', then the module will log a transaction even if the amount sent is zero
           (which the gateway would normally reject). The point of this is to allow a zero amount
           in the middle of a subscription billing series for audit purposes. If not explicitly
           'yes', defaults to 'no'.  Note: this is only useful if you are using an invoicing
           system or the Payment Terminal, both of which 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.

       logempty If 'yes, then if the response from SagePay is read as empty (ie, zero bytes) then
       the module will use the VendorTxID to check on the Sagepay txstatus page to see if that
       transaction has been logged. If it has then the result found on that page will be used to
       push the result to either success or failure and log accordingly. 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 SagePay
       before dispatching goods' and you should include these into the report emailed to you. It
       will also call a logorder Usertag to log a backup of the order: if you don't already have
       this then get it from
           If the result is not found on that txstatus page then the result is forced to
           'failure' and the transaction shown as failed to the customer.

           SagePay requires that the card type be sent. Valid types are: VISA, MC, AMEX, DELTA,
           SOLO, MAESTRO, UKE, JCB, DINERS (UKE is Visa Electron issued in the UK).

           You may display a select box on the checkout page like so:

                         <select name=mv_credit_card_type>
                     <option value="[loop-code]"> [loop-param label]

           SagePay requires that a currency code be sent, using the 3 letter ISO currency code
           standard, eg, GBP, EUR, USD. The value is taken firstly from either a page setting or
           a possible value in the products database, viz 'iso_currency_code'; then falling back
           to the locale setting - for this you need to add to locale.txt:

               code    en_GB   en_EUR  en_US
               iso_currency_code   GBP EUR USD

           It then falls back to a 'Route sagepay currency EUR' type entry in catalog.cfg; then
           falls back to a global variable (eg MV_PAYMENT_CURRENCY EUR Payment); and finally
           defaults to GBP hard-coded into the module. This variable is returned to the module
           and logged using the value returned from SagePay, rather than a value from the page
           which possibly may not exist.

           This is sent to SagePay as mv_credit_card_cvv2. Put this on the checkout page:

               <b>CVV2: &nbsp; <input type=text name=mv_credit_card_cvv2 size=6></b>

           but note that under Card rules you must not log this value anywhere.

           This is used for some debit cards, and taken from an input box on the checkout page:

               Card issue number: <input type=text name=mv_credit_card_issue_number value='' size=6>

           This is used for some debit cards, and is taken from select boxes on the 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:

                             <select name=mv_credit_card_start_year>
                             [loop option=start_date_year lr=1 list=`
                             my $year = $Tag->time( '', { format => '%Y' }, '%Y' );
                             my $out = '';
                             for ($year - 7 .. $year) {
                                             $last_two = $1;
                                             $out .= "$last_two\t$_\n";
                             return $out;
                             <option value="[loop-code]"> [loop-pos 1]

           Make the select box for the start month a copy of the existing one for the expiry
           month, but with the label changed and the addition of = --select --, as the first
           entry. This intentionally returns nothing for that selection and prevents the
           StartDate being sent.

       SagePay API v2.23 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. They also have Paypal integrated into this version, but I have no interest
       in implementing Paypal through Sagepay. There is a separate PaypalExpress module for that.
           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'

           ClientIPAddress: will show in SagePay reports, and they will attempt to Geo-locate the

       AVSCV2 SagePay do not use your rulebase or return any checks for these when using 3ds and
       AUTHORISE. As this data is essential for many business models you should use DEFERRED
       instead. While thought was given to running a PAYMENT and VOID for AX1 first, simply to
       get the AVSCV2 results, this cannot be done with Maestro cards which legally must go
       through 3ds and so I have abandoned the idea.
       Encrypted email with card info
           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:

           Variable<tab>MV_CREDIT_CARD_INFO_TEMPLATE Card type: {MV_CREDIT_CARD_TYPE}; Card no:
           Issue no: {MV_CREDIT_CARD_ISSUE_NUMBER}; StartDate:

           The SagePay test site is, and their live site is
           Enable one of these in MV_PAYMENT_HOST in variable.txt (*without* any leading
           https://) or as 'Route sagepay host' in catalog.cfg. Be aware that
           the test site is not an exact replica of the live site, and errors there can be
           misleading. In particular the "SecureStatus" returned from the test site is liable to
           be 'NOTAUTHED' when the live site will return 'OK'.

           An AUTHENTICATE will check that the card is not stolen and contains sufficient funds.
           SagePay will keep the details, so that you may settle against this a month or more
           later. Against an AUTHENTICATE you may do an AUTHORISE (which settles the

           A DEFERRED will place a shadow ('block') on the funds for seven days (or so, depending
           on the acquiring bank). Against a DEFERRED you may do a RELEASE to settle the

           A PAYMENT will take the funds immediately. Against a PAYMENT, you may do a REFUND or

           A REPEAT may be performed against an AUTHORISE or a PAYMENT. This will re-check and
           take the funds in real time. You may then REPEAT a REPEAT, eg for regular
           subscriptions. As you need to send the amount and currency with each REPEAT, you may
           vary the amount of the REPEAT to suit a variation in subscription fees.

           A RELEASE is performed to settle a DEFERRED. Payment of the originally specified
           amount is guaranteed if the RELEASE is performed within the seven days for which the
           card-holder's funds are 'blocked'.

           A REFUND may be performed against a PAYMENT, RELEASE, or REPEAT. It may be for a
           partial amount or the entire amount, and may be repeated with several partial REFUNDs
           so long as the total does not exceed the original amount.

           A DIRECTREFUND sends funds from your registered bank account to the nominated credit
           card.  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.

       Try a sale with  any other test number given by SagePay, eg:      Visa VISA 4929 0000 0000
           Mastercard  MC 5404 0000 0000 0001
           Delta DELTA 4462 0000 0000 0000 0003
           Visa Electron UK Debit    UKE       4917300000000008
           Solo SOLO 6334 9000 0000 0000 0005 issue no 1
           Switch (UK Maestro) MAESTRO 5641 8200 0000 0005 issue no 01.
           Maestro MAESTRO 300000000000000004      AmericanExpress AMEX     3742 0000 0000 004

       You need these following values to ensure a positive response: CV2: 123 Billing Address:
       88 Billing PostCode: 412 and the password at their test server is 'password'.

       If nothing works:

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

               Require module Vend::Payment::SagePay

       ·   Make sure either Net::SSLeay or Crypt::SSLeay and LWP::UserAgent are installed and
           working. You can test to see whether your Perl thinks they are:

               perl -MNet::SSLeay -e 'print "It works\n"'
               perl -MLWP::UserAgent -MCrypt::SSLeay -e 'print "It works\n"'

           If either one prints "It works." and returns to the prompt you should be OK (presuming
           they are in working order otherwise).

       ·   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:

                   my $string = $Tag->uneval( { ref => $Session->{payment_result} });
                   $string =~ s/{/{\n/;
                   $string =~ s/,/,\n/g;
                   return $string;

           That should show what happened.

       ·   If you have a PGP/GPG failure when placing an order through your catalogue then this
           may cause the module to be immediately re-run. As the first run would have been
           successful, meaning that both the basket and the credit card information would have
           been emptied, the second run will fail. The likely error message within the catalogue
           will be: "Can't figure out credit card expiration". Fixing PGP/GPG will fix this

           If you get the same error message within the Virtual Terminal, then you haven't set
           the order route as noted above.

       ·   If all else fails, Zolotek and other consultants are available to help with
           integration for a fee.


       Lyn St George <>, based on original code by Mike Heins <>
       and others.

   CREDITS Hillary Corney (, Jamie Neil (, Andy Mayer
       ( for testing and suggestions.