diff --git a/AdyenPayment.php b/AdyenPayment.php
index 81fd409d..44476daa 100644
--- a/AdyenPayment.php
+++ b/AdyenPayment.php
@@ -7,18 +7,23 @@
 namespace AdyenPayment;
 
 use AdyenPayment\Components\CompilerPass\NotificationProcessorCompilerPass;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
 use AdyenPayment\Models\Notification;
 use AdyenPayment\Models\PaymentInfo;
 use AdyenPayment\Models\Refund;
 use AdyenPayment\Models\TextNotification;
+use AdyenPayment\Models\UserPreference;
 use Doctrine\ORM\Tools\SchemaTool;
 use Shopware\Bundle\AttributeBundle\Service\TypeMapping;
 use Shopware\Components\Logger;
+use Shopware\Components\Model\ModelManager;
 use Shopware\Components\Plugin;
 use Shopware\Components\Plugin\Context\DeactivateContext;
 use Shopware\Components\Plugin\Context\InstallContext;
 use Shopware\Components\Plugin\Context\UninstallContext;
 use Shopware\Components\Plugin\Context\UpdateContext;
+use Shopware\Models\Payment\Payment;
+use Shopware\Models\Shop\Shop;
 use Symfony\Component\Config\FileLocator;
 use Symfony\Component\Config\Loader\LoaderResolver;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -29,9 +34,10 @@ final class AdyenPayment extends Plugin
 {
     public const NAME = 'AdyenPayment';
     public const ADYEN_CODE = 'adyen_type';
-    public const ADYEN_STORED_METHOD_ID = 'adyen_stored_method_id';
+    public const ADYEN_STORED_PAYMENT_UMBRELLA_CODE = 'adyen_stored_payment_umbrella';
     public const SESSION_ADYEN_RESTRICT_EMAILS = 'adyenRestrictEmail';
     public const SESSION_ADYEN_PAYMENT_INFO_ID = 'adyenPaymentInfoId';
+    public const SESSION_ADYEN_STORED_METHOD_ID = 'adyenStoredMethodId';
 
     public static function isPackage(): bool
     {
@@ -82,6 +88,7 @@ private function loadServices(ContainerBuilder $container): void
     public function install(InstallContext $context): void
     {
         $this->installAttributes();
+        $this->installStoredPaymentUmbrella($context);
 
         $tool = new SchemaTool($this->container->get('models'));
         $classes = $this->getModelMetaData();
@@ -91,6 +98,7 @@ public function install(InstallContext $context): void
     public function update(UpdateContext $context): void
     {
         $this->installAttributes();
+        $this->installStoredPaymentUmbrella($context);
 
         $tool = new SchemaTool($this->container->get('models'));
         $classes = $this->getModelMetaData();
@@ -129,7 +137,6 @@ private function uninstallAttributes(UninstallContext $uninstallContext): void
     {
         $crudService = $this->container->get('shopware_attribute.crud_service');
         $crudService->delete('s_core_paymentmeans_attributes', self::ADYEN_CODE);
-        $crudService->delete('s_core_paymentmeans_attributes', self::ADYEN_STORED_METHOD_ID);
 
         $this->rebuildAttributeModels();
     }
@@ -150,16 +157,6 @@ private function installAttributes(): void
                 'label' => 'Adyen payment type',
             ]
         );
-        $crudService->update(
-            's_core_paymentmeans_attributes',
-            self::ADYEN_STORED_METHOD_ID,
-            TypeMapping::TYPE_STRING,
-            [
-                'displayInBackend' => true,
-                'readonly' => true,
-                'label' => 'Adyen stored payment method id',
-            ]
-        );
 
         $this->rebuildAttributeModels();
     }
@@ -173,6 +170,7 @@ private function getModelMetaData(): array
             $entityManager->getClassMetadata(PaymentInfo::class),
             $entityManager->getClassMetadata(Refund::class),
             $entityManager->getClassMetadata(TextNotification::class),
+            $entityManager->getClassMetadata(UserPreference::class),
         ];
     }
 
@@ -187,6 +185,36 @@ private function rebuildAttributeModels(): void
             ['s_user_attributes', 's_core_paymentmeans_attributes']
         );
     }
