How to Create a Custom "Gift Wrapping" Module in Magento 2

How to Create a Custom "Gift Wrapping" Module in Magento 2

Adding a gift-wrapping feature to your Magento 2 store can enhance the shopping experience, especially during holidays and special occasions. While some extensions offer this functionality, building a custom module gives you full control over the design, pricing, and conditions. In this guide, we’ll walk through creating a simple yet effective gift-wrapping module from scratch.

Why Build a Custom Gift Wrapping Module?

Before diving into the code, let’s quickly discuss why you might want a custom solution:

  • Flexibility: Define your own rules (e.g., per-product, per-category, or cart-based wrapping).
  • Cost Control: Set fixed or percentage-based fees.
  • Unique Branding: Customize the UI to match your store’s theme.
  • No Dependency: Avoid third-party extensions that may conflict with other modules.

Step 1: Module Setup

First, create the basic module structure. Place it under app/code/Magefine/GiftWrapping.

1. Create registration.php:

<?php  
use Magento\Framework\Component\ComponentRegistrar;  

ComponentRegistrar::register(  
    ComponentRegistrar::MODULE,  
    'Magefine_GiftWrapping',  
    __DIR__  
);  

2. Define module.xml in etc/:

<?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="Magefine_GiftWrapping" setup_version="1.0.0">  
        <sequence>  
            <module name="Magento_Checkout"/>  
        </sequence>  
    </module>  
</config>  

Run bin/magento setup:upgrade to activate the module.

Step 2: Database Setup

We’ll store gift-wrapping options in a custom table. Create Setup/InstallSchema.php:

<?php  
namespace Magefine\GiftWrapping\Setup;  

use Magento\Framework\DB\Ddl\Table;  
use Magento\Framework\Setup\InstallSchemaInterface;  
use Magento\Framework\Setup\ModuleContextInterface;  
use Magento\Framework\Setup\SchemaSetupInterface;  

class InstallSchema implements InstallSchemaInterface  
{  
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)  
    {  
        $installer = $setup;  
        $installer->startSetup();  

        $table = $installer->getConnection()->newTable(  
            $installer->getTable('magefine_giftwrapping_options')  
        )->addColumn(  
            'option_id',  
            Table::TYPE_INTEGER,  
            null,  
            ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],  
            'Option ID'  
        )->addColumn(  
            'title',  
            Table::TYPE_TEXT,  
            255,  
            ['nullable' => false],  
            'Gift Wrapping Title'  
        )->addColumn(  
            'price',  
            Table::TYPE_DECIMAL,  
            '12,4',  
            ['nullable' => false],  
            'Price'  
        )->addColumn(  
            'is_active',  
            Table::TYPE_BOOLEAN,  
            null,  
            ['nullable' => false, 'default' => 1],  
            'Is Active'  
        );  

        $installer->getConnection()->createTable($table);  
        $installer->endSetup();  
    }  
}  

Step 3: Admin Configuration

Let admins manage wrapping options via the backend. Create etc/adminhtml/system.xml:

<?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>  
        <tab id="magefine" translate="label" sortOrder="100">  
            <label>Magefine</label>  
        </tab>  
        <section id="giftwrapping" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">  
            <label>Gift Wrapping</label>  
            <tab>magefine</tab>  
            <resource>Magefine_GiftWrapping::config</resource>  
            <group id="settings" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">  
                <label>General Settings</label>  
                <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">  
                    <label>Enable Gift Wrapping</label>  
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>  
                </field>  
            </group>  
        </section>  
    </system>  
</config>  

Step 4: Frontend Integration

Now, let’s add the gift-wrapping option to the checkout. Override the checkout_index_index.xml layout:

<?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>  
        <referenceBlock name="checkout.root">  
            <arguments>  
                <argument name="jsLayout" xsi:type="array">  
                    <item name="components" xsi:type="array">  
                        <item name="checkout" xsi:type="array">  
                            <item name="children" xsi:type="array">  
                                <item name="steps" xsi:type="array">  
                                    <item name="children" xsi:type="array">  
                                        <item name="shipping-step" xsi:type="array">  
                                            <item name="children" xsi:type="array">  
                                                <item name="shippingAddress" xsi:type="array">  
                                                    <item name="children" xsi:type="array">  
                                                        <item name="gift-wrapping" xsi:type="array">  
                                                            <item name="component" xsi:type="string">Magefine_GiftWrapping/js/view/gift-wrapping</item>  
                                                            <item name="sortOrder" xsi:type="string">100</item>  
                                                        </item>  
                                                    </item>  
                                                </item>  
                                            </item>  
                                        </item>  
                                    </item>  
                                </item>  
                            </item>  
                        </item>  
                    </item>  
                </argument>  
            </arguments>  
        </referenceBlock>  
    </body>  
</page>  

Step 5: JavaScript Component

Create view/frontend/web/js/view/gift-wrapping.js:

define([  
    'ko',  
    'uiComponent',  
    'Magento_Checkout/js/model/quote'  
], function (ko, Component, quote) {  
    'use strict';  

    return Component.extend({  
        defaults: {  
            template: 'Magefine_GiftWrapping/gift-wrapping'  
        },  
        isVisible: ko.observable(true),  
        initialize: function () {  
            this._super();  
        },  
        getWrappingOptions: function () {  
            // Fetch options via AJAX or predefined config  
            return [  
                { id: 1, title: 'Premium Wrapping', price: 5.00 },  
                { id: 2, title: 'Standard Wrapping', price: 2.50 }  
            ];  
        }  
    });  
});  

Step 6: Template File

Add view/frontend/web/template/gift-wrapping.html:

<div class="gift-wrapping-options" data-bind="visible: isVisible">  
    <h3>Gift Wrapping</h3>  
    <!-- ko foreach: getWrappingOptions() -->  
    <div class="option">  
        <input type="radio" name="gift_wrapping" data-bind="attr: { id: 'wrapping_' + id, value: id }" />  
        <label data-bind="attr: { for: 'wrapping_' + id }">  
            <span data-bind="text: title"></span> (+<span data-bind="text: price"></span>)  
        </label>  
    </div>  
    <!-- /ko -->  
</div>  

Step 7: Save Selection to Quote

Create an observer to save the selected option to the quote. Add etc/frontend/events.xml:

<?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="checkout_cart_save_after">  
        <observer name="magefine_giftwrapping_save" instance="Magefine\GiftWrapping\Observer\SaveGiftWrapping"/>  
    </event>  
</config>  

Then, create the observer:

<?php  
namespace Magefine\GiftWrapping\Observer;  

use Magento\Framework\Event\Observer;  
use Magento\Framework\Event\ObserverInterface;  

class SaveGiftWrapping implements ObserverInterface  
{  
    public function execute(Observer $observer)  
    {  
        $cart = $observer->getEvent()->getCart();  
        $wrappingOption = $cart->getQuote()->getGiftWrappingOption();  
        // Save to quote or order  
    }  
}  

Final Thoughts

This custom module gives you full control over gift-wrapping logic in Magento 2. You can extend it further by:

  • Adding per-product wrapping options.
  • Integrating with payment gateways for dynamic pricing.
  • Enabling gift messages alongside wrapping.

Need a pre-built solution? Check out Magefine’s Magento 2 extensions for optimized performance.