X-Cart:Protx/Sage Pay API update

From X-Cart 4 Classic
Revision as of 18:23, 2 February 2010 by Admin (talk | contribs) (=func.cc_protx_common.php)
Jump to: navigation, search

Protx changed its name to Sage Pay this spring. However, this change affects not just the name and the logo. There are slight changes in the products as well.

A Sage Pay rep said:

On the integration side of things there is a requirement to change the URL end points from Protx.com to Sagepay.com: http://protx.gtml1.com/Protxlz/Instances/Protxlz/documents/URLs.pdf
But it is not an urgent change as both old and new URL end points will be active for the next 6 months.

But it is not the only change. Protx/Sagepay updated their validation rules:

In order to continue to deliver high levels of security we recently performed an in-depth scan of vendors who are passing invalid characters to the Sage Pay systems. We noticed that you are one of these vendors and strongly advise you to update your integration as soon as possible. Accepting invalid characters is not considered best practice and we are therefore updating our systems accordingly.

How this affects you:

We are implementing an update to the Sage Pay integration protocol on Friday the 29th of May 2009.

This update increases the stringency of the character validation checks performed on incoming transactions to our systems, and is designed to ensure that transactions which include invalid characters are rejected.

This means that as of the 29th of May 2009 any transactions passed to Sage Pay that include invalid characters will be rejected. Your business will therefore be unable to process transactions after this date if no action is taken.

The SagePay/Protx integration was updated in X-Cart v.4.2.2 and higher. So if you use this or newer version of X-Cart, you don't need to modify anything.

If you use the older version of X-Cart, you need to update your store and apply the attached patches.

For v4.2.1 and v4.2.0:

  • upload the attached func.cc_protx_common.php file to the include/func directory of your store
  • if you use Protx Direct, apply the protx_direct_42x.txt patch. If you use Protx Form, apply the protx_form_42x.txt one

For 4.1.x:

  • upload the attached func.cc_protx_common.php file to the include/func directory of your store
  • if you use Protx Direct, apply the protx_direct_41x.txt patch. If you use Protx Form, apply the protx_form_41x.txt one

If you use v4.0.x or older, please contact our support team.

Files:

func.cc_protx_common.php

<?php
/*****************************************************************************\
+-----------------------------------------------------------------------------+
| X-Cart                                                                      |
| Copyright (c) 2001-2009 Ruslan R. Fazliev <rrf@rrf.ru>                      |
| All rights reserved.                                                        |
+-----------------------------------------------------------------------------+
| PLEASE READ  THE FULL TEXT OF SOFTWARE LICENSE AGREEMENT IN THE "COPYRIGHT" |
| FILE PROVIDED WITH THIS DISTRIBUTION. THE AGREEMENT TEXT IS ALSO AVAILABLE  |
| AT THE FOLLOWING URL: http://www.x-cart.com/license.php                     |
|                                                                             |
| THIS  AGREEMENT  EXPRESSES  THE  TERMS  AND CONDITIONS ON WHICH YOU MAY USE |
| THIS SOFTWARE   PROGRAM   AND  ASSOCIATED  DOCUMENTATION   THAT  RUSLAN  R. |
| FAZLIEV (hereinafter  referred to as "THE AUTHOR") IS FURNISHING  OR MAKING |
| AVAILABLE TO YOU WITH  THIS  AGREEMENT  (COLLECTIVELY,  THE  "SOFTWARE").   |
| PLEASE   REVIEW   THE  TERMS  AND   CONDITIONS  OF  THIS  LICENSE AGREEMENT |
| CAREFULLY   BEFORE   INSTALLING   OR  USING  THE  SOFTWARE.  BY INSTALLING, |
| COPYING   OR   OTHERWISE   USING   THE   SOFTWARE,  YOU  AND  YOUR  COMPANY |
| (COLLECTIVELY,  "YOU")  ARE  ACCEPTING  AND AGREEING  TO  THE TERMS OF THIS |
| LICENSE   AGREEMENT.   IF  YOU    ARE  NOT  WILLING   TO  BE  BOUND BY THIS |
| AGREEMENT, DO  NOT INSTALL OR USE THE SOFTWARE.  VARIOUS   COPYRIGHTS   AND |
| OTHER   INTELLECTUAL   PROPERTY   RIGHTS    PROTECT   THE   SOFTWARE.  THIS |
| AGREEMENT IS A LICENSE AGREEMENT THAT GIVES  YOU  LIMITED  RIGHTS   TO  USE |
| THE  SOFTWARE   AND  NOT  AN  AGREEMENT  FOR SALE OR FOR  TRANSFER OF TITLE.|
| THE AUTHOR RETAINS ALL RIGHTS NOT EXPRESSLY GRANTED BY THIS AGREEMENT.      |
|                                                                             |
| The Initial Developer of the Original Code is Ruslan R. Fazliev             |
| Portions created by Ruslan R. Fazliev are Copyright (C) 2001-2009           |
| Ruslan R. Fazliev. All Rights Reserved.                                     |
+-----------------------------------------------------------------------------+
\*****************************************************************************/

