Magento2 | PWA | GraphQL

Magento2 WhatsApp Order Notification Module with API Integration


In this post, How to Integrate WhatsApp Order Notifications in Magento 2 Using WaAPI

Let's start it by creating custom extension. 

Find Complete module files on Magelearn_OrderPopup

Create folder inside app/code/Magelearn/OrderPopup

Add registration.php file in it:
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Magelearn_OrderPopup',
    __DIR__
);
Add composer.json file in it:
{
    "name": "magelearn/module-orderpopup",
    "description": "Magento 2 WhatsApp order notification module",
    "type": "magento2-module",
    "license": "proprietary",
    "authors": [
        {
            "name": "Magelearn"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "psr-4": {
            "Magelearn\\OrderPopup\\": ""
        },
        "files": [
            "registration.php"
        ]
    }
}
Add etc/module.xml file in it:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Magelearn_OrderPopup">
        <sequence>
            <module name="Magento_Sales"/>
        </sequence>
    </module>
</config>
Add etc/adminhtml/system.xml to give admin configurations.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="magelearn_orderpopup"
                 translate="label"
                 sortOrder="900"
                 showInDefault="1"
                 showInWebsite="1"
                 showInStore="1">
            <label>WhatsApp Order Notification</label>
            <tab>sales</tab>
            <resource>Magelearn_OrderPopup::config</resource>
            <group id="whatsapp"
                   translate="label"
                   sortOrder="10"
                   showInDefault="1"
                   showInWebsite="1"
                   showInStore="1">
                <label>WhatsApp Settings</label>
                <field id="enabled"
                       translate="label"
                       type="select"
                       sortOrder="10"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>Enable WhatsApp Notification</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="auto_send"
                       translate="label"
                       type="select"
                       sortOrder="20"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>Send Automatically After Order Placement</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
                <field id="provider"
                       translate="label"
                       type="select"
                       sortOrder="30"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>WhatsApp Provider</label>
                    <source_model>Magelearn\OrderPopup\Model\Config\Source\Provider</source_model>
                </field>
                <field id="country_code"
                       translate="label"
                       type="text"
                       sortOrder="40"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>Default Country Code</label>
                    <comment>Example: 91 for India. Do not include +.</comment>
                </field>
                <field id="message_template"
                    translate="label"
                    type="textarea"
                    sortOrder="50"
                    showInDefault="1"
                    showInWebsite="1"
                    showInStore="1">
                    <label>Order Message Template</label>
                    <frontend_model>Magelearn\OrderPopup\Block\Adminhtml\System\Config\MessageTemplate</frontend_model>
                </field>
            </group>
            <group id="waapi"
                   translate="label"
                   sortOrder="20"
                   showInDefault="1"
                   showInWebsite="1"
                   showInStore="1">
                <label>WaAPI Configuration</label>
                <field id="instance_id"
                       translate="label"
                       type="text"
                       sortOrder="10"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>WaAPI Instance ID</label>
                </field>
                <field id="api_token"
                       translate="label"
                       type="obscure"
                       sortOrder="20"
                       showInDefault="1"
                       showInWebsite="1"
                       showInStore="1">
                    <label>WaAPI API Token</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>
As per highlighted code, we will add Model/Config/Source/Provider.php file.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Model\Config\Source;

use Magento\Framework\Data\OptionSourceInterface;

class Provider implements OptionSourceInterface
{
    public function toOptionArray(): array
    {
        return [
            [
                'value' => 'waapi',
                'label' => __('WaAPI'),
            ],
        ];
    }
}
And to display the Message Template Front-end Model class, we will add Block/Adminhtml/System/Config/MessageTemplate.php file.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Block\Adminhtml\System\Config;

use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;

class MessageTemplate extends Field
{
    /**
     * Render the message template field.
     */
    protected function _getElementHtml(
        AbstractElement $element
    ): string {
        $html = parent::_getElementHtml($element);

        $variables = [
            '{{customer_name}}' => 'Customer first name and last name',
            '{{increment_id}}' => 'Magento order number',
            '{{order_total}}' => 'Order grand total',
            '{{currency}}' => 'Order currency code, e.g. USD or CAD',
            '{{store_name}}' => 'Magento store name',
            '{{shipping_method}}' => 'Selected shipping method',
            '{{payment_method}}' => 'Selected payment method',
            '{{billing_address}}' => 'Customer billing address',
            '{{shipping_address}}' => 'Customer shipping address',
        ];

        $html .= '<div class="magelearn-message-template-help">';
        $html .= '<strong>Available Variables</strong>';
        $html .= '<p>Use the following variables in your message template. '
            . 'They will be automatically replaced with the order information '
            . 'when the message is sent.</p>';

        $html .= '<table class="admin__table-secondary">';
        $html .= '<thead>';
        $html .= '<tr>';
        $html .= '<th>Variable</th>';
        $html .= '<th>Description</th>';
        $html .= '</tr>';
        $html .= '</thead>';
        $html .= '<tbody>';

        foreach ($variables as $variable => $description) {
            $html .= '<tr>';
            $html .= '<td><code>'
                . $this->escapeHtml($variable)
                . '</code></td>';
            $html .= '<td>'
                . $this->escapeHtml($description)
                . '</td>';
            $html .= '</tr>';
        }

        $html .= '</tbody>';
        $html .= '</table>';
        $html .= '</div>';

        return $html;
    }
}
Add etc/acl.xml file.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Sales::sales">
                    <resource
                        id="Magelearn_OrderPopup::send_whatsapp"
                        title="Send WhatsApp Order Notification"
                        sortOrder="10"
                    />
                </resource>
                <resource
                    id="Magelearn_OrderPopup::config"
                    title="WhatsApp Order Notification Configuration"
                    sortOrder="20"
                />
            </resource>
        </resources>
    </acl>
</config>
Now Create your di.xml file inside etc and use plugin method to add button HTML code on sales order view page as well as API insterface for sending Whatsapp Messages.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Sales\Block\Adminhtml\Order\View">
        <plugin
            name="magelearn_orderpopup_order_view"
            type="Magelearn\OrderPopup\Plugin\Magento\Sales\Block\Adminhtml\Order\View"
            sortOrder="10"
        />
    </type>
    <preference
        for="Magelearn\OrderPopup\Api\WhatsAppServiceInterface"
        type="Magelearn\OrderPopup\Model\WhatsApp\WaApiService"
    />
</config>
First we will add Api/WhatsAppServiceInterface.php and define our sendMessage() function.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Api;

interface WhatsAppServiceInterface
{
    /**
     * Send WhatsApp message.
     *
     * @param string $phoneNumber
     * @param string $message
     * @return bool
     */
    public function sendMessage(
        string $phoneNumber,
        string $message,
        ?int $storeId = null
    ): bool;
}
And will add our Model class at Model/WhatsApp/WaApiService.php
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Model\WhatsApp;

use Magelearn\OrderPopup\Api\WhatsAppServiceInterface;
use Magelearn\OrderPopup\Model\Config;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\HTTP\Client\Curl;
use Psr\Log\LoggerInterface;

class WaApiService implements WhatsAppServiceInterface
{
    private const API_URL =
        'https://waapi.app/api/v1/instances/%s/client/action/send-message';

    public function __construct(
        private readonly Curl $curl,
        private readonly Config $config,
        private readonly LoggerInterface $logger
    ) {
    }

    /**
     * @inheritDoc
     */
    public function sendMessage(
        string $phoneNumber,
        string $message,
        ?int $storeId = null
    ): bool {
        $instanceId = $this->config->getWaApiInstanceId($storeId);
        $apiToken = $this->config->getWaApiToken($storeId);

        $this->logger->info(
            'WaAPI configuration loaded.',
            [
                'instance_id' => $instanceId,
                'token_length' => strlen($apiToken),
                'token_is_encrypted' => str_starts_with($apiToken, '0:'),
            ]
        );

        if ($instanceId === '' || $apiToken === '') {
            $this->logger->error(
                'WaAPI configuration is incomplete.'
            );

            throw new LocalizedException(
                __('WaAPI configuration is incomplete.')
            );
        }

        $chatId = $this->buildChatId($phoneNumber);

        $url = sprintf(self::API_URL, $instanceId);

        $payload = [
            'chatId' => $chatId,
            'message' => $message,
        ];

        try {
            $this->logger->info('WaAPI: before setHeaders.');
            $this->curl->setHeaders([
                'Authorization' => 'Bearer ' . $apiToken,
                'Content-Type' => 'application/json',
            ]);
            $this->logger->info('WaAPI: headers configured.');
            $this->curl->setOption(CURLOPT_TIMEOUT, 15);
            $this->logger->info('WaAPI: before POST.', [
                'url' => $url,
                'chat_id' => $chatId,
            ]);

            $this->curl->post(
                $url,
                json_encode($payload, JSON_THROW_ON_ERROR)
            );
            $this->logger->info('WaAPI: POST completed.');
            $status = $this->curl->getStatus();
            $response = $this->curl->getBody();

            $this->logger->info(
                'WaAPI response received.',
                [
                    'status' => $status,
                    'response' => $response,
                ]
            );

            if ($status < 200 || $status >= 300) {
                $this->logger->info(
                    'WaAPI request failed.',
                    [
                        'status' => $status,
                        'response' => $response,
                    ]
                );

                return false;
            }

            $responseData = json_decode($response, true, 512, JSON_THROW_ON_ERROR);

            return ($responseData['data']['status'] ?? '') === 'success';
        } catch (\Throwable $exception) {
            $$this->logger->info(
                'Unable to send WhatsApp message through WaAPI.',
                [
                    'exception_class' => get_class($exception),
                    'message' => $exception->getMessage(),
                    'file' => $exception->getFile(),
                    'line' => $exception->getLine(),
                ]
            );

            return false;
        }
    }

    private function buildChatId(string $phoneNumber): string
    {
        $phoneNumber = preg_replace('/\D+/', '', $phoneNumber);

        if ($phoneNumber === '') {
            throw new LocalizedException(
                __('Invalid WhatsApp phone number.')
            );
        }

        if (str_starts_with($phoneNumber, '0')) {
            $phoneNumber = ltrim($phoneNumber, '0');
        }

        $countryCode = preg_replace(
            '/\D+/',
            '',
            $this->config->getCountryCode()
        );

        if (
            $countryCode !== ''
            && !str_starts_with($phoneNumber, $countryCode)
        ) {
            $phoneNumber = $countryCode . $phoneNumber;
        }

        return $phoneNumber . '@c.us';
    }
}
Now create plugin file View.php inside app/code/Magelearn/OrderPopup/Plugin/Magento/Sales/Block/Adminhtml/Order
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Plugin\Magento\Sales\Block\Adminhtml\Order;

use Magento\Sales\Block\Adminhtml\Order\View as OrderView;

class View
{
    public function beforeSetLayout(
        OrderView $subject,
        $layout
    ): array {
        $subject->addButton(
            'sendorderwhatsapp',
            [
                'label' => __('Send WhatsApp'),
                'onclick' => 'require(["jquery"], function ($) {
                    $("#popup-modal").modal("openModal");
                });',
                'class' => 'action-default',
            ]
        );

        return [$layout];
    }
}
Now we will add the layout file sales_order_view.xml and will modify the Sales Order View Page.
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block
                class="Magelearn\OrderPopup\Block\Adminhtml\Order\ModalBox"
                name="magelearn.orderpopup.modal"
                template="Magelearn_OrderPopup::order/modalbox.phtml"
                after="-"
            />
        </referenceContainer>
    </body>
