Merge branch 'ticket-14959-fraud' into develop

This commit is contained in:
Michael RICOIS 2017-12-06 15:18:08 +01:00
commit 9c8625b389
3 changed files with 148 additions and 95 deletions

View File

@ -202,7 +202,7 @@ abstract class AdminTabCore
*
* @param mixed $string term or expression in english
* @param string $class
* @param boolan $addslashes if set to true, the return value will pass through addslashes(). Otherwise, stripslashes().
* @param boolean $addslashes if set to true, the return value will pass through addslashes(). Otherwise, stripslashes().
* @param boolean $htmlentities if set to true(default), the return value will pass through htmlentities($string, ENT_QUOTES, 'utf-8')
* @return string the translation if available, or the english default text.
*/
@ -227,8 +227,8 @@ abstract class AdminTabCore
}
/**
* ajaxDisplay is the default ajax return sytem
*
* ajaxDisplay is the default ajax return sytem
*
* @return void
*/
public function displayAjax()
@ -357,7 +357,6 @@ abstract class AdminTabCore
{
switch ($action)
{
case 'submitAdd1':
if (Tools::getValue('submitAdd'.$adminTab->table))
$ok_inc = true;
@ -452,7 +451,7 @@ abstract class AdminTabCore
/* Checking for fields validity */
foreach ($rules['validate'] AS $field => $function)
if (($value = Tools::getValue($field)) !== false AND !empty($value) AND ($field != 'passwd'))
if (($value = Tools::getValue($field)) !== false AND !empty($value) AND ($field != 'passwd'))
if (!Validate::$function($value))
$this->_errors[] = $this->l('the field').' <b>'.call_user_func(array($className, 'displayFieldName'), $field, $className).'</b> '.$this->l('is invalid');
@ -506,8 +505,8 @@ abstract class AdminTabCore
}
/**
* ajaxPreProcess is a method called in ajax-tab.php before displayConf().
*
* ajaxPreProcess is a method called in ajax-tab.php before displayConf().
*
* @return void
*/
public function ajaxPreProcess()
@ -516,7 +515,7 @@ abstract class AdminTabCore
/**
* ajaxProcess is the default handle method for request with ajax-tab.php
*
*
* @return void
*/
public function ajaxProcess()
@ -1240,7 +1239,7 @@ abstract class AdminTabCore
echo '<form method="post" action="'.$currentIndex;
if (Tools::getIsset($this->identifier))
echo '&'.$this->identifier.'='.(int)(Tools::getValue($this->identifier));
echo '&token='.$token;
echo '&token='.$token;
if (Tools::getIsset($this->table.'Orderby'))
echo '&'.$this->table.'Orderby='.urlencode($this->_orderBy).'&'.$this->table.'Orderway='.urlencode(strtolower($this->_orderWay));
echo '#'.$this->table.'" class="form">

View File

@ -4,9 +4,10 @@ if (!defined('_PS_VERSION_'))
include_once dirname(__FILE__).'/models/FraudCore.php';
class Fraud extends Module {
public function __construct() {
class Fraud extends Module
{
public function __construct()
{
$this->name = 'fraud';
$this->tab = 'payments_gateways';
$this->version = 1.0;
@ -19,18 +20,20 @@ class Fraud extends Module {
$this->description = $this->l('Check fraud orders.');
}
public function install() {
public function install()
{
if(!(parent::install()
&& $this->installDB()
&& $this->registerHook('adminOrder')
&& $this->registerHook('backBeforePayment')
&& $this->registerHook('afterChangeStatus'))) {
return false;
return false;
}
return true;
}
public function installDB() {
public function installDB()
{
return Db::getInstance()->Execute('
CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'reputation` (
`id_customer` INTEGER UNSIGNED NOT NULL,
@ -56,22 +59,29 @@ class Fraud extends Module {
');
}
public function hookbackBeforePayment($params) {
public function hookbackBeforePayment($params)
{
FraudCore::CartFraudConnexion($params['cart']);
}
public function hookAdminOrder($params) {
public function hookAdminOrder($params)
{
global $currentIndex;
if (Tools::getIsset('validFraud')) {
$token = Tools::getValue('token');
$id_order = Tools::getValue('id_order');
if (!FraudCore::validOrder($id_order)) {
echo '<br /><p class="alert" style="width:300px">'.$this->l('Update impossible').'</p>';
} else {
echo '<br /><p class="conf">'.$this->l('Valid order with success').'</p>';
Tools::redirectAdmin($currentIndex.'&id_order='.$id_order.'&vieworder&hookconf=1&token='.$token);
}
}
if (Tools::getIsset('hookconf')) {
echo '<br /><p class="conf">'.$this->l('Valid order with success').'</p>';
}
$reputation = FraudCore::getReputationOrder((int) $params['id_order']);
if ($reputation) {
if ($reputation['score'] >= 100) {
@ -97,8 +107,10 @@ class Fraud extends Module {
}
}
public function hookAdminOrderNew($params) {
public function hookAdminOrderNew($params)
{
global $currentIndex;
$data .= '';
$reputation = FraudCore::getReputationOrder((int) $params['id_order']);
if ($reputation) {
@ -110,28 +122,36 @@ class Fraud extends Module {
<div class="clearfix"></div>
</div>
<div class="panel-content">';
if (Tools::getIsset('validFraud')) {
$id_order = Tools::getValue('id_order');
if (!FraudCore::validOrder($id_order)) {
$data .= '<br /><p class="alert">'.$this->l('Update impossible').'</p>';
} else {
$data .= '<br /><p class="conf">'.$this->l('Valid order with success').'</p>';
}
}
if ($reputation['score'] >= 100) {
$info = json_decode($reputation['report']);
$data .= '
<a class="btn btn-danger" role="button" data-toggle="collapse" href="#collapseFraudModule">
<b>'.$this->l('Score : ').'</b> <span class="badge">'.$reputation['score'].'</span>
</a>
<div class="collapse" id="collapseFraudModule">
<h4>'.$this->l('Details : ').'</h4>
<p>'.implode('<br />', $info).'</p>
</div>';
if ($reputation['pass'] == 0) {
$data .= '<a class="pull-right btn btn-primary btn-sm" onclick="if(!confirm(\'Voulez-vous valider la commande ? \')) return false;" href="'.$_SERVER['REQUEST_URI'].'&validFraud=1" class="button">'.$this->l('Valid Order').'</a>';
}
}
if (Tools::getIsset('validFraud')) {
$token = Tools::getValue('token');
$id_order = Tools::getValue('id_order');
if (!FraudCore::validOrder($id_order)) {
$data .= '<br /><p class="alert">'.$this->l('Update impossible').'</p>';
} else {
Tools::redirectAdmin($currentIndex.'&id_order='.$id_order.'&vieworder&hookconf=1&token='.$token);
}
}
if (Tools::getIsset('hookconf')) {
$data .= '<br /><p class="conf">'.$this->l('Valid order with success').'</p>';
}
if ($reputation['score'] >= 100) {
$info = json_decode($reputation['report']);
$data .= '
<a class="btn btn-danger" role="button" data-toggle="collapse" href="#collapseFraudModule">
<b>'.$this->l('Score : ').'</b> <span class="badge">'.$reputation['score'].'</span>
</a>
<div class="collapse" id="collapseFraudModule">
<h4>'.$this->l('Details : ').'</h4>
<p>'.implode('<br />', $info).'</p>
</div>';
if ($reputation['pass'] == 0) {
$data .= '<a class="pull-right btn btn-primary btn-sm" onclick="if(!confirm(\'Voulez-vous valider la commande ? \')) return false;" href="'.$_SERVER['REQUEST_URI'].'&validFraud=1" class="button">'.$this->l('Valid Order').'</a>';
}
}
$data .= '</div>
</div>
</div>';
@ -145,13 +165,13 @@ class Fraud extends Module {
$order = new Order($id_order);
$authorized = array('FRA','ESP','DEU','ITA','NLD','SWE','GBR','PRT','CHE','LUX','POL','AUT','BEL');
if(!in_array($ip_country, $authorized)) {
if (!in_array($ip_country, $authorized)) {
$total_score = 110;
$fraud_report = array();
$fraud_report[] = 'Pays de paiement hors EU (+110)';
$order_reputation = FraudCore::getReputationOrder((int)$id_order);
if($order_reputation) {
if ($order_reputation) {
$fraud_report = array_merge($fraud_report, json_decode($order_reputation['report']));
$total_score += (int)$order_reputation['score'];
}
@ -217,11 +237,11 @@ class Fraud extends Module {
}
}
public function hookafterChangeStatus($params) {
if($params['newOrderState'] == 2) {
public function hookafterChangeStatus($params)
{
if ($params['newOrderState'] == 2) {
$order = new Order($params['order']['id']);
if ( Validate::isLoadedObject($order) ) {
if (Validate::isLoadedObject($order)) {
if ($order->module != 'paybox') {
return FALSE;
@ -233,7 +253,7 @@ class Fraud extends Module {
FROM `ps_order_reputation`
WHERE `id_cart` ='.(int)$order->id_cart
);
if(!empty($already_test)) {
if (!empty($already_test)) {
return true;
}
@ -249,7 +269,7 @@ class Fraud extends Module {
FROM `'._DB_PREFIX_.'reputation`
WHERE `id_customer` = '.(int) $order->id_customer);
if($query && count($query) > 0) {
if ($query && count($query) > 0) {
foreach($query as $r) {
$current_reputation += $r['score'] - floor((time() - strtotime($r['date_upd'])) / (86400 * 7)) * 20;
$i++;
@ -279,7 +299,7 @@ class Fraud extends Module {
');
// check fraud score
if($total_score < 100) {
if ($total_score < 100) {
Db::getInstance()->ExecuteS('
INSERT INTO `'._DB_PREFIX_.'reputation`
VALUES (
@ -318,7 +338,8 @@ class Fraud extends Module {
}
}
private function _changeStatutFraud($order_id) {
private function _changeStatutFraud($order_id)
{
$history = new OrderHistory();
$history->id_order = $order_id;
$history->changeIdOrderState(18, $order_id);

View File

@ -1,7 +1,7 @@
<?php
class FraudCore {
class FraudCore
{
public $order;
public $cart;
public $customer;
@ -11,8 +11,9 @@ class FraudCore {
private $delivery_country;
private $invoice_country;
public function __construct(Order $order) {
if( !Validate::isLoadedObject($order) ) {
public function __construct(Order $order)
{
if (!Validate::isLoadedObject($order)) {
return false;
} else {
$this->order = $order;
@ -23,7 +24,8 @@ class FraudCore {
}
}
public function setFraudScore() {
public function setFraudScore()
{
if( !Validate::isLoadedObject($this->order) ) {
throw new Exception("this->order is not an object");
return false;
@ -37,17 +39,21 @@ class FraudCore {
$this->alreadyInFraud();
}
public function getFraudScore() {
public function getFraudScore()
{
return $this->fraud_score;
}
public function getFraudReport() {
public function getFraudReport()
{
return $this->fraud_report;
}
/**
* Tools fraud
**/
private function fraudAccount() {
private function fraudAccount()
{
$account_today = time() - strtotime($this->customer->date_add) < 86400;
$time_warn = in_array(date('H'), array('23', '00', '01', '02', '03', '04', '05'));
$lower_warn = (mb_strtolower($this->customer->firstname) === $this->customer->firstname) && (mb_strtolower($this->customer->lastname) === $this->customer->lastname);
@ -62,7 +68,8 @@ class FraudCore {
$this->fraud_score = $account_today * 50 + $time_warn * 20 + $lower_warn * 20 + $name_warn * 100;
}
private function fraudAdresses() {
private function fraudAdresses()
{
$this->delivery_country = (int) Db::getInstance()->getValue('
SELECT `id_country`
FROM `'._DB_PREFIX_.'address`
@ -111,7 +118,8 @@ class FraudCore {
$this->fraud_score += $foreign_delivery * 50 + $foreign_invoice * 50 + $delivery_warn * 20;
}
private function fraudEmail() {
private function fraudEmail()
{
$freemail_warn = preg_match('/^(.*)@(yahoo|hotmail|gmail|yopmail|mail|mail2web|fastmail|mailinator|jetable|msn)\.(.*)$/i', $this->customer->email);
$cctldmail_warn = preg_match('/^(.*)@(.*)\.(cn|cc|in|tw|ru|pl|ua|bg|bj|br|by|ci|dj|dz|eg|et|fj|hk|it|ma|si|tn|zw)$/i', $this->customer->email);
@ -121,8 +129,8 @@ class FraudCore {
$this->fraud_score += $freemail_warn * 20 + $cctldmail_warn * 100;
}
private function fraudConnexion() {
private function fraudConnexion()
{
$data = Db::getInstance()->getRow('
SELECT *
FROM `ps_cart_fraud`
@ -141,11 +149,12 @@ class FraudCore {
$this->fraud_score += $proxy * 20 + $ip_foreign * 200 + $ip_alert * 50 + $freewifi * 50;
}
}
private function fraudCountData() {
$count_addresses = Db::getInstance()->getValue('
private function fraudCountData()
{
// Adresses count
$count_addresses = Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'address`
WHERE `id_customer` = '.(int) $this->customer->id.'
@ -156,10 +165,11 @@ class FraudCore {
$this->fraud_report[] = 'Compte à plus de 2 adresses (+'.(5 * ((int) $count_addresses - 2)).')';
}
// Orders count last 7 days
$count_orders = Db::getInstance()->getRow('
SELECT COUNT(*) AS `total`
FROM `ps_orders`
WHERE `id_customer` = '.(int) $this->customer->id.'
WHERE `id_customer` = '.(int)$this->customer->id.'
AND `date_add` >= DATE_SUB(NOW(), INTERVAL 7 DAY)
');
if($count_orders['total'] + 1 > 3) {
@ -167,31 +177,53 @@ class FraudCore {
$this->fraud_report[] = 'Compte origine de plus de 3 commandes ces 7 derniers jours (+100)';
}
$count_products = Db::getInstance()->getRow('
SELECT COUNT(*) AS `total`
FROM `ps_cart_product`
WHERE `id_cart` = '.(int) $this->cart->id.'
');
// Products and Total Paid
$count_total_paid = $this->cart->getOrderTotal();
if($count_products['total'] > 7 && $count_total_paid > 250) {
$this->fraud_score += 100;
$this->fraud_report[] = 'Plus de 7 produits dans le panier et total de plus de 250€ (+100)';
if ($count_total_paid > 250) {
$count_products = Db::getInstance()->getRow('
SELECT COUNT(*) AS `total`
FROM `ps_cart_product`
WHERE `id_cart` = '.(int)$this->cart->id.'
');
if ($count_products['total'] > 7) {
$this->fraud_score += 100;
$this->fraud_report[] = 'Plus de 7 produits dans le panier et total de plus de 250€ (+100)';
}
}
$count_oldorders = Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'orders`
WHERE `id_customer` = '.(int) $this->customer->id.'
AND `valid` = 1
AND `date_add` < DATE_SUB(NOW(), INTERVAL 30 DAY)
');
if($count_oldorders > 3) {
$this->fraud_score -= 50;
$this->fraud_report[] = 'Compte avec plus de 3 commandes valides de plus de 30 jours (-50)';
// Orders delivered and valid
$count_delivered = Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'orders`
WHERE `id_customer` = '.(int)$this->customer->id.'
AND `valid` = 1 AND `delivery_date` != 0
');
if ($count_delivered > 10) {
$this->fraud_score -= 100;
$this->fraud_report[] = 'Compte avec plus de 10 commandes valides expédiées (-100)';
}
elseif ($count_delivered > 5) {
$this->fraud_score -= 50;
$this->fraud_report[] = 'Compte avec plus de 5 commandes valides expédiées (-50)';
}
else {
// Old Orders
$count_oldorders = Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'orders`
WHERE `id_customer` = '.(int)$this->customer->id.'
AND `valid` = 1
AND `date_add` < DATE_SUB(NOW(), INTERVAL 30 DAY)
');
if($count_oldorders > 3) {
$this->fraud_score -= 50;
$this->fraud_report[] = 'Compte avec plus de 3 commandes valides de plus de 30 jours (-50)';
}
}
}
private function fraudInvite() {
private function fraudInvite()
{
$has_order = (bool) (int) Db::getInstance()->getValue('
SELECT `id_order`
FROM `'._DB_PREFIX_.'orders`
@ -282,7 +314,8 @@ class FraudCore {
}
public static function getReputationOrder($id_order) {
public static function getReputationOrder($id_order)
{
$id_cart = Order::getCartIdStatic($id_order);
return Db::getInstance()->getRow('
SELECT *
@ -292,7 +325,8 @@ class FraudCore {
}
public static function validOrder($id_order) {
public static function validOrder($id_order)
{
$id_cart = Order::getCartIdStatic($id_order);
$order = new Order($id_order);
@ -333,7 +367,8 @@ class FraudCore {
}
}
public static function cidr_match($ip, $range) {
public static function cidr_match($ip, $range)
{
list ($subnet, $bits) = explode('/', $range);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
@ -342,9 +377,8 @@ class FraudCore {
return ($ip & $mask) == $subnet;
}
public static function CartFraudConnexion(Cart $cart) {
public static function CartFraudConnexion(Cart $cart)
{
if (!Validate::isLoadedObject($cart)) {
return false;
}
@ -436,5 +470,4 @@ class FraudCore {
`ip_cart` = "'.Tools::getRemoteAddr().'"
');
}
}