subscribepro-magento2-ext icon indicating copy to clipboard operation
subscribepro-magento2-ext copied to clipboard

Voids on already released Stripe Charges

Open ragboyjr opened this issue 7 years ago • 0 comments

Currently, if you try to cancel a pending order 7 days after an authorization has been made, you'll receive a Transaction Declined error.

This happens because stripe will release authorizations automatically after 7 days. When performing a cancel in the admin, the SP gateway will try to void the payment via the SP api which then tries a refund request on the given charge. Stripe will throw 40x error because you can't refund a charge that's already been refunded. SP Api then returns this error back to the client which in turn triggers the Transaction Declined error message.

Possible Solutions

  • Update in the SP Api to where they will check stripe (and maybe even other gateway providers) to see if the charge has been already refunded and then return a successful response back to the Client
  • Update in the SP api to return a specific error code when trying to refund a charge that's already been refunded, and then in the Swarming\SubscribePro\Gateway\Validator::validate check for that error code and allow the transaction to be seen as valid

Workaround

Currently, to get around this, I've built the following class like this:

<?php

namespace JoinEby\Sales\Gateway\Validator;

use Magento\Payment\Gateway\Validator\ValidatorInterface;
use Magento\Payment\Gateway\Validator\AbstractValidator;
use SubscribePro\Service\Transaction\TransactionInterface;

/**
 * Stripe will release authorizations after 7 days. If you try to void a charge that's already been released, stripe will throw an error.
 * This validator decorator will check for that specific error message and allow the transaction to be valid if that's the case.
 */
class StripeCancelledResponseValidator extends AbstractValidator implements ValidatorInterface
{
    private $validator;

    /**
     * @var \Swarming\SubscribePro\Gateway\Helper\SubjectReader
     */
    protected $subjectReader;

    /**
     * @param \Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory
     * @param \Swarming\SubscribePro\Gateway\Helper\SubjectReader $subjectReader
     */
    public function __construct(
        ValidatorInterface $validator,
        \Magento\Payment\Gateway\Validator\ResultInterfaceFactory $resultFactory,
        \Swarming\SubscribePro\Gateway\Helper\SubjectReader $subjectReader
    ) {
        parent::__construct($resultFactory);
        $this->validator = $validator;
        $this->subjectReader = $subjectReader;
    }

    /**
     * @param array $validationSubject
     * @return \Magento\Payment\Gateway\Validator\ResultInterface
     * @throws \InvalidArgumentException
     */
    public function validate(array $validationSubject)
    {
        $transaction = $this->subjectReader->readTransaction($validationSubject);

        if ($transaction->getState() != TransactionInterface::STATE_GATEWAY_PROCESSING_FAILED) {
            return $this->validator->validate($validationSubject);
        }

        $message = $transaction->getResponseMessage();

        return $this->createResult(preg_match('/Charge .+ has already been refunded./i', $message) === 1);
    }
}

and have the following entries in my di.xml

    <!-- Decorate the SP Response Validator -->
    <type name="JoinEby\Sales\Gateway\Validator\StripeCancelledResponseValidator">
        <arguments>
            <argument name="validator" xsi:type="object">Swarming\SubscribePro\Gateway\Validator\ResponseValidator</argument>
        </arguments>
    </type>

    <!-- VoidCommand - override the validator -->
    <type name="Swarming\SubscribePro\Gateway\Command\VoidCommand">
        <arguments>
            <argument name="validator" xsi:type="object">JoinEby\Sales\Gateway\Validator\StripeCancelledResponseValidator</argument>
        </arguments>
    </type>

This 100% is a brittle hack and will break if any of the messaging changes, but it works for now...

ragboyjr avatar Oct 12 '17 22:10 ragboyjr