</page>
Now we will create a Block and template file as per the Layout.

First Lets create template file modalbox.phtml inside app/code/Magelearn/OrderPopup/view/adminhtml/templates/order/modalbox.phtml
<?php
/**
 * @var \Magelearn\OrderPopup\Block\Adminhtml\Order\ModalBox $block
 */
?>

<div id="popup-modal" style="display: none;">

    <div class="admin__fieldset">

        <div class="admin__field field">
            <label class="admin__field-label" for="whatsapp-phone">
                <span><?= $block->escapeHtml(__('Phone Number')) ?></span>
            </label>

            <div class="admin__field-control">
                <input
                    type="text"
                    id="whatsapp-phone"
                    name="phone"
                    class="admin__control-text"
                    value="<?= $block->escapeHtmlAttr($block->getOrderPhone()) ?>"
                />
            </div>
        </div>

        <div class="admin__field field">
            <label class="admin__field-label" for="whatsapp-message">
                <span><?= $block->escapeHtml(__('Message')) ?></span>
            </label>

            <div class="admin__field-control">
                <textarea
                    id="whatsapp-message"
                    name="message"
                    rows="8"
                    class="admin__control-textarea"
                ><?= $block->escapeHtml($block->getMessage()) ?></textarea>
            </div>
        </div>

    </div>

