Reputation: 21
I'm currently developing a fraud detection module for PrestaShop 1.6. In this module, I need to make a call to an API just after the customer presses the button to confirm their order, but before the payment is submitted, regardless of payment gateway.
At first, after looking through all the available hooks in 1.6, I thought about using the actionValidateOrder hook to do this, the problem is that this hook is executed right after the payment is submitted/processed by the payment gateway, which is not what I'm looking for.
I also thought about using javascript to intercept the execution and then calling to a validation controller to perform, but it becomes way too specific for each gateway and the data flow between prestashop objects/ajax/javascript seems like it could turn out to be problematic.
I know you can create a custom hook, but every example I see on internet is about display hooks, none of it shows how to create a custom action hook.
This is what I was using in the actionValidateOrder hook
public function hookActionValidateOrder($params)
{
include_once(_PS_MODULE_DIR_.'bayonet/sdk/Paymethods.php');
$this->order = $params['order'];
$cart = $this->context->cart;
$address_delivery = new Address((int)$cart->id_address_delivery);
$address_invoice = new Address((int)$cart->id_address_invoice);
$state_delivery = new State((int)$address_delivery->id_state);
$country_delivery = new Country((int)$address_delivery->id_country);
$state_invoice = new State((int)$address_invoice->id_state);
$country_invoice = new Country((int)$address_invoice->id_country);
$customer = $this->context->customer;
$currency = $this->context->currency;
$products = $cart->getProducts();
$product_list = array();
foreach($products as $product)
{
$products_list[] = [
"product_id" => $product['id_product'],
"product_name" => $product['name'],
"product_price" => $product['price'],
"product_category" => $product['category']
];
}
$request = [
'channel' => 'ecommerce',
'consumer_name' => $customer->firstname.' '.$customer->lastname,
"consumer_internal_id" => $customer->id,
"transaction_amount" => $cart->getOrderTotal(),
"currency_code" => $currency->iso_code,
"telephone" => $address_invoice->phone,
"email" => $customer->email,
"payment_gateway" => $this->order->module,
"shipping_address" => [
"line_1" => $address_delivery->address1,
"line_2" => $address_delivery->address2,
"city" => $address_delivery->city,
"state" => $state_delivery->name,
"country" => convert_country_code($country_delivery->iso_code),
"zip_code" => $address_delivery->postcode
],
"billing_address" => [
"line_1" => $address_invoice->address1,
"line_2" => $address_invoice->address2,
"city" => $address_invoice->city,
"state" => $state_invoice->name,
"country" => convert_country_code($country_invoice->iso_code),
"zip_code" => $address_invoice->postcode
],
"products" => $products_list,
"order_id" => (int)$this->order->id
];
foreach ($paymentMethods as $key => $value) {
if ($this->order->module == $key)
{
$request['payment_method'] = $value;
if ($this->order->module == 'paypalmx')
$request['payment_gateway'] = 'paypal';
}
}
if (Configuration::get('BAYONET_API_MODE') == 0)
{
$this->bayonet = new BayonetClient([
'api_key' => Configuration::get('BAYONET_API_TEST_KEY'),
'version' => Configuration::get('BAYONET_API_VERSION')
]);
}
else if (Configuration::get('BAYONET_API_MODE') == 1)
{
$this->bayonet = new BayonetClient([
'api_key' => Configuration::get('BAYONET_API_LIVE_KEY_KEY'),
'version' => Configuration::get('BAYONET_API_VERSION')
]);
}
$this->bayonet->consulting([
'body' => $request,
'on_success' => function($response) {
$this->dataToInsert = array(
'id_cart' => $this->context->cart->id,
'order_no' => $this->order->id,
'status' => $response->decision,
'bayonet_tracking_id' => $response->bayonet_tracking_id,
'consulting_api' => 1,
'consulting_api_response' => json_encode(array(
'reason_code' => $response->reason_code,
'tracking_id' => $response->bayonet_tracking_id
)),
'is_executed' => 1,
);
Db::getInstance()->insert('bayonet', $this->dataToInsert);
if ($response->decision == 'decline')
{
$this->module = Module::getInstanceByName('bayonet');
Tools::redirect($this->context->link->getModuleLink($this->module->name,'rejected', array()));
}
},
'on_failure' => function($response) {
$this->dataToInsert = array(
'id_cart' => $this->context->cart->id,
'order_no' => $this->order->id,
'status' => $response->decision,
'bayonet_tracking_id' => $response->bayonet_tracking_id,
'consulting_api' => 0,
'consulting_api_response' => json_encode(array(
'reason_code' => $response->reason_code,
)),
'is_executed' => 1,
);
Db::getInstance()->insert('bayonet', $this->dataToInsert);
}
]);
}
And after detecting the issue with the hook, this is what I tried with JavaScript, which calls a validation page using ajax, this page has almost the same code as what it was in the hook
$('#form-pagar-mp').submit(function(event) {
if (lock == 1) {
params = {};
$.ajax({
url: url,
type: 'post',
data: params,
dataType: 'json',
processData: false,
success: function(data) {
if (data.error == 0) {
lock = 0;
}
else {
window.location.href = data.url;
}
}
});
}
});
I still think using a hook is the best option, but out of all available hooks, none of them meets my needs. Right now, I don't really know that else to try, that's why I need your help in how to approach this situation, any ideas are welcome and will be highly appreciated. Thank you.
Upvotes: 2
Views: 1804
Reputation: 4337
You can use a hook which executes before an order payment creation
public function hookActionObjectOrderPaymentAddBefore($params)
{
/** OrderPayment $orderPayment */
$orderPayment = $params['object'];
$cart = $this->context->cart;
// to stop order payment creation you need to redirect from this hook
}
Or before order is created
public function hookActionObjectOrderAddBefore($params)
{
/** Order $order */
$order = $params['object'];
$cart = $this->context->cart;
// to stop order creation you need to redirect from this hook
}
Upvotes: 0