Zum Inhalt

Zahlungs-Plugins

Ein Zahlungs-Plugin definiert über den Knoten <PaymentMethod> in der info.xml eine oder mehrere Zahlungsmethoden, die dann über eine Zuordnung zu Versandarten im Onlineshop für Bezahlvorgänge genutzt werden können. Die grundsätzliche XML-Struktur einer Zahlungsmethode finden Sie im Abschnitt Zahlungsmethoden unter Die info.xml.

Grundlegendes

Jede Zahlungsmethode wird durch eine Payment-Klasse repräsentiert. Der Klassenname und die zugehörige Klassendatei werden in der info.xml mit den Knoten <ClassName> und <ClassFile> festgelegt. Die Klassendatei muss sich für eine erfolgreiche Validierung der Zahlungsmethode im Unterverzeichnis paymentmethod innerhalb des Plugin-Verzeichnisses befinden. Bezeichner für Klassenname und Klassendatei müssen der PSR-4-Spezifikation folgen. Jede Payment-Klasse muss ab Version 5.0 das Interface JTL\Plugin\Payment\MethodInterface implementieren oder die Klasse JTL\Plugin\Payment\Method erweitern. Über die Methoden der Payment-Klasse wird standardmäßig der komplette Zahlungsvorgang abgedeckt. Die Registrierung weiterer Hooks für den Zahlungsprozess ist normalerweise nur notwendig, wenn durch die Zahlungsmethode weitergehende Eingriffe in den Ablauf des Zahlungsvorganges oder des gesamten Bestellprozesses notwendig sind.

Implementation einer Payment-Klasse ab JTL-Shop Version 5.0

Hint

Im Weiteren wird von einer Implementation für JTL-Shop Version 5.2.x ausgegangen.

<?php declare(strict_types=1);

namespace Plugin\jtl_example_payment\paymentmethod;

use JTL\Plugin\Payment\Method;

/**
 * Class SimplePayment.
 */
class SimplePayment extends Method
{
    // ...
}

Die Basis-Payment-Klasse JTL\Plugin\Payment\Method implementiert das benötigte Interface und stellt alle grundlegenden Funktionen einer Zahlungsmethode zur Verfügung. Die eigene Payment-Klasse sollte deshalb immer diese Basis-Klasse erweitern. Eine einfache Zahlungsmethode, die lediglich Informationen für eine Banküberweisung versendet, muss damit lediglich die Methode preparePaymentProcess überschreiben.

<?php declare(strict_types=1);

namespace Plugin\jtl_example_payment\paymentmethod;

use JTL\Checkout\Bestellung;
use JTL\Mail\Mail\Mail;
use JTL\Mail\Mailer;
use JTL\Plugin\Helper as PluginHelper;
use JTL\Plugin\Payment\Method;
use JTL\Plugin\PluginInterface;
use JTL\Plugin\State;
use JTL\Session\Frontend;
use JTL\Shop;
use stdClass;

/**
* Class SimplePayment
* @package Plugin\jtl_example_payment\paymentmethod
*/
final class SimplePayment extends Method
{
   private const MAILTEMPLATE_SIMPLEPAYMENT = 'kPlugin_%d_SimplePaymentTransferData';

   /** @var PluginInterface|null */
   private ?PluginInterface $plugin;

   /**
    * @inheritDoc
    */
   public function init(int $nAgainCheckout = 0): self
   {
       parent::init($nAgainCheckout);

       $this->plugin = PluginHelper::getPluginById('jtl_example_payment');

       return $this;
   }

   /**
    * @inheritDoc
    */
   public function isValidIntern(array $args_arr = []): bool
   {
       return parent::isValidIntern($args_arr)
           && $this->plugin !== null
           && $this->plugin->getState() === State::ACTIVATED;
   }

   /**
    * @inheritDoc
    */
   public function preparePaymentProcess(Bestellung $order): void
   {
       parent::preparePaymentProcess($order);

       $obj              = new stdClass();
       $obj->tkunde      = Frontend::getCustomer();
       $obj->tbestellung = $order;
       $tplKey           = \sprintf(self::MAILTEMPLATE_SIMPLEPAYMENT, $this->plugin->getID());

       /** @var Mailer $mailer */
       $mailer = Shop::Container()->get(Mailer::class);
       $mailer->getHydrator()->add('Bestellung', $order);

       $mail = new Mail();
       try {
           $mailer->send($mail->createFromTemplateID($tplKey, $obj));
       } catch (\Exception) {
           Shop::Container()->getAlertService()->addError(
               __('Payment mail for Simple payment cant be send'),
               'simplePaymentCantSendMail'
           );
       }
   }
}

Die Methode preparePaymentProcess wird durch den Bestellabschluss nach Abschluss der Bestellung aufgerufen und startet den Bezahlvorgang der Zahlungsmethode. Im Beispiel wird das über die info.xml definierte E-Mail-Template für die Zahlungsmethode geladen und über den Mailer-Service von JTL-Shop versendet.

Zahlung vor Bestellabschluss

Im Modus "Zahlung vor Bestellabschluss" wird beim Abschluss des Bestellvorganges durch den Kunden, die Bestellung nicht in der Datenbank gespeichert! Die Bestellung existiert weiterhin nur in der aktuellen Kunden-Session. Die Zahlungsmethode muss bei erfolgreicher Zahlung über einen Aufruf von /includes/modules/notify.php dafür sorgen, dass der Kunde zum Bestellabschluss gelangt und die Bestellung festgeschrieben wird. Dies kann z. B. über eine URL-Weiterleitung erfolgen. Die dafür notwendige URL kann mittels getNotificationURL ermittelt werden. Im Fehlerfall kann der Kunde zurück in den Bestellprozess geleitet werden, um die Bezahlung ggf. zu wiederholen oder den Checkout mit einer anderen Zahlungsart fortsetzen zu können.

Warning

Bei Zahlungsmethoden, die eine zeitversetzte Bestätigung der Zahlung via Webhook versenden, kann es passieren, dass die Bestellung nicht mehr gespeichert werden kann, da diese aufgrund einer abgelaufenen Kunden-Session bereits verfallen ist. In diesem Fall existiert dann eine Zahlung, zu der es keine Bestellung gibt! Für solche Zahlungsmethoden sollte besser nur der Modus "Zahlung nach Bestellabschluss" gewählt werden.

Die "Zahlung vor Bestellabschluss" kann für die Zahlungsmethode über den XML-Parameter <PreOrder>1</PreOrder> voreingestellt werden. Dieser Wert lässt sich jedoch in den Einstellungen der Zahlungsmethode vom Betreiber des Onlineshops nachträglich ändern.

Zahlung nach Bestellabschluss

Im Modus "Zahlung nach Bestellabschluss" wird die Bestellung komplett abgeschlossen und in der Datenbank gespeichert, bevor der Bezahlvorgang gestartet wird. Die Zahlungsmethode muss hier dafür sorgen, dass bei erfolgreicher Zahlung die Bestellung per setOrderStatusToPaid auf den Status "bezahlt" gesetzt und mittels addIncomingPayment der Zahlungseingang gespeichert wird. Ein Zahlvorgang, der in diesen Modus läuft, kann normalerweise auch neu gestartet werden, falls Fehler aufgetreten sind. Die Zahlungsmethode sollte dies dann auch entsprechend signalisieren. Siehe hierzu auch canPayAgain Ein Rücksprung in den Bestellvorgang und die Auswahl einer anderen Zahlungsmethode durch den Kunden ist jedoch nicht möglich.

Die "Zahlung nach Bestellabschluss" kann für die Zahlungsmethode über den XML-Parameter <PreOrder>0</PreOrder> voreingestellt werden. Dieser Wert lässt sich jedoch in den Einstellungen der Zahlungsmethode vom Betreiber des Onlineshops nachträglich ändern.

Hint

Sollte die Zahlungsmethode nur einen der beiden Modi unterstützen, dann sollte bei geänderter Einstellung über HOOK_PLUGIN_SAVE_OPTIONS ein entsprechender Hinweis ausgegeben und die Zahlungsmethode über isValidIntern als "nicht verfügbar" markiert werden. Ein Beispiel dafür befindet sich in Programmierbeispiele im Zusammenhang mit Zahluns-Plugins.

Zahlung VOR vs. NACH Bestellaschluss

Diese Einstellung hat keinerlei Einfluss darauf, wie der Ablauf einer Zahlung im Frontend gestaltet wird. Dies obliegt dem Plugin. Der Shop-Core regelt darüber jedoch die Speicherung der Zahlung in der Datenbank. Jede Variante hat Vor- und Nachteile. Eine Zahlung vor Bestellabschluss kann z.B. abgebrochen und durch den Kunden eine komplett andere Zahlungsart gewählt werden, wenn z.B. beim Bezahlvorgang Fehler auftreten oder eine Zahlung anderweitig nicht möglich ist (z.B. Kreditkarte abgelaufen). Bei der Zahlung nach Bestellabschluss ist dies nicht mehr möglich. Zahlungsvorgänge die sehr lange dauern können (z.B. Ratenzahlung mit entsprechender Risiko- / Bonitätsprüfung) und den endgültigen Status erst per s.g. Webhook an den Shop übermitteln, lassen sich mit "Zahlung nach Bestellabschluss" einfacher implementieren. Hierbei wird zuerst die Bestellung in der DB gespeichert und es muss nicht mehr die Kunden-Session beachtet werden.

Beschreibung der Payment-Klasse JTL\Plugin\Payment\Method

public function init(): self