</div>

<script>
require([
    'jquery',
    'Magento_Ui/js/modal/modal',
    'mage/translate'
], function ($, modal) {
    'use strict';

    var options = {
        type: 'popup',
        responsive: true,
        innerScroll: true,
        title: $.mage.__('Send WhatsApp Message'),
        modalClass: 'magelearn-whatsapp-modal',
        buttons: [
            {
                text: $.mage.__('Send WhatsApp'),
                class: 'action-primary',
                click: function () {
                    var popup = this;

                    $.ajax({
                        url: '<?= $block->escapeUrl($block->getFormUrl()) ?>',
                        type: 'POST',
                        dataType: 'json',
                        data: {
                            form_key: window.FORM_KEY,
                            phone: $('#whatsapp-phone').val(),
                            message: $('#whatsapp-message').val()
                        },
                        showLoader: true
                    }).done(function (response) {
                        if (response.success) {
                            alert(response.message);
                            popup.closeModal();
                            return;
                        }

                        alert(response.message);
                    }).fail(function () {
                        alert(
                            $.mage.__(
                                'Unable to send the WhatsApp message.'
                            )
                        );
                    });
                }
            },
            {
                text: $.mage.__('Close'),
                class: '',
                click: function () {
                    this.closeModal();
                }
            }
        ]
    };

    modal(
        options,
        $('#popup-modal')
    );
});
</script>
Now, Create Block file ModalBox.php inside Block/Adminhtml/Order/
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Block\Adminhtml\Order;

