237 lines
9.7 KiB
PHP
Executable File
237 lines
9.7 KiB
PHP
Executable File
<?php
|
|
// /v1/main/ride/syriatel/driver/confirm_payment.php
|
|
// يعتمد على jwtconnect.php و syriatel_token_handler.php فقط
|
|
include_once "../../../jwtconnect.php";
|
|
include_once "./syriatel_token_handler.php";
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// ================== Helpers (طباعة/لوج) ==================
|
|
if (!function_exists('printSuccess')) {
|
|
function printSuccess($data = null, $message = "success") {
|
|
echo json_encode(["status" => "success", "message" => $message, "data" => $data], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
}
|
|
if (!function_exists('printFailure')) {
|
|
function printFailure($message = "failure", $code = null) {
|
|
$resp = ["status" => "failure", "message" => $message];
|
|
if ($code !== null) $resp["code"] = $code;
|
|
echo json_encode($resp, JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
define("WALLET_LOG", __DIR__ . "/../logs/payment_verification.log");
|
|
function wlog($step, $msg, $data = null) {
|
|
$dir = dirname(WALLET_LOG);
|
|
if (!is_dir($dir)) { @mkdir($dir, 0755, true); }
|
|
$line = "[".date('Y-m-d H:i:s')."] STEP {$step}: {$msg}";
|
|
if ($data !== null) $line .= " | Data: ".json_encode($data, JSON_UNESCAPED_UNICODE);
|
|
file_put_contents(WALLET_LOG, $line.PHP_EOL, FILE_APPEND);
|
|
}
|
|
|
|
// ================== منطق التوكن/المعرف ==================
|
|
function generateToken(PDO $con, string $driverId, float $amount): ?string {
|
|
// secretKey يُفترض قادم من connect/env
|
|
global $secretKey;
|
|
$data = $driverId.$amount.time().($secretKey ?? 'default_secret');
|
|
$hash = hash('sha256', $data);
|
|
$rand = bin2hex(random_bytes(16));
|
|
$token = substr($hash.$rand, 0, 64);
|
|
|
|
$stmt = $con->prepare("INSERT INTO payment_tokens (token, driverID, dateCreated, amount)
|
|
VALUES (:t, :d, NOW(), :a)");
|
|
$stmt->execute([':t'=>$token, ':d'=>$driverId, ':a'=>$amount]);
|
|
return $stmt->rowCount() ? $token : null;
|
|
}
|
|
|
|
function generatePaymentID(PDO $con, string $driverId, float $amount, string $method): ?string {
|
|
$stmt = $con->prepare("INSERT INTO paymentsDriverPoints (amount, payment_method, driverID)
|
|
VALUES (:a, :m, :d)");
|
|
$stmt->execute([':a'=>$amount, ':m'=>$method, ':d'=>$driverId]);
|
|
return $stmt->rowCount() ? $con->lastInsertId() : null;
|
|
}
|
|
|
|
// ================== منطق إنهاء الدفع (مضمَّن) ==================
|
|
function finalizeWalletPaymentInline(PDO $con, string $orderRef): void {
|
|
// نقفل الصف لمنع السباقات
|
|
$stmt = $con->prepare("SELECT * FROM paymentsLogSyriaDriver
|
|
WHERE order_ref = :r LIMIT 1 FOR UPDATE");
|
|
$stmt->execute([':r'=>$orderRef]);
|
|
$payment = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$payment) {
|
|
wlog("FINALIZE", "Payment row not found", ['orderRef'=>$orderRef]);
|
|
throw new RuntimeException("Payment not found.");
|
|
}
|
|
|
|
// نتأكد من نجاح مزود الدفع
|
|
if ((int)$payment['status'] !== 1) {
|
|
wlog("FINALIZE", "Payment not completed yet", ['orderRef'=>$orderRef, 'status'=>$payment['status']]);
|
|
throw new RuntimeException("Payment not completed.");
|
|
}
|
|
|
|
// حارس التكرار: إذا تمّت معالجة المحفظة سابقاً لا نكرر
|
|
if (!empty($payment['processed_wallet']) && (int)$payment['processed_wallet'] === 1) {
|
|
wlog("FINALIZE", "Already processed_wallet=1, skip.", ['orderRef'=>$orderRef]);
|
|
return; // لا نرمي استثناء؛ العملية سبق احتسابها
|
|
}
|
|
|
|
$driverId = $payment['user_id']; // This is now correctly handled as a string
|
|
$originalAmount = (float)$payment['amount'];
|
|
$paymentMethod = $payment['payment_method'] ?: 'ecash';
|
|
|
|
// البونص
|
|
$bonusAmount = match ((int)$originalAmount) {
|
|
100 => 100.0,
|
|
200 => 210.0,
|
|
400 => 450.0,
|
|
1000 => 1100.0,
|
|
default => $originalAmount,
|
|
};
|
|
|
|
// تنفيذ ككل داخل معاملة
|
|
$con->beginTransaction();
|
|
try {
|
|
// توليد التوكنات/المعرف
|
|
$tokenDriver = generateToken($con, $driverId, $bonusAmount);
|
|
if (!$tokenDriver) throw new RuntimeException("Failed to generate driver token");
|
|
|
|
$tokenSiro = generateToken($con, $driverId, $originalAmount);
|
|
if (!$tokenSiro) throw new RuntimeException("Failed to generate siro token");
|
|
|
|
$paymentID = generatePaymentID($con, $driverId, $bonusAmount, $paymentMethod);
|
|
if (!$paymentID) throw new RuntimeException("Failed to create payment ID");
|
|
|
|
// driverWallet
|
|
$q = $con->prepare("INSERT INTO driverWallet (driverID, paymentID, amount, paymentMethod)
|
|
VALUES (:d, :r, :a, :m)");
|
|
$q->execute([
|
|
':d'=>$driverId, ':a'=>$bonusAmount,
|
|
':m'=>$paymentMethod, ':r'=>$orderRef
|
|
]);
|
|
if ($q->rowCount() === 0) throw new RuntimeException("Insert driverWallet failed");
|
|
|
|
// وسم التوكن كمستخدم
|
|
$con->prepare("UPDATE payment_tokens SET isUsed = 1 WHERE token = :t")->execute([':t'=>$tokenDriver]);
|
|
|
|
// siroWallet
|
|
$q2 = $con->prepare("INSERT INTO siroWallet (driverId, passengerId, amount, paymentMethod, token, createdAt)
|
|
VALUES (:d, :p, :a, :m, :t, CURRENT_TIMESTAMP)");
|
|
$q2->execute([
|
|
':d'=>$driverId, ':p'=>'driver', ':a'=>$originalAmount,
|
|
':m'=>$paymentMethod, ':t'=>$tokenSiro
|
|
]);
|
|
if ($q2->rowCount() === 0) throw new RuntimeException("Insert siroWallet failed");
|
|
|
|
$con->prepare("UPDATE payment_tokens SET isUsed = 1 WHERE token = :t")->execute([':t'=>$tokenSiro]);
|
|
|
|
// تأشير السجل كمنتهٍ للمحفظة
|
|
$con->prepare("UPDATE paymentsLogSyriaDriver
|
|
SET processed_wallet = 1, updated_at = NOW()
|
|
WHERE order_ref = :r")->execute([':r'=>$orderRef]);
|
|
|
|
$con->commit();
|
|
wlog("FINALIZE", "Wallets updated successfully", ['orderRef'=>$orderRef, 'driverId'=>$driverId]);
|
|
} catch (Throwable $e) {
|
|
$con->rollBack();
|
|
wlog("FINALIZE", "Exception: ".$e->getMessage(), ['orderRef'=>$orderRef]);
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
// ================== مدخل تأكيد الدفع ==================
|
|
$transactionID = filterRequest('transactionID');
|
|
$otp = filterRequest('otp');
|
|
|
|
error_log("Syriatel Confirm: Request for transaction {$transactionID}");
|
|
|
|
if (!$transactionID || !$otp) {
|
|
printFailure("Missing transaction ID or OTP.");
|
|
}
|
|
|
|
// بيئة
|
|
$baseUrl = rtrim((string)getenv('SYRIATEL_API_BASE_URL'), '/');
|
|
$merchantMSISDN = (string)getenv('SYRIATEL_MERCHANT_MSISDN');
|
|
if ($baseUrl === '' || $merchantMSISDN === '') {
|
|
error_log("Syriatel Confirm: Missing SYRIATEL envs");
|
|
printFailure("Server configuration error.");
|
|
}
|
|
|
|
try {
|
|
// 1) Auth token
|
|
$token = function_exists('GetSerialToken') ? GetSerialToken() : getSyriatelToken();
|
|
if (!$token) { printFailure("Could not authenticate with the payment provider."); }
|
|
|
|
// 2) Call provider
|
|
$body = [
|
|
"OTP" => $otp,
|
|
"merchantMSISDN" => $merchantMSISDN,
|
|
"transactionID" => $transactionID,
|
|
"token" => $token
|
|
];
|
|
$url = "{$baseUrl}/ePaymentExternalModule/paymentConfirmation";
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_UNICODE),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => [
|
|
"Content-Type: application/json",
|
|
"Accept: application/json",
|
|
"User-Agent: Mozilla/5.0",
|
|
],
|
|
CURLOPT_CONNECTTIMEOUT => 8,
|
|
CURLOPT_TIMEOUT => 40,
|
|
CURLOPT_SSL_VERIFYPEER => true,
|
|
CURLOPT_SSL_VERIFYHOST => 2,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$errno = curl_errno($ch);
|
|
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
error_log("Syriatel Confirm: HTTP {$httpCode} - ".substr((string)$response, 0, 300));
|
|
|
|
if ($errno) { printFailure("Payment gateway communication error.", $errno); }
|
|
if ($httpCode !== 200 || !$response) { printFailure("Payment gateway communication error (HTTP {$httpCode})."); }
|
|
|
|
$responseData = json_decode($response, true);
|
|
if (!is_array($responseData)) { printFailure("Invalid provider response."); }
|
|
|
|
$ok = (isset($responseData['errorDesc']) && $responseData['errorDesc'] === "Success");
|
|
|
|
if ($ok) {
|
|
// نحدّث حالة الدفع، ونُتم المحفظة بشكل آمن داخل نفس الطلب
|
|
$con->prepare("UPDATE paymentsLogSyriaDriver
|
|
SET status = 1, updated_at = NOW()
|
|
WHERE order_ref = :r")->execute([':r'=>$transactionID]);
|
|
|
|
// إنهاء المحفظة (داخلية)
|
|
$walletOk = true; $walletMsg = null;
|
|
try {
|
|
finalizeWalletPaymentInline($con, $transactionID);
|
|
} catch (Throwable $ex) {
|
|
$walletOk = false; $walletMsg = $ex->getMessage();
|
|
}
|
|
|
|
$payload = [
|
|
'message' => 'Payment confirmed.',
|
|
'transactionID' => $transactionID,
|
|
'provider' => $responseData
|
|
];
|
|
if (!$walletOk && $walletMsg) {
|
|
$payload['walletNotice'] = "Payment successful, but wallet update failed: ".$walletMsg;
|
|
}
|
|
|
|
printSuccess($payload);
|
|
}
|
|
|
|
$errorMessage = $responseData['errorDesc'] ?? 'Unknown provider error';
|
|
printFailure($errorMessage);
|
|
|
|
} catch (Throwable $e) {
|
|
error_log("Syriatel Confirm: Exception - ".$e->getMessage());
|
|
printFailure("An unexpected server error occurred.");
|
|
} |