Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7b8e5e5
agregar consulta de comprobantes y corregir nombre de consulta de rec…
droa6 Jul 30, 2025
3dea3c1
ajustar descripcion del README en el modulo de consulta
droa6 Jul 30, 2025
6e2537e
feat: agregar validacion al crear FE
droa6 Aug 23, 2025
532ca2a
feat: agregar validacion al crear FE y demas documentos
droa6 Aug 23, 2025
c1ee1e9
validacion general de campos para factura electronica, mejorar respue…
droa6 Aug 24, 2025
5270c6d
ultimos cambios para comparar
droa6 Aug 27, 2025
57d295a
ultimos cambios
droa6 Aug 29, 2025
444bbb6
deshacer cambio en tool_reply
droa6 Aug 29, 2025
ce227fc
fix: error en el detalle de respuesta
droa6 Aug 29, 2025
0a04561
fix: error en modulo consultar cuando el comprobante no ha sido enviado
droa6 Aug 29, 2025
967f82a
ultimos cambios, validaciones y calculos automaticos mejorados
droa6 Sep 4, 2025
aa8e7ee
ultimos cambios, manejo de errores y calculo automatico de totales
droa6 Sep 4, 2025
613b71c
fix problema en la validacion de la cedula en el certificado
droa6 Sep 5, 2025
051b586
ajustar la revision de sumatoria de metodo de pago a 0 decimales
droa6 Sep 5, 2025
223b51c
fix: redondear valores
droa6 Sep 6, 2025
c278f0c
fix en genXML redondear valores al maximo permitido
droa6 Sep 6, 2025
4431a91
remover id en tiquetes si no se brinda ambos datos bien
droa6 Sep 6, 2025
3816356
fix para usar el tipo de cambio que se envia
droa6 Sep 9, 2025
88a53e5
fix ignorar diferencias en pagos de menos de 1
droa6 Sep 16, 2025
2eb5219
ultimos cambios, agrega validaciones adicionales
droa6 Sep 22, 2025
5e07107
parametro no agregado al redondear valor
droa6 Sep 23, 2025
ea7dbe8
fix: definir DECIMALES solo si no se ha definido
droa6 Sep 23, 2025
25aa82b
fix uso de comillas inconsistente
droa6 Sep 23, 2025
351bd70
medio de pago auto cuando se pasa -1
droa6 Sep 23, 2025
d259b0b
remover condicion de requerido del codigo cabys para notas de credito
droa6 Sep 24, 2025
f10f714
fix: ultimos cambios
droa6 Oct 6, 2025
81f6dbf
ultimos cambios
droa6 Dec 4, 2025
ceab7a5
latest changes, fix on decimales
droa6 Dec 31, 2025
ad49b61
cambios en el genxml
droa6 Jan 2, 2026
92f7acd
feat: adding latest changes on CRLibre API
droa6 Apr 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions api/contrib/clave/clave.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
*/
function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacion = "", $codigoPais = "", $consecutivo = "", $codigoSeguridad = "")
{
$tipoDocumento = params_get('tipoDocumento');
$tipoCedula = params_get('tipoCedula');
$cedula = params_get('cedula');
$situacion = params_get('situacion');
Expand All @@ -54,9 +53,8 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
$sucursal = params_get("sucursal", "001");
$terminal = params_get("terminal", "00001");

$dia = date('d');
$mes = date('m');
$ano = date('y');
$date = new DateTime('now', new DateTimeZone('America/Costa_Rica'));
$fechaClave = $date->format('dmy');

// Validamos el parametro de cedula
if (!ctype_digit($cedula))
Expand Down Expand Up @@ -85,7 +83,7 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
}

