Passed
Push — master ( 54bf0c...76f02b )
by Christian
12:06 queued 12s
created

PaymentService::getPaymentHandlerById()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 6
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Checkout\Payment;
4
5
use Psr\Log\LoggerInterface;
6
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
7
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler;
8
use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct;
9
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\AsynchronousPaymentHandlerInterface;
10
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerRegistry;
11
use Shopware\Core\Checkout\Payment\Cart\PaymentTransactionChainProcessor;
12
use Shopware\Core\Checkout\Payment\Cart\Token\TokenFactoryInterfaceV2;
13
use Shopware\Core\Checkout\Payment\Cart\Token\TokenStruct;
14
use Shopware\Core\Checkout\Payment\Exception\AsyncPaymentFinalizeException;
15
use Shopware\Core\Checkout\Payment\Exception\AsyncPaymentProcessException;
16
use Shopware\Core\Checkout\Payment\Exception\CustomerCanceledAsyncPaymentException;
17
use Shopware\Core\Checkout\Payment\Exception\InvalidOrderException;
18
use Shopware\Core\Checkout\Payment\Exception\InvalidTransactionException;
19
use Shopware\Core\Checkout\Payment\Exception\PaymentProcessException;
20
use Shopware\Core\Checkout\Payment\Exception\SyncPaymentProcessException;
21
use Shopware\Core\Checkout\Payment\Exception\TokenExpiredException;
22
use Shopware\Core\Checkout\Payment\Exception\UnknownPaymentMethodException;
23
use Shopware\Core\Framework\Context;
24
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
25
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
26
use Shopware\Core\Framework\Uuid\Uuid;
27
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
28
use Shopware\Core\System\SalesChannel\SalesChannelContext;
29
use Symfony\Component\HttpFoundation\RedirectResponse;
30
use Symfony\Component\HttpFoundation\Request;
31
32
class PaymentService
33
{
34
    /**
35
     * @var PaymentTransactionChainProcessor
36
     */
37
    private $paymentProcessor;
38
39
    /**
40
     * @var TokenFactoryInterfaceV2
41
     */
42
    private $tokenFactory;
43 1
44
    /**
45
     * @var EntityRepositoryInterface
46
     */
47
    private $paymentMethodRepository;
48
49 1
    /**
50 1
     * @var PaymentHandlerRegistry
51 1
     */
52 1
    private $paymentHandlerRegistry;
53 1
54
    /**
55
     * @var EntityRepositoryInterface
56
     */
57
    private $orderTransactionRepository;
58
59 2
    /**
60
     * @var OrderTransactionStateHandler
61 2
     */
62
    private $transactionStateHandler;
63
64
    /**
65 2
     * @var LoggerInterface
66
     */
67 1
    private $logger;
68 1
69
    public function __construct(
70
        PaymentTransactionChainProcessor $paymentProcessor,
71
        TokenFactoryInterfaceV2 $tokenFactory,
72
        EntityRepositoryInterface $paymentMethodRepository,
73
        PaymentHandlerRegistry $paymentHandlerRegistry,
74
        EntityRepositoryInterface $orderTransactionRepository,
75
        OrderTransactionStateHandler $transactionStateHandler,
76
        LoggerInterface $logger
77
    ) {
78 2
        $this->paymentProcessor = $paymentProcessor;
79
        $this->tokenFactory = $tokenFactory;
80 2
        $this->paymentMethodRepository = $paymentMethodRepository;
81
        $this->paymentHandlerRegistry = $paymentHandlerRegistry;
82
        $this->orderTransactionRepository = $orderTransactionRepository;
83
        $this->transactionStateHandler = $transactionStateHandler;
84
        $this->logger = $logger;
85
    }
86
87
    /**
88
     * @throws AsyncPaymentProcessException
89
     * @throws InvalidOrderException
90
     * @throws SyncPaymentProcessException
91 2
     * @throws UnknownPaymentMethodException
92
     */
93 2
    public function handlePaymentByOrder(
94 2
        string $orderId,
95 2
        RequestDataBag $dataBag,
96
        SalesChannelContext $context,
97
        ?string $finishUrl = null,
98 1
        ?string $errorUrl = null
99 1
    ): ?RedirectResponse {
100
        if (!Uuid::isValid($orderId)) {
101
            throw new InvalidOrderException($orderId);
102
        }
103
104
        try {
105
            return $this->paymentProcessor->process($orderId, $dataBag, $context, $finishUrl, $errorUrl);
106
        } catch (PaymentProcessException $e) {
107
            $transactionId = $e->getOrderTransactionId();
108
            $this->logger->error('An error occurred during processing the payment', ['orderTransactionId' => $transactionId, 'exceptionMessage' => $e->getMessage()]);
109
            $this->transactionStateHandler->fail($transactionId, $context->getContext());
110
            if ($errorUrl !== null) {
111
                return new RedirectResponse($errorUrl);
112
            }
113
114
            throw $e;
115
        }
116
    }
117
118
    /**
119
     * @throws AsyncPaymentFinalizeException
120
     * @throws InvalidTransactionException
121
     * @throws TokenExpiredException
122
     * @throws UnknownPaymentMethodException
123
     */
124
    public function finalizeTransaction(
125
        string $paymentToken,
126
        Request $request,
127
        SalesChannelContext $salesChannelContext
128
    ): TokenStruct {
129
        $paymentTokenStruct = $this->parseToken($paymentToken);
130
        $transactionId = $paymentTokenStruct->getTransactionId();
131
        if ($transactionId === null || !Uuid::isValid($transactionId)) {
132
            throw new AsyncPaymentProcessException((string) $transactionId, "Payment JWT didn't contain a valid orderTransactionId");
133
        }
134
135
        $context = $salesChannelContext->getContext();
136
        $paymentTransactionStruct = $this->getPaymentTransactionStruct($transactionId, $context);
137
138
        $paymentHandler = $this->getPaymentHandlerById($paymentTokenStruct->getPaymentMethodId(), $context);
0 ignored issues
show
Bug introduced by
It seems like $paymentTokenStruct->getPaymentMethodId() can also be of type null; however, parameter $paymentMethodId of Shopware\Core\Checkout\P...getPaymentHandlerById() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

138
        $paymentHandler = $this->getPaymentHandlerById(/** @scrutinizer ignore-type */ $paymentTokenStruct->getPaymentMethodId(), $context);
Loading history...
139
140
        if (!$paymentHandler) {
141
            throw new UnknownPaymentMethodException($paymentTokenStruct->getPaymentMethodId());
142
        }
143
144
        try {
145
            $paymentHandler->finalize($paymentTransactionStruct, $request, $salesChannelContext);
146
        } catch (CustomerCanceledAsyncPaymentException $e) {
147
            $this->transactionStateHandler->cancel($transactionId, $context);
148
            $paymentTokenStruct->setException($e);
149
150
            return $paymentTokenStruct;
151
        } catch (PaymentProcessException $e) {
152
            $this->logger->error('An error occurred during finalizing async payment', ['orderTransactionId' => $transactionId, 'exceptionMessage' => $e->getMessage()]);
153
            $this->transactionStateHandler->fail($transactionId, $context);
154
            $paymentTokenStruct->setException($e);
155
156
            return $paymentTokenStruct;
157
        }
158
159
        return $paymentTokenStruct;
160
    }
161
162
    /**
163
     * @throws TokenExpiredException
164
     */
165
    private function parseToken(string $token): TokenStruct
166
    {
167
        $tokenStruct = $this->tokenFactory->parseToken($token);
168
169
        if ($tokenStruct->isExpired()) {
170
            throw new TokenExpiredException($tokenStruct->getToken());
0 ignored issues
show
Bug introduced by
It seems like $tokenStruct->getToken() can also be of type null; however, parameter $token of Shopware\Core\Checkout\P...xception::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
            throw new TokenExpiredException(/** @scrutinizer ignore-type */ $tokenStruct->getToken());
Loading history...
171
        }
172
173
        $this->tokenFactory->invalidateToken($tokenStruct->getToken());
0 ignored issues
show
Bug introduced by
It seems like $tokenStruct->getToken() can also be of type null; however, parameter $tokenId of Shopware\Core\Checkout\P...ceV2::invalidateToken() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        $this->tokenFactory->invalidateToken(/** @scrutinizer ignore-type */ $tokenStruct->getToken());
Loading history...
174
175
        return $tokenStruct;
176
    }
177
178
    /**
179
     * @throws UnknownPaymentMethodException
180
     */
181
    private function getPaymentHandlerById(string $paymentMethodId, Context $context): ?AsynchronousPaymentHandlerInterface
182
    {
183
        $paymentMethods = $this->paymentMethodRepository->search(new Criteria([$paymentMethodId]), $context);
184
185
        /** @var PaymentMethodEntity|null $paymentMethod */
186
        $paymentMethod = $paymentMethods->get($paymentMethodId);
187
        if (!$paymentMethod) {
188
            throw new UnknownPaymentMethodException($paymentMethodId);
189
        }
190
191
        return $this->paymentHandlerRegistry->getAsyncHandler($paymentMethod->getHandlerIdentifier());
192
    }
193
194
    /**
195
     * @throws InvalidTransactionException
196
     */
197
    private function getPaymentTransactionStruct(string $orderTransactionId, Context $context): AsyncPaymentTransactionStruct
198
    {
199
        $criteria = new Criteria([$orderTransactionId]);
200
        $criteria->addAssociation('order');
201
        /** @var OrderTransactionEntity|null $orderTransaction */
202
        $orderTransaction = $this->orderTransactionRepository->search($criteria, $context)->first();
203
204
        if ($orderTransaction === null) {
205
            throw new InvalidTransactionException($orderTransactionId);
206
        }
207
208
        return new AsyncPaymentTransactionStruct($orderTransaction, $orderTransaction->getOrder(), '');
0 ignored issues
show
Bug introduced by
It seems like $orderTransaction->getOrder() can also be of type null; however, parameter $order of Shopware\Core\Checkout\P...onStruct::__construct() does only seem to accept Shopware\Core\Checkout\Order\OrderEntity, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

208
        return new AsyncPaymentTransactionStruct($orderTransaction, /** @scrutinizer ignore-type */ $orderTransaction->getOrder(), '');
Loading history...
209
    }
210
}
211