Skip to content

Commit

Permalink
Merge pull request #271 from OXID-eSales/b-7.0.x-loberon-UNZER-321
Browse files Browse the repository at this point in the history
UNZER-321 Add TmpOrder to store in between the checkout proccess
  • Loading branch information
mariolorenz authored May 30, 2024
2 parents e2242ce + 46803db commit 337f9b2
Show file tree
Hide file tree
Showing 22 changed files with 547 additions and 111 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).


## [2.2.0] - 2024-??-??
- [0007638](https://bugs.oxid-esales.com/view.php?id=7638) - Fix: Sometimes duplicate order-positions in Backend, and dublicate ordermails ...

### NEW
- provide content for smarty and twig
- If a customer interrupt the order in the checkout for any reason, the order is still saved using a temporary order and Unzer's webhook

### FIXED
- fix: PayPal sporadically fails on GHA CC Tests
- [0007638](https://bugs.oxid-esales.com/view.php?id=7638) - Fix: Sometimes duplicate order-positions in Backend, and dublicate ordermails ...

## [2.1.4] - 2023-11-23

Expand Down
8 changes: 7 additions & 1 deletion metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
</ul>',
],
'thumbnail' => 'logo.svg',
'version' => '2.2.0-rc.2',
'version' => '2.2.0-rc.3',
'author' => 'OXID eSales AG',
'url' => 'https://www.oxid-esales.com',
'email' => 'info@oxid-esales.com',
Expand Down Expand Up @@ -433,6 +433,12 @@
'type' => 'bool',
'value' => '0',
],
[
'group' => 'unzerother',
'name' => 'UnzerWebhookTimeDifference',
'type' => 'str',
'value' => '5',
],
// this options are invisible because of missing group
[
'group' => '',
Expand Down
42 changes: 42 additions & 0 deletions migration/data/Version20240321104606.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace OxidSolutionCatalysts\Unzer\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240321104606 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
if (!$schema->hasTable('oscunzertmporder')) {
$this->createTmpOrderTable();
}
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
}
protected function createTmpOrderTable()
{
$this->addSql("CREATE TABLE `oscunzertmporder` (
`OXID` char(32) NOT NULL,
`OXSHOPID` int(11) NOT NULL,
`OXORDERID` char(32) NOT NULL,
`OXUNZERORDERNR` int(11) NOT NULL,
`TMPORDER` mediumtext NOT NULL,
`STATUS` enum('FINISHED','NOT_FINISHED') NOT NULL,
`TIMESTAMP` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;");
}
}
2 changes: 1 addition & 1 deletion src/Controller/Admin/AdminOrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
namespace OxidSolutionCatalysts\Unzer\Controller\Admin;

use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Application\Model\Order;
use OxidEsales\Eshop\Core\Registry;
use OxidSolutionCatalysts\Unzer\Core\UnzerDefinitions;
use OxidSolutionCatalysts\Unzer\Model\Order;
use OxidSolutionCatalysts\Unzer\Model\Payment;
use OxidSolutionCatalysts\Unzer\Model\Order as UnzerOrder;
use OxidSolutionCatalysts\Unzer\Model\TransactionList;
Expand Down
136 changes: 103 additions & 33 deletions src/Controller/DispatcherController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
use JsonException;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Application\Model\Order;
use OxidSolutionCatalysts\Unzer\Model\Order as UnzerOrder;
use OxidSolutionCatalysts\Unzer\Model\TmpOrder;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
Expand All @@ -22,7 +24,11 @@
use OxidSolutionCatalysts\Unzer\Traits\ServiceContainer;
use UnzerSDK\Constants\PaymentState;
use UnzerSDK\Exceptions\UnzerApiException;
use UnzerSDK\Resources\Payment;

/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class DispatcherController extends FrontendController
{
use ServiceContainer;
Expand All @@ -39,6 +45,7 @@ class DispatcherController extends FrontendController
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ElseExpression)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
public function updatePaymentTransStatus(): void
{
Expand Down Expand Up @@ -86,52 +93,115 @@ public function updatePaymentTransStatus(): void
Registry::getUtils()->showMessageAndExit("No valid retrieveUrl");
}

$unzer = $this->getServiceFromContainer(UnzerSDKLoader::class)->getUnzerSDKbyKey($unzerKey);
$resource = $unzer->fetchResourceFromEvent($jsonRequest);
$paymentId = $resource->getId();

if (!$transaction->isValidTransactionTypeId($typeid)) {
Registry::getUtils()->showMessageAndExit("Invalid type id");
}

