Natively, the Magento API doesn't support online refunds. You have to create a local version of it which implements some methods which are normally only called when raising credit memos in the admin.
If you don't want to read the logic behind these changes and just need the solution, scroll down to the TL;DR section further below.
First, let's override the Credit memo API create action. Assuming you have a custom sales module for your overrides, you'll add this rewrite to its config:
app/code/local/Mycompany/Sales/etc/config.xml:
<models>
<sales>
<rewrite>
<order_creditmemo_api>Mycompany_Sales_Model_Order_Creditmemo_Api</order_creditmemo_api>
</rewrite>
</sales>
</models>
Then, let's extend that model and its create method:
app/code/local/Mycompany/Sales/Model/Order/Creditmemo/Api.php
<?php
class Mycompany_Sales_Model_Order_Creditmemo_Api extends Mage_Sales_Model_Order_Creditmemo_Api
{
public function create($orderIncrementId, $creditmemoData = null, $comment = null, $notifyCustomer = false,
$includeComment = false, $refundToStoreCreditAmount = null)
{
/** @var $order Mage_Sales_Model_Order */
$order = Mage::getModel('sales/order')->load($orderIncrementId, 'increment_id');
if (!$order->getId()) {
$this->_fault('order_not_exists');
}
if (!$order->canCreditmemo()) {
$this->_fault('cannot_create_creditmemo');
}
$creditmemoData = $this->_prepareCreateData($creditmemoData);
/** @var $service Mage_Sales_Model_Service_Order */
$service = Mage::getModel('sales/service_order', $order);
/** @var $creditmemo Mage_Sales_Model_Order_Creditmemo */
$creditmemo = $service->prepareCreditmemo($creditmemoData);
// refund to Store Credit
if ($refundToStoreCreditAmount) {
// check if refund to Store Credit is available
if ($order->getCustomerIsGuest()) {
$this->_fault('cannot_refund_to_storecredit');
}
$refundToStoreCreditAmount = max(
0,
min($creditmemo->getBaseCustomerBalanceReturnMax(), $refundToStoreCreditAmount)
);
if ($refundToStoreCreditAmount) {
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice($refundToStoreCreditAmount);
$creditmemo->setBaseCustomerBalanceTotalRefunded($refundToStoreCreditAmount);
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice(
$refundToStoreCreditAmount*$order->getStoreToOrderRate()
);
// this field can be used by customer balance observer
$creditmemo->setBsCustomerBalTotalRefunded($refundToStoreCreditAmount);
// setting flag to make actual refund to customer balance after credit memo save
$creditmemo->setCustomerBalanceRefundFlag(true);
}
}
$creditmemo->setPaymentRefundDisallowed(true)->register();
// add comment to creditmemo
if (!empty($comment)) {
$creditmemo->addComment($comment, $notifyCustomer);
}
try {
Mage::getModel('core/resource_transaction')
->addObject($creditmemo)
->addObject($order)
->save();
// send email notification
$creditmemo->sendEmail($notifyCustomer, ($includeComment ? $comment : ''));
} catch (Mage_Core_Exception $e) {
$this->_fault('data_invalid', $e->getMessage());
}
return $creditmemo->getIncrementId();
}
}
To create an online refund, you need to raise your credit memo against an invoice. As the API call only offers to raise credit memos against an order, the simplest way is to get the invoice from the order. We'll leave a fallback to the previous behaviour for edge cases where you cannot get invoices from an order.
To do so, let's replace:
$creditmemo = $service->prepareCreditmemo($creditmemoData);
by:
$invoices = $order->getInvoiceCollection();
if ($invoices) {
$creditmemo = $service->prepareInvoiceCreditmemo($invoices->getFirstItem(), $creditmemoData);
} else {
$creditmemo = $service->prepareCreditmemo($creditmemoData);
}
When registering the credit memo, there's a safeguard that prevents refunding from happening. Let's remove it:
$creditmemo->setPaymentRefundDisallowed(true)->register();
Becomes:
$creditmemo->register();
Because we attached that credit memo to an invoice, we'll need to save the invoice as well. Let's replace the following:
Mage::getModel('core/resource_transaction')
->addObject($creditmemo)
->addObject($order)
->save();
by this:
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($creditmemo)
->addObject($order);
if ($creditmemo->getInvoice()) {
$transactionSave->addObject($creditmemo->getInvoice());
}
$transactionSave->save();
Now you should be all set. Your final override should look like that:
TL;DR
<?php
class Mycompany_Sales_Model_Order_Creditmemo_Api extends Mage_Sales_Model_Order_Creditmemo_Api
{
public function create($orderIncrementId, $creditmemoData = null, $comment = null, $notifyCustomer = false,
$includeComment = false, $refundToStoreCreditAmount = null)
{
/** @var $order Mage_Sales_Model_Order */
$order = Mage::getModel('sales/order')->load($orderIncrementId, 'increment_id');
if (!$order->getId()) {
$this->_fault('order_not_exists');
}
if (!$order->canCreditmemo()) {
$this->_fault('cannot_create_creditmemo');
}
$creditmemoData = $this->_prepareCreateData($creditmemoData);
/** @var $service Mage_Sales_Model_Service_Order */
$service = Mage::getModel('sales/service_order', $order);
/** @var $creditmemo Mage_Sales_Model_Order_Creditmemo */
$invoices = $order->getInvoiceCollection();
if ($invoices) {
$creditmemo = $service->prepareInvoiceCreditmemo($invoices->getFirstItem(), $creditmemoData);
} else {
$creditmemo = $service->prepareCreditmemo($creditmemoData);
}
// refund to Store Credit
if ($refundToStoreCreditAmount) {
// check if refund to Store Credit is available
if ($order->getCustomerIsGuest()) {
$this->_fault('cannot_refund_to_storecredit');
}
$refundToStoreCreditAmount = max(
0,
min($creditmemo->getBaseCustomerBalanceReturnMax(), $refundToStoreCreditAmount)
);
if ($refundToStoreCreditAmount) {
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice($refundToStoreCreditAmount);
$creditmemo->setBaseCustomerBalanceTotalRefunded($refundToStoreCreditAmount);
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice(
$refundToStoreCreditAmount*$order->getStoreToOrderRate()
);
// this field can be used by customer balance observer
$creditmemo->setBsCustomerBalTotalRefunded($refundToStoreCreditAmount);
// setting flag to make actual refund to customer balance after credit memo save
$creditmemo->setCustomerBalanceRefundFlag(true);
}
}
$creditmemo->register();
// add comment to creditmemo
if (!empty($comment)) {
$creditmemo->addComment($comment, $notifyCustomer);
}
try {
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($creditmemo)
->addObject($order);
if ($creditmemo->getInvoice()) {
$transactionSave->addObject($creditmemo->getInvoice());
}
$transactionSave->save();
// send email notification
$creditmemo->sendEmail($notifyCustomer, ($includeComment ? $comment : ''));
} catch (Mage_Core_Exception $e) {
$this->_fault('data_invalid', $e->getMessage());
}
return $creditmemo->getIncrementId();
}
}