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,103 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Payments;
|
||||
|
||||
/**
|
||||
* Protokoll aller Anbieter-Antworten in PostgreSQL (krempl_data.payment_log).
|
||||
*
|
||||
* Warum PG: MySQL `orders` hat keine Uhrzeit und keinen Platz fuer JSON. Hier liegt
|
||||
* jede Anfrage/Antwort mit Zeitstempel -- die Beweiskette bei Rueckfragen von
|
||||
* Kunden oder Anbietern.
|
||||
*
|
||||
* Faellt STILL aus (wie shop_event): ein Protokoll darf den Checkout nie gefaehrden.
|
||||
* Verbindung kommt aus core/postgres/config.ini, Sektion [postgresql].
|
||||
*/
|
||||
final class Log
|
||||
{
|
||||
/** @var resource|\PgSql\Connection|null|false */
|
||||
private static $conn = null;
|
||||
private static bool $dead = false;
|
||||
|
||||
/**
|
||||
* @param array<mixed>|null $payload wird als JSON abgelegt (Secrets vorher entfernen!)
|
||||
*/
|
||||
public static function write(
|
||||
string $provider,
|
||||
string $action,
|
||||
?int $orderId = null,
|
||||
?string $orderNumber = null,
|
||||
?string $reference = null,
|
||||
?string $status = null,
|
||||
?int $httpStatus = null,
|
||||
?array $payload = null,
|
||||
?string $detail = null
|
||||
): bool {
|
||||
$c = self::conn();
|
||||
if (!$c) {
|
||||
return false;
|
||||
}
|
||||
$json = $payload === null ? null : \json_encode(self::scrub($payload), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$ok = @\pg_query_params(
|
||||
$c,
|
||||
'INSERT INTO payment_log
|
||||
(provider, action, order_id, order_number, reference, status, http_status, payload, detail, host)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',
|
||||
[
|
||||
$provider, $action, $orderId, $orderNumber, $reference, $status, $httpStatus,
|
||||
$json, $detail, (string) ($_SERVER['SERVER_NAME'] ?? \gethostname()),
|
||||
]
|
||||
);
|
||||
if (!$ok) {
|
||||
self::$dead = true;
|
||||
\error_log('payment_log: INSERT fehlgeschlagen -- ' . @\pg_last_error($c));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return resource|\PgSql\Connection|false */
|
||||
private static function conn()
|
||||
{
|
||||
if (self::$dead) {
|
||||
return false;
|
||||
}
|
||||
if (self::$conn !== null) {
|
||||
return self::$conn;
|
||||
}
|
||||
if (!\function_exists('pg_connect')) {
|
||||
self::$dead = true;
|
||||
return false;
|
||||
}
|
||||
$ini = @\parse_ini_file(\dirname(__DIR__) . '/postgres/config.ini', true);
|
||||
if (!$ini || empty($ini['postgresql'])) {
|
||||
self::$dead = true;
|
||||
return false;
|
||||
}
|
||||
$p = $ini['postgresql'];
|
||||
$dsn = \sprintf(
|
||||
'host=%s port=%s dbname=%s user=%s password=%s connect_timeout=2',
|
||||
$p['host'] ?? 'localhost', $p['port'] ?? '5432', $p['database'] ?? '', $p['user'] ?? '', $p['password'] ?? ''
|
||||
);
|
||||
$c = @\pg_connect($dsn);
|
||||
if (!$c) {
|
||||
self::$dead = true;
|
||||
\error_log('payment_log: keine PG-Verbindung -- Protokoll uebersprungen');
|
||||
return false;
|
||||
}
|
||||
return self::$conn = $c;
|
||||
}
|
||||
|
||||
/** Entfernt Schluessel, die nach Zugangsdaten aussehen, rekursiv. */
|
||||
private static function scrub(array $data): array
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
if (\is_string($k) && \preg_match('/secret|password|token|authorization|client_id/i', $k)) {
|
||||
$data[$k] = '***';
|
||||
} elseif (\is_array($v)) {
|
||||
$data[$k] = self::scrub($v);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user