$unzer = $this->getServiceFromContainer(UnzerSDKLoader::class)->getUnzerSDKbyKey($unzerKey);
$resource = $unzer->fetchResourceFromEvent($jsonRequest);

$paymentId = $resource->getId();
if (is_string($paymentId)) {
/** @var \OxidSolutionCatalysts\Unzer\Model\Order $order */
/** @var UnzerOrder $order */
$order = oxNew(Order::class);
/** @var array $data */
$data = $transaction::getTransactionDataByPaymentId($paymentId);

$unzerPayment = $unzer->fetchPayment($paymentId);

if ($order->load($data[0]['OXORDERID'])) {
if ($unzerPayment->getState() === PaymentState::STATE_COMPLETED) {
$order->markUnzerOrderAsPaid();
}

if ($unzerPayment->getState() === PaymentState::STATE_CANCELED) {
$order->cancelOrder();
}

$translator = $this->getServiceFromContainer(Translator::class);
$transactionService = $this->getServiceFromContainer(Transaction::class);

if (
$transactionService->writeTransactionToDB(
$order->getId(),
$order->getOrderUser()->getId() ?: '',
$unzerPayment
)
) {
$result = sprintf(
$translator->translate('oscunzer_TRANSACTION_CHANGE'),
$unzerPayment->getStateName(),
$paymentId
);
} else {
$result = $translator->translate('oscunzer_TRANSACTION_NOTHINGTODO') . $paymentId;
}
$result = $this->writeOrder($unzerPayment, $order, $paymentId);
} else {
$this->prepareTmpOrder($unzerPayment);
}
}

Registry::getUtils()->showMessageAndExit($result);
}

private function handleTmpOrder(
Payment $unzerPayment,
TmpOrder $tmpOrder,
array $tmpData,
bool $error
): void {
$translator = $this->getServiceFromContainer(Translator::class);
$result = $translator->translate('oscunzer_ERROR_HANDLE_TMP_ORDER');
if ($tmpOrder->load($tmpData['OXID'])) {
$aOrderData = unserialize(base64_decode($tmpData['TMPORDER']), ['allowed_classes' => [Order::class]]);
/** @var UnzerOrder $oOrder */
$oOrder = is_array($aOrderData) && isset($aOrderData['order']) ? $aOrderData['order'] : null;
if ($oOrder) {
$oOrder->finalizeTmpOrder($unzerPayment, $error);
$tmpOrder->assign(['status' => 'FINISHED']);
$tmpOrder->save();
$result = $translator->translate('oscunzer_SUCCESS_HANDLE_TMP_ORDER');
}
}
Registry::getUtils()->showMessageAndExit($result);
}

private function prepareTmpOrder(Payment $unzerPayment): void
{
$tmpOrder = oxNew(TmpOrder::class);
$orderId = $unzerPayment->getBasket() ? $unzerPayment->getBasket()->getOrderId() : '';
$tmpData = $tmpOrder->getTmpOrderByUnzerId($orderId);
if (
isset($tmpData['OXID']) &&
$tmpOrder->load($tmpData['OXID']) &&
$this->hasExceededTimeLimit($tmpOrder)
) {
$bError = !($unzerPayment->getState() === PaymentState::STATE_COMPLETED ||
$unzerPayment->getState() === PaymentState::STATE_CANCELED ||
$unzerPayment->getState() === PaymentState::STATE_PENDING);
$this->handleTmpOrder($unzerPayment, $tmpOrder, $tmpData, $bError);
}
}

/**
* @param $tmpOrder TmpOrder
* @return bool
*/
private function hasExceededTimeLimit(TmpOrder $tmpOrder): bool
{
$defTimeDiffMin = Registry::getConfig()->getConfigParam('defTimeDiffMin', 5);
$timeDiffSec = $defTimeDiffMin * 60;
$tmpOrderTime = is_string($tmpOrder->getFieldData('timestamp'))
? $tmpOrder->getFieldData('timestamp')
: '';
$tmpOrderTimeUnix = strtotime($tmpOrderTime);
$nowTimeUnix = time();
$difference = $nowTimeUnix - $tmpOrderTimeUnix;

return $difference >= $timeDiffSec;
}