if (!ctype_digit($consecutivo)) {
return "El parametro consecutivo no es numeral";
return "El parametro consecutivo [$consecutivo] no es numeral";
} else if (strlen($consecutivo) < 10) {
$consecutivo = str_pad($consecutivo, 10, "0", STR_PAD_LEFT);
} else if (strlen($consecutivo) > 10) {
Expand Down Expand Up @@ -187,7 +185,7 @@ function getClave($tipoDocumento = "", $tipoCedula = "", $cedula = "", $situacio
}

// Crea la clave
$clave = $codigoPais . $dia . $mes . $ano . $identificacion . $consecutivoFinal . $codSituacion . $codigoSeguridad;
$clave = $codigoPais . $fechaClave . $identificacion . $consecutivoFinal . $codSituacion . $codigoSeguridad;
$arrayResp = array(
"clave" => "$clave",
"consecutivo" => "$consecutivoFinal",
Expand Down
7 changes: 4 additions & 3 deletions api/contrib/consultar/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Modulo para hacer comprobar estado de los comprobantes
# Modulo para hacer comprobar estado de los comprobantes y su estado de recepcion
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Spanish spelling and grammar issues.

The header has spelling errors that should be corrected.

-# Modulo para hacer comprobar estado de los comprobantes y su estado de recepcion
+# Módulo para comprobar el estado de los comprobantes y su estado de recepción
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Modulo para hacer comprobar estado de los comprobantes y su estado de recepcion
# Módulo para comprobar el estado de los comprobantes y su estado de recepción
🧰 Tools
🪛 LanguageTool

[misspelling] ~1-~1: Si es adjetivo o nombre, se escribe con tilde.
Context: # Modulo para hacer comprobar estado de los comp...

(DIACRITICS_02)


[uncategorized] ~1-~1: Probablemente falta un determinante.
Context: # Modulo para hacer comprobar estado de los comprobantes y su estado de rece...

(AI_ES_GGEC_MISSING_DETERMINER)


[uncategorized] ~1-~1: Probablemente falta un signo diacrítico.
Context: ...tado de los comprobantes y su estado de recepcion Se envia: * w : consultar * r : r...

(AI_ES_GGEC_MISSING_ORTHOGRAPHY_DIACRITIC)

🤖 Prompt for AI Agents
In api/contrib/consultar/README.md around line 1, the header contains Spanish
spelling/grammar errors; replace it with a corrected, accented and concise
version such as: "Módulo para comprobar el estado de los comprobantes y su
recepción" (ensure "Módulo" and "recepción" include accents and remove the
redundant "hacer").


Se envia:

* `w` : consultar
* `r` : consultarCom
* `r` : recepcion o comprobante
* `clave` : Clave numerica del comprobante
* `token`: Token funcional para enviar en el header
* `token`: Token obtenido de Hacienda
194 changes: 164 additions & 30 deletions api/contrib/consultar/consultar.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

function consutar()
function consultarRecepcion()
{
$curl = curl_init();
$clave = params_get('clave');
$curl = curl_init();
$clave = params_get('clave');

if ($clave == "" || strlen($clave) == 0)
return "La clave no puede ser en blanco";
Expand All @@ -31,45 +31,179 @@ function consutar()
$url = "https://api.comprobanteselectronicos.go.cr/recepcion/v1/recepcion/";

if ($url == null)
return "Ha ocurrido un error en el client_id.";

curl_setopt_array($curl, array(
CURLOPT_URL => $url . $clave,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
return "El client_id proporcionado (" . params_get("client_id") . ") no es válido. ";

Comment on lines 33 to 35
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo and return a structured error for invalid client_id.

Keep responses consistent and correct the Spanish typo.

-    if ($url == null)
-        return "El client_id proprocionado (".params_get("client_id").") no es válido. ";
+    if ($url == null) {
+        return array(
+            "Status" => 400,
+            "to" => null,
+            "text" => "El client_id proporcionado (" . params_get("client_id") . ") no es válido."
+        );
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($url == null)
return "Ha ocurrido un error en el client_id.";
return "El client_id proprocionado (".params_get("client_id").") no es válido. ";
if ($url == null) {
return array(
"Status" => 400,
"to" => null,
"text" => "El client_id proporcionado (" . params_get("client_id") . ") no es válido."
);
}
🤖 Prompt for AI Agents
In api/contrib/consultar/consultar.php around lines 33 to 35, fix the Spanish
typo ("proprocionado" → "proporcionado") and replace the plain string return
with a structured JSON error response (e.g., an object with keys like "error"
and "message" that includes the provided client_id via params_get("client_id")).
Also set the response Content-Type to application/json and return an appropriate
HTTP status (400) for invalid client_id.

$args = array(
CURLOPT_URL => $url . $clave,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Authorization: Bearer " . params_get('token'),
"Cache-Control: no-cache",
"Content-Type: application/x-www-form-urlencoded",
"Postman-Token: bf8dc171-5bb7-fa54-7416-56c5cda9bf5c"
"Content-Type: application/x-www-form-urlencoded"
),
));
);

curl_setopt_array($curl, $args);
$response = curl_exec($curl);

$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);

$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);