+
+    private function installStoredPaymentUmbrella(InstallContext $context): void
+    {
+        $database = $this->container->get('db');
+        /** @var ModelManager $modelsManager */
+        $modelsManager = $this->container->get(ModelManager::class);
+
+        $models = $this->container->get('models');
+        $shops = $models->getRepository(Shop::class)->findAll();
+
+        $payment = new Payment();
+        $payment->setActive(true);
+        $payment->setName(self::ADYEN_STORED_PAYMENT_UMBRELLA_CODE);
+        $payment->setSource(SourceType::adyen()->getType());
+        $payment->setHide(true);
+        $payment->setPluginId($context->getPlugin()->getId());
+        $payment->setDescription($description = 'Adyen Stored Payment Method');
+        $payment->setAdditionalDescription($description);
+        $payment->setShops($shops);
+
+        $paymentId = $database->fetchRow(
+            'SELECT `id` FROM `s_core_paymentmeans` WHERE `name` = :name',
+            [':name' => self::ADYEN_STORED_PAYMENT_UMBRELLA_CODE]
+            )['id'] ?? null;
+
+        if (null === $paymentId) {
+            $modelsManager->persist($payment);
+            $modelsManager->flush($payment);
+        }
+    }
 }
 
 if (AdyenPayment::isPackage()) {
diff --git a/Collection/Payment/PaymentMeanCollection.php b/Collection/Payment/PaymentMeanCollection.php
index ea797942..eba89f3b 100644
--- a/Collection/Payment/PaymentMeanCollection.php
+++ b/Collection/Payment/PaymentMeanCollection.php
@@ -4,12 +4,11 @@
 
 namespace AdyenPayment\Collection\Payment;
 
+use AdyenPayment\AdyenPayment;
 use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
 use AdyenPayment\Models\Payment\PaymentMean;
-use Countable;
-use IteratorAggregate;
 
-final class PaymentMeanCollection implements IteratorAggregate, Countable
+final class PaymentMeanCollection implements \IteratorAggregate, \Countable
 {
     /**
      * @var array<PaymentMean>
@@ -23,12 +22,10 @@ public function __construct(PaymentMean ...$paymentMeans)
 
     public static function createFromShopwareArray(array $paymentMeans): self
     {
-        return new self(
-            ...array_map(
-                static fn(array $paymentMean) => PaymentMean::createFromShopwareArray($paymentMean),
-                $paymentMeans
-            )
-        );
+        return new self(...array_map(
+            static fn(array $paymentMean): PaymentMean => PaymentMean::createFromShopwareArray($paymentMean),
+            $paymentMeans
+        ));
     }
 
     /**
@@ -57,7 +54,7 @@ public function filter(callable $filter): self
     public function filterBySource(SourceType $source): self
     {
         return $this->filter(
-            static function(PaymentMean $paymentMean) use ($source) {
+            static function(PaymentMean $paymentMean) use ($source): bool {
                 return $source->equals($paymentMean->getSource());
             }
         );
@@ -66,27 +63,70 @@ static function(PaymentMean $paymentMean) use ($source) {
     public function filterExcludeAdyen(): self
     {
         return $this->filter(
-            static function(PaymentMean $paymentMean) {
+            static function(PaymentMean $paymentMean): bool {
                 return !$paymentMean->getSource()->equals(SourceType::adyen());
             }
         );
     }
 
-    public function filterByAdyenSource(): self
+    public function filterExcludeHidden(): self
+    {
+        return new self(...array_filter(
+            $this->paymentMeans,
+            static fn(PaymentMean $paymentMean): bool => !$paymentMean->isHidden()
+        ));
+    }
+
+    public function fetchStoredMethodUmbrellaPaymentMean(): ?PaymentMean
+    {
+        foreach ($this->paymentMeans as $paymentMean) {
+            if (AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE === $paymentMean->getValue('name')) {
+                return $paymentMean;
+            }
+        }
+
+        return null;
+    }
+
+    public function fetchById(int $paymentId): ?PaymentMean
+    {
+        foreach ($this->paymentMeans as $paymentMean) {
+            if ($paymentMean->getId() === $paymentId) {
+                return $paymentMean;
+            }
+        }
+
+        return null;
+    }
+
+    public function fetchByStoredMethodId(string $storedMethodId): ?PaymentMean
+    {
+        foreach ($this->paymentMeans as $paymentMean) {
+            if ($paymentMean->getValue('stored_method_id') === $storedMethodId) {
+                return $paymentMean;
+            }
+        }
+
+        return null;
+    }
+
+    public function fetchByUmbrellaStoredMethodId(string $storedMethodId): ?PaymentMean
     {
-        return $this->filterBySource(SourceType::adyen());
+        foreach ($this->paymentMeans as $paymentMean) {
+            if ($paymentMean->getValue('stored_method_umbrella_id') === $storedMethodId) {
+                return $paymentMean;
+            }
+        }
+
+        return null;
     }
 
     public function toShopwareArray(): array
     {
-        return array_reduce(
-            $this->paymentMeans,
-            static function(array $payload, PaymentMean $paymentMean) {
-                $payload[$paymentMean->getId()] = $paymentMean->getRaw();
+        return array_reduce($this->paymentMeans, static function(array $payload, PaymentMean $paymentMean): array {
+            $payload[$paymentMean->getId()] = $paymentMean->getRaw();
 
-                return $payload;
-            },
-            []
-        );
+            return $payload;
+        }, []);
     }
 }
diff --git a/Collection/Payment/PaymentMethodCollection.php b/Collection/Payment/PaymentMethodCollection.php
index 00d865e8..110d3a15 100644
--- a/Collection/Payment/PaymentMethodCollection.php
+++ b/Collection/Payment/PaymentMethodCollection.php
@@ -39,6 +39,10 @@ public static function fromAdyenMethods(array $adyenMethods): self
             ...array_map(
                 static fn(array $paymentMethod) => PaymentMethod::fromRaw($paymentMethod),
                 $adyenMethods['paymentMethods'] ?? []
+            ),
+            ...array_map(
+                static fn(array $paymentMethod) => PaymentMethod::fromRaw($paymentMethod),
+                $adyenMethods['storedPaymentMethods'] ?? []
             )
         );
     }
@@ -79,7 +83,7 @@ public function mapToRaw(): array
      * $identifierOrStoredId is the Adyen "unique identifier" or Adyen "stored payment id"
      * NOT the Shopware id.
      */
-    public function fetchByIdentifierOrStoredId(string $identifierOrStoredId): ?PaymentMethod
+    private function fetchByIdentifierOrStoredId(string $identifierOrStoredId): ?PaymentMethod
     {
         foreach ($this->paymentMethods as $paymentMethod) {
             if ($paymentMethod->getStoredPaymentMethodId() === $identifierOrStoredId) {
diff --git a/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProvider.php b/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProvider.php
index 74ed0efe..4dfb1640 100644
--- a/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProvider.php
+++ b/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProvider.php
@@ -4,23 +4,25 @@
 
 namespace AdyenPayment\Components\Adyen\PaymentMethod;
 
-use AdyenPayment\AdyenPayment;
 use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Collection\Payment\PaymentMethodCollection;
 use AdyenPayment\Components\Adyen\Builder\PaymentMethodOptionsBuilderInterface;
-use AdyenPayment\Components\Adyen\PaymentMethodService;
+use AdyenPayment\Components\Adyen\PaymentMethodServiceInterface;
 use AdyenPayment\Enricher\Payment\PaymentMethodEnricherInterface;
+use AdyenPayment\Exceptions\UmbrellaPaymentMeanNotFoundException;
 use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Models\Payment\PaymentGroup;
 use AdyenPayment\Models\Payment\PaymentMean;
-use Shopware\Bundle\StoreFrontBundle\Struct\Attribute;
+use AdyenPayment\Models\Payment\PaymentMethod;
 
 final class EnrichedPaymentMeanProvider implements EnrichedPaymentMeanProviderInterface
 {
-    private PaymentMethodService $paymentMethodService;
+    private PaymentMethodServiceInterface $paymentMethodService;
     private PaymentMethodOptionsBuilderInterface $paymentMethodOptionsBuilder;
     private PaymentMethodEnricherInterface $paymentMethodEnricher;
 
     public function __construct(
-        PaymentMethodService $paymentMethodService,
+        PaymentMethodServiceInterface $paymentMethodService,
         PaymentMethodOptionsBuilderInterface $paymentMethodOptionsBuilder,
         PaymentMethodEnricherInterface $paymentMethodEnricher
     ) {
@@ -29,9 +31,6 @@ public function __construct(
         $this->paymentMethodEnricher = $paymentMethodEnricher;
     }
 
-    /**
-     * @throws \Adyen\AdyenException
-     */
     public function __invoke(PaymentMeanCollection $paymentMeans): PaymentMeanCollection
     {
         $paymentMethodOptions = ($this->paymentMethodOptionsBuilder)();
@@ -45,32 +44,52 @@ public function __invoke(PaymentMeanCollection $paymentMeans): PaymentMeanCollec
             $paymentMethodOptions['value']
         );
 
-        $enricher = $this->paymentMethodEnricher;
-
-        return new PaymentMeanCollection(...$paymentMeans->map(
-            static function(PaymentMean $shopwareMethod) use ($adyenPaymentMethods, $enricher): ?PaymentMean {
-                if (!$shopwareMethod->getSource()->equals(SourceType::adyen())) {
-                    return $shopwareMethod;
-                }
+        $umbrellaPaymentMean = $paymentMeans->fetchStoredMethodUmbrellaPaymentMean();
+        if (null === $umbrellaPaymentMean) {
+            throw UmbrellaPaymentMeanNotFoundException::missingUmbrellaPaymentMean();
+        }
 
-                /** @var Attribute $attribute */
-                $attribute = $shopwareMethod->getValue('attribute');
-                if (!$attribute) {
-                    return $shopwareMethod;
-                }
+        return new PaymentMeanCollection(
+            ...$this->provideEnrichedPaymentMeans($paymentMeans, $adyenPaymentMethods),
+            ...$this->provideEnrichedStoredPaymentMeans($adyenPaymentMethods, $umbrellaPaymentMean)
+        );
+    }
 
-                $identifierOrStoredId = '' !== (string) $attribute->get(AdyenPayment::ADYEN_STORED_METHOD_ID)
-                    ? $attribute->get(AdyenPayment::ADYEN_STORED_METHOD_ID)
-                    : $attribute->get(AdyenPayment::ADYEN_CODE);
+    private function provideEnrichedPaymentMeans(
+        PaymentMeanCollection $paymentMeans,
+        PaymentMethodCollection $adyenPaymentMethods
+    ): array {
+        $enricher = $this->paymentMethodEnricher;
 
-                $paymentMethod = $adyenPaymentMethods->fetchByIdentifierOrStoredId($identifierOrStoredId);
+        return $paymentMeans
+            ->filterExcludeHidden()
+            ->map(static function(PaymentMean $paymentMean) use ($adyenPaymentMethods, $enricher): ?PaymentMean {
+                if (!$paymentMean->getSource()->equals(SourceType::adyen())) {
+                    return $paymentMean;
+                }
 
+                $paymentMethod = $adyenPaymentMethods->fetchByPaymentMean($paymentMean);
                 if (null === $paymentMethod) {
                     return null;
                 }
 
-                return PaymentMean::createFromShopwareArray(($enricher)($shopwareMethod->getRaw(), $paymentMethod));
+                return PaymentMean::createFromShopwareArray(($enricher)($paymentMean->getRaw(), $paymentMethod));
+            });
+    }
+
+    private function provideEnrichedStoredPaymentMeans(
+        PaymentMethodCollection $adyenPaymentMethods,
+        PaymentMean $umbrellaPaymentMean
+    ): array {
+        $enricher = $this->paymentMethodEnricher;
+        $storedAdyenMethods = $adyenPaymentMethods->filterByPaymentType(PaymentGroup::stored());
+
+        return $storedAdyenMethods->map(
+            static function(PaymentMethod $paymentMethod) use ($umbrellaPaymentMean, $enricher): PaymentMean {
+                return PaymentMean::createFromShopwareArray(
+                    ($enricher)($umbrellaPaymentMean->getRaw(), $paymentMethod)
+                );
             }
-        ));
+        );
     }
 }
diff --git a/Components/Adyen/PaymentMethod/StoredPaymentMeanProvider.php b/Components/Adyen/PaymentMethod/StoredPaymentMeanProvider.php
new file mode 100644
index 00000000..0b8a06f5
--- /dev/null
+++ b/Components/Adyen/PaymentMethod/StoredPaymentMeanProvider.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Models\Payment\PaymentMean;
+use Doctrine\DBAL\Connection;
+use Enlight_Controller_Request_Request;
+
+final class StoredPaymentMeanProvider implements StoredPaymentMeanProviderInterface
+{
+    private EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider;
+    private Connection $connection;
+
+    public function __construct(
+        EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider,
+        Connection $connection
+    ) {
+        $this->enrichedPaymentMeanProvider = $enrichedPaymentMeanProvider;
+        $this->connection = $connection;
+    }
+
+    public function fromRequest(Enlight_Controller_Request_Request $request): ?PaymentMean
+    {
+        $registerPayment = $request->getParam('register', [])['payment'] ?? null;
+        if (null === $registerPayment) {
+            return null;
+        }
+
+        $enrichedPaymentMeans = ($this->enrichedPaymentMeanProvider)(
+            PaymentMeanCollection::createFromShopwareArray($this->fetchUmbrellaMethod())
+        );
+
+        return $enrichedPaymentMeans->fetchByUmbrellaStoredMethodId($registerPayment);
+    }
+
+    private function fetchUmbrellaMethod(): array
+    {
+        $queryBuilder = $this->connection->createQueryBuilder();
+
+        return $queryBuilder->select('*')
+            ->from('s_core_paymentmeans')
+            ->where('name = :umbrellaMethodName')
+            ->setParameter(':umbrellaMethodName', AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE)
+            ->execute()
+            ->fetchAll();
+    }
+}
diff --git a/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderInterface.php b/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderInterface.php
new file mode 100644
index 00000000..e7ade33a
--- /dev/null
+++ b/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\Models\Payment\PaymentMean;
+use Enlight_Controller_Request_Request;
+
+interface StoredPaymentMeanProviderInterface
+{
+    public function fromRequest(Enlight_Controller_Request_Request $request): ?PaymentMean;
+}
diff --git a/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProvider.php b/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProvider.php
new file mode 100644
index 00000000..c83e1cb9
--- /dev/null
+++ b/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use Psr\Log\LoggerInterface;
+
+final class TraceableEnrichedPaymentMeanProvider implements EnrichedPaymentMeanProviderInterface
+{
+    private EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider;
+    private LoggerInterface $logger;
+
+    public function __construct(
+        EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider,
+        LoggerInterface $logger
+    ) {
+        $this->enrichedPaymentMeanProvider = $enrichedPaymentMeanProvider;
+        $this->logger = $logger;
+    }
+
+    /**
+     * @throws \Adyen\AdyenException
+     */
+    public function __invoke(PaymentMeanCollection $paymentMeans): PaymentMeanCollection
+    {
+        try {
+            return ($this->enrichedPaymentMeanProvider)($paymentMeans);
+        } catch (\Exception $exception) {
+            $this->logger->critical($exception->getMessage(), ['exception' => $exception]);
+        }
+
+        return new PaymentMeanCollection();
+    }
+}
diff --git a/Components/Adyen/PaymentMethodService.php b/Components/Adyen/PaymentMethodService.php
index 4d5e3f36..0200b269 100644
--- a/Components/Adyen/PaymentMethodService.php
+++ b/Components/Adyen/PaymentMethodService.php
@@ -15,7 +15,7 @@
 use Shopware\Components\Model\ModelManager;
 use Shopware\Models\Customer\Customer;
 
-final class PaymentMethodService
+final class PaymentMethodService implements PaymentMethodServiceInterface
 {
     /** @todo cleanup the public const (unify the services) */
     public const IMPORT_LOCALE = 'en_GB';
diff --git a/Components/Adyen/PaymentMethodServiceInterface.php b/Components/Adyen/PaymentMethodServiceInterface.php
new file mode 100644
index 00000000..ace594de
--- /dev/null
+++ b/Components/Adyen/PaymentMethodServiceInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Adyen;
+
+use Adyen\Service\Checkout;
+use AdyenPayment\Collection\Payment\PaymentMethodCollection;
+
+interface PaymentMethodServiceInterface
+{
+    public function getPaymentMethods(
+        ?string $countryCode = null,
+        ?string $currency = null,
+        ?float $value = null,
+        ?string $locale = null,
+        bool $cache = true
+    ): PaymentMethodCollection;
+
+    /**
+     * @internal
+     */
+    public function getCheckout(): Checkout;
+}
diff --git a/Components/BasketService.php b/Components/BasketService.php
index 57242b07..14e58448 100644
--- a/Components/BasketService.php
+++ b/Components/BasketService.php
@@ -279,7 +279,7 @@ private function insertInToBasket(Detail $optionData): int
             'currencyFactor' => Shopware()->Shop()->getCurrency()->getFactor(),
         ]);
 
-        return $this->db->lastInsertId();
+        return (int) $this->db->lastInsertId();
     }
 
     /**
diff --git a/Components/Manager/UserPreferenceManager.php b/Components/Manager/UserPreferenceManager.php
new file mode 100644
index 00000000..eb79968e
--- /dev/null
+++ b/Components/Manager/UserPreferenceManager.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Manager;
+
+use AdyenPayment\Models\UserPreference;
+use Doctrine\ORM\EntityManager;
+
+final class UserPreferenceManager implements UserPreferenceManagerInterface
+{
+    private EntityManager $modelManager;
+
+    public function __construct(EntityManager $modelManager)
+    {
+        $this->modelManager = $modelManager;
+    }
+
+    public function save(UserPreference $userPreference): void
+    {
+        $this->modelManager->persist($userPreference);
+        $this->modelManager->flush($userPreference);
+    }
+}
diff --git a/Components/Manager/UserPreferenceManagerInterface.php b/Components/Manager/UserPreferenceManagerInterface.php
new file mode 100644
index 00000000..fe8e5262
--- /dev/null
+++ b/Components/Manager/UserPreferenceManagerInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Components\Manager;
+
+use AdyenPayment\Models\UserPreference;
+
+interface UserPreferenceManagerInterface
+{
+    public function save(UserPreference $userPreference): void;
+}
diff --git a/Controllers/Backend/AdyenPaymentRefund.php b/Controllers/Backend/AdyenPaymentRefund.php
index ddf0cf98..f2a1ec30 100644
--- a/Controllers/Backend/AdyenPaymentRefund.php
+++ b/Controllers/Backend/AdyenPaymentRefund.php
@@ -1,5 +1,6 @@
 <?php
 
+use AdyenPayment\Components\Adyen\RefundService;
 use Shopware\Components\CSRFWhitelistAware;
 
 //phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace, Squiz.Classes.ValidClassName.NotCamelCaps, Generic.Files.LineLength.TooLong
@@ -8,7 +9,8 @@ class Shopware_Controllers_Backend_AdyenPaymentRefund extends Shopware_Controlle
     public function refundAction(): void
     {
         $orderId = $this->Request()->getParam('orderId');
-        $notificationManager = $this->get('AdyenPayment\Components\Adyen\RefundService');
+        $notificationManager = $this->get(RefundService::class);
+
         $refund = $notificationManager->doRefund($orderId);
 
         $this->View()->assign('refundReference', $refund->getPspReference());
@@ -21,7 +23,7 @@ public function refundAction(): void
      *
      * @psalm-return array{0: 'refund'}
      */
-    public function getWhitelistedCSRFActions()
+    public function getWhitelistedCSRFActions(): array
     {
         return ['refund'];
     }
diff --git a/Controllers/Backend/ImportPaymentMethods.php b/Controllers/Backend/ImportPaymentMethods.php
index cbcee661..dcc5d88e 100644
--- a/Controllers/Backend/ImportPaymentMethods.php
+++ b/Controllers/Backend/ImportPaymentMethods.php
@@ -2,8 +2,10 @@
 
 declare(strict_types=1);
 
+use AdyenPayment\Import\PaymentMethodImporter;
 use AdyenPayment\Import\PaymentMethodImporterInterface;
 use Psr\Log\LoggerInterface;
+use Shopware\Components\CacheManager;
 use Symfony\Component\HttpFoundation\Response;
 
 //phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace, Squiz.Classes.ValidClassName.NotCamelCaps, Generic.Files.LineLength.TooLong
@@ -11,6 +13,7 @@ class Shopware_Controllers_Backend_ImportPaymentMethods extends Shopware_Control
 {
     private PaymentMethodImporterInterface $paymentMethodImporter;
     private LoggerInterface $logger;
+    private CacheManager $cacheManager;
 
     /**
      * @return void
@@ -19,13 +22,16 @@ public function preDispatch()
     {
         parent::preDispatch();
 
-        $this->paymentMethodImporter = $this->get('AdyenPayment\Import\PaymentMethodImporter');
+        $this->cacheManager = $this->get(CacheManager::class);
+        $this->paymentMethodImporter = $this->get(PaymentMethodImporter::class);
         $this->logger = $this->get('adyen_payment.logger');
     }
 
     public function importAction(): void
     {
         try {
+            $this->cacheManager->clearConfigCache();
+
             $total = $success = 0;
             foreach ($this->paymentMethodImporter->importAll() as $result) {
                 ++$total;
diff --git a/Controllers/Backend/TestAdyenApi.php b/Controllers/Backend/TestAdyenApi.php
index c7285e05..1ae672d3 100644
--- a/Controllers/Backend/TestAdyenApi.php
+++ b/Controllers/Backend/TestAdyenApi.php
@@ -3,6 +3,7 @@
 use AdyenPayment\Components\Adyen\ApiConfigValidator;
 use AdyenPayment\Rule\AdyenApi\UsedFallbackConfigRule;
 use AdyenPayment\Rule\AdyenApi\UsedFallbackConfigRuleInterface;
+use Shopware\Components\CacheManager;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Validator\ConstraintViolationInterface;
 
@@ -11,11 +12,13 @@ class Shopware_Controllers_Backend_TestAdyenApi extends Shopware_Controllers_Bac
 {
     private ApiConfigValidator $apiConfigValidator;
     private UsedFallbackConfigRuleInterface $usedFallbackConfigRule;
+    private CacheManager $cacheManager;
 
     public function preDispatch(): void
     {
         parent::preDispatch();
 
+        $this->cacheManager = $this->get(CacheManager::class);
         $this->apiConfigValidator = $this->get(ApiConfigValidator::class);
         $this->usedFallbackConfigRule = $this->get(UsedFallbackConfigRule::class);
     }
@@ -23,6 +26,8 @@ public function preDispatch(): void
     public function runAction(): void
     {
         $shopId = (int) $this->request->get('shopId', 1);
+        $this->cacheManager->clearConfigCache();
+
         $violations = $this->apiConfigValidator->validate($shopId);
         if ($violations->count() > 0) {
             $this->response->setHttpResponseCode(Response::HTTP_BAD_REQUEST);
@@ -40,7 +45,7 @@ static function (ConstraintViolationInterface $violation) {
         $this->response->setHttpResponseCode(Response::HTTP_OK);
         $this->View()->assign('responseText', sprintf(
             '%sAdyen API connection successful.',
-            $usedFallback ?  "Fallback to main shop API configuration<br />" : ''
+            $usedFallback ? "Fallback to main shop API configuration<br />" : ''
         ));
     }
 }
diff --git a/Doctrine/Writer/PaymentAttributeWriter.php b/Doctrine/Writer/PaymentAttributeWriter.php
index 53fd42dc..ceefd14a 100644
--- a/Doctrine/Writer/PaymentAttributeWriter.php
+++ b/Doctrine/Writer/PaymentAttributeWriter.php
@@ -23,10 +23,7 @@ public function __construct(DataPersisterInterface $dataPersister, AttributeWrit
 
     public function __invoke(int $paymentMeanId, PaymentMethod $adyenPaymentMethod): void
     {
-        $attributesColumns = [
-            AdyenPayment::ADYEN_CODE => TypeMappingInterface::TYPE_STRING,
-            AdyenPayment::ADYEN_STORED_METHOD_ID => TypeMappingInterface::TYPE_STRING,
-        ];
+        $attributesColumns = [AdyenPayment::ADYEN_CODE => TypeMappingInterface::TYPE_STRING];
 
         $dataPersister = $this->dataPersister;
         $this->attributeUpdater->writeReadOnlyAttributes(
@@ -37,7 +34,6 @@ public function __invoke(int $paymentMeanId, PaymentMethod $adyenPaymentMethod):
                     '_table' => $table,
                     '_foreignKey' => $paymentMeanId,
                     AdyenPayment::ADYEN_CODE => $adyenPaymentMethod->code(),
-                    AdyenPayment::ADYEN_STORED_METHOD_ID => $adyenPaymentMethod->getStoredPaymentMethodId(),
                 ],
                 's_core_paymentmeans_attributes',
                 $paymentMeanId
diff --git a/Enricher/Payment/PaymentMethodEnricher.php b/Enricher/Payment/PaymentMethodEnricher.php
index 45edbc9c..21b67139 100644
--- a/Enricher/Payment/PaymentMethodEnricher.php
+++ b/Enricher/Payment/PaymentMethodEnricher.php
@@ -5,6 +5,7 @@
 namespace AdyenPayment\Enricher\Payment;
 
 use AdyenPayment\Components\Adyen\PaymentMethod\ImageLogoProviderInterface;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
 use AdyenPayment\Models\Payment\PaymentMethod;
 use Shopware_Components_Snippet_Manager;
 
@@ -25,16 +26,18 @@ public function __invoke(array $shopwareMethod, PaymentMethod $paymentMethod): a
     {
         return array_merge($shopwareMethod, [
             'enriched' => true,
-            'additionaldescription' => $this->enrichDescription($paymentMethod),
+            'additionaldescription' => $this->enrichAdditionalDescription($paymentMethod),
             'image' => $this->imageLogoProvider->provideByType($paymentMethod->adyenType()->type()),
             'isStoredPayment' => $paymentMethod->isStoredPayment(),
             'isAdyenPaymentMethod' => true,
             'adyenType' => $paymentMethod->adyenType()->type(),
             'metadata' => $paymentMethod->rawData(),
-        ]);
+        ],
+            $this->enrichStoredPaymentMethodData($shopwareMethod, $paymentMethod)
+        );
     }
 
-    private function enrichDescription(PaymentMethod $adyenMethod): string
+    private function enrichAdditionalDescription(PaymentMethod $adyenMethod): string
     {
         $description = $this->snippets
             ->getNamespace('adyen/method/description')
@@ -53,4 +56,22 @@ private function enrichDescription(PaymentMethod $adyenMethod): string
             $adyenMethod->getValue('lastFour', '')
         );
     }
+
+    private function enrichStoredPaymentMethodData(array $shopwareMethod, PaymentMethod $paymentMethod): array
+    {
+        if (!$paymentMethod->isStoredPayment()) {
+            return [];
+        }
+
+        return [
+            'stored_method_umbrella_id' => sprintf(
+                '%s_%s',
+                $shopwareMethod['id'],
+                $paymentMethod->getStoredPaymentMethodId()
+            ),
+            'stored_method_id' => $paymentMethod->getStoredPaymentMethodId(),
+            'description' => $paymentMethod->getValue('name'),
+            'source' => SourceType::adyen()->getType(),
+        ];
+    }
 }
diff --git a/Exceptions/UmbrellaPaymentMeanNotFoundException.php b/Exceptions/UmbrellaPaymentMeanNotFoundException.php
new file mode 100644
index 00000000..2982414a
--- /dev/null
+++ b/Exceptions/UmbrellaPaymentMeanNotFoundException.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Exceptions;
+
+class UmbrellaPaymentMeanNotFoundException extends \Exception
+{
+    public static function missingUmbrellaPaymentMean(): self
+    {
+        return new static('Umbrella payment mean not found.');
+    }
+}
diff --git a/Models/Payment/PaymentMean.php b/Models/Payment/PaymentMean.php
index f522fb7e..1dcfcf78 100644
--- a/Models/Payment/PaymentMean.php
+++ b/Models/Payment/PaymentMean.php
@@ -38,6 +38,11 @@ public function getSource(): SourceType
         return $this->source;
     }
 
+    public function isHidden(): bool
+    {
+        return (bool) ($this->raw['hide'] ?? false);
+    }
+
     public function getAttribute(): Attribute
     {
         return $this->raw['attribute'] ?? new Attribute();
@@ -59,11 +64,7 @@ public function getAdyenCode(): string
 
     public function getAdyenStoredMethodId(): string
     {
-        if ($this->getAttribute()->exists(AdyenPayment::ADYEN_STORED_METHOD_ID)) {
-            return (string) $this->getAttribute()->get(AdyenPayment::ADYEN_STORED_METHOD_ID);
-        }
-
-        return '';
+        return (string) $this->getValue('stored_method_id', '');
     }
 
     public function adyenType(): ?PaymentType
diff --git a/Models/Payment/PaymentMethod.php b/Models/Payment/PaymentMethod.php
index 18b9ced6..1ad42ccf 100644
--- a/Models/Payment/PaymentMethod.php
+++ b/Models/Payment/PaymentMethod.php
@@ -35,12 +35,12 @@ public static function fromRaw(array $data): self
         return $new;
     }
 
-    public function withCode(string $code): self
+    public function withCode(string $name): self
     {
         $new = clone $this;
         $new->code = mb_strtolower(sprintf('%s_%s',
             $this->type->type(),
-            Sanitize::removeNonWord($code)
+            Sanitize::removeNonWord($name)
         ));
 
         return $new;
diff --git a/Models/PaymentInfo.php b/Models/PaymentInfo.php
index a5acf094..5b6903ff 100644
--- a/Models/PaymentInfo.php
+++ b/Models/PaymentInfo.php
@@ -79,6 +79,12 @@ class PaymentInfo extends ModelEntity
      */
     private $paymentData;
 
+    /**
+     * @var string
+     * @ORM\Column(name="stored_method_id", type="text", nullable=true)
+     */
+    private $storedMethodId;
+
     public function __construct()
     {
         $this->setCreatedAt(new \DateTime('now'));
@@ -279,4 +285,16 @@ public function setPaymentData(string $paymentData): self
 
         return $this;
     }
+
+    public function getStoredMethodId(): string
+    {
+        return (string) $this->storedMethodId;
+    }
+
+    public function setStoredMethodId(string $storedMethodId): self
+    {
+        $this->storedMethodId = $storedMethodId;
+
+        return $this;
+    }
 }
diff --git a/Models/UserPreference.php b/Models/UserPreference.php
new file mode 100644
index 00000000..92ca3859
--- /dev/null
+++ b/Models/UserPreference.php
@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Models;
+
+use Doctrine\ORM\Mapping as ORM;
+use Shopware\Components\Model\ModelEntity;
+
+/**
+ * @ORM\Entity
+ * @ORM\Table(name="s_plugin_adyen_user_preference")
+ */
+class UserPreference extends ModelEntity
+{
+    /**
+     * @var int
+     *
+     * @ORM\Column(name="id", type="integer", nullable=false)
+     * @ORM\Id
+     * @ORM\GeneratedValue(strategy="IDENTITY")
+     */
+    private $id;
+
+    /**
+     * @var int
+     * @ORM\Column(name="user_id", type="integer")
+     */
+    private $userId;
+
+    /**
+     * @var string
+     * @ORM\Column(name="stored_method_id", type="text", nullable=true)
+     */
+    private $storedMethodId;
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function setId(int $id): self
+    {
+        $this->id = $id;
+
+        return $this;
+    }
+
+    public function getUserId(): int
+    {
+        return $this->userId;
+    }
+
+    public function setUserId(int $userId): self
+    {
+        $this->userId = $userId;
+
+        return $this;
+    }
+
+    public function getStoredMethodId(): string
+    {
+        return (string) $this->storedMethodId;
+    }
+
+    public function setStoredMethodId(?string $storedMethodId): self
+    {
+        $this->storedMethodId = $storedMethodId;
+
+        return $this;
+    }
+
+    public function mapToView(): array
+    {
+        return [
+            'id' => $this->getId(),
+            'userId' => $this->getUserId(),
+            'storedMethodId' => $this->getStoredMethodId(),
+        ];
+    }
+}
diff --git a/Resources/frontend/js/jquery.adyen-payment-selection.js b/Resources/frontend/js/jquery.adyen-payment-selection.js
index 83964cf1..f76f1102 100644
--- a/Resources/frontend/js/jquery.adyen-payment-selection.js
+++ b/Resources/frontend/js/jquery.adyen-payment-selection.js
@@ -122,10 +122,19 @@
         },
         onPaymentFormSubmit: function (e) {
             var me = this;
-            if ($(me.opts.paymentMethodFormSubmitSelector).hasClass('is--disabled')) {
+            var $formSubmit = $(me.opts.paymentMethodFormSubmitSelector);
+            if ($formSubmit.hasClass('is--disabled')) {
                 e.preventDefault();
                 return false;
             }
+            var $paymentElement = $('#' + me.selectedPaymentElementId)[0];
+            var paymentMethod = this.getPaymentMethodById($paymentElement.value);
+            if(paymentMethod.isStoredPayment){
+                $formSubmit.append(
+                    $('<input type="hidden" name="adyenStoredMethodId" value="'+paymentMethod.stored_method_id+'"/>')
+                );
+            }
+            $paymentElement.value = paymentMethod.id;
         },
         isPaymentElement: function (elementId) {
             return $('#' + elementId).parents(this.opts.paymentMethodSelector).length > 0;
@@ -142,7 +151,10 @@
             }
 
             me.selectedPaymentElementId = selectedPaymentElementId;
-            me.selectedPaymentId = $(event.target).val();
+
+            var elementValue = $(event.target).val();
+            var paymentMethod = this.getPaymentMethodById(elementValue);
+            me.selectedPaymentId = paymentMethod.isStoredPayment ? paymentMethod.stored_method_id : elementValue;
 
             var paymentMethodSession = this.getPaymentSession();
             if (0 === Object.keys(paymentMethodSession).length) {
@@ -234,8 +246,10 @@
         getPaymentMethodById: function (id) {
             var me = this;
 
-            return me.opts.enrichedPaymentMethods.filter(function(enrichedPaymentMethod) {
-                return enrichedPaymentMethod.id === id;
+            return me.opts.enrichedPaymentMethods.filter(function(paymentMethod) {
+                return paymentMethod.id === id || (
+                    paymentMethod.isStoredPayment === true
+                    && (paymentMethod.stored_method_id === id || paymentMethod.stored_method_umbrella_id === id));
             })[0] || {};
         },
         /**
@@ -503,10 +517,7 @@
          * @private
          */
         __enableStoreDetails: function (paymentMethod) {
-            // ignore property "paymentMethod.supportsRecurring"
-            // return 'scheme' === paymentMethod.adyenType;
-            // @fixme temporarily disable stored payment details
-            return false;
+            return 'scheme' === paymentMethod.adyenType;
         },
         /**
          * Modify AdyenPaymentMethod with additional data for the web-component library
diff --git a/Resources/services/applepay-merchant.xml b/Resources/services/applepay-merchant.xml
index 087e2027..7281a0f2 100644
--- a/Resources/services/applepay-merchant.xml
+++ b/Resources/services/applepay-merchant.xml
@@ -6,7 +6,7 @@
     <parameters>
         <parameter key="apple_pay.merchant_association.base_uri">https://eu.adyen.link</parameter>
         <parameter key="apple_pay.merchant_association.storage_path">%kernel.project_dir%/.well-known/apple-developer-merchantid-domain-association</parameter>
-        <parameter key="apple_pay.merchant_association.archive_path">%adyen_payment.plugin_dir%/storage/apple-developer-merchantid-domain-association.zip</parameter>
+        <parameter key="apple_pay.merchant_association.archive_path">%adyen_payment.plugin_dir%/storage/apple-developer-merchantid-domain-association.archive</parameter>
     </parameters>
 
     <services>
diff --git a/Resources/services/components.xml b/Resources/services/components.xml
index df611da2..94f57b34 100644
--- a/Resources/services/components.xml
+++ b/Resources/services/components.xml
@@ -31,7 +31,7 @@
             <argument type="service" id="adyen_payment.logger"/>
         </service>
         <service id="AdyenPayment\Components\Adyen\ApiClientMap">
-            <argument type="service" id="AdyenPayment\Components\Adyen\ApiFactory" />
+            <argument type="service" id="AdyenPayment\Components\Adyen\ApiFactory"/>
         </service>
         <service id="AdyenPayment\Components\Adyen\PaymentMethodService" public="true">
             <argument type="service" id="AdyenPayment\Components\Adyen\ApiClientMap"/>
@@ -41,7 +41,7 @@
             <argument type="service" id="models"/>
         </service>
         <service id="AdyenPayment\Components\Adyen\RefundService" public="true">
-            <argument type="service" id="AdyenPayment\Components\Adyen\ApiClientMap" />
+            <argument type="service" id="AdyenPayment\Components\Adyen\ApiClientMap"/>
             <argument type="service" id="models"/>
             <argument type="service" id="AdyenPayment\Components\NotificationManager"/>
         </service>
@@ -61,7 +61,8 @@
         </service>
 
         <!-- Checkout Payment Payload -->
-        <service id="AdyenPayment\Components\Payload\PaymentPayloadProvider" class="AdyenPayment\Components\Payload\Chain" public="true">
+        <service id="AdyenPayment\Components\Payload\PaymentPayloadProvider"
+                 class="AdyenPayment\Components\Payload\Chain" public="true">
             <argument type="service" id="AdyenPayment\Components\Payload\Providers\ApplicationInfoProvider"/>
             <argument type="service" id="AdyenPayment\Components\Payload\Providers\ShopperInfoProvider"/>
             <argument type="service" id="AdyenPayment\Components\Payload\Providers\OrderInfoProvider"/>
@@ -71,21 +72,21 @@
             <argument type="service" id="AdyenPayment\Components\Payload\Providers\StorePaymentProvider"/>
             <argument type="service" id="AdyenPayment\Components\Payload\Providers\RecurringPaymentProvider"/>
         </service>
-        <service id="AdyenPayment\Components\Payload\Chain" />
+        <service id="AdyenPayment\Components\Payload\Chain"/>
         <service id="AdyenPayment\Components\Payload\Providers\ApplicationInfoProvider" public="true">
-            <argument type="service" id="router" />
-            <argument type="service" id="models" />
-            <argument type="service" id="AdyenPayment\Components\Configuration" />
+            <argument type="service" id="router"/>
+            <argument type="service" id="models"/>
+            <argument type="service" id="AdyenPayment\Components\Configuration"/>
         </service>
-        <service id="AdyenPayment\Components\Payload\Providers\BrowserInfoProvider" />
+        <service id="AdyenPayment\Components\Payload\Providers\BrowserInfoProvider"/>
         <service id="AdyenPayment\Components\Payload\Providers\LineItemsInfoProvider">
             <argument type="service" id="AdyenPayment\Components\Calculator\PriceCalculationService"/>
         </service>
-        <service id="AdyenPayment\Components\Payload\Providers\OrderInfoProvider" />
-        <service id="AdyenPayment\Components\Payload\Providers\PaymentMethodProvider" />
-        <service id="AdyenPayment\Components\Payload\Providers\ShopperInfoProvider" />
-        <service id="AdyenPayment\Components\Payload\Providers\StorePaymentProvider" />
-        <service id="AdyenPayment\Components\Payload\Providers\RecurringPaymentProvider" />
+        <service id="AdyenPayment\Components\Payload\Providers\OrderInfoProvider"/>
+        <service id="AdyenPayment\Components\Payload\Providers\PaymentMethodProvider"/>
+        <service id="AdyenPayment\Components\Payload\Providers\ShopperInfoProvider"/>
+        <service id="AdyenPayment\Components\Payload\Providers\StorePaymentProvider"/>
+        <service id="AdyenPayment\Components\Payload\Providers\RecurringPaymentProvider"/>
 
         <!-- Notification Processors -->
         <service id="AdyenPayment\Components\NotificationProcessor\Authorisation">
@@ -160,8 +161,9 @@
             <tag name="adyen.payment.notificationprocessor"/>
             <argument type="service" id="events"/>
         </service>
-        <service id="adyen_payment.components.shopware_version_check" class="AdyenPayment\Components\ShopwareVersionCheck">
-            <argument type="service" id="service_container" />
+        <service id="adyen_payment.components.shopware_version_check"
+                 class="AdyenPayment\Components\ShopwareVersionCheck">
+            <argument type="service" id="service_container"/>
             <argument type="service" id="adyen_payment.logger"/>
         </service>
         <service id="AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider" public="true">
@@ -169,6 +171,11 @@
             <argument type="service" id="AdyenPayment\Components\Adyen\Builder\PaymentMethodOptionsBuilder"/>
             <argument type="service" id="AdyenPayment\Enricher\Payment\PaymentMethodEnricher"/>
         </service>
+        <service id="AdyenPayment\Components\Adyen\PaymentMethod\TraceableEnrichedPaymentMeanProvider"
+                 decorates="AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider">
+            <argument type="service" id="AdyenPayment\Components\Adyen\PaymentMethod\TraceableEnrichedPaymentMeanProvider.inner"/>
+            <argument type="service" id="adyen_payment.logger"/>
+        </service>
         <service id="AdyenPayment\Components\Adyen\PaymentMethod\ImageLogoProvider">
         </service>
     </services>
diff --git a/Resources/services/managers.xml b/Resources/services/managers.xml
index b466ed62..dfbd4728 100644
--- a/Resources/services/managers.xml
+++ b/Resources/services/managers.xml
@@ -21,5 +21,8 @@
             <argument type="service" id="AdyenPayment\Components\Builder\NotificationBuilder"/>
             <argument type="service" id="models"/>
         </service>
+        <service id="AdyenPayment\Components\Manager\UserPreferenceManager">
+            <argument type="service" id="models"/>
+        </service>
     </services>
 </container>
diff --git a/Resources/services/providers.xml b/Resources/services/providers.xml
index e02f5105..5c7267d2 100644
--- a/Resources/services/providers.xml
+++ b/Resources/services/providers.xml
@@ -8,6 +8,10 @@
             <argument type="service" id="AdyenPayment\Components\Adyen\ApiFactory"/>
             <argument type="service" id="adyen_payment.logger"/>
         </service>
+        <service id="AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProvider">
+            <argument type="service" id="AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider"/>
+            <argument type="service" id="dbal_connection"/>
+        </service>
     </services>
 </container>
 
diff --git a/Resources/services/shopware.xml b/Resources/services/shopware.xml
index 7e903c46..490cb309 100644
--- a/Resources/services/shopware.xml
+++ b/Resources/services/shopware.xml
@@ -6,6 +6,6 @@
             <argument type="service" id="shopware_plugininstaller.plugin_manager"/>
             <argument type="service" id="adyen_payment.logger"/>
         </service>
+        <service id="AdyenPayment\Shopware\Provider\PaymentMeansProvider"/>
     </services>
 </container>
-
diff --git a/Resources/services/subscribers.xml b/Resources/services/subscribers.xml
index d18f2cd6..a05c526f 100644
--- a/Resources/services/subscribers.xml
+++ b/Resources/services/subscribers.xml
@@ -33,6 +33,9 @@
         <service id="AdyenPayment\Subscriber\Applepay\MerchantAssociation\PerformanceLoaderSubscriber">
             <argument>%adyen_payment.plugin_dir%</argument>
         </service>
+        <service id="AdyenPayment\Subscriber\Backend\HideStoredPaymentsSubscriber">
+            <tag name="shopware.event_subscriber"/>
+        </service>
 
         <!-- Frontend Subscribers -->
         <service id="AdyenPayment\Subscriber\AddPluginTemplatesSubscriber">
@@ -50,6 +53,11 @@
             <argument type="service" id="models"/>
             <argument type="service" id="AdyenPayment\Components\OrderMailService"/>
         </service>
+        <service id="AdyenPayment\Subscriber\AddStoredMethodIdOnOrderSubscriber">
+            <tag name="shopware.event_subscriber"/>
+            <argument type="service" id="models"/>
+            <argument type="service" id="session"/>
+        </service>
         <service id="AdyenPayment\Subscriber\CheckoutSubscriber">
             <tag name="shopware.event_subscriber"/>
             <argument type="service" id="AdyenPayment\Components\Configuration"/>
@@ -87,11 +95,34 @@
             <tag name="shopware.event_subscriber"/>
         </service>
         <service id="AdyenPayment\Subscriber\Checkout\EnrichUserAdditionalPaymentSubscriber">
-            <argument type="service" id="AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider" />
+            <argument type="service" id="AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider"/>
+            <argument type="service" id="AdyenPayment\Shopware\Provider\PaymentMeansProvider"/>
+            <argument type="service" id="session"/>
+            <tag name="shopware.event_subscriber"/>
+        </service>
+        <service id="AdyenPayment\Subscriber\Checkout\EnrichUmbrellaPaymentMeanSubscriber">
+            <argument type="service" id="session"/>
+            <argument type="service" id="AdyenPayment\Shopware\Provider\PaymentMeansProvider"/>
+            <tag name="shopware.event_subscriber"/>
+        </service>
+        <service id="AdyenPayment\Subscriber\Checkout\PersistStoredMethodIdSubscriber">
+            <argument type="service" id="session"/>
             <tag name="shopware.event_subscriber"/>
         </service>
         <service id="AdyenPayment\Subscriber\Applepay\MerchantAssociation\RegisterUrlCountSubscriber">
             <tag name="shopware.event_subscriber"/>
         </service>
+        <service id="AdyenPayment\Subscriber\Account\SaveStoredMethodPreferenceSubscriber">
+            <argument type="service" id="session"/>
+            <argument type="service" id="AdyenPayment\Components\Manager\UserPreferenceManager"/>
+            <argument type="expression">service('models').getRepository('AdyenPayment\\Models\\UserPreference')</argument>
+            <argument type="service" id="AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProvider"/>
+            <tag name="shopware.event_subscriber"/>
+        </service>
+        <service id="AdyenPayment\Subscriber\EnrichUserPreferenceSubscriber">
+            <argument type="service" id="session"/>
+            <argument type="expression">service('models').getRepository('AdyenPayment\\Models\\UserPreference')</argument>
+            <tag name="shopware.event_subscriber"/>
+        </service>
     </services>
 </container>
diff --git a/Resources/views/frontend/checkout/change_payment.tpl b/Resources/views/frontend/checkout/change_payment.tpl
index bf0d77a7..84a8423b 100644
--- a/Resources/views/frontend/checkout/change_payment.tpl
+++ b/Resources/views/frontend/checkout/change_payment.tpl
@@ -7,6 +7,7 @@
     {assign var="storedPaymentMethods" value=[]}
     {foreach $sPayments as $paymentMethod}
         {if 'isStoredPayment'|array_key_exists:$paymentMethod && true === $paymentMethod.isStoredPayment}
+            {append var="paymentMethod" value=$paymentMethod.stored_method_umbrella_id index='id'}
             {$storedPaymentMethods[] = $paymentMethod}
         {else}
             {$paymentMethods[] = $paymentMethod}
diff --git a/Resources/views/frontend/register/payment_fieldset.tpl b/Resources/views/frontend/register/payment_fieldset.tpl
new file mode 100644
index 00000000..b9667b9c
--- /dev/null
+++ b/Resources/views/frontend/register/payment_fieldset.tpl
@@ -0,0 +1,18 @@
+{extends file='parent:frontend/register/payment_fieldset.tpl'}
+
+{block name="frontend_register_payment_fieldset_input_radio"}
+    {assign var='isStoredPayment' value=('isStoredPayment'|array_key_exists:$payment_mean && true === $payment_mean.isStoredPayment)}
+    {if $isStoredPayment}
+        {append var="payment_mean" value=($payment_mean.stored_method_umbrella_id) index='id'}
+    {/if}
+    <input
+        type="radio"
+        name="register[payment]"
+        value="{$payment_mean.id}"
+        id="payment_mean{$payment_mean.id}"
+        {if ($payment_mean.id eq $form_data.payment
+            or (!$form_data and !$payment_mean@index)
+            or ($isStoredPayment and $adyenUserPreference and $payment_mean.stored_method_id === $adyenUserPreference.storedMethodId)
+        )} checked="checked"{/if}
+        />
+{/block}
diff --git a/Shopware/Provider/PaymentMeansProvider.php b/Shopware/Provider/PaymentMeansProvider.php
new file mode 100644
index 00000000..745e2e81
--- /dev/null
+++ b/Shopware/Provider/PaymentMeansProvider.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Shopware\Provider;
+
+final class PaymentMeansProvider implements PaymentMeansProviderInterface
+{
+    public function __invoke(): array
+    {
+        return Shopware()->Modules()->Admin()->sGetPaymentMeans();
+    }
+}
diff --git a/Shopware/Provider/PaymentMeansProviderInterface.php b/Shopware/Provider/PaymentMeansProviderInterface.php
new file mode 100644
index 00000000..67848b21
--- /dev/null
+++ b/Shopware/Provider/PaymentMeansProviderInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Shopware\Provider;
+
+interface PaymentMeansProviderInterface
+{
+    public function __invoke(): array;
+}
diff --git a/Subscriber/Account/SaveStoredMethodPreferenceSubscriber.php b/Subscriber/Account/SaveStoredMethodPreferenceSubscriber.php
new file mode 100644
index 00000000..9f4fee65
--- /dev/null
+++ b/Subscriber/Account/SaveStoredMethodPreferenceSubscriber.php
@@ -0,0 +1,64 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber\Account;
+
+use AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProviderInterface;
+use AdyenPayment\Components\Manager\UserPreferenceManagerInterface;
+use AdyenPayment\Models\UserPreference;
+use Doctrine\ORM\EntityRepository;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+
+final class SaveStoredMethodPreferenceSubscriber implements SubscriberInterface
+{
+    private Enlight_Components_Session_Namespace $session;
+    private UserPreferenceManagerInterface $userPreferenceManager;
+    private EntityRepository $userPreferenceRepository;
+    private StoredPaymentMeanProviderInterface $storedPaymentMeanProvider;
+
+    public function __construct(
+        Enlight_Components_Session_Namespace $session,
+        UserPreferenceManagerInterface $userPreferenceManager,
+        EntityRepository $userPreferenceRepository,
+        StoredPaymentMeanProviderInterface $storedPaymentMeanProvider
+    ) {
+        $this->session = $session;
+        $this->userPreferenceManager = $userPreferenceManager;
+        $this->userPreferenceRepository = $userPreferenceRepository;
+        $this->storedPaymentMeanProvider = $storedPaymentMeanProvider;
+    }
+
+    public static function getSubscribedEvents(): array
+    {
+        return ['Enlight_Controller_Action_PostDispatch_Frontend_Account' => '__invoke'];
+    }
+
+    public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
+    {
+        $userId = $this->session->get('sUserId');
+        if (null === $userId) {
+            return;
+        }
+
+        $request = $args->getRequest();
+
+        $isSavePayment = 'savePayment' === $request->getActionName() && $request->isPost();
+        if (!$isSavePayment) {
+            return;
+        }
+
+        $storedMethod = $this->storedPaymentMeanProvider->fromRequest($request);
+        $storedMethodId = null !== $storedMethod ? $storedMethod->getValue('stored_method_id') : null;
+
+        $userPreference = $this->userPreferenceRepository->findOneBy(['userId' => $userId]);
+        if (null === $userPreference) {
+            $userPreference = new UserPreference();
+            $userPreference->setUserId($userId);
+        }
+
+        $userPreference = $userPreference->setStoredMethodId($storedMethodId);
+        $this->userPreferenceManager->save($userPreference);
+    }
+}
diff --git a/Subscriber/AddStoredMethodIdOnOrderSubscriber.php b/Subscriber/AddStoredMethodIdOnOrderSubscriber.php
new file mode 100644
index 00000000..70c39c92
--- /dev/null
+++ b/Subscriber/AddStoredMethodIdOnOrderSubscriber.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Models\PaymentInfo;
+use Doctrine\Persistence\ObjectRepository;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Enlight_Event_EventArgs;
+use Shopware\Components\Model\ModelManager;
+
+final class AddStoredMethodIdOnOrderSubscriber implements SubscriberInterface
+{
+    private ModelManager $modelManager;
+    private ObjectRepository $paymentInfoRepository;
+    private Enlight_Components_Session_Namespace $session;
+
+    public function __construct(ModelManager $modelManager, Enlight_Components_Session_Namespace $session)
+    {
+        $this->modelManager = $modelManager;
+        $this->paymentInfoRepository = $this->modelManager->getRepository(PaymentInfo::class);
+        $this->session = $session;
+    }
+
+    public static function getSubscribedEvents()
+    {
+        return ['Shopware_Modules_Order_SaveOrder_ProcessDetails' => 'persistPaymentInfoStoredMethodId'];
+    }
+
+    public function persistPaymentInfoStoredMethodId(Enlight_Event_EventArgs $args)
+    {
+        $paymentInfoId = $this->session->get(AdyenPayment::SESSION_ADYEN_PAYMENT_INFO_ID);
+        $storedMethodId = (string) $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, '');
+
+        if (null === $paymentInfoId) {
+            return $args->getReturn();
+        }
+
+        /** @var PaymentInfo $paymentInfo */
+        $paymentInfo = $this->paymentInfoRepository->findOneBy([
+            'id' => $paymentInfoId,
+        ]);
+
+        if ($paymentInfo) {
+            $paymentInfo->setStoredMethodId($storedMethodId);
+            $this->modelManager->persist($paymentInfo);
+            $this->modelManager->flush($paymentInfo);
+        }
+
+        return $args->getReturn();
+    }
+}
diff --git a/Subscriber/Backend/HideStoredPaymentsSubscriber.php b/Subscriber/Backend/HideStoredPaymentsSubscriber.php
new file mode 100644
index 00000000..9459e779
--- /dev/null
+++ b/Subscriber/Backend/HideStoredPaymentsSubscriber.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber\Backend;
+
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use Enlight\Event\SubscriberInterface;
+use Symfony\Component\HttpFoundation\Response;
+
+final class HideStoredPaymentsSubscriber implements SubscriberInterface
+{
+    private const GET_PAYMENTS_ACTION = 'getPayments';
+
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            'Enlight_Controller_Action_PostDispatchSecure_Backend_Payment' => '__invoke',
+            'Enlight_Controller_Action_PostDispatchSecure_Backend_Shipping' => '__invoke',
+        ];
+    }
+
+    public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
+    {
+        if (!$this->isSuccessGetPaymentAction($args)) {
+            return;
+        }
+
+        $data = $args->getSubject()->View()->getAssign('data') ?? [];
+        if (!count($data)) {
+            return;
+        }
+
+        $data = PaymentMeanCollection::createFromShopwareArray($data)
+            ->filterExcludeHidden()
+            ->toShopwareArray();
+
+        $args->getSubject()->View()->assign('data', array_values($data));
+    }
+
+    private function isSuccessGetPaymentAction(\Enlight_Controller_ActionEventArgs $args): bool
+    {
+        if (!$args->getResponse() || Response::HTTP_OK !== $args->getResponse()->getHttpResponseCode()) {
+            return false;
+        }
+
+        if (!$args->getRequest() || self::GET_PAYMENTS_ACTION !== $args->getRequest()->getActionName()) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriber.php b/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriber.php
new file mode 100644
index 00000000..82889f3c
--- /dev/null
+++ b/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriber.php
@@ -0,0 +1,78 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber\Checkout;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Shopware\Provider\PaymentMeansProviderInterface;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+
+final class EnrichUmbrellaPaymentMeanSubscriber implements SubscriberInterface
+{
+    private Enlight_Components_Session_Namespace $session;
+    private PaymentMeansProviderInterface $paymentMeansProvider;
+
+    public function __construct(
+        Enlight_Components_Session_Namespace $session,
+        PaymentMeansProviderInterface $paymentMeansProvider
+    ) {
+        $this->session = $session;
+        $this->paymentMeansProvider = $paymentMeansProvider;
+    }
+
+    public static function getSubscribedEvents(): array
+    {
+        return ['Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => '__invoke'];
+    }
+
+    public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
+    {
+        $subject = $args->getSubject();
+        $actionName = $args->getRequest()->getActionName();
+        $isShippingPaymentView = 'shippingPayment' === $actionName && !$args->getRequest()->getParam('isXHR');
+        if (!$isShippingPaymentView) {
+            return;
+        }
+
+        $enrichedPaymentMeans = PaymentMeanCollection::createFromShopwareArray(($this->paymentMeansProvider)());
+        $userData = $subject->View()->getAssign('sUserData');
+
+        // if the stored method is not saved in session it means it was not selected in the payment step
+        $storedMethodId = $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID);
+        if (null === $storedMethodId) {
+            $preselectedPaymentId = $userData['additional']['payment']['id'] ?? null;
+            if (null === $preselectedPaymentId) {
+                return;
+            }
+
+            $umbrellaPayment = $enrichedPaymentMeans->fetchStoredMethodUmbrellaPaymentMean();
+            if (null === $umbrellaPayment) {
+                // guest user won't have stored method
+                return;
+            }
+            // but if the umbrella payment is in the user data it means a stored method was preselected by the user
+            if ($umbrellaPayment->getId() !== (int) $preselectedPaymentId) {
+                return;
+            }
+            // we use the saved user preference to get the stored method and allow the rest of the flow work normally
+            $storedMethodId = $args->getSubject()->View()->getAssign('adyenUserPreference')['storedMethodId'] ?? null;
+        }
+
+        if (null === $storedMethodId) {
+            return;
+        }
+
+        $paymentMean = $enrichedPaymentMeans->fetchByStoredMethodId($storedMethodId);
+        if (null === $paymentMean) {
+            return;
+        }
+
+        $userData = $subject->View()->getAssign('sUserData');
+        $userData['additional']['payment'] = $paymentMean->getRaw();
+        $subject->View()->assign('sUserData', $userData);
+        $subject->View()->assign('sFormData', ['payment' => $paymentMean->getValue('stored_method_umbrella_id')]);
+    }
+}
diff --git a/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriber.php b/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriber.php
index a050a9e8..eb157adc 100644
--- a/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriber.php
+++ b/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriber.php
@@ -4,18 +4,27 @@
 
 namespace AdyenPayment\Subscriber\Checkout;
 
+use AdyenPayment\AdyenPayment;
 use AdyenPayment\Collection\Payment\PaymentMeanCollection;
 use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProviderInterface;
-use AdyenPayment\Models\Payment\PaymentMean;
+use AdyenPayment\Shopware\Provider\PaymentMeansProviderInterface;
 use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
 
 final class EnrichUserAdditionalPaymentSubscriber implements SubscriberInterface
 {
     private EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider;
+    private PaymentMeansProviderInterface $paymentMeansProvider;
+    private Enlight_Components_Session_Namespace $session;
 
-    public function __construct(EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider)
-    {
+    public function __construct(
+        EnrichedPaymentMeanProviderInterface $enrichedPaymentMeanProvider,
+        PaymentMeansProviderInterface $paymentMeansProvider,
+        Enlight_Components_Session_Namespace $session
+    ) {
         $this->enrichedPaymentMeanProvider = $enrichedPaymentMeanProvider;
+        $this->paymentMeansProvider = $paymentMeansProvider;
+        $this->session = $session;
     }
 
     public static function getSubscribedEvents(): array
@@ -29,21 +38,30 @@ public static function getSubscribedEvents(): array
     public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
     {
         $subject = $args->getSubject();
-        if ('confirm' !== $subject->Request()->getActionName()) {
+        if ('confirm' !== $args->getRequest()->getActionName()) {
             return;
         }
 
+        $storedMethodId = $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID);
         $userData = $subject->View()->getAssign('sUserData');
-        $paymentMean = PaymentMean::createFromShopwareArray(
-            $subject->View()->getAssign('sUserData')['additional']['payment'] ?? []
+        $paymentMeanId = $userData['additional']['payment']['id'] ?? null;
+
+        if (null === $storedMethodId && null === $paymentMeanId) {
+            return;
+        }
+
+        $enrichedPaymentMeans = ($this->enrichedPaymentMeanProvider)(
+            PaymentMeanCollection::createFromShopwareArray(($this->paymentMeansProvider)())
         );
-        if (!$paymentMean->getId()) {
+
+        $paymentMean = null === $storedMethodId
+            ? $enrichedPaymentMeans->fetchById((int) $paymentMeanId)
+            : $enrichedPaymentMeans->fetchByStoredMethodId($storedMethodId);
+
+        if (null === $paymentMean) {
             return;
         }
 
-        $paymentMeans = ($this->enrichedPaymentMeanProvider)(new PaymentMeanCollection($paymentMean));
-        /** @var PaymentMean $paymentMean */
-        $paymentMean = iterator_to_array($paymentMeans->getIterator())[0];
         $userData['additional']['payment'] = $paymentMean->getRaw();
         $subject->View()->assign('sUserData', $userData);
     }
diff --git a/Subscriber/Checkout/PersistStoredMethodIdSubscriber.php b/Subscriber/Checkout/PersistStoredMethodIdSubscriber.php
new file mode 100644
index 00000000..6642a4bf
--- /dev/null
+++ b/Subscriber/Checkout/PersistStoredMethodIdSubscriber.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber\Checkout;
+
+use AdyenPayment\AdyenPayment;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+
+final class PersistStoredMethodIdSubscriber implements SubscriberInterface
+{
+    private Enlight_Components_Session_Namespace $session;
+
+    public function __construct(Enlight_Components_Session_Namespace $session)
+    {
+        $this->session = $session;
+    }
+
+    public static function getSubscribedEvents(): array
+    {
+        return ['Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => '__invoke'];
+    }
+
+    public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
+    {
+        $actionName = $args->getRequest()->getActionName();
+
+        $isShippingPaymentUpdate = 'shippingPayment' === $actionName && $args->getRequest()->getParam('isXHR');
+        $isSaveShippingPayment = 'saveShippingPayment' === $actionName;
+        if (!$isShippingPaymentUpdate && !$isSaveShippingPayment) {
+            return;
+        }
+
+        $storedMethodId = $args->getRequest()->getParam(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID);
+        $this->session->set(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, $storedMethodId);
+    }
+}
diff --git a/Subscriber/EnrichUserPreferenceSubscriber.php b/Subscriber/EnrichUserPreferenceSubscriber.php
new file mode 100755
index 00000000..0840cba3
--- /dev/null
+++ b/Subscriber/EnrichUserPreferenceSubscriber.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Subscriber;
+
+use AdyenPayment\Models\UserPreference;
+use Doctrine\ORM\EntityRepository;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+
+final class EnrichUserPreferenceSubscriber implements SubscriberInterface
+{
+    private Enlight_Components_Session_Namespace $session;
+    private EntityRepository $userPreferenceRepository;
+
+    public function __construct(
+        Enlight_Components_Session_Namespace $session,
+        EntityRepository $userPreferenceRepository
+    ) {
+        $this->session = $session;
+        $this->userPreferenceRepository = $userPreferenceRepository;
+    }
+
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            // inject in the view as early as possible to get the info in the other subscribers
+            'Enlight_Controller_Action_PostDispatch_Frontend_Account' => ['__invoke', -99999],
+            'Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => ['__invoke', -99999],
+        ];
+    }
+
+    public function __invoke(\Enlight_Controller_ActionEventArgs $args): void
+    {
+        $userId = $this->session->get('sUserId');
+        if (null === $userId) {
+            return;
+        }
+
+        /** @var UserPreference $userPreference */
+        $userPreference = $this->userPreferenceRepository->findOneBy(['userId' => $userId]);
+        if (null === $userPreference) {
+            return;
+        }
+
+        $args->getSubject()->View()->assign('adyenUserPreference', $userPreference->mapToView());
+    }
+}
diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
index e7e57b43..a0c4692b 100644
--- a/bitbucket-pipelines.yml
+++ b/bitbucket-pipelines.yml
@@ -21,4 +21,5 @@ pipelines:
                   script:
                       - echo "memory_limit=-1" >> /usr/local/etc/php/php.ini
                       - composer install --prefer-dist
+                      - composer install --prefer-dist -d ./tools/
                       - php ./vendor/bin/grumphp run --testsuite="code-compatibility"
diff --git a/grumphp.yml.dist b/grumphp.yml.dist
index 87435f22..537b226c 100644
--- a/grumphp.yml.dist
+++ b/grumphp.yml.dist
@@ -25,7 +25,7 @@ grumphp:
                 - 'die('
                 - 'dump('
                 - 'exit;'
-                - "\\bdd\\b\\("
+                - "\\bdd\\b("
             triggered_by: [php,js,tpl]
         file_size:
             max_size: 5M
diff --git a/plugin.xml b/plugin.xml
index 1117f3f8..a52fa3ea 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -4,7 +4,7 @@
 
     <label>Adyen Shopware Plugin</label>
     <label lang="de">Adyen Shopware Plugin</label>
-    <version>3.3.0</version>
+    <version>3.4.0</version>
     <copyright>Adyen</copyright>
     <author>Adyen</author>
     <link>https://adyen.com</link>
@@ -289,4 +289,20 @@
             Plugin compatibility with SwagPayPal
         </changes>
     </changelog>
+    <changelog version="3.3.1">
+        <changes lang="en">
+            Same release as 3.3.0, but alternate working for archive file
+        </changes>
+        <changes lang="de">
+            Same release as 3.3.0, but alternate working for archive file
+        </changes>
+    </changelog>
+    <changelog version="3.4.0">
+        <changes lang="en">
+            Enable Adyen's stored payment methods feature
+        </changes>
+        <changes lang="de">
+            Enable Adyen's stored payment methods feature
+        </changes>
+    </changelog>
 </plugin>
diff --git a/storage/apple-developer-merchantid-domain-association.zip b/storage/apple-developer-merchantid-domain-association.archive
similarity index 100%
rename from storage/apple-developer-merchantid-domain-association.zip
rename to storage/apple-developer-merchantid-domain-association.archive
diff --git a/tests/Unit/Collection/Payment/PaymentMeanCollectionTest.php b/tests/Unit/Collection/Payment/PaymentMeanCollectionTest.php
new file mode 100644
index 00000000..99ea9bb0
--- /dev/null
+++ b/tests/Unit/Collection/Payment/PaymentMeanCollectionTest.php
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Collection\Payment;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Models\Payment\PaymentMean;
+use PHPUnit\Framework\TestCase;
+
+final class PaymentMeanCollectionTest extends TestCase
+{
+    private PaymentMeanCollection $collection;
+
+    protected function setUp(): void
+    {
+        $this->collection = new PaymentMeanCollection();
+    }
+
+    /** @test */
+    public function it_implements_iterable(): void
+    {
+        self::assertInstanceOf(\IteratorAggregate::class, $this->collection);
+    }
+
+    /** @test */
+    public function it_can_count(): void
+    {
+        self::assertInstanceOf(\Countable::class, $this->collection);
+        self::assertCount(0, $this->collection);
+    }
+
+    /** @test */
+    public function it_can_map_with_a_callback(): void
+    {
+        $filteredSource = SourceType::adyen();
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['source' => $filteredSource->getType()],
+        ]);
+
+        $result = $collection->map(static fn(PaymentMean $payment) => ['mapped']);
+
+        self::assertEquals([['mapped']], $result);
+    }
+
+    /** @test */
+    public function it_can_filter_by_source(): void
+    {
+        $filteredSource = SourceType::adyen();
+
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['id' => $expected = 123, 'source' => $filteredSource->getType()],
+            ['id' => 456, 'source' => '1'],
+        ]);
+
+        $result = $collection->filterBySource($filteredSource);
+
+        self::assertInstanceOf(PaymentMeanCollection::class, $result);
+        self::assertCount(1, $result);
+        self::assertEquals($expected, iterator_to_array($result)[0]->getId());
+    }
+
+    /** @test */
+    public function it_can_exclude_adyen(): void
+    {
+        $filteredSource = SourceType::adyen();
+        $expected = PaymentMeanCollection::createFromShopwareArray([
+            ['source' => '1'],
+        ]);
+
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['source' => $filteredSource->getType()],
+            ['source' => '1'],
+        ]);
+
+        $result = $collection->filterExcludeAdyen();
+
+        self::assertEquals($expected, $result);
+    }
+
+    /** @test */
+    public function it_can_exclude_hidden(): void
+    {
+        $filteredSource = SourceType::adyen();
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['id' => 123, 'source' => $filteredSource->getType(), 'hide' => true],
+            ['id' => $expected = 345, 'source' => $filteredSource->getType()],
+        ]);
+
+        $result = $collection->filterExcludeHidden();
+
+        self::assertInstanceOf(PaymentMeanCollection::class, $result);
+        self::assertCount(1, $result);
+        self::assertEquals($expected, iterator_to_array($result)[0]->getId());
+    }
+
+    /** @test */
+    public function it_can_fetch_umbrella_payment_if_available(): void
+    {
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['source' => SourceType::adyen()->getType(), 'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE],
+            ['source' => '1'],
+        ]);
+
+        $result = $collection->fetchStoredMethodUmbrellaPaymentMean();
+
+        self::assertInstanceOf(PaymentMean::class, $result);
+        self::assertEquals(AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE, $result->getValue('name'));
+    }
+
+    /** @test */
+    public function it_will_return_null_on_fetch_umbrella_if_payment_not_available(): void
+    {
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['source' => SourceType::adyen()->getType()],
+            ['source' => '1'],
+        ]);
+
+        $result = $collection->fetchStoredMethodUmbrellaPaymentMean();
+
+        self::assertNull($result);
+    }
+
+    /** @test */
+    public function it_can_fetch_a_payment_by_stored_method_id(): void
+    {
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['id' => 123, 'source' => SourceType::adyen()->getType()],
+            ['id' => $expected = 456, 'source' => '1', 'stored_method_id' => $paymentMeanId = 'test123'],
+        ]);
+
+        $result = $collection->fetchByStoredMethodId($paymentMeanId);
+
+        self::assertInstanceOf(PaymentMean::class, $result);
+        self::assertEquals(1, $result->getSource()->getType());
+        self::assertEquals($expected, $result->getId());
+    }
+
+    /** @test */
+    public function it_can_fetch_a_payment_by_stored_method_umbrella_id(): void
+    {
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['id' => 123, 'source' => SourceType::adyen()->getType()],
+            ['id' => $expected = 456, 'source' => '1', 'stored_method_umbrella_id' => $paymentMeanId = 'test123'],
+        ]);
+
+        $result = $collection->fetchByUmbrellaStoredMethodId($paymentMeanId);
+
+        self::assertInstanceOf(PaymentMean::class, $result);
+        self::assertEquals(1, $result->getSource()->getType());
+        self::assertEquals($expected, $result->getId());
+    }
+
+    /** @test */
+    public function it_can_fetch_a_payment_by_payment_id(): void
+    {
+        $filteredSource = SourceType::adyen();
+        $collection = PaymentMeanCollection::createFromShopwareArray([
+            ['id' => $paymentMeanId = 123, 'source' => $filteredSource->getType()],
+            ['id' => '456', 'source' => '1'],
+        ]);
+
+        $result = $collection->fetchById($paymentMeanId);
+
+        self::assertInstanceOf(PaymentMean::class, $result);
+        self::assertEquals($paymentMeanId, $result->getId());
+    }
+
+    /** @test */
+    public function it_returns_collection_in_shopware_array_format(): void
+    {
+        $filteredSource = SourceType::adyen();
+        $paymentData = ['id' => '123', 'source' => $filteredSource->getType()];
+        $expected = [$paymentData['id'] => $paymentData];
+        $collection = PaymentMeanCollection::createFromShopwareArray([$paymentData]);
+
+        $result = $collection->toShopwareArray();
+
+        self::assertEquals($expected, $result);
+    }
+}
diff --git a/tests/Unit/Collection/Payment/PaymentMethodCollectionTest.php b/tests/Unit/Collection/Payment/PaymentMethodCollectionTest.php
new file mode 100644
index 00000000..eaac913a
--- /dev/null
+++ b/tests/Unit/Collection/Payment/PaymentMethodCollectionTest.php
@@ -0,0 +1,150 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Collection\Payment;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMethodCollection;
+use AdyenPayment\Models\Payment\PaymentMean;
+use AdyenPayment\Models\Payment\PaymentMethod;
+use PHPUnit\Framework\TestCase;
+use Shopware\Bundle\StoreFrontBundle\Struct\Attribute;
+
+final class PaymentMethodCollectionTest extends TestCase
+{
+    /** @test */
+    public function it_implements_iterable(): void
+    {
+        self::assertInstanceOf(\IteratorAggregate::class, new PaymentMethodCollection());
+    }
+
+    /** @test */
+    public function it_can_count(): void
+    {
+        $result = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => [['type' => 'someType']]]);
+
+        self::assertInstanceOf(\Countable::class, $result);
+        self::assertCount(1, $result);
+    }
+
+    /** @test */
+    public function it_can_map_with_a_callback(): void
+    {
+        $expectedMethod = [true, false];
+        $collection = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => [
+            ['type' => $filteredType = 'someType'],
+            ['type' => 'otherType'],
+        ]]);
+
+        $result = $collection->map(static function(PaymentMethod $payment) use ($filteredType) {
+            return $filteredType === $payment->adyenType()->type();
+        });
+
+        self::assertEquals($expectedMethod, $result);
+    }
+
+    /** @test */
+    public function it_can_map_to_raw(): void
+    {
+        $expected = [
+            ['type' => 'someType'],
+            ['type' => 'otherType'],
+        ];
+        $collection = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => $expected]);
+
+        $result = $collection->mapToRaw();
+
+        self::assertEquals($expected, $result);
+    }
+
+    /** @test */
+    public function it_can_map_adyen_payment_methods(): void
+    {
+        $expectedMethod = PaymentMethod::fromRaw($paymentMethodData = ['type' => 'someType']);
+        $result = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => [$paymentMethodData]]);
+
+        self::assertInstanceOf(PaymentMethodCollection::class, $result);
+        self::assertEquals($expectedMethod, $result->getIterator()->current());
+    }
+
+    /** @test */
+    public function it_can_map_adyen_stored_payment_methods(): void
+    {
+        $expectedMethod = PaymentMethod::fromRaw($storedPaymentMethodData = ['type' => 'someType', 'id' => '1234']);
+        $result = PaymentMethodCollection::fromAdyenMethods(['storedPaymentMethods' => [$storedPaymentMethodData]]);
+
+        self::assertInstanceOf(PaymentMethodCollection::class, $result);
+        self::assertEquals($expectedMethod, $result->getIterator()->current());
+    }
+
+    /** @test */
+    public function it_can_enrich_with_import_locale(): void
+    {
+        $paymentMethod = PaymentMethod::fromRaw($paymentMethodData = [
+            'type' => 'someType',
+            'name' => $name = 'someName',
+        ]);
+        $expectedMethod = $paymentMethod->withCode($name);
+        $paymentMethodsCollection = PaymentMethodCollection::fromAdyenMethods([
+            'paymentMethods' => [$paymentMethodData],
+        ]);
+
+        $result = $paymentMethodsCollection->withImportLocale($paymentMethodsCollection);
+
+        self::assertInstanceOf(PaymentMethodCollection::class, $result);
+        self::assertEquals($expectedMethod, $result->getIterator()->current());
+    }
+
+    /** @test */
+    public function it_will_return_null_on_missing_methods_for_fetch_by_payment_mean(): void
+    {
+        $paymentMean = PaymentMean::createFromShopwareArray([
+            'id' => 1,
+            'source' => 1425514,
+        ]);
+        $collection = new PaymentMethodCollection();
+
+        $result = $collection->fetchByPaymentMean($paymentMean);
+
+        self::assertNull($result);
+    }
+
+    /** @test */
+    public function it_can_fetch_a_method_by_payment_mean(): void
+    {
+        $attribute = new Attribute();
+        $attribute->set(AdyenPayment::ADYEN_CODE, 'my_adyen_code');
+        $paymentMean = PaymentMean::createFromShopwareArray([
+            'id' => $methodStoredId = 'test_stored_method_id',
+            'source' => 1425514,
+            'attribute' => $attribute,
+            'stored_method_id' => $methodStoredId,
+        ]);
+        $testPayment = PaymentMethod::fromRaw(['type' => 'someType']);
+        $expectedPayment = PaymentMethod::fromRaw(['type' => 'someType2', 'id' => $methodStoredId]);
+        $collection = new PaymentMethodCollection($testPayment, $expectedPayment);
+
+        $result = $collection->fetchByPaymentMean($paymentMean);
+
+        self::assertSame($expectedPayment, $result);
+    }
+
+    /** @test */
+    public function it_can_filter_with_a_callback(): void
+    {
+        $collection = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => [
+            ['type' => $filteredType = 'someType'],
+            ['type' => 'otherType'],
+        ]]);
+        $expected = PaymentMethodCollection::fromAdyenMethods(['paymentMethods' => [
+            ['type' => $filteredType],
+        ]]);
+
+        $result = $collection->filter(static function(PaymentMethod $payment) use ($filteredType) {
+            return $filteredType === $payment->adyenType()->type();
+        });
+
+        self::assertEquals($expected, $result);
+    }
+}
diff --git a/tests/Unit/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProviderTest.php b/tests/Unit/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProviderTest.php
new file mode 100644
index 00000000..3405c4b4
--- /dev/null
+++ b/tests/Unit/Components/Adyen/PaymentMethod/EnrichedPaymentMeanProviderTest.php
@@ -0,0 +1,296 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Collection\Payment\PaymentMethodCollection;
+use AdyenPayment\Components\Adyen\Builder\PaymentMethodOptionsBuilderInterface;
+use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProvider;
+use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProviderInterface;
+use AdyenPayment\Components\Adyen\PaymentMethodServiceInterface;
+use AdyenPayment\Enricher\Payment\PaymentMethodEnricherInterface;
+use AdyenPayment\Exceptions\UmbrellaPaymentMeanNotFoundException;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Models\Payment\PaymentMean;
+use AdyenPayment\Models\Payment\PaymentMethod;
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+use Shopware\Bundle\StoreFrontBundle\Struct\Attribute;
+
+final class EnrichedPaymentMeanProviderTest extends TestCase
+{
+    use ProphecyTrait;
+
+    /**
+     * @var ObjectProphecy|PaymentMethodServiceInterface
+     */
+    private $paymentMethodService;
+
+    /**
+     * @var ObjectProphecy|PaymentMethodOptionsBuilderInterface
+     */
+    private $paymentMethodOptionsBuilder;
+
+    /**
+     * @var ObjectProphecy|PaymentMethodEnricherInterface
+     */
+    private $paymentMethodEnricher;
+    private EnrichedPaymentMeanProvider $provider;
+
+    protected function setUp(): void
+    {
+        $this->paymentMethodService = $this->prophesize(PaymentMethodServiceInterface::class);
+        $this->paymentMethodOptionsBuilder = $this->prophesize(PaymentMethodOptionsBuilderInterface::class);
+        $this->paymentMethodEnricher = $this->prophesize(PaymentMethodEnricherInterface::class);
+
+        $this->provider = new EnrichedPaymentMeanProvider(
+            $this->paymentMethodService->reveal(),
+            $this->paymentMethodOptionsBuilder->reveal(),
+            $this->paymentMethodEnricher->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_an_enriched_payment_mean_provider(): void
+    {
+        $this->assertInstanceOf(EnrichedPaymentMeanProviderInterface::class, $this->provider);
+    }
+
+    /** @test */
+    public function it_does_not_enrich_on_empty_cart_value_and_excludes_adyen_methods(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMeanOne = PaymentMean::createFromShopwareArray([
+                'id' => 1,
+                'source' => SourceType::shopwareDefault()->getType(),
+            ]),
+            PaymentMean::createFromShopwareArray([
+                'id' => 2,
+                'source' => SourceType::adyen()->getType(),
+            ]),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn(['value' => 0.0]);
+        $this->paymentMethodService->getPaymentMethods(Argument::cetera())->shouldNotBeCalled();
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(1, $result);
+        $this->assertSame($paymentMeanOne, iterator_to_array($result)[0]);
+    }
+
+    /** @test */
+    public function it_throws_an_exception_on_missing_umbrella_payment(): void
+    {
+        $adyenIdentifier = sprintf('%s_%s', $adyenType = 'non', $adyenName = 'adyen');
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMeanOne = PaymentMean::createFromShopwareArray([
+                'id' => 1,
+                'source' => SourceType::shopwareDefault()->getType(),
+            ]),
+        );
+
+        // filled with a matching identifier, to catch if the early returns fails
+        $adyenPaymentMethods = new PaymentMethodCollection(
+            PaymentMethod::fromRaw(['type' => $adyenType])->withCode($adyenName),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'BE',
+            'currency' => $currency = 'EUR',
+            'value' => $value = 17.7,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn($adyenPaymentMethods);
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $this->expectException(UmbrellaPaymentMeanNotFoundException::class);
+
+        $result = $this->provider->__invoke($paymentMeans);
+    }
+
+    /** @test */
+    public function it_does_not_enrich_non_adyen_methods(): void
+    {
+        $adyenIdentifier = sprintf('%s_%s', $adyenType = 'non', $adyenName = 'adyen');
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray([
+                'id' => 17,
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+                'source' => SourceType::shopwareDefault()->getType(),
+                'attribute' => new Attribute([
+                    'adyen_type' => $adyenIdentifier,
+                ]),
+            ]),
+        );
+
+        // filled with a matching identifier, to catch if the early returns fails
+        $adyenPaymentMethods = new PaymentMethodCollection(
+            PaymentMethod::fromRaw(['type' => $adyenType])->withCode($adyenName),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'BE',
+            'currency' => $currency = 'EUR',
+            'value' => $value = 17.7,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn($adyenPaymentMethods);
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(1, $result);
+        $this->assertSame($paymentMean, iterator_to_array($result)[0]);
+    }
+
+    /** @test */
+    public function it_does_not_enrich_and_removes_payment_means_without_attribute(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMeanOne = PaymentMean::createFromShopwareArray([
+                'id' => 19,
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+                'source' => SourceType::shopwareDefault()->getType(),
+            ]),
+            $paymentMeanTwo = PaymentMean::createFromShopwareArray([
+                'id' => 21,
+                'source' => SourceType::adyen()->getType(),
+            ]),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'BE',
+            'currency' => $currency = 'EUR',
+            'value' => $value = 17.7,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn(new PaymentMethodCollection());
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(1, $result);
+        $this->assertEquals($paymentMeanOne, iterator_to_array($result)[0]);
+    }
+
+    /** @test */
+    public function it_does_not_enrich_payment_means_with_attribute_null_values(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray([
+                'id' => 9,
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+                'source' => SourceType::adyen()->getType(),
+                'attribute' => new Attribute([
+                    'adyen_type' => null,
+                ]),
+            ]),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'GB',
+            'currency' => $currency = 'GBP',
+            'value' => $value = 9.39,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn(new PaymentMethodCollection());
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(0, $result);
+    }
+
+    /** @test */
+    public function it_removes_adyen_payment_means_without_matching_adyen_payment_method(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray([
+                'id' => 25,
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+                'source' => SourceType::adyen()->getType(),
+                'attribute' => new Attribute([
+                    'adyen_type' => 'non_matching_adyen_identifier',
+                ]),
+            ]),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'BE',
+            'currency' => $currency = 'EUR',
+            'value' => $value = 17.7,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn(new PaymentMethodCollection());
+        $this->paymentMethodEnricher->__invoke(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(0, $result);
+    }
+
+    /** @test */
+    public function it_enriches_adyen_payment_methods(): void
+    {
+        $adyenIdentifier = sprintf('%s_%s', $adyenType = 'bcmc', $adyenName = 'adyen_name');
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray($raw = [
+                'id' => $id = 15,
+                'source' => $source = SourceType::adyen()->getType(),
+                'attribute' => new Attribute([
+                    'adyen_type' => $adyenIdentifier,
+                ]),
+            ]),
+            $umbrellaMean = PaymentMean::createFromShopwareArray([
+                'id' => 25,
+                'source' => SourceType::adyen()->getType(),
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+            ]),
+        );
+
+        $adyenPaymentMethods = new PaymentMethodCollection(
+            $paymentMethod = PaymentMethod::fromRaw([
+                'type' => $adyenType,
+            ])->withCode($adyenName),
+            $storedPaymentMethod = PaymentMethod::fromRaw($storedRaw = [
+                'id' => $storedMethodId = 'adyen-stored-payment-method-id',
+                'type' => $schemaType = 'scheme',
+            ]),
+        );
+
+        $this->paymentMethodOptionsBuilder->__invoke()->willReturn([
+            'countryCode' => $countryCode = 'DE',
+            'currency' => $currency = 'EUR',
+            'value' => $value = 15.0,
+        ]);
+        $this->paymentMethodService->getPaymentMethods($countryCode, $currency, $value)
+            ->willReturn($adyenPaymentMethods);
+
+        $this->paymentMethodEnricher->__invoke($raw, $paymentMethod)->willReturn($rawEnriched = [
+            'id' => $id,
+            'source' => $source,
+            'enriched' => true,
+            'adyenType' => $adyenType,
+        ]);
+
+        $this->paymentMethodEnricher->__invoke($umbrellaMean->getRaw(), $storedPaymentMethod)->willReturn($storedRawEnriched = [
+            'id' => $storedMethodId,
+            'adyenType' => $schemaType,
+            'source' => $source,
+        ]);
+
+        $result = $this->provider->__invoke($paymentMeans);
+
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(2, $result);
+        $this->assertEquals($rawEnriched, iterator_to_array($result)[0]->getRaw());
+        $this->assertEquals($storedRawEnriched, iterator_to_array($result)[1]->getRaw());
+    }
+}
diff --git a/tests/Unit/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderTest.php b/tests/Unit/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderTest.php
new file mode 100644
index 00000000..8043db3e
--- /dev/null
+++ b/tests/Unit/Components/Adyen/PaymentMethod/StoredPaymentMeanProviderTest.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProviderInterface;
+use AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProvider;
+use AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProviderInterface;
+use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\ForwardCompatibility\DriverResultStatement;
+use Doctrine\DBAL\Query\QueryBuilder;
+use Enlight_Controller_Request_Request;
+use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class StoredPaymentMeanProviderTest extends TestCase
+{
+    use ProphecyTrait;
+    private StoredPaymentMeanProviderInterface $storedPaymentMeanProvider;
+
+    /** @var EnrichedPaymentMeanProviderInterface|ObjectProphecy */
+    private $enrichedPaymentMeanProvider;
+
+    /** @var Connection|ObjectProphecy */
+    private $connection;
+
+    protected function setUp(): void
+    {
+        $this->enrichedPaymentMeanProvider = $this->prophesize(EnrichedPaymentMeanProviderInterface::class);
+        $this->connection = $this->prophesize(Connection::class);
+
+        $this->storedPaymentMeanProvider = new StoredPaymentMeanProvider(
+            $this->enrichedPaymentMeanProvider->reveal(),
+            $this->connection->reveal(),
+        );
+    }
+
+    /** @test */
+    public function it_is_an_stored_payment_mean_provider(): void
+    {
+        $this->assertInstanceOf(StoredPaymentMeanProviderInterface::class, $this->storedPaymentMeanProvider);
+    }
+
+    /** @test */
+    public function it_will_return_null_on_missing_params(): void
+    {
+        $request = $this->prophesize(Enlight_Controller_Request_Request::class);
+        $request->getParam('register', [])->willReturn([]);
+
+        $result = $this->storedPaymentMeanProvider->fromRequest($request->reveal());
+
+        self::assertNull($result);
+    }
+
+    /** @test */
+    public function it_will_try_to_provide_a_payment_by_umbrella_stored_method_id(): void
+    {
+        $request = $this->prophesize(Enlight_Controller_Request_Request::class);
+        $request->getParam('register', [])->willReturn(['payment' => $id = 'stored_method_umbrella_id']);
+
+        $emptyCollection = PaymentMeanCollection::createFromShopwareArray([]);
+        $this->enrichedPaymentMeanProvider->__invoke($emptyCollection)->willReturn($emptyCollection);
+        $queryBuilder = $this->prophesize(QueryBuilder::class);
+        $queryBuilder->select('*')->willReturn($queryBuilder);
+        $queryBuilder->from('s_core_paymentmeans')->willReturn($queryBuilder);
+        $queryBuilder->where('name = :umbrellaMethodName')->willReturn($queryBuilder);
+        $queryBuilder->setParameter(':umbrellaMethodName', AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE)->willReturn($queryBuilder);
+        $driverResultStatement = $this->prophesize(DriverResultStatement::class);
+        $driverResultStatement->fetchAll()->willReturn([]);
+
+        $queryBuilder->execute()->willReturn($driverResultStatement->reveal());
+        $this->connection->createQueryBuilder()->willReturn($queryBuilder->reveal());
+
+        $result = $this->storedPaymentMeanProvider->fromRequest($request->reveal());
+
+        self::assertNull($result);
+    }
+}
diff --git a/tests/Unit/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProviderTest.php b/tests/Unit/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProviderTest.php
new file mode 100644
index 00000000..40499328
--- /dev/null
+++ b/tests/Unit/Components/Adyen/PaymentMethod/TraceableEnrichedPaymentMeanProviderTest.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Components\Adyen\PaymentMethod;
+
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProviderInterface;
+use AdyenPayment\Components\Adyen\PaymentMethod\TraceableEnrichedPaymentMeanProvider;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Models\Payment\PaymentMean;
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+use Psr\Log\LoggerInterface;
+
+class TraceableEnrichedPaymentMeanProviderTest extends TestCase
+{
+    use ProphecyTrait;
+
+    /**
+     * @var LoggerInterface|ObjectProphecy
+     */
+    private $logger;
+
+    /**
+     * @var EnrichedPaymentMeanProviderInterface|ObjectProphecy
+     */
+    private $enrichedPaymentMeanProvider;
+    private TraceableEnrichedPaymentMeanProvider $provider;
+
+    protected function setUp(): void
+    {
+        $this->enrichedPaymentMeanProvider = $this->prophesize(EnrichedPaymentMeanProviderInterface::class);
+        $this->logger = $this->prophesize(LoggerInterface::class);
+
+        $this->provider = new TraceableEnrichedPaymentMeanProvider(
+            $this->enrichedPaymentMeanProvider->reveal(),
+            $this->logger->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_provides_enriched_payment_means(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray([
+                'source' => SourceType::adyen()->getType(),
+            ])
+        );
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willReturn(
+            $enriched = new PaymentMeanCollection($paymentMean)
+        );
+        $this->logger->critical(Argument::cetera())->shouldNotBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertSame($enriched, $result);
+    }
+
+    /** @test */
+    public function it_logs_silently_exceptions(): void
+    {
+        $paymentMeans = new PaymentMeanCollection(
+            $paymentMean = PaymentMean::createFromShopwareArray([
+                'source' => SourceType::adyen()->getType(),
+            ])
+        );
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willThrow(
+            $exception = new \Exception($message = 'invalid type')
+        );
+
+        $this->logger->critical($message, ['exception' => $exception])->shouldBeCalled();
+
+        $result = $this->provider->__invoke($paymentMeans);
+        $this->assertNotSame($paymentMeans, $result);
+        $this->assertInstanceOf(PaymentMeanCollection::class, $result);
+        $this->assertCount(0, $result);
+    }
+}
diff --git a/tests/Unit/Components/Manager/UserPreferenceManagerTest.php b/tests/Unit/Components/Manager/UserPreferenceManagerTest.php
new file mode 100644
index 00000000..4d401d90
--- /dev/null
+++ b/tests/Unit/Components/Manager/UserPreferenceManagerTest.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Components\Manager;
+
+use AdyenPayment\Components\Manager\UserPreferenceManager;
+use AdyenPayment\Components\Manager\UserPreferenceManagerInterface;
+use AdyenPayment\Models\UserPreference;
+use Doctrine\ORM\EntityManager;
+use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class UserPreferenceManagerTest extends TestCase
+{
+    use ProphecyTrait;
+    private UserPreferenceManager $userPreferenceManager;
+
+    /**
+     * @var EntityManager|ObjectProphecy
+     */
+    private $modelManager;
+
+    protected function setUp(): void
+    {
+        $this->modelManager = $this->prophesize(EntityManager::class);
+
+        $this->userPreferenceManager = new UserPreferenceManager($this->modelManager->reveal());
+    }
+
+    /** @test */
+    public function it_is_an_user_preference_manager(): void
+    {
+        $this->assertInstanceOf(UserPreferenceManagerInterface::class, $this->userPreferenceManager);
+    }
+
+    /** @test */
+    public function it_can_save_a_record(): void
+    {
+        $userPreference = new UserPreference();
+        $userPreference->setUserId(1234);
+        $userPreference->setStoredMethodId('expected-method-id');
+
+        $this->modelManager->persist($userPreference)->shouldBeCalled();
+        $this->modelManager->flush($userPreference)->shouldBeCalled();
+
+        $this->userPreferenceManager->save($userPreference);
+    }
+}
diff --git a/tests/Unit/Enricher/Payment/PaymentMethodEnricherTest.php b/tests/Unit/Enricher/Payment/PaymentMethodEnricherTest.php
new file mode 100644
index 00000000..d46adb1d
--- /dev/null
+++ b/tests/Unit/Enricher/Payment/PaymentMethodEnricherTest.php
@@ -0,0 +1,115 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Enricher\Payment;
+
+use AdyenPayment\Components\Adyen\PaymentMethod\ImageLogoProviderInterface;
+use AdyenPayment\Enricher\Payment\PaymentMethodEnricher;
+use AdyenPayment\Enricher\Payment\PaymentMethodEnricherInterface;
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Models\Payment\PaymentMethod;
+use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+use Shopware_Components_Snippet_Manager;
+
+final class PaymentMethodEnricherTest extends TestCase
+{
+    use ProphecyTrait;
+    private PaymentMethodEnricher $paymentMethodEnricher;
+
+    /** @var ObjectProphecy|Shopware_Components_Snippet_Manager */
+    private $snippets;
+
+    /** @var ImageLogoProviderInterface|ObjectProphecy */
+    private $imageLogoProvider;
+
+    protected function setUp(): void
+    {
+        $this->snippets = $this->prophesize(Shopware_Components_Snippet_Manager::class);
+        $this->imageLogoProvider = $this->prophesize(ImageLogoProviderInterface::class);
+
+        $this->paymentMethodEnricher = new PaymentMethodEnricher(
+            $this->snippets->reveal(),
+            $this->imageLogoProvider->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_a_payment_method_enricher(): void
+    {
+        $this->assertInstanceOf(PaymentMethodEnricherInterface::class, $this->paymentMethodEnricher);
+    }
+
+    /** @test */
+    public function it_will_enrich_a_payment_method_without_stored_method_data(): void
+    {
+        $shopwareMethod = [
+            'id' => 'shopware-method-id',
+            'additionaldescription' => '',
+            'image' => '',
+        ];
+        $paymentMethod = PaymentMethod::fromRaw($rawData = [
+            'code' => 'test_method',
+            'type' => 'test_type',
+        ]);
+        $snippetsNamespace = $this->prophesize(\Enlight_Components_Snippet_Namespace::class);
+        $snippetsNamespace->get($paymentMethod->adyenType()->type())->willReturn($description = 'Adyen Method');
+        $this->snippets->getNamespace('adyen/method/description')->willReturn($snippetsNamespace);
+        $this->imageLogoProvider->provideByType($paymentMethod->adyenType()->type())->willReturn($image = 'image');
+
+        $result = ($this->paymentMethodEnricher)($shopwareMethod, $paymentMethod);
+
+        $expected = [
+            'id' => 'shopware-method-id',
+            'additionaldescription' => $description,
+            'image' => $image,
+            'enriched' => true,
+            'isStoredPayment' => false,
+            'isAdyenPaymentMethod' => true,
+            'adyenType' => $paymentMethod->adyenType()->type(),
+            'metadata' => $rawData,
+        ];
+
+        self::assertEquals($expected, $result);
+    }
+
+    /** @test */
+    public function it_will_enrich_a_payment_method_with_stored_method_data(): void
+    {
+        $shopwareMethod = ['id' => $shopwareMethodId = 'shopware-method-id'];
+        $paymentMethod = PaymentMethod::fromRaw($rawData = [
+            'id' => $storedMethodId = 'stored_method_id',
+            'name' => $storedMethodName = 'stored method name',
+            'code' => 'test_method',
+            'type' => 'test_type',
+            'lastFour' => $lastFour = '1234',
+        ]);
+        $snippetsNamespace = $this->prophesize(\Enlight_Components_Snippet_Namespace::class);
+        $snippetsNamespace->get($paymentMethod->adyenType()->type())->willReturn($description = 'Stored Method');
+        $snippetsNamespace->get('CardNumberEndingOn', $text = 'Card number ending on', true)->willReturn($text);
+        $this->snippets->getNamespace('adyen/method/description')->willReturn($snippetsNamespace);
+        $this->snippets->getNamespace('adyen/checkout/payment')->willReturn($snippetsNamespace);
+        $this->imageLogoProvider->provideByType($paymentMethod->adyenType()->type())->willReturn($image = 'image');
+
+        $result = ($this->paymentMethodEnricher)($shopwareMethod, $paymentMethod);
+
+        $expected = [
+            'id' => 'shopware-method-id',
+            'additionaldescription' => sprintf('%s%s: %s', $description.' ', $text, $lastFour),
+            'image' => $image,
+            'enriched' => true,
+            'isStoredPayment' => true,
+            'isAdyenPaymentMethod' => true,
+            'adyenType' => $paymentMethod->adyenType()->type(),
+            'metadata' => $rawData,
+            'stored_method_umbrella_id' => sprintf('%s_%s', $shopwareMethodId, $storedMethodId),
+            'stored_method_id' => $storedMethodId,
+            'description' => $storedMethodName,
+            'source' => SourceType::adyen()->getType(),
+        ];
+
+        self::assertEquals($expected, $result);
+    }
+}
diff --git a/tests/Unit/Mock/ControllerActionMock.php b/tests/Unit/Mock/ControllerActionMock.php
new file mode 100644
index 00000000..d32ff66f
--- /dev/null
+++ b/tests/Unit/Mock/ControllerActionMock.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Mock;
+
+final class ControllerActionMock extends \Enlight_Controller_Action
+{
+}
diff --git a/tests/Unit/Models/Payment/PaymentMeanTest.php b/tests/Unit/Models/Payment/PaymentMeanTest.php
index 4d9ee955..3b5c7729 100644
--- a/tests/Unit/Models/Payment/PaymentMeanTest.php
+++ b/tests/Unit/Models/Payment/PaymentMeanTest.php
@@ -21,10 +21,10 @@ protected function setUp(): void
             'source' => '1425514',
             'attribute' => new Attribute([
                 'adyen_type' => 'adyen-type',
-                'adyen_stored_method_id' => 'stored payment method id',
             ]),
             'enriched' => true,
             'adyenType' => 'adyen-type',
+            'hide' => true,
         ]);
     }
 
@@ -42,6 +42,12 @@ public function it_contains_a_source(): void
         $this->assertEquals(SourceType::adyen(), $this->paymentMean->getSource());
     }
 
+    /** @test */
+    public function it_knows_it_is_hidden(): void
+    {
+        $this->assertTrue($this->paymentMean->isHidden());
+    }
+
     /** @test */
     public function it_contains_raw_data(): void
     {
@@ -51,10 +57,10 @@ public function it_contains_raw_data(): void
             'source' => '1425514',
             'attribute' => new Attribute([
                 'adyen_type' => 'adyen-type',
-                'adyen_stored_method_id' => 'stored payment method id',
             ]),
             'enriched' => true,
             'adyenType' => 'adyen-type',
+            'hide' => true,
         ], $this->paymentMean->getRaw());
     }
 
