Creating a custom API and utilizing REST API for tax zones and rates CRUD operations in Magento 2 involves defining a set of functions to perform HTTP requests and responses. A custom REST API is created to manage custom data and fields. 

An API, or Application Programming Interface, acts as a middleman between a programmer and an application, allowing access to data. In Magento 2, default APIs are available, but one must create custom APIs or specific functionalities or data requirements beyond the default offerings. 

This article will cover different methods such as getTaxRates, updateTaxRate, deleteTaxRate, insertTaxRate, and loadTaxRateById, as well as different API request methods like GET, PUT, DELETE, and POST. 

6 Simple Steps to Create a Custom REST API in Magento 2

Here are the steps to Create a Custom REST API in Magento 2: 

Step 1

Create a “registration.php”

<?php
Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'Klizer_AlphanumericTax',
   __DIR__
);

Step 2

Create a “Module.xml” file and the file path app/code/Klizer/AlphanumericTax/etc/module.xml

<?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="Klizer_AlphanumericTax" setup_version="1.0.3">
       <sequence>
           <module name="Magento_Tax"/>
       </sequence>
   </module>   
</config>

Step 3

Now, create one more file “webapi.xml” and file path that has
app/code/Klizer/AlphanumericTax/etc/webapi.xml

<!-- tax rate api -->
   <route url="/V1/taxrates/get" method="GET">
       <service class="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" method="getTaxRates"/>
       <resources>
           <resource ref="Magento_Tax::manage_tax"/>
       </resources>
   </route>
   <route url="/V1/taxrates/update/" method="PUT">
       <service class="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" method="updateTaxRate"/>
       <resources>
           <resource ref="Magento_Tax::manage_tax"/>
       </resources>
   </route>
   <route url="/V1/taxrates/delete/:taxRateId" method="DELETE">
       <service class="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" method="deleteTaxRate"/>
       <resources>
           <resource ref="Magento_Tax::manage_tax"/>
       </resources>
   </route>
   <route url="/V1/taxrates/insert" method="POST">
       <service class="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" method="insertTaxRate"/>
       <resources>
           <resource ref="Magento_Tax::manage_tax"/>
       </resources>
   </route>
   <route url="/V1/taxrates/load/:taxRateId" method="GET">
       <service class="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" method="loadTaxRateById"/>
       <resources>
           <resource ref="Magento_Tax::manage_tax"/>
       </resources>
   </route>




</routes>

Step 4

Create “di.xml” file and file path has and the file path: 

app/code/Klizer/AlphanumericTax/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
     <preference for="Klizer\AlphanumericTax\Api\TaxRateManagementInterface" type="Klizer\AlphanumericTax\Model\Api\TaxRateManagement"/>


</config>

Step 5

Then create TaxRateManagementInterface.php and the file path: 

<?php


namespace Klizer\AlphanumericTax\Api;


interface TaxRateManagementInterface
{
   /**
    * Get tax rates
    *
    * @return array
    */
   public function getTaxRates();


   /**
    * GET for Post api
    * @param string[] $title
    * @return string
    */
   public function updateTaxRate();


   /**
    * Delete tax rate by ID
    *
    * @param int $taxRateId
    * @return bool
    */
   public function deleteTaxRate($taxRateId);


   /**
   * POST for test api
   * @param string[] $data
   * @return string
   */
   public function insertTaxRate($data);


   /**
    * Load tax rate by ID
    *
    * @param int $taxRateId
    * @return array
    */
   public function loadTaxRateById($taxRateId);
}

Step 6

The last step is to create TaxRateManagement.php and file path

app/code/Klizer/AlphanumericTax/Model/Api/TaxRateManagement.php

<?php


namespace Klizer\AlphanumericTax\Model\Api;


use Klizer\AlphanumericTax\Api\TaxRateManagementInterface;
use Magento\Tax\Model\Calculation\RateFactory;
use Psr\Log\LoggerInterface;


class TaxRateManagement implements TaxRateManagementInterface
{
   /**
    * @var RateFactory
    */
   protected $rateFactory;


   /**
    * @var LoggerInterface
    */
   protected $logger;


   /**
    * TaxRateManagement constructor.
    *
    * @param RateFactory $rateFactory
    */
   public function __construct(
       RateFactory $rateFactory,
       LoggerInterface $logger


   ) {
       $this->rateFactory = $rateFactory;
       $this->logger = $logger;
   }


