Skip to content

Commit 4aa19db

Browse files
committed
MAGETWO-63551: Paypal Refund - Credit Memo No Status Change
1 parent c7ad941 commit 4aa19db

File tree

8 files changed

+306
-25
lines changed

8 files changed

+306
-25
lines changed

app/code/Magento/Sales/Model/Order/Creditmemo/RefundOperation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public function execute(CreditmemoInterface $creditmemo, OrderInterface $order,
114114
$order->getBaseTotalInvoicedCost() - $creditmemo->getBaseCost()
115115
);
116116

117-
$creditmemo->setDoTransaction($online);
117+
$creditmemo->setDoTransaction(!$creditmemo->getPaymentRefundDisallowed() && $online);
118118
$order->getPayment()->refund($creditmemo);
119119

120120
$this->eventManager->dispatch('sales_order_creditmemo_refund', ['creditmemo' => $creditmemo]);

app/code/Magento/Sales/Model/Order/Payment.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Magento\Sales\Model\Order\Payment\Info;
1717
use Magento\Sales\Model\Order\Payment\Transaction;
1818
use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface;
19+
use Magento\Sales\Api\CreditmemoManagementInterface as CreditmemoManager;
1920

2021
/**
2122
* Order payment information
@@ -111,6 +112,11 @@ class Payment extends Info implements OrderPaymentInterface
111112
*/
112113
private $orderStateResolver;
113114

115+
/**
116+
* @var CreditmemoManager
117+
*/
118+
private $creditmemoManager = null;
119+
114120
/**
115121
* @param \Magento\Framework\Model\Context $context
116122
* @param \Magento\Framework\Registry $registry
@@ -125,6 +131,7 @@ class Payment extends Info implements OrderPaymentInterface
125131
* @param Transaction\BuilderInterface $transactionBuilder
126132
* @param Payment\Processor $paymentProcessor
127133
* @param OrderRepositoryInterface $orderRepository
134+
* @param CreditmemoManager $creditmemoManager
128135
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
129136
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
130137
* @param array $data
@@ -144,6 +151,7 @@ public function __construct(
144151
\Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface $transactionBuilder,
145152
\Magento\Sales\Model\Order\Payment\Processor $paymentProcessor,
146153
OrderRepositoryInterface $orderRepository,
154+
CreditmemoManager $creditmemoManager,
147155
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
148156
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
149157
array $data = []
@@ -155,6 +163,7 @@ public function __construct(
155163
$this->transactionBuilder = $transactionBuilder;
156164
$this->orderPaymentProcessor = $paymentProcessor;
157165
$this->orderRepository = $orderRepository;
166+
$this->creditmemoManager = $creditmemoManager;
158167
parent::__construct(
159168
$context,
160169
$registry,
@@ -779,7 +788,8 @@ public function registerRefundNotification($amount)
779788
)->addComment(
780789
__('The credit memo has been created automatically.')
781790
);
782-
$creditmemo->save();
791+
792+
$this->creditmemoManager->refund($creditmemo, false);
783793

784794
$this->_updateTotals(
785795
['amount_refunded' => $creditmemo->getGrandTotal(), 'base_amount_refunded_online' => $amount]

app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/RefundOperationTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected function setUp()
5050

5151
$this->creditmemoMock = $this->getMockBuilder(\Magento\Sales\Api\Data\CreditmemoInterface::class)
5252
->disableOriginalConstructor()
53-
->setMethods(['getBaseCost', 'setDoTransaction'])
53+
->setMethods(['getBaseCost', 'setDoTransaction', 'getPaymentRefundDisallowed'])
5454
->getMockForAbstractClass();
5555

5656
$this->paymentMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class)

app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Magento\Sales\Model\Order;
99
use Magento\Sales\Model\Order\Payment;
1010
use Magento\Sales\Model\Order\Payment\Transaction;
11+
use Magento\Sales\Api\CreditmemoManagementInterface;
12+
use Magento\Sales\Model\Order\Creditmemo;
1113

1214
/**
1315
* Class PaymentTest
@@ -113,6 +115,11 @@ class PaymentTest extends \PHPUnit_Framework_TestCase
113115
*/
114116
protected $orderRepository;
115117

