Magento 2 APIs offer developers and integrators the ability to integrate Magento 2 with external systems such as CRM, ERP, CMS, PIM, POS, shopping app, and more.

Magento 2 by default, does not include custom orders attributes in sales order API. When building an app or integrating your Magento 2 store with an external system to synchronize orders, or for any other purposes, you may have custom orders attributes that you want to include in order API, to use it further in your external systems (CRM, ERP, CMS, PIM, POS, etc).

In this tutorial, we provide you with a step-by-step guide to add a custom order attribute to order API.

We will use"my_custom_order_attribute" as our custom order attribute, and we assume that "my_custom_order_attribute" already exists in sales_order, and quote database tables.

Step 1. Declare The Custom Attribute

Create this file app/code/Vendor/ModuleOne/etc/extension_attributes.xml and add this code:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
    <attribute code="my_custom_order_attribute" type="string"/>
</extension_attributes>
</config>

We use type="string" because our custom order attribute is of type text. Feel free to change to your custom order attribute type.

Step 2. Declare The Plugin

Create this file app/code/Vendor/ModuleOne/etc/di.xml and add this code:

<?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\Api\OrderRepositoryInterface">
		<plugin name="order_custom_attribute" type="Vendor\ModuleOne\Plugin\Model\Order\AddCustomOrderAttribute" />
     </type>		
</config>

Step 3. Create The Plugin

Create this file app/code/Vendor/ModuleOne/Plugin/Model/Order/AddCustomOrderAttribute.php and add this code:

<?php

namespace Vendor\ModuleOne\Plugin\Model\Order;

use Magento\Sales\Api\Data\OrderSearchResultInterface;
use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Api\Data\OrderExtensionFactory;
use Magento\Sales\Api\Data\OrderInterface;

class AddCustomOrderAttribute
{
    /**
     * @var OrderFactory
     */
    private $orderFactory;

    /**
     * @var OrderExtensionFactory
     */
    private $orderExtensionFactory;

    /**
     * @param OrderExtensionFactory $extensionFactory
     * @param OrderFactory $orderFactory
     */
    public function __construct(
        OrderExtensionFactory $extensionFactory,
        OrderFactory $orderFactory
    ) {
        $this->orderExtensionFactory = $extensionFactory;
        $this->orderFactory = $orderFactory;
    }

    /**
     * Set "my_custom_order_attribute" to order data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderSearchResultInterface $searchResult
     *
     * @return OrderSearchResultInterface
     */
    public function setMyCustomOrderAttributeData(OrderInterface $order)
    {
        if ($order instanceof \Magento\Sales\Model\Order) {
            $myCustomOrderAttribute = $order->getMyCustomOrderAttribute();
        } else {
            $orderModel = $this->orderFactory->create();
            $orderModel->load($order->getId());
            $myCustomOrderAttribute = $orderModel->getMyCustomOrderAttribute();
        }

        $extensionAttributes = $order->getExtensionAttributes();
        $orderExtensionAttributes = $extensionAttributes ? $extensionAttributes
            : $this->orderExtensionFactory->create();
            
        $orderExtensionAttributes->setMyCustomOrderAttribute($myCustomOrderAttribute);
        
        $order->setExtensionAttributes($orderExtensionAttributes);
    }
    
    /**
     * Add "my_custom_order_attribute" extension attribute to order data object
     * to make it accessible in API data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderSearchResultInterface $searchResult
     *
     * @return OrderSearchResultInterface
     */
    public function afterGetList(
        OrderRepositoryInterface $subject,
        OrderSearchResultInterface $orderSearchResult
    ) {
        foreach ($orderSearchResult->getItems() as $order) {
            $this->setMyCustomOrderAttributeData($order);
        }
        return $orderSearchResult;
    }

    /**
     * Add "my_custom_order_attribute" extension attribute to order data object
     * to make it accessible in API data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderInterface $order
     *
     * @return OrderInterface
     */
    public function afterGet(
        OrderRepositoryInterface $subject,
        OrderInterface $resultOrder
    ) {
        $this->setMyCustomOrderAttributeData($resultOrder);
        return $resultOrder;
    }
}
  • replace: Vendor\ModuleOne with your vendor and module name
  • replace "my_custom_order_attribute" with your custom order attribute
  • replace setMyCustomOrderAttribute and getMyCustomOrderAttribute with your custom order attribute set/get methods

Step 4. Empty Generated Folder

Make sure to empty the generated folder under your Magento 2 root directory before testing.

Step 5. Testing

We will use:

  • order_id is: 6
  • access Token: fikpb8s2as8gt8mjre2lk3mgi38cfnpz
  • base URL: mydomain.com