@@ -94,7 +100,6 @@ public function it_can_retrieve_an_attribute(): void
     {
         $this->assertEquals(new Attribute([
             'adyen_type' => 'adyen-type',
-            'adyen_stored_method_id' => 'stored payment method id',
         ]), $this->paymentMean->getAttribute());
     }
 
@@ -111,12 +116,6 @@ public function it_can_retrieve_default_attribute_adyen_type(): void
         $this->assertEquals('', $paymentMean->getAdyenCode());
     }
 
-    /** @test */
-    public function it_can_retrieve_attribute_adyen_stored_method_id(): void
-    {
-        $this->assertEquals('stored payment method id', $this->paymentMean->getAdyenStoredMethodId());
-    }
-
     /** @test */
     public function it_can_retrieve_default_attribute_adyen_stored_method_id(): void
     {
diff --git a/tests/Unit/Subscriber/Account/SaveStoredMethodPreferenceSubscriberTest.php b/tests/Unit/Subscriber/Account/SaveStoredMethodPreferenceSubscriberTest.php
new file mode 100644
index 00000000..3176137b
--- /dev/null
+++ b/tests/Unit/Subscriber/Account/SaveStoredMethodPreferenceSubscriberTest.php
@@ -0,0 +1,203 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Checkout;
+
+use AdyenPayment\Components\Adyen\PaymentMethod\StoredPaymentMeanProviderInterface;
+use AdyenPayment\Components\Manager\UserPreferenceManagerInterface;
+use AdyenPayment\Models\Payment\PaymentMean;
+use AdyenPayment\Models\UserPreference;
+use AdyenPayment\Subscriber\Account\SaveStoredMethodPreferenceSubscriber;
+use Doctrine\ORM\EntityRepository;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Enlight_Controller_ActionEventArgs;
+use Enlight_Controller_Request_Request;
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class SaveStoredMethodPreferenceSubscriberTest extends TestCase
+{
+    use ProphecyTrait;
+    private SaveStoredMethodPreferenceSubscriber $subscriber;
+
+    /** @var Enlight_Components_Session_Namespace|ObjectProphecy */
+    private $session;
+
+    /** @var ObjectProphecy|UserPreferenceManagerInterface */
+    private $userPreferenceManager;
+
+    /** @var EntityRepository|ObjectProphecy */
+    private $userPreferenceRepository;
+
+    /** @var StoredPaymentMeanProviderInterface */
+    private $storedPaymentMeanProvider;
+
+    /** @var Enlight_Controller_ActionEventArgs|ObjectProphecy */
+    private $args;
+
+    /** @var Enlight_Controller_Request_Request|ObjectProphecy */
+    private $request;
+
+    protected function setUp(): void
+    {
+        $this->args = $this->prophesize(Enlight_Controller_ActionEventArgs::class);
+        $this->request = $this->prophesize(Enlight_Controller_Request_Request::class);
+        $this->session = $this->prophesize(Enlight_Components_Session_Namespace::class);
+        $this->userPreferenceManager = $this->prophesize(UserPreferenceManagerInterface::class);
+        $this->userPreferenceRepository = $this->prophesize(EntityRepository::class);
+        $this->storedPaymentMeanProvider = $this->prophesize(StoredPaymentMeanProviderInterface::class);
+
+        $this->subscriber = new SaveStoredMethodPreferenceSubscriber(
+            $this->session->reveal(),
+            $this->userPreferenceManager->reveal(),
+            $this->userPreferenceRepository->reveal(),
+            $this->storedPaymentMeanProvider->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            ['Enlight_Controller_Action_PostDispatch_Frontend_Account' => '__invoke'],
+            SaveStoredMethodPreferenceSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_user_id(): void
+    {
+        $this->session->get('sUserId')->willReturn(null);
+        $this->request->getActionName()->shouldNotBeCalled();
+        $this->request->isPost()->shouldNotBeCalled();
+        $this->args->getRequest()->shouldNotBeCalled();
+        $this->storedPaymentMeanProvider->fromRequest(Argument::cetera())->shouldNotBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_action_name(): void
+    {
+        $this->session->get('sUserId')->willReturn(123456);
+        $this->request->getActionName()->willReturn('wrong-action-name');
+        $this->request->isPost()->willReturn(true);
+        $this->args->getRequest()->willReturn($this->request);
+        $this->storedPaymentMeanProvider->fromRequest(Argument::cetera())->shouldNotBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_method(): void
+    {
+        $this->session->get('sUserId')->willReturn(123456);
+        $this->request->getActionName()->willReturn('savePayment');
+        $this->request->isPost()->willReturn(false);
+        $this->args->getRequest()->willReturn($this->request);
+        $this->storedPaymentMeanProvider->fromRequest(Argument::cetera())->shouldNotBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_will_save_the_user_preferences_with_empty_params(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 123456);
+        $this->request->getActionName()->willReturn('savePayment');
+        $this->request->isPost()->willReturn(true);
+        $this->request->getParam('register', [])->willReturn([]);
+        $this->args->getRequest()->willReturn($this->request->reveal());
+        $this->storedPaymentMeanProvider->fromRequest($this->request->reveal())->willReturn(null);
+
+        $userPreference = new UserPreference();
+        $userPreference->setUserId($userId);
+        $userPreference->setStoredMethodId(null);
+
+        $this->userPreferenceManager->save($userPreference)->shouldBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_will_save_the_user_preferences_with_null_for_none_stored_method_param(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 123456);
+        $this->request->getActionName()->willReturn('savePayment');
+        $this->request->isPost()->willReturn(true);
+        $this->request->getParam('register', [])->willReturn(['payment' => 'noneStoredPaymentId']);
+        $this->args->getRequest()->willReturn($this->request->reveal());
+        $this->storedPaymentMeanProvider->fromRequest($this->request->reveal())->willReturn(null);
+
+        $userPreference = new UserPreference();
+        $userPreference->setUserId($userId);
+        $userPreference->setStoredMethodId(null);
+
+        $this->userPreferenceManager->save($userPreference)->shouldBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_will_save_the_user_preferences_with_param_value(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 123456);
+        $this->request->getActionName()->willReturn('savePayment');
+        $this->request->isPost()->willReturn(true);
+        $this->request->getParam('register', [])->willReturn([
+            'payment' => 'proper_'.($storedMethodId = 'storedMethodId'),
+        ]);
+        $this->args->getRequest()->willReturn($this->request->reveal());
+        $storedPaymentMean = PaymentMean::createFromShopwareArray([
+            'source' => 'any',
+            'stored_method_id' => $storedMethodId,
+        ]);
+        $this->storedPaymentMeanProvider->fromRequest($this->request->reveal())->willReturn($storedPaymentMean);
+
+        $userPreference = new UserPreference();
+        $userPreference->setUserId($userId);
+        $userPreference->setStoredMethodId($storedMethodId);
+
+        $this->userPreferenceManager->save($userPreference)->shouldBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+
+    /** @test */
+    public function it_will_update_the_user_preferences_with_param_value(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 123456);
+        $this->request->getActionName()->willReturn('savePayment');
+        $this->request->isPost()->willReturn(true);
+        $this->request->getParam('register', [])->willReturn([
+            'payment' => 'proper_'.($storedMethodId = 'storedMethodId'),
+        ]);
+        $this->args->getRequest()->willReturn($this->request->reveal());
+        $storedPaymentMean = PaymentMean::createFromShopwareArray([
+            'source' => 'any',
+            'stored_method_id' => $storedMethodId,
+        ]);
+        $this->storedPaymentMeanProvider->fromRequest($this->request->reveal())->willReturn($storedPaymentMean);
+
+        $userPreference = new UserPreference();
+        $userPreference->setId(123);
+        $userPreference->setUserId($userId);
+        $userPreference->setStoredMethodId($storedMethodId);
+
+        $this->userPreferenceRepository->findOneBy(['userId' => $userId])->willReturn($userPreference);
+
+        $this->userPreferenceManager->save($userPreference)->shouldBeCalled();
+
+        $this->subscriber->__invoke($this->args->reveal());
+    }
+}
diff --git a/tests/Unit/Subscriber/Backend/HideStoredPaymentsSubscriberTest.php b/tests/Unit/Subscriber/Backend/HideStoredPaymentsSubscriberTest.php
new file mode 100644
index 00000000..b825f572
--- /dev/null
+++ b/tests/Unit/Subscriber/Backend/HideStoredPaymentsSubscriberTest.php
@@ -0,0 +1,119 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Backend;
+
+use AdyenPayment\Models\Enum\PaymentMethod\SourceType;
+use AdyenPayment\Subscriber\Backend\HideStoredPaymentsSubscriber;
+use AdyenPayment\Tests\Unit\Subscriber\SubscriberTestCase;
+use Enlight\Event\SubscriberInterface;
+use Symfony\Component\HttpFoundation\Response;
+
+final class HideStoredPaymentsSubscriberTest extends SubscriberTestCase
+{
+    private HideStoredPaymentsSubscriber $subscriber;
+
+    protected function setUp(): void
+    {
+        $this->subscriber = new HideStoredPaymentsSubscriber();
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            [
+                'Enlight_Controller_Action_PostDispatchSecure_Backend_Payment' => '__invoke',
+                'Enlight_Controller_Action_PostDispatchSecure_Backend_Shipping' => '__invoke',
+            ],
+            HideStoredPaymentsSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_request(): void
+    {
+        $eventArgs = new \Enlight_Controller_ActionEventArgs([
+            'subject' => $this->buildSubject($viewData = ['data' => 'view-data']),
+            'request' => null,
+            'response' => new \Enlight_Controller_Response_ResponseTestCase(),
+        ]);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_response(): void
+    {
+        $eventArgs = new \Enlight_Controller_ActionEventArgs([
+            'subject' => $this->buildSubject($viewData = ['data' => 'view-data']),
+            'request' => new \Enlight_Controller_Request_RequestTestCase(),
+            'response' => null,
+        ]);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_non_success_response_code(): void
+    {
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data'], Response::HTTP_BAD_REQUEST);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_action_name(): void
+    {
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_empty_view_data(): void
+    {
+        $eventArgs = $this->buildEventArgs('getPayments', $viewData = []);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_filters_hidden_payment_means(): void
+    {
+        $viewData = [
+            'data' => [
+                $nonHiddenPaymentMean = [
+                    'name' => 'A payment mean not hidden',
+                    'source' => SourceType::shopwareDefault()->getType(),
+                    'hide' => false,
+                ],
+                [
+                    'name' => 'A hidden payment mean',
+                    'source' => SourceType::shopwareDefault()->getType(),
+                    'hide' => true,
+                ],
+            ],
+        ];
+        $eventArgs = $this->buildEventArgs('getPayments', $viewData);
+
+        $this->subscriber->__invoke($eventArgs);
+        $this->assertEquals([
+            'data' => [
+                $nonHiddenPaymentMean,
+            ],
+        ], $eventArgs->getSubject()->View()->getAssign());
+    }
+}
diff --git a/tests/Unit/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriberTest.php b/tests/Unit/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriberTest.php
new file mode 100644
index 00000000..f6a4bcf6
--- /dev/null
+++ b/tests/Unit/Subscriber/Checkout/EnrichUmbrellaPaymentMeanSubscriberTest.php
@@ -0,0 +1,174 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Checkout;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Shopware\Provider\PaymentMeansProviderInterface;
+use AdyenPayment\Subscriber\Checkout\EnrichUmbrellaPaymentMeanSubscriber;
+use AdyenPayment\Tests\Unit\Subscriber\SubscriberTestCase;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class EnrichUmbrellaPaymentMeanSubscriberTest extends SubscriberTestCase
+{
+    use ProphecyTrait;
+    private EnrichUmbrellaPaymentMeanSubscriber $subscriber;
+
+    /** @var Enlight_Components_Session_Namespace|ObjectProphecy */
+    private $session;
+
+    /** @var ObjectProphecy|PaymentMeansProviderInterface */
+    private $paymentMeansProvider;
+
+    protected function setUp(): void
+    {
+        $this->session = $this->prophesize(Enlight_Components_Session_Namespace::class);
+        $this->paymentMeansProvider = $this->prophesize(PaymentMeansProviderInterface::class);
+        $this->subscriber = new EnrichUmbrellaPaymentMeanSubscriber(
+            $this->session->reveal(),
+            $this->paymentMeansProvider->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            ['Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => '__invoke'],
+            EnrichUmbrellaPaymentMeanSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_action_name(): void
+    {
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_xhr_request(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = ['data' => 'view-data']);
+        $eventArgs->getRequest()->setParam('isXHR', true);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_session_and_none_preselected_stored_method_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = ['data' => 'view-data']);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+        $this->paymentMeansProvider->__invoke()->willReturn([]);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_umbrella_method_for_preselected_payment(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = [
+            'sUserData' => ['additional' => ['payment' => ['id' => 'preselectedPaymentId']]],
+        ]);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+        $this->paymentMeansProvider->__invoke()->willReturn([]);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_payment_mean_for_stored_method(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = ['data' => 'view-data']);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->paymentMeansProvider->__invoke()->willReturn([]);
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn($storedMethodId = 'method-id');
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_use_the_preselected_stored_method_id_on_preselected_umbrella_payment(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = [
+            'adyenUserPreference' => ['storedMethodId' => $preselectedStoredMethod = 'any-stored-method-id'],
+            'sUserData' => ['additional' => ['payment' => ['id' => $preselectedUmbrellaId = 'umbrellaPaymentId']]],
+            'sFormData' => [],
+        ]);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+        $this->paymentMeansProvider->__invoke()->willReturn([
+            $umbrellaPaymentMeanRaw = [
+                'id' => $preselectedUmbrellaId,
+                'name' => AdyenPayment::ADYEN_STORED_PAYMENT_UMBRELLA_CODE,
+                'source' => 123,
+                'adyenType' => 'test',
+            ],
+            $paymentMeanRaw = [
+                'source' => 1234,
+                'adyenType' => 'test',
+                'stored_method_id' => $preselectedStoredMethod,
+                'stored_method_umbrella_id' => $umbrellaId = 'umbrella-id',
+            ],
+        ]);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        $expected = [
+            'adyenUserPreference' => ['storedMethodId' => $preselectedStoredMethod],
+            'sUserData' => ['additional' => ['payment' => $paymentMeanRaw]],
+            'sFormData' => ['payment' => $umbrellaId],
+        ];
+
+        self::assertEquals($expected, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_will_enrich_the_payment_mean_for_stored_method(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = [
+            'sUserData' => ['additional' => ['payment' => ['not-enriched-payment-data']]],
+            'sFormData' => [],
+        ]);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn($storedMethodId = 'method-id');
+        $this->paymentMeansProvider->__invoke()->willReturn([$paymentMeanRaw = [
+            'source' => 123,
+            'adyenType' => 'test',
+            'stored_method_id' => $storedMethodId,
+            'stored_method_umbrella_id' => $umbrellaId = 'umbrella-id',
+        ]]);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        $expected = [
+            'sUserData' => ['additional' => ['payment' => $paymentMeanRaw]],
+            'sFormData' => ['payment' => $umbrellaId],
+        ];
+
+        self::assertEquals($expected, $eventArgs->getSubject()->View()->getAssign());
+    }
+}
diff --git a/tests/Unit/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriberTest.php b/tests/Unit/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriberTest.php
new file mode 100644
index 00000000..66ce633a
--- /dev/null
+++ b/tests/Unit/Subscriber/Checkout/EnrichUserAdditionalPaymentSubscriberTest.php
@@ -0,0 +1,168 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Checkout;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Collection\Payment\PaymentMeanCollection;
+use AdyenPayment\Components\Adyen\PaymentMethod\EnrichedPaymentMeanProviderInterface;
+use AdyenPayment\Shopware\Provider\PaymentMeansProviderInterface;
+use AdyenPayment\Subscriber\Checkout\EnrichUserAdditionalPaymentSubscriber;
+use AdyenPayment\Tests\Unit\Subscriber\SubscriberTestCase;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class EnrichUserAdditionalPaymentSubscriberTest extends SubscriberTestCase
+{
+    use ProphecyTrait;
+    private EnrichUserAdditionalPaymentSubscriber $subscriber;
+
+    /** @var EnrichedPaymentMeanProviderInterface|ObjectProphecy */
+    private $enrichedPaymentMeanProvider;
+
+    /** @var ObjectProphecy|PaymentMeansProviderInterface */
+    private $paymentMeansProvider;
+
+    /** @var Enlight_Components_Session_Namespace|ObjectProphecy */
+    private $session;
+
+    protected function setUp(): void
+    {
+        $this->enrichedPaymentMeanProvider = $this->prophesize(EnrichedPaymentMeanProviderInterface::class);
+        $this->paymentMeansProvider = $this->prophesize(PaymentMeansProviderInterface::class);
+        $this->session = $this->prophesize(Enlight_Components_Session_Namespace::class);
+        $this->subscriber = new EnrichUserAdditionalPaymentSubscriber(
+            $this->enrichedPaymentMeanProvider->reveal(),
+            $this->paymentMeansProvider->reveal(),
+            $this->session->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            ['Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => ['__invoke', -99999]],
+            EnrichUserAdditionalPaymentSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_action_name(): void
+    {
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_stored_method_id_and_payment_mean_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('confirm', $viewData = ['sUserData' => []]);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+
+        $this->subscriber->__invoke($eventArgs);
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_payment_mean_for_stored_method_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('confirm', $viewData = ['sUserData' => []]);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn('method-id');
+        $this->paymentMeansProvider->__invoke()->willReturn($paymentMeansRaw = [[
+            'source' => 123,
+            'adyenType' => 'test',
+        ]]);
+
+        $paymentMeans = PaymentMeanCollection::createFromShopwareArray($paymentMeansRaw);
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willReturn($paymentMeans);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_payment_mean_for_payment_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('confirm', $viewData = ['sUserData' => [
+            'additional' => ['payment' => ['id' => $paymentId = '123123']],
+        ]]);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+        $this->paymentMeansProvider->__invoke()->willReturn($paymentMeansRaw = [[
+            'source' => 123,
+            'adyenType' => 'test',
+        ]]);
+
+        $paymentMeans = PaymentMeanCollection::createFromShopwareArray($paymentMeansRaw);
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willReturn($paymentMeans);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_will_update_the_view_with_payment_mean_for_stored_method_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('confirm', $viewData = ['sUserData' => []]);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn($storedMethodId = 'method-id');
+        $this->paymentMeansProvider->__invoke()->willReturn($paymentMeansRaw = [$paymentMeanRaw = [
+            'source' => 123,
+            'adyenType' => 'test',
+            'stored_method_id' => $storedMethodId,
+        ]]);
+
+        $paymentMeans = PaymentMeanCollection::createFromShopwareArray($paymentMeansRaw);
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willReturn($paymentMeans);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        $expected = [
+            'sUserData' => ['additional' => ['payment' => $paymentMeanRaw]],
+        ];
+
+        self::assertEquals($expected, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_will_update_the_view_with_payment_mean_for_payment_id(): void
+    {
+        $eventArgs = $this->buildEventArgs('confirm', $viewData = ['sUserData' => [
+            'additional' => ['payment' => ['id' => $paymentId = '123123']],
+        ]]);
+
+        $this->session->get(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID)->willReturn(null);
+        $this->paymentMeansProvider->__invoke()->willReturn($paymentMeansRaw = [$paymentMeanRaw = [
+            'id' => $paymentId,
+            'source' => 123,
+            'adyenType' => 'test',
+        ]]);
+
+        $paymentMeans = PaymentMeanCollection::createFromShopwareArray($paymentMeansRaw);
+        $this->enrichedPaymentMeanProvider->__invoke($paymentMeans)->willReturn($paymentMeans);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        $expected = [
+            'sUserData' => ['additional' => ['payment' => $paymentMeanRaw]],
+        ];
+
+        self::assertEquals($expected, $eventArgs->getSubject()->View()->getAssign());
+    }
+}
diff --git a/tests/Unit/Subscriber/Checkout/PersistStoredMehtodIdSubscriberTest.php b/tests/Unit/Subscriber/Checkout/PersistStoredMehtodIdSubscriberTest.php
new file mode 100644
index 00000000..c55b1386
--- /dev/null
+++ b/tests/Unit/Subscriber/Checkout/PersistStoredMehtodIdSubscriberTest.php
@@ -0,0 +1,90 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Checkout;
+
+use AdyenPayment\AdyenPayment;
+use AdyenPayment\Subscriber\Checkout\PersistStoredMethodIdSubscriber;
+use AdyenPayment\Tests\Unit\Subscriber\SubscriberTestCase;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class PersistStoredMehtodIdSubscriberTest extends SubscriberTestCase
+{
+    use ProphecyTrait;
+    private PersistStoredMethodIdSubscriber $subscriber;
+
+    /** @var Enlight_Components_Session_Namespace|ObjectProphecy */
+    private $session;
+
+    protected function setUp(): void
+    {
+        $this->session = $this->prophesize(Enlight_Components_Session_Namespace::class);
+        $this->subscriber = new PersistStoredMethodIdSubscriber($this->session->reveal());
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            ['Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => '__invoke'],
+            PersistStoredMethodIdSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_wrong_request_action_name(): void
+    {
+        $eventArgs = $this->buildEventArgs('', $viewData = []);
+        $eventArgs->getRequest()->setParam('isXHR', true);
+
+        $this->session->set(Argument::cetera())->shouldNotBeCalled();
+
+        $this->subscriber->__invoke($eventArgs);
+    }
+
+    /** @test */
+    public function it_does_nothing_on_shipping_payment_non_xhr_request(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = []);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+
+        $this->session->set(Argument::cetera())->shouldNotBeCalled();
+
+        $this->subscriber->__invoke($eventArgs);
+    }
+
+    /** @test */
+    public function it_saves_in_session_the_stored_method_id_on_shipping_payment_xhr_request(): void
+    {
+        $eventArgs = $this->buildEventArgs('shippingPayment', $viewData = []);
+        $eventArgs->getRequest()->setParam('isXHR', true);
+        $eventArgs->getRequest()->setParam(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, $storedMethodId = '123123');
+
+        $this->session->set(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, $storedMethodId)->shouldBeCalled();
+
+        $this->subscriber->__invoke($eventArgs);
+    }
+
+    /** @test */
+    public function it_saves_in_session_the_stored_method_id_on_save_shipping_payment(): void
+    {
+        $eventArgs = $this->buildEventArgs('saveShippingPayment', $viewData = []);
+        $eventArgs->getRequest()->setParam('isXHR', false);
+        $eventArgs->getRequest()->setParam(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, $storedMethodId = '123123');
+
+        $this->session->set(AdyenPayment::SESSION_ADYEN_STORED_METHOD_ID, $storedMethodId)->shouldBeCalled();
+
+        $this->subscriber->__invoke($eventArgs);
+    }
+}
diff --git a/tests/Unit/Subscriber/EnrichUserPreferenceSubscriberTest.php b/tests/Unit/Subscriber/EnrichUserPreferenceSubscriberTest.php
new file mode 100644
index 00000000..654c2993
--- /dev/null
+++ b/tests/Unit/Subscriber/EnrichUserPreferenceSubscriberTest.php
@@ -0,0 +1,105 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber\Checkout;
+
+use AdyenPayment\Models\UserPreference;
+use AdyenPayment\Subscriber\EnrichUserPreferenceSubscriber;
+use AdyenPayment\Tests\Unit\Subscriber\SubscriberTestCase;
+use Doctrine\ORM\EntityRepository;
+use Enlight\Event\SubscriberInterface;
+use Enlight_Components_Session_Namespace;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+
+final class EnrichUserPreferenceSubscriberTest extends SubscriberTestCase
+{
+    use ProphecyTrait;
+    private EnrichUserPreferenceSubscriber $subscriber;
+
+    /** @var Enlight_Components_Session_Namespace|ObjectProphecy */
+    private $session;
+
+    /** @var EntityRepository|ObjectProphecy */
+    private $userPreferenceRepository;
+
+    protected function setUp(): void
+    {
+        $this->session = $this->prophesize(Enlight_Components_Session_Namespace::class);
+        $this->userPreferenceRepository = $this->prophesize(EntityRepository::class);
+        $this->subscriber = new EnrichUserPreferenceSubscriber(
+            $this->session->reveal(),
+            $this->userPreferenceRepository->reveal()
+        );
+    }
+
+    /** @test */
+    public function it_is_a_subscriber(): void
+    {
+        self::assertInstanceOf(SubscriberInterface::class, $this->subscriber);
+    }
+
+    /** @test */
+    public function it_subscribe_to_the_proper_events(): void
+    {
+        self::assertEquals(
+            [
+                // inject in the view as early as possible to get the info in the other subscribers
+                'Enlight_Controller_Action_PostDispatch_Frontend_Account' => ['__invoke', -99999],
+                'Enlight_Controller_Action_PostDispatch_Frontend_Checkout' => ['__invoke', -99999],
+            ],
+            EnrichUserPreferenceSubscriber::getSubscribedEvents()
+        );
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_user_id(): void
+    {
+        $this->session->get('sUserId')->willReturn(null);
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_does_nothing_on_missing_user_preference(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 1234);
+        $this->userPreferenceRepository->findOneBy(['userId' => $userId])->willReturn(null);
+
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        self::assertEquals($viewData, $eventArgs->getSubject()->View()->getAssign());
+    }
+
+    /** @test */
+    public function it_will_enrich_the_view_with_the_user_preference(): void
+    {
+        $this->session->get('sUserId')->willReturn($userId = 1234);
+
+        $userPreference = new UserPreference();
+        $userPreference->setId($id = 123123123);
+        $userPreference->setUserId($userId);
+        $userPreference->setStoredMethodId($storedMethodId = 'storedMethodId');
+        $this->userPreferenceRepository->findOneBy(['userId' => $userId])->willReturn($userPreference);
+
+        $eventArgs = $this->buildEventArgs('', $viewData = ['data' => 'view-data']);
+
+        $this->subscriber->__invoke($eventArgs);
+
+        $expected = [
+            'data' => 'view-data',
+            'adyenUserPreference' => [
+                'id' => $id,
+                'userId' => $userId,
+                'storedMethodId' => $storedMethodId,
+            ],
+        ];
+        self::assertEquals($expected, $eventArgs->getSubject()->View()->getAssign());
+    }
+}
diff --git a/tests/Unit/Subscriber/SubscriberTestCase.php b/tests/Unit/Subscriber/SubscriberTestCase.php
new file mode 100644
index 00000000..4a650e36
--- /dev/null
+++ b/tests/Unit/Subscriber/SubscriberTestCase.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace AdyenPayment\Tests\Unit\Subscriber;
+
+use AdyenPayment\Tests\Unit\Mock\ControllerActionMock;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Webmozart\Assert\Assert;
+
+abstract class SubscriberTestCase extends TestCase
+{
+    protected function buildEventArgs(
+        string $actionName,
+        array $viewData,
+        int $status = Response::HTTP_OK,
+        string $requestMethod = Request::METHOD_GET
+    ): \Enlight_Controller_ActionEventArgs {
+        $request = new \Enlight_Controller_Request_RequestTestCase();
+        $request->setActionName($actionName);
+        $request->setMethod($requestMethod);
+
+        return new \Enlight_Controller_ActionEventArgs([
+            'subject' => $this->buildSubject($viewData),
+            'request' => $request,
+            'response' => new \Enlight_Controller_Response_ResponseTestCase('', $status),
+        ]);
+    }
+
+    protected function buildSubject(array $viewData): \Enlight_Controller_Action
+    {
+        Assert::allString(array_keys($viewData));
+
+        $subject = new ControllerActionMock();
+        $subject->setView(new \Enlight_View_Default(new \Enlight_Template_Manager()));
+        $subject->View()->assign($viewData);
+
+        return $subject;
+    }
+}