Wird bei jedem Instanziieren der Zahlungsmethode aufgerufen. In der Payment-Basisklasse werden die Properties caption und duringCheckout initialisiert. Als Rückgabewert wird die Klasseninstanz selbst erwartet. Diese Methode sollte überschrieben werden, wenn eigene Initialisierungen vorgenommen werden müssen. Z.B. können hier die notwendigen Sprachdateien des Plugins geladen werden, um eine saubere Trennung von Code und Sprache zu ermöglichen.

/**
 * @inheritDoc
 */
public function init(int $nAgainCheckout = 0): self
{
    parent::init($nAgainCheckout);

    $pluginID     = PluginHelper::getIDByModuleID($this->moduleID);
    $this->plugin = PluginHelper::getLoaderByPluginID($pluginID)->init($pluginID);
    Shop::Container()->getGetText()->loadPluginLocale(
        'simple_payment',
        $this->plugin
    );
    Shop::Smarty()->assign('pluginLocale', $this->plugin->getLocalization());

    return $this;
}

public function getOrderHash(): ?string

Der Shop-Core generiert beim Speichern einer Bestellung eine Unique ID und ordnet diese eindeutig einer Bestellung zu. Mittels der Methode getOrderHash kann diese Unique ID für eine bestehende Bestellung ermittelt werden. Diese Methode muss normalerweise nicht überschrieben werden.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    SimplePaymentAPI::Call('CapturePayment', $this->getOrderHash($order));
}

Hint

Bei der Übergabe von Referenzen auf eine Shop-Bestellung (z.B. mittels API-Call oder URL-Parameter) sollte immer diese Unique ID anstelle eines direkten Datenbank-Schlüssels verwendet werden, um Angreifern nicht die Möglichkeit zu bieten, durch z.B. einfaches Hochzählen die Kennung einer Bestellung zu "erraten".

public function getReturnURL(): string

Die Methode getReturnURL liefert eine absolute URL für die Abschlussseite der Bestellung bei erfolgreicher Zahlung und muss normalerweise nicht überschrieben werden. Dies ist standardmäßig eine Route auf /bestellabschluss?i=<OrderHash> oder (je nach Einstellung des Parameters "Abschluss-Seite nach externer Bezahlung") /status.php?uid==<cUID>. Für die Erzeugung des wird die Methode getOrderHash verwendet. Diese URL kann auch in einem "Zahlung nach Bestellabschluss"-Szenario als Return-URL für einen externen Zahlungsanbieter verwendet werden. Diese URL ist immer eine absolute Shop-URL.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    $result = SimplePaymentAPI::Call('CapturePayment', $this->getOrderHash($order));
    if ($result === 'OK') {
        header('Location: ' . $this->getReturnURL($order), true, 303);
        exit();
    }
}

public function getNotificationURL(): string

Mittels getNotificationURL kann die URL zum Shop ermittelt werden, die bspw. bei einer externen Weiterleitung zu einem Zahlungsanbieter als Return-URL übergeben wird. Diese URL sollte in einem "Zahlung vor Bestellabschluss"-Szenario als Return-URL verwendet werden. Der übergebene Parameter für den Session-Hash kann dabei über generateHash ermittelt werden. Diese Methode sollte überschrieben werden, wenn für die Rückkehr vom Zahlungsanbieter ein eigener Einstiegspunkt vom Plugin verwendet wird. Diese URL ist immer eine absolute Shop-URL.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    SimplePaymentAPI::Call('InitPayment', (object)[
        'PaymentAction'    => 'Sale',
        'ButtonSource'     => 'Cart',
        'ItemTotal'        => $order->fGesamtsumme,
        'ReturnURL'        => $this->getNotificationURL($this->generateHash($order))
    ]);
}

public function updateNotificationID(): self

Diese Methode kann verwendet werden, um die Notify-ID einer Zahlungssession für eine bestehende Bestellung mit einem eigenen Wert zu überschreiben. Dies ist normalerweise nicht notwendig. Diese Funktion wird nicht vom Shop-Core aufgerufen und muss normalerweise nicht überschrieben werden.

public function getShopTitle(): string

Liefert den Namen des Onlineshops, der ggf. an einen Payment-Provider übergeben wird. In der Payment-Basisklasse wird hier der Name des Onlineshops aus der Konfiguration ermittelt. Diese Methode muss normalerweise nicht überschrieben werden.

public function preparePaymentProcess(): void

Die Methode preparePaymentProcess wird durch den Bestellabschluss nach Finalisierung der Bestellung aufgerufen und startet den Bezahlvorgang der Zahlungsmethode. Je nachdem, ob die Zahlungsmethode im Modus "Zahlung vor Bestellabschluss" oder "Zahlung nach Bestellabschluss" ausgeführt wird, ist zum Zeitpunkt des Aufrufs die zugrundeliegende Bestellung bereits in der Tabelle tbestellung persistiert oder sie existiert nur innerhalb der aktiven Kunden-Session.

Hint

