%PDF- %PDF-
| Direktori : /home1/lightco1/www/lightingrepublic.com.au/plugins/vmpayment/payzen/payzen/ |
| Current File : //home1/lightco1/www/lightingrepublic.com.au/plugins/vmpayment/payzen/payzen/payzen_api.php |
<?php
#####################################################################################################
#
# Module pour la plateforme de paiement PayZen
# Version : 1.3c (révision 46701)
# ########################
# Développé pour VirtueMart
# Version : 2.0.8
# Compatibilité plateforme : V2
# ########################
# Développé par Lyra Network
# http://www.lyra-network.com/
# 29/04/2013
# Contact : support@payzen.eu
#
#####################################################################################################
/**
* @package payzen
* @author Alain Dubrulle <supportvad@lyra-network.com>
* @copyright www.lyra-network.com
* PHP classes to integrate an e-commerce solution with the payment platform supported by lyra-network.
*/
if(!@class_exists('PayzenApi', false)) {
/**
* Class managing parameters checking, form and signature building, response analysis and more
* @version 2.2
*/
class PayzenApi {
// **************************************
// PROPERTIES
// **************************************
/**
* The fields to send to the PayZen platform
* @var array[string]PayzenField
* @access private
*/
var $requestParameters;
/**
* Certificate to send in TEST mode
* @var string
* @access private
*/
var $keyTest;
/**
* Certificate to send in PRODUCTION mode
* @var string
* @access private
*/
var $keyProd;
/**
* Url of the payment page
* @var string
* @access private
*/
var $platformUrl;
/**
* Set to true to send the redirect_* parameters
* @var boolean
* @access private
*/
var $redirectEnabled;
/**
* SHA-1 authentication signature
* @var string
* @access private
*/
var $signature;
/**
* Raw response sent by the platform
* @var PayzenResponse
* @access private
* @deprecated see PayzenResponse constructor
*/
var $response;
/**
* The original data encoding.
* @var string
* @access private
*/
var $encoding;
/**
* The list of categories for payment with bank accord. To be sent with the products detail if you use this payment mean.
* @static
* @var array
* @access public
*/
var $ACCORD_CATEGORIES = array("FOOD_AND_GROCERY","AUTOMOTIVE","ENTERTAINMENT","HOME_AND_GARDEN","HOME_APPLIANCE","AUCTION_AND_GROUP_BUYING","FLOWERS_AND_GIFTS","COMPUTER_AND_SOFTWARE","HEALTH_AND_BEAUTY","SERVICE_FOR_INDIVIDUAL","SERVICE_FOR_BUSINESS","SPORTS","CLOTHING_AND_ACCESSORIES","TRAVEL","HOME_AUDIO_PHOTO_VIDEO","TELEPHONY");
/**
* The list of encodings supported by this API.
* @static
* @var array
* @access public
*/
var $SUPPORTED_ENCODINGS = array("UTF-8", "ASCII", "Windows-1252", "ISO-8859-15", "ISO-8859-1", "ISO-8859-6", "CP1256");
// **************************************
// CONSTRUCTOR
// **************************************
/**
* Constructor.
* Initialize request fields definitions.
*/
function PayzenApi($encoding="UTF-8") {
//TODO success n'est pas utilisé
$success = true;
// Initialize encoding
$this->encoding = in_array(strtoupper($encoding), $this->SUPPORTED_ENCODINGS) ? strtoupper($encoding) : "UTF-8";
/*
* Définition des paramètres de la requête
*/
// Common or long regexes
$ans = "[^<>]"; // Any character (except the dreadful "<" and ">")
$an63 = '#^[A-Za-z0-9]{0,63}$#';
$an255 = '#^[A-Za-z0-9]{0,255}$#';
$ans255 = '#^' . $ans . '{0,255}$#';
$ans127 = '#^' . $ans . '{0,127}$#';
$supzero = '[1-9]\d*';
$regex_payment_cfg = '#^(SINGLE|MULTI:first=\d+;count=' . $supzero
. ';period=' . $supzero . ')$#';
$regex_trans_date = '#^\d{4}' . '(1[0-2]|0[1-9])'
. '(3[01]|[1-2]\d|0[1-9])' . '(2[0-3]|[0-1]\d)' . '([0-5]\d){2}$#';//AAAAMMJJhhmmss
$regex_mail = '#^[^@]+@[^@]+\.\w{2,4}$#'; //TODO plus restrictif
$regex_params = '#^([^&=]+=[^&=]*)?(&[^&=]+=[^&=]*)*$#'; //name1=value1&name2=value2...
// Déclaration des paramètres, de leur valeurs par défaut, de leur format...
// $this->_addRequestField('raw_signature', 'DEBUG Signature', '#^.+$#', false);
$this->_addRequestField('signature', 'Signature', "#^[0-9a-f]{40}$#", true);
$this->_addRequestField('vads_action_mode', 'Action mode',
"#^INTERACTIVE|SILENT$#", true, 11);
$this->_addRequestField('vads_amount', 'Amount', '#^' . $supzero . '$#',
true);
$this->_addRequestField('vads_available_languages', 'Available languages',
"#^(|[A-Za-z]{2}(;[A-Za-z]{2})*)$#", false, 2);
$this->_addRequestField('vads_capture_delay', 'Capture delay', "#^\d*$#");
$this->_addRequestField('vads_contracts', 'Contracts', $ans255);
$this->_addRequestField('vads_contrib', 'Contribution', $ans255);
$this->_addRequestField('vads_ctx_mode', 'Mode', "#^TEST|PRODUCTION$#",
true);
$this->_addRequestField('vads_currency', 'Currency', "#^\d{3}$#", true, 3);
$this->_addRequestField('vads_cust_antecedents', 'Customer history',
"#^NONE|NO_INCIDENT|INCIDENT$#");
$this->_addRequestField('vads_cust_address', 'Customer address', $ans255);
$this->_addRequestField('vads_cust_country', 'Customer country',
"#^[A-Za-z]{2}$#", false, 2);
$this->_addRequestField('vads_cust_email', 'Customer email', $regex_mail,
false, 127);
$this->_addRequestField('vads_cust_id', 'Customer id',
$an63, false, 63);
$this->_addRequestField('vads_cust_name', 'Customer name',
$ans127, false, 127);
$this->_addRequestField('vads_cust_cell_phone', 'Customer cell phone',
$an63, false, 63);
$this->_addRequestField('vads_cust_phone', 'Customer phone', $an63, false,
63);
$this->_addRequestField('vads_cust_title', 'Customer title', '#^'.$ans.'{0,63}$#', false,
63);
$this->_addRequestField('vads_cust_city', 'Customer city',
'#^' . $ans . '{0,63}$#', false, 63);
$this->_addRequestField('vads_cust_state', 'Customer state/region', '#^'.$ans.'{0,63}$#', false,
63);
$this->_addRequestField('vads_cust_zip', 'Customer zip code', $an63, false,
63);
$this->_addRequestField('vads_language', 'Language', "#^[A-Za-z]{2}$#",
false, 2);
$this->_addRequestField('vads_order_id', 'Order id',
"#^[A-za-z0-9]{0,12}$#", false, 12);
$this->_addRequestField('vads_order_info', 'Order info', $ans255);
$this->_addRequestField('vads_order_info2', 'Order info 2', $ans255);
$this->_addRequestField('vads_order_info3', 'Order info 3', $ans255);
$this->_addRequestField('vads_page_action', 'Page action', "#^PAYMENT$#",
true, 7);
$this->_addRequestField('vads_payment_cards', 'Payment cards',
"#^([A-Za-z0-9\-_]+;)*[A-Za-z0-9\-_]*$#", false, 127);
$this->_addRequestField('vads_payment_config', 'Payment config',
$regex_payment_cfg, true);
$this->_addRequestField('vads_payment_src', 'Payment source', "#^$#", false,
0);
$this->_addRequestField('vads_redirect_error_message',
'Redirection error message', $ans255, false);
$this->_addRequestField('vads_redirect_error_timeout',
'Redirection error timeout', $ans255, false);
$this->_addRequestField('vads_redirect_success_message',
'Redirection success message', $ans255, false);
$this->_addRequestField('vads_redirect_success_timeout',
'Redirection success timeout', $ans255, false);
$this->_addRequestField('vads_return_mode', 'Return mode',
"#^NONE|GET|POST?$#", false, 4);
$this->_addRequestField('vads_return_get_params', 'GET return parameters',
$regex_params, false);
$this->_addRequestField('vads_return_post_params',
'POST return parameters', $regex_params, false);
$this->_addRequestField('vads_ship_to_name', 'Shipping name',
'#^' . $ans . '{0,127}$#', false, 127);
$this->_addRequestField('vads_ship_to_phone_num', 'Shipping phone',
$ans255, false, 63);
$this->_addRequestField('vads_ship_to_street', 'Shipping street', $ans127,
false, 127);
$this->_addRequestField('vads_ship_to_street2', 'Shipping street (2)',
$ans127, false, 127);
$this->_addRequestField('vads_ship_to_state', 'Shipping state', $an63,
false, 63);
$this->_addRequestField('vads_ship_to_country', 'Shipping country',
"#^[A-Za-z]{2}$#", false, 2);
$this->_addRequestField('vads_ship_to_city', 'Shipping city',
'#^' . $ans . '{0,63}$#', false, 63);
$this->_addRequestField('vads_ship_to_zip', 'Shipping zip code', $an63,
false, 63);
$this->_addRequestField('vads_shop_name', 'Shop name', $ans127);
$this->_addRequestField('vads_shop_url', 'Shop url', $ans127);
$this->_addRequestField('vads_site_id', 'Site id', "#^\d{8}$#", true, 8);
$this->_addRequestField('vads_theme_config', 'Theme', $ans255);
$this->_addRequestField('vads_trans_date', 'Transaction date',
$regex_trans_date, true, 14);
$this->_addRequestField('vads_trans_id', 'Transaction id',
"#^[0-8]\d{5}$#", true, 6);
$this->_addRequestField('vads_url_success', 'Success url', $ans127, false,
127);
$this->_addRequestField('vads_url_referral', 'Referral url', $ans127,
false, 127);
$this->_addRequestField('vads_url_refused', 'Refused url', $ans127, false,
127);
$this->_addRequestField('vads_url_cancel', 'Cancel url', $ans127, false,
127);
$this->_addRequestField('vads_url_error', 'Error url', $ans127, false, 127);
$this->_addRequestField('vads_url_return', 'Return url', $ans127, false,
127);
$this->_addRequestField('vads_user_info', 'User info', $ans255);
$this->_addRequestField('vads_validation_mode', 'Validation mode',
"#^[01]?$#", false, 1);
$this->_addRequestField('vads_version', 'Gateway version', "#^V2$#", true,
2);
// Credit Card info
$this->_addRequestField('vads_card_number', 'Card number', "#^\d{13,19}$#");
$this->_addRequestField('vads_cvv', 'Card verification number', "#^\d{3,4}$#");
$this->_addRequestField('vads_expiry_year', 'Year of card expiration', "#^20[0-9]{2}$#");
$this->_addRequestField('vads_expiry_month', 'Month of card expiration', "#^\d[0-2]{1}$#");
// Enable / disable 3D Secure
$this->_addRequestField('vads_threeds_mpi', 'Enable / disable 3D Secure', '#^[0-2]$#', false);
// Declaration of parameters for Oney payment
$this->_addRequestField('vads_cust_first_name', 'Customer first name', $an63, false, 63);
$this->_addRequestField('vads_cust_last_name', 'Customer last name', $an63, false, 63);
$this->_addRequestField('vads_cust_status', 'Customer status (private or company)', "#^PRIVATE|COMPANY$#", false, 7);
$this->_addRequestField('vads_ship_to_first_name', 'Shipping first name', $an63, false, 63);
$this->_addRequestField('vads_ship_to_last_name', 'Shipping last name', $an63, false, 63);
$this->_addRequestField('vads_ship_to_status', 'Shipping status (private or company)', "#^PRIVATE|COMPANY$#", false, 7);
$this->_addRequestField('vads_ship_to_delivery_company_name', 'Name of the delivery company', $ans127, false, 127);
$this->_addRequestField('vads_ship_to_speed', 'Speed of the shipping method', "#^STANDARD|EXPRESS$#", false, 8);
$this->_addRequestField('vads_ship_to_type', 'Type of the shipping method',
"#^RECLAIM_IN_SHOP|RELAY_POINT|RECLAIM_IN_STATION|PACKAGE_DELIVERY_COMPANY|ETICKET$#", false, 24);
$this->_addRequestField('vads_insurance_amount', 'The amount of insurance', '#^' . $supzero . '$#', false, 12);
$this->_addRequestField('vads_tax_amount', 'The amount of tax', '#^' . $supzero . '$#', false, 12);
$this->_addRequestField('vads_shipping_amount', 'The amount of shipping', '#^' . $supzero . '$#', false, 12);
$this->_addRequestField('vads_nb_products', 'Number of products', '#^' . $supzero . '$#', false);
// Set some default parameters
$success &= $this->set('vads_version', 'V2');
$success &= $this->set('vads_page_action', 'PAYMENT');
$success &= $this->set('vads_action_mode', 'INTERACTIVE');
$success &= $this->set('vads_payment_config', 'SINGLE');
$timestamp = time();
$success &= $this->set('vads_trans_id', $this->_generateTransId($timestamp));
$success &= $this->set('vads_trans_date', gmdate('YmdHis', $timestamp));
}
/**
* Generate a trans_id.
* To be independent from shared/persistent counters, we use the number of 1/10seconds since midnight,
* which has the appropriate format (000000-899999) and has great chances to be unique.
* @return string the generated trans_id
* @access private
*/
function _generateTransId($timestamp) {
list($usec, $sec) = explode(" ", microtime()); // microseconds, php4 compatible
$temp = ($timestamp + $usec - strtotime('today 00:00')) * 10;
$temp = sprintf('%06d', $temp);
return $temp;
}
/**
* Shortcut function used in constructor to build requestParameters
* @param string $name
* @param string $label
* @param string $regex
* @param boolean $required
* @param mixed $value
* @return boolean true on success
* @access private
*/
function _addRequestField($name, $label, $regex, $required = false,
$length = 255, $value = null) {
$this->requestParameters[$name] = new PayzenField($name, $label, $regex,
$required, $length);
if($value !== null) {
return $this->set($name, $value);
} else {
return true;
}
}
// **************************************
// INTERNATIONAL FUNCTIONS
// **************************************
/**
* Returns the iso codes of language accepted by the payment page
* @static
* @return array[int]string
*/
function getSupportedLanguages() {
return array('fr', 'de', 'en', 'es', 'zh', 'it', 'ja', 'pt', 'nl');
}
/**
* Return the list of currencies recognized by the PayZen platform
* @static
* @return array[int]PayzenCurrency
*/
function getSupportedCurrencies() {
$currencies = array(
array('ARS', 32, 2), array('AUD', 36, 2), array('KHR', 116, 0), array('CAD', 124, 2), array('CNY', 156, 1), array('HRK', 191, 2), array('CZK', 203, 2), array('DKK', 208, 2), array('EKK', 233, 2), array('HKD', 344, 2), array('HUF', 348, 2), array('ISK', 352, 0), array('IDR', 360, 0), array('JPY', 392, 0), array('KRW', 410, 0), array('LVL', 428, 2), array('LTL', 440, 2), array('MYR', 458, 2), array('MXN', 484, 2), array('NZD', 554, 2), array('NOK', 578, 2), array('PHP', 608, 2), array('RUB', 643, 2), array('SGD', 702, 2), array('ZAR', 710, 2), array('SEK', 752, 2), array('CHF', 756, 2), array('THB', 764, 2), array('GBP', 826, 2), array('USD', 840, 2), array('TWD', 901, 1), array('RON', 946, 2), array('TRY', 949, 2), array('XOF', 952, 0), array('BGN', 975, 2), array('EUR', 978, 2), array('PLN', 985, 2), array('BRL', 986, 2)
);
$payzenCurrencies = array();
foreach($currencies as $currency) {
$payzenCurrencies[] = new PayzenCurrency($currency[0], $currency[1], $currency[2]);
}
return $payzenCurrencies;
}
/**
* Return a currency from its iso 3-letters code
* @static
* @param string $alpha3
* @return PayzenCurrency
*/
function findCurrencyByAlphaCode($alpha3) {
$list = $this->getSupportedCurrencies();
foreach ($list as $currency) {
/** @var PayzenCurrency $currency */
if ($currency->alpha3 == $alpha3) {
return $currency;
}
}
return null;
}
/**
* Returns a currency form its iso numeric code
* @static
* @param int $num
* @return PayzenCurrency
*/
function findCurrencyByNumCode($numeric) {
$list = $this->getSupportedCurrencies();
foreach ($list as $currency) {
/** @var PayzenCurrency $currency */
if ($currency->num == $numeric) {
return $currency;
}
}
return null;
}
/**
* Returns a currency numeric code from its 3-letters code
* @static
* @param string $alpha3
* @return int
*/
function getCurrencyNumCode($alpha3) {
$currency = $this->findCurrencyByAlphaCode($alpha3);
return is_a($currency, 'PayzenCurrency') ? $currency->num : null;
}
// **************************************
// GETTERS/SETTERS
// **************************************
/**
* Shortcut for setting multiple values with one array
* @param array[string]mixed $parameters
* @return boolean true on success
*/
function setFromArray($parameters) {
$ok = true;
foreach ($parameters as $name => $value) {
$ok &= $this->set($name, $value);
}
return $ok;
}
/**
* General getter.
* Retrieve an api variable from its name. Automatically add 'vads_' to the name if necessary.
* Example : <code><?php $siteId = $api->get('site_id'); ?></code>
* @param string $name
* @return mixed null if $name was not recognised
*/
function get($name) {
if (!$name || !is_string($name)) {
return null;
}
// V1/shortcut notation compatibility
$name = (substr($name, 0, 5) != 'vads_') ? 'vads_' . $name : $name;
if ($name == 'vads_key_test') {
return $this->keyTest;
} elseif ($name == 'vads_key_prod') {
return $this->keyProd;
} elseif ($name == 'vads_platform_url') {
return $this->platformUrl;
} elseif ($name == 'vads_redirect_enabled') {
return $this->redirectEnabled;
} elseif (array_key_exists($name, $this->requestParameters)) {
return $this->requestParameters[$name]->getValue();
} else {
return null;
}
}
/**
* General setter.
* Set an api variable with its name and the provided value. Automatically add 'vads_' to the name if necessary.
* Example : <code><?php $api->set('site_id', '12345678'); ?></code>
* @param string $name
* @param mixed $value
* @return boolean true on success
*/
function set($name, $value) {
if (!$name || !is_string($name)) {
return false;
}
// V1/shortcut notation compatibility
$name = (substr($name, 0, 5) != 'vads_') ? 'vads_' . $name : $name;
// Convert the parameters if they are not encoded in utf8
if($this->encoding !== "UTF-8") {
$value = iconv($this->encoding, "UTF-8", $value);
}
// Search appropriate setter
if ($name == 'vads_key_test') {
return $this->setCertificate($value, 'TEST');
} elseif ($name == 'vads_key_prod') {
return $this->setCertificate($value, 'PRODUCTION');
} elseif ($name == 'vads_platform_url') {
return $this->setPlatformUrl($value);
} elseif ($name == 'vads_redirect_enabled') {
return $this->setRedirectEnabled($value);
} elseif (array_key_exists($name, $this->requestParameters)) {
return $this->requestParameters[$name]->setValue($value);
} else {
return false;
}
}
/**
* Set target url of the payment form
* @param string $url
* @return boolean
*/
function setPlatformUrl($url) {
if (!preg_match('#https?://([^/]+/)+#', $url)) {
return false;
}
$this->platformUrl = $url;
return true;
}
/**
* Enable/disable redirect_* parameters
* @param mixed $enabled false, '0', a null or negative integer or 'false' to disable
* @return boolean
*/
function setRedirectEnabled($enabled) {
$this->redirectEnabled = !(!$enabled || $enabled == '0'
|| strtolower($enabled) == 'false');
return true;
}
/**
* Set TEST or PRODUCTION certificate
* @param string $key
* @param string $mode
* @return boolean true if the certificate could be set
*/
function setCertificate($key, $mode) {
// Check format
if (!preg_match('#\d{16}#', $key)) {
return false;
}
if ($mode == 'TEST') {
$this->keyTest = $key;
} elseif ($mode == 'PRODUCTION') {
$this->keyProd = $key;
} else {
return false;
}
return true;
}
/**
* Add product infos as request parameters.
* @param string $label
* @param int $amount
* @param int $qty
* @param string $ref
* @param string $type
* @return boolean true if product infos are set correctly
*/
function addProductRequestField($label, $amount, $qty, $ref, $type) {
$index = $this->get("nb_products") ? $this->get("nb_products") : 0;
$ok = true;
// Add product infos as request parameters
$ok &= $this->_addRequestField("vads_product_label" . $index, "Product label", '#^[^<>"+-]{0,255}$#', false, 255, $label);
$ok &= $this->_addRequestField("vads_product_amount" . $index, "Product amount", '#^[1-9]\d*$#', false, 12, $amount);
$ok &= $this->_addRequestField("vads_product_qty" . $index, "Product quantity", '#^[1-9]\d*$#', false, 255, $qty);
$ok &= $this->_addRequestField("vads_product_ref" . $index, "Product reference", '#^[A-Za-z0-9]{0,64}$#', false, 64, $ref);
$ok &= $this->_addRequestField("vads_product_type" . $index, "Product type", "#^".implode("|", $this->ACCORD_CATEGORIES)."$#",
false, 30, $type);
// Increment the number of products
$ok &= $this->set("nb_products", $index + 1);
return $ok;
}
/**
* Add extra info as a request parameter.
* @param string $key
* @param string $value
* @return boolean true if extra info is set correctly
*/
function addExtInfoRequestField($key, $value) {
return $this->_addRequestField("vads_ext_info_" . $key, "Extra info " . $key, '#^.{0,255}$#', false, 255, $value);
}
/**
* Return certificate according to current mode, false if mode was not set
* @return string|boolean
*/
function getCertificate() {
switch ($this->requestParameters['vads_ctx_mode']
->getValue()) {
case 'TEST':
return $this->keyTest;
break;
case 'PRODUCTION':
return $this->keyProd;
break;
default:
return false;
break;
}
}
/**
* Generate signature from a list of PayzenField
* @param array[string]PayzenField $fields
* @return string
* @access private
*/
function _generateSignatureFromFields($fields = null, $hashed = true) {
$params = array();
$fields = ($fields !== null) ? $fields : $this->requestParameters;
foreach ($fields as $field) {
if ($field->isRequired() || $field->isFilled()) {
$params[$field->getName()] = $field->getValue();
}
}
return $this->sign($params, $this->getCertificate(), $hashed);
}
/**
* Public static method to compute a PayZen signature. Parameters must be in utf-8.
* @param array[string]string $parameters payment gateway request/response parameters
* @param string $key shop certificate
* @param boolean $hashed set to false to get the raw, unhashed signature
* @access public
* @static
*/
function sign($parameters, $key, $hashed = true) {
$signContent = "";
ksort($parameters);
foreach ($parameters as $name => $value) {
if (substr($name, 0, 5) == 'vads_') {
$signContent .= $value . '+';
}
}
$signContent .= $key;
$sign = $hashed ? sha1($signContent) : $signContent;
return $sign;
}
// **************************************
// REQUEST PREPARATION FUNCTIONS
// **************************************
/**
* Unset the value of optionnal fields if they are unvalid
*/
function clearInvalidOptionnalFields() {
$fields = $this->getRequestFields();
foreach ($fields as $field) {
if (!$field->isValid() && !$field->isRequired()) {
$field->setValue(null);
}
}
}
/**
* Check all payment fields
* @param array $errors will be filled with the name of invalid fields
* @return boolean
*/
function isRequestReady(&$errors = null) {
$errors = is_array($errors) ? $errors : array();
$fields = $this->getRequestFields();
foreach ($fields as $field) {
if (!$field->isValid()) {
$errors[] = $field->getName();
}
}
return sizeof($errors) == 0;
}
/**
* Return the list of fields to send to the payment gateway
* @return array[string]PayzenField a list of PayzenField or false if a parameter was invalid
* @see PayzenField
*/
function getRequestFields() {
$fields = $this->requestParameters;
// Filter redirect_parameters if redirect is disabled
if (!$this->redirectEnabled) {
$redirectFields = array(
'vads_redirect_success_timeout',
'vads_redirect_success_message',
'vads_redirect_error_timeout',
'vads_redirect_error_message');
foreach ($redirectFields as $fieldName) {
unset($fields[$fieldName]);
}
}
foreach ($fields as $fieldName => $field) {
if (!$field->isFilled() && !$field->isRequired()) {
unset($fields[$fieldName]);
}
}
// Compute signature
$fields['signature']->setValue($this->_generateSignatureFromFields($fields));
// Return the list of fields
return $fields;
}
/**
* Return the url of the payment page with urlencoded parameters (GET-like url)
* @return boolean|string
*/
function getRequestUrl() {
$fields = $this->getRequestFields();
$url = $this->platformUrl . '?';
foreach ($fields as $field) {
if ($field->isFilled()) {
$url .= $field->getName() . '=' . rawurlencode($field->getValue())
. '&';
}
}
$url = substr($url, 0, -1); // remove last &
return $url;
}
/**
* Return the html form to send to the payment gateway
* @param string $enteteAdd
* @param string $inputType
* @param string $buttonValue
* @param string $buttonAdd
* @param string $buttonType
* @return string
*/
function getRequestHtmlForm($enteteAdd = '', $inputType = 'hidden',
$buttonValue = 'Aller sur la plateforme de paiement', $buttonAdd = '',
$buttonType = 'submit') {
$html = "";
$html .= '<form action="' . $this->platformUrl . '" method="POST" '
. $enteteAdd . '>';
$html .= "\n";
$html .= $this->getRequestFieldsHtml('type="' . $inputType . '"');
$html .= '<input type="' . $buttonType . '" value="' . $buttonValue . '" '
. $buttonAdd . '/>';
$html .= "\n";
$html .= '</form>';
return $html;
}
/**
* Return the html code of the form fields to send to the payment page
* @param string $inputAttributes
* @return string
*/
function getRequestFieldsHtml($inputAttributes = 'type="hidden"') {
$fields = $this->getRequestFields();
$html = '';
$format = '<input name="%s" value="%s" ' . $inputAttributes . "/>\n";
foreach ($fields as $field) {
if ($field->isFilled()) {
// Convert special chars to HTML entities to avoid data troncation
$value = htmlspecialchars($field->getValue(), ENT_QUOTES, 'UTF-8');
$html .= sprintf($format, $field->getName(), $value);
}
}
return $html;
}
/**
* Return the html fields to send to the payment page as a key/value array
* @return array[string][string]
*/
function getRequestFieldsArray() {
$fields = $this->getRequestFields();
$result = array();
foreach ($fields as $field) {
if ($field->isFilled()) {
// Convert special chars to HTML entities to avoid data troncation
$result[$field->getName()] = htmlspecialchars($field->getValue(), ENT_QUOTES, 'UTF-8');
}
}
return $result;
}
// **************************************
// RESPONSE ANALYSIS FUNCTIONS
// **************************************
/**
* Prepare to analyse check url or return url call
* @param array[string]string $parameters $_REQUEST by default
* @param string $ctx_mode
* @param string $key_test
* @param string $key_prod
* @deprecated see PayzenResponse constructor
*/
function loadResponse($parameters = null, $ctx_mode = null, $key_test = null,
$key_prod = null) {
$parameters = is_null($parameters) ? $_REQUEST : $parameters;
$parameters = $this->uncharm($parameters);
// Load site credentials if provided
if (!is_null($ctx_mode)) {
$this->set('vads_ctx_mode', $ctx_mode);
}
if (!is_null($key_test)) {
$this->set('vads_key_test', $key_test);
}
if (!is_null($key_prod)) {
$this->set('vads_key_prod', $key_prod);
}
$this->response = new PayzenResponse();
$this->response->load($parameters, $this->getCertificate());
}
/**
* Return a PayzenResponse object representing the result of the payment.
* You can provide arguments to load data as in loadResponse.
* @see PayzenApi::loadResponse
* @return PayzenResponse
* @deprecated see PayzenResponse constructor
*/
function getResponse($parameters = null, $ctx_mode = null, $key_test = null,
$key_prod = null) {
//TODO ? redondance avec loadResponse
if (is_null($this->response)) {
$this->loadResponse($parameters, $ctx_mode, $key_test, $key_prod);
}
return $this->response;
}
/**
* PHP is not yet a sufficiently advanced technology to be indistinguishable from magic...
* so don't use magic_quotes, they mess up with the gateway response analysis.
*
* @param array $potentiallyMagicallyQuotedData
*/
function uncharm($potentiallyMagicallyQuotedData) {
if (get_magic_quotes_gpc()) {
$sane = array();
foreach ($potentiallyMagicallyQuotedData as $k => $v) {
$saneKey = stripslashes($k);
$saneValue = is_array($v) ? $this->uncharm($v) : stripslashes($v);
$sane[$saneKey] = $saneValue;
}
} else {
$sane = $potentiallyMagicallyQuotedData;
}
return $sane;
}
}
}
if(!@class_exists('PayzenResponse', false)) {
/**
* Class representing the result of a transaction (sent by the check url or by the client return)
*/
class PayzenResponse {
/**
* Raw response parameters array
* @var array
* @access private
*/
var $raw_response = array();
/**
* Certificate used to check the signature
* @see PayzenApi::sign
* @var boolean
* @access private
*/
var $certificate;
/**
* Value of vads_result
* @var string
* @access private
*/
var $code;
/**
* Translation of $code (vads_result)
* @var string
* @access private
*/
var $message;
/**
* Value of vads_extra_result
* @var string
* @access private
*/
var $extraCode;
/**
* Translation of $extraCode (vads_extra_result)
* @var string
* @access private
*/
var $extraMessage;
/**
* Value of vads_auth_result
* @var string
* @access private
*/
var $authCode;
/**
* Translation of $authCode (vads_auth_result)
* @var string
* @access private
*/
var $authMessage;
/**
* Value of vads_warranty_result
* @var string
* @access private
*/
var $warrantyCode;
/**
* Translation of $warrantyCode (vads_warranty_result)
* @var string
* @access private
*/
var $warrantyMessage;
/**
* Internal reference to PayzenApi for using util methods
* @var PayzenApi
* @access private
*/
var $api;
/**
* Associative array containing human-readable translations of response codes. Initialized to french translations.
* @var array
* @access private
*/
var $translation = array(
'no_code' => '',
'no_translation' => '',
'results' => array(
'00' => 'Paiement réalisé avec succès',
'02' => 'Le commerçant doit contacter la banque du porteur',
'05' => 'Paiement refusé',
'17' => 'Annulation client',
'30' => 'Erreur de format de la requête',
'96' => 'Erreur technique lors du paiement'),
'extra_results_default' => array(
'empty' => 'Pas de contrôle effectué',
'00' => 'Tous les contrôles se sont déroulés avec succès',
'02' => 'La carte a dépassé l’encours autorisé',
'03' => 'La carte appartient à la liste grise du commerçant',
'04' => 'Le pays d’émission de la carte appartient à la liste grise du commerçant',
'05' => 'L’adresse IP appartient à la liste grise du commerçant',
'06' => 'Le code BIN appartient à la liste grise du commerçant',
'07' => 'Détection d\'une e-carte bleue',
'08' => 'Détection d\'une carte commerciale nationale',
'09' => 'Détection d\'une carte commerciale étrangère',
'14' => 'La carte est une carte à autorisation systématique',
'20' => 'Aucun pays ne correspond (pays IP, pays carte, pays client)',
'99' => 'Problème technique rencontré par le serveur lors du traitement d’un des contrôles locaux'),
'extra_results_30' => array(
'00' => 'signature',
'01' => 'version',
'02' => 'merchant_site_id',
'03' => 'transaction_id',
'04' => 'date',
'05' => 'validation_mode',
'06' => 'capture_delay',
'07' => 'config',
'08' => 'payment_cards',
'09' => 'amount',
'10' => 'currency',
'11' => 'ctx_mode',
'12' => 'language',
'13' => 'order_id',
'14' => 'order_info',
'15' => 'cust_email',
'16' => 'cust_id',
'17' => 'cust_title',
'18' => 'cust_name',
'19' => 'cust_address',
'20' => 'cust_zip',
'21' => 'cust_city',
'22' => 'cust_country',
'23' => 'cust_phone',
'24' => 'url_success',
'25' => 'url_refused',
'26' => 'url_referral',
'27' => 'url_cancel',
'28' => 'url_return',
'29' => 'url_error',
'30' => 'identifier',
'31' => 'contrib',
'32' => 'theme_config',
'34' => 'redirect_success_timeout',
'35' => 'redirect_success_message',
'36' => 'redirect_error_timeout',
'37' => 'redirect_error_message',
'38' => 'return_post_params',
'39' => 'return_get_params',
'40' => 'card_number',
'41' => 'expiry_month',
'42' => 'expiry_year',
'43' => 'card_cvv',
'44' => 'card_info',
'45' => 'card_options',
'46' => 'page_action',
'47' => 'action_mode',
'48' => 'return_mode',
'50' => 'secure_mpi',
'51' => 'secure_enrolled',
'52' => 'secure_cavv',
'53' => 'secure_eci',
'54' => 'secure_xid',
'55' => 'secure_cavv_alg',
'56' => 'secure_status',
'60' => 'payment_src',
'61' => 'user_info',
'62' => 'contracts',
'70' => 'empty_params',
'99' => 'other'),
'auth_results' => array(
'00' => 'transaction approuvée ou traitée avec succès',
'02' => 'contacter l’émetteur de carte',
'03' => 'accepteur invalide',
'04' => 'conserver la carte',
'05' => 'ne pas honorer',
'07' => 'conserver la carte, conditions spéciales',
'08' => 'approuver après identification',
'12' => 'transaction invalide',
'13' => 'montant invalide',
'14' => 'numéro de porteur invalide',
'30' => 'erreur de format',
'31' => 'identifiant de l’organisme acquéreur inconnu',
'33' => 'date de validité de la carte dépassée',
'34' => 'suspicion de fraude',
'41' => 'carte perdue',
'43' => 'carte volée',
'51' => 'provision insuffisante ou crédit dépassé',
'54' => 'date de validité de la carte dépassée',
'56' => 'carte absente du fichier',
'57' => 'transaction non permise à ce porteur',
'58' => 'transaction interdite au terminal',
'59' => 'suspicion de fraude',
'60' => 'l’accepteur de carte doit contacter l’acquéreur',
'61' => 'montant de retrait hors limite',
'63' => 'règles de sécurité non respectées',
'68' => 'réponse non parvenue ou reçue trop tard',
'90' => 'arrêt momentané du système',
'91' => 'émetteur de cartes inaccessible',
'96' => 'mauvais fonctionnement du système',
'94' => 'transaction dupliquée',
'97' => 'échéance de la temporisation de surveillance globale',
'98' => 'serveur indisponible routage réseau demandé à nouveau',
'99' => 'incident domaine initiateur'),
'warranty_results' => array(
'YES' => 'Le paiement est garanti',
'NO' => 'Le paiement n\'est pas garanti',
'UNKNOWN' => 'Suite à une erreur technique, le paiment ne peut pas être garanti'));
/**
* Constructor for PayzenResponse class. Prepare to analyse check url or return url call.
* @param array[string]string $parameters $_REQUEST by default
* @param string $ctx_mode
* @param string $key_test
* @param string $key_prod
* @param string $encoding
*/
function PayzenResponse($parameters = null, $ctx_mode = null, $key_test = null, $key_prod = null) {
$this->api = new PayzenApi(); // Use default API encoding (UTF-8) since the payment platform returns UTF-8 data
if(is_null($parameters)) {
$parameters = $_REQUEST;
}
$parameters = $this->api->uncharm($parameters);
// Load site credentials if provided
if (!is_null($ctx_mode)) {
$this->api->set('vads_ctx_mode', $ctx_mode);
}
if (!is_null($key_test)) {
$this->api->set('vads_key_test', $key_test);
}
if (!is_null($key_prod)) {
$this->api->set('vads_key_prod', $key_prod);
}
$this->load($parameters, $this->api->getCertificate());
}
/**
* Load response codes and translations from a parameter array.
* @param array[string]string $raw
* @param boolean $authentified
*/
function load($raw, $certificate) {
$this->raw_response = is_array($raw) ? $raw : array();
$this->certificate = $certificate;
// Get codes
$code = $this->_findInArray('vads_result', $raw, null);
$extraCode = $this->_findInArray('vads_extra_result', $raw, null);
$authCode = $this->_findInArray('vads_auth_result', $raw, null);
$warrantyCode = $this->_findInArray('vads_warranty_code', $raw, null);
// Common translations
$noCode = $this->translation['no_code'];
$noTrans = $this->translation['no_translation'];
// Result and extra result
if ($code == null) {
$message = $noCode;
$extraMessage = ($extraCode == null) ? $noCode : $noTrans;
} else {
$message = $this->_findInArray($code, $this->translation['results'],
$noTrans);
if ($extraCode == null) {
$extraMessage = $noCode;
} elseif ($code == 30) {
$extraMessage = $this->_findInArray($extraCode,
$this->translation['extra_results_30'], $noTrans);
} else {
$extraMessage = $this->_findInArray($extraCode,
$this->translation['extra_results_default'], $noTrans);
}
}
// auth_result
if ($authCode == null) {
$authMessage = $noCode;
} else {
$authMessage = $this->_findInArray($authCode,
$this->translation['auth_results'], $noTrans);
}
// warranty_result
if ($warrantyCode == null) {
$warrantyMessage = $noCode;
} else {
$warrantyMessage = $this->_findInArray($warrantyCode,
$this->translations['warranty_results'], $noTrans);
}
$this->code = $code;
$this->message = $message;
$this->authCode = $authCode;
$this->authMessage = $authMessage;
$this->extraCode = $extraCode;
$this->extraMessage = $extraMessage;
$this->warrantyCode = $warrantyCode;
$this->warrantyMessage = $warrantyMessage;
}
/**
* Check response signature
* @return boolean
*/
function isAuthentified() {
return $this->api->sign($this->raw_response, $this->certificate)
== $this->getSignature();
}
/**
* Return the signature computed from the received parameters, for log/debug purposes.
* @param boolean $hashed apply sha1, false by default
* @return string
*/
function getComputedSignature($hashed = false) {
return $this->api->sign($this->raw_response, $this->certificate, $hashed);
}
/**
* Check if the payment was successful (waiting confirmation or captured)
* @return boolean
*/
function isAcceptedPayment() {
return $this->code == '00';
}
/**
* Check if the payment is waiting confirmation (successful but the amount has not been transfered and is not yet guaranteed)
* @return boolean
*/
function isPendingPayment() {
return $this->get('auth_mode') == 'MARK';
}
/**
* Check if the payment process was interrupted by the client
* @return boolean
*/
function isCancelledPayment() {
return $this->code == '17';
}
/**
* Return the value of a response parameter.
* @param string $name
* @return string
*/
function get($name) {
// Manage shortcut notations by adding 'vads_'
$name = (substr($name, 0, 5) != 'vads_') ? 'vads_' . $name : $name;
return @$this->raw_response[$name];
}
/**
* Shortcut for getting ext_info_* fields.
* @param string $key
* @return string
*/
function getExtInfo($key) {
return $this->get("ext_info_$key");
}
/**
* Returns the expected signature received from gateway.
* @return string
*/
function getSignature() {
return @$this->raw_response['signature'];
}
/**
* Return the paid amount converted from cents (or currency equivalent) to a decimal value
* @return float
*/
function getFloatAmount() {
$currency = $this->api->findCurrencyByNumCode($this->get('currency'));
return $currency->convertAmountToFloat($this->get('amount'));
}
/**
* Return a short description of the payment result, useful for logging
* @return string
*/
function getLogString() {
$log = $this->code . ' : ' . $this->message;
if ($this->code == '30') {
$log .= ' (' . $this->extraCode . ' : ' . $this->extraMessage . ')';
}
return $log;
}
/**
* Return a formatted string to output as a response to the check url call
* @param string $case shortcut code for current situations. Most useful : payment_ok, payment_ko, auth_fail
* @param string $extraMessage some extra information to output to the payment gateway
* @return string
*/
function getOutputForGateway($case = '', $extraMessage = '', $originalEncoding="UTF-8") {
$success = false;
$message = '';
// Messages prédéfinis selon le cas
$cases = array(
'payment_ok' => array(true, 'Paiement valide traité'),
'payment_ko' => array(true, 'Paiement invalide traité'),
'payment_ok_already_done' => array(true, 'Paiement valide traité, déjà enregistré'),
'payment_ko_already_done' => array(true, 'Paiement invalide traité, déjà enregistré'),
'order_not_found' => array(false, 'Impossible de retrouver la commande'),
'payment_ko_on_order_ok' => array(false, 'Code paiement invalide reçu pour une commande déjà validée'),
'auth_fail' => array(false, 'Echec authentification'),
'ok' => array(true, ''),
'ko' => array(false, ''));
if (array_key_exists($case, $cases)) {
$success = $cases[$case][0];
$message = $cases[$case][1];
}
$message .= ' ' . $extraMessage;
$message = str_replace("\n", '', $message);
// Set original CMS encoding to convert if necessary response to send to platform
$encoding = in_array(strtoupper($originalEncoding), $this->api->SUPPORTED_ENCODINGS) ? strtoupper($originalEncoding) : "UTF-8";
if($encoding !== "UTF-8") {
$message = iconv($encoding, "UTF-8", $message);
}
$response = '';
$response .= '<span style="display:none">';
$response .= $success ? "OK-" : "KO-";
$response .= $this->get('trans_id');
$response .= ($message === ' ') ? "\n" : "=$message\n";
$response .= '</span>';
return $response;
}
/**
* Private shortcut function
* @param string $value
* @param array[string]string $translations
* @param string $defaultTransation
* @access private
*/
function _findInArray($key, $array, $default) {
if (is_array($array) && array_key_exists($key, $array)) {
return $array[$key];
}
return $default;
}
}
}
if(!@class_exists('PayzenField', false)) {
/**
* Class representing a field of the form to send to the payment gateway
*/
class PayzenField {
/**
* Field's name. Matches the html input attribute
* @var string
* @access private
*/
var $name;
/**
* Field's label in english, to be used by translation systems
* @var string
* @access private
*/
var $label;
/**
* Field's maximum length. Matches the html text input attribute
* @var int
* @access private
*/
var $length;
/**
* PCRE regular expression the field value must match
* @var string
* @access private
*/
var $regex;
/**
* Whether the form requires the field to be set (even to an empty string)
* @var boolean
* @access private
*/
var $required;
/**
* Field's value. Null or string
* @var string
* @access private
*/
var $value = null;
/**
* Constructor
* @param string $name
* @param string $label
* @param string $regex
* @param boolean $required
* @param string $value
* @return PayzenField
*/
function PayzenField($name, $label, $regex, $required = false, $length = 255) {
$this->name = $name;
$this->label = $label;
$this->regex = $regex;
$this->required = $required;
$this->length = $length;
}
/**
* Setter for value
* @param mixed $value
* @return boolean true if the value is valid
*/
function setValue($value) {
$value = ($value === null) ? null : (string) $value;
// We save value even if invalid (in case the validate function is too restrictive, it happened once) ...
$this->value = $value;
if (!$this->validate($value)) {
// ... but we return a "false" warning
return false;
}
return true;
}
/**
* Checks the current value
* @return boolean false if the current value is invalid or null and required
*/
function isValid() {
return $this->validate($this->value);
}
/**
* Check if a value is valid for this field
* @param string $value
* @return boolean
*/
function validate($value) {
if ($value === null && $this->isRequired()) {
return false;
}
if ($value !== null && !preg_match($this->regex, $value)) {
return false;
}
return true;
}
/**
* Setter for the required attribute
* @param boolean $required
*/
function setRequired($required) {
$this->required = (boolean) $required;
}
/**
* Is the field required in the payment request ?
* @return boolean
*/
function isRequired() {
return $this->required;
}
/**
* Return the current value of the field.
* @return string
*/
function getValue() {
return $this->value;
}
/**
* Return the name (html attribute) of the field.
* @return string
*/
function getName() {
return $this->name;
}
/**
* Return the english human-readable name of the field.
* @return string
*/
function getLabel() {
return $this->label;
}
/**
* Return the maximum length of the field's value.
* @return number
*/
function getLength() {
return $this->length;
}
/**
* Has a value been set ?
* @return boolean
*/
function isFilled() {
return !is_null($this->getValue());
}
}
}
if(!@class_exists('PayzenCurrency', false)) {
/**
* Class representing a currency, used for converting alpha/numeric iso codes and float/integer amounts
*/
class PayzenCurrency {
var $alpha3;
var $num;
var $decimals;
function PayzenCurrency($alpha3, $num, $decimals = 2) {
$this->alpha3 = $alpha3;
$this->num = $num;
$this->decimals = $decimals;
}
function convertAmountToInteger($float) {
$coef = pow(10, $this->decimals);
return intval(strval($float * $coef));
}
function convertAmountToFloat($integer) {
$coef = pow(10, $this->decimals);
return floatval($integer) / $coef;
}
}
}
?>