use Magelearn\OrderPopup\Model\MessageTemplate;
use Magento\Backend\Block\Template;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\OrderRepositoryInterface;

class ModalBox extends Template
{
    private ?OrderInterface $order = null;

    public function __construct(
        Context $context,
        private readonly OrderRepositoryInterface $orderRepository,
        private readonly MessageTemplate $messageTemplate,
        private readonly RequestInterface $request,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    public function getInfo(): string
    {
        return (string) __('Send WhatsApp Order Notification');
    }

    public function getFormUrl(): string
    {
        return $this->getUrl(
            'orderpopup/order/order',
            [
                'order_id' => $this->getOrder()
                    ? (int) $this->getOrder()->getEntityId()
                    : 0,
            ]
        );
    }

    public function getOrder(): ?OrderInterface
    {
        if ($this->order !== null) {
            return $this->order;
        }

        $orderId = (int) $this->request->getParam('order_id');

        if ($orderId <= 0) {
            return null;
        }

        try {
            $this->order = $this->orderRepository->get($orderId);
        } catch (NoSuchEntityException) {
            return null;
        }

        return $this->order;
    }

    public function getOrderPhone(): string
    {
        $order = $this->getOrder();

        if (!$order) {
            return '';
        }

        $shippingAddress = $order->getShippingAddress();

        if ($shippingAddress && $shippingAddress->getTelephone()) {
            return (string) $shippingAddress->getTelephone();
        }

        $billingAddress = $order->getBillingAddress();

        if ($billingAddress && $billingAddress->getTelephone()) {
            return (string) $billingAddress->getTelephone();
        }

        return '';
    }

    public function getMessage(): string
    {
        $order = $this->getOrder();

        if (!$order) {
            return '';
        }

        return $this->messageTemplate->getMessage($order);
    }
}
Now as per the highlighted code above, to display the message template in the Modal Popup when clicking on "Send Whatsapp" button we will add Model/MessageTemplate.php file.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Model;

use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderAddressInterface;

class MessageTemplate
{
    public function __construct(
        private readonly Config $config
    ) {
    }

