Search code examples
phpsymfonyeventsevent-handlingshopware

Shopware6 : Which event to subscribe when an order is paid?


I would like to know which event should I use if I want to trigger a function when an order is PAID after its creation by the cusstomer.

I have already tried with this one : state_enter.order_transaction.state.paid => 'onOrderCheckout'.

Unfortunately, it gave an error :" Warning: Use of undefined constant state_enter - assumed 'state_enter' (this will throw an Error in a future version of PHP)*".

Here is my subscriber :


namespace Emakers\TransmissionPlugin\Subscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Order\OrderEvents;
use Shopware\Core\System\StateMachine\Event;
use Shopware\Core\Framework\Event\EventData\EntityType;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Emakers\TransmissionPlugin\Services\ShopwareConnectService;


class OrderSubscriber implements EventSubscriberInterface
{
    /**
     * @ContainerInterface $container
     */
    private $container;


     /**
     * @var datetime
     */
     private $now;


     public function __construct(ContainerInterface $container) {
                $this->container = $container;
                $this->now = date('Y-m-d H:i:s');
     }

    public static function getSubscribedEvents(): array
    {
        return [
                'state_enter.order_transaction.state.paid'   => 'onOrderCheckout',
        ];
    }
        public function orOrderCheckout($event)
        {
                var_dump($event);
                die('here we are');
        }
}

Solution

  • I use the StateMachineTransitionEvent for this. Tested with Shopware 6.2.

    <?php declare(strict_types=1);
    
    namespace Your\Namespace;
    
    use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition;
    use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates;
    use Shopware\Core\Checkout\Order\OrderEntity;
    use Shopware\Core\Framework\Context;
    use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
    use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
    use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
    use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class OrderStateSubscriber implements EventSubscriberInterface {
    
        /**
         * @var EntityRepositoryInterface
         */
        private $stateMachineStateRepository;
    
        /**
         * @var EntityRepositoryInterface
         */
        private $orderRepository;
    
        public function __construct(EntityRepositoryInterface $stateMachineStateRepository, EntityRepositoryInterface $orderRepository)
        {
            $this->stateMachineStateRepository = $stateMachineStateRepository;
            $this->orderRepository = $orderRepository;
        }
    
        public static function getSubscribedEvents(): array
        {
            return [
                StateMachineTransitionEvent::class => 'onStateTransition'
            ];
        }
    
        public function onStateTransition(StateMachineTransitionEvent $event) {
            // Since you are only interested in order transitions
            if ($event->getEntityName() !== OrderTransactionDefinition::ENTITY_NAME) {
                return;
            }
    
            $orderTransactionsStatePaidId = $this->getOrderTransactionsStatePaidId($event->getContext());
            if ($orderTransactionsStatePaidId === null) {
                return;
            }
    
            // Check if it was transitioned to paid
            if ($event->getToPlace()->getId() !== $orderTransactionsStatePaidId) {
                return;
            }
    
            // Transaction was changed to paid do your thing
            $order = $this->getOrder($event->getEntityId(), $event->getContext());
        }
    
        private function getOrderTransactionsStatePaidId(Context $context): ?string {
            $criteria = new Criteria();
            // Add filter for OrderTransactionStateMachine
            $criteria->addFilter(
                new EqualsFilter('stateMachine.technicalName', \sprintf('%s.state', OrderTransactionDefinition::ENTITY_NAME)),
                new EqualsFilter('technicalName', OrderTransactionStates::STATE_PAID)
            );
    
            return $this->stateMachineStateRepository->searchIds($criteria, $context)->firstId();
        }
        
        private function getOrder(string $orderTransactionId, Context $context): ?OrderEntity
        {
            $criteria = new Criteria();
            $criteria->addFilter(
                new EqualsFilter('transactions.id', $orderTransactionId)
            );
            
            return $this->orderRepository->search($criteria, $context)->first();
        }
    }
    

    In your services xml add something like the following. Of course you need to adjust the FQCN (FullyQualifiedClassName).

    <service id="Your\Namespace\OrderStateSubscriber">
        <argument type="service" id="state_machine_state.repository"/>
        <argument type="service" id="order.repository"/>
        <tag name="kernel.event_subscriber"/>
    </service>