Order Status Sync between PrestaShop and External APIs
Challenge: Synchronizing prestaShop order status with external systems
Solution: Implement real-time status synchronization module to synchronize prestashop statuses
In the world of e-commerce, the synchronization of order statuses between a shop’s internal management system and external APIs can be very useful. This blog post is showing implementation of a PrestaShop module tailored for version 8.1.2.
PrestaShop’s Order Lifecycle
PrestaShop meticulously manages each order’s journey, from the moment it’s placed to the final stages of fulfillment. Each status, be it ‘Payment Accepted’, ‘Shipped’, or ‘Delivered’, signifies a critical juncture in the order’s lifecycle. However, keeping these statuses aligned with external systems (like fulfillment services or payment gateways) poses a unique set of challenges.
Utilizing the hookActionOrderStatusUpdate hook, the module translates PrestaShop’s statuses into a dialect understood by the external APIs. Once the module intercepts a status change, it crafting the payload for the external API.
Order Status Sync Module
When PrestaShop’s ‘Payment Accepted’ status is mapped to ‘ORDERED’ in the external system, it signifies that the order has been placed and acknowledged, a commitment that triggers a series of actions in the external system. Similarly, the transformation of ‘Preparation in Progress’ to ‘FULFILLED’ is more than a status update; it’s a declaration that the order is ready for the next phase, be it shipping or pickup.
Using presta hook
The actionOrderStatusUpdate hook is triggered whenever there is a change in the status of an order. This change can occur due to various reasons, such as the progression of an order’s processing state, updates in shipment status, or alterations in payment status. Whenever such a change occurs, PrestaShop notifies all registered modules by triggering this hook, allowing them to execute their custom code.
public function install()
{
if (extension_loaded('curl') == false)
{
$this->_errors[] = $this->l('You have to enable the cURL extension on your server to install this module');
return false;
}
return parent::install()
&& $this->registerHook('actionOrderStatusUpdate');
}
Secure Transmission
The use of HMAC (Hash-based Message Authentication Code) is a strong security method that makes sure the data sent is exactly the same when it arrives. It’s like sending a sealed letter that no one can open or change on the way.
Another way to keep the communication safe is by using an api_key, which acts like a special passcode for each request. The final choice of using HMAC, api_key, or both depends on the rules of the API you’re connecting to, ensuring the data is sent safely.
Synchronization Process
Once a relevant status change is confirmed, the function prepares to communicate with the external system. It constructs the API endpoint, combines it with the merchant’s base URL, and prepares the payload.
As the payload is sent, the function stands ready to handle the response. It logs the outcome, whether successful or otherwise, providing a clear trail of the synchronization process.
public function hookActionOrderStatusUpdate($params) {
$id_order = $params['id_order'];
$newOrderStatus = $params['newOrderStatus'];
$oldOrderStatus = $params['oldOrderStatus'];
if ($newOrderStatus->id == $oldOrderStatus->id) {
// If the status hasn't changed, do nothing
return;
}
$endpoint = '/v1/orders/fulfillment';
$method = 'POST';
$merchant_url = Tools::getValue('OA_API_BASE_URL', Configuration::get('OA_API_BASE_URL'));
$url = $merchant_url . $endpoint ;
/**
* OA statuses
*/
$osPreparation = Configuration::get('PS_OS_PREPARATION'); // Preparation in progress
$osShipped = Configuration::get('PS_OS_SHIPPING'); // Shipped
$osDelivered = Configuration::get('PS_OS_DELIVERED'); // Delivered
$osCanceled = Configuration::get('PS_OS_CANCELED'); // Canceled
$osPaymentAccepted = Configuration::get('PS_OS_PAYMENT'); // Payment accepted
// Mapping statuses
$status_mapping = array(
$osPaymentAccepted => 'ORDERED', // Assuming that 'Payment Accepted' means the order is placed
$osPreparation => 'FULFILLED', // Assuming that 'Preparation in progress' means the order is fulfilled and ready for shipping or pickup
$osShipped => 'SHIPPED',
$osDelivered => 'DELIVERED',
$osCanceled => 'CANCELLED_MERCHANT'
);
// Check if the changed status exists in our mapping
if (!isset($status_mapping[$newOrderStatus->id])) {
return;
}
/**
* Get transactionId
*/
$order = new Order((int) $id_order);
$order_reference = $order->reference;
$payments = $order->getOrderPaymentCollection();
$oaOrderId = null;
if ($payments && count($payments) > 0) {
// Get the transaction_id from the first payment
$payment = $payments[0];
$oaOrderId = $payment->transaction_id;
}
if (!$oaOrderId) {
return;
}
$order_data = array(
'oaOrderId' => $oaOrderId,
'shopOrderId' => $order_reference,
'status' => $status_mapping[$newOrderStatus->id],
'notes' => '',
);
$body = json_encode($order_data, JSON_UNESCAPED_SLASHES);
/**
* @TODO implement HMAC request signing
*/
$api_key = '';
$timestamp = (int) (microtime(true) * 1000);
$nonce = '';
$path = strtoupper($endpoint);
$authHeader = "hmac v1$".$api_key."$". $method ."$". $path ."$".$timestamp."$".$nonce;
$my_headers = array(
'Content-Type' => 'application/json',
'authorization' => $authHeader
);
try {
$client = new NativeHttpClient();
$response = $client->request(
'POST',
$url,
[
'headers' => $my_headers,
'body' => $body
]
);
/**
* Check error message
*/
$status_code = $response->getStatusCode();
$response_body = $response->getContent(false);
if ($status_code >= 200 && $status_code < 300) {
$this->ct_custom_log("Request successful. Response: " . $response_body);
} else {
$this->ct_custom_log("Request failed with status code $status_code. Response: " . $response_body);
}
} catch (\Exception $e) {
$this->ct_custom_log("An error occurred during the request: " . $e->getMessage());
}
}
Configuring the Logger
The ct_custom_log function, embedded within the PrestaShop module, is helping with events logging. Leveraging the power of Monolog, this function establishes a dedicated logging channel named ‘mydebug’, ensuring that every log is structured, traceable, and isolated from the general system logs.
private function ct_custom_log($message)
{
// Configure the logger
$logger = new \Monolog\Logger('mydebug');
$logPath = _PS_ROOT_DIR_.'/var/logs/mydebug.log'; // Define the log path
$logStreamHandler = new \Monolog\Handler\StreamHandler($logPath, \Monolog\Logger::DEBUG);
$logger->pushHandler($logStreamHandler);
$logger->debug($message);
}
Now you know how to synchronize prestashop order statuses
Consider the scenario of a customer eagerly tracking their shipment. With synchronized order statuses, the moment an order is marked as ‘Shipped’ in PrestaShop, the corresponding update is instantly reflected in the external shipping provider’s system. The customer receives timely notifications.
In a B2B context, synchronized order statuses can streamline supply chain operations. For instance, when an order status in PrestaShop changes to ‘Payment Accepted’, an ERP system immediately updates its inventory levels and financial records, ensuring real-time accuracy across all business functions.
If you need ecommerce optimization services feel free to contact us!