if ($err)
{
if ($err) {
$arrayResp = array(
"Status" => $status,
"to" => $apiTo,
"to" => $url,
"text" => $err
);

return $arrayResp;
}
else
{
$response = json_decode($response);
return $response;

$responseObj = json_decode($body);

if (is_object($responseObj)) {
// si hay respuesta de hacienda, procesar detalle de mensaje
if (isset($responseObj->{'respuesta-xml'})) {

$xmlRespuesta = base64_decode($responseObj->{'respuesta-xml'});
$xmlRespuesta = preg_replace('/>\s+</', '><', $xmlRespuesta);
$startLength = strlen("tiene los siguientes errores: ") + 7;
$startPos = strpos($xmlRespuesta, "tiene los siguientes errores: ", 0);
if (!$startPos) {
// si no hay errores, retornar la respuesta normal
return $responseObj;
}
$endPos = strpos($xmlRespuesta, "</DetalleMensaje>", $startPos);
$detalleMensaje = substr($xmlRespuesta, $startPos + $startLength, $endPos - ($startPos + $startLength + 1));
$separadorMensaje = "&#13;\n";
$responseObj->{'DetalleMensaje'} = explode($separadorMensaje, trim($detalleMensaje, $separadorMensaje));
}

return array(
"Status" => $status,
"to" => $url,
"text" => $responseObj
);
}

if (empty($body)) {
$header_lines = explode("\r\n", trim($headers));
$header_array = [];
foreach ($header_lines as $line) {
if (strpos($line, ':') !== false) {
list($key, $value) = explode(': ', $line, 2);
$header_array[$key] = $value;
}
}
return array(
"Status" => $header_array['x-http-status'],
"to" => $url,
"text" => $header_array['x-error-cause']
);
}

return array(
"Status" => $status,
"to" => $url,
"text" => $body
);
}


function consultarComprobante()
{
$curl = curl_init();
$clave = params_get('clave');

if ($clave == "" || strlen($clave) == 0) {
return "La clave no puede ser en blanco";
}

$url = null;
if (params_get("client_id") == 'api-stag')
$url = "https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/comprobantes/";
else if (params_get("client_id") == 'api-prod')
$url = "https://api.comprobanteselectronicos.go.cr/recepcion/v1/comprobantes/";

if ($url == null) {
return "El client_id proporcionado (" . params_get("client_id") . ") no es válido. ";
}

$args = array(
CURLOPT_URL => $url . $clave,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Authorization: Bearer " . params_get('token')
),
);

curl_setopt_array($curl, $args);
$response = curl_exec($curl);

$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);

$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);

if ($err) {
$arrayResp = array(
"Status" => $status,
"to" => $url,
"text" => $err
);

return tools_reply($arrayResp, true);

}

if (empty($body)) {
$header_lines = explode("\r\n", trim($headers));
$header_array = [];
foreach ($header_lines as $line) {
if (strpos($line, ':') !== false) {
list($key, $value) = explode(': ', $line, 2);
$header_array[$key] = $value;
}
}
return tools_reply(array(
"Status" => $header_array['x-http-status'] ?? $status,
"to" => $url,
"text" => $header_array['x-error-cause']
), true);
Comment on lines +192 to +196
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard x-error-cause access consistently.

Line 193 correctly guards x-http-status with null coalescing, but line 195 still accesses x-error-cause without verification.

Apply this diff:

         return tools_reply(array(
             "Status" => $header_array['x-http-status'] ?? $status,
             "to" => $url,
-            "text" => $header_array['x-error-cause']
+            "text" => $header_array['x-error-cause'] ?? 'Error desconocido'
         ), true);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return tools_reply(array(
"Status" => $header_array['x-http-status'] ?? $status,
"to" => $url,
"text" => $header_array['x-error-cause']
), true);
return tools_reply(array(
"Status" => $header_array['x-http-status'] ?? $status,
"to" => $url,
"text" => $header_array['x-error-cause'] ?? 'Error desconocido'
), true);
🤖 Prompt for AI Agents
In api/contrib/consultar/consultar.php around lines 192 to 196, the return
builds a response but accesses $header_array['x-error-cause'] without guarding
it; change that access to use a null-coalescing fallback (or isset) like
$header_array['x-error-cause'] ?? null (or empty string) so the key is checked
before use and prevents undefined index notices, leaving the rest of the return
structure unchanged.

Comment on lines +192 to +196
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The function consultarComprobante calls tools_reply, which contains an exit statement. This causes the script to terminate abruptly instead of returning a value to the caller.
Severity: HIGH | Confidence: High

🔍 Detailed Analysis

The function consultarComprobante has several paths, including on cURL error or when the response body is empty, where it calls return tools_reply(...). The tools_reply function, via _tools_reply, executes an exit; statement, which terminates the entire PHP script. This prevents the return statement from ever executing and violates the expectation of the calling function tools_proccesPath, which anticipates a return value. Instead of a graceful error response, the request will terminate unexpectedly, making the actual error difficult to diagnose.

💡 Suggested Fix

In consultarComprobante, instead of calling tools_reply, construct the response array and return it directly. This allows the response to propagate back to the calling function tools_proccesPath for proper handling, consistent with the behavior of the sibling function consultarRecepcion.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: api/contrib/consultar/consultar.php#L192-L196

Potential issue: The function `consultarComprobante` has several paths, including on
cURL error or when the response body is empty, where it calls `return tools_reply(...)`.
The `tools_reply` function, via `_tools_reply`, executes an `exit;` statement, which
terminates the entire PHP script. This prevents the `return` statement from ever
executing and violates the expectation of the calling function `tools_proccesPath`,
which anticipates a return value. Instead of a graceful error response, the request will
terminate unexpectedly, making the actual error difficult to diagnose.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 8114258

}

