Search

Jan 3, 2024

Magento 2: Create controller to get country by IP client via API free, store in local files for using in the next time

<?php
declare(strict_types=1);

namespace Lexim\Override\Controller\Currency;

use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\Http\Context as HttpContext;
use Magento\Framework\Controller\Result\Json;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\HTTP\PhpEnvironment\Request as HttpRequest;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Store\Model\StoreIsInactiveException;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Store\Api\StoreCookieManagerInterface;
use Magento\UrlRewrite\Model\UrlFinderInterface;
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;

/**
 * Class Change
 *
 * @package Lexim\Override\Controller\Currency
 */
class Change implements ActionInterface, HttpPostActionInterface
{
    /** @var RequestInterface */
    private $request;

    /** @var Curl */
    private $_curl;

    /** @var HttpRequest */
    private $httpRequest;

    /** @var JsonFactory */
    private $resultJsonFactory;

    /** @var StoreManagerInterface */
    private $storeManager;

    /** @var UrlFinderInterface */
    private $urlFinder;

    /** @var StoreRepositoryInterface */
    private $storeRepository;

    /** @var StoreCookieManagerInterface */
    private $storeCookieManager;

    /** @var HttpContext */
    private $httpContext;


    /**
     * @param RequestInterface $request
     * @param HttpContext $httpContext
     * @param JsonFactory $resultJsonFactory
     * @param Curl $curl
     * @param StoreManagerInterface $storeManager
     * @param StoreRepositoryInterface $storeRepository
     * @param StoreCookieManagerInterface $storeCookieManager
     * @param HttpRequest $httpRequest
     * @param UrlFinderInterface $urlFinder
     */
    public function __construct(
        RequestInterface            $request,
        HttpContext                 $httpContext,
        JsonFactory                 $resultJsonFactory,
        Curl                        $curl,
        StoreManagerInterface       $storeManager,
        StoreRepositoryInterface    $storeRepository,
        StoreCookieManagerInterface $storeCookieManager,
        HttpRequest                 $httpRequest,
        UrlFinderInterface          $urlFinder,
    )
    {
        $this->request = $request;
        $this->resultJsonFactory = $resultJsonFactory;
        $this->_curl = $curl;
        $this->storeManager = $storeManager;
        $this->storeRepository = $storeRepository;
        $this->storeCookieManager = $storeCookieManager;
        $this->httpContext = $httpContext;
        $this->httpRequest = $httpRequest;
        $this->urlFinder = $urlFinder;
    }

    /**
     * Switch Store by country IP
     *
     * @return Json
     * @throws NoSuchEntityException
     * @throws StoreIsInactiveException
     * @throws \Zend_Log_Exception
     */
    public function execute()
    {
        // TODO: Implement execute() method.
        $url = $this->request->getParam('url');
        $countryCode = $this->request->getParam('countryCode');

        // Response Data
        $curStoreCode = $this->storeManager->getStore()->getCode(); // english, french, us

        $data = [
            'oldUrl' => $url,
            'ip' => false,
            'switch' => false,
            'curStoreCode' => $curStoreCode,
            'curStore' => $curStoreCode === 'us' ? 'us' : 'ca',
            'targetStoreCode' => 'us',
            'apiWorked' => false,
            'countryCode' => '',
            'targetStore' => 'us'
        ];


        // Check country by IP if the country is null
        if (!$countryCode || $countryCode === '') {
            $ip = $this->httpRequest->getClientIp(true);
            $ip = explode(',', strval($ip));
            if (is_array($ip) && count($ip) > 0) {
                $ip = reset($ip);
            }
            if ($ip) {
                //$ip = '113.160.226.108'; // VN 113.160.226.108
                //$ip = '173.238.88.28';  // CA 204.41.127.133
                //$ip = '52.12.129.239'; // US 52.12.129.239
                $data['ip'] = $ip;

                $ipResponseArray = $this->_getCountryFromIp($ip);
                if ($ipResponseArray && is_array($ipResponseArray)) {
                    $data = array_merge($data, $ipResponseArray);
                }
            }
        }

        // Switch store
        if (in_array($data['countryCode'], ['CA', 'VN'])) { // in_array($countryCode, ['CA', 'VN']
            $data['targetStore'] = "ca";
            $data['targetStoreCode'] = 'english'; // CA_en
        }

        if ($data['curStore'] !== $data['targetStore'] && $data['targetStore'] === "ca") {
            $data['switch'] = true;

            if ($curStoreCode === 'french') { // Rewrite path url from French to US
                $pathRedirect = $this->getRequest()->getParam('path');
                $pageType = $this->getRequest()->getParam('pageType');
                $data['pageType'] = $pageType;

                $stores = $this->storeManager->getStores(true, true);

                if ($pageType === 'staticPage') { // cms static page
                    $rewrite = $this->urlFinder->findOneByData(
                        [
                            UrlRewrite::TARGET_PATH => $pathRedirect,
                            UrlRewrite::STORE_ID => $stores['french']->getId(), // 2 french, 1 eng, 4 us
                            UrlRewrite::REDIRECT_TYPE => 302
                        ]
                    );
                    if ($rewrite) {
                        $data['pathRedirect'] = $rewrite->getRequestPath();
                    }

                } else if ($pageType === 'another') { // another pages exclude home page
                    $rewrite = $this->urlFinder->findOneByData(
                        [
                            UrlRewrite::REQUEST_PATH => $pathRedirect,
                            UrlRewrite::STORE_ID => $stores['french']->getId(),
                        ]
                    );
                    if ($rewrite) {
                        $targetPath = $rewrite->getTargetPath();
                        if ($targetPath) {
                            $rewriteUS = $this->urlFinder->findOneByData(
                                [
                                    UrlRewrite::TARGET_PATH => $targetPath,
                                    UrlRewrite::STORE_ID => $stores['us']->getId(),
                                ]
                            );
                            if ($rewriteUS) {
                                $data['pathRedirect'] = $rewriteUS->getRequestPath();
                            }
                        }
                    }
                }
            }

            // Switch Store to English CA
            $data['targetStoreCode'] = 'english';
            $store = $this->storeRepository->getActiveStoreByCode('english');
            $this->httpContext->setValue('store', 'english', 'english');
            $this->storeCookieManager->setStoreCookie($store);
        }

        // response
        $result = $this->resultJsonFactory->create();

        return $result->setData($data);
    }