#
# $Id: func.cc_protx_common.php,v 1.2.2.1 2009/06/02 11:21:22 ferz Exp $
#

if ( !defined('XCART_START') ) { header("Location: ../../"); die("Access denied"); }

#
# Common functions used in the Sage Pay payment modules
#
# The functions below are based on the examples from the PHP Integration
# Kits, which were downloaded from the official Sage Pay website www.sagepay.com.
# The original code was adapted to fit the X-Cart architecture.


// Filters unwanted characters out of an input string.  Useful for tidying up FORM field inputs.
function cleanInput($strRawText, $strType, $maxChars=false, $customPattern=false) {

switch ($strType) {
case "Number":
$strClean = "0123456789.";
$bolHighOrder = false;
break;
case "Digits":
$strClean = "0123456789";
$bolHighOrder = false;
break;
case "Text":
$strClean =" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,'/{}@():?-_&ё$=%~<>*+\"";
$bolHighOrder = true;
break;
case "Custom":
$strClean = $customPattern;
$bolHighOrder = false;
break;
default:
break;
}

$strCleanedText="";
$iCharPos = 0;

do
{
// Only include valid characters
$chrThisChar=substr($strRawText,$iCharPos,1);

if (strspn($chrThisChar,$strClean,0,strlen($strClean))>0) {
$strCleanedText=$strCleanedText . $chrThisChar;
}
else if ($bolHighOrder==true) {
// Fix to allow accented characters and most high order bit chars which are harmless
if (bin2hex($chrThisChar)>=191) {
$strCleanedText=$strCleanedText . $chrThisChar;
}
}

$iCharPos=$iCharPos+1;
}
while ($iCharPos<strlen($strRawText));

$cleanInput = ltrim($strCleanedText);

if ($maxChars && strlen($cleanInput) > $maxChars)
$cleanInput = substr($cleanInput, 0, $maxChars);

return $cleanInput;

}

/* Base 64 Encoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/

function base64Encode($plain) {
// Initialise output variable
$output = "";

// Do encoding
$output = base64_encode($plain);

// Return the result
return $output;
}

/* Base 64 decoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/

function base64Decode($scrambled) {
// Initialise output variable
$output = "";

// Fix plus to space conversion issue
$scrambled = str_replace(" ","+",$scrambled);

// Do encoding
$output = base64_decode($scrambled);

// Return the result
return $output;
}

/*  The SimpleXor encryption algorithm                                                                                **
**  NOTE: This is a placeholder really.  Future releases of Form will use AES or TwoFish.  Proper encryption          **
**  This simple function and the Base64 will deter script kiddies and prevent the "View Source" type tampering        **
**  It won't stop a half decent hacker though, but the most they could do is change the amount field to something     **
**  else, so provided the vendor checks the reports and compares amounts, there is no harm done.  It's still          **
**  more secure than the other PSPs who don't both encrypting their forms at all                                      */

