The Problem:
You want to issue a refund, electronically, through your payment gateway from Magento.
The Solution:
By default, the community edition of Magento has refunds disabled in the Paygate Models. In order to enable them you need to create a custom Model and overwrite a protected variable. Refer to Part IV: Extending Models for more details on extending a Model.
First, lets take a look at the native Model so that you can understand what’s standing in the way. I’m going to use the Authorize.net gateway as my example.
The top portion of Mage_Paygate_Model_Authorizenet (/Mage/Paygate/Model/Authorizenet.php)
class Mage_Paygate_Model_Authorizenet extends Mage_Payment_Model_Method_Cc
{
const CGI_URL = 'https://secure.authorize.net/gateway/transact.dll';
const REQUEST_METHOD_CC = 'CC';
const REQUEST_METHOD_ECHECK = 'ECHECK';
const REQUEST_TYPE_AUTH_CAPTURE = 'AUTH_CAPTURE';
const REQUEST_TYPE_AUTH_ONLY = 'AUTH_ONLY';
const REQUEST_TYPE_CAPTURE_ONLY = 'CAPTURE_ONLY';
const REQUEST_TYPE_CREDIT = 'CREDIT';
const REQUEST_TYPE_VOID = 'VOID';
const REQUEST_TYPE_PRIOR_AUTH_CAPTURE = 'PRIOR_AUTH_CAPTURE';
const ECHECK_ACCT_TYPE_CHECKING = 'CHECKING';
const ECHECK_ACCT_TYPE_BUSINESS = 'BUSINESSCHECKING';
const ECHECK_ACCT_TYPE_SAVINGS = 'SAVINGS';
const ECHECK_TRANS_TYPE_CCD = 'CCD';
const ECHECK_TRANS_TYPE_PPD = 'PPD';
const ECHECK_TRANS_TYPE_TEL = 'TEL';
const ECHECK_TRANS_TYPE_WEB = 'WEB';
const RESPONSE_DELIM_CHAR = ',';
const RESPONSE_CODE_APPROVED = 1;
const RESPONSE_CODE_DECLINED = 2;
const RESPONSE_CODE_ERROR = 3;
const RESPONSE_CODE_HELD = 4;
protected $_code = 'authorizenet';
/**
* Availability options
*/
protected $_isGateway = true;
protected $_canAuthorize = true;
protected $_canCapture = true;
protected $_canCapturePartial = false;
protected $_canRefund = false;
protected $_canVoid = true;
protected $_canUseInternal = true;
protected $_canUseCheckout = true;
protected $_canUseForMultishipping = true;
protected $_canSaveCc = false;
protected $_allowCurrencyCode = array('USD');
The variable that we’re interested in is _canRefund. The game plan is to create a custom Model that overrides Mage_Paygate_Model_Authorizenet and in that Model, override _canRefund. If you’re thinking, why not just change false to true right here, right now! Well my friend, you certainly could, but you’d certainly break your upgrade path too, check out Part IV: Extending Models and Part I: Custom Module.
I’m going to create the class: Spinonesolutions_Paygate_Model_Authorizenet (/local/Spineonesolutions/Paygate/Model/Authorizenet.php) and extend Mage_Paygate_Model_Authorizenet. Now I just override _canRefund and we’re halfway there.
<?php
class Spinonesolutions_Paygate_Model_Authorizenet extends Mage_Paygate_Model_Authorizenet
{
//Override Mage_Paygate_Model_Authorizenet
protected $_canRefund = true;
}
?>
If you read Part IV: Extending Models you’ll know that there’s one last step, and that is to modify config.xml so that our custom Model overrides the native Model.
/Spinonesolutions/Paygate/etc/config.xml
<global>
<models>
...
<paygate>
<rewrite>
<authorizenet>Spinonesolutions_Paygate_Model_Authorizenet</authorizenet>
</rewrite>
</paygate>
...
</models>
</global>
That should do it! If you’ve got everything else setup correctly this should enable the Model to post refunds electronically to your gateway. Of course, if you want to enable more than one gateway, or perhaps a gateway that’s not Authorize.net you can do that by just following the same steps above with the Model for the gateway of your choice.
As always, questions and comments are welcome!
Update: From Commenter Cindy
Afer doing the above (and not forgetting to fill in app/etc/modules/MyNameSpace_All.xml to activate the new module), I still wasn’t getting authorize.net to really refund the cc.
I found that I needed to add the CC to the request, and then add the actual amount to be refunded. Now issuing a credit memo on the invoice will cause authorize.net to issue a refund to the CC.
Also, it appears that the ‘refund’ button only shows up if you click credit memo from an invoice – it doesn’t show up if you click credit memo on an order (only ‘refund offline’ shows up then).
Anyway, this is the code to make this work for me:
//
// Override Mage_Paygate_Model_Authorizenet
// enable online refunds via credit memos
// enable partial refunds
//
class Sbr_Paygate_Model_Authorizenet extends Mage_Paygate_Model_Authorizenet {
protected $_canCapturePartial = true;
protected $_canRefund = true;
//
// override _buildRequest to fill in the CC number as it is required yet not set peviously
//
protected function _buildRequest (Varien_Object $payment) {
if (($payment->getAnetTransMethod() == self::REQUEST_METHOD_CC) &&($payment->getCcLast4()))
$payment->setCcNumber($payment->getCcLast4());
return parent::_buildRequest($payment);
}
// Override refund in order to fill out the amount
//
public function refund(Varien_Object $payment, $amount) {
$error = false;
if ($payment->getRefundTransactionId() && $amount>0) {
$payment->setAnetTransType(self::REQUEST_TYPE_CREDIT);
$request = $this->_buildRequest($payment);
// set the amount (beginning of changes)
$order = $payment->getOrder();
$request->setXAmount($amount,2);
$request->setXCurrencyCode($order->getBaseCurrencyCode());
// (end of changes)
$request->setXTransId($payment->getRefundTransactionId());
$result = $this->_postRequest($request);
if ($result->getResponseCode() == self::RESPONSE_CODE_APPROVED) {
$payment->setStatus(self::STATUS_SUCCESS);
} else {
$error = $result->getResponseReasonText();
}
} else {
$error = Mage::helper(’paygate’)->__(’Error in refunding the payment’);
}
if ($error !== false) {
Mage::throwException($error);
}
return $this;
}
10 Comments for Magento – Enable Paygate Refunds
Cindy | October 30, 2009 at 1:10 pm
erictr1ck | December 4, 2009 at 1:24 pm
followed all the above instructions and am getting “Can not save credit memo”. ideas?
Haroon | December 17, 2009 at 10:44 am
Hello,
I want to use the same solution for Paypal what attributes do i ahve to change in order to get that working.
Regards
Haroon
Jeremiah Lewis | January 15, 2010 at 7:59 am
For the config.xml file, do we just need that bit of code in a config.xml file in the local directory, or is that code snippet added to (or replacing a section) of the whole original config.xml file?
If the latter, which section is it replacing?
Will Wright | January 18, 2010 at 8:06 am
That snippet goes in the local “etc” folder. In my example it’s /app/code/local/SpinoneSolutions/etc/ .
That snippet is an addition to whatever else you may have in your config.xml. You’ll need to copy and paste the “paygate” block into your “models” block.
Note that the actual class path that you wrap with “authorizenet” could be different based on the namespaces that you choose.
Ravi Kishore | April 1, 2010 at 1:52 am
Hi Will,
Thanks for the above information.
We have followed the above changes and the module is enabled perfectly. But for the orders, which are having invoice also showing the button as REFUND OFFLINE.
We are unable to go forward on this.
Waiting for your reply. Thanks in advance.
Ravi Kishore. V
Will Wright | April 5, 2010 at 7:10 am
I think you’re OK there. If you trace that button down into the code you’ll find that it’s in /app/code/core/Mage/Adminhtml/BlockSales/Order/Creditmemo/Create/Items.php line 71, canRefund().
If I had to guess I’d say everything is working properly, it’s just that the button is mislabeled.
elam | August 26, 2010 at 7:29 am
I have the same problem as Will. And it does not fire the refund function. The button does as it says. Version 1.4.0.1. I’m using a custom Payment Modulle but the canRefund = true is the first thing that I tried and it doesn’t work. I’m thinking of using the event sales_order_creditmemo_refund unless there is a better solution.
Will Wright | August 31, 2010 at 7:32 am
You enabled canRefund, but the refund() function still isn’t firing? Have you verified that your override is working?
phillcollier | September 3, 2010 at 9:55 am
I am having the same problem… the only button that shows up is the “refund offline”. I don’t see the “refund”


Thank you!
I followed your very clear instructions and it worked. Thank you for saving me so much time and frustration!