Skip to content

Commit

Permalink
Merge pull request #44 from shopware5/pt-13151/capture-timeout
Browse files Browse the repository at this point in the history
PT-13151 - Handle session timeout while capture payment
  • Loading branch information
mitelg authored Mar 26, 2024
2 parents e0aa11c + 38697a6 commit 64d48ba
Show file tree
Hide file tree
Showing 27 changed files with 833 additions and 17 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/php-code-analysis-legacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: PHP

on:
workflow_call:

jobs:
call-legacy-analyse-workflow:
name: PHP legacy code analysis
runs-on: ubuntu-latest
container:
image: ghcr.io/shopware5/docker-images-testing/install:shopware_5.2_5.7_5.6_5.2.11-5.2.27
credentials:
username: ${{ github.actor }}
password: ${{ secrets.github_token }}
env:
GH_TOKEN: ${{ github.token }}

steps:
- run: /usr/bin/supervisord -c /etc/supervisord.conf &

- name: Checkout SwagPaymentPayPalUnified
uses: actions/checkout@v3
with:
path: plugin

- name: Move plugin
run: mv "$(pwd)/plugin" /shopware/custom/plugins/SwagPaymentPayPalUnified

- name: Setup SwagPaymentPayPalUnified
run: |
cd /shopware/custom/plugins/SwagPaymentPayPalUnified
make init-legacy
- name: Setup legacy tests
run: |
cd /shopware/custom/plugins/SwagPaymentPayPalUnified
composer require --dev phpcompatibility/php-compatibility
- name: Run legacy tests
run: |
cd /shopware/custom/plugins/SwagPaymentPayPalUnified
./vendor/bin/phpcs --config-set installed_paths vendor/phpcompatibility/php-compatibility
./vendor/bin/phpcs -p --ignore="./vendor/,./PhpStan/" --standard="PHPCompatibility" --runtime-set testVersion 5.6 ./
10 changes: 7 additions & 3 deletions .github/workflows/tests-launcher.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,22 @@ jobs:
uses: ./.github/workflows/php-code-analysis.yml
secrets: inherit

php-code-analysis-legacy:
name: PHP
uses: ./.github/workflows/php-code-analysis-legacy.yml
secrets: inherit

php-unit-tests-shopware-5-7:
name: Unit tests
uses: ./.github/workflows/php-unit-tests-shopware-5-7.yml
secrets: inherit
needs: [ javascript-code-analysis, php-code-analysis ]
needs: [ javascript-code-analysis, php-code-analysis, php-code-analysis-legacy ]

php-unit-tests-shopware-legacy-versions:
name: Unit tests legacy
uses: ./.github/workflows/php-unit-tests-shopware-legacy.yml
secrets: inherit
needs: [ javascript-code-analysis, php-code-analysis ]

needs: [ javascript-code-analysis, php-code-analysis, php-code-analysis-legacy ]

php-e2e-tests-shopware-5-7:
name: E2E
Expand Down
1 change: 1 addition & 0 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
'single_line_throw' => false,
'visibility_required' => ['elements' => ['property', 'method']],
'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false],
'nullable_type_declaration_for_default_null_value' => false,

NoSuperfluousConcatenationFixer::name() => true,
NoUselessCommentFixer::name() => true,
Expand Down
24 changes: 24 additions & 0 deletions Components/Exception/InvalidOrderException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* (c) shopware AG <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace SwagPaymentPayPalUnified\Components\Exception;

use Exception;

class InvalidOrderException extends Exception
{
/**
* @param string $paypalOrderId
*/
public function __construct($paypalOrderId)
{
$message = \sprintf('No capture found in order with ID: %s', $paypalOrderId);

parent::__construct($message);
}
}
24 changes: 24 additions & 0 deletions Components/Exception/TimeoutInfoException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* (c) shopware AG <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace SwagPaymentPayPalUnified\Components\Exception;

use Exception;