    public function getMessage(
        OrderInterface $order
    ): string {
        $storeId = (int) $order->getStoreId();

        $template = $this->config->getMessageTemplate($storeId);

        $customerName = trim(
            (string) $order->getCustomerFirstname()
            . ' '
            . (string) $order->getCustomerLastname()
        );

        return strtr(
            $template,
            [
                '{{customer_name}}' => $customerName,
                '{{increment_id}}' => (string) $order->getIncrementId(),
                '{{order_total}}' => (string) $order->getGrandTotal(),
                '{{currency}}' => (string) $order->getOrderCurrencyCode(),
                '{{store_name}}' => (string) $order->getStore()->getName(),
                '{{shipping_method}}' => $this->getShippingMethod($order),
                '{{payment_method}}' => $this->getPaymentMethod($order),
                '{{billing_address}}' => $this->getBillingAddress($order),
                '{{shipping_address}}' => $this->getShippingAddress($order),
            ]
        );
    }

    private function getShippingMethod(OrderInterface $order): string
    {
        return (string) $order->getShippingDescription();
    }

    private function getPaymentMethod(OrderInterface $order): string
    {
        $payment = $order->getPayment();

        if (!$payment) {
            return '';
        }

        return (string) $payment->getAdditionalInformation('method_title');
    }

    private function getBillingAddress(OrderInterface $order): string
    {
        $address = $order->getBillingAddress();

        if (!$address) {
            return '';
        }

        return $this->formatAddress($address);
    }

    private function getShippingAddress(OrderInterface $order): string
    {
        $address = $order->getShippingAddress();

        if (!$address) {
            return '';
        }

        return $this->formatAddress($address);
    }

    private function formatAddress(
        OrderAddressInterface $address
    ): string
    {
        $lines = [
            trim(
                (string) $address->getFirstname()
                . ' '
                . (string) $address->getLastname()
            ),
            ...$address->getStreet(),
            trim(
                (string) $address->getCity()
                . ', '
                . (string) $address->getRegion()
                . ' '
                . (string) $address->getPostcode()
            ),
            (string) $address->getCountryId(),
            (string) $address->getTelephone(),
        ];

        return implode(
            PHP_EOL,
            array_filter(
                $lines,
                static fn ($line): bool => trim((string) $line) !== ''
            )
        );
    }
}
Also here in block file getFormUrl() function, we have give a link for form action. Which call to route (controller) file.

So for that, Create routes.xml file inside etc/adminhtml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="orderpopup" frontName="orderpopup">
            <module name="Magelearn_OrderPopup"/>
        </route>
    </router>
</config>
Now Create, controller file Order.php inside Controller/Adminhtml/Order/
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Controller\Adminhtml\Order;

use Magelearn\OrderPopup\Model\WhatsApp\ServiceFactory;
use Magento\Backend\App\Action;
use Magento\Framework\App\Response\Http\FileFactory;
use Magento\Framework\Controller\Result\Json;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Controller\Result\RawFactory;
use Magento\Framework\Registry;
use Magento\Framework\Translate\InlineInterface;
use Magento\Framework\View\Result\LayoutFactory;
use Magento\Framework\View\Result\PageFactory;
use Magento\Sales\Api\OrderManagementInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Controller\Adminhtml\Order as AdminOrder;
use Psr\Log\LoggerInterface;

class Order extends AdminOrder
{
    public const ADMIN_RESOURCE = 'Magelearn_OrderPopup::send_whatsapp';