private function writeOrder(Payment $unzerPayment, UnzerOrder $order, string $paymentId): string
{
if ($unzerPayment->getState() === PaymentState::STATE_COMPLETED) {
$order->markUnzerOrderAsPaid();
}

if ($unzerPayment->getState() === PaymentState::STATE_CANCELED) {
$order->cancelOrder();
}

$translator = $this->getServiceFromContainer(Translator::class);
$transactionService = $this->getServiceFromContainer(Transaction::class);
$result = $translator->translate('oscunzer_TRANSACTION_NOTHINGTODO') . $paymentId;

if (
$transactionService->writeTransactionToDB(
$order->getId(),
$order->getOrderUser()->getId() ?: '',
$unzerPayment
)
) {
$result = sprintf(
$translator->translate('oscunzer_TRANSACTION_CHANGE'),
$unzerPayment->getStateName(),
$paymentId
);
}

return $result;
}
}
2 changes: 1 addition & 1 deletion src/Controller/OrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function unzerExecuteAfterRedirect(): void
$unzerService = $this->getServiceFromContainer(Unzer::class);
Registry::getSession()->setVariable('orderDisableSqlActiveSnippet', false);

if ('thankyou' === $nextStep) {
if (stripos($nextStep, 'thankyou') !== false) {
$oDB->commitTransaction();

$paymentService = $this->getServiceFromContainer(PaymentService::class);
Expand Down
50 changes: 45 additions & 5 deletions src/Controller/PaymentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
namespace OxidSolutionCatalysts\Unzer\Controller;

use OxidEsales\Eshop\Application\Model\Order;
use OxidEsales\Eshop\Core\Session;
use OxidSolutionCatalysts\Unzer\Service\UnzerSDKLoader;
use OxidSolutionCatalysts\Unzer\Service\UserRepository;
use OxidSolutionCatalysts\Unzer\Service\UnzerDefinitions as UnzerDefinitionsService;
use OxidSolutionCatalysts\Unzer\Traits\ServiceContainer;
use OxidEsales\Eshop\Application\Model\Payment;
use OxidEsales\Eshop\Core\Registry;
use UnzerSDK\Constants\PaymentState;
use UnzerSDK\Exceptions\UnzerApiException;

class PaymentController extends PaymentController_parent
{
Expand Down Expand Up @@ -88,6 +92,8 @@ public function getPaymentList()
*/
protected function checkForUnzerPaymentErrors(): void
{
$session = Registry::getSession();
$this->checkForDuplicateOrderAttempt($session);
/** @var \OxidSolutionCatalysts\Unzer\Model\Payment $payment */
$payment = oxNew(Payment::class);
$actualPaymentId = $this->getCheckedPaymentId();
Expand All @@ -97,12 +103,46 @@ protected function checkForUnzerPaymentErrors(): void
$payment->load($actualPaymentId) &&
$payment->isUnzerPayment()
) {
$session = Registry::getSession();
/** @var string $orderId */
$orderId = $session->getVariable('sess_challenge');
$order = oxNew(Order::class);
$order->delete($orderId);
$orderId = is_string($session->getVariable('sess_challenge')) ?
$session->getVariable('sess_challenge') :
'';
if ($orderId) {
$order = oxNew(Order::class);
$order->delete($orderId);
$session->deleteVariable('sess_challenge');
}
}
}


protected function checkForDuplicateOrderAttempt(Session $session): void
{
$unzerSDK = $this->getServiceFromContainer(UnzerSDKLoader::class);
$unzerSDK = $unzerSDK->getUnzerSDK();
$oxOrderIdOfTmpOrder = $session->getVariable('oxOrderIdOfTmpOrder');
$paymentId = is_string($session->getVariable('paymentid')) ? $session->getVariable('paymentid') : '';
if ($oxOrderIdOfTmpOrder) {
if ($paymentId) {
try {
$unzerPayment = $unzerSDK->fetchPayment($paymentId);
$unzerOrderId = $unzerPayment->getOrderId();
$sessionUnzerOrderId = $session->getVariable('UnzerOrderId');
if (
(int) $unzerOrderId === $sessionUnzerOrderId &&
($unzerPayment->getState() === PaymentState::STATE_COMPLETED ||
$unzerPayment->getState() === PaymentState::STATE_PENDING)
) {
$session->deleteVariable('paymentid');
$session->deleteVariable('UnzerOrderId');
}
} catch (UnzerApiException $e) {
Registry::getLogger()->warning(
'Payment not found with key: ' . $paymentId . ' and message: ' . $e->getMessage()
);
}
}
$session->deleteVariable('sess_challenge');
$session->deleteVariable('oxOrderIdOfTmpOrder');
}
}
}
Loading

0 comments on commit 337f9b2

Please sign in to comment.