   /**
    * @inheritDoc
    */
   public function getTaxRates()
   {
       try {
           $taxRates = $this->rateFactory->create()->getCollection()->getData();
           return ['status' => true, 'tax_rates' => $taxRates];
       } catch (\Exception $e) {
           return ['status' => false, 'message' => $e->getMessage()];
       }
   }


      /** * GET for Post api * * @param string[] $title * @return string[] */


      public function updateTaxRate()
      {
          $edit = file_get_contents("php://input");
          $data = json_decode($edit, true);
     
          $taxRateId = $data['tax_calculation_rate_id'];
     
          $taxRate = $this->rateFactory->create()->load($taxRateId);
          $taxid = $taxRate->getData('tax_calculation_rate_id');
         
          if (in_array($taxRateId,(array)$taxid)) {
              try {


                                 $existingRate = $this->rateFactory->create()
                   ->getCollection()
                   ->addFieldToFilter('code', $data['code'])
                   ->addFieldToFilter('tax_calculation_rate_id', ['neq' => $taxRateId])
                   ->getFirstItem();


               if ($existingRate->getId()) {
                   return ['status' => false, 'message' => 'Code already exists for another tax rate.'];
               }
                   $taxRate->load($taxRateId);
                   $taxRate->setTaxCountryId($data['tax_country_id'])->save();
                   $taxRate->setTaxRegionId($data['tax_region_id'])->save();
                   $taxRate->setTaxPostcode($data['tax_postcode'])->save(); 
                   $taxRate->setCode($data['code'])->save(); 
                   $taxRate->setRate($data['rate'])->save();
                   $taxRate->setZipIsRange($data['zip_is_range'])->save(); 
                   $taxRate->setZipFrom($data['zip_from'])->save(); 
                   $taxRate->setZipTo($data['zip_to'])->save();
                   $taxRate->setTaxIdentifierLabel($data['tax_identifier_label'])->save(); 
                   
                  $response = ['status' => true, 'message' => 'Tax rate updated successfully.'];
                  return $response;
              } catch (\Exception $e) {
                  $this->logger->error('Error in updateTaxRate: ' . $e->getMessage());
                  return ['status' => false, 'message' => $e->getMessage()];
              }
          } else {
              $this->logger->info('Tax rate not found for ID: ' . $taxRateId);
     
              return ['status' => false, 'message' => 'Tax rate does not exist.'];
          }
      }




   /**
    * @inheritDoc
    */
   public function deleteTaxRate($taxRateId)
   {
       try {
           $taxRate = $this->rateFactory->create()->load($taxRateId);
           if (!$taxRate->getId()) {
               return ['status' => false, 'message' => 'Tax rate not found.'];
           }


           $taxRate->delete();
           return ['status' => true, 'message' => 'Tax rate deleted successfully.'];
       } catch (\Exception $e) {
           return ['status' => false, 'message' => $e->getMessage()];
       }
   }




   /**
    * @inheritDoc
    */
   public function insertTaxRate($data)
   {


       try {
           $taxRate = $this->rateFactory->create();


           $taxRate->setTaxCountryId(!empty($data["tax_country_id"]) ? $data["tax_country_id"] : 'default_country_id');
           $taxRate->setTaxRegionId($data["tax_region_id"]);
           $taxRate->setTaxPostcode($data["tax_postcode"]);
           $taxRate->setCode($data["code"]);
           $taxRate->setRate($data["rate"]);
           $taxRate->setZipIsRange($data["zip_is_range"]);
           $taxRate->setZipFrom($data["zip_from"]);
           $taxRate->setZipTo($data["zip_to"]);
           $taxRate->setTaxIdentifierLabel($data["tax_identifier_label"]);


           $taxRate->save();


           $response = ['status' => true, 'message' => 'Tax rate inserted successfully.'];


           return $response;
       } catch (\Exception $e) {
           $this->logger->error('Error in insertTaxRate: ' . $e->getMessage());
           return ['status' => false, 'message' => $e->getMessage()];
       }




   }




   /**
    * @inheritDoc
    */
   public function loadTaxRateById($taxRateId)
   {
       try {
           $taxRate = $this->rateFactory->create()->load($taxRateId);


           if (!$taxRate->getId()) {
               return ['status' => false, 'message' => 'Tax rate not found.'];
           }


           return ['status' => true, 'tax_rate' => $taxRate->getData()];
       } catch (\Exception $e) {
           return ['status' => false, 'message' => $e->getMessage()];
       }
   }


}

4 API Methods Using POSTMAN

Afterward, we can use POSTMAN to send requests and receive responses. We can try each of the four methods individually, including GET, POST, PUT, and DELETE, to interact with the API and observe the corresponding responses.

getTaxRates – In this method, we can get all data for the tax-rated grid by using this API

1. Method: GET

Request: http://localhost/stenvens.com/rest/V1/taxrates/get

Output:

updateTaxRate – update the data by IDs and tax_calculation_rate_id is needed 

2. Method: PUT

Request:http://localhost/stenvens.com/rest/V1/taxrates/update/

Payload:

{

    “tax_calculation_rate_id”: “3”,

    “tax_country_id”: “CA”,

    “tax_region_id”: “0”,

    “tax_postcode”: “M0A 0A0-M9Z 9Z9”,

    “code”: “testing2222222”,

    “rate”: “15.0000”,

    “zip_is_range”: “1”,

    “zip_from”: “M0A 0A0”,

    “zip_to”: “M9Z 9Z9”,

    “tax_identifier_label”: “SK”

}

Output:

deleteTaxRate  – we need to remove the data by tax_calculation_rate_id

3. Method: DELETE

Request: http://localhost/stenvens.com/rest/V1/taxrates/delete/8

Output:

insertTaxRate   – we need to insert data by payload data in the grid 

4. Method: POST

Request:http://localhost/stenvens.com/rest/V1/taxrates/insert

Payload:

{

    "data":{

    "tax_calculation_rate_id": "164",

    "tax_country_id": "CA",

    "tax_region_id": "0",

    "tax_postcode": "M0A 0A0-M9Z 9Z9",

    "code": "testing",

    "rate": "13.0000",

    "zip_is_range": "1",

    "zip_from": "M0A 0A0",

    "zip_to": "M9Z 9Z9",

    "tax_identifier_label": "ON-HST"

    }

}

Output:


loadTaxRateById – we need to load the data by tax_calculation_rate_id

Method: GET

Request: http://localhost/stenvens.com/rest/V1/taxrates/load/7

Output:

You have successfully Implemented Custom Rest API in Magento 2 and you can also customize this code according to your need for fetching data using REST API.

Magento 2 REST API Authentication and User Access Levels

1. In Magento 2, you can access both frontend and backend functionalities by calling REST API endpoints. These requests use three types of authentication:

  • Token-Based Authentication: Uses tokens generated using a username and password to authenticate the requests.
  • Session-Based Authentication: Uses a Session ID generated by the server for request authentication.
  • OAuth-Based Authentication: Uses Authorization Tokens to validate the API requests.

Magento 2 REST API endpoints can also be categorized based on the type of user:

  • Guest User: Public API endpoints that require no authentication. (For example, get product information)
  • Admin User: Private API endpoints that require admin-level access. (For example, change product information)
  • Customer: Private API endpoints that require customer-level access. (For example, view previous orders)

Advantages of Custom REST API in Magento 2

  • Allows for real-time data access, enabling you to provide up-to-date 
  • Is easy to use and understand, with a simple and consistent interface that makes it easy to work with.
  • REST API allows for easy integration with third-party systems, making it easier to scale your business
  • Magento 2 is secure, with built-in authentication and authorization mechanisms, ensuring that only authorized users can access the data.
  • Magento 2 allows you to easily integrate with third-party services, such as payment gateways, shipping providers, and more.

Disadvantages of Custom REST API in Magento 2

  • We need to pass the data properly or we can face issues with the request and response.
  • We must follow the endpoint steps, as some APIs accept both labels and numbers, while others do not.
  • Implementing and managing a REST API can be complex, especially for developers who are not familiar with the technology.
  • If your application relies heavily on third-party services, any issues with those services can impact the functionality of your application.
  • Some APIs may have rate limits, which can restrict the number of requests that can be made within a certain period.

Conclusion

Creating a custom REST API in Magento 2 allows you to manage tax zones and rates efficiently using CRUD operations. With proper implementation and testing via tools like Postman, you can extend Magento’s core functionality to meet specific business needs.

Looking for expert help? Connect with a Magento Development Partner like Klizer. You can also explore our Magento Solutions to take your store to the next level.

Picture of Rathina Pranesh C

Rathina Pranesh C

Rathina Pranesh C is a Software Engineer at Klizer, specializing in Magento ecommerce solutions. With over 1.5 years of hands-on experience in the Magento platform, Rathina is dedicated to creating seamless online shopping experiences. His passion for coding and commitment to excellence drives them to continuously innovate and deliver top-notch solutions to enhance the field of ecommerce.
Scroll to Top