$responseObj = json_decode($body);

return tools_reply(array(
"Status" => $status,
"to" => $url,
"text" => $body
), true);

}

?>
?>
16 changes: 14 additions & 2 deletions api/contrib/consultar/module.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ function consultar_init()
{
$paths = array(
array(
'r' => 'consultarCom',
'action' => 'consutar',
'r' => 'recepcion',
'action' => 'consultarRecepcion',
'access' => 'users_openAccess',
'access_params' => 'accessName',
'params' => array(
array("key" => "clave", "def" => "", "req" => true),
array("key" => "token", "def" => "", "req" => true),
array("key" => "client_id", "def" => "", "req" => true)
),
'file' => 'consultar.php'
),
array(
'r' => 'comprobante',
'action' => 'consultarComprobante',
'access' => 'users_openAccess',
'access_params' => 'accessName',
'params' => array(
Expand Down
2 changes: 1 addition & 1 deletion api/contrib/crlibreall/module.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function crlibreall_init()
array("key" => "total_comprobante", "def" => "", "req" => true),
array("key" => "otros", "def" => "", "req" => false),
array("key" => "detalles", "def" => "", "req" => true),
array("key" => "informacion_referencia", "def" => "", "req" => true),
array("key" => "informacion_referencia", "def" => "", "req" => false),
array("key" => "otrosCargos", "def" => "", "req" => false)
),
'file' => 'genXML.php'
Expand Down
6 changes: 3 additions & 3 deletions api/contrib/facturador/companny_user.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function companny_users_generateSessionKey($idUser, $idMasterUser) {
$q = sprintf("delete from " . db_escape($idMasterUser) . "_master_sessions where idUser='" . db_escape($idUser) . "'");
db_query($q, 0);

$sessionKey = password_hash(time() * rand(0, 1000), PASSWORD_DEFAULT);
$sessionKey = password_hash(time() * rand(0, 1000), PASSWORD_ARGON2ID);

$q = sprintf("INSERT INTO " . db_escape($idMasterUser) . "_master_sessions (idUser, sessionKey, ip, lastAccess) "
. "VALUES('%s', '%s', '%s', '%s')", db_escape($idUser), db_escape($sessionKey), db_escape($_SERVER['REMOTE_ADDR']), time());
Expand Down Expand Up @@ -409,7 +409,7 @@ function companny_users_getMyDetails() {
* Helper function to actually create a new user and register it in the db
*/
function _companny_users_register($compannyUserDets, $idMasterUser) {
$pwd = password_hash($compannyUserDets['pwd'], PASSWORD_DEFAULT);
$pwd = password_hash($compannyUserDets['pwd'], PASSWORD_ARGON2ID);
$q = sprintf("INSERT INTO " . db_escape($idMasterUser) . "_master_users (idMasterUser,fullName, userName, email, about, country, status, timestamp, lastAccess, pwd, avatar,settings)
VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", db_escape($idMasterUser), db_escape($compannyUserDets['fullName']), db_escape($compannyUserDets['userName']), db_escape($compannyUserDets['email']), db_escape(addslashes($compannyUserDets['about'])), db_escape($compannyUserDets['country']), db_escape($compannyUserDets['status']), db_escape($compannyUserDets['timestamp']), db_escape($compannyUserDets['lastAccess']), db_escape($pwd), db_escape($compannyUserDets['avatar']), db_escape($compannyUserDets['settings'])
);
Expand Down Expand Up @@ -511,7 +511,7 @@ function companny_users_recoverPwd() {

$temp = rand(0, 1000) + time();

$compannyUser->pwd = password_hash($temp, PASSWORD_DEFAULT);
$compannyUser->pwd = password_hash($temp, PASSWORD_ARGON2ID);
grace_debug("New tmp pwd: " . $compannyUser->pwd);

Comment on lines 512 to 516
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t email a new plaintext password; issue a time-bound reset token instead.

Current flow generates and emails a password, which is insecure and hard to audit. Prefer a reset token + link.

Minimal improvement if you must keep this flow:

  • Store a forced-change flag and require update on next login.
  • Set very short validity and invalidate all sessions.

Better approach (recommended):

  • Create a reset token with random_bytes(32), store hashed, email a one-time link, and let the user set a new password.

# Update account
Expand Down
Loading
Loading