$raw_post_data]); $data = json_decode($raw_post_data, true); if (!$data) { logError("0.1", "Invalid JSON payload."); http_response_code(400); exit; } // 2. Extract data and verify the token from ecash $isSuccess = $data['isSuccess'] ?? null; $orderRef = $data['orderRef'] ?? null; $transactionNo = $data['transactionNo'] ?? null; $amount = $data['amount'] ?? null; $receivedToken = $data['token'] ?? ''; $string_to_hash = ECASH_MERCHANT_ID . ECASH_MERCHANT_SECRET . $transactionNo . $amount . $orderRef; $expected_token = md5($string_to_hash); if (strcasecmp($expected_token, $receivedToken) !== 0) { logError("1", "Token Mismatch", [ "expected" => $expected_token, "received" => $receivedToken, "string" => $string_to_hash ]); http_response_code(401); // Unauthorized exit; } logError("1", "Token Verified Successfully."); // 3. Check if payment was successful if ($isSuccess !== true) { logError("2", "Payment was not successful according to ecash.", $data); // Optionally, update your database to mark the order as 'failed' updateTransactionStatus($orderRef, 'failed', $transactionNo); http_response_code(200); // Respond OK to ecash, but do nothing else exit; } logError("2", "Payment reported as SUCCESS by ecash."); // 4. Find and process the transaction atomically try { $con->beginTransaction(); $stmt = $con->prepare("SELECT * FROM ecash_transactions WHERE order_ref = ? LIMIT 1 FOR UPDATE"); $stmt->execute([$orderRef]); $transaction = $stmt->fetch(PDO::FETCH_ASSOC); if (!$transaction) { $con->rollBack(); logError("3", "OrderRef not found in our database.", ["orderRef" => $orderRef]); http_response_code(404); exit; } if ($transaction['status'] !== 'pending') { $con->rollBack(); logError("3.1", "Transaction already processed.", ["orderRef" => $orderRef, "status" => $transaction['status']]); http_response_code(200); exit; } // Atomically mark as processing to prevent concurrent webhooks $lockStmt = $con->prepare("UPDATE ecash_transactions SET status = 'processing' WHERE order_ref = ? AND status = 'pending'"); $lockStmt->execute([$orderRef]); if ($lockStmt->rowCount() === 0) { $con->rollBack(); logError("3.2", "Concurrent webhook detected, transaction already claimed.", ["orderRef" => $orderRef]); http_response_code(200); exit; } $passengerId = $transaction['passenger_id']; $paidAmount = $transaction['amount']; logError("3", "Transaction found in DB.", ["passengerId" => $passengerId, "amount" => $paidAmount]); $finalAmount = calculateBonus($paidAmount); logError("4", "Bonus calculated.", ["original" => $paidAmount, "final" => $finalAmount]); $passengerToken = generatePaymentToken($passengerId, $finalAmount); if ($passengerToken) { addToPassengerWallet($passengerId, $finalAmount, $passengerToken); } $paymentMethod = 'ecash'; addToSiroWallet($passengerId, $paidAmount, $paymentMethod); $stmtUpdate = $con->prepare("UPDATE ecash_transactions SET status = 'success', ecash_transaction_no = ?, updated_at = NOW() WHERE order_ref = ?"); $stmtUpdate->execute([$transactionNo, $orderRef]); $con->commit(); logError("7", "Process completed successfully."); } catch (PDOException $e) { logError("DB_ERROR", "Database error: " . $e->getMessage()); http_response_code(500); exit; } catch (Exception $e) { logError("GENERAL_ERROR", "General error: " . $e->getMessage()); http_response_code(500); exit; } // 7. Respond to ecash server http_response_code(200); echo "Webhook processed."; // --- ALL HELPER FUNCTIONS FROM paymet_verfy.php --- function updateTransactionStatus($orderRef, $status, $transactionNo) { global $con; try { $stmt = $con->prepare( "UPDATE ecash_transactions SET status = ?, ecash_transaction_no = ?, updated_at = NOW() WHERE order_ref = ?" ); $stmt->execute([$status, $transactionNo, $orderRef]); } catch (PDOException $e) { logError("DB_UPDATE_ERROR", "Failed to update transaction status", ["error" => $e->getMessage()]); } } function generatePaymentToken($passengerId, $amount) { $url = BASE_URL . "/passengerWallet/addPaymentTokenPassenger.php"; $postData = [ 'passengerId' => $passengerId, 'amount' => $amount ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { logError("4.1", "cURL error in token generation", [ "error" => $curlError, "url" => $url ]); return null; } if ($httpCode != 200) { logError("4.2", "HTTP error in token generation", [ "http_code" => $httpCode, "response" => $response ]); return null; } $data = json_decode($response, true); if (!$data || !isset($data['message'])) { logError("4.3", "Invalid response format in token generation", [ "response" => $response ]); return null; } return $data['message']; // ✅ Return token } // 🎯 Function to add balance to passenger's wallet with error logging function addToPassengerWallet($passengerId, $amount, $token) { $url = BASE_URL . "/passengerWallet/add.php"; $postData = [ 'passenger_id' => $passengerId, 'balance' => $amount, 'token' => $token ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { logError("5.1", "cURL error in passenger wallet update", [ "error" => $curlError, "url" => $url ]); return null; } if ($httpCode != 200) { logError("5.2", "HTTP error in passenger wallet update", [ "http_code" => $httpCode, "response" => $response ]); return null; } $data = json_decode($response, true); if (!$data) { logError("5.3", "Invalid response format in passenger wallet update", [ "response" => $response ]); return null; } return $data; // ✅ Return result } // 🎯 Function to add balance to Siro wallet with error logging function addToSiroWallet($passengerId, $amount, $paymentMethod) { // Generate a new token specifically for the Siro wallet $siroToken = generatePaymentToken($passengerId, $amount); if (!$siroToken) { logError("6.0.1", "Failed to generate Siro token"); return null; } logError("6.0.2", "Generated new Siro token", [ "token_length" => ($siroToken) ]); $url = BASE_URL . "/siroWallet/add.php"; $postData = [ 'amount' => $amount, 'paymentMethod' => $paymentMethod, 'passengerId' => $passengerId, 'token' => $siroToken, // Use the new Siro-specific token 'driverId' => 'passenger' ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { logError("6.1", "cURL error in Siro wallet update", [ "error" => $curlError, "url" => $url ]); return null; } if ($httpCode != 200) { logError("6.2", "HTTP error in Siro wallet update", [ "http_code" => $httpCode, "response" => $response ]); return null; } $data = json_decode($response, true); if (!$data) { logError("6.3", "Invalid response format in Siro wallet update", [ "response" => $response ]); return null; } return $data; // ✅ Return result } // 🎯 Function to calculate bonus function calculateBonus($amount) { logError("3.1", "Bonus calculation input", ["amount" => $amount]); $result = 0; if ($amount == 100) $result = 100; else if ($amount == 200) $result = 215; else if ($amount == 400) $result = 450; else if ($amount == 1000) $result = 1140; logError("3.2", "Bonus calculation result", [ "input" => $amount, "output" => $result ]); return $result; } ?>