Skip to content

Commit

Permalink
RATESWSX-307
Browse files Browse the repository at this point in the history
  • Loading branch information
rommelfreddy committed Sep 12, 2024
1 parent 6733ab0 commit f6b0c0d
Show file tree
Hide file tree
Showing 31 changed files with 302 additions and 120 deletions.
2 changes: 1 addition & 1 deletion src/Components/Account/Subscriber/AccountSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public function onPaymentOrderRouteRequest(SetPaymentOrderRouteRequestEvent $eve
$requestData = new DataBag($event->getStorefrontRequest()->request->all());
$ratepayData = RequestHelper::getRatepayData($requestData);

$validationDefinitions = $paymentHandler->getValidationDefinitions($requestData, $orderEntity);
$validationDefinitions = $paymentHandler->getValidationDefinitions($requestData, $event->getSalesChannelContext(), $orderEntity);
$definition = new DataValidationDefinition();
$definition->addSub(RequestHelper::RATEPAY_DATA_KEY, DataValidationHelper::addSubConstraints(new DataValidationDefinition(), $validationDefinitions));
try {
Expand Down
3 changes: 3 additions & 0 deletions src/Components/AdminOrders/DependencyInjection/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<service id="Ratepay\RpayPayments\Components\AdminOrders\Service\DfpService"
decorates="Ratepay\RpayPayments\Components\DeviceFingerprint\DfpService">
<argument key="$decorated" type="service" id=".inner"/>
</service>

<service id="Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService">
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
</service>
</services>
Expand Down
11 changes: 3 additions & 8 deletions src/Components/AdminOrders/DependencyInjection/subscriber.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,9 @@
<tag name="kernel.event_subscriber"/>
</defaults>

<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\ProfileConfigSubscriber">
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
</service>

<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\PageSubscriber">
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
<tag name="kernel.event_subscriber"/>
</service>
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\ProfileConfigSubscriber"/>
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\PageSubscriber"/>
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\LoginSubscriber"/>

</services>

Expand Down
20 changes: 10 additions & 10 deletions src/Components/AdminOrders/Service/DfpService.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,32 @@ class DfpService implements DfpServiceInterface
public function __construct(
private readonly DfpServiceInterface $decorated,
private readonly RequestStack $requestStack,
private readonly string $sessionKey
private readonly SessionService $sessionService
) {
}

public function generatedDfpId(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
public function generatedDfpId(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
{
return $this->isDfpRequired($baseData) ? $this->decorated->generatedDfpId($request, $baseData) : null;
return $this->isDfpRequired($salesChannelContext, $orderEntity) ? $this->decorated->generatedDfpId($request, $salesChannelContext, $orderEntity) : null;
}

public function getDfpSnippet(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
public function getDfpSnippet(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
{
return $this->isDfpRequired($baseData) ? $this->decorated->getDfpSnippet($request, $baseData) : null;
return $this->isDfpRequired($salesChannelContext, $orderEntity) ? $this->decorated->getDfpSnippet($request, $salesChannelContext, $orderEntity) : null;
}

public function isDfpIdValid(OrderEntity|SalesChannelContext $baseData, string $dfpId = null): bool
public function isDfpIdValid(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null, string $dfpId = null): bool
{
return !$this->isDfpRequired($baseData) || $this->decorated->isDfpIdValid($baseData, $dfpId);
return !$this->isDfpRequired($salesChannelContext, $orderEntity) || $this->decorated->isDfpIdValid($salesChannelContext, $orderEntity, $dfpId);
}

public function isDfpRequired(OrderEntity|SalesChannelContext $object): bool
public function isDfpRequired(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): bool
{
$session = $this->requestStack->getMainRequest()->getSession();
if ($session->get($this->sessionKey)) {
if ($this->sessionService->isAdminSession($salesChannelContext, $session)) {
return false;
}

return $this->decorated->isDfpRequired($object);
return $this->decorated->isDfpRequired($salesChannelContext, $orderEntity);
}
}
85 changes: 85 additions & 0 deletions src/Components/AdminOrders/Service/SessionService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);
/*
* Copyright (c) Ratepay GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ratepay\RpayPayments\Components\AdminOrders\Service;

use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class SessionService
{
public function __construct(
private readonly string $sessionKey
) {
}

public function isAdminSession(SalesChannelContext $context, SessionInterface $session): bool
{
return $this->isRatepayAdminSession($session) || $this->isLoggedInAsCustomer($context, $session);
}

public function canLogout(SalesChannelContext $context, SessionInterface $session): bool
{
// allow only session logout if the session has been created by ratepay
return $this->isRatepayAdminSession($session) && !$this->isLoggedInAsCustomer($context, $session);
}

public function isLoggedInAsCustomer(SalesChannelContext $context, SessionInterface $session): bool
{
// supported since SW 6.6.5.x - TODO remove this check if compatibility has been change to Shopware >= 6.6.5
if (method_exists($context, 'getImitatingUserId') && $context->getImitatingUserId() !== null) {
return true;
}

if ($context->getCustomerId() === null) {
return false;
}

foreach ($this->getThirdPartyLoginAsSessionKeys() as $key) {
if ($session->has($key)) {
return true;
}
}

return false;
}

public function destroy(SessionInterface $session): void
{
$session->remove($this->sessionKey);

// make sure that the third-party modules did not left any data, which we will check
foreach ($this->getThirdPartyLoginAsSessionKeys() as $key) {
$session->remove($key);
}
}

private function isRatepayAdminSession(SessionInterface $session): bool
{
return $session->get($this->sessionKey) === true;
}

private function getThirdPartyLoginAsSessionKeys(): array
{
$keys = [];

// login as (module: https://store.shopware.com/de/jlau706451421896/als-kunde-einloggen.html)
if (defined('Jlau\LoginAsCustomer\Controller\LoginAsCustomer::SESSION_NAME')) {
$keys[] = constant('Jlau\LoginAsCustomer\Controller\LoginAsCustomer::SESSION_NAME');
}

// login as (module: https://store.shopware.com/de/swpa452746080451m/als-kunde-einloggen.html)
if (defined('Swpa\SwpaLoginAsCustomer\Service\LoginService::ADMIN_CUSTOMER_CONTEXT_EXTENSION')) {
$keys[] = constant('Swpa\SwpaLoginAsCustomer\Service\LoginService::ADMIN_CUSTOMER_CONTEXT_EXTENSION');
}

return $keys;
}
}
43 changes: 43 additions & 0 deletions src/Components/AdminOrders/Subscriber/LoginSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);
/*
* Copyright (c) Ratepay GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;

use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class LoginSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly SessionService $sessionService,
private readonly RequestStack $requestStack
) {
}

public static function getSubscribedEvents(): array
{
return [
CustomerLogoutEvent::class => ['onLogout', -3000], // as late as possible to prioritize thirdparty modules
];
}

public function onLogout(CustomerLogoutEvent $event): void
{
$session = $this->requestStack->getMainRequest()?->getSession();
if (!$session instanceof SessionInterface) {
return;
}

$this->sessionService->destroy($session);
}
}
9 changes: 7 additions & 2 deletions src/Components/AdminOrders/Subscriber/PageSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@

namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;

use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
use Shopware\Storefront\Event\StorefrontRenderEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PageSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly string $sessionKey
private readonly SessionService $sessionService
) {
}

Expand All @@ -31,6 +32,10 @@ public static function getSubscribedEvents(): array
public function onPage(StorefrontRenderEvent $event): void
{
$session = $event->getRequest()->getSession();
$event->setParameter('ratepayAdminOrderSession', $session->get($this->sessionKey) === true);
$event->setParameter('ratepayAdminOrderSession', [
'active' => $this->sessionService->isAdminSession($event->getSalesChannelContext(), $session),
'canLogout' => $this->sessionService->canLogout($event->getSalesChannelContext(), $session),
'isLoggedInAsCustomer' => $this->sessionService->isLoggedInAsCustomer($event->getSalesChannelContext(), $session),
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;

use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
use Ratepay\RpayPayments\Components\ProfileConfig\Event\CreateProfileConfigCriteriaEvent;
use Ratepay\RpayPayments\Components\ProfileConfig\Model\ProfileConfigEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
Expand All @@ -21,7 +22,7 @@ class ProfileConfigSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly RequestStack $requestStack,
private readonly string $sessionKey
private readonly SessionService $sessionService
) {
}

Expand All @@ -34,9 +35,7 @@ public static function getSubscribedEvents(): array

public function onLoadConfig(CreateProfileConfigCriteriaEvent $event): void
{
$session = $this->requestStack->getMainRequest()->getSession();

if ($session->get($this->sessionKey) === true) {
if ($this->sessionService->isAdminSession($event->getSalesChannelContext(), $this->requestStack->getMainRequest()->getSession())) {
$event->getCriteria()->addFilter(new EqualsFilter(ProfileConfigEntity::FIELD_ONLY_ADMIN_ORDERS, true));
} else {
$event->getCriteria()->addFilter(new EqualsFilter(ProfileConfigEntity::FIELD_ONLY_ADMIN_ORDERS, false));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function load(Request $request, SalesChannelContext $context): HandlePaym
if ($request->request->getBoolean('updatePayment')) {
$orderId = $request->request->get('orderId');

/** @var OrderEntity|null $order */
$order = $this->orderRepository->search(CriteriaHelper::getCriteriaForOrder($orderId), $context->getContext())->first();
if ($order instanceof OrderEntity && ($transaction = $order->getTransactions()->last()) instanceof OrderTransactionEntity) {
$paymentHandlerIdentifier = $transaction->getPaymentMethod()->getHandlerIdentifier();
Expand All @@ -57,7 +58,7 @@ public function load(Request $request, SalesChannelContext $context): HandlePaym
}

if ($paymentHandlerIdentifier !== null && is_subclass_of($paymentHandlerIdentifier, AbstractPaymentHandler::class)) {
$this->dataValidationService->validatePaymentData(new DataBag($request->request->all()), $order ?? $context);
$this->dataValidationService->validatePaymentData(new DataBag($request->request->all()), $context, $order ?? null);
}

return $this->innerService->load($request, $context);
Expand Down
10 changes: 5 additions & 5 deletions src/Components/Checkout/Service/DataValidationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ public function __construct(
) {
}

public function validatePaymentData(DataBag $parameterBag, SalesChannelContext|OrderEntity $validationScope): void
public function validatePaymentData(DataBag $parameterBag, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): void
{
if ($validationScope instanceof OrderEntity) {
$paymentMethodId = $validationScope->getTransactions()->last()->getPaymentMethodId();
if ($orderEntity instanceof OrderEntity) {
$paymentMethodId = $orderEntity->getTransactions()->last()->getPaymentMethodId();
} else {
$paymentMethodId = $validationScope->getPaymentMethod()->getId();
$paymentMethodId = $salesChannelContext->getPaymentMethod()->getId();
}

$paymentHandler = $this->paymentHandlerRegistry->getPaymentMethodHandler($paymentMethodId);
Expand All @@ -46,7 +46,7 @@ public function validatePaymentData(DataBag $parameterBag, SalesChannelContext|O

/** @var DataBag $_parameterBag */
$_parameterBag = $parameterBag->get(RequestHelper::WRAPPER_KEY, $parameterBag); // paymentDetails is using for pwa request
$validationDefinitions = $paymentHandler->getValidationDefinitions(new RequestDataBag($_parameterBag->all()), $validationScope);
$validationDefinitions = $paymentHandler->getValidationDefinitions(new RequestDataBag($_parameterBag->all()), $salesChannelContext, $orderEntity);

$definitions = new DataValidationDefinition();
DataValidationHelper::addSubConstraints($definitions, $validationDefinitions);
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Checkout/Service/ExtensionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public function buildPaymentDataExtension(

$searchService = $order instanceof OrderEntity ? $this->profileByOrderEntity : $this->profileBySalesChannelContext;
$profileConfig = $searchService->search(
$searchService->createSearchObject($order ?? $salesChannelContext)->setPaymentMethodId($paymentMethod->getId())
$searchService->createSearchObject($order ?? $salesChannelContext)->setPaymentMethodId($paymentMethod->getId()),
$salesChannelContext
)->first();

if ($profileConfig === null) {
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Checkout/Service/PaymentFilterService.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public function filterPaymentMethods(PaymentMethodCollection $paymentMethodColle

$searchService = $order instanceof OrderEntity ? $this->profileByOrderEntity : $this->profileBySalesChannelContext;
$profileConfig = $searchService->search(
$searchService->createSearchObject($order ?? $salesChannelContext)->setPaymentMethodId($paymentMethod->getId())
$searchService->createSearchObject($order ?? $salesChannelContext)->setPaymentMethodId($paymentMethod->getId()),
$salesChannelContext
)->first();

if ($profileConfig === null) {
Expand Down
22 changes: 11 additions & 11 deletions src/Components/DeviceFingerprint/DfpService.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ public function __construct(
* provide the user-agent via header or a request variable `userAgent` to generate a more unique device-identifier
* the request-variable is prioritized
*/
public function generatedDfpId(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
public function generatedDfpId(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
{
if (!$this->isDfpRequired($baseData)) {
if (!$this->isDfpRequired($salesChannelContext, $orderEntity)) {
return null;
}

if ($baseData instanceof OrderEntity && $token = $this->getOrderDeviceToken($baseData)) {
if ($orderEntity instanceof OrderEntity && $token = $this->getOrderDeviceToken($orderEntity)) {
// token has been used for (failed) payment. So we can reuse it.
return $token;
}
Expand All @@ -57,37 +57,37 @@ public function generatedDfpId(Request $request, OrderEntity|SalesChannelContext
$dataForId = [
((string) $userAgent),
];
if ($baseData instanceof SalesChannelContext) {
$dataForId[] = (string) $this->getCustomerFallBack($baseData);
if (!$orderEntity instanceof OrderEntity) {
$dataForId[] = (string) $this->getCustomerFallBack($salesChannelContext);
}

$generatedId = md5(implode('', $dataForId));

$prefix = $this->getDfpPrefix($baseData);
$prefix = $this->getDfpPrefix($orderEntity ?: $salesChannelContext);

// replace the beginning of the generated id with the generated prefix
return $prefix . substr($generatedId, strlen($prefix));
}

public function isDfpIdValid(OrderEntity|SalesChannelContext $baseData, string $dfpId = null): bool
public function isDfpIdValid(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null, string $dfpId = null): bool
{
$prefix = $this->getDfpPrefix($baseData);
$prefix = $this->getDfpPrefix($salesChannelContext);

// verify if the prefix is at the beginning of the id
return str_starts_with((string) $dfpId, $prefix);
}

public function getDfpSnippet(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
public function getDfpSnippet(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
{
if ($id = $this->generatedDfpId($request, $baseData)) {
if ($id = $this->generatedDfpId($request, $salesChannelContext, $orderEntity)) {
$dfpHelper = new DeviceFingerprint($this->configService->getDeviceFingerprintSnippetId());
return str_replace('\"', '"', $dfpHelper->getDeviceIdentSnippet($id));
}

return null;
}

public function isDfpRequired(OrderEntity|SalesChannelContext $object): bool
public function isDfpRequired(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): bool
{
return true;
}
Expand Down
Loading

0 comments on commit f6b0c0d

Please sign in to comment.