diff --git a/config/common.yml b/config/common.yml index f94a67ac5..ac6c9ec74 100644 --- a/config/common.yml +++ b/config/common.yml @@ -472,7 +472,9 @@ services: PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForCheckoutCompletedQuery: "ps_checkout.query.handler.paypal.order.get_paypal_order_for_checkout_completed" PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForOrderConfirmationQuery: "ps_checkout.query.handler.paypal.order.get_paypal_order_for_order_confirmation" PrestaShop\Module\PrestashopCheckout\Checkout\Command\UpdatePaymentMethodSelectedCommand: "ps_checkout.query.handler.checkout.update_payment_method_selected" - PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Command\RefundPayPalCaptureCommand: 'PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\CommandHandler\RefundPayPalCaptureCommandHandler' + PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Command\RefundPayPalCaptureCommand: "PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\CommandHandler\RefundPayPalCaptureCommandHandler" + PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundQuery: "PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundQueryHandler" + PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundForPDFOrderSlipQuery: "PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundForPDFOrderSlipQueryHandler" ps_checkout.event.dispatcher.factory: class: 'PrestaShop\Module\PrestashopCheckout\Event\SymfonyEventDispatcherFactory' @@ -627,6 +629,7 @@ services: public: true arguments: - "@ps_checkout.repository.pscheckoutcart" + - "@ps_checkout.order.state.service.order_state_mapper" ps_checkout.query.handler.order.get_order_for_payment_reversed: class: 'PrestaShop\Module\PrestashopCheckout\Order\QueryHandler\GetOrderForPaymentReversedQueryHandler' @@ -735,3 +738,15 @@ services: public: true arguments: - "@ps_checkout.http.client" + + PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundQueryHandler: + class: 'PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundQueryHandler' + public: true + arguments: + - "@ps_checkout.paypal.provider.order" + + PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundForPDFOrderSlipQueryHandler: + class: 'PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler\GetPayPalRefundForPDFOrderSlipQueryHandler' + public: true + arguments: + - "@ps_checkout.paypal.provider.order" diff --git a/ps_checkout.php b/ps_checkout.php index 4b367e558..cf139b0ca 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -46,6 +46,8 @@ class Ps_checkout extends PaymentModule 'actionObjectOrderPaymentUpdateAfter', 'displayPaymentReturn', 'displayOrderDetail', + 'actionOrderSlipAdd', + 'displayPDFOrderSlip', ]; /** @@ -1671,4 +1673,82 @@ public function hookDisplayOrderDetail(array $params) return $this->display(__FILE__, 'views/templates/hook/displayOrderDetail.tpl'); } + + /** + * Refund based on OrderSlip + * + * @param array{cookie: Cookie, cart: Cart, altern: int, order: Order} $params + * + * @return void + */ + public function hookActionOrderSlipAdd(array $params) + { + try { + /** @var Order $order */ + $order = $params['order']; + if (!Validate::isLoadedObject($order)) { + throw new \PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException('Unable to get the Order from params'); + } + + /** @var \PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface $commandBus */ + $commandBus = $this->getService('ps_checkout.bus.command'); + + /** @var \PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundQueryResult $getPayPalRefundQueryResult */ + $getPayPalRefundQueryResult = $commandBus->handle(new \PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundQuery( + $order + )); + + $commandBus->handle(new \PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Command\RefundPayPalCaptureCommand( + $getPayPalRefundQueryResult->getPayPalOrderId(), + $getPayPalRefundQueryResult->getPayPalCaptureId(), + $getPayPalRefundQueryResult->getCurrencyIsoCode(), + sprintf('%01.2F', $getPayPalRefundQueryResult->getAmount()) + )); + } catch (Exception $exception) { + // Do not break the Admin process if an exception is thrown + $this->getLogger()->error(__FUNCTION__, [ + 'exception' => $exception, + ]); + } + } + + /** + * Add content to the PDF OrderSlip. + * + * @param array{cookie: Cookie, cart: Cart, altern: int, object: OrderSlip} $params + * + * @return string + */ + public function hookDisplayPDFOrderSlip(array $params) + { + try { + /** @var \PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface $commandBus */ + $commandBus = $this->getService('ps_checkout.bus.command'); + + /** @var \PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundForPDFOrderSlipQueryResult $getPayPalRefundForPDFOrderSlipQueryResult */ + $getPayPalRefundForPDFOrderSlipQueryResult = $commandBus->handle(new \PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundForPDFOrderSlipQuery( + $params['object'] + )); + + $this->context->smarty->assign([ + 'refund_id' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundId(), + 'refund_amount' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundAmount(), + 'refund_currency' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundCurrency(), + 'refund_currency_id' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundCurrencyId(), + 'refund_status' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundStatus(), + 'refund_note_to_payer' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundNote(), + 'refund_create_time' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundCreateTime(), + 'refund_update_time' => $getPayPalRefundForPDFOrderSlipQueryResult->getPaypalRefundUpdateTime(), + ]); + + return $this->display(__FILE__, 'views/templates/hook/displayPDFOrderSlip.tpl'); + } catch (Exception $exception) { + // Do not break the Admin process if an exception is thrown + $this->getLogger()->error(__FUNCTION__, [ + 'exception' => $exception, + ]); + + return ''; + } + } } diff --git a/src/Order/Query/GetOrderForPaymentRefundedQueryResult.php b/src/Order/Query/GetOrderForPaymentRefundedQueryResult.php index b3cff68f1..a72e5322f 100644 --- a/src/Order/Query/GetOrderForPaymentRefundedQueryResult.php +++ b/src/Order/Query/GetOrderForPaymentRefundedQueryResult.php @@ -62,6 +62,11 @@ class GetOrderForPaymentRefundedQueryResult */ private $currencyId; + /** + * @var int[] + */ + private $orderStateIdHistory; + /** * @param int $orderId * @param int $currentStateId @@ -70,6 +75,7 @@ class GetOrderForPaymentRefundedQueryResult * @param string $totalAmount * @param string $totalRefund * @param int $currencyId + * @param int[] $orderStateIdHistory * * @throws OrderException * @throws OrderStateException @@ -81,7 +87,8 @@ public function __construct( $hasBeenTotallyRefund, $totalAmount, $totalRefund, - $currencyId + $currencyId, + array $orderStateIdHistory = [] ) { $this->orderId = new OrderId($orderId); $this->currentStateId = new OrderStateId($currentStateId); @@ -90,6 +97,7 @@ public function __construct( $this->totalAmount = $totalAmount; $this->totalRefund = $totalRefund; $this->currencyId = $currencyId; + $this->orderStateIdHistory = $orderStateIdHistory; } /** @@ -147,4 +155,12 @@ public function getCurrencyId() { return $this->currencyId; } + + /** + * @return int[] + */ + public function getOrderStateIdHistory() + { + return $this->orderStateIdHistory; + } } diff --git a/src/Order/QueryHandler/GetOrderForPaymentRefundedQueryHandler.php b/src/Order/QueryHandler/GetOrderForPaymentRefundedQueryHandler.php index 3ba5b28b5..6dd564748 100644 --- a/src/Order/QueryHandler/GetOrderForPaymentRefundedQueryHandler.php +++ b/src/Order/QueryHandler/GetOrderForPaymentRefundedQueryHandler.php @@ -28,6 +28,9 @@ use PrestaShop\Module\PrestashopCheckout\Order\Exception\OrderNotFoundException; use PrestaShop\Module\PrestashopCheckout\Order\Query\GetOrderForPaymentRefundedQuery; use PrestaShop\Module\PrestashopCheckout\Order\Query\GetOrderForPaymentRefundedQueryResult; +use PrestaShop\Module\PrestashopCheckout\Order\State\Exception\OrderStateException; +use PrestaShop\Module\PrestashopCheckout\Order\State\OrderStateConfigurationKeys; +use PrestaShop\Module\PrestashopCheckout\Order\State\Service\OrderStateMapper; use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; use PrestaShopCollection; use PrestaShopDatabaseException; @@ -42,9 +45,15 @@ class GetOrderForPaymentRefundedQueryHandler */ private $psCheckoutCartRepository; - public function __construct(PsCheckoutCartRepository $psCheckoutCartRepository) + /** + * @var OrderStateMapper + */ + private $orderStateMapper; + + public function __construct(PsCheckoutCartRepository $psCheckoutCartRepository, OrderStateMapper $orderStateMapper) { $this->psCheckoutCartRepository = $psCheckoutCartRepository; + $this->orderStateMapper = $orderStateMapper; } /** @@ -88,7 +97,8 @@ public function handle(GetOrderForPaymentRefundedQuery $query) $this->hasBeenTotallyRefunded($totalRefund, $order), (string) $order->getTotalPaid(), (string) $totalRefund, - (int) $order->id_currency + (int) $order->id_currency, + $this->getOrderStateHistory($order) ); } @@ -109,4 +119,39 @@ private function getTotalRefund(Order $order) return $refundAmount; } + + /** + * @param Order $order + * + * @return int[] + */ + private function getOrderStateHistory(Order $order) + { + $orderHistory = $order->getHistory($order->id_lang); + + if (!$orderHistory) { + return []; + } + + try { + $orderStateRefundedId = $this->orderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_REFUNDED); + $orderStatePartiallyRefundedId = $this->orderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PARTIALLY_REFUNDED); + } catch (OrderStateException $exception) { + return []; + } + + $orderStateIdHistory = []; + + foreach ($orderHistory as $historyItem) { + $orderStateId = (int) $historyItem['id_order_state']; + + if ($orderStateId !== $orderStateRefundedId && $orderStateId !== $orderStatePartiallyRefundedId) { + continue; + } + + $orderStateIdHistory[] = $orderStateId; + } + + return $orderStateIdHistory; + } } diff --git a/src/PayPal/Payment/Refund/EventSubscriber/PayPalRefundEventSubscriber.php b/src/PayPal/Payment/Refund/EventSubscriber/PayPalRefundEventSubscriber.php index 19699c7d7..b4eb9127c 100644 --- a/src/PayPal/Payment/Refund/EventSubscriber/PayPalRefundEventSubscriber.php +++ b/src/PayPal/Payment/Refund/EventSubscriber/PayPalRefundEventSubscriber.php @@ -114,10 +114,6 @@ public function setPaymentRefundedOrderStatus(PayPalCaptureRefundedEvent $event) $this->orderPayPalCache->delete($event->getPayPalOrderId()->getValue()); } - if (!$order->hasBeenPaid() || $order->hasBeenTotallyRefund()) { - return; - } - $orderPayPal = $this->orderProvider->getById($event->getPayPalOrderId()->getValue()); if (empty($orderPayPal['purchase_units'][0]['payments']['refunds'])) { @@ -129,11 +125,16 @@ public function setPaymentRefundedOrderStatus(PayPalCaptureRefundedEvent $event) }); $orderFullyRefunded = (float) $order->getTotalAmount() <= (float) $totalRefunded; + $newOrderStateId = $this->orderStateMapper->getIdByKey($orderFullyRefunded ? OrderStateConfigurationKeys::PS_CHECKOUT_STATE_REFUNDED : OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PARTIALLY_REFUNDED); + + if (in_array($newOrderStateId, $order->getOrderStateIdHistory())) { + return; + } $this->commandBus->handle( new UpdateOrderStatusCommand( $order->getOrderId()->getValue(), - $this->orderStateMapper->getIdByKey($orderFullyRefunded ? OrderStateConfigurationKeys::PS_CHECKOUT_STATE_REFUNDED : OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PARTIALLY_REFUNDED) + $newOrderStateId ) ); } diff --git a/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQuery.php b/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQuery.php new file mode 100644 index 000000000..8cf177a8b --- /dev/null +++ b/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQuery.php @@ -0,0 +1,45 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query; + +use OrderSlip; + +class GetPayPalRefundForPDFOrderSlipQuery +{ + /** @var OrderSlip */ + private $orderSlip; + + /** + * @param OrderSlip $orderSlip + */ + public function __construct($orderSlip) + { + $this->orderSlip = $orderSlip; + } + + /** + * @return OrderSlip + */ + public function getOrderSlip() + { + return $this->orderSlip; + } +} diff --git a/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQueryResult.php b/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQueryResult.php new file mode 100644 index 000000000..476a55556 --- /dev/null +++ b/src/PayPal/Payment/Refund/Query/GetPayPalRefundForPDFOrderSlipQueryResult.php @@ -0,0 +1,142 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query; + +class GetPayPalRefundForPDFOrderSlipQueryResult +{ + /** @var int */ + private $paypalRefundId; + + /** @var string */ + private $paypalRefundAmount; + + /** @var string */ + private $paypalRefundCurrency; + + /** @var int */ + private $paypalRefundCurrencyId; + + /** @var string */ + private $paypalRefundStatus; + + /** @var string */ + private $paypalRefundNote; + + /** @var string */ + private $paypalRefundCreateTime; + + /** @var string */ + private $paypalRefundUpdateTime; + + /** + * @param int $paypalRefundId + * @param string $paypalRefundAmount + * @param string $paypalRefundCurrency + * @param int $paypalRefundCurrencyId + * @param string $paypalRefundStatus + * @param string $paypalRefundNote + * @param string $paypalRefundCreateTime + * @param string $paypalRefundUpdateTime + */ + public function __construct( + $paypalRefundId, + $paypalRefundAmount, + $paypalRefundCurrency, + $paypalRefundCurrencyId, + $paypalRefundStatus, + $paypalRefundNote, + $paypalRefundCreateTime, + $paypalRefundUpdateTime + ) { + $this->paypalRefundId = $paypalRefundId; + $this->paypalRefundAmount = $paypalRefundAmount; + $this->paypalRefundCurrency = $paypalRefundCurrency; + $this->paypalRefundCurrencyId = $paypalRefundCurrencyId; + $this->paypalRefundStatus = $paypalRefundStatus; + $this->paypalRefundNote = $paypalRefundNote; + $this->paypalRefundCreateTime = $paypalRefundCreateTime; + $this->paypalRefundUpdateTime = $paypalRefundUpdateTime; + } + + /** + * @return int + */ + public function getPaypalRefundId() + { + return $this->paypalRefundId; + } + + /** + * @return string + */ + public function getPaypalRefundAmount() + { + return $this->paypalRefundAmount; + } + + /** + * @return string + */ + public function getPaypalRefundCurrency() + { + return $this->paypalRefundCurrency; + } + + /** + * @return int + */ + public function getPaypalRefundCurrencyId() + { + return $this->paypalRefundCurrencyId; + } + + /** + * @return string + */ + public function getPaypalRefundStatus() + { + return $this->paypalRefundStatus; + } + + /** + * @return string + */ + public function getPaypalRefundNote() + { + return $this->paypalRefundNote; + } + + /** + * @return string + */ + public function getPaypalRefundCreateTime() + { + return $this->paypalRefundCreateTime; + } + + /** + * @return string + */ + public function getPaypalRefundUpdateTime() + { + return $this->paypalRefundUpdateTime; + } +} diff --git a/src/PayPal/Payment/Refund/Query/GetPayPalRefundQuery.php b/src/PayPal/Payment/Refund/Query/GetPayPalRefundQuery.php new file mode 100644 index 000000000..de239cb3d --- /dev/null +++ b/src/PayPal/Payment/Refund/Query/GetPayPalRefundQuery.php @@ -0,0 +1,45 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query; + +use Order; + +class GetPayPalRefundQuery +{ + /** @var Order */ + private $order; + + /** + * @param Order $order + */ + public function __construct($order) + { + $this->order = $order; + } + + /** + * @return Order + */ + public function getOrder() + { + return $this->order; + } +} diff --git a/src/PayPal/Payment/Refund/Query/GetPayPalRefundQueryResult.php b/src/PayPal/Payment/Refund/Query/GetPayPalRefundQueryResult.php new file mode 100644 index 000000000..a841fbdda --- /dev/null +++ b/src/PayPal/Payment/Refund/Query/GetPayPalRefundQueryResult.php @@ -0,0 +1,89 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query; + +use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Exception\PayPalOrderException; +use PrestaShop\Module\PrestashopCheckout\PayPal\Order\ValueObject\PayPalOrderId; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Capture\Exception\PayPalCaptureException; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Capture\ValueObject\PayPalCaptureId; + +class GetPayPalRefundQueryResult +{ + /** @var PayPalOrderId */ + private $paypalOrderId; + + /** @var PayPalCaptureId */ + private $paypalCaptureId; + + /** @var string */ + private $amount; + + /** @var string */ + private $currencyIsoCode; + + /** + * @param string $paypalOrderId + * @param string $paypalCaptureId + * @param string $amount + * @param string $currencyIsoCode + * + * @throws PayPalOrderException|PayPalCaptureException + */ + public function __construct($paypalOrderId, $paypalCaptureId, $amount, $currencyIsoCode) + { + $this->paypalOrderId = new PayPalOrderId($paypalOrderId); + $this->paypalCaptureId = new PayPalCaptureId($paypalCaptureId); + $this->amount = $amount; + $this->currencyIsoCode = $currencyIsoCode; + } + + /** + * @return string + */ + public function getPayPalOrderId() + { + return $this->paypalOrderId->getValue(); + } + + /** + * @return string + */ + public function getPayPalCaptureId() + { + return $this->paypalCaptureId->getValue(); + } + + /** + * @return string + */ + public function getAmount() + { + return $this->amount; + } + + /** + * @return string + */ + public function getCurrencyIsoCode() + { + return $this->currencyIsoCode; + } +} diff --git a/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundForPDFOrderSlipQueryHandler.php b/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundForPDFOrderSlipQueryHandler.php new file mode 100644 index 000000000..6c7844eb8 --- /dev/null +++ b/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundForPDFOrderSlipQueryHandler.php @@ -0,0 +1,115 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler; + +use Currency; +use Exception; +use Order; +use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundForPDFOrderSlipQuery; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundForPDFOrderSlipQueryResult; +use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalOrderProvider; +use PrestaShopCollection; +use PrestaShopDatabaseException; +use PrestaShopException; +use PsCheckoutCart; + +class GetPayPalRefundForPDFOrderSlipQueryHandler +{ + /** @var PayPalOrderProvider */ + private $paypalOrderProvider; + + /** + * @param PayPalOrderProvider $paypalOrderProvider + */ + public function __construct($paypalOrderProvider) + { + $this->paypalOrderProvider = $paypalOrderProvider; + } + + /** + * @param GetPayPalRefundForPDFOrderSlipQuery $getPayPalRefundQuery + * + * @return GetPayPalRefundForPDFOrderSlipQueryResult + * + * @throws PrestaShopDatabaseException|PrestaShopException|PsCheckoutException + */ + public function handle($getPayPalRefundQuery) + { + $orderSlip = $getPayPalRefundQuery->getOrderSlip(); + + $order = new Order($orderSlip->id_order); + + if ($order->module !== 'ps_checkout') { + throw new PsCheckoutException(); + } + + $psCheckoutCartCollection = new PrestaShopCollection('PsCheckoutCart'); + $psCheckoutCartCollection->where('id_cart', '=', (int) $order->id_cart); + $psCheckoutCartCollection->where('paypal_status', 'in', [PsCheckoutCart::STATUS_COMPLETED, PsCheckoutCart::STATUS_PARTIALLY_COMPLETED]); + $psCheckoutCartCollection->orderBy('date_upd', 'ASC'); + + if (!$psCheckoutCartCollection->count()) { + throw new PsCheckoutException(); + } + + /** @var PsCheckoutCart|bool $psCheckoutCart */ + $psCheckoutCart = $psCheckoutCartCollection->getFirst(); + + if (!$psCheckoutCart) { + throw new PsCheckoutException(); + } + + try { + $paypalOrder = $this->paypalOrderProvider->getById($psCheckoutCart->paypal_order); + } catch (Exception $exception) { + throw new PsCheckoutException(); + } + + if (!isset($paypalOrder['purchase_units'][0]['payments']['refunds'][0])) { + throw new PsCheckoutException(); + } + + // TODO: Clean it when we'll have db values + foreach ($paypalOrder['purchase_units'][0]['payments']['refunds'] as $refund) { + if (number_format($refund['amount']['value'], 2) !== number_format($orderSlip->amount, 2)) { + continue; + } + + $paypalRefund = $refund; + } + + if (!isset($paypalRefund)) { + throw new PsCheckoutException(); + } + + return new GetPayPalRefundForPDFOrderSlipQueryResult( + $paypalRefund['id'], + $paypalRefund['amount']['value'], + $paypalRefund['amount']['currency_code'], + Currency::getIdByIsoCode($paypalRefund['amount']['currency_code'], $order->id_shop), + $paypalRefund['status'], + isset($paypalRefund['note_to_payer']) ? $paypalRefund['note_to_payer'] : '', + isset($paypalRefund['create_time']) ? $paypalRefund['create_time'] : '', + isset($paypalRefund['update_time']) ? $paypalRefund['update_time'] : '' + ); + } +} diff --git a/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundQueryHandler.php b/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundQueryHandler.php new file mode 100644 index 000000000..45ad7e1bf --- /dev/null +++ b/src/PayPal/Payment/Refund/QueryHandler/GetPayPalRefundQueryHandler.php @@ -0,0 +1,164 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\QueryHandler; + +use Currency; +use Customer; +use Exception; +use Group; +use OrderSlip; +use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundQuery; +use PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\Query\GetPayPalRefundQueryResult; +use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalOrderProvider; +use PrestaShopCollection; +use PrestaShopException; +use PsCheckoutCart; +use Validate; + +class GetPayPalRefundQueryHandler +{ + /** @var PayPalOrderProvider */ + private $paypalOrderProvider; + + /** + * @param PayPalOrderProvider $paypalOrderProvider + */ + public function __construct($paypalOrderProvider) + { + $this->paypalOrderProvider = $paypalOrderProvider; + } + + /** + * @param GetPayPalRefundQuery $getRefundTotalAmountQuery + * + * @return GetPayPalRefundQueryResult + * + * @throws PsCheckoutException|PrestaShopException + */ + public function handle(GetPayPalRefundQuery $getRefundTotalAmountQuery) + { + $order = $getRefundTotalAmountQuery->getOrder(); + + $psCheckoutCartCollection = new PrestaShopCollection('PsCheckoutCart'); + $psCheckoutCartCollection->where('id_cart', '=', $order->id_cart); + $psCheckoutCartCollection->where('paypal_status', 'in', [PsCheckoutCart::STATUS_COMPLETED, PsCheckoutCart::STATUS_PARTIALLY_COMPLETED]); + $psCheckoutCartCollection->orderBy('date_upd', 'ASC'); + + if (!$psCheckoutCartCollection->count()) { + throw new PsCheckoutException(sprintf('Unable to retrieve a PsCheckoutCartCollection for Cart %s', $order->id_cart)); + } + + /** @var PsCheckoutCart|bool $psCheckoutCart */ + $psCheckoutCart = $psCheckoutCartCollection->getFirst(); + + if (!$psCheckoutCart) { + throw new PsCheckoutException(sprintf('Unable to retrieve a PsCheckoutCart for Cart %s', $order->id_cart)); + } + + try { + $paypalOrder = $this->paypalOrderProvider->getById($psCheckoutCart->paypal_order); + } catch (Exception $exception) { + throw new PsCheckoutException(sprintf('Unable to retrieve PayPal Order %s', $psCheckoutCart->paypal_order)); + } + + if (!isset($paypalOrder['purchase_units'][0])) { + throw new PsCheckoutException(sprintf('Unable to get a Purchase Unit for PayPal Order %s', $psCheckoutCart->paypal_order)); + } + + $purchaseUnit = $paypalOrder['purchase_units'][0]; + if (!isset($purchaseUnit['payments']['captures'][0])) { + throw new PsCheckoutException(sprintf('Unable to get Capture for Order %s', $order->id)); + } + + $capture = $purchaseUnit['payments']['captures'][0]; + + /** @var OrderSlip[]|bool $orderSlipCollection */ + $orderSlipCollection = $order->getOrderSlipsCollection()->getResults(); + + if (!$orderSlipCollection) { + throw new PsCheckoutException(sprintf('Unable to get OrderSlip for Order %s', $order->id)); + } + + /** @var OrderSlip $orderSlip */ + $orderSlip = end($orderSlipCollection); + + if (!Validate::isLoadedObject($orderSlip)) { + throw new PsCheckoutException(sprintf('Unable to load last OrderSlip with id %s', $orderSlip->id)); + } + + $customer = new Customer((int) $order->id_customer); + $useTax = Group::getPriceDisplayMethod((int) $customer->id_default_group); + + $amount = $orderSlip->total_products_tax_incl; + if ($useTax) { + $amount = $orderSlip->total_products_tax_excl; + } + + if ($orderSlip->shipping_cost) { + if ($useTax) { + $amount += $orderSlip->total_shipping_tax_excl; + } else { + $amount += $orderSlip->total_shipping_tax_incl; + } + } + + // Refund based on product prices, but do not refund the voucher amount + $cartRuleTotal = 0; + if ($orderSlip->order_slip_type == 1 && is_array($cartRules = $order->getCartRules())) { + foreach ($cartRules as $cartRule) { + if ($useTax) { + $cartRuleTotal -= $cartRule['value_tax_excl']; + } else { + $cartRuleTotal -= $cartRule['value']; + } + } + } + + $amount += $cartRuleTotal; + + if ($amount <= 0) { + throw new PsCheckoutException('Refund amount cannot be less than or equal to zero'); + } + + $totalCaptured = (float) $capture['amount']['value']; + + $totalAlreadyRefund = 0; + if (isset($purchaseUnit['payments']['refunds'])) { + $totalAlreadyRefund = array_reduce($purchaseUnit['payments']['refunds'], function ($totalRefunded, $refund) { + return $totalRefunded + (float) $refund['amount']['value']; + }); + } + + if ($totalCaptured < $amount + $totalAlreadyRefund) { + throw new PsCheckoutException(sprintf('Refund amount %s is greater than captured amount %s', $totalCaptured, $amount)); + } + + $currency = new Currency($order->id_currency); + + return new GetPayPalRefundQueryResult( + $psCheckoutCart->getPaypalOrderId(), + $capture['id'], + (string) $amount, + $currency->iso_code + ); + } +} diff --git a/views/templates/hook/displayPDFOrderSlip.tpl b/views/templates/hook/displayPDFOrderSlip.tpl new file mode 100644 index 000000000..3ab768242 --- /dev/null +++ b/views/templates/hook/displayPDFOrderSlip.tpl @@ -0,0 +1,33 @@ +{** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License version 3.0 + * that is bundled with this package in the file LICENSE.md. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA and Contributors + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + *} + + + + + + + + + + + + + + +
{l s='Transaction reference' d='Shop.Pdf' pdf='true'}{$refund_id}
{l s='Amount' d='Shop.Pdf' pdf='true'}{displayPrice currency=$refund_currency_id price=$refund_amount}
{l s='Status' d='Shop.Pdf' pdf='true'}{$refund_status}