Skip to content

Commit

Permalink
Merge pull request #247 from OXID-eSales/UNZER-312_PaymentWithoutOrder
Browse files Browse the repository at this point in the history
Unzer 312 payment without order
  • Loading branch information
mariolorenz authored Apr 4, 2024
2 parents c420f91 + f415a7d commit d13749f
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 68 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### NEW
- new Paymentmethod Unzer installment (Paylater)
- refactor basket-change-check before payment
- fix Typed property must not be accessed before initialization in Payment-Service
- If customers - for whatever reason - interrupt the order in the checkout, the order is still saved using a temporary order and Unzer's webhook

### FIXED
- [0007553](https://bugs.oxid-esales.com/view.php?id=7553) revert this task because, it is possible to have different billing and delivery addresses for invoice purchases (Paylater)
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' => '1.2.0-rc.3',
'version' => '1.2.0-rc.4',
'author' => 'OXID eSales AG',
'url' => 'https://www.oxid-esales.com',
'email' => '[email protected]',
Expand Down Expand Up @@ -424,6 +424,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;");
}
}
77 changes: 71 additions & 6 deletions src/Controller/DispatcherController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Request;
use OxidSolutionCatalysts\Unzer\Model\TmpOrder;
use OxidSolutionCatalysts\Unzer\Service\Transaction;
use OxidSolutionCatalysts\Unzer\Service\Translator;
use OxidSolutionCatalysts\Unzer\Service\UnzerSDKLoader;
use OxidSolutionCatalysts\Unzer\Service\UnzerWebhooks;
use OxidSolutionCatalysts\Unzer\Traits\ServiceContainer;
use UnzerSDK\Constants\PaymentState;
use UnzerSDK\Exceptions\UnzerApiException;
use UnzerSDK\Resources\Payment;

/**
* TODO: Decrease count of dependencies to 13
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class DispatcherController extends FrontendController
{
use ServiceContainer;
Expand Down Expand Up @@ -65,7 +71,6 @@ public function updatePaymentTransStatus(): void
$transaction = $this->getServiceFromContainer(Transaction::class);
$aPath = explode("/", $url['path']);
$typeid = end($aPath);

/** @var Request $request */
$request = Registry::getRequest();
/** @var string $context */
Expand All @@ -85,15 +90,14 @@ 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");
// 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 */
$order = oxNew(Order::class);
Expand Down Expand Up @@ -129,9 +133,70 @@ public function updatePaymentTransStatus(): void
} else {
$result = $translator->translate('oscunzer_TRANSACTION_NOTHINGTODO') . $paymentId;
}
} else {
$tmpOrder = oxNew(TmpOrder::class);
$iOrderId = $unzerPayment->getBasket() ? (int) $unzerPayment->getBasket()->getOrderId() : 0;
$tmpData = $tmpOrder->getTmpOrderByUnzerId($iOrderId);
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);
}
}
}

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

/**
* @param Payment $unzerPayment
* @param TmpOrder $tmpOrder
* @param array $tmpData
* @param bool $error
* @return void
* @throws Exception
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
protected function handleTmpOrder(
Payment $unzerPayment,
TmpOrder $tmpOrder,
array $tmpData,
bool $error = false
): 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 \OxidSolutionCatalysts\Unzer\Model\Order $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);
}

/**
* @param $tmpOrder TmpOrder
* @return bool
*/
protected 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;
}
}
5 changes: 3 additions & 2 deletions src/Controller/OrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace OxidSolutionCatalysts\Unzer\Controller;

use Exception;
use OxidEsales\Eshop\Application\Model\Basket;
use OxidEsales\Eshop\Application\Model\Country;
use OxidEsales\Eshop\Application\Model\Order;
Expand Down Expand Up @@ -105,7 +106,7 @@ public function execute()
/**
* @throws Redirect
* @throws DatabaseErrorException
* @throws \UnzerSDK\Exceptions\UnzerApiException
* @throws Exception
* @SuppressWarnings(PHPMD.StaticAccess)
* @SuppressWarnings(PHPMD.ElseExpression)
*/
Expand All @@ -130,7 +131,7 @@ public function unzerExecuteAfterRedirect(): void

$nextStep = $this->_getNextStep($iSuccess);
$unzerService = $this->getServiceFromContainer(Unzer::class);
if ('thankyou' === $nextStep) {
if (stripos($nextStep, 'thankyou') !== false) {
$oDB->commitTransaction();

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

use OxidEsales\Eshop\Application\Model\Order;
use OxidSolutionCatalysts\Unzer\Core\UnzerDefinitions as CoreUnzerDefinitions;
use OxidEsales\Eshop\Core\Session;
use OxidSolutionCatalysts\Unzer\Service\UnzerSDKLoader;
use OxidSolutionCatalysts\Unzer\Service\UserRepository;
use OxidSolutionCatalysts\Unzer\Core\UnzerDefinitions;
use OxidSolutionCatalysts\Unzer\Service\UnzerDefinitions as UnzerDefinitionsService;
use OxidSolutionCatalysts\Unzer\Service\ModuleSettings;
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 @@ -91,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 @@ -100,12 +103,49 @@ 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');
}
}
}

/**
* @param $session Session
* @return void
*/
protected function checkForDuplicateOrderAttempt(Session $session)
{
$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 d13749f

Please sign in to comment.