Zahlungsart-Konfiguration und Verfügbarkeitsprüfung: canUseCheckout und isAvailable
Zahlungsart-Konfiguration und Verfügbarkeitsprüfung: canUseCheckout und isAvailable
~7 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Kapitel 62 hat can_use_checkout und can_use_internal bereits als statische Config-Werte gesetzt, Kapitel 63 hat begründet, in welchem einen Fall die Zahlungsart überhaupt sichtbar werden soll. Dieses Kapitel verbindet beides: die Capability-Flags im Detail, und die individuelle isAvailable()-Logik, die diese eine Bedingung tatsächlich prüft.
canUseCheckout und canUseInternal im Detail
MethodInterface kennt eine ganze Reihe von can*-Fähigkeiten - canCapture, canRefund, canVoid und mehr, für ein rein rabattbasiertes Verfahren ohne echtes Gateway größtenteils irrelevant. Zwei davon sind hier bewusst gesetzt: can_use_checkout = 1, weil die Methode ausschließlich im Storefront-Checkout Sinn ergibt, und can_use_internal = 0, weil Admin-Bestellungsanlage (Sales > Orders > Create New Order) keinen vergleichbaren, eingeloggten Kundenkontext kennt wie der Storefront-Checkout - CustomerSession existiert dort schlicht nicht in derselben Form. Der Adapter aus Kapitel 62 liest beide Werte automatisch über den default-Handler des ValueHandlerPools aus payment/mironsoft_loyalty_points/can_use_checkout bzw. .../can_use_internal - keine einzige Zeile PHP nötig, reine Konfiguration.
isAvailable() und der individuelle AvailabilityHandler
isAvailable() ist etwas anderes als can_use_checkout: Es fragt nicht "darf diese Methode grundsätzlich im Checkout auftauchen", sondern "soll sie es JETZT, für DIESEN Warenkorb" - dynamisch, pro Request. Der default-Handler kann das nicht beantworten, er liest nur statische Config-Werte. Genau dafür registriert ValueHandlerPool einen zweiten, eigenen Handler unter dem Feldnamen availability:
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Model\Payment;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Payment\Gateway\Config\ValueHandlerInterface;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Store\Model\ScopeInterface;
use Mironsoft\Loyalty\Model\Config\LoyaltyConfig;
/**
* Decides whether "Mit Treuepunkten bezahlt" may be offered for the current
* quote. Unlike the static can_use_checkout/can_use_internal flags from
* chapter 62's config.xml, availability depends on live, per-quote data - the
* default ConfigValueHandler cannot answer this, hence a dedicated handler.
*/
class AvailabilityHandler implements ValueHandlerInterface
{
/**
* Config path for the method's own active flag.
*/
private const string XML_PATH_ACTIVE = 'payment/mironsoft_loyalty_points/active';
/**
* @param ScopeConfigInterface $scopeConfig Store-scoped configuration reader
* @param LoyaltyConfig $loyaltyConfig Loyalty module configuration reader
*/
public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
private readonly LoyaltyConfig $loyaltyConfig,
) {
}
/**
* Returns true only if the method is enabled, a customer is logged in, and
* the points already applied to the quote (chapter 63) already cover the
* quote's full grand total - the one edge case chapter 63 identified where
* the points payment method itself, rather than a discount, is used.
*
* @param array $subject Handler subject, contains the "quote" key for availability checks
* @param int|null $storeId Store ID for scoped config reads
* @return bool
*/
public function handle(array $subject, $storeId = null): bool
{
$quote = $subject['quote'] ?? null;
if (!$quote instanceof CartInterface || !$quote->getCustomerId()) {
return false;
}
$isActive = (bool) $this->scopeConfig->getValue(
self::XML_PATH_ACTIVE,
ScopeInterface::SCOPE_STORE,
$storeId
);
if (!$isActive) {
return false;
}
$pointsToRedeem = (int) $quote->getData('loyalty_points_to_redeem');
if ($pointsToRedeem <= 0) {
return false;
}
$coveredAmount = $pointsToRedeem / $this->loyaltyConfig->getPointsPerEuro();
return $coveredAmount >= (float) $quote->getData('grand_total');
}
}
etc/di.xml aus Kapitel 62 bekommt dafür genau eine Ergänzung im bereits bestehenden ValueHandlerPool-virtualType:
<virtualType name="Mironsoft\Loyalty\Model\Payment\ValueHandlerPool"
type="Magento\Payment\Gateway\Config\ValueHandlerPool">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="default" xsi:type="string">Magento\Payment\Gateway\Config\ConfigValueHandler</item>
<item name="availability" xsi:type="string">Mironsoft\Loyalty\Model\Payment\AvailabilityHandler</item>
</argument>
</arguments>
</virtualType>
<!-- rest of etc/di.xml unchanged, see chapter 62 -->
Achtung: AvailabilityHandler::handle() darf niemals eine Exception werfen. Der ValueHandlerPool fängt hier nichts ab - eine unbehandelte Exception beim Rendern der Zahlungsart-Liste würde den GESAMTEN Payment-Step abstürzen lassen, auch für Kunden, die die Punkte-Zahlungsart gar nicht nutzen wollen. Kapitel 70 nimmt diesen Fehler noch einmal konkret auf.
Die Adminkonfiguration
Damit active, title und sort_order überhaupt im Admin unter Stores > Configuration > Sales > Payment Methods editierbar sind, braucht die bestehende payment-Sektion eine neue Gruppe:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="payment">
<group id="mironsoft_loyalty_points" translate="label" type="text"
sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Mironsoft Loyalty - Punkte einlösen</label>
<field id="active" translate="label" type="select" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Aktiviert</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="title" translate="label" type="text" sortOrder="20"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Titel im Checkout</label>
</field>
<field id="sort_order" translate="label" type="text" sortOrder="30"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Sortierreihenfolge</label>
</field>
</group>
</section>
</system>
</config>
Tipp: Nach jeder Änderung an system.xml oder config.xml reicht bin/cache-clean config - dieselbe Faustregel wie bei widget.xml (Kapitel 55) oder crontab.xml (Kapitel 32). Ein volles setup:upgrade ist hier nicht nötig, solange keine Datenbanktabelle betroffen ist; für die neue quote-Spalte und den Data Patch aus Kapitel 63 dagegen schon.
Konfiguriert, verfügbarkeitsgeprüft - was jetzt noch fehlt, ist die Sichtbarkeit im Checkout selbst. Kapitel 65 registriert dafür die Frontend-Komponente.