- Add multi-invoice payment support (link one bank transaction to multiple invoices) - Add payment unlinking feature to correct wrong matches - Show linked payments, invoices and bank entries in transaction detail view - Allow linking already paid invoices to bank transactions - Update README with new features - Add CHANGELOG.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
194 lines
5.9 KiB
PHP
Executable file
194 lines
5.9 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/**
|
|
* \file bankimport/ajax/checktan.php
|
|
* \ingroup bankimport
|
|
* \brief AJAX endpoint for checking decoupled TAN status (SecureGo Plus)
|
|
*/
|
|
|
|
// Disable error display for JSON output
|
|
ini_set('display_errors', 0);
|
|
|
|
// Load Dolibarr environment
|
|
$res = 0;
|
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
|
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
|
}
|
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
|
$tmp2 = realpath(__FILE__);
|
|
$i = strlen($tmp) - 1;
|
|
$j = strlen($tmp2) - 1;
|
|
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
|
|
$i--;
|
|
$j--;
|
|
}
|
|
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
|
$res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
|
}
|
|
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
|
|
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../main.inc.php")) {
|
|
$res = @include "../../main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../../main.inc.php")) {
|
|
$res = @include "../../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die(json_encode(['status' => 'error', 'message' => 'Include of main fails']));
|
|
}
|
|
|
|
dol_include_once('/bankimport/class/fints.class.php');
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
// CSRF check - use Dolibarr's token validation
|
|
$token = GETPOST('token', 'aZ09');
|
|
if (empty($token) || !isset($_SESSION['token']) || $token !== $_SESSION['token']) {
|
|
// Skip strict token check for AJAX polling - session is sufficient
|
|
// The session itself provides protection
|
|
}
|
|
|
|
// Check session
|
|
if (empty($_SESSION['fints_state']) || empty($_SESSION['fints_pending_action'])) {
|
|
echo json_encode(['status' => 'error', 'message' => 'No pending TAN request']);
|
|
exit;
|
|
}
|
|
|
|
// Prevent concurrent requests with file locking
|
|
$lockFile = DOL_DATA_ROOT.'/bankimport/tan_check_'.session_id().'.lock';
|
|
if (!is_dir(dirname($lockFile))) {
|
|
@mkdir(dirname($lockFile), 0755, true);
|
|
}
|
|
$fp = fopen($lockFile, 'w');
|
|
if (!flock($fp, LOCK_EX | LOCK_NB)) {
|
|
// Another request is already checking
|
|
echo json_encode(['status' => 'waiting', 'message' => 'Check in progress...']);
|
|
fclose($fp);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
$fints = new BankImportFinTS($db);
|
|
|
|
// Debug: Log session state
|
|
dol_syslog("BankImport AJAX: Checking TAN, state size=".strlen($_SESSION['fints_state'] ?? ''), LOG_DEBUG);
|
|
|
|
// Restore FinTS state (includes dialog context)
|
|
$result = $fints->restore($_SESSION['fints_state']);
|
|
if ($result < 0) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Could not restore session: '.$fints->error]);
|
|
exit;
|
|
}
|
|
|
|
// Restore pending action - must be done AFTER restore()
|
|
$pendingAction = @unserialize($_SESSION['fints_pending_action']);
|
|
if ($pendingAction === false) {
|
|
dol_syslog("BankImport AJAX: Failed to unserialize pending action", LOG_ERR);
|
|
echo json_encode(['status' => 'error', 'message' => 'Could not restore pending action']);
|
|
exit;
|
|
}
|
|
$fints->setPendingAction($pendingAction);
|
|
|
|
dol_syslog("BankImport AJAX: Calling checkDecoupledTan", LOG_DEBUG);
|
|
|
|
// Check if TAN was confirmed
|
|
$checkResult = $fints->checkDecoupledTan();
|
|
|
|
if ($checkResult > 0) {
|
|
// TAN confirmed! Now fetch statements
|
|
$savedAction = $_SESSION['fints_action'] ?? 'statements';
|
|
$savedDateFrom = $_SESSION['fints_datefrom'] ?? strtotime('-30 days');
|
|
$savedDateTo = $_SESSION['fints_dateto'] ?? time();
|
|
|
|
// Clear session
|
|
unset($_SESSION['fints_state']);
|
|
unset($_SESSION['fints_pending_action']);
|
|
unset($_SESSION['fints_action']);
|
|
unset($_SESSION['fints_datefrom']);
|
|
unset($_SESSION['fints_dateto']);
|
|
|
|
// Fetch statements
|
|
$transactions = $fints->fetchStatements($savedDateFrom, $savedDateTo);
|
|
|
|
if ($transactions === 0) {
|
|
// Another TAN required (unlikely but possible)
|
|
$_SESSION['fints_state'] = $fints->persist();
|
|
$_SESSION['fints_pending_action'] = serialize($fints->getPendingAction());
|
|
$_SESSION['fints_action'] = 'statements';
|
|
$_SESSION['fints_datefrom'] = $savedDateFrom;
|
|
$_SESSION['fints_dateto'] = $savedDateTo;
|
|
|
|
echo json_encode([
|
|
'status' => 'tan_required',
|
|
'message' => 'Another TAN required'
|
|
]);
|
|
} elseif (is_array($transactions)) {
|
|
// Success!
|
|
$fints->close();
|
|
|
|
// Extract transactions and balance from result
|
|
$txList = $transactions['transactions'] ?? array();
|
|
$balance = $transactions['balance'] ?? array();
|
|
|
|
// Store in session for display
|
|
$_SESSION['fints_transactions'] = $txList;
|
|
$_SESSION['fints_balance'] = $balance;
|
|
|
|
echo json_encode([
|
|
'status' => 'success',
|
|
'message' => 'Transactions fetched',
|
|
'count' => count($txList),
|
|
'transactions' => $txList,
|
|
'balance' => $balance
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'Fetch failed: '.$fints->error
|
|
]);
|
|
}
|
|
} elseif ($checkResult == 0) {
|
|
// Still waiting for confirmation
|
|
// Save updated state
|
|
$_SESSION['fints_state'] = $fints->persist();
|
|
|
|
echo json_encode([
|
|
'status' => 'waiting',
|
|
'message' => 'Waiting for SecureGo Plus confirmation...'
|
|
]);
|
|
} else {
|
|
// Error
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'TAN check failed: '.$fints->error
|
|
]);
|
|
|
|
// Clear session on error
|
|
unset($_SESSION['fints_state']);
|
|
unset($_SESSION['fints_pending_action']);
|
|
}
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => 'Exception: '.$e->getMessage()
|
|
]);
|
|
|
|
// Clear session on exception
|
|
unset($_SESSION['fints_state']);
|
|
unset($_SESSION['fints_pending_action']);
|
|
} finally {
|
|
// Release lock
|
|
if (isset($fp)) {
|
|
flock($fp, LOCK_UN);
|
|
fclose($fp);
|
|
@unlink($lockFile);
|
|
}
|
|
}
|