    /**
     * @param $ip
     *
     * @return array|false
     */
    private function _getCountryFromIp($ip)
    {
        if (!$ip) {
            return false;
        }

        $data = [
            'apiWorked' => false,
            'countryCode' => '',
            'apiUrl' => ''
        ];
        $ipFile = BP . '/var/ip/' . $ip;
        $_getCountryFromFile = false;

        // Use saved files
        if (file_exists($ipFile)) {
            $response = file_get_contents($ipFile);
            $response = json_decode($response, true);
            if (isset($response['countryCode'])) {
                $data['countryCode'] = $response['countryCode'];
                $data['apiUrl'] = 'fromIpFolder';
                $_getCountryFromFile = true;
            }
        }

        /**
         * Use API and prepare response data to the below format JSON before save to file
         * Format response: {"status":"success","countryCode":"CA","query":"173.238.88.28"}
         */
        if (!$_getCountryFromFile) {

            /**
             * API (open source): https://api.country.is/
             * Country is an geolocation API that finds a user's country and nothing else from their IP.
             * It works both with IPv4 and IPv6.
             * The public instance we run at api.country.is is free to use.
             * It does not require an API key or impose usage quotas.
             * https://api.country.is/113.160.226.108
             */
            $apiUrl = 'https://api.country.is/' . $ip;
            $this->_curl->addHeader('accept', 'application/json');
            // fix bug SSL certificate has expired
            $this->_curl->setOption(CURLOPT_SSL_VERIFYHOST, false);
            $this->_curl->setOption(CURLOPT_SSL_VERIFYPEER, false);
            $this->_curl->get($apiUrl);
            $body = $this->_curl->getBody();
            $response = json_decode($body, true);
            if (isset($response['country'])) {
                $data['apiWorked'] = true;
                $data['countryCode'] = $response['country'];
                $data['apiUrl'] = 'api.country.is';
                $json = '{"countryCode":"' . $response['country'] . '","ip":"' . $ip . '"}';
                $this->_saveIpResponse($ip, $json);

            } else {
                /**
                 * API: ip-api.com
                 * Free for non-commercial use, no API key required
                 * Limited to 45 HTTP requests per minute from an IP address
                 * The average response time of the API: under 50 milliseconds in most parts of the world.
                 */
                $apiUrl = 'http://ip-api.com/json/';
                $apiUrl .= $ip;
                $apiUrl .= '?fields=status,message,countryCode,query';
                $this->_curl->addHeader('accept', 'application/json');
                $this->_curl->get($apiUrl);
                $body = $this->_curl->getBody();
                $response = json_decode($body, true);
                if (isset($response['countryCode'])) {
                    $data['apiWorked'] = true;
                    $data['countryCode'] = $response['countryCode'];
                    $data['apiUrl'] = 'ip-api.com';
                    $json = '{"countryCode":"' . $response['countryCode'] . '","ip":"' . $ip . '"}';
                    $this->_saveIpResponse($ip, $json);

                } else {
                    /**
                     * API: https://ipapi.co/173.238.88.28/country_code  => 'CA'
                     * Free: 30k request per month, up to 1000 request per day
                     */
                    $apiUrl = 'https://ipapi.co/' . $ip . '/country_code';
                    $this->_curl->get($apiUrl);
                    $countryCode = trim($this->_curl->getBody());
                    if ($countryCode && $countryCode != 'Undefined') {
                        $data['apiWorked'] = true;
                        $data['countryCode'] = $countryCode;
                        $data['apiUrl'] = 'ipapi.co/IP/country_code';
                        $json = '{"countryCode":"' . $countryCode . '","ip":"' . $ip . '"}';
                        $this->_saveIpResponse($ip, $json);
                    }
                }
            }

        }

        return $data;
    }

    /**
     * @param string $ip
     * @param string $response
     *
     * @return false|int
     */
    private function _saveIpResponse(string $ip, string $response)
    {
        if (!$ip || !$response) {
            return false;
        }
        $folderPath = BP . '/var/ip/';
        // Check if the folder exists, create it if not
        if (!file_exists($folderPath)) {
            mkdir($folderPath, 0777, true);
        }
        $filePath = $folderPath . $ip;
        return file_put_contents($filePath, $response);
    }

}

No comments:

Post a Comment