    public function __construct(
        Action\Context $context,
        Registry $coreRegistry,
        FileFactory $fileFactory,
        InlineInterface $translateInline,
        PageFactory $resultPageFactory,
        JsonFactory $resultJsonFactory,
        LayoutFactory $resultLayoutFactory,
        RawFactory $resultRawFactory,
        OrderManagementInterface $orderManagement,
        OrderRepositoryInterface $orderRepository,
        LoggerInterface $logger,
        private readonly ServiceFactory $serviceFactory
    ) {
        parent::__construct(
            $context,
            $coreRegistry,
            $fileFactory,
            $translateInline,
            $resultPageFactory,
            $resultJsonFactory,
            $resultLayoutFactory,
            $resultRawFactory,
            $orderManagement,
            $orderRepository,
            $logger
        );
    }

    public function execute(): Json
    {
        $resultJson = $this->resultJsonFactory->create();

        $order = $this->_initOrder();

        if (!$order) {
            return $resultJson->setData([
                'success' => false,
                'message' => __('Unable to load the order.'),
            ]);
        }

        $phone = trim(
            (string) $this->getRequest()->getParam('phone')
        );

        $message = trim(
            (string) $this->getRequest()->getParam('message')
        );

        if ($phone === '') {
            return $resultJson->setData([
                'success' => false,
                'message' => __('Please enter a phone number.'),
            ]);
        }

        if ($message === '') {
            return $resultJson->setData([
                'success' => false,
                'message' => __('Please enter a message.'),
            ]);
        }

        try {
            $service = $this->serviceFactory->create(
                (int) $order->getStoreId()
            );

            $success = $service->sendMessage(
                $phone,
                $message,
                (int) $order->getStoreId()
            );

            if (!$success) {
                return $resultJson->setData([
                    'success' => false,
                    'message' => __('Unable to send the WhatsApp message.'),
                ]);
            }

            return $resultJson->setData([
                'success' => true,
                'message' => __('WhatsApp message sent successfully.'),
            ]);
        } catch (\Throwable $exception) {
            $this->logger->error(
                'Unable to send manual WhatsApp order notification.',
                [
                    'order_id' => $order->getId(),
                    'exception' => $exception,
                ]
            );

            return $resultJson->setData([
                'success' => false,
                'message' => __('An error occurred while sending the message.'),
            ]);
        }
    }
}

Now as per the highlighted code above, we will add our Factory class at Model/WhatsApp/ServiceFactory.php
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Model\WhatsApp;

use Magelearn\OrderPopup\Api\WhatsAppServiceInterface;
use Magelearn\OrderPopup\Model\Config;
use Magento\Framework\Exception\LocalizedException;

class ServiceFactory
{
    public function __construct(
        private readonly Config $config,
        private readonly WaApiService $waApiService
    ) {
    }

    public function create(?int $storeId = null): WhatsAppServiceInterface
    {
        $provider = $this->config->getProvider($storeId);

        return match ($provider) {
            'waapi' => $this->waApiService,
            default => throw new LocalizedException(
                __('Unsupported WhatsApp provider: %1', $provider)
            ),
        };
    }
}
Also as per highlighted code above, we will add Model/Config.php file.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Store\Model\ScopeInterface;

class Config
{
    private const XML_PATH_ENABLED = 'magelearn_orderpopup/whatsapp/enabled';
    private const XML_PATH_AUTO_SEND = 'magelearn_orderpopup/whatsapp/auto_send';
    private const XML_PATH_PROVIDER = 'magelearn_orderpopup/whatsapp/provider';

    private const XML_PATH_WAAPI_INSTANCE_ID =
        'magelearn_orderpopup/waapi/instance_id';

    private const XML_PATH_WAAPI_API_TOKEN =
        'magelearn_orderpopup/waapi/api_token';

    private const XML_PATH_COUNTRY_CODE =
        'magelearn_orderpopup/whatsapp/country_code';

    private const XML_PATH_MESSAGE_TEMPLATE =
        'magelearn_orderpopup/whatsapp/message_template';

    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig,
        private readonly EncryptorInterface $encryptor
    ) {
    }