class TimeoutInfoException extends Exception
{
/**
* @param string $paypalOrderId
*/
public function __construct($paypalOrderId)
{
$message = \sprintf('Timeout information not found for PayPal order with ID: %s', $paypalOrderId);

parent::__construct($message);
}
}
120 changes: 120 additions & 0 deletions Components/Services/TimeoutRefundService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/**
* (c) shopware AG <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace SwagPaymentPayPalUnified\Components\Services;

use DateTime;
use Doctrine\DBAL\Connection;
use PDO;
use Shopware\Bundle\StoreFrontBundle\Service\Core\ContextService;
use SwagPaymentPayPalUnified\Components\Exception\TimeoutInfoException;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Api\Order\PurchaseUnit\Payments\Refund;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Api\Order\PurchaseUnit\Payments\Refund\Amount;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Resource\CaptureResource;

class TimeoutRefundService
{
/**
* @var Connection
*/
private $connection;

/**
* @var ContextService
*/
private $contextService;

/**
* @var CaptureResource
*/
private $captureResource;

public function __construct(
Connection $connection,
ContextService $contextService,
CaptureResource $captureResource
) {
$this->connection = $connection;
$this->contextService = $contextService;
$this->captureResource = $captureResource;
}

/**
* @param string $payPalOrderId
* @param float $orderAmount
*
* @return void
*/
public function saveInfo($payPalOrderId, $orderAmount)
{
$this->connection->insert('swag_payment_paypal_unified_order_refund_info', [
'paypal_order_id' => (string) $payPalOrderId,
'order_amount' => (string) $orderAmount,
'currency' => $this->contextService->getShopContext()->getCurrency()->getCurrency(),
'created_at' => (string) (new DateTime())->getTimestamp(),
]);
}

/**
* @param string $payPalOrderId
*
* @return void
*/
public function deleteInfo($payPalOrderId)
{
$this->connection->delete('swag_payment_paypal_unified_order_refund_info', [
'paypal_order_id' => $payPalOrderId,
]);
}

/**
* @param string $payPalOrderId
* @param string $captureId
*
* @return void
*/
public function refund($payPalOrderId, $captureId)
{
$info = $this->getInfo($payPalOrderId);
if (!\is_array($info)) {
throw new TimeoutInfoException($payPalOrderId);
}

$amount = new Amount();
$amount->setCurrencyCode($info['currency']);
$amount->setValue($info['order_amount']);

$refund = new Refund();
$refund->setAmount($amount);
$refund->setNoteToPayer('User timeout');

$this->captureResource->refund($captureId, $refund);
}

/**
* @param string $payPalOrderId
*
* @return array<string, string>|null
*/
private function getInfo($payPalOrderId)
{
$result = $this->connection->createQueryBuilder()
->select(['*'])
->from('swag_payment_paypal_unified_order_refund_info')
->where('paypal_order_id = :paypalOrderId')
->setParameter('paypalOrderId', $payPalOrderId)
->execute()
->fetch(PDO::FETCH_ASSOC);

if (!\is_array($result)) {
return null;
}

return $result;
}
}
2 changes: 1 addition & 1 deletion Controllers/Backend/PaypalUnifiedSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public function updateCredentialsAction()
$credentialsService->updateCredentials($credentials, $shopId, $sandbox);

