Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webhook Status not updating #10

Closed
solverat opened this issue Dec 12, 2023 · 3 comments · Fixed by solverat/PayumStripeBundle#1
Closed

Webhook Status not updating #10

solverat opened this issue Dec 12, 2023 · 3 comments · Fixed by solverat/PayumStripeBundle#1

Comments

@solverat
Copy link
Collaborator

Not sure if even someone here is maintaining or using this repository (@dpfaffenbauer, @ramundomario, @BlackbitDevs) - I've already forked this repo a while ago, and I'll also fix it there. Since this took my a full day of debugging/hunting/reading/comparing, you might be interested:

If you're using payments like SOFORT / Klarna, you need to rely on webhooks because payments gets authorized only first. But there are two issues with CoreShop:

I. Order never gets in confirmed state

This is a fundamental problem of CoreShop, I'll open a dedicated ticket for that on CoreShop side.

The user never reaches the success page, because stripe returns with state processing which is not a "success" state in CoreShop:

https://github.com/coreshop/CoreShop/blob/a1a456fdefaa259f791cc3a0b04c05be183948e5/src/CoreShop/Bundle/PayumBundle/Action/ResolveNextRouteAction.php#L44-L45

(and other two places)

Reference: In Sylius they already made some adjustment to handle processing/pending payments:

II. Payment State not updating

If stripe dispatches the checkout.session.async_payment_succeeded webhook, the payment details will be updated but not the payment state => I have no Idea why. I've fixed it by adding an extension which is basically the same as SyliusPayumStripePlugin is using:

https://github.com/FLUX-SE/SyliusPayumStripePlugin/blob/master/src/Extension/UpdatePaymentStateExtension.php

<?php

use CoreShop\Bundle\PayumBundle\Factory\GetStatusFactoryInterface;
use CoreShop\Bundle\WorkflowBundle\Manager\StateMachineManager;
use CoreShop\Component\Core\Model\PaymentInterface;
use CoreShop\Component\Payment\PaymentTransitions;
use CoreShop\Component\Payment\Repository\PaymentRepositoryInterface;
use Payum\Core\Extension\Context;
use Payum\Core\Extension\ExtensionInterface;
use Payum\Core\Model\ModelAggregateInterface;
use Payum\Core\Storage\IdentityInterface;

final class UpdatePaymentStateExtension implements ExtensionInterface
{
    private $scheduledPaymentsToProcess = [];

    public function __construct(
        private StateMachineManager $stateMachineManager,
        private PaymentRepositoryInterface $paymentRepository,
        private GetStatusFactoryInterface $getStatusRequestFactory
    ) {
    }

    public function onPreExecute(Context $context): void
    {
        /** @var mixed|ModelAggregateInterface $request */
        $request = $context->getRequest();

        if (false === $request instanceof ModelAggregateInterface) {
            return;
        }

        if ($request->getModel() instanceof IdentityInterface) {
            $payment = $this->paymentRepository->find($request->getModel()->getId());
        } else {
            /** @var PaymentInterface|mixed $payment */
            $payment = $request->getModel();
        }

        if (false === $payment instanceof PaymentInterface) {
            return;
        }

        $this->scheduleForProcessingIfSupported($payment);
    }

    public function onExecute(Context $context): void
    {
    }

    public function onPostExecute(Context $context): void
    {
        if (null !== $context->getException()) {
            return;
        }

        /** @var mixed|ModelAggregateInterface $request */
        $request = $context->getRequest();

        if ($request instanceof ModelAggregateInterface) {
            /** @var PaymentInterface|mixed $payment */
            $payment = $request->getModel();
            if ($payment instanceof PaymentInterface) {
                $this->scheduleForProcessingIfSupported($payment);
            }
        }

        if (count($context->getPrevious()) > 0) {
            return;
        }

        // Process scheduled payments only when we are post executing a
        // root payum request
        foreach ($this->scheduledPaymentsToProcess as $id => $payment) {
            $this->processPayment($context, $payment);
            unset($this->scheduledPaymentsToProcess[$id]);
        }
    }

    private function processPayment(Context $context, PaymentInterface $payment): void
    {
        $status = $this->getStatusRequestFactory->createNewWithModel($payment);
        $context->getGateway()->execute($status);
        /** @var string $value */
        $value = $status->getValue();
        if ($payment->getState() === $value) {
            return;
        }

        if (PaymentInterface::STATE_UNKNOWN === $value) {
            return;
        }

        $this->updatePaymentState($payment, $value);
    }

    private function scheduleForProcessingIfSupported(PaymentInterface $payment): void
    {
        $id = $payment->getId();
        if (null === $id) {
            return;
        }

        if (false === is_int($id)) {
            return;
        }

        $this->scheduledPaymentsToProcess[$id] = $payment;
    }

    private function updatePaymentState(PaymentInterface $payment, string $nextState): void
    {
        $workflow = $this->stateMachineManager->get($payment, PaymentTransitions::IDENTIFIER);
        if (null !== $transition = $this->stateMachineManager->getTransitionToState($workflow, $payment, $nextState)) {
            $workflow->apply($payment, $transition);
        }
    }
}
@dpfaffenbauer
Copy link
Member

@solverat Do you want to get permissions here so your fixes can be done here?

@solverat
Copy link
Collaborator Author

@dpfaffenbauer I've already fixed it in my fork because I needed this ASAP :) - but yes, maintaining this repository would make more sense.

@dpfaffenbauer
Copy link
Member

@solverat done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants