This commit is contained in:
2025-03-21 20:24:43 +01:00
parent 224398df90
commit f34c9162d4
12427 changed files with 5329941 additions and 373384 deletions

View File

@@ -3,11 +3,14 @@
* Class Przelewy24ServiceRefund
*
* This service has the features required for cash returns.
*
* @author Przelewy24
* @copyright Przelewy24
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html
*
*/
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Class Przelewy24ServiceRefund
@@ -22,23 +25,23 @@ class Przelewy24ServiceRefund extends Przelewy24Service
private $suffix = '';
/**
* P24 soap api client.
* P24 REST api client.
*
* @var SoapClient
* @var Przelewy24RestRefund
*/
private $soap;
private $restApi;
/**
* Array of allowed refund statuses.
*
* @var array
*/
private $status = array(
private $status = [
0 => 'Refund error',
1 => 'Refund done',
3 => 'Awaiting for refund',
4 => 'Refund rejected',
);
];
/**
* Default refund status.
@@ -50,14 +53,14 @@ class Przelewy24ServiceRefund extends Przelewy24Service
/**
* Przelewy24ServiceRefund constructor.
*
* @param Przelewy24 $przelewy24
* @param string $suffix
* @param Przelewy24Soap $przelewy24Soap
* @param Przelewy24 $przelewy24
* @param string $suffix
* @param Przelewy24RestRefund $restApi
*/
public function __construct(Przelewy24 $przelewy24, $suffix, $przelewy24Soap)
public function __construct(Przelewy24 $przelewy24, $suffix, $restApi)
{
$this->setSuffix($suffix);
$this->soap = $przelewy24Soap->getSoapClient();
$this->restApi = $restApi;
parent::__construct($przelewy24);
}
@@ -71,61 +74,16 @@ class Przelewy24ServiceRefund extends Przelewy24Service
$this->suffix = $suffix;
}
/**
* Get refunds.
*
* @param int $orderId
*
* @return array
*/
public function getRefunds($orderId)
{
$return = array();
try {
$soap = $this->soap;
$result = $soap->GetRefundInfo(
Configuration::get('P24_MERCHANT_ID' . $this->suffix),
Configuration::get('P24_API_KEY' . $this->suffix),
$orderId
);
if ($result && is_array($result->result)) {
$maxToRefund = 0;
$refunds = array();
foreach ($result->result as $key => $value) {
$refunds[$key]['amount_refunded'] = $value->amount;
$date = new DateTime($value->date);
$refunds[$key]['created'] = $date->format('Y-m-d H:i:s');
$refunds[$key]['status'] = $this->getStatusMessage($value->status);
if ((1 === $value->status) || (3 === $value->status)) {
$maxToRefund += $value->amount;
}
}
$return = array(
'maxToRefund' => $maxToRefund,
'refunds' => $refunds,
);
}
return $return;
} catch (Exception $e) {
PrestaShopLogger::addLog(__METHOD__ . ' ' . $e->getMessage(), 1);
return array();
}
}
/**
* Get status message.
*
* @param integer $status
* @param int $status
*
* @return string
*/
public function getStatusMessage($status)
{
$status = (int)$status;
$status = (int) $status;
$return = $this->statusDefault;
if (isset($this->status[$status])) {
$return = $this->status[$status];
@@ -134,62 +92,52 @@ class Przelewy24ServiceRefund extends Przelewy24Service
return $return;
}
/**
* Check if refund is possible and returns data to refund.
*
* @param int $orderId
*
* @return array
*
* @throws PrestaShopDatabaseException
*/
public function checkIfRefundIsPossibleAndReturnDataToRefund($orderId)
private function extractDate($input)
{
$return = array();
$dataFromDB = $this->getRefundDataFromDB($orderId);
if ($dataFromDB) {
$dataFromP24 = $this->checkIfPaymentCompletedUsingWSAndReturnData($dataFromDB['sessionId']);
if ($dataFromP24) {
$return = array(
'sessionId' => $dataFromDB['sessionId'],
'amount' => isset($dataFromP24->result->amount) ? (int)$dataFromP24->result->amount : 0,
'p24OrderId' => isset($dataFromP24->result->orderId) ? (int)$dataFromP24->result->orderId : 0,
);
}
$rx = '/^(?P<Y>\\d{4})(?P<m>\\d{2})(?P<d>\\d{2})/';
if (preg_match($rx, $input, $m)) {
return $m['Y'] . '-' . $m['m'] . '-' . $m['d'];
} else {
return '';
}
return $return;
}
/**
* Check if payment is completed using ws and return data.
* Get refund data from Przelewy24.
*
* @param $sessionId
* @param array $refundDataFromDb
*
* @return bool
* @return array
*/
public function checkIfPaymentCompletedUsingWSAndReturnData($sessionId)
public function getDataToRefund(array $refundDataFromDb)
{
try {
$soap = $this->soap;
$result = $soap->GetTransactionBySessionId(
Configuration::get('P24_MERCHANT_ID' . $this->suffix),
Configuration::get('P24_API_KEY' . $this->suffix),
$sessionId
);
if (isset($result->error->errorCode, $result->result->status) &&
(($result->error->errorCode > 0) || (2 !== (int)$result->result->status))
) {
return false;
$return = [];
$order = $this->restApi->transactionBySessionId($refundDataFromDb['sessionId']);
$refunds = $this->restApi->refundByOrderId($refundDataFromDb['p24OrderId']);
if (isset($order['data'])) {
$return = [
'sessionId' => (string) $order['data']['sessionId'],
'p24OrderId' => (int) $order['data']['orderId'],
'originalAmount' => (int) $order['data']['amount'],
'amount' => null, /* Reserve field position. */
'refunded' => 0,
'refunds' => [],
];
if (isset($refunds['data'])) {
foreach ($refunds['data']['refunds'] as $refund) {
$amountRefunded = (int) $refund['amount'];
$return['refunded'] += $amountRefunded;
$return['refunds'][] = [
'amount_refunded' => $amountRefunded,
'created' => $this->extractDate($refund['date']),
'status' => $this->status[$refund['status']],
];
}
}
return $result;
} catch (Exception $e) {
PrestaShopLogger::addLog(__METHOD__ . ' ' . $e->getMessage(), 1);
return false;
$return['amount'] = $return['originalAmount'] - $return['refunded'];
}
return $return;
}
/**
@@ -203,16 +151,16 @@ class Przelewy24ServiceRefund extends Przelewy24Service
*/
public function getRefundDataFromDB($orderId)
{
$return = array();
$return = [];
$orderId = (int)$orderId;
$orderId = (int) $orderId;
$przelewy24Order = new Przelewy24Order();
$result = $przelewy24Order->getByPshopOrderId($orderId);
if ($result) {
$return = array(
$return = [
'sessionId' => $result->p24_session_id,
'p24OrderId' => $result->p24_order_id,
);
];
}
return $return;
@@ -223,93 +171,257 @@ class Przelewy24ServiceRefund extends Przelewy24Service
*
* @param string $sessionId
* @param int $p24OrderId
* @param float $amountToRefund
* @param int $amountToRefund
*
* @return false|stdClass
*/
public function refundProcess($sessionId, $p24OrderId, $amountToRefund)
{
$refunds = array(
array(
'sessionId' => $sessionId,
'orderId' => $p24OrderId,
'amount' => $amountToRefund,
),
);
$sessionId = (string) $sessionId;
$p24OrderId = (int) $p24OrderId;
$amountToRefund = (int) $amountToRefund;
try {
return $this->soap->refundTransaction(
Configuration::get('P24_MERCHANT_ID' . $this->suffix),
Configuration::get('P24_API_KEY' . $this->suffix),
time(),
$refunds
);
} catch (Exception $e) {
PrestaShopLogger::addLog(__METHOD__ . ' ' . $e->getMessage(), 1);
return false;
$response = $this->restApi->transactionRefund($p24OrderId, $sessionId, $amountToRefund);
if (isset($response['data'][0])) {
return $response['data'][0]['amount'] === $amountToRefund;
}
return false;
}
/**
* The function checks the availability of the following functions :
* GetTransactionBySessionId
* RefundTransaction
* Update products for a refund.
*
* @param Order $order A order
* @param array $products List of products to refund
* @return bool
*/
public function checkRefundFunction()
public function updateProductsForRefund($order, $products)
{
try {
return (
$this->soapMethodExists('GetTransactionBySessionId') &&
$this->soapMethodExists('RefundTransaction')
);
} catch (Exception $e) {
PrestaShopLogger::addLog(__METHOD__ . ' ' . $e->getMessage(), 1);
return false;
$ok = true;
if (Configuration::get('P24_REFUND_WITH_ALTER_STOCK')) {
$ok &= $this->updateStock($order, $products);
}
$ok &= $this->addOrderSlip($order, $products);
$ok &= $this->updateOrderDetails($order, $products);
return (bool) $ok;
}
/**
* The function checks the availability of the following functions :
* GetRefundInfo
* Update stock.
*
* @return bool
* @param Order $order A order
* @param array $products List of products to refund
* @return float|null
*/
public function checkGetRefundInfo()
private function updateStock($order, $products)
{
try {
return ($this->soapMethodExists('GetRefundInfo'));
} catch (Exception $e) {
PrestaShopLogger::addLog(__METHOD__ . ' ' . $e->getMessage(), 1);
$totalPrice = 0.0;
$removed = false;
return false;
}
}
$orderProducts = $order->getProductsDetail();
foreach ($products as $productId => $quantity) {
if (!$quantity) {
continue;
}
foreach ($orderProducts as $orderProduct) {
if ((int) $orderProduct['id_order_detail'] === (int) $productId) {
$productObject = new Product($orderProduct['product_id']);
$stock = new StockAvailable();
$stock->updateQuantity(
$orderProduct['product_id'],
$orderProduct['product_attribute_id'],
$quantity
);
$totalPrice += round($productObject->price * $quantity, 2);
$totalPrice = round($totalPrice, 2);
$removed = true;
/**
* Checks if soap method exists.
*
* @param string $method
*
* @return bool
*/
private function soapMethodExists($method)
{
$return = false;
$list = $this->soap->__getFunctions();
if (is_array($list)) {
foreach ($list as $line) {
$explodeLine = explode(' ', $line, 2);
if (array_key_exists(1, $explodeLine) && (0 === strpos($explodeLine[1], $method))) {
$return = true;
break;
}
}
}
return $return;
if ($removed) {
$order->update();
return $totalPrice;
} else {
return null;
}
}
/**
* Update a order slip.
*
* @param Order $order A order
* @param array $products List of products to refund
* @return bool
*/
private function addOrderSlip(Order $order, $products)
{
$orderProducts = $order->getProductsDetail();
$productsToPass = [];
foreach ($products as $productId => $quantity) {
if (!$quantity) {
continue;
}
foreach ($orderProducts as $orderProduct) {
if ((int) $orderProduct['id_order_detail'] === (int) $productId) {
$quantity = min((int) $quantity, (int) $orderProduct['product_quantity']);
$orderProduct['quantity'] = $quantity;
$orderProduct['unit_price'] = $orderProduct['unit_price_tax_excl'];
$productsToPass[] = $orderProduct;
break;
}
}
}
return OrderSlip::create($order, $productsToPass);
}
/**
* Update all order dtails.
*
* @param Order $order A order
* @param array $products Array of products to update
* @return bool True on cucess
*/
private function updateOrderDetails(Order $order, $products)
{
$orderProducts = $order->getProductsDetail();
$success = true;
foreach ($products as $productId => $quantity) {
if (!$quantity) {
continue;
}
$productFound = false;
foreach ($orderProducts as $orderProduct) {
if ((int) $orderProduct['id_order_detail'] === (int) $productId) {
$quantity = min((int) $quantity, (int) $orderProduct['product_quantity']);
if ($quantity) {
$this->updateOneLineOfOrderDetails($productId, $quantity);
}
$productFound = true;
break;
}
}
if (!$productFound) {
$success = false;
}
}
$order->update();
return $success;
}
/**
* Internal update one order detail.
*
* @param int $id_order_detail Id of a order detail
* @param int $quantity Quantity of a product to refund
* @return void
*/
private function updateOneLineOfOrderDetails($id_order_detail, $quantity)
{
$orderDetail = new OrderDetail($id_order_detail);
if (!property_exists($orderDetail, 'total_refunded_tax_incl')) {
/* Old version. Nothing to do. */
return;
}
if (!property_exists($orderDetail, 'total_refunded_tax_excl')) {
/* Old version. Nothing to do. */
return;
}
$withTaxUnit = round($orderDetail->unit_price_tax_incl, 2);
$withoutTaxUnit = round($orderDetail->unit_price_tax_excl, 2);
$withTax = round($withTaxUnit * $quantity, 2);
$withoutTax = round($withoutTaxUnit * $quantity, 2);
$withTaxRefunded = round($orderDetail->total_refunded_tax_incl, 2);
$withoutTaxRefunded = round($orderDetail->total_refunded_tax_excl, 2);
$withTaxRefunded = round($withTaxRefunded + $withTax, 2);
$withoutTaxRefunded = round($withoutTaxRefunded + $withoutTax, 2);
$orderDetail->total_refunded_tax_incl = $withTaxRefunded;
$orderDetail->total_refunded_tax_excl = $withoutTaxRefunded;
$orderDetail->update();
}
/**
* Get list of products that may be refunded.
*
* @param Order $order Order that may be refunded
* @return array List of products
*/
public function extractProductsDetails($order)
{
$context = Context::getContext();
if (method_exists($context, 'getCurrentLocale')) {
return $this->extractProductsDetailsModern($order, $context);
} else {
return $this->extractProductsDetailsLegacy($order, $context);
}
}
/**
* Get list of products that may be refunded. Modern version.
*
* @param Order $order Order that may be refunded
* @param Context $context Context of request
* @return array List of products
*/
private function extractProductsDetailsModern($order, $context)
{
$currency = Currency::getCurrencyInstance($order->id_currency);
$locale = $context->getCurrentLocale();
$products = $order->getProductsDetail();
$ret = [];
foreach ($products as $product) {
$line = [
'productId' => $product['id_order_detail'],
'name' => $product['product_name'],
'quantity' => $product['product_quantity'] - $product['product_quantity_refunded'],
'price' => $product['unit_price_tax_incl'],
'priceFormatted' => $locale->formatPrice($product['unit_price_tax_incl'], $currency->iso_code),
];
$ret[] = $line;
}
return $ret;
}
/**
* Get list of products that may be refunded. Legacy version.
*
* It is used for versions older than 1.7.6.
*
* @param Order $order Order that may be refunded
* @param Context $context Context of request
* @return array List of products
*/
private function extractProductsDetailsLegacy($order, $context)
{
$currency = Currency::getCurrencyInstance($order->id_currency);
$products = $order->getProductsDetail();
$ret = [];
foreach ($products as $product) {
$line = [
'productId' => $product['id_order_detail'],
'name' => $product['product_name'],
'quantity' => $product['product_quantity'] - $product['product_quantity_refunded'],
'price' => $product['unit_price_tax_incl'],
'priceFormatted' => Tools::displayPrice($product['unit_price_tax_incl'], $currency, false, $context),
];
$ret[] = $line;
}
return $ret;
}
}