Magento 2.2.1 - how to give discount on payment methods?

I have debug core code for this issue and find bug in file Magento\SalesRule\Model\Rule\Condition\Address.php at line number:58

Solution:

Replace code in function loadAttributeOptions from

public function loadAttributeOptions()
{
    $attributes = [
        'base_subtotal' => __('Subtotal'),
        'total_qty' => __('Total Items Quantity'),
        'weight' => __('Total Weight'),
        'shipping_method' => __('Shipping Method'),
        'postcode' => __('Shipping Postcode'),
        'region' => __('Shipping Region'),
        'region_id' => __('Shipping State/Province'),
        'country_id' => __('Shipping Country'),
    ];

    $this->setAttributeOption($attributes);

    return $this;
}

To

public function loadAttributeOptions()
{
        $attributes = [
            'base_subtotal' => __('Subtotal'),
            'total_qty' => __('Total Items Quantity'),
            'weight' => __('Total Weight'),
            'shipping_method' => __('Shipping Method'),
            'payment_method' => __('Payment Method'),
            'postcode' => __('Shipping Postcode'),
            'region' => __('Shipping Region'),
            'region_id' => __('Shipping State/Province'),
            'country_id' => __('Shipping Country'),
        ];

        $this->setAttributeOption($attributes);

        return $this;
}

Note: First override model file Address.php in local after update above code in file.

Override model file code:

app\code\AR\SalesRule\etc\module.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 Ccc. All rights reserved.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="AR_SalesRule" setup_version="1.0.0">
        <sequence>
            <module name="Magento_SalesRule"/>
        </sequence>
   </module>
</config>

app\code\AR\SalesRule\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\SalesRule\Model\Rule\Condition\Address" type="AR\SalesRule\Model\Rule\Condition\Address" />
</config>

app\code\AR\SalesRule\Model\Rule\Condition\Address.php

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace AR\SalesRule\Model\Rule\Condition;

class Address extends \Magento\SalesRule\Model\Rule\Condition\Address
{
    /**
     * Load attribute options
     *
     * @return $this
     */
    public function loadAttributeOptions()
    {
        $attributes = [
            'base_subtotal' => __('Subtotal'),
            'total_qty' => __('Total Items Quantity'),
            'weight' => __('Total Weight'),
            'shipping_method' => __('Shipping Method'),
            'payment_method' => __('Payment Method'),
            'postcode' => __('Shipping Postcode'),
            'region' => __('Shipping Region'),
            'region_id' => __('Shipping State/Province'),
            'country_id' => __('Shipping Country'),
        ];

        $this->setAttributeOption($attributes);

        return $this;
    }
}

app\code\AR\SalesRule\registration.php

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
   \Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
       'AR_SalesRule',
       __DIR__
 );

After run below command:

php bin/magento setup:upgrade


Thanks Abdul for the hint and debugging.

I think using a plugin/interceptor for adding some extra functionality to core files should be considered first. If that fails or is not possible, go with the override method. With interceptors you won't loose future updates to Magento\SalesRule\Model\Rule\Condition\Adress::loadAttributeOptions().

If Magento adds some new options to the core function you'll be missing the option in your overriding model so you need to touch your code again.

At least if using the override method i would try to call the parent method and merge the base options with my new option, something like that

parent::loadAttributeOptions();

$this->setAttributeOption(array_merge(
    $this->getAttributeOption(), 
    ['payment_method' => __('Payment Method'))
);

Example for adding "Payment Method" to cart rules using a plugin/interceptor

In your custom extension add following files:

  • app/code/YOUR/EXTENSION/etc/di.xml
  • app/code/YOUR/EXTENSION/Plugin/SalesRule/AdressPlugin.php

app/code/Your/Extension/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">
    <type name="Magento\SalesRule\Model\Rule\Condition\Address">
        <plugin name="Your_Extension_Plugin_SalesRule_Address" type="Your\Extension\Plugin\SalesRule\AddressPlugin" sortOrder="10" disabled="false" />
    </type>
</config>

app/code/Your/Extension/Plugin/SalesRule/AdressPlugin.php:

<?php

namespace Your\Extension\Plugin\SalesRule;

use Magento\SalesRule\Model\Rule\Condition\Address;

class AddressPlugin
{
    /**
     * Your Description.
     *
     * @param  Address
     *
     * @return Address
     */
    public function afterLoadAttributeOptions(Address $subject)
    {
        $options = $subject->getAttributeOption();

        if (!array_key_exists('payment_method', $options)) {
            $subject->setAttributeOption(array_merge($options, ['payment_method' => __('Payment Method')]));
        }

        return $subject;
    }
}