En vous promenant sur Beamreactor, nous stockons votre IP 48h pour des raisons de sécurité.

Lecteur Markdown

payment Documentation › PAYMENT_DOCUMENTATION_DE

Payment Documentation De

Plugin: Payment #

Zahlungs-Rückkehrseite (Erfolg, Abbruch, Wartend). Empfängt den Benutzer nach dem Checkout über Stripe / PayPal / Überweisung / Scheck, prüft den Status, zeigt die Bestätigung an und versendet die Zusammenfassungs-E-Mail.

Rolle

Das payment-Plugin initiiert keine Zahlung — es empfängt die Rückläufer:

| Provider | Rückkehr-URL | Aktion |

|----------|--------------|--------|

| Stripe | ?obj=payment.php&order=NUM&session_id=cs_... | Synchrone Sitzungsprüfung, markiert die Bestellung als paid, wenn Stripe bestätigt |

| PayPal | ?obj=payment.php&order=NUM&token=... | Zahlungsabbuchung über Payment::handleCallback('paypal', …) |

| Überweisung | ?obj=payment.php&order=NUM | Zeigt IBAN/BIC + Bestellreferenz |

| Scheck | ?obj=payment.php&order=NUM | Zeigt Postanschrift + Empfänger |

| Abbruch | ?obj=payment.php&action=cancel&order=NUM | Stellt den Warenkorb wieder her, leitet zu products_checkout.php weiter |

Die Zahlungsinitiierung wird vorgelagert von products_checkout (klassischer Warenkorb), abo_checkout (erstes Abonnement) und reabo_checkout (Wieder-Abonnement) übernommen.

Netto / Brutto Architektur

| Schicht | Format |

|---------|--------|

| products.price, products.subscription_price (DB) | Netto |

| order_items.unit_price_ht, unit_price_recurring_ht (DB) | Netto |

| Benutzeranzeige (Warenkorb, Produktseite, Zahlung) | Brutto |

| Stripe/PayPal API unit_amount (Cent) | Brutto (berechnet über vat_rate) |

| Admin-Editor-Eingabe | Brutto über Toggle (aber als Netto gespeichert) |

| B2B VIES nullbesteuert | beim Checkout vat_rate=0 übergeben |

Alle an die Provider gesendeten Beträge werden über bcmul(number_format(Netto × (1 + vat/100), 2, '.', ''), '100', 0) berechnet, um Float-Rundungen zu vermeiden.

Stripe-Flow — synchrone Prüfung bei der Rückkehr

Stripe leitet zur success_url weiter, bevor der Webhook die Verarbeitung immer abgeschlossen hat. Ohne synchrone Prüfung sah der Benutzer „Zahlung wird bearbeitet", obwohl alles in Ordnung war.

text
Stripe Checkout → success_url (payment.php?session_id=...)
        ↓
payment.php : Payment::verifySession('stripe', $sessionId, $orderId)
        ↓
PaymentStripe::verifySession()
   GET /v1/checkout/sessions/{id}
   Prüft: status='complete' UND payment_status IN ('paid','no_payment_required')
        ↓
Wenn OK → Payment::updateStatus($orderId, 'paid', $paymentIntent, 'Stripe session verified on return')
        ↓
Bestätigung wird angezeigt

Der Webhook bleibt die Backup-Quelle der Wahrheit (idempotent dank Payment::updateStatus). Wenn die synchrone Prüfung fehlschlägt (Netzwerk, langsames Stripe), übernimmt der Webhook und der Benutzer sieht „in Bearbeitung" und kann dann aktualisieren.

Anzeige des Gesamtbetrags mit Abonnement

Bei gemischten Bestellungen (Einmal-Produkte + Abonnement) enthält orders.total_ttc nur den Einmal-Teil. Die erste Abo-Rate wird von Stripe über einen separaten line_item abgerechnet.

Die Anzeige berechnet daher:

php
$paidTodayTtc = (float)$order['total_ttc'] + $recurringTtc;

mit $recurringTtc = Σ (unit_price_recurring_ht × (1 + vat_rate/100) × quantity) über order_items, bei denen is_subscription = 1.

