chore: cleanup unused code, update docs for payment integration

This commit is contained in:
2026-02-06 16:23:23 +01:00
parent 52634f84bf
commit 3c7d475d36
7 changed files with 97 additions and 108 deletions

View File

@@ -50,15 +50,30 @@
- **Provider**: BTCpay Server (self-hosted)
- **URL**: https://pay.xmr.rocks/
- **Proxy**: `pow.dgray.io` — alle API-Aufrufe laufen über den PHP-Proxy (BTCPay API-Key bleibt serverseitig)
- **Primär**: Monero (XMR)
- **Alternativ**: Andere Kryptos via Trocador-Plugin (automatischer Swap zu XMR)
- **Preisumrechnung**: Live XMR-Kurs via Kraken API
- EUR: `https://api.kraken.com/0/public/Ticker?pair=XMREUR`
- USD: `https://api.kraken.com/0/public/Ticker?pair=XMRUSD`
- CHF: `https://api.kraken.com/0/public/Ticker?pair=XMRCHF`
- GBP: `https://api.kraken.com/0/public/Ticker?pair=XMRGBP`
- JPY: `https://api.kraken.com/0/public/Ticker?pair=XMRJPY`
- **Bestätigung**: Nach 1-2 Blockchain-Confirmations
- EUR, USD, CHF, GBP, JPY
- **Bestätigung**: Nach 1 Blockchain-Confirmation (via Webhook)
### Flow: Draft → Processing → Published
1. User erstellt Listing → wird als `draft` mit `payment_status: unpaid` gespeichert
2. BTCPay Invoice wird über `pow.dgray.io/btcpay/invoice` erstellt
3. BTCPay Checkout-Modal öffnet sich im Frontend (`js/services/btcpay.js`)
4. Nach Zahlung:
- **Frontend**: Prüft Status via `pow.dgray.io/btcpay/status` nach Modal-Close
- **Webhook**: `pow.dgray.io/btcpay/webhook` empfängt BTCPay Events, setzt `status: published` + `payment_status: paid` nach 1 Confirmation
5. Listing wird veröffentlicht (30 Tage Laufzeit, `expires_at` wird gesetzt)
### Endpunkte (pow.dgray.io)
| Endpoint | Methode | Beschreibung |
|----------|---------|-------------|
| `/btcpay/invoice` | POST | Invoice erstellen (listingId, currency) |
| `/btcpay/status?id={id}` | GET | Invoice-Status abfragen |
| `/btcpay/webhook` | POST | BTCPay Webhook (auto-publish nach Confirmation) |
## Offene Fragen
@@ -66,3 +81,5 @@
- [x] ~~XMR-Kurs API für Umrechnung~~ → Kraken API
- [x] ~~Anzahl Deals für Power-User Status~~ → 5/15/50 Stufen
- [x] ~~Captcha-Lösung~~ → Eigenes PoW-Captcha (keine Lizenzkosten)
- [x] ~~Payment-Proxy~~`pow.dgray.io` (PHP, API-Key serverseitig)
- [x] ~~Webhook für Auto-Publish~~`btcpay-webhook.php` auf `pow.dgray.io`

View File

@@ -73,6 +73,14 @@ Response:
}
```
### POST /btcpay/webhook
Empfängt BTCPay Server Webhook-Events. Wird in BTCPay unter Store → Settings → Webhooks konfiguriert.
- **URL**: `https://pow.dgray.io/btcpay/webhook`
- **Event**: `InvoiceSettled` (nach 1 Blockchain-Confirmation)
- **Aktion**: Setzt das zugehörige Listing in Directus auf `status: published`, `payment_status: paid`, setzt `paid_at` und `expires_at` (30 Tage)
- **Sicherheit**: Webhook-Secret wird serverseitig geprüft
## Gebühren
| Währung | Betrag |

View File

@@ -19,7 +19,7 @@ $payload = json_decode($rawBody, true);
if ($payload === null) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON', 'raw' => substr($rawBody, 0, 500)]);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
@@ -36,11 +36,10 @@ if (BTCPAY_WEBHOOK_SECRET) {
}
$type = $payload['type'] ?? null;
$invoiceId = $payload['invoiceId'] ?? null;
if (!$type) {
http_response_code(400);
echo json_encode(['error' => 'Missing type', 'keys' => array_keys($payload ?: [])]);
echo json_encode(['error' => 'Missing type']);
exit;
}
@@ -50,35 +49,11 @@ if ($type !== 'InvoiceSettled' && $type !== 'InvoicePaymentSettled') {
exit;
}
// For settled events, invoiceId is required
if (!$invoiceId) {
echo json_encode(['ok' => true, 'action' => 'test_acknowledged', 'type' => $type]);
exit;
}
// Fetch invoice from BTCPay to get listing ID from metadata
$btcpayUrl = BTCPAY_BASE_URL . '/api/v1/stores/' . BTCPAY_STORE_ID . '/invoices/' . urlencode($invoiceId);
$btcpayContext = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "Authorization: token " . BTCPAY_API_KEY . "\r\n",
'ignore_errors' => true,
'timeout' => 10,
],
]);
$btcpayResponse = file_get_contents($btcpayUrl, false, $btcpayContext);
if ($btcpayResponse === false) {
http_response_code(502);
echo json_encode(['error' => 'Failed to fetch invoice from BTCPay']);
exit;
}
$invoice = json_decode($btcpayResponse, true);
$listingId = $invoice['metadata']['listingId'] ?? null;
// Read listingId directly from webhook payload metadata
$listingId = $payload['metadata']['listingId'] ?? null;
if (!$listingId) {
echo json_encode(['ok' => true, 'action' => 'skipped', 'reason' => 'No listingId in invoice metadata']);
echo json_encode(['ok' => true, 'action' => 'skipped', 'reason' => 'No listingId in metadata']);
exit;
}
@@ -94,26 +69,43 @@ $directusPayload = json_encode([
]);
$directusUrl = DIRECTUS_URL . '/items/listings/' . urlencode($listingId);
$directusContext = stream_context_create([
'http' => [
'method' => 'PATCH',
'header' => "Content-Type: application/json\r\nAuthorization: Bearer " . DIRECTUS_TOKEN . "\r\n",
'content' => $directusPayload,
'ignore_errors' => true,
'timeout' => 10,
$ch = curl_init($directusUrl);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $directusPayload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . DIRECTUS_TOKEN,
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5,
]);
$directusResponse = file_get_contents($directusUrl, false, $directusContext);
$directusResponse = curl_exec($ch);
$directusStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
$directusStatus = 500;
if (isset($http_response_header[0]) && preg_match('/\d{3}/', $http_response_header[0], $matches)) {
$directusStatus = (int)$matches[0];
if ($directusResponse === false || $curlError) {
http_response_code(502);
echo json_encode([
'error' => 'Connection to Directus failed',
'listingId' => $listingId,
'curl_error' => $curlError,
]);
exit;
}
if ($directusStatus >= 400) {
http_response_code(502);
echo json_encode(['error' => 'Failed to update listing in Directus', 'status' => $directusStatus]);
echo json_encode([
'error' => 'Directus returned error',
'status' => $directusStatus,
'listingId' => $listingId,
'response' => substr($directusResponse, 0, 500),
]);
exit;
}