Back to Top

How to integrate the payment method in Shopware 6

Updated 31 December 2021

In this blog, you are going to learn “How to integrate the payment method in shopware 6.”
I hope you know the directory structure of Shopware 6 plugin, if you don’t know, see here- https://docs.shopware.com/en/shopware-platform-dev-en/internals/directory-structure.

There is a two way of integrate the payment methods:-
one is Synchronous and second is Asynchronous.

Both interface method contains transaction id, order details, amount, a return URL , payment method information and language information.
In the Asynchronous interface handler return the redirect response to redirect the customer to an external payment provider. It also contain the return URL. External API return the error AsyncPaymentProcessException so that Shopware handle the exception.
In the Synchronous interface handler doesn’t return the any redirect response. It basically use for internal purpose. You can used them according to your need.

Let’s take the example of synchronous methods:-

TestPaymentHandler.php
<?php declare(strict_types=1);

namespace Webkul\Test\Service;

use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\SynchronousPaymentHandlerInterface;
use Shopware\Core\Checkout\Payment\Cart\SyncPaymentTransactionStruct;
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;

class TestPaymentHandler implements SynchronousPaymentHandlerInterface
{
    /**
     * @var OrderTransactionStateHandler
     */
    private $transactionStateHandler;

    /**
     * @var EntityRepositoryInterface
     */
    private $testRepository;

    public function __construct(
        OrderTransactionStateHandler $transactionStateHandler,
        EntityRepositoryInterface $testRepository
    )
    {
        $this->transactionStateHandler = $transactionStateHandler;
        $this->testRepository = $testRepository;
    }

    public function pay(SyncPaymentTransactionStruct $transaction, RequestDataBag $dataBag, SalesChannelContext $salesChannelContext): void
    {   
        // demo example you can do your logic
        $test = $this->testRepository->search(
            (new Criteria())
            ->addFilter(new EqualsFilter('customerId', $salesChannelContext->getCustomer()->getId())),
            Context::createDefaultContext()
        )->first();

        $updatetest = [
            'id' => $test->getId(),
            'amount' => (float)($test->getAmount() - ($transaction->getOrderTransaction()->getAmount()->getTotalPrice()/ $salesChannelContext->getCurrency()->getFactor()))
        ];

        $this->testRepository->upsert([$updatetest], Context::createDefaultContext());

        //SyncPaymentProcessException
        $context = $salesChannelContext->getContext();
        $this->transactionStateHandler->paid($transaction->getOrderTransaction()->getId(), $context);
       //do your stuff
    }
}

Create your own payment handler class and implements SynchronousPaymentHandlerInterface so that when you go with checkout process this class is called and add the tag shopware.payment.method.sync from the tag shopware knows that service is type of synchronous payment handler and must register your payment handler service in the services.xml file.

Start your headless eCommerce
now.
Find out More
services.xml
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="Webkul\Test\Service\TestPaymentHandler">
            <argument type="service" id="Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler"/>
            <argument type="service" id="wk_test.repository" />
            <tag name="shopware.payment.method.sync" />
        </service>

        <service id="Webkul\Test\Core\Content\Test\TestDefinition">
            <tag name="shopware.entity.definition" entity="wk_test" />
        </service>

    </services>
</container>

Your handler is not handling any payment method, so create payment method.

WebkulTest.php
<?php declare(strict_types=1);

namespace Webkul\Test;

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\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
use Shopware\Core\Framework\Plugin\Context\DeactivateContext;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Util\PluginIdProvider;
use Shopware\Core\Framework\Uuid\Uuid;
use Webkul\Test\Service\TestPaymentHandler;
use Doctrine\DBAL\Connection;

class WebkulTest extends Plugin
{
    public function install(InstallContext $context): void
    {   
       $this->addPaymentMethod($context->getContext());
    }

    public function uninstall(UninstallContext $context): void
    {
        $this->setPaymentMethodIsActive(false, $context->getContext());

        parent:: uninstall($context);
        if ($context->keepUserData()) {
            return;
        }
        
        $connection = $this->container->get(Connection::class);
        $connection->executeUpdate('DROP TABLE IF EXISTS `wk_test`');

    }

    public function activate(ActivateContext $context): void
    {
        $this->setPaymentMethodIsActive(true, $context->getContext());
        parent::activate($context);
    }

    public function deactivate(DeactivateContext $context): void
    {
        $this->setPaymentMethodIsActive(false, $context->getContext());
        parent::deactivate($context);
    }

    private function addPaymentMethod(Context $context): void
    {
        $paymentMethodExists = $this->getPaymentMethodId();

        if ($paymentMethodExists) {
            return;
        }

        /** @var PluginIdProvider $pluginIdProvider */
        $pluginIdProvider = $this->container->get(PluginIdProvider::class);
        $pluginId = $pluginIdProvider->getPluginIdByBaseClass(get_class($this), $context);
        $languageRepo = $this->container->get('language.repository');
        $languageEN = $languageRepo->search((new Criteria())->addFilter(new EqualsFilter('name','English')),Context::createDefaultContext())->first()->getId();
        $languageDE = $languageRepo->search((new Criteria())->addFilter(new EqualsFilter('name','Deutsch')),Context::createDefaultContext())->first()->getId();

        $paymentId = Uuid::randomHex();
        $PaymentData = [
            'id' => $paymentId,
            'handlerIdentifier' => TestPaymentHandler::class,
            'pluginId' => $pluginId,
            'name' => 'Test payment',
            'description' => 'Payment'
        ];

        /** @var EntityRepositoryInterface $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');
        $paymentTransRepository = $this->container->get('payment_method_translation.repository');
        
        $paymentRepository->create([$PaymentData], $context);

        $paymentTransRepository->upsert([
            [
                'paymentMethodId' => $paymentId,
                'languageId' => $languageEN,
                'name' => 'Test Payment',
                'description' => 'Payment'
            ],
            [
                'paymentMethodId' => $paymentId,
                'languageId' => $languageDE,
                'name' => 'Test Payment',
                'description' => 'Zahlung'
            ]
        ], $context);
    }

    private function setPaymentMethodIsActive(bool $active, Context $context): void
    {
        /** @var EntityRepositoryInterface $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');

        $paymentMethodId = $this->getPaymentMethodId();

        if (!$paymentMethodId) {
            return;
        }

        $paymentMethod = [
            'id' => $paymentMethodId,
            'active' => $active,
        ];

        $paymentRepository->update([$paymentMethod], $context);
    }

    private function getPaymentMethodId(): ?string
    {
        /** @var EntityRepositoryInterface $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');

        $paymentCriteria = (new Criteria())->addFilter(new EqualsFilter('handlerIdentifier', TestPaymentHandler::class));
        return $paymentRepository->searchIds($paymentCriteria, Context::createDefaultContext())->firstId();
    }
}

The payment method can be added to the system while installing your plugin in the install method, you actually start by creating a new payment method. he activate method and deactivate method just do that, activating and deactivating the payment method.

Now your payment method is created and payment handler service is working with your payment method.

Hope it will help you. Thanks for reading. Happy Coding 🙂
Thank You.

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home