121 lines
3.4 KiB
PHP
121 lines
3.4 KiB
PHP
<?php
|
|
require __DIR__ . '/config.php';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['error' => 'Method not allowed']);
|
|
exit;
|
|
}
|
|
|
|
$rawBody = file_get_contents('php://input');
|
|
|
|
if (!$rawBody) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Empty body']);
|
|
exit;
|
|
}
|
|
|
|
$payload = json_decode($rawBody, true);
|
|
|
|
if ($payload === null) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Invalid JSON', 'raw' => substr($rawBody, 0, 500)]);
|
|
exit;
|
|
}
|
|
|
|
// Verify HMAC signature if secret is configured
|
|
if (BTCPAY_WEBHOOK_SECRET) {
|
|
$sigHeader = $_SERVER['HTTP_BTCPAY_SIG'] ?? '';
|
|
$expectedSig = 'sha256=' . hash_hmac('sha256', $rawBody, BTCPAY_WEBHOOK_SECRET);
|
|
|
|
if (!hash_equals($expectedSig, $sigHeader)) {
|
|
http_response_code(403);
|
|
echo json_encode(['error' => 'Invalid signature']);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$type = $payload['type'] ?? null;
|
|
$invoiceId = $payload['invoiceId'] ?? null;
|
|
|
|
if (!$type) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Missing type', 'keys' => array_keys($payload ?: [])]);
|
|
exit;
|
|
}
|
|
|
|
// Only handle settled invoices
|
|
if ($type !== 'InvoiceSettled' && $type !== 'InvoicePaymentSettled') {
|
|
echo json_encode(['ok' => true, 'action' => 'ignored', 'type' => $type]);
|
|
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;
|
|
|
|
if (!$listingId) {
|
|
echo json_encode(['ok' => true, 'action' => 'skipped', 'reason' => 'No listingId in invoice metadata']);
|
|
exit;
|
|
}
|
|
|
|
// Update listing in Directus: publish + set paid
|
|
$expiresAt = date('c', strtotime('+30 days'));
|
|
$now = date('c');
|
|
|
|
$directusPayload = json_encode([
|
|
'status' => 'published',
|
|
'payment_status' => 'paid',
|
|
'paid_at' => $now,
|
|
'expires_at' => $expiresAt,
|
|
]);
|
|
|
|
$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,
|
|
],
|
|
]);
|
|
|
|
$directusResponse = file_get_contents($directusUrl, false, $directusContext);
|
|
|
|
$directusStatus = 500;
|
|
if (isset($http_response_header[0]) && preg_match('/\d{3}/', $http_response_header[0], $matches)) {
|
|
$directusStatus = (int)$matches[0];
|
|
}
|
|
|
|
if ($directusStatus >= 400) {
|
|
http_response_code(502);
|
|
echo json_encode(['error' => 'Failed to update listing in Directus', 'status' => $directusStatus]);
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(['ok' => true, 'listingId' => $listingId, 'action' => 'published']);
|