Im Modus "Zahlung vor Bestellabschluss" muss diese Methode dafür sorgen, dass mittels Aufruf von /includes/modules/notify.php der Bestellabschluss ausgeführt und damit die Bestellung festgeschrieben wird. Die URL für diesen Aufruf kann über getNotificationURL ermittelt werden.

Die Payment-Basisklasse definiert diese Methode ohne Funktionalität, so dass diese in jedem Fall überschrieben werden muss!

Beispiel für eine Implementation im Modus "Zahlung nach Bestellabschluss".

/**
 * @inheritDoc
 */
public function preparePaymentProcess($order): void
{
    parent::preparePaymentProcess($order);

    $credentials     = Frontend::get(self::USERCREDENTIALS, []);
    $serviceProvider = new ServiceProvider($this->getSetting('prepaid_card_provider_url'));
    try {
        $payValue = $order->fGesamtsumme;

        if ($payValue <= 0) {
            $this->setOrderStatusToPaid($order);

            return;
        }

        $hash    = $this->generateHash($order);
        $payment = $serviceProvider->payPrepaidTransaction(
            'PrepaidPayment: '.$hash,
            $this->getSetting('prepaid_card_merchant_login'),
            $this->getSetting('prepaid_card_merchant_secret'),
            $credentials['token'],
            '',
            $payValue
        );

        $payStatus = $payment->paymentValue >= $payValue
            ? self::PAYSTATUS_SUCCESS
            : self::PAYSTATUS_PARTIAL;

        $this->deletePaymentHash($hash);
        $this->addIncomingPayment($order, (object)[
            'fBetrag'  => $payment->payment_value,
            'cZahler'  => $credentials['name'],
            'cHinweis' => $payment->payment_key,
        ]);
        if ($payStatus === self::PAYSTATUS_SUCCESS) {
            $this->setOrderStatusToPaid($order);
        }
    } catch (ServiceProviderException $e) {
        Shop::Container()->getAlertService()->addError(
            $e->getMessage(),
            'paymentFailed'
        );
    }
}

public function sendErrorMail(): self

Diese Methode ist eine Debug-Funktion und kann verwendet werden, um im Falle eines Fehlers eine E-Mail direkt an den Shop-Betreiber zu versenden. Diese Funktion wird nicht vom Shop-Core aufgerufen und muss normalerweise nicht überschrieben werden.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    $result = SimplePaymentAPI::Call('CapturePayment', $this->getOrderHash($order));
    if ($result === 'OK') {
        header('Location: ' . $this->getReturnURL($order), true, 303);
    } elseif ($result === 'API CALL DENIED') {
        $this->sendErrorMail('API CALL DENIED: ' . SimplePaymentAPI::getAPIState());
    }
}

public function generateHash(): string

Die Methode generateHash erzeugt zu einer Bestellung eine Unique ID. Je nach Szenario (Zahlung vor / nach Bestellabschluss) ist der erzeugte Hash unterschiedlich zu interpretieren.

  • Ist die übergebene Bestellung eine persistierte Bestellung (kBestellung > 0), wird die dieser Bestellung zugeordnete Unique ID zurückgeliefert (siehe: public function getOrderHash(): ?string).
  • Ist die übergebene Bestellung noch nicht in der Datenbank gespeichert (kein Primärschlüssel gesetzt), so wird eine neue Unique ID erzeugt und diese an die aktuelle Kunden-Session gebunden.

Zur Unterscheidung zwischen Bestellung- und Session-Key, wird der Session-Key mit einem führenden Unterstrich (_) versehen. Diese Methode kann für die Verwendung eines eigenen Key-Handlings überschrieben werden.

public function deletePaymentHash(): self

Mit Hilfe dieser Methode wird die Zuordnung einer Bestellung zu einer Unique ID (siehe: public function getOrderHash(): ?string) gelöscht. Diese Methode wird nicht vom Shop-Core aufgerufen und muss normalerweise nicht überschrieben werden. Ein Aufruf dieser Methode ist z.B. dann sinnvoll, wenn die Verbindung einer gespeicherten Bestellung zu einer Unique ID, die für API-Calls oder URLs verwendet wurde, aus Sicherheitsgründen dauerhaft zu löschen.

Warning

Eine gelöschte Beziehung einer Bestellung zu einer Unique ID kann nicht rückgängig gemacht werden.

public function addIncomingPayment(): self

Über addIncomingPayment wird ein Zahlungseingang angelegt. Die Methode der Payment-Basisklasse legt dazu in der Tabelle tzahlungseingang einen entsprechenden Eintrag an. Diese Methode muss normalerweise nicht überschrieben werden.

public function setOrderStatusToPaid(): self

Mit setOrderStatusToPaid wird die übergebene Bestellung in den Status "bezahlt" versetzt. Die Methode der Payment-Basisklasse führt dazu ein Update der Tabelle tbestellung durch. Diese Methode muss normalerweise nicht überschrieben werden.

public function sendConfirmationMail(): self