    public function isEnabled(?int $storeId = null): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLED,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    public function isAutoSendEnabled(?int $storeId = null): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_AUTO_SEND,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    public function getProvider(?int $storeId = null): string
    {
        return (string) $this->scopeConfig->getValue(
            self::XML_PATH_PROVIDER,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    public function getWaApiInstanceId(?int $storeId = null): string
    {
        return (string) $this->scopeConfig->getValue(
            self::XML_PATH_WAAPI_INSTANCE_ID,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    public function getWaApiToken(?int $storeId = null): string
    {
        $value = (string) $this->scopeConfig->getValue(
            self::XML_PATH_WAAPI_API_TOKEN,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );

        if ($value === '') {
            return '';
        }

        if (!str_starts_with($value, '0:')) {
            return $value;
        }

        return $this->encryptor->decrypt($value);
    }

    public function getCountryCode(?int $storeId = null): string
    {
        return (string) $this->scopeConfig->getValue(
            self::XML_PATH_COUNTRY_CODE,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    public function getMessageTemplate(?int $storeId = null): string
    {
        return (string) $this->scopeConfig->getValue(
            self::XML_PATH_MESSAGE_TEMPLATE,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }
}
Also to send the whatsapp message automatically after placing an order, we will add event observer.
For that, first add etc/events.xml file.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer
            name="magelearn_orderpopup_send_order_whatsapp"
            instance="Magelearn\OrderPopup\Observer\SendOrderWhatsApp"
        />
    </event>
</config>
And Observer file at Observer/SendOrderWhatsApp.php file.
<?php

declare(strict_types=1);

namespace Magelearn\OrderPopup\Observer;

use Magelearn\OrderPopup\Model\Config;
use Magelearn\OrderPopup\Model\WhatsApp\ServiceFactory;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magelearn\OrderPopup\Model\MessageTemplate;
use Psr\Log\LoggerInterface;

class SendOrderWhatsApp implements ObserverInterface
{
    public function __construct(
        private readonly Config $config,
        private readonly ServiceFactory $serviceFactory,
        private readonly MessageTemplate $messageTemplate,
        private readonly LoggerInterface $logger
    ) {
    }

    /**
     * @inheritDoc
     */
    public function execute(Observer $observer): void
    {
        /** @var OrderInterface|null $order */
        $order = $observer->getEvent()->getOrder();

        if (!$order || !$order->getEntityId()) {
            return;
        }

        $storeId = (int) $order->getStoreId();

        if (
            !$this->config->isEnabled($storeId)
            || !$this->config->isAutoSendEnabled($storeId)
        ) {
            return;
        }

        $phoneNumber = $this->getOrderPhoneNumber($order);

        if ($phoneNumber === '') {
            $this->logger->warning(
                'Unable to send WhatsApp order notification because phone number is missing.',
                [
                    'order_id' => $order->getEntityId(),
                ]
            );

            return;
        }

        $message = $this->messageTemplate->getMessage($order);

        try {
            $service = $this->serviceFactory->create($storeId);

            $service->sendMessage(
                $phoneNumber,
                $message,
                $storeId
            );
        } catch (\Throwable $exception) {
            $this->logger->error(
                'Unable to send automatic WhatsApp order notification.',
                [
                    'order_id' => $order->getEntityId(),
                    'exception' => $exception,
                ]
            );
        }
    }

    private function getOrderPhoneNumber(OrderInterface $order): string
    {
        $shippingAddress = $order->getShippingAddress();

        if ($shippingAddress && $shippingAddress->getTelephone()) {
            return (string) $shippingAddress->getTelephone();
        }

        $billingAddress = $order->getBillingAddress();

        if ($billingAddress && $billingAddress->getTelephone()) {
            return (string) $billingAddress->getTelephone();
        }

        return '';
    }
}
Now to display textare for Message Template and its information display properly add CSS file at view/adminhtml/web/css/source/_module.less

After adding all those files, New "Send WhatApp" button will be added on Sales order view page. 
On click on that button one modal window will open with Pre-filled customer Phone number and message template form.

And after clicking on "Send WhatsApp" button one message will be send on the added customer Phone number Whatsapp.






0 Comments On "Magento2 WhatsApp Order Notification Module with API Integration"

Back To Top