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/
registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'CCHY_OverrideCoingate',
__DIR__
);
composer.json
{
"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": [
"registration.php"
],
"psr-4": {
"CCHY\\OverrideCoingate\\": ""
}
},
"extra": {
"map": [
[
"*",
"CCHY/OverrideCoingate"
]
]
}
}
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="Coingate\Merchant\Model\Payment" type="CCHY\OverrideCoingate\Model\Payment" />
</config>
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="CCHY_OverrideCoingate" setup_version="1.0.0">
<sequence>
<module name="CoinGate_Merchant"/>
</sequence>
</module>
</config>
Model/Payment.php: (this is the original file. I just changed namespace and the portions in BOLD inside function getCoinGateRequest)
<?php
/**
* 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
) {
parent::__construct(
$context,
$registry,
$extensionFactory,
$customAttributeFactory,
$paymentData,
$scopeConfig,
$logger,
$resource,
$resourceCollection,
$data
);
$this->urlBuilder = $urlBuilder;
$this->storeManager = $storeManager;
$this->orderManagement = $orderManagement;
$this->coingate = $coingate;
\CoinGate\CoinGate::config([
'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);
$payment->save();
$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') {
$order->setState(Order::STATE_PROCESSING);
$order->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING));
$order->save();
} elseif (in_array($cgOrder->status, ['invalid', 'expired', 'canceled', 'refunded'])) {
$this->orderManagement->cancel($cgOrder->order_id);
}
} catch (\Exception $e) {
$this->_logger->error($e);
}
}
}
No matter what I do after setup:upgrade , setup:di:compile and c:f it will use the original file insider vendor folder.