So, We have 3 environments: development, staging and production.

.gitignore file includes "vendor" folder and it has been working fine for us fine. Once We pull changes in Staging We would just run:

composer install
php bin/magento setup:upgrade && php bin/magento setup:di:compile && php bin/magento setup:static-content:deploy && php bin/magento c:enable && php bin/magento c:c

The issue is: We had to customize a third party extension installed by composer (under "vendor" folder) and this path is ignored by Git. What should I do to keep track of this changes and deploy them to staging server?

I could "git add -f " but would like your opinion on best practices.

EDIT: I've tried to follow instructions as per this reply without success: https://magento.stackexchange.com/a/282689/54768

Basically I installed a module using composer. I just need to edit one file (actually a few lines in a specific function):

File location: /magento2/vendor/coingate/magento2-plugin/Model/Payment.php

So, I created following module:

Directory location: /magento2/app/code/CCHY/OverrideCoingate/





    "name": "cchy/overridecoingate",
    "description": "",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "coingate/magento2-plugin": "null",

        "magento/magento-composer-installer": "*"
    "suggest": {

    "type": "magento2-module",
    "version": "1.0.0",
    "license": [

    "autoload": {
        "files": [
        "psr-4": {
            "CCHY\\OverrideCoingate\\": ""
    "extra": {
        "map": [


<?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="Coingate\Merchant\Model\Payment" type="CCHY\OverrideCoingate\Model\Payment" />


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <module name="CCHY_OverrideCoingate" setup_version="1.0.0">
            <module name="CoinGate_Merchant"/>

Model/Payment.php: (this is the original file. I just changed namespace and the portions in BOLD inside function getCoinGateRequest)

 * CoinGate payment method model
 * @category    CoinGate
 * @package     CoinGate_Merchant
 * @author      CoinGate
 * @copyright   CoinGate (https://coingate.com)
 * @license     https://github.com/coingate/magento2-plugin/blob/master/LICENSE The MIT License (MIT)
namespace CCHY\OverrideCoingate\Model;

use CoinGate\CoinGate;
use CoinGate\Merchant as CoinGateMerchant;
use Magento\Directory\Model\CountryFactory;
use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Response\Http;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Module\ModuleListInterface;
use Magento\Framework\Registry;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Framework\UrlInterface;
use Magento\Payment\Helper\Data;
use Magento\Payment\Model\Method\AbstractMethod;
use Magento\Payment\Model\Method\Logger;
use Magento\Sales\Model\Order;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Sales\Api\OrderManagementInterface;

class Payment extends AbstractMethod
    const COINGATE_MAGENTO_VERSION = '1.2.6';
    const CODE = 'coingate_merchant';

    protected $_code = 'coingate_merchant';

    protected $_isInitializeNeeded = true;

    protected $urlBuilder;
    protected $coingate;
    protected $storeManager;
    protected $orderManagement;

     * @param Context $context
     * @param Registry $registry
     * @param ExtensionAttributesFactory $extensionFactory
     * @param AttributeValueFactory $customAttributeFactory
     * @param Data $paymentData
     * @param ScopeConfigInterface $scopeConfig
     * @param Logger $logger
     * @param CoinGateMerchant $coingate
     * @param UrlInterface $urlBuilder
     * @param StoreManagerInterface $storeManager
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param OrderManagementInterface $orderManagement
     * @param array $data
     * @internal param ModuleListInterface $moduleList
     * @internal param TimezoneInterface $localeDate
     * @internal param CountryFactory $countryFactory
     * @internal param Http $response
    public function __construct(
        Context $context,
        Registry $registry,
        ExtensionAttributesFactory $extensionFactory,
        AttributeValueFactory $customAttributeFactory,
        Data $paymentData,
        ScopeConfigInterface $scopeConfig,
        Logger $logger,
        UrlInterface $urlBuilder,
        StoreManagerInterface $storeManager,
        OrderManagementInterface $orderManagement,
        CoinGateMerchant $coingate,
        array $data = [],
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null

    ) {

        $this->urlBuilder = $urlBuilder;
        $this->storeManager = $storeManager;
        $this->orderManagement = $orderManagement;
        $this->coingate = $coingate;

            'environment' => $this->getConfigData('sandbox_mode') ? 'sandbox' : 'live',
            'auth_token'  => $this->getConfigData('api_auth_token'),
            'user_agent'  => 'CoinGate - Magento 2 Extension v' . self::COINGATE_MAGENTO_VERSION

     * @param Order $order
     * @return array
    public function getCoinGateRequest(Order $order)
        $token = substr(md5(rand()), 0, 32);

        $payment = $order->getPayment();
        $payment->setAdditionalInformation('coingate_order_token', $token);

        $description = [];
        foreach ($order->getAllItems() as $item) {
            $description[] = number_format($item->getQtyOrdered(), 0) . ' × ' . $item->getName();

        $params = [
            'order_id' => $order->getIncrementId(),
            'price_amount' => number_format($order->**getBaseGrandTotal()**, 2, '.', ''),
            'price_currency' => $order->**getBaseOrderCurrencyCode(),**
            'receive_currency' => $this->getConfigData**('base_currency')**,
            'callback_url' => ($this->urlBuilder->getUrl('coingate/payment/callback') .
               '?token=' . $payment->getAdditionalInformation('coingate_order_token')),
            'cancel_url' => $this->urlBuilder->getUrl('coingate/payment/cancelOrder'),
            'success_url' => $this->urlBuilder->getUrl('coingate/payment/returnAction'),
            'title' => $this->storeManager->getWebsite()->getName(),
            'description' => join($description, ', '),
            'token' => $payment->getAdditionalInformation('coingate_order_token')

        $cgOrder = \CoinGate\Merchant\Order::create($params);

        if ($cgOrder) {
            return [
                'status' => true,
                'payment_url' => $cgOrder->payment_url
        } else {
            return [
                'status' => false

     * @param Order $order
    public function validateCoinGateCallback(Order $order)

        try {
            if (!$order || !$order->getIncrementId()) {
                $request_order_id = (filter_input(INPUT_POST, 'order_id')
                    ? filter_input(INPUT_POST, 'order_id') : filter_input(INPUT_GET, 'order_id')

                throw new \Exception('Order #' . $request_order_id . ' does not exists');

            $payment = $order->getPayment();
            $get_token = filter_input(INPUT_GET, 'token');
            $token1 = $get_token ? $get_token : '';
            $token2 = $payment->getAdditionalInformation('coingate_order_token');

            if ($token2 == '' || $token1 != $token2) {
                throw new \Exception('Tokens do match.');

            $request_id = (filter_input(INPUT_POST, 'id')
                ? filter_input(INPUT_POST, 'id') :  filter_input(INPUT_GET, 'id'));
            $cgOrder = \CoinGate\Merchant\Order::find($request_id);

            if (!$cgOrder) {
                throw new \Exception('CoinGate Order #' . $request_id . ' does not exist');

            if ($cgOrder->status == 'paid') {
            } elseif (in_array($cgOrder->status, ['invalid', 'expired', 'canceled', 'refunded'])) {

        } catch (\Exception $e) {

No matter what I do after setup:upgrade , setup:di:compile and c:f it will use the original file insider vendor folder.

  • For better use of that 3rd party extension, you have to run for e.g. "composer require mailchimp/mc-magento2:102.3.36" for that extension in each environments. Commented Jul 19, 2019 at 13:06

3 Answers 3


First of all: do not edit any vendor files. If you want to change anything, do it by custom extensions. If you edit vendor files in any way, you will not have possibility to easy udpate Magento Core in the future. If your problems are environment related: be sure to have all packages locked on specific version and your PHP version is same on all envs (because some packages have few versions for different PHP version).

However, you can use diff files and run them (automatically) as patches, just after composer install specific package. You need to install cweagans/composer-patches package to make it work. Just read about it here: https://github.com/cweagans/composer-patches.

For example:


  "extra": {
    "magento-force": "override",
    "composer-exit-on-patch-failure": true,
    "patches": {
      "magento/framework": {
        "PRODSECBUG-2198": "patches/PRODSECBUG-2198/PRODSECBUG-2198-2.3-CE.composer-2019-03-27-06-12-47-magento-framework.patch"
      "magento/module-catalog": {
        "PRODSECBUG-2198": "patches/PRODSECBUG-2198/PRODSECBUG-2198-2.3-CE.composer-2019-03-27-06-12-47-magento-module-catalog.patch"

Above example install patches published by Magento Team on two packages magento/framework and magento/module-catalog.

  • I was trying to use method recommended by "Dominic Xigen" of creating a local override but couldn't make it work. So, this is the one that worked for me. Thanks.
    – jtalel
    Commented Jul 23, 2019 at 19:41

For the customization of third party in your website you need to override/extend the module according to your requirements.

In your case I think you need to create your module for customization of controller or model in case of controller or model customization, however you may override/extend the templates and the layout by copying the third party module to your current theme.

For more information you may check this link https://webkul.com/blog/overriding-rewriting-classes-magento2/ and extending/overriding https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/layouts/layout-extend.html


You can customise third party extensions that are in vendor by creating local override. You would version your local override.

I was going to type up an answer but came across this question

Magento 2 override Third party module installed with Composer

And this great answer


So the key is it depends on what you are overriding but this should get you started

  • I have tried the local override solution following your suggested links but I've not been able to get Magento to use my local file instead of the one inside /magento2/vendor/coingate ; what can I do? Any suggestions?
    – jtalel
    Commented Jul 22, 2019 at 18:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.