Order Status Sync between PrestaShop and External APIs - createIT
Get a free advice now!

    Pick the topic
    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    Order Status Sync between PrestaShop and External APIs

    August 26, 2024
    Last update: September 26, 2024
    4 min read
    507
    0
    0
    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 order processing

    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!

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us