function simpleXor($InString, $Key) {

// Initialise key array
$KeyList = array();

// Initialise out variable
$output = "";

// Convert $Key into array of ASCII values
for($i = 0; $i < strlen($Key); $i++){
$KeyList[$i] = ord(substr($Key, $i, 1));
}

// Step through string a character at a time
for($i = 0; $i < strlen($InString); $i++) {
// Get ASCII code from string, get ASCII code from key (loop through with MOD), XOR the two, get the character from the result
// % is MOD (modulus), ^ is XOR
$output.= chr(ord(substr($InString, $i, 1)) ^ ($KeyList[$i % strlen($Key)]));
}

// Return the result
return $output;
}


#
# Common functions to check and tide up the values
#

#
# Function tides up the values in accordance with the fields
# specification
#
function func_sagepay_clean_inputs($data) {
$fields_specs = func_sagepay_get_allowed_fields();

foreach ($fields_specs as $field => $spec) {
if (!isset($data[$field]) || isset($spec["skip"]))
continue;

if (isset($fields_specs[$field]["allowed_values"])) {
if ( !in_array($data[$field], $spec["allowed_values"])) {
func_unset($data, $field);
}
continue;
}
$pattern = ($spec["filter"] == "Custom") ? $spec["pattern"] : false;
$data[$field] = cleanInput($data[$field], $spec["filter"], $spec["max"], $pattern);
}

return $data;
}


#
# Function returns an array of allowed fields
#  max: max length of the string for Text and Digits filters,
#  filter: filter to be applied in the cleanInput function
#  pattern: pattern for Custom filter
#  skip: skip checking of this input, since it is already perfomed in X-Cart
#
function func_sagepay_get_allowed_fields() {

$fields_specification = array(

"VendorTxCode" => array(
"max" => 40,
"filter" => "Custom",
"pattern" => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_."
),
"Amount" => array(
"skip" => true,
),
"Currency" => array(
"skip" => true
),
"Description" => array(
"max" => 100,
"filter" => "Text"
),
"SuccessURL" => array(
"max" => 2000,
"filter" => "Text"
),
"FailureURL" => array(
"max" => 2000,
"filter" => "Text"
),
"CustomerName" => array(
"max" => 100,
"filter" => "Text"
),
"CustomerEMail" => array(
"max" => 255,
"filter" => "Text"
),
"VendorEMail" => array(
"max" => 255,
"filter" => "Text"
),
"SendEMail" => array(
"allowed_values" => array(0,1,2,3)
),
"eMailMessage" => array(
"max" => 7500,
"filter" => "Text"
),
"BillingSurname" => array(
"max" => 20,
"filter" => "Text"
),
"BillingFirstnames" => array(
"max" => 20,
"filter" => "Text"
),
"BillingAddress1" => array(
"max" => 100,
"filter" => "Text"
),
"BillingAddress2" => array(
"max" => 100,
"filter" => "Text"
),
"BillingCity" => array(
"max" => 40,
"filter" => "Text"
),
"BillingPostCode" => array(
"max" => 10,
"filter" => "Text"
),
"BillingCountry" => array(
"skip" => true
),
"BillingState"=> array(
"skip" => true
),
"BillingPhone" => array(
"max" => 20,
"filter" => "Text"
),
"DeliverySurname" => array(
"max" => 20,
"filter" => "Text"
),
"DeliveryFirstnames" => array(
"max" => 20,
"filter" => "Text"
),
"DeliveryAddress1" => array(
"max" => 100,
"filter" => "Text"
),
"DeliveryAddress2" => array(
"max" => 100,
"filter" => "Text"
),
"DeliveryCity" => array(
"max" => 40,
"filter" => "Text"
),
"DeliveryPostCode" => array(
"max" => 10,
"filter" => "Text"
),
"DeliveryCountry" => array(
"skip" => true
),
"DeliveryState" => array(
"skip" => true
),
"DeliveryPhone" => array(
"max" => 20,
"filter" => "Text"
),
"Basket" => array(
"max" => 7500,
"filter" => "Text"
),
"AllowGiftAid" => array(
"allowed_values" => array("0","1")
),
"ApplyAVSCV2" => array(
"allowed_values" => array("0","1","2","3")
),
"Apply3DSecure" => array(
"allowed_values" => array("0","1","2","3")
),
"TxType" => array(
"allowed_values" => array("PAYMENT","DEFERRED","AUTHENTICATE","RELEASE","AUTHORISE","CANCEL","ABORT","MANUAL","REFUND","REPEAT",
"REPEATDEFERRED","VOID","PREAUTH","COMPLETE")
),
"NotificationURL" => array(
"max" => 255,
"filter" => "Text"
),
"Vendor" => array(
"max" => 15,
"filter" => "Text"
),
"Profile" => array(
"allowed_values" => array("LOW","NORMAL")
),
"CardHolder" => array(
"max" => 50,
"filter" => "Text"
),
"CardNumber" => array(
"max" => 20,
"filter" => "Digits"
),
"StartDate" => array(
"max" => 4,
"filter" => "Digits"
),
"ExpiryDate" => array(
"max" => 4,
"filter" => "Digits"
),
"IssueNumber" => array(
"max" => 2,
"filter" => "Digits"
),
"CV2" => array(
"max" => 4,
"filter" => "Digits"
),
"CardType" => array(
"allowed_values" => array("VISA","MC","DELTA","SOLO","MAESTRO","UKE","AMEX","DC","JCB","LASER","PAYPAL")
),
"PayPalCallbackURL" => array(
"max" => 255,
"filter" => "Text"
),
"GiftAidPayment" => array(
"allowed_values" => array("0","1")
),
"ClientIPAddress" => array(
"max" => 15,
"filter" => "Text"
),
"MD" => array(
"max" => 35,
"Text"
),
"PARes" => array(
"max" => 7500,
"filter" => "Text"
),
"VPSTxID" => array(
"max" => 38,
"filter" => "Text"
),
"Accept" => array(
"allowed_values" => array("Yes","No")
),
"Crypt" => array(
"max" => 16384,
"filter" => "Text"
),
"AccountType" => array(
"allowed_values" => array("E","M","C")
)
);

return $fields_specification;
}

