> ## Documentation Index
> Fetch the complete documentation index at: https://partner-integrations.voyado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom attributes

<Danger>
  When adding custom functionality to this extension, make sure to create your own plugin. Otherwise, you may interfere with the existing code and risk breaking it.
</Danger>

## How to add custom attributes to the Contact

To store custom attributes for a contact in Engage, these plugins can be used:

<AccordionGroup>
  <Accordion title="magento/app/code/Voyado/ExtendedVoyado/etc/di.xml">
    ```xml theme={null}
    <?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="Voyado\Magento2\Helper\ContactAttributesHelper">
    		<plugin name="Voyado_ExtendedVoyado_Plugin_Sdk_To_Voyado" type="Voyado\ExtendedVoyado\Plugin\Sdk\ToVoyado"/>
    	</type>
    </config>
    ```
  </Accordion>

  <Accordion title="magento/app/code/Voyado/ExtendedVoyado/Plugin/Sdk/ToVoyado.php">
    ```php theme={null}
    <?php
    namespace Voyado\ExtendedVoyado\Plugin\Sdk;

    use Magento\Customer\Api\Data\CustomerInterface;
    use Magento\Customer\Model\Customer;
    use Voyado\Magento2\Helper\ContactAttributesHelper as VoyadoContactAttributesHelper;

    class ToVoyado
    {
        /**
         * @SuppressWarnings(PHPMD.UnusedFormalParameter)
         */
        public function afterGetRequiredFieldsCustomer(
            VoyadoContactAttributesHelper $object,
            array $requiredFieldsCustomer
        ): array {
            return array_merge($requiredFieldsCustomer, ['married', 'region']);
        }

        /**
         * @param CustomerInterface|Customer $customer
         * @return \Swagger\Client\model\ContactAttributes
         * @SuppressWarnings(PHPMD.UnusedFormalParameter)
         */
        public function afterGetContactAttributeDataFromCustomer(
            VoyadoContactAttributesHelper $object,
            array $result,
            $customer
        ): array {
            $extraData = [
                'married' => 'MarriedExtraPropertyDummyValue',
                'region' => 'RegionExtraPropertyDummyValue',
            ];
            return array_merge($result, $extraData);
        }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## How to add custom data to the Order

This guide walks you through adding custom key-value pairs to the `extra_data` field in the `api/v2/orders` payload sent to Voyado. You do this with a Magento after-plugin — no modification to the Voyado extension source is needed.

### Before you start

* You need a working Voyado Adobe Commerce integration.
* All customizations must live in a **separate Adobe Commerce module** that depends on `Voyado_Magento2`. Do not edit the extension's files directly — your changes would be overwritten on the next Composer update.

### How extra data works

Every order exported to `api/v2/orders` includes an `extra_data` object. This is a **flat string-to-string map** — both keys and values must be strings. The extension populates it by default with shipping, billing, and payment details.

The extension calls `getExtraDataForOrder()` in `Voyado\Magento2\Helper\VoyadoHelper` to build this map. You extend it with an `afterGetExtraDataForOrder` plugin that receives the existing array and returns it with your additions.

<Warning>
  The Voyado API model types `extra_data` as `map[string,string]`. All values must be strings. If you pass a nested array or object, the serializer will cast it to the string `"Array"`, which is not what you want. To include structured data, **JSON-encode it into a string value** as shown below.
</Warning>

### How to create the module

<AccordionGroup>
  <Accordion title="1. Register the module">
    Create `app/code/VendorName/ExtendedVoyado/registration.php`:

    ```php theme={null}
    <?php

    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'VendorName_ExtendedVoyado',
        __DIR__
    );
    ```

    Replace `VendorName` with your own vendor name throughout this guide.
  </Accordion>

  <Accordion title="2. Declare the module and dependency">
    Create `app/code/VendorName/ExtendedVoyado/etc/module.xml`:

    ```xml theme={null}
    <?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="VendorName_ExtendedVoyado">
            <sequence>
                <module name="Voyado_Magento2" />
            </sequence>
        </module>
    </config>
    ```
  </Accordion>

  <Accordion title="3. Register the plugin">
    Create `app/code/VendorName/ExtendedVoyado/etc/di.xml`:

    ```xml theme={null}
    <?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="Voyado\Magento2\Helper\VoyadoHelper">
            <plugin name="VendorName_ExtendedVoyado_Plugin_AddExtraOrderData"
                    type="VendorName\ExtendedVoyado\Plugin\AddExtraOrderData" />
        </type>
    </config>
    ```
  </Accordion>

  <Accordion title="4. Implement the plugin">
    Create `app/code/VendorName/ExtendedVoyado/Plugin/AddExtraOrderData.php`.

    The plugin receives the existing `extra_data` array in `$result` — which already contains all the default fields like shipping address, billing address, and payment method. You add your field to this array and return it. Nothing else in the payload is affected.

    In this example, we read a custom order field called `discount_code` and add it as `discountCode` in the `extraData` object:

    ```php theme={null}
    <?php
    declare(strict_types=1);

    namespace VendorName\ExtendedVoyado\Plugin;

    use Voyado\Magento2\Helper\VoyadoHelper;

    class AddExtraOrderData
    {
        public function afterGetExtraDataForOrder(
            VoyadoHelper $subject,
            array $result,
            $order
        ): array {
            $discountCode = $order->getData('discount_code');

            if (!empty($discountCode)) {
                $result['discountCode'] = (string) $discountCode;
            }

            return $result;
        }
    }
    ```

    After this plugin runs, the `extraData` object in the `api/v2/orders` payload will contain your new field alongside all the existing fields:

    ```json theme={null}
    {
      "extraData": {
        "shipping_method": "DHL",
        "shipping_amount": "49.00",
        "customer_firstname": "John",
        "customer_lastname": "Doe",
        "billing_street": "Example Avenue 12",
        "billing_zipcode": "123 45",
        "billing_city": "Examplecity",
        "billing_country": "SE",
        "payment_method": "Kort",
        "discountCode": "WELCOME10"
      }
    }
    ```

    <Info>
      The `$result` parameter contains the full existing `extra_data` array built by the extension. By adding a key and returning the array, your field is merged into the same object. You are not creating a new object — you are extending the one that already exists.
    </Info>

    <Warning>
      All values in `extra_data` must be strings. The Voyado API model types this field as `map[string,string]`. If you pass a non-string value (e.g. an integer or array), cast it with `(string)` or use `json_encode()` for complex types.
    </Warning>
  </Accordion>

  <Accordion title="5. Enable the module and test">
    Run the standard Magento setup commands:

    ```bash theme={null}
    bin/magento module:enable VendorName_ExtendedVoyado
    bin/magento setup:upgrade
    bin/magento setup:di:compile
    bin/magento cache:flush
    ```

    Then place a test order and check the `voyado_magento2_status` table. Look at the **request** column for the order export — your new fields should appear in the `extraData` object of the JSON body.
  </Accordion>
</AccordionGroup>

<Accordion title="What the default extra data already contains">
  The extension populates these fields by default. Your plugin adds to this — it does not replace it.

  | Key                                                                                                                        | Source                                  |
  | :------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------- |
  | `website_code`                                                                                                             | Store ID                                |
  | `shipping_method`                                                                                                          | Order shipping description              |
  | `shipping_description`                                                                                                     | Order shipping description              |
  | `shipping_amount`                                                                                                          | Base shipping amount including tax      |
  | `customer_firstname`, `customer_middlename`, `customer_lastname`                                                           | Billing address                         |
  | `customer_telephonenumber`                                                                                                 | Billing address telephone               |
  | `billing_company`, `billing_street`, `billing_zipcode`, `billing_city`, `billing_country`                                  | Billing address                         |
  | `shipping_firstname`, `shipping_middlename`, `shipping_lastname`                                                           | Shipping address                        |
  | `shipping_telephonenumber`, `shipping_company`, `shipping_street`, `shipping_zipcode`, `shipping_city`, `shipping_country` | Shipping address                        |
  | `payment_method`                                                                                                           | Payment method config value             |
  | `shipping_track` or `shipping_track_number` + `shipping_track_url`                                                         | Shipment tracking (depending on config) |
</Accordion>

<Warning>
  Do not overwrite any of these default keys unless you specifically intend to replace their values. Your plugin receives the full existing array in `$result` — add to it, do not reassign it.
</Warning>

## Common mistakes

| Mistake                                         | What happens                                            | Fix                                                                   |
| ----------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------- |
| Passing an array as a value instead of a string | The value is serialized as `"Array"` in the API request | Use `json_encode()` to convert the array to a string                  |
| Reassigning `$result` to a new array            | All default extra\_data fields are lost                 | Always add keys to the existing `$result` and return it               |
| Forgetting `(string)` cast on non-string data   | Integer or float values may cause type mismatches       | Cast all values to `string`, or use `json_encode()` for complex types |
| Editing the Voyado extension source directly    | Changes are lost on the next `composer update`          | Always use a separate module with a plugin as shown above             |
