How to override model in magento2?

All other answers tell you how to rewrite the model, exactly what is asked. But Magento 2 offers another solution to your problem: Plugins. With plugins you can change the outcome of just about any public method in Magento 2. You stated you want to change the result of this function:

if (!$this->zeroTotalValidator->isApplicable($payment->getMethodInstance(), $quote)) {
    throw new InvalidTransitionException(__('The requested Payment Method is not available.'));
}

You can do this by adding a plugin on the \Magento\Payment\Model\Checks\ZeroTotal class. Add this to your etc/di.xml file:

<type name="Magento\Payment\Model\Checks\ZeroTotal">
    <plugin name="your_unique_name_for_this_plugin" type="Vendor\Module\Plugin\ZeroTotal" />
</type>

Then create the class:

<VendorName>\<ModuleName>\Plugin\ZeroTotal

namespace Vendor\Module\Plugin;

class ZeroTotal
{
    public function afterIsApplicable($subject, $result)
    {
        return true;
    }
}

This will make sure the function always returns true. If you want a more granular approach you can change the function to this:

public function aroundIsApplicable($subject, callable $proceed, ...$args)
{
   if ($this->meetsMyCustomRequirement()) {
       // This will call the original function and return it's results.
       return $proceed(...$args);
   }

   return false;
}

If the $proceed function isn't called then the original function is also never called.


Steps to Follow :

  • Override di.xml
  • Override PaymentMethodManagement.php
  • Launch SSH and Run Commands

Override di.xml

app/code/Namespace/Modulename/etc

<?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="Magento\Quote\Model\PaymentMethodManagement" type="Namespace\Modulename\PaymentMethodManagement" />
   </config>

Override PaymentMethodManagement.php

/**
* {@inheritdoc}
*/
public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method)
{
    /** @var \Magento\Quote\Model\Quote $quote */
    $quote = $this->quoteRepository->get($cartId);

    $method->setChecks([
        \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT,
        \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY,
        \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY,
        \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX,
    ]);
    $payment = $quote->getPayment();

    $data = $method->getData();
    $payment->importData($data);

    if ($quote->isVirtual()) {
        $quote->getBillingAddress()->setPaymentMethod($payment->getMethod());
    } else {
        // check if shipping address is set
        if ($quote->getShippingAddress()->getCountryId() === null) {
            throw new InvalidTransitionException(__('Shipping address is not set'));
        }
        $quote->getShippingAddress()->setPaymentMethod($payment->getMethod());
    }
    if (!$quote->isVirtual() && $quote->getShippingAddress()) {
        $quote->getShippingAddress()->setCollectShippingRates(true);
    }
    /*** Do stuff with this code ***/
    if (!$this->zeroTotalValidator->isApplicable($payment->getMethodInstance(), $quote)) {
        throw new InvalidTransitionException(__(''));
    }
    /*** Do stuff with this code ***/

    $quote->setTotalsCollectedFlag(false)->collectTotals()->save();
    return $quote->getPayment()->getId();
}

Launch SSH and Run Commands

php bin/magento setup:di:compile
php bin/magento cache:clean
php bin/magento cache:flush

I hope it helps!


in you 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="Magento\Quote\Model\PaymentMethodManagement" type="Vendor\Module\Model\Rewrite\Quote\PaymentMethodManagement" />

</config>

in your Model file

<?php 
 namespace Vendor\Module\Model\Rewrite\Quote;

        class PaymentMethodManagement extends \Magento\Quote\Model\PaymentMethodManagement
        {
            public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method)
            {
                /** @var \Magento\Quote\Model\Quote $quote */
                $quote = $this->quoteRepository->get($cartId);

                $method->setChecks([
                    \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT,
                    \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY,
                    \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY,
                    \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX,
                ]);
                $payment = $quote->getPayment();

                $data = $method->getData();
                $payment->importData($data);

                if ($quote->isVirtual()) {
                    $quote->getBillingAddress()->setPaymentMethod($payment->getMethod());
                } else {
                    // check if shipping address is set
                    if ($quote->getShippingAddress()->getCountryId() === null) {
                        throw new InvalidTransitionException(__('Shipping address is not set'));
                    }
                    $quote->getShippingAddress()->setPaymentMethod($payment->getMethod());
                }
                if (!$quote->isVirtual() && $quote->getShippingAddress()) {
                    $quote->getShippingAddress()->setCollectShippingRates(true);
                }
                /*** Do stuff with this code ***/
                if (!$this->zeroTotalValidator->isApplicable($payment->getMethodInstance(), $quote)) {
                    throw new InvalidTransitionException(__(''));
                }
                /*** Do stuff with this code ***/

                $quote->setTotalsCollectedFlag(false)->collectTotals()->save();
                return $quote->getPayment()->getId();
            }

        }