#
# Format cart information for Protx payment methods.
#
function func_cc_protx_get_basket_new() {
global $cart, $config;

$cnt = 0;
$basket = '';

# Products
if (isset($cart['products']) && is_array($cart['products'])) {
$cnt += count($cart['products']);
foreach($cart['products'] as $product) {
$basket .= ':'.str_replace(':', ' ', $product['product']).':'.$product['amount'].':---:---:---:'.price_format($product['display_price'] * $product['amount']);
}
}

# Gift Certificates
if (isset($cart['giftcerts']) && is_array($cart['giftcerts'])) {
$cnt += count($cart['giftcerts']);
foreach ($cart['giftcerts'] as $tmp_gc) {
$basket .= ':GIFT CERTIFICATE:---:---:---:---:'.price_format($tmp_gc['amount']);
}
}

# Discounts
if ($cart['display_discounted_subtotal'] - $cart['display_subtotal'] != 0) {
$cnt++;
$basket .= ':Discount:---:---:---:---:'.price_format($cart['display_discounted_subtotal'] - $cart['display_subtotal']);
}

# Shipping
if ($cart['shipping_cost'] > 0) {
$cnt++;
$basket .= ':Shipping cost:---:---:---:---:'.price_format($cart['display_shipping_cost']);
}

# Taxes
if ($cart['tax_cost'] != 0 && $config['Taxes']['display_taxed_order_totals'] != 'Y') {
$cnt++;
$basket .= ':Tax:---:---:---:---:'.price_format($cart['tax_cost']);
}

# Payment Surcharge
if (isset($cart['payment_surcharge']) && $cart['payment_surcharge'] != 0) {
$cnt++;
$basket .= ':Payment Handling Fee:---:---:---:---:'.price_format($cart['payment_surcharge']);
}

# Applied Gift Certificates
if (isset($cart['giftcert_discount']) && $cart['giftcert_discount'] != 0) {
$cnt++;
$basket .= ':Applied Gift Certificates Discount:---:---:---:---:'.price_format($cart['giftcert_discount']*-1);
}

$basket = (string)$cnt . $basket;
$basket = preg_replace("/[&+]/", " ", $basket);

return $basket;
}

?>