intval($invoice), 'Phone' => $phone, 'Guid' => $guid, 'OperationNumber' => intval($operationNumber), 'Code' => $codeB64 ]; $bodyJson = json_encode($body, JSON_UNESCAPED_UNICODE); error_log("MTN Confirm (Driver): Prepared body JSON: " . $bodyJson); // Generate signature $signResult = openssl_sign($bodyJson, $sig, $privateKey, OPENSSL_ALGO_SHA256); if (!$signResult) { error_log("MTN Confirm (Driver): Failed to generate signature"); printFailure("Signature error."); exit; } $xSignature = base64_encode($sig); error_log("MTN Confirm (Driver): Generated signature"); // Send the request $ch = curl_init("{$baseUrl}/pos_web/payment_phone/confirm"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $bodyJson, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Request-Name: pos_web/payment_phone/confirm", "Subject: {$terminalId}", "X-Signature: {$xSignature}" ] ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); error_log("MTN Confirm (Driver): HTTP $httpCode - Response: $response"); if ($curlError) { error_log("MTN Confirm (Driver): cURL error - $curlError"); } // --- SOLUTION IMPLEMENTED HERE --- // 1. Decode the response from MTN to check its contents. $responseData = json_decode($response, true) ?: []; // 2. First, check for network/gateway level failure. if ($httpCode !== 200) { error_log("MTN Confirm (Driver): HTTP failure for invoice {$invoice}. Code: {$httpCode}"); // Use printFailure to send a structured error to Flutter printFailure(['message' => 'MTN Gateway Error', 'http' => $httpCode, 'details' => $responseData]); exit; } // 3. Now, check for business-logic failure (like "Balance not enough"). $errno = $responseData['Errno'] ?? -1; // Default to an error state if Errno is missing if ($errno !== 0) { $apiError = $responseData['Error'] ?? 'Unknown MTN API Error'; error_log("MTN Confirm (Driver): Business failure for invoice {$invoice}. Errno: {$errno}, Reason: {$apiError}"); // This now sends the specific error message in the format Flutter expects! printFailure(['message' => $apiError, 'errno' => $errno, 'details' => $responseData]); exit; } // --- ONLY PROCEED TO DATABASE ON FULL SUCCESS (HTTP 200 AND Errno 0) --- try { $stmt = $con->prepare( "UPDATE `paymentsLogSyriaDriver` SET status = 1, updated_at = NOW() WHERE order_ref = :inv" ); $stmt->execute([':inv' => $invoice]); error_log("MTN Confirm (Driver): Payment updated successfully in DB for invoice={$invoice}"); // The file path correction from before remains important. include_once __DIR__ . '/finalize_wallet_payment.php'; // Call the wallet finalization logic if (function_exists('finalizeWalletPayment')) { $_GET['orderRef'] = $invoice; finalizeWalletPayment($con); } else { error_log("MTN Confirm (Driver): FATAL - finalizeWalletPayment() function does not exist after include."); } // On success, send a success response to Flutter printSuccess(['message' => 'Payment confirmed successfully', 'details' => $responseData]); } catch (PDOException $e) { error_log("MTN Confirm (Driver): DB update error - " . $e->getMessage()); printFailure("Database processing error."); }