Ein Aufruf von sendConfirmationMail der Payment-Basisklasse versendet über die Methode sendMail die Standard-E-Mail für "Bestellung bezahlt". Diese Methode muss normalerweise nicht überschrieben werden.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    $payment = SimplePaymentAPI::Call('CapturePayment', $this->getOrderHash($order));
    if ($payment->result === 'OK') {
        $this->addIncomingPayment($order, (object)[
            'fBetrag'          => $payment->amount,
            'fZahlungsgebuehr' => $payment->fee,
            'cHinweis'         => $payment->additionalText,
        ]);
        $this->setOrderStatusToPaid($order);
        $this->sendConfirmationMail($order);
        header('Location: ' . $this->getReturnURL($order), true, 303);
    }
}

public function handleNotification(): void

Die Methode handleNotification wird aufgerufen, wenn /includes/modules/notify.php über die Notification-URL (siehe public function getNotificationURL(): string) ausgeführt wird. In einem "Zahlung vor Bestellabschluss"-Szenario erfolgt der Aufruf, nachdem der Shop die Bestellung angelegt hat. Über den Parameter $args besteht hiermit die Möglichkeit für die Zahlungsart auf Rückgaben des Zahlungs-Providers zu reagieren.

/**
 * @inheritDoc
 */
public function handleNotification(Bestellung $order, string $hash, array $args): void
{
    parent::handleNotification($order, $hash, $args);

    if (isset($args['additionalPaymentInformation'])) {
        $this->sendMail(
            $order->kBestellung,
            'kPlugin_' . $this->plugin->getID() . '_paymentinformation',
            $args['additionalPaymentInformation']
        );
    }
}

public function finalizeOrder(): bool

Diese Methode wird aufgerufen, unmittelbar bevor in einem "Zahlung vor Bestellabschluss"-Szenario die Bestellung durch den Shop angelegt wurde. Der Zahlungsart wird dadurch die Möglichkeit gegeben abschließende Prüfungen vorzunehmen und über den Rückgabewert der Funktion Einfluss auf die Persistierung der Bestellung zu nehmen. Die Basis-Klasse implementiert diese Funktion mit einem Rückgabewert false, so dass diese Methode für eine "Zahlung vor Bestellabschluss" überschrieben werden muss. In einem "Zahlung nach Bestellabschluss"-Szenario wird diese Funktion nicht verwendet,

/**
 * @inheritDoc
 */
public function finalizeOrder(Bestellung $order, string $hash, array $args): bool
{
    if (isset($args['paymentresult']) && $args['paymentresult'] === 'DECLINED') {
        // do something to inform customer
        ...
        return false;
    }

    return true;
}

public function redirectOnCancel(): bool

Diese Methode wird nur in einem "Zahlung vor Bestellabschluss"-Szenario verwendet und muss entsprechend überschrieben werden. redirectOnCancel wird aufgerufen, wenn public function finalizeOrder(): bool false zurückliefert und dadurch die Bestellung nicht gespeichert wurde. Um eine korrekte Funktionalität und vor allem Anzeige zu gewährleisten, muss diese Funktion entweder

  • true zurückgeben, dann wird automatisch auf die Zahlungsart-Auswahlseite weitergeleitet
  • false zurückgeben und das über die info.xml defnierte Template initialisieren (Smarty-Variablen befüllen)
  • oder selbst einen passenden Redirect ausführen.

public function redirectOnPaymentSuccess(): bool

Diese Methode wird nur in einem "Zahlung vor Bestellabschluss"-Szenario verwendet und muss entsprechend überschrieben werden. redirectOnPaymentSuccess wird aufgerufen, wenn public function finalizeOrder(): bool true zurückgeliefert hat und die Bestellung vom Shop gespeichert wurde. Um eine korrekte Funktionalität und vor allem Anzeige zu gewährleisten, muss diese Funktion entweder

  • true zurückgeben, dann wird automatisch auf die von public function getReturnURL(): string zurückgelieferte URL weitergeleitet
  • false zurückgeben und das über die info.xml defnierte Template initialisieren (Smarty-Variablen befüllen)
  • selbst einen passenden Redirect ausführen.

public function doLog(): self

Die Methode doLog schreibt einen Eintrag ins Zahlungslog der Zahlungsart.

/**
 * @inheritDoc
 */
public function preparePaymentProcess(Bestellung $order): void
{
    parent::preparePaymentProcess($order);
    ...
    $payment = SimplePaymentAPI::Call('CapturePayment', $this->getOrderHash($order));
    if ($payment->result === 'OK') {
        ...
    } else {
        $this->doLog('API error: ' . $payment->result, \LOGLEVEL_ERROR);
    }
}

public function getCustomerOrderCount(): int

Mit dieser Methode der Payment-Basisklasse wird zu einem bestehenden Kunden die Anzahl an Bestellungen ermittelt, die "in Bearbeitung", "bezahlt" oder "versandt" sind. Diese Methode muss normalerweise nicht überschrieben werden.

