sync: Zahlungsstrecke, PG-Events, Fatal-Log und PayPal-v2-Blaupause vom Live-Stand 2026-09-13
Deploy to Dev / deploy (push) Successful in 1s

This commit is contained in:
Thomas Bartelt
2026-09-13 19:20:24 +02:00
parent 20d55f77b8
commit af81e8f63a
22 changed files with 1963 additions and 58 deletions
+62
View File
@@ -0,0 +1,62 @@
<?php
/**
* Webhook-Endpunkt fuer PayPal-v2.
*
* Dient als verlaessliche Quelle des Zahlstatus (server-zu-server), unabhaengig
* davon, ob der Kunde nach der Zahlung tatsaechlich zum Shop zurueckkehrt.
*
* PayPal-Dashboard: Webhook auf diese URL anlegen, mindestens Event
* PAYMENT.CAPTURE.COMPLETED
* abonnieren und die entstehende Webhook-ID in die Config eintragen.
*/
declare(strict_types=1);
require __DIR__ . '/bootstrap.php';
use PaypalV2\Webhook;
use PaypalV2\Gateway;
$rawBody = (string) \file_get_contents('php://input');
// Header einsammeln (lowercase Keys)
$headers = [];
foreach ($_SERVER as $key => $value) {
if (\strncmp($key, 'HTTP_', 5) === 0) {
$name = \strtolower(\str_replace('_', '-', \substr($key, 5)));
$headers[$name] = (string) $value;
}
}
// Signatur verifizieren – sonst sofort ablehnen
try {
if (!(new Webhook())->verify($headers, $rawBody)) {
\http_response_code(400);
echo 'invalid signature';
exit;
}
} catch (\Throwable $e) {
\error_log('PayPal v2 webhook verify: ' . $e->getMessage());
\http_response_code(500);
exit;
}
$event = \json_decode($rawBody, true);
$type = \is_array($event) ? (string) ($event['event_type'] ?? '') : '';
try {
if ($type === 'PAYMENT.CAPTURE.COMPLETED') {
$resource = $event['resource'] ?? [];
if (\is_array($resource)) {
(new Gateway($base_object))->confirmFromWebhookCapture($resource);
}
}
// Weitere Event-Typen (Refund/Denied) koennen hier ergaenzt werden.
} catch (\Throwable $e) {
\error_log('PayPal v2 webhook handle (' . $type . '): ' . $e->getMessage());
\http_response_code(500);
exit;
}
// PayPal erwartet 2xx, sonst wird das Event erneut zugestellt.
\http_response_code(200);
echo 'ok';