I'm trying to make a preference to override the PayPal Module's ResponseValidator class, so that i can save the validation errors for easier debugging on our end if a customer's checkout fails.

The problem is, magento just doesn't seem to want to respect my preference code.

My module/etc/di.xml looks like this:

<?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\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator" type="Acme\Module\Plugin\Validators\ResponseValidatorOverride"/>


And my ResponseValidatorOverride class looks like this:

namespace Acme\Module\Plugin\Validators;

use Magento\Paypal\Model\Payflowpro;
use Magento\Framework\DataObject;
use Magento\Paypal\Model\Payflow\Transparent;
use Magento\Framework\Exception\LocalizedException;
use Psr\Log\LoggerInterface;
use Magento\Customer\Model\Session;

class ResponseValidatorOverride extends \Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator
    /** @var LoggerInterface logger*/
    protected $logger;
    /** @var  Session session */
    protected $session;

    public function __construct($validators, LoggerInterface $log, Session $session)
        $this->logger = $log;
        $this->session = $session;

     * Validate data
     * @param DataObject $response
     * @param Transparent $transparentModel
     * @return bool
     * @throws LocalizedException
    public function validate(DataObject $response, Transparent $transparentModel)
        $message = $response->getData('respmsg');
        $this->logger->debug('Validate: ' . var_export($response->toArray(), true));
        switch ($response->getResult()) {
            case Payflowpro::RESPONSE_CODE_APPROVED:
            case Payflowpro::RESPONSE_CODE_FRAUDSERVICE_FILTER:
                foreach ($this->validators as $validator) {
                    if ($validator->validate($response, $transparentModel) === false) {
                        $this->logger->debug('After Validate: ' . var_export($response->toArray(), true));
                        $this->session->setData('CUSTOMER_LAST_PAYMENT_ERROR_INFO', array(
                            'message' => $message,
                            'last_four' => $response['ACCT'],
                            'cardholder' => (isset($response['BILLTONAME']) ? $response['BILLTONAME'] : 'Unknown Customer.')
                        throw new LocalizedException(__('??????'));
            case Payflowpro::RESPONSE_CODE_INVALID_AMOUNT:
                throw new LocalizedException(__('Transaction has been declined'));

I've tried clearing all the var/di, var/generation, etc, and recompiling, and all that fun stuff, as one would expect

I've confirmed it never even runs my code, because when i run xdebug and step through a payment, the default ResponseInterceptor gets used.

So I thought maybe it was because the class was manually initialized somewhere, not given via DI, and thus a preference wouldn't work, so I tracked down where the ResponseInterceptor is given, and it's actually created via DI.

I confirmed the mapping existed by checking in /var/di/global.ser, so I know it's telling the DI about the preference

I also tried switching between development and production modes, to see if that made any difference, and it didn't.

Does anyone have any ideas as to what could be causing this?

2 Answers 2


I think it is because the class you are trying to override via preferences is actually being used by the system via virtualtype.


<virtualType name="Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidatorInController" type="Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator">
        <argument name="validators" xsi:type="array">
            <item name="secure_token" xsi:type="object">Magento\Paypal\Model\Payflow\Service\Response\Validator\SecureToken</item>
            <item name="cvv2_code" xsi:type="object">Magento\Paypal\Model\Payflow\Service\Response\Validator\CVV2Match</item>
            <item name="avs_response" xsi:type="object">Magento\Paypal\Model\Payflow\Service\Response\Validator\AVSResponse</item>
<type name="Magento\Paypal\Controller\Transparent\Response">
        <argument name="responseValidator" xsi:type="object">Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidatorInController</argument>

I think to override this you will want to actually set a preference for the responseValidator argument.

Something like this might work (untested)....

EDIT: This works according to @zack6849

<type name="Magento\Paypal\Controller\Transparent\Response">
        <argument name="responseValidator" xsi:type="object">Acme\Module\Plugin\Validators\ResponseValidatorOverride</argument>
  • This did work, you just made my day, thank you so much! Aug 11, 2017 at 11:57

I think some class have made the preference to this class bellow:


So your ResponseValidator have never run.

Solution: Use plugin around method to rewrite function validate() in this case.

  • The DI code in magento is meant to substitute any direct references to that class with my class, isn't that the point of a preference? I tried an around method, but i can't access some of the private variables or function parameters from an interceptor, despite trying. Aug 10, 2017 at 17:57

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.