getMessage() . "\n", FILE_APPEND ); } } // ================= LOAD CONFIG ================= $config = require __DIR__ . '/doku_config.php'; $secretKey = trim($config['secret_key']); // ================= CONNECT DB ================= try { require __DIR__ . '/../../../config/connect.php'; } catch (Throwable $e) { writeLog("ERROR DB CONNECT", $e->getMessage()); } // ================= AMBIL BODY ================= $rawBody = file_get_contents("php://input"); $data = json_decode($rawBody, true); // ================= AMBIL HEADER ================= if (function_exists('getallheaders')) { $headers = getallheaders(); } else { $headers = []; foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $headers[str_replace('_', '-', substr($name, 5))] = $value; } } } // ================= NORMALISASI HEADER ================= $signature = $headers['Signature'] ?? $headers['signature'] ?? ''; $requestId = $headers['Request-Id'] ?? $headers['request-id'] ?? ''; $timestamp = $headers['Request-Timestamp'] ?? $headers['request-timestamp'] ?? ''; $clientId = $headers['Client-Id'] ?? $headers['client-id'] ?? ''; $target = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); // ================= VALIDASI SIGNATURE ================= $digest = base64_encode(hash('sha256', $rawBody, true)); $stringToSign = "Client-Id:" . $clientId . "\n" . "Request-Id:" . $requestId . "\n" . "Request-Timestamp:" . $timestamp . "\n" . "Request-Target:" . $target . "\n" . "Digest:" . $digest; $generated = "HMACSHA256=" . base64_encode( hash_hmac('sha256', $stringToSign, $secretKey, true) ); if ($generated !== $signature) { writeLog("ERROR SIGNATURE INVALID"); http_response_code(401); echo "INVALID SIGNATURE"; exit; } // ================= PARSE DATA ================= $invoice = $data['order']['invoice_number'] ?? null; $amount = $data['order']['amount'] ?? 0; $status = strtoupper($data['transaction']['status'] ?? 'PENDING'); // ================= VALIDASI ================= if (!$invoice) { writeLog("ERROR NO INVOICE"); http_response_code(400); exit('INVALID DATA'); } // ================= CEK DB ================= $stmt = $pdo->prepare("SELECT id,status,amount FROM payment_winpay WHERE trx_id = ?"); $stmt->execute([$invoice]); $trx = $stmt->fetch(PDO::FETCH_ASSOC); if (!$trx) { writeLog("ERROR NOT FOUND"); http_response_code(404); exit('NOT FOUND'); } // ================= VALIDASI AMOUNT ================= if ((int)$trx['amount'] !== (int)$amount) { writeLog("ERROR AMOUNT MISMATCH", [ 'db' => $trx['amount'], 'callback' => $amount ]); http_response_code(400); exit('INVALID AMOUNT'); } // ================= ANTI DOUBLE ================= if ($trx['status'] === 'PAID') { writeLog("ALREADY PAID"); echo json_encode(['message' => 'ALREADY PAID']); exit; } // ================= MAP STATUS ================= switch ($status) { case 'SUCCESS': $dbStatus = 'PAID'; break; case 'EXPIRED': $dbStatus = 'EXPIRED'; break; default: $dbStatus = 'PENDING'; } // ================= UPDATE DB ================= try { $stmt = $pdo->prepare(" UPDATE payment_winpay SET status = :status, paid_at = CASE WHEN :status = 'PAID' THEN NOW() ELSE paid_at END, response_callback = :callback, updated_at = NOW() WHERE trx_id = :trx_id "); $stmt->execute([ ':status' => $dbStatus, ':callback' => $rawBody, ':trx_id' => $invoice ]); } catch (Throwable $e) { writeLog("DB UPDATE ERROR", $e->getMessage()); http_response_code(500); exit('DB ERROR'); } echo json_encode([ "responseCode" => "2000000", "responseMessage" => "SUCCESS" ]);