* @copyright 2007-2011 PrestaShop SA * @version Release: $Revision: 7654 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ include(_PS_MODULE_DIR_.'trustedshops/lib/TSBPException.php'); /** * @see the technical doc for entire description. * too long to set it here. * @author Prestashop - Nans Pellicari * @since prestashop 1.4 * @version 0.1 */ class TSBuyerProtection extends AbsTrustedShops { const PREFIX_TABLE = 'TS_TAB1_'; const ENV_TEST = 'test'; const ENV_PROD = 'production'; const DB_ITEMS = 'ts_buyerprotection_items'; const DB_APPLI = 'ts_application_id'; const WEBSERVICE_BO = 'administration'; const WEBSERVICE_FO = 'front-end'; /** * List of registration link, need to add parameters * @see TSBuyerProtection::_getRegistrationLink() * @var array */ private $registration_link = array( 'DE' => 'http://www.trustedshops.de/shopbetreiber/mitgliedschaft.html', 'EN' => 'http://www.trustedshops.com/merchants/membership.html', 'FR' => 'http://www.trustedshops.com/marchands/affiliation.html', 'PL' => 'http://www.trustedshops.pl/handlowcy/cennik.html', ); /** * Link to obtain the certificate about the shop. * Use by seal of approval. * @see TSBuyerProtection::hookRightColumn() * @var array */ private static $certificate_link = array( 'DE' => 'http://www.trustedshops.de/profil/#shop_name#_#shop_id#.html', 'EN' => 'http://www.trustedshops.com/profile/#shop_name#_#shop_id#.html', 'FR' => 'http://www.trustedshops.fr', 'PL' => 'http://www.trustedshops.de/profil/#shop_name#_#shop_id#.html', ); /** * Available language for used TrustedShops Buyer Protection * @see TSBuyerProtection::__construct() * @var array */ private $available_languages = array('EN'=>'', 'FR'=>'', 'DE'=>'', 'PL'=>'', ); /** * @todo : be sure : see TrustedShopsRating::__construct() * @var array */ public $limited_countries = array('PL', 'GB', 'US', 'FR', 'DE'); /** * Differents urls to call for Trusted Shops API * @var array */ private static $webservice_urls = array( 'administration' => array( 'test' => 'https://qa.trustedshops.de/ts/services/TsProtection?wsdl', 'production' => 'https://www.trustedshops.de/ts/services/TsProtection?wsdl', ), 'front-end' => array( 'test' => 'https://protection-qa.trustedshops.com/ts/protectionservices/ApplicationRequestService?wsdl', 'production' => 'https://protection.trustedshops.com/ts/protectionservices/ApplicationRequestService?wsdl', ), ); // Configuration vars private static $SHOPSW; private static $ET_CID; private static $ET_LID; /** * Its must look like : * array( * 'lang_iso(ex: FR)' => array('stateEnum'=>'', 'typeEnum'=>'', 'url'=>'', 'tsID'=>'', 'user'=>'', 'password'=>''), * ... * ) * @var array */ private static $CERTIFICATE; private static $DEFAULT_LANG; private static $CAT_ID; private static $ENV_API; /** * save shop url * @var string */ private $site_url; /** * Payment type used by Trusted Shops. * @var array */ private static $payments_type; public function __construct() { // need to set this in constructor to allow translation TSBuyerProtection::$payments_type = array( 'DIRECT_DEBIT' => $this->l('Direct debit'), 'CREDIT_CARD' => $this->l('Credit Card'), 'INVOICE' => $this->l('Invoice'), 'CASH_ON_DELIVERY' => $this->l('Cash on delivery'), 'PREPAYMENT' => $this->l('Prepayment'), 'CHEQUE' => $this->l('Cheque'), 'PAYBOX' => $this->l('Paybox'), 'PAYPAL' => $this->l('PayPal'), 'CASH_ON_PICKUP' => $this->l('Cash on pickup'), 'FINANCING' => $this->l('Financing'), 'LEASING' => $this->l('Leasing'), 'T_PAY' => $this->l('T-Pay'), 'CLICKANDBUY' => $this->l('Click&Buy'), 'GIROPAY' => $this->l('Giropay'), 'GOOGLE_CHECKOUT' => $this->l('Google Checkout'), 'SHOP_CARD' => $this->l('Online shop payment card'), 'DIRECT_E_BANKING' => $this->l('DIRECTebanking.com'), 'MONEYBOOKERS' => $this->l('moneybookers.com'), 'OTHER' => $this->l('Other method of payment'), ); $this->tab_name = $this->l('Trusted Shops quality seal and buyer protection'); $this->site_url = Tools::htmlentitiesutf8('http://'.$_SERVER['HTTP_HOST'].__PS_BASE_URI__); TSBPException::setTranslationObject($this); if (!method_exists('Tools', 'jsonDecode') || !method_exists('Tools', 'jsonEncode')) { $this->warnings[] = $this->l('Json functions must be implemented in your php version'); } else { foreach ($this->available_languages as $iso => $lang) { if ($lang === '') $this->available_languages[$iso] = Language::getLanguage(Language::getIdByIso($iso)); TSBuyerProtection::$CERTIFICATE[strtoupper($iso)] = (array)Tools::jsonDecode( Tools::htmlentitiesDecodeUTF8(Configuration::get(TSBuyerProtection::PREFIX_TABLE.'CERTIFICATE_'.strtoupper($iso)))); } if (TSBuyerProtection::$SHOPSW === NULL) { TSBuyerProtection::$SHOPSW = Configuration::get(TSBuyerProtection::PREFIX_TABLE.'SHOPSW'); TSBuyerProtection::$ET_CID = Configuration::get(TSBuyerProtection::PREFIX_TABLE.'ET_CID'); TSBuyerProtection::$ET_LID = Configuration::get(TSBuyerProtection::PREFIX_TABLE.'ET_LID'); TSBuyerProtection::$DEFAULT_LANG = (int)Configuration::get('PS_LANG_DEFAULT'); TSBuyerProtection::$CAT_ID = (int)Configuration::get(TSBuyerProtection::PREFIX_TABLE.'CAT_ID'); TSBuyerProtection::$ENV_API = Configuration::get(TSBuyerProtection::PREFIX_TABLE.'ENV_API'); } } } public function install() { if (!method_exists('Tools', 'jsonDecode') || !method_exists('Tools', 'jsonEncode')) return false; foreach ($this->available_languages as $iso=>$lang) Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'CERTIFICATE_'.strtoupper($iso), Tools::htmlentitiesUTF8(Tools::jsonEncode(array('stateEnum'=>'', 'typeEnum'=>'', 'url'=>'', 'tsID'=>'', 'user'=>'', 'password'=>'')))); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'SHOPSW', ''); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'ET_CID', ''); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'ET_LID', ''); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'ENV_API', TSBuyerProtection::ENV_PROD); $req = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.TSBuyerProtection::DB_ITEMS.'` ( `id_item` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `id_product` INT NOT NULL, `ts_id` VARCHAR( 33 ) NOT NULL, `id` INT NOT NULL, `currency` VARCHAR( 3 ) NOT NULL , `gross_fee` DECIMAL( 20, 6 ) NOT NULL , `net_fee` DECIMAL( 20, 6 ) NOT NULL , `protected_amount_decimal` INT NOT NULL , `protection_duration_int` INT NOT NULL , `ts_product_id` TEXT NOT NULL , `creation_date` VARCHAR( 25 ) NOT NULL ); '; Db::getInstance()->Execute($req); $req = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.TSBuyerProtection::DB_APPLI.'` ( `id_application` INT NOT NULL PRIMARY KEY, `ts_id` VARCHAR( 33 ) NOT NULL, `id_order` INT NOT NULL, `statut_number` INT NOT NULL DEFAULT \'0\', `creation_date` DATETIME NOT NULL, `last_update` DATETIME NOT NULL ); '; Db::getInstance()->Execute($req); //add hidden category $category = new Category(); $languages = Language::getLanguages(true); foreach ($this->available_languages as $iso=>$lang) { $category->name[Language::getIdByIso(strtolower($iso))] = 'Trustedshops'; $category->link_rewrite[Language::getIdByIso(strtolower($iso))] = 'trustedshops'; } // If the default lang is different than available languages : // (Bug occurred otherwise) if (!array_key_exists(Language::getIsoById((int)Configuration::get('PS_LANG_DEFAULT')), $this->available_languages)) { $category->name[(int)Configuration::get('PS_LANG_DEFAULT')] = 'Trustedshops'; $category->link_rewrite[(int)Configuration::get('PS_LANG_DEFAULT')] = 'trustedshops'; } $category->id_parent = 0; $category->level_depth = 0; $category->active = 0; $category->add(); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'CAT_ID', intval($category->id)); Configuration::updateValue(TSBuyerProtection::PREFIX_TABLE.'SECURE_KEY', strtoupper(Tools::passwdGen(16))); return true; } public function uninstall() { foreach ($this->available_languages as $iso=>$lang) Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'CERTIFICATE_'.strtoupper($iso)); $category = new Category((int)TSBuyerProtection::$CAT_ID); $category->delete(); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'CAT_ID'); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'SHOPSW'); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'ET_CID'); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'ET_LID'); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'ENV_API'); Configuration::deleteByName(TSBuyerProtection::PREFIX_TABLE.'SECURE_KEY'); return true; } /** * Just for return the file path * @return string */ public function getCronFilePath() { return $this->site_url.'modules/'.self::$module_name.'/cron_garantee.php?secure_key='.Configuration::get(TSBuyerProtection::PREFIX_TABLE.'SECURE_KEY'); } /** * This method is used to access of TrustedShops API * from a SoapClient object. * * @uses TSBuyerProtection::$webservice_urls with TSBuyerProtection::$ENV_API * To get the api url according to the environment (test or production) * @param string $type * @return SoapClient */ private function _getClient($type = TSBuyerProtection::WEBSERVICE_BO) { $url = TSBuyerProtection::$webservice_urls[$type][TSBuyerProtection::$ENV_API]; $client = new SoapClient($url); return $client; } /** * Checks the Trusted Shops IDs entered in the shop administration * and returns the characteristics of the corresponding certificate. * * @uses TSBuyerProtection::_getClient() * @param string $certificate certificate code already send by Trusted Shops */ private function _checkCertificate($certificate) { $array_state = array( 'PRODUCTION' => $this->l('The certificate is valid'), 'CANCELLED' => $this->l('The certificate has expired'), 'DISABLED' => $this->l('The certificate has been disabled'), 'INTEGRATION' => $this->l('The shop is currently being certified'), 'INVALID_TS_ID' => $this->l('No certificate has been allocated to the Trusted Shops ID'), 'TEST' => $this->l('Test certificate'), ); $client = $this->_getClient(); $validation = false; try { $validation = $client->checkCertificate($certificate); } catch (SoapFault $fault) { $this->errors[] = $this->l('Code #').$fault->faultcode.',
'.$this->l('message:').$fault->faultstring; } if (is_int($validation)) throw new TSBPException($validation, TSBPException::ADMINISTRATION); if (!$validation OR array_key_exists($validation->stateEnum, $array_state)) { if ($validation->stateEnum === 'TEST' || $validation->stateEnum === 'PRODUCTION' || $validation->stateEnum === 'INTEGRATION') { $this->confirmations[] = $array_state[$validation->stateEnum]; return $validation; } else { $this->errors[] = $array_state[$validation->stateEnum]; return false; } } else { $this->errors[] = $this->l('Unknown error.'); } } /** * Checks the shop's web service access credentials. * * @uses TSBuyerProtection::_getClient() * @param string $ts_id * @param string $user * @param string $password */ private function _checkLogin($ts_id, $user, $password) { $client = $this->_getClient(); $return = 0; try { $return = $client->checkLogin($ts_id, $user, $password); } catch (SoapClient $fault) { $this->errors[] = $this->l('Code #').$fault->faultcode.',
'.$this->l('message:').$fault->faultstring; } if ($return < 0) throw new TSBPException($return, TSBPException::ADMINISTRATION); return true; } /** * Returns the characteristics of the buyer protection products * that are allocated individually to each certificate by Trusted Shops. * * @uses TSBuyerProtection::_getClient() * @param string $ts_id */ private function _getProtectionItems($ts_id) { $client = $this->_getClient(); try { $items = $client->getProtectionItems($ts_id); } catch (SoapFault $fault) { $this->errors[] = $this->l('Code #').$fault->faultcode.',
'.$this->l('message:').$fault->faultstring; } if (isset($items->item)) return $items->item; return false; } /** * Check validity for params required for TSBuyerProtection::_requestForProtectionV2() * * @param array $params */ private function _requestForProtectionV2ParamsValidator($params) { $bool_flag = true; $mandatory_keys = array( array('name'=>'tsID', 'validator'=>array('isCleanHtml'),), array('name'=>'tsProductID', 'validator'=>array('isCleanHtml'),), array('name'=>'amount', 'validator'=>array('isFloat'),), array('name'=>'currency', 'length'=>3, 'validator'=>array('isString'),), array('name'=>'paymentType', 'validator'=>array('isString'),), array('name'=>'buyerEmail', 'validator'=>array('isEmail'),), array('name'=>'shopCustomerID', 'validator'=>array('isInt'),), array('name'=>'shopOrderID', 'validator'=>array('isInt'),), array('name'=>'orderDate', 'ereg'=>'#[0-9]{4}\-[0-9]{2}\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}#',), array('name'=>'shopSystemVersion','validator'=>array('isCleanHtml'),), array('name'=>'wsUser','validator'=>array('isCleanHtml'),), array('name'=>'wsPassword', 'validator'=>array('isCleanHtml'),), ); foreach ($mandatory_keys as $key) { $bool_flag = (!array_key_exists($key['name'], $params)) ? false : $bool_flag; if ($bool_flag) { if (isset($key['length'])) $bool_flag = strlen((string)$params[$key['name']]) === $key['length']; if (isset($key['length-min'])) $bool_flag = strlen((string)$params[$key['name']]) > $key['length-min']; if (isset($key['length-max'])) $bool_flag = strlen((string)$params[$key['name']]) < $key['length-max']; if (isset($key['validator'])) foreach ($key['validator'] as $validator) if (method_exists('Validate', $validator)) $bool_flag = !Validate::$validator((string)$params[$key['name']]) ? false : $bool_flag; if (isset($key['ereg'])) $bool_flag = !preg_match($key['ereg'], $params[$key['name']]) ? false : $bool_flag ; } if (!$bool_flag) { $this->errors[] = sprintf($this->l('The field %s is wrong, please ensure it was correctly filled.'), $key['name']); break; } } return $bool_flag; } /** * Create the Buyer Protection application by the web service. * Applications are saved by Trusted Shops and are processed at regular intervals. * * @uses TSBuyerProtection::_getClient() * @uses TSBuyerProtection::_requestForProtectionV2ParamsValidator() * to check required params * @see TSBuyerProtection::cronTasks() * @param array $params */ private function _requestForProtectionV2($params) { $client = $this->_getClient(TSBuyerProtection::WEBSERVICE_FO); $testing_params = $this->_requestForProtectionV2ParamsValidator($params); $code = 0; $sql = ' SELECT * FROM `'._DB_PREFIX_.TSBuyerProtection::DB_APPLI.'` WHERE `id_order` = "'.(int)$params['shopOrderID'].'" '; $order = Db::getInstance()->ExecuteS($sql); // If an order was already added, no need to continue. // Otherwise a new application is created by TrustedShops. // this can occurred when order confirmation page is reload. if (isset($order[0])) return false; if ($testing_params) { try { $code = $client->requestForProtectionV2($params['tsID'], $params['tsProductID'], $params['amount'], $params['currency'], $params['paymentType'], $params['buyerEmail'], $params['shopCustomerID'], $params['shopOrderID'], $params['orderDate'], $params['shopSystemVersion'], $params['wsUser'], $params['wsPassword']); if ($code < 0) throw new TSBPException($code, TSBPException::FRONT_END); } catch (SoapFault $fault) { $this->errors[] = $this->l('Code #').$fault->faultcode.',
'.$this->l('message:').$fault->faultstring; } catch (TSBPException $e) { $this->errors[] = $e->getMessage(); } if ($code > 0) { $date = date('Y-m-d H:i:s'); $sql = ' INSERT INTO `'._DB_PREFIX_.TSBuyerProtection::DB_APPLI.'` ( `id_application`, `ts_id`, `id_order`, `creation_date`, `last_update` ) VALUES ( "'.pSQL($code).'", "'.pSQL($params['tsID']).'", "'.pSQL($params['shopOrderID']).'", "'.$date.'", "'.$date.'" ) '; Db::getInstance()->Execute($sql); // To reset product quantity in database. $sql = ' SELECT `id_product` FROM `'._DB_PREFIX_.TSBuyerProtection::DB_ITEMS.'` WHERE `ts_product_id` = "'.(int)$params['tsProductID'].'" '; $ts_product = Db::getInstance()->ExecuteS($sql); $product = new Product($ts_product[0]['id_product']); $product->quantity = 1000; $product->update(); } } else $this->errors[] = $this->l('Some parameters sending to "requestForProtectionV2" method are wrong or missing.'); } /** * With the getRequestState() method, * the status of a guarantee application is requested * and in the event of a successful transaction, * the guarantee number is returned. * * @uses TSBuyerProtection::_getClient() * @param array $params * @throws TSBPException */ private function _getRequestState($params) { $client = $this->_getClient(TSBuyerProtection::WEBSERVICE_FO); $code = 0; try { $code = $client->getRequestState($params['tsID'], $params['applicationID']); if ($code < 0) throw new TSBPException($code, TSBPException::FRONT_END); } catch (SoapFault $fault) { $this->errors[] = $this->l('Code #').$fault->faultcode.',
'.$this->l('message:').$fault->faultstring; } catch (TSBPException $e) { $this->errors[] = $e->getMessage(); } return $code; } /** * Check statut of last applications * saved with TSBuyerProtection::_requestForProtectionV2() * * Negative value means an error occurred. * Error code are managed in TSBPException. * @see (exception) TSBPException::_getFrontEndMessage() method * * Trusted Shops recommends that the request * should be automated by a cronjob with an interval of 10 minutes. * @see /../cron_garantee.php * * A message is added to the sheet order in Back-office, * @see Message class * * @uses TSBuyerProtection::_getRequestState() * @uses Message class * @return void */ public function cronTask() { // get the last 20min to get the api number (to be sure) $mktime = mktime(date('H'), date('i')-20, date('s'), date('m'), date('d'), date('Y')); $date = date('Y-m-d H:i:s', $mktime); $db_name = _DB_PREFIX_.TSBuyerProtection::DB_APPLI; $sql = ' SELECT * FROM `'.$db_name.'` WHERE `last_update` >= "'.$date.'" OR `statut_number` <= 0 '; $to_check = Db::getInstance()->ExecuteS($sql); foreach ($to_check as $application) { $code = $this->_getRequestState(array('tsID'=>$application['ts_id'], 'applicationID'=>$application['id_application'])); if (!empty($this->errors)) { $return_message = '

'.$this->l('Trusted Shops API returns an error concerning the application #').$application['id_application'].':
'.implode(',
', $this->errors).'

'; $this->errors = array(); } elseif ($code > 0) { $return_message = sprintf($this->l('Trusted Shops application number %1$d was successfully processed. The guarantee number is: %2$d'), $application['id_application'], $code); } $sql = ' UPDATE `'.$db_name.'` SET `statut_number` = "'.$code.'" WHERE `id_application` >= "'.$application['id_application'].'" '; Db::getInstance()->Execute($sql); $msg = new Message(); $msg->message = $return_message; $msg->id_order = (int)$application['id_order']; $msg->private = 1; $msg->add(); } } /** * Registration link to Trusted Shops * * @param string $shopsw * @param string $et_cid * @param string $et_lid * @param string $lang * @return boolean|string boolean in case of $lang is not supported by Trusted Shops * string return is the url to access of form subscription */ private function _makeRegistrationLink($shopsw, $et_cid, $et_lid, $lang) { if (array_key_exists($lang, $this->registration_link)) return $this->registration_link[$lang].sprintf('?shopsw=%s&et_cid=%s&et_lid=%s', urlencode($shopsw), urlencode($et_cid), urlencode($et_lid)); return false; } /** * Method to display or redirect the subscription link. * * @param string $link */ private function _getRegistrationLink($link) { return '