feat(payments): Anbieter-Gerüst core/payments mit PayPal Orders v2
- ProviderInterface/AbstractProvider: Vorgang mit Uhrzeit+Session (payment_transactions), Mail und Warenkorb erst nach Zahlung, idempotentes markPaid, Betragsabgleich, shop_event + PG payment_log - Secrets verschluesselt (libsodium) in MySQL, Schluessel ausserhalb Docroot - Cashdesk: ?payment_return/?payment_cancel, Pending-Riegel, zahlung_erst_spaeter() - Backend Zahlart-Editor: Einstellungen speichern (war ohne Empfaenger), Secret-Felder - payment_webhook.php (Standalone), cli.php (init-key, secret, reconcile, status) - PayPal-v2-Blaupause vom Juli entfernt
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Zahlart-Adapter fuer den Cashdesk: payment_methods.module = 'paypal_v2'.
|
||||
*
|
||||
* Duenn mit Absicht. Der Cashdesk erwartet diese Klasse (ucfirst(module).'PaymentMethod')
|
||||
* mit set_data/before_order/during_order/after_order; alles Fachliche liegt im Provider
|
||||
* unter core/payments/providers/paypal_v2/. Ein weiterer Anbieter bekommt eine Kopie
|
||||
* dieser Datei mit anderem Modulnamen -- mehr nicht.
|
||||
*/
|
||||
require_once __DIR__ . '/../payments/Registry.php';
|
||||
|
||||
class Paypal_v2PaymentMethod
|
||||
{
|
||||
private const MODULE = 'paypal_v2';
|
||||
|
||||
private $base_object;
|
||||
private $layout_object;
|
||||
private $customer_data;
|
||||
private $gift_certificate = false;
|
||||
private $order_id = false;
|
||||
|
||||
public function __construct($base_object, $layout_object)
|
||||
{
|
||||
$this->base_object = $base_object;
|
||||
$this->layout_object = $layout_object;
|
||||
}
|
||||
|
||||
public function set_data($customer, $shipping, $billing, $shoppingcart, $delivery, $payment, $order_id = false)
|
||||
{
|
||||
$this->customer_data = $customer;
|
||||
$this->order_id = $order_id;
|
||||
}
|
||||
|
||||
public function set_gift_certificate($gift_certificate)
|
||||
{
|
||||
$this->gift_certificate = $gift_certificate;
|
||||
}
|
||||
|
||||
public function set_order_id($order_id)
|
||||
{
|
||||
$this->order_id = $order_id;
|
||||
}
|
||||
|
||||
/** Vor dem Speichern: Anbieter einsatzbereit? Sonst Fehlertext fuer die Kasse. */
|
||||
public function before_order($argument = null)
|
||||
{
|
||||
$provider = \Payments\Registry::get(self::MODULE, $this->base_object);
|
||||
if (!$provider) {
|
||||
return 'PayPal ist derzeit nicht verfuegbar. Bitte waehlen Sie eine andere Zahlart.';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function during_order($argument = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Nach dem Speichern: Zahlung anlegen, Kunde per 302 zum Anbieter.
|
||||
* Der POST bekommt damit einen Redirect als Antwort -- die Zurueck-Taste landet auf
|
||||
* dem Kassenformular, nicht auf einem erneut abschickbaren POST.
|
||||
*/
|
||||
public function after_order($order_total = false)
|
||||
{
|
||||
$amount = (float) $order_total;
|
||||
if ($this->gift_certificate && isset($this->gift_certificate->with_vat)) {
|
||||
$amount -= (float) $this->gift_certificate->with_vat;
|
||||
}
|
||||
$amount = \round($amount, 2);
|
||||
|
||||
try {
|
||||
$provider = \Payments\Registry::get(self::MODULE, $this->base_object);
|
||||
if (!$provider) {
|
||||
throw new \RuntimeException('Provider paypal_v2 nicht installiert');
|
||||
}
|
||||
$url = $provider->start((int) $this->order_id, $amount);
|
||||
} catch (\Throwable $e) {
|
||||
\error_log('PayPal v2 after_order (Order ' . $this->order_id . '): ' . $e->getMessage());
|
||||
if (\function_exists('shop_event')) {
|
||||
\shop_event('failed', [
|
||||
'order_id' => $this->order_id, 'betrag' => $amount,
|
||||
'customer_id' => $this->customer_data->id ?? null,
|
||||
'detail' => 'paypal_v2: start fehlgeschlagen -- ' . $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
$this->layout_object->assign('call', 'payment_error');
|
||||
$this->layout_object->assign('provider_label', 'PayPal');
|
||||
$this->layout_object->assign('message', 'PayPal konnte nicht gestartet werden. Ihr Warenkorb ist noch da – bitte versuchen Sie es erneut oder waehlen Sie eine andere Zahlart.');
|
||||
return $this->layout_object->_fetch('content_cash_desk/payment_result.tpl');
|
||||
}
|
||||
|
||||
if (!\headers_sent()) {
|
||||
\header('Location: ' . $url, true, 302);
|
||||
exit;
|
||||
}
|
||||
// Es wurde schon Output gesendet: Weiterleitungsseite mit Link + Meta-Refresh.
|
||||
$this->layout_object->assign('call', 'payment_open');
|
||||
$this->layout_object->assign('provider_label', 'PayPal');
|
||||
$this->layout_object->assign('payment_link', $url);
|
||||
return $this->layout_object->_fetch('content_cash_desk/payment_result.tpl');
|
||||
}
|
||||
|
||||
/** Rueckkehr laeuft ueber website_cashdesk::run() (?payment_return=paypal_v2), nicht hier. */
|
||||
public function change_order()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user