118+
/**
119+
* @var CreditmemoManagementInterface
120+
*/
121+
private $creditmemoManagerMock;
122+
116123
/**
117124
* @return void
118125
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -269,29 +276,34 @@ function ($value) {
269276
'',
270277
false
271278
);
272-
$this->creditMemoMock = $this->getMock(
273-
\Magento\Sales\Model\Order\Creditmemo::class,
274-
[
275-
'setPaymentRefundDisallowed',
276-
'getItemsCollection',
277-
'getItems',
278-
'setAutomaticallyCreated',
279-
'register',
280-
'addComment',
281-
'save',
282-
'getGrandTotal',
283-
'getBaseGrandTotal',
284-
'getDoTransaction',
285-
'getInvoice',
286-
'getOrder'
287-
],
288-
[],
289-
'',
290-
false
291-
);
292279
$this->orderStateResolverMock = $this->getMockBuilder(Order\OrderStateResolverInterface::class)
293280
->disableOriginalConstructor()
294281
->getMockForAbstractClass();
282+
$this->creditMemoMock = $this->getMockBuilder(Creditmemo::class)
283+
->disableOriginalConstructor()
284+
->setMethods(
285+
[
286+
'setPaymentRefundDisallowed',
287+
'getItemsCollection',
288+
'getItems',
289+
'setAutomaticallyCreated',
290+
'register',
291+
'addComment',
292+
'save',
293+
'getGrandTotal',
294+
'getBaseGrandTotal',
295+
'getDoTransaction',
296+
'getInvoice',
297+
'getOrder',
298+
'getPaymentRefundDisallowed'
299+
]
300+
)
301+
->getMock();
302+
303+
$this->creditmemoManagerMock = $this->getMockBuilder(CreditmemoManagementInterface::class)
304+
->disableOriginalConstructor()
305+
->getMockForAbstractClass();
306+
295307
$this->payment = $this->initPayment();
296308
$helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
297309
$helper->setBackwardCompatibleProperty($this->payment, 'orderStateResolver', $this->orderStateResolverMock);
@@ -1362,7 +1374,12 @@ public function testRegisterRefundNotification()
13621374
$this->creditMemoMock->expects($this->once())->method('setPaymentRefundDisallowed')->willReturnSelf();
13631375
$this->creditMemoMock->expects($this->once())->method('setAutomaticallyCreated')->willReturnSelf();
13641376
$this->creditMemoMock->expects($this->once())->method('addComment')->willReturnSelf();
1365-
$this->creditMemoMock->expects($this->once())->method('save')->willReturnSelf();
1377+
1378+
$this->creditmemoManagerMock->expects($this->once())
1379+
->method('refund')
1380+
->with($this->creditMemoMock, false)
1381+
->willReturn($this->creditMemoMock);
1382+
13661383
$this->orderMock->expects($this->once())->method('getBaseCurrency')->willReturn($this->currencyMock);
13671384

13681385
$parentTransaction = $this->getMock(
@@ -1581,7 +1598,8 @@ protected function initPayment()
15811598
'transactionManager' => $this->transactionManagerMock,
15821599
'transactionBuilder' => $this->transactionBuilderMock,
15831600
'paymentProcessor' => $this->paymentProcessor,
1584-
'orderRepository' => $this->orderRepository
1601+
'orderRepository' => $this->orderRepository,
1602+
'creditmemoManager' => $this->creditmemoManagerMock
15851603
]
15861604
);
15871605
}

dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
*/
66
namespace Magento\Paypal\Model;
77