From the command line, we make an API call:

curl -X GET "http://mydomain.com/index.php/rest/V1/orders/6" -H "Authorization: Bearer fikpb8s2as8gt8mjre2lk3mgi38cfnpz"

The custom order attribute "my_custom_order_attribute" appear in the order API response, under "extension_attributes" section

{
  ...
  "extension_attributes": {
   ...
    "my_custom_order_attribute": "MY CUSTOM ORDER ATTRIBUTE DATA"
  }
}

Here is the complete order API response data:

{
  "base_currency_code": "USD",
  "base_discount_amount": 0,
  "base_grand_total": 53.71,
  "base_discount_tax_compensation_amount": 0,
  "base_shipping_amount": 5,
  "base_shipping_discount_amount": 0,
  "base_shipping_discount_tax_compensation_amnt": 0,
  "base_shipping_incl_tax": 5,
  "base_shipping_tax_amount": 0,
  "base_subtotal": 45,
  "base_subtotal_incl_tax": 48.71,
  "base_tax_amount": 3.71,
  "base_total_due": 53.71,
  "base_to_global_rate": 1,
  "base_to_order_rate": 1,
  "billing_address_id": 12,
  "created_at": "2019-11-01 09:43:37",
  "customer_dob": "1973-12-15 00:00:00",
  "customer_email": "[email protected]",
  "customer_firstname": "Veronica",
  "customer_gender": 2,
  "customer_group_id": 1,
  "customer_id": 1,
  "customer_is_guest": 0,
  "customer_lastname": "Costello",
  "customer_note_notify": 1,
  "discount_amount": 0,
  "entity_id": 6,
  "global_currency_code": "USD",
  "grand_total": 53.71,
  "discount_tax_compensation_amount": 0,
  "increment_id": "000000006",
  "is_virtual": 0,
  "order_currency_code": "USD",
  "protect_code": "9ecf8fbe586a971550177972c818532e",
  "quote_id": 11,
  "remote_ip": "172.17.0.1",
  "shipping_amount": 5,
  "shipping_description": "Flat Rate - Fixed",
  "shipping_discount_amount": 0,
  "shipping_discount_tax_compensation_amount": 0,
  "shipping_incl_tax": 5,
  "shipping_tax_amount": 0,
  "state": "new",
  "status": "pending",
  "store_currency_code": "USD",
  "store_id": 1,
  "store_name": "Main Website\nMain Website Store\nDefault Store View",
  "store_to_base_rate": 0,
  "store_to_order_rate": 0,
  "subtotal": 45,
  "subtotal_incl_tax": 48.71,
  "tax_amount": 3.71,
  "total_due": 53.71,
  "total_item_count": 1,
  "total_qty_ordered": 1,
  "updated_at": "2019-11-01 09:43:41",
  "weight": 0,
  "items": [
    {
      "amount_refunded": 0,
      "base_amount_refunded": 0,
      "base_discount_amount": 0,
      "base_discount_invoiced": 0,
      "base_discount_tax_compensation_amount": 0,
      "base_original_price": 45,
      "base_price": 45,
      "base_price_incl_tax": 48.71,
      "base_row_invoiced": 0,
      "base_row_total": 45,
      "base_row_total_incl_tax": 48.71,
      "base_tax_amount": 3.71,
      "base_tax_invoiced": 0,
      "created_at": "2019-11-01 09:43:38",
      "discount_amount": 0,
      "discount_invoiced": 0,
      "discount_percent": 0,
      "free_shipping": 0,
      "discount_tax_compensation_amount": 0,
      "is_qty_decimal": 0,
      "is_virtual": 0,
      "item_id": 6,
      "name": "Push It Messenger Bag",
      "no_discount": 0,
      "order_id": 6,
      "original_price": 45,
      "price": 45,
      "price_incl_tax": 48.71,
      "product_id": 14,
      "product_type": "simple",
      "qty_canceled": 0,
      "qty_invoiced": 0,
      "qty_ordered": 1,
      "qty_refunded": 0,
      "qty_shipped": 0,
      "quote_item_id": 21,
      "row_invoiced": 0,
      "row_total": 45,
      "row_total_incl_tax": 48.71,
      "row_weight": 0,
      "sku": "24-WB04",
      "store_id": 1,
      "tax_amount": 3.71,
      "tax_invoiced": 0,
      "tax_percent": 8.25,
      "updated_at": "2019-11-01 09:43:38"
    }
  ],
  "billing_address": {
    "address_type": "billing",
    "city": "Calder",
    "country_id": "US",
    "customer_address_id": 1,
    "email": "[email protected]",
    "entity_id": 12,
    "firstname": "Veronica",
    "lastname": "Costello",
    "parent_id": 6,
    "postcode": "49628-7978",
    "region": "Michigan",
    "region_code": "MI",
    "region_id": 33,
    "street": [
      "6146 Honey Bluff Parkway"
    ],
    "telephone": "(555) 229-3326"
  },
  "payment": {
    "account_status": null,
    "additional_information": [
      "Check / Money order"
    ],
    "amount_ordered": 53.71,
    "base_amount_ordered": 53.71,
    "base_shipping_amount": 5,
    "cc_last4": null,
    "entity_id": 6,
    "method": "checkmo",
    "parent_id": 6,
    "shipping_amount": 5
  },
  "status_histories": [],
  "extension_attributes": {
    "shipping_assignments": [
      {
        "shipping": {
          "address": {
            "address_type": "shipping",
            "city": "Calder",
            "country_id": "US",
            "customer_address_id": 1,
            "email": "[email protected]",
            "entity_id": 11,
            "firstname": "Veronica",
            "lastname": "Costello",
            "parent_id": 6,
            "postcode": "49628-7978",
            "region": "Michigan",
            "region_code": "MI",
            "region_id": 33,
            "street": [
              "6146 Honey Bluff Parkway"
            ],
            "telephone": "(555) 229-3326"
          },
          "method": "flatrate_flatrate",
          "total": {
            "base_shipping_amount": 5,
            "base_shipping_discount_amount": 0,
            "base_shipping_discount_tax_compensation_amnt": 0,
            "base_shipping_incl_tax": 5,
            "base_shipping_tax_amount": 0,
            "shipping_amount": 5,
            "shipping_discount_amount": 0,
            "shipping_discount_tax_compensation_amount": 0,
            "shipping_incl_tax": 5,
            "shipping_tax_amount": 0
          }
        },
        "items": [
          {
            "amount_refunded": 0,
            "base_amount_refunded": 0,
            "base_discount_amount": 0,
            "base_discount_invoiced": 0,
            "base_discount_tax_compensation_amount": 0,
            "base_original_price": 45,
            "base_price": 45,
            "base_price_incl_tax": 48.71,
            "base_row_invoiced": 0,
            "base_row_total": 45,
            "base_row_total_incl_tax": 48.71,
            "base_tax_amount": 3.71,
            "base_tax_invoiced": 0,
            "created_at": "2019-11-01 09:43:38",
            "discount_amount": 0,
            "discount_invoiced": 0,
            "discount_percent": 0,
            "free_shipping": 0,
            "discount_tax_compensation_amount": 0,
            "is_qty_decimal": 0,
            "is_virtual": 0,
            "item_id": 6,
            "name": "Push It Messenger Bag",
            "no_discount": 0,
            "order_id": 6,
            "original_price": 45,
            "price": 45,
            "price_incl_tax": 48.71,
            "product_id": 14,
            "product_type": "simple",
            "qty_canceled": 0,
            "qty_invoiced": 0,
            "qty_ordered": 1,
            "qty_refunded": 0,
            "qty_shipped": 0,
            "quote_item_id": 21,
            "row_invoiced": 0,
            "row_total": 45,
            "row_total_incl_tax": 48.71,
            "row_weight": 0,
            "sku": "24-WB04",
            "store_id": 1,
            "tax_amount": 3.71,
            "tax_invoiced": 0,
            "tax_percent": 8.25,
            "updated_at": "2019-11-01 09:43:38"
          }
        ]
      }
    ],
    "payment_additional_info": [
      {
        "key": "method_title",
        "value": "Check / Money order"
      }
    ],
    "applied_taxes": [
      {
        "code": "US-MI-*-Rate 1",
        "title": "US-MI-*-Rate 1",
        "percent": 8.25,
        "amount": 3.71,
        "base_amount": 3.71
      }
    ],
    "item_applied_taxes": [
      {
        "type": "product",
        "item_id": 6,
        "applied_taxes": [
          {
            "code": "US-MI-*-Rate 1",
            "title": "US-MI-*-Rate 1",
            "percent": 8.25,
            "amount": 3.71,
            "base_amount": 3.71
          }
        ]
      }
    ],
    "converting_from_quote": true,
    "my_custom_order_attribute": "MY CUSTOM ORDER ATTRIBUTE DATA"
  }
}

Conclusion

We hope you find this Magento 2 Order API tutorial helpful. Feel free to share this tutorial.

If you have any issues or questions in adding custom order attributes to order API in Magento 2, feel free to let us know in the comment field below.

Looking to adjust the code for your specific requirement? feel free to contact us

Tags: magento 2 api api