$this->updateOnboardingStatus($shopId, $sandbox);
} catch (\Exception $e) {
} catch (Exception $e) {
$this->response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
$this->view->assign([
'exception' => $e->getMessage(),
Expand Down
6 changes: 3 additions & 3 deletions Controllers/Frontend/PaypalUnified.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Shopware_Controllers_Frontend_PaypalUnified extends Shopware_Controllers_F
private $settingsService;

/**
* @var \Shopware_Components_Config
* @var Shopware_Components_Config
*/
private $shopwareConfig;

Expand Down Expand Up @@ -141,7 +141,7 @@ public function gatewayAction()
$this->handleError(ErrorCodes::COMMUNICATION_FAILURE, $requestEx);

return;
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->handleError(ErrorCodes::UNKNOWN, $exception);

return;
Expand All @@ -163,7 +163,7 @@ public function gatewayAction()
$addressPatch,
$payerInfoPatch,
]);
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->handleError(ErrorCodes::ADDRESS_VALIDATION_ERROR, $exception);

return;
Expand Down
54 changes: 54 additions & 0 deletions Controllers/Frontend/PaypalUnifiedApm.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,32 @@
*/

use SwagPaymentPayPalUnified\Components\ErrorCodes;
use SwagPaymentPayPalUnified\Components\Exception\InvalidOrderException;
use SwagPaymentPayPalUnified\Components\Exception\TimeoutInfoException;
use SwagPaymentPayPalUnified\Components\PayPalOrderParameter\ShopwareOrderData;
use SwagPaymentPayPalUnified\Components\Services\TimeoutRefundService;
use SwagPaymentPayPalUnified\Controllers\Frontend\AbstractPaypalPaymentController;
use SwagPaymentPayPalUnified\Controllers\Frontend\Exceptions\EmptyCartException;
use SwagPaymentPayPalUnified\Controllers\Frontend\Exceptions\InvalidBillingAddressException;
use SwagPaymentPayPalUnified\Controllers\Frontend\Exceptions\InvalidShippingAddressException;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Api\Common\Link;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Api\Order;
use SwagPaymentPayPalUnified\PayPalBundle\V2\Api\Order\PurchaseUnit\Payments\Capture;

class Shopware_Controllers_Frontend_PaypalUnifiedApm extends AbstractPaypalPaymentController
{
/**
* @var TimeoutRefundService
*/
private $timeoutRefundService;

public function preDispatch()
{
parent::preDispatch();

$this->timeoutRefundService = $this->get('swag_payment_paypal_unified.timeout_refund_service');
}

/**
* @return void
*/
Expand Down Expand Up @@ -85,6 +101,8 @@ public function indexAction()
return;
}

$this->timeoutRefundService->saveInfo($payPalOrder->getId(), $this->getAmount());

$url = $this->getUrl($payPalOrder, Link::RELATION_PAYER_ACTION_REQUIRED);

$this->logger->debug(sprintf('%s REDIRECT TO: %s', __METHOD__, $url));
Expand Down Expand Up @@ -113,6 +131,27 @@ public function returnAction()
}

if (!$this->isCartValid($payPalOrder)) {
if ($this->getUser() === null) {
try {
$this->timeoutRefundService->refund($payPalOrderId, $this->getCaptureId($payPalOrder));
} catch (TimeoutInfoException $exception) {
$this->logger->error($exception->getMessage());
} catch (InvalidOrderException $exception) {
$this->logger->error($exception->getMessage());
}

$this->timeoutRefundService->deleteInfo($payPalOrderId);

$this->redirect([
'module' => 'frontend',
'controller' => 'register',
'action' => 'index',
'paymentApproveTimeout' => true,
]);

return;
}

$redirectDataBuilder = $this->redirectDataBuilderFactory->createRedirectDataBuilder()
->setCode(ErrorCodes::BASKET_VALIDATION_ERROR);

Expand All @@ -121,6 +160,8 @@ public function returnAction()
return;
}

$this->timeoutRefundService->deleteInfo($payPalOrderId);

if ($this->Request()->isXmlHttpRequest()) {
$this->view->assign('token', $payPalOrderId);

Expand Down Expand Up @@ -180,4 +221,17 @@ public function returnAction()
'requireContactToMerchant' => true,
]);
}

/**
* @return string
*/
private function getCaptureId(Order $payPalOrder)
{
$capture = $this->orderPropertyHelper->getFirstCapture($payPalOrder);
if (!$capture instanceof Capture) {
throw new InvalidOrderException($payPalOrder->getId());
}

return $capture->getId();
}
}
2 changes: 1 addition & 1 deletion Controllers/Widgets/PayPalUnifiedOrderNumber.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function restoreOrderNumberAction()

try {
$this->orderNumberService->restoreOrderNumberToPool();
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->logger->error(sprintf('%s Cannot restore order number to pool.', __METHOD__), [
'exceptionMessage' => $exception->getMessage(),
'exceptionTrace' => $exception->getTrace(),
Expand Down
2 changes: 1 addition & 1 deletion Controllers/Widgets/PaypalUnifiedV2PayUponInvoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function pollOrderAction()
$payPalOrderId = $this->request->get('sUniqueID');

if (!\is_string($payPalOrderId)) {
throw new \DomainException('The Paypal id must exist in the session');
throw new DomainException('The Paypal id must exist in the session');
}

$order = $this->orderResource->get($payPalOrderId);
Expand Down
Loading

0 comments on commit 64d48ba

Please sign in to comment.