Update: 2026-05-09 18:11:10

This commit is contained in:
Hamza-Ayed
2026-05-09 18:11:10 +03:00
parent e1bdda3cbf
commit 0dbf812be4
4 changed files with 127 additions and 63 deletions

View File

@@ -11,6 +11,7 @@ declare(strict_types=1);
use App\Core\Database;
use App\Core\AI;
use App\Core\PaymentParser;
use App\Middleware\AuthMiddleware;
$decoded = AuthMiddleware::check();
@@ -20,18 +21,17 @@ if (!in_array($decoded['role'], ['admin', 'accountant'])) {
}
$paymentId = $_POST['payment_id'] ?? null;
$bankRef = trim($_POST['bank_reference'] ?? '');
$rawBankRef = trim($_POST['bank_reference'] ?? '');
$bankRef = PaymentParser::extractReference($rawBankRef) ?: $rawBankRef;
if (!$paymentId) {
json_error('معرف طلب الدفع مطلوب.', 422);
}
if (!$bankRef) {
json_error('رقم مرجع الحوالة مطلوب للتفعيل الآلي.', 422);
}
$hasReceipt = isset($_FILES['receipt']) && $_FILES['receipt']['error'] === UPLOAD_ERR_OK;
if (!isset($_FILES['receipt']) || $_FILES['receipt']['error'] !== UPLOAD_ERR_OK) {
json_error('صورة وصل الدفع مطلوبة.', 422);
if (!$bankRef && !$hasReceipt) {
json_error('الرجاء إدخال رقم المرجع (أو نص الرسالة) أو إرفاق صورة الوصل.', 422);
}
$db = Database::getInstance();
@@ -79,58 +79,65 @@ try {
}
}
// 3. If no immediate match, save the receipt and wait for AI/Bot backup
$uploadDir = STORAGE_PATH . '/receipts/' . $tenantId;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0750, true);
// 3. If no immediate match and no receipt image, we can't do more
if (!$hasReceipt && !$transaction) {
json_error('لم نتمكن من التحقق التلقائي من الرقم المرجعي. يرجى إرفاق صورة الوصل للمراجعة اليدوية.', 422);
}
$ext = pathinfo($_FILES['receipt']['name'], PATHINFO_EXTENSION) ?: 'jpg';
$filename = $paymentId . '_' . time() . '.' . $ext;
$filepath = $uploadDir . '/' . $filename;
$aiResult = [];
$matchScore = 0.0;
$filepath = null;
if (!move_uploaded_file($_FILES['receipt']['tmp_name'], $filepath)) {
json_error('فشل في حفظ صورة الوصل.', 500);
}
if ($hasReceipt) {
$uploadDir = STORAGE_PATH . '/receipts/' . $tenantId;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0750, true);
}
// 3. AI Analysis of receipt image
$aiResult = analyzeReceipt($filepath, $payment);
$ext = pathinfo($_FILES['receipt']['name'], PATHINFO_EXTENSION) ?: 'jpg';
$filename = $paymentId . '_' . time() . '.' . $ext;
$filepath = $uploadDir . '/' . $filename;
// 4. Smart Match: Use AI-extracted reference to search bank_transactions
$aiExtractedRef = trim($aiResult['reference_number'] ?? '');
if (!empty($aiExtractedRef) && $aiExtractedRef !== 'unknown') {
$stmt = $db->prepare("SELECT * FROM bank_transactions WHERE bank_reference = ? AND is_claimed = 0 LIMIT 1");
$stmt->execute([$aiExtractedRef]);
$aiMatchTransaction = $stmt->fetch();
if (!move_uploaded_file($_FILES['receipt']['tmp_name'], $filepath)) {
json_error('فشل في حفظ صورة الوصل.', 500);
}
if ($aiMatchTransaction) {
$expectedAmount = (float)$payment['amount_jod'];
$actualAmount = (float)$aiMatchTransaction['amount'];
// 4. AI Analysis of receipt image
$aiResult = analyzeReceipt($filepath, $payment);
if (abs($expectedAmount - $actualAmount) < 0.1) {
// AI FOUND THE MATCH!
activateSubscription($db, $payment, $decoded['user_id']);
// 5. Smart Match: Use AI-extracted reference to search bank_transactions
$aiExtractedRef = trim($aiResult['reference_number'] ?? '');
if (!empty($aiExtractedRef) && $aiExtractedRef !== 'unknown') {
$stmt = $db->prepare("SELECT * FROM bank_transactions WHERE bank_reference = ? AND is_claimed = 0 LIMIT 1");
$stmt->execute([$aiExtractedRef]);
$aiMatchTransaction = $stmt->fetch();
$stmt = $db->prepare("UPDATE payment_requests SET status = 'approved', bank_reference = ?, verified_at = NOW() WHERE id = ?");
$stmt->execute([$aiExtractedRef, $paymentId]);
if ($aiMatchTransaction) {
$expectedAmount = (float)$payment['amount_jod'];
$actualAmount = (float)$aiMatchTransaction['amount'];
$stmt = $db->prepare("UPDATE bank_transactions SET is_claimed = 1 WHERE id = ?");
$stmt->execute([$aiMatchTransaction['id']]);
if (abs($expectedAmount - $actualAmount) < 0.1) {
activateSubscription($db, $payment, $decoded['user_id']);
$stmt = $db->prepare("UPDATE payment_requests SET status = 'approved', bank_reference = ?, receipt_image_path = ?, verified_at = NOW() WHERE id = ?");
$stmt->execute([$aiExtractedRef, $filepath, $paymentId]);
$stmt = $db->prepare("UPDATE bank_transactions SET is_claimed = 1 WHERE id = ?");
$stmt->execute([$aiMatchTransaction['id']]);
json_success([
'status' => 'approved',
'auto_verified' => true,
'method' => 'ai_ref_matching',
'message' => 'تم العثور على الحوالة بنجاح وتفعيل الاشتراك آلياً!'
], 'تم تفعيل الاشتراك بنجاح');
json_success([
'status' => 'approved',
'auto_verified' => true,
'method' => 'ai_ref_matching',
'message' => 'تم العثور على الحوالة بنجاح وتفعيل الاشتراك آلياً!'
], 'تم تفعيل الاشتراك بنجاح');
}
}
}
// 6. Calculate match score
$matchScore = calculateMatchScore($aiResult, $payment);
}
// 5. Calculate match score (for legacy manual review fallback)
$matchScore = calculateMatchScore($aiResult, $payment);
// 6. Update payment request with AI data
// 7. Update payment request
$newStatus = $matchScore >= 85.0 ? 'verified' : 'uploaded';
$stmt = $db->prepare("
@@ -150,7 +157,7 @@ try {
$paymentId
]);
// 7. Final attempt activation if high confidence match score
// 8. Final attempt activation if high confidence
if ($matchScore >= 90.0) {
activateSubscription($db, $payment, $decoded['user_id']);
$stmt = $db->prepare("UPDATE payment_requests SET status = 'approved', verified_at = NOW() WHERE id = ?");
@@ -166,8 +173,8 @@ try {
json_success([
'status' => $newStatus,
'match_score' => $matchScore,
'message' => $matchScore >= 70 ? 'تم استلام الوصل بنجاح، جاري المراجعة النهائية.' : 'تم استلام الوصل، بانتظار تأكيد الحوالة من البنك.'
], 'تم رفع الوصل');
'message' => $matchScore >= 70 ? 'تم استلام الوصل بنجاح، جاري المراجعة النهائية.' : 'تم استلام الطلب، بانتظار تأكيد الحوالة من البنك.'
], 'تم الاستلام');
} catch (\Exception $e) {
error_log("Payment Receipt Upload Error: " . $e->getMessage());