Drei angezeigte Zeilen:

  • Heute gezahlt: $paidTodayTtc
  • Davon Produkte: $order['total_ttc']
  • Davon erste Abo-Zahlung: $recurringTtc
  • Dann: $recurringTtc / Monat (wiederkehrend)

Bestätigungs-E-Mail

Wird nur einmal pro Bestellung versendet (Flag orders.email_sent). Enthält:

  • Zeilen-Zusammenfassung (Name, Menge, Brutto-Zeile)
  • Netto-Zwischensumme + MwSt. + Brutto-Gesamtbetrag
  • Rechnungs-/Lieferadressen
  • Überweisungs-/Scheckhinweise falls zutreffend
  • Stripe/PayPal-Transaktionsreferenz falls zutreffend

Versand über natives PHP mail(), Absender $cfg[34]['headoffice_name'] <$cfg[11]['commercial']>.

Abbruch

Wenn ?action=cancel UND payment_status='unpaid':

1. order_items der Bestellung abrufen

2. Aktuellen Warenkorb leeren (Panier::vider())

3. Artikel wieder einfügen (Panier::ajouterProduit(productId, quantity))

4. Bestellung als failed markieren über Payment::updateStatus(..., 'failed', ..., 'Payment cancelled by user')

5. Weiterleitung zu products_checkout.php

Gleiche Logik, wenn der Benutzer mit einer bereits failed-Bestellung zu payment.php zurückkehrt (Warenkorb-Wiederherstellung).

Webhook — `handlers/payment.mod.php`

Endpoint: ?obj=payment.mod.php&method=stripe (oder paypal)

text
POST raw payload
  ↓
$rawPayload = file_get_contents('php://input')
$method     = $_GET['method']
  ↓
Payment::handleCallback($method, ['_raw'=>…, '_headers'=>…, 'event'=>…])
  ↓
Provider prüft Signatur + verarbeitet das Event
  ↓
HTTP 200 {status:ok} | 400 {status:error}

Stripe und PayPal müssen in ihren jeweiligen Dashboards mit diesen URLs konfiguriert werden:

  • https://shop.example.com/index.php?obj=payment.mod.php&method=stripe
  • https://shop.example.com/index.php?obj=payment.mod.php&method=paypal

Konfiguration `cog.php`

| Index | Rolle |

|-------|-------|

| $cfg[34] | Firmendaten (Name, Adresse, SIRET) — in der E-Mail verwendet |

| $cfg[35] | IBAN / BIC für Überweisungen |

| $cfg[36] | Stripe: public_key, secret_key, webhook_secret, mode |

| $cfg[37] | PayPal: client_id, client_secret, webhook_id, mode |

| $cfg[38] | Scheck: order_to (Scheck-Empfänger) |

| $cfg[11]['commercial'] | Absender-E-Mail für Bestätigungen |

Abhängigkeiten

  • Beamreactor\Database\SQL
  • Beamreactor\Payment\Payment — öffentliche Fassade (processSubscription, verifySession, handleCallback, updateStatus, getInstructions)
  • Beamreactor\Payment\PaymentStripe, PaymentPaypal, PaymentVirement, PaymentCheque — konkrete Handler, autoloaded durch Payment::loadHandler() (expliziter include, nicht über Namespace)
  • Beamreactor\Shop\Panier — Warenkorb-Wiederherstellung bei Abbruch/Fehler
  • Beamreactor\Sanitizer\Parser — Bereinigung der GET-Parameter
  • PHP-Funktion mail() zum Versenden der E-Mail

⚠️ Niemals PaymentStripe direkt mit new \Beamreactor\Payment\PaymentStripe() instanziieren — die Klasse wird nicht über den Namespace autoloaded. Immer über die statischen Methoden von Payment:: gehen.

SQL-Tabellen

  • orders: Hauptbestellung (payment_status, total_ttc, payment_method, email_sent, …)
  • order_items: Bestellzeilen, einschließlich Abo-Spalten (is_subscription, unit_price_recurring_ht, subscription_billing_anchor, subscription_interval)
  • payment_subscriptions: aktive Stripe/PayPal-Abonnements (durch Webhooks gespeist)

Installation

Das Verzeichnis payment/ in /plugins/ ablegen. Keine spezifische Migration. Die Tabellen orders / order_items werden vom Haupt-Shop-Modul verwaltet.

de en fr