8+
use Magento\Paypal\Model\IpnFactory;
9+
use Magento\Sales\Api\Data\OrderInterface;
10+
use Magento\Sales\Model\Order;
11+
use Magento\Sales\Model\Order\Creditmemo;
12+
813
/**
914
* @magentoAppArea frontend
1015
*/
@@ -33,6 +38,126 @@ public function testProcessIpnRequestExpressCurrency($currencyCode)
3338
$this->_processIpnRequestCurrency($currencyCode);
3439
}
3540

41+
/**
42+
* Refund full order amount by Paypal Express IPN message service.
43+
*
44+
* @magentoDataFixture Magento/Paypal/_files/order_express_with_invoice_and_shipping.php
45+
* @magentoConfigFixture current_store payment/paypal_express/active 1
46+
* @magentoConfigFixture current_store paypal/general/merchant_country US
47+
*/
48+
public function testProcessIpnRequestFullRefund()
49+
{
50+
$ipnData = require __DIR__ . '/../_files/ipn_refund.php';
51+
$ipnFactory = $this->_objectManager->create(IpnFactory::class);
52+
$ipnModel = $ipnFactory->create(
53+
[
54+
'data' => $ipnData,
55+
'curlFactory' => $this->_createMockedHttpAdapter()
56+
]
57+
);
58+
59+
$ipnModel->processIpnRequest();
60+
61+
$order = $this->_objectManager->create(Order::class);
62+
$order->loadByIncrementId('100000001');
63+
64+
$creditmemoItems = $order->getCreditmemosCollection()->getItems();
65+
$creditmemo = current($creditmemoItems);
66+
67+
$this->assertEquals(Order::STATE_CLOSED, $order->getState()) ;
68+
$this->assertEquals(1, count($creditmemoItems));
69+
$this->assertEquals(Creditmemo::STATE_REFUNDED, $creditmemo->getState());
70+
$this->assertEquals(10, $order->getSubtotalRefunded());
71+
$this->assertEquals(10, $order->getBaseSubtotalRefunded());
72+
$this->assertEquals(20, $order->getShippingRefunded());
73+
$this->assertEquals(20, $order->getBaseShippingRefunded());
74+
$this->assertEquals(30, $order->getTotalRefunded());
75+
$this->assertEquals(30, $order->getBaseTotalRefunded());
76+
$this->assertEquals(30, $order->getTotalOnlineRefunded());
77+
$this->assertEmpty($order->getTotalOfflineRefunded());
78+
}
79+
80+
/**
81+
* Partial refund of order amount by Paypal Express IPN message service.
82+
*
83+
* @magentoDataFixture Magento/Paypal/_files/order_express_with_invoice_and_shipping.php
84+
* @magentoConfigFixture current_store payment/paypal_express/active 1
85+
* @magentoConfigFixture current_store paypal/general/merchant_country US
86+
*/
87+
public function testProcessIpnRequestPartialRefund()
88+
{
89+
$ipnData = require __DIR__ . '/../_files/ipn_refund.php';
90+
91+
$refundAmount = -15;
92+
$ipnData['mc_gross'] = $refundAmount;
93+
94+
$ipnFactory = $this->_objectManager->create(IpnFactory::class);
95+
$ipnModel = $ipnFactory->create(
96+
[
97+
'data' => $ipnData,
98+
'curlFactory' => $this->_createMockedHttpAdapter()
99+
]
100+
);
101+
102+
$ipnModel->processIpnRequest();
103+
104+
$order = $this->_objectManager->create(Order::class);
105+
$order->loadByIncrementId('100000001');
106+
107+
$creditmemoItems = $order->getCreditmemosCollection()->getItems();
108+
$comments = $order->load($order->getId())->getAllStatusHistory();
109+
$commentData = reset($comments);
110+
$commentOrigin = sprintf(
111+
'IPN "Refunded". Refund issued by merchant. Registered notification about refunded amount of $%d.00. '.
112+
'Transaction ID: "%s". Credit Memo has not been created. Please create offline Credit Memo.',
113+
abs($refundAmount),
114+
$ipnData['txn_id']
115+
);
116+
117+
$this->assertEquals(Order::STATE_PROCESSING, $order->getState()) ;
118+
$this->assertEmpty(count($creditmemoItems));
119+
$this->assertEquals(1, count($comments));
120+
$this->assertEquals($commentOrigin, $commentData->getComment());
121+
}
122+
123+
/**
124+
* Refund rest of order amount by Paypal Express IPN message service.
125+
*
126+
* @magentoDataFixture Magento/Paypal/_files/order_express_with_invoice_and_creditmemo.php
127+
* @magentoConfigFixture current_store payment/paypal_express/active 1
128+
* @magentoConfigFixture current_store paypal/general/merchant_country US
129+
*/
130+
public function testProcessIpnRequestRestRefund()
131+
{
132+
$ipnData = require __DIR__ . '/../_files/ipn_refund.php';
133+
134+
$ipnFactory = $this->_objectManager->create(IpnFactory::class);
135+
$ipnModel = $ipnFactory->create(
136+
[
137+
'data' => $ipnData,
138+
'curlFactory' => $this->_createMockedHttpAdapter()
139+
]
140+
);
141+
142+
$ipnModel->processIpnRequest();
143+
144+
$order = $this->_objectManager->create(Order::class);
145+
$order->loadByIncrementId('100000001');
146+
147+
$creditmemoItems = $order->getCreditmemosCollection()->getItems();
148+
149+
$this->assertEquals(Order::STATE_CLOSED, $order->getState()) ;
150+
$this->assertEquals(2, count($creditmemoItems));
151+
$this->assertEquals(10, $order->getSubtotalRefunded());
152+
$this->assertEquals(10, $order->getBaseSubtotalRefunded());
153+
$this->assertEquals(20, $order->getShippingRefunded());
154+
$this->assertEquals(20, $order->getBaseShippingRefunded());
155+
$this->assertEquals(30, $order->getTotalRefunded());
156+
$this->assertEquals(30, $order->getBaseTotalRefunded());
157+
$this->assertEquals(30, $order->getTotalOnlineRefunded());
158+
$this->assertEmpty($order->getTotalOfflineRefunded());
159+
}
160+
36161
/**
37162
* Test processIpnRequest() currency check for paypal_express and paypal_standard payment methods
38163
*
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
return [
8+
'mc_gross' => -30.00,
9+
'invoice' => '100000001',
10+
'payment_status' => 'Refunded',
11+
'auth_status' => 'Completed',
12+
'reason_code' => 'refund',
13+
'receiver_email' => 'merchant_2012050718_biz@example.com',
14+
'parent_txn_id' => '84J11393WC835693U',
15+
'txn_id' => '1P566839F9694230H'
16+
];
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\Sales\Api\CreditmemoRepositoryInterface;
8+
use Magento\Sales\Model\Order\Creditmemo;
9+
use Magento\Sales\Model\Order\CreditmemoFactory;
10+
11+
require __DIR__ . '/order_express_with_invoice_and_shipping.php';
12+
13+
/** @var CreditmemoFactory $creditmemoFactory */
14+
$creditmemoFactory = $objectManager->create(CreditmemoFactory::class);
15+
/** @var Creditmemo $creditmemo */
16+
$creditmemo = $creditmemoFactory->createByInvoice($invoice, $invoice->getData());
17+
18+
$creditmemo->setOrder($order);
19+
$creditmemo->setState(Creditmemo::STATE_REFUNDED);
20+
$creditmemo->setIncrementId('100000001');
21+
$creditmemo->setGrandTotal($itemsAmount);
22+
23+
/** @var CreditmemoRepositoryInterface $creditMemoRepository */
24+
$creditMemoRepository = $objectManager->get(CreditmemoRepositoryInterface::class);
25+
$creditMemoRepository->save($creditmemo);

0 commit comments

Comments
 (0)