public function loadSettings(): self

Diese Methode initialisiert die Eigenschaft $this->paymentConfig mit der aktuellen Konfiguration der Standard-Zahlungsarten. loadSettings muss explizit einmalig (z.B. in public function init(): self aufgerufen werden, wenn über $this->paymentConfig auf die Konfiguration der Standard-Zahlungsarten zugegriffen werden soll. $this->paymentConfig enthält nach dem Aufruf ein Array in der Form:

array (
  'zahlungsart_nachnahme_min_bestellungen' => 0,
  'zahlungsart_nachnahme_min' => '0',
  'zahlungsart_nachnahme_max' => '0',
  'zahlungsart_lastschrift_min_bestellungen' => 0,
  'zahlungsart_lastschrift_min' => '0',
  'zahlungsart_lastschrift_max' => '0',
  'zahlungsart_lastschrift_bic_abfrage' => 'O',
  'zahlungsart_lastschrift_kontoinhaber_abfrage' => 'Y',
  'zahlungsart_lastschrift_kreditinstitut_abfrage' => 'O',
  'zahlungsart_rechnung_min_bestellungen' => 0,
  'zahlungsart_rechnung_min' => '10',
  'zahlungsart_rechnung_max' => '0',
  'zahlungsart_ueberweisung_min_bestellungen' => 0,
  'zahlungsart_ueberweisung_min' => '0',
  'zahlungsart_ueberweisung_max' => '0',
  'zahlungsart_barzahlung_min_bestellungen' => 0,
  'zahlungsart_barzahlung_min' => '0',
  'zahlungsart_barzahlung_max' => '0',
)

Die Methode muss normalerweise nicht überschrieben werden.

/**
 * @inheritDoc
 */
public function init(int $nAgainCheckout = 0): self
{
    parent::init($nAgainCheckout);
    ...
    $this->loadSettings();

    return $this;
}

public function getSetting(): mixed

Mit Hilfe von getSetting kann eine gespeicherte Einstellung zur Zahlungsart ermittelt werden. Diese Methode kann für den Zugriff auf eine eigene Konfiguration überschrieben werden.

/**
 * @inheritDoc
 */
public function isValid(object $customer, Cart $cart): bool
{
    return (int)$this->getSetting('min_bestellungen') >= $this->getCustomerOrderCount($customer->kKunde);
}

public function isValid(): bool

Diese Methode gibt die Validität der Zahlungsmethode im aktuellen Zahlvorgang - also abhängig von Kunde und / oder Warenkorb - an. Bei Rückgabe von false wird die Zahlungsmethode im Bestellprozess nicht angeboten bzw. als ungültig zurückgewiesen. Der Rückgabewert true zeigt dagegen an, dass die Zahlungsart verwendet werden kann. In der Payment-Basisklasse wird hier das Ergebnis von isValidIntern und zusätzlich die Erfüllung der Bedingungen für die Mindestanzahl an Bestellungen durch den Kunden sowie der Mindestbestellwert im aktuellen Warenkorb geprüft. Diese Methode muss nur überschrieben werden, wenn eigene kunden- und warenkorbabhängige Bedingungen geprüft werden müssen.

/**
 * @inheritDoc
 */
public function isValid(object $customer, Cart $cart): bool
{
    return parent::isValid($customer, $cart) && !$this->isBlacklisted($customer->cMail);
}

public function isValidIntern(): bool

Mit dieser Methode wird die grundsätzliche (interne) Validität der Zahlungsmethode geprüft. Ein Rückgabewert true signalisiert hierbei, dass die Zahlungsmethode gültig ist und verwendet werden kann. Bei Rückgabe von false wird die Zahlungsmethode als ungültig angesehen und im Bestellprozess nicht zur Auswahl angezeigt. Im Gegensatz zu isValid erfolgt die Prüfung unabhängig vom aktuellen Zahlvorgang. Die Implementation der Payment-Basisklasse liefert immer true. Diese Methode muss also überschrieben werden, wenn die Zahlungsmethode aufgrund "interner" Gründe wie fehlender oder fehlerhafter Konfiguration nicht verfügbar ist.

/**
 * @inheritDoc
 */
public function isValidIntern($args_arr = []): bool
{
    if (empty($this->getSetting('postpaid_card_provider_url'))
        || empty($this->getSetting('postpaid_card_login_url'))
        || empty($this->getSetting('postpaid_card_merchant_login'))
        || empty($this->getSetting('postpaid_card_merchant_secret'))
    ) {
        $this->state = self::STATE_NOT_CONFIGURED;

        return false;
    }

    return parent::isValidIntern($args_arr);
}

public function isSelectable(): bool

Mit isSelectable steht eine Möglichkeit zur Verfügung, die Zahlungsmethode im Bestellprozess auszublenden. Im Unterschied zu isValid und isValidIntern wird diese Methode für reine Frontend-Bedingungen genutzt. Dies ist z. B. dann der Fall, wenn eine grundsätzlich zulässige Zahlungsmethode nicht in der Liste zur Auswahl der Versand- und Zahlungsart aufgeführt werden soll, weil diese nur für einen Expresskauf-Button oder für ein direktes Bezahlen am Artikel oder aus dem Warenkorb heraus genutzt wird. In der Payment-Basisklasse liefert diese Methode immer das Ergebnis von isValid.

/**
 * @inheritDoc
 */
public function isSelectable(): bool
{
    return parent::isSelectable() && !$this->isExpressPaymentOnly();
}

Note

Die Methoden isValidIntern(), isValid() und isSelectable() bedingen einander. Dabei hat isValidIntern() die höchste und isSelectable() die geringste Wertigkeit. Eine Zahlungsmethode, die über isValidIntern() false liefert, ist auch nicht valide und auch nicht auswählbar. Eine nicht auswählbare Zahlungsmethode kann aber durchaus valide sein. Durch den Aufruf der geerbten Methoden aus der Payment-Basisklasse kann diese Abhängigkeit einfach sichergestellt werden.

public function handleAdditional(): bool

Wird im Bestellprozess aufgerufen, um zu prüfen, ob der Zusatzschritt im Bestellprozess angezeigt werden soll. Ist der Zwischenschritt aus Plugin-Sicht notwendig, muss false zurückgegeben werden. Dies kann z. B. genutzt werden, um zusätzliche, für die Zahlungsart relevante Daten wie Kreditkartendaten vom Kunden abzufragen. Sind diese Daten z. B. bereits in der Kunden-Session vorhanden, kann der Schritt mit Rückgabe von true übersprungen werden. In der Payment-Basisklasse liefert diese Methode immer true und muss deshalb nur überschrieben werden, wenn ein eigener Zwischenschritt (siehe: ) vorhanden ist.

/**
 * @inheritDoc
 */
public function handleAdditional($post): bool
{
    $credentials = Frontend::get(self::USERCREDENTIALS, []);

    if (empty($credentials['name']) || empty($credentials['token'])) {
        Shop::Smarty()
            ->assign('credentials_loginName', empty($credentials['name'])
                ? Frontend::getCustomer()->cMail
                : $credentials['name'])
            ->assign('credentials_secret', '')
            ->assign('additionalNeeded', true);

        return false;
    }

    return parent::handleAdditional($post);
}

public function validateAdditional(): bool

Diese Methode wird im Bestellprozess aufgerufen und entscheidet im Zusammenspiel mit handleAdditional, ob das Zusatzschritt-Template (siehe: ) nach der Auswahl der Zahlungsart angezeigt werden muss. Können die Daten aus dem Zwischenschritt nicht validiert werden, wird false zurückgegeben, ansonsten true.

/**
 * @inheritDoc
 */
public function validateAdditional(): bool
{
    $credentials     = Frontend::get(self::USERCREDENTIALS, []);
    $postCredentials = Request::postVar('credentials', []);

    if (Request::getInt('editZahlungsart') > 0 || Request::getInt('editVersandart') > 0) {
        $this->resetToken();

        return false;
    }

    if (isset($postCredentials['post'])) {
        if (!Form::validateToken()) {
            Shop::Container()->getAlertService()->addAlert(
                Alert::TYPE_ERROR,
                Shop::Lang()->get('invalidToken'),
                'invalidToken'
            );

            return false;
        }

        $secret               = StringHandler::filterXSS($postCredentials['secret']);
        $credentials['name']  = StringHandler::filterXSS($postCredentials['loginName']);
        $credentials['token'] = $this->validateCredentials($credentials['name'], $secret);

        Frontend::set(self::USERCREDENTIALS, $credentials);

        return !empty($credentials['token']);
    }

    if (!empty($credentials['token'])) {
        return parent::validateAdditional();
    }

    return false;
}

public function addCache(): self

Mit addCache wird ein Key-Value-Pair zwischengespeichert. Die Payment-Basisklasse benutzt für die Methoden addCache, unsetCache und getCache die aktuelle Kunden-Session als Zwischenspeicher. Diese Methode muss überschrieben werden, wenn eine andere Cache-Methode verwendet werden soll.

public function unsetCache(): self

Mit unsetCache wird ein Key-Value-Pair aus dem Zwischenspeicher entfernt. Die Payment-Basisklasse benutzt für die Methoden addCache, unsetCache und getCache die aktuelle Kunden-Session als Zwischenspeicher. Diese Methode muss überschrieben werden, wenn eine andere Cache-Methode verwendet werden soll.

public function getCache(): mixed

Mit getCache wird ein Key-Value-Pair aus dem Zwischenspeicher gelesen. Die Payment-Basisklasse benutzt für die Methoden addCache, unsetCache und getCache die aktuelle Kunden-Session als Zwischenspeicher. Diese Methode muss überschrieben werden, wenn eine andere Cache-Methode verwendet werden soll.

public function createInvoice(): object

Die Methode createInvoice wird beim Abgleich mit JTL-Wawi aufgerufen, wenn für eine Bestellung eine Rechnung erzeugt wird. Über den Rückgabewert kann die Zahlungsart hier Informationen bereitstellen, die als zusätzlicher Text auf der Rechnung ausgegeben werden. Diese Methode muss überschrieben werden, wenn Rechnungsdaten an die Wawi übergeben werden sollen. Zurückgegeben werden muss ein Objekt mit den Eigenschaften nType und cInfo. nType ist ein Integer mit den möglichen Werten:

  • 0 - Funktion nicht unterstützt
  • 1 - Rechnungstext vorhanden

In cInfo wird die zusätzliche Rechnungsinformation als String übergeben.

/**
 * @inheritDoc
 */
public function createInvoice(int $orderID, int $languageID): object
{
    $result = parent::createInvoice($orderID, $languageID);

    $additionInfo = $this->getAdditionalOrderInfo($orderID);
    if ($additionInfo !== null) {
        $result->nType = 1;
        $result->cInfo = $additionInfo;
    }

    return $result;
}

public function reactivateOrder(): self

Die Methode createInvoice wird beim Abgleich mit JTL-Wawi aufgerufen, wenn eine Bestellung reaktiviert (Storno rückgängig) wird. Die Zahlungsart hat hiermit die Möglichkeit, Änderungen an der Zahlung vorzunehmen oder eigene Status anzupassen. Die Methode der Basis-Klasse versendet über sendMail die "Bestellung reaktiviert"-E-Mail und setzt den Status der Bestellung wieder auf "In Bearbeitung". Für weitergehnde Funktionen muss die Methode überschrieben werden. Das kann z.B. die Reaktivierung / Neuauslösung der Zahlung beim Payment-Provider sein.

/**
 * @inheritDoc
 */
public function reactivateOrder(int $orderID): self
{
    parent::reactivateOrder($orderID);

    SimplePaymentAPI::Call('RecoverPayment', $this->getOrderHash(new Bestellung($orderID)));

    return $this;
}

public function cancelOrder(): self

Diese Methode wird vom JTL-Shop-Core bei der Synchronisation mit JTL-Wawi aufgerufen, wenn eine Bestellung storniert wurde. Die Payment-Basisklasse setzt den Status der zugeordneten Bestellung auf "storniert" und versendet über sendMail die "Bestellung storniert"-E-Mail. Diese Methode muss überschrieben werden, wenn weitergehende Operationen notwendig sind. Das kann z. B. die Stornierung der Zahlung beim Payment-Provider sein.

/**
 * @inheritDoc
 */
public function cancelOrder(int $orderID, bool $delete = false): self
{
    parent::cancelOrder($orderID, $delete);

    SimplePaymentAPI::Call('CancelPayment', $this->getOrderHash(new Bestellung($orderID)));

    return $this;
}

public function canPayAgain(): bool

Hier wird festgelegt, ob die Bezahlung über das Plugin erneut gestartet werden kann. Gibt diese Methode true zurück, dann wird bei einer unbezahlten Bestellung im Kundenaccount ein "Jetzt bezahlen"-Link angezeigt. Wird dieser Link angeklickt, dann wird der Bezahlvorgang neu gestartet. Die Init-Methode für die Zahlungsmethode wird dann mit dem Parameter $nAgainCheckout = 1 aufgerufen. Die Methode der Payment-Basisklasse liefert immer false und muss überschrieben werden, wenn die Zahlungsmethode einen erneuten Zahlungsvorgang unterstützt.

public function sendMail(): self

Die sendMail-Methode der Payment-Basisklasse unterstützt die E-Mail-Templates für "Bestellbestätigung", "Bestellung teilversandt", "Bestellung aktualisiert", "Bestellung versandt", "Bestellung bezahlt", "Bestellung storniert" und "Bestellung reaktiviert" mit dem $type-Parameter. Für die unterstützten Templates werden die notwendigen Daten ermittelt und die jeweilige E-Mail versendet. Diese Methode muss überschrieben werden, wenn weitere oder eigene E-Mail-Templates unterstützt werden sollen.

/**
 * @inheritDoc
 */
public function sendMail(int $orderID, string $type, $additional = null): self
{
    if (\str_starts_with($type, 'kPlugin_' . $this->plugin->getID())) {
        $order = new Bestellung($orderID);
        $order->fuelleBestellung(false);
        $data   = (object)[
            'tkunde'      => new Customer($order->kKunde),
            'tbestellung' => $order,
        ];
        $mailer = Shop::Container()->get(Mailer::class);
        $mail   = new Mail();
        $mailer->send($mail->createFromTemplateID($type, $data));

        return $this;
    }

    return parent::sendMail($orderID, $type, $additional);
}

Weitere Informationen zu Zahlungs-Plugins