# Code Citations

## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response === false || $http_code != 200) {
        return array(
            'exito' => false,
            'error' => $curl_error ? $curl_error : "HTTP $http_code"
        );
    }

    $data = json_decode($response, true);
    if (!$data || !isset($data['exito'])) {
        return array('exito' => false, 'error' => 'Respuesta inválida del gateway');
    }

    return $data;
}
?>
```

#### `ecf/reintentar_ecf.php` — Script de reintentos (cron o manual)

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

// Obtener facturas pendientes cuyo próximo intento ya pasó
$sql = mysql_query("SELECT * FROM ecf_cola 
    WHERE estado='Pendiente' 
    AND proximo_intento <= NOW() 
    AND intentos < max_intentos 
    ORDER BY fecha_creacion ASC 
    LIMIT 20") or die(mysql_error());

$procesados = 0;
$exitosos = 0;
$fallidos = 0;

while ($cola = mysql_fetch_assoc($sql)) {
    $id_venta = $cola['id_venta'];
    $intentos = $cola['intentos'] + 1;

    // Reutilizar la lógica de enviar_ecf.php via include interno
    // Obtener datos de la venta
    $sql_v = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'");
    if (mysql_num_rows($sql_v) == 0) {
        mysql_query("UPDATE ecf_cola SET estado='Error', error_msg='Venta no encontrada' WHERE id='$cola[id]'");
        continue;
    }
    $venta = mysql_fetch_assoc($sql_v);

    // Si ya fue firmada por otro medio, marcar como completada
    if ($venta['estado_dgi'] == 'Aceptado' || $venta['estado_dgi'] == 'Firmado') {
        mysql_query("UPDATE ecf_cola SET estado='Enviado' WHERE id='$cola[id]'");
        continue;
    }

    // Reconstruir y enviar (misma lógica que enviar_ecf.php)
    $sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1");
    $empresa = mysql_fetch_assoc($sql_empresa);

    $nombre_cliente = mysql_real_escape_string($venta['cliente']);
    $sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1");
    $cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
        'nombre' => $venta['cliente'], 'cedula' => '', 'telefono' => '', 
        'direccion' => '', 'tipo_ncf' => $venta['tipo_ncf']
    );

    $sql_det = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta'");
    $items = array();
    while ($row = mysql_fetch_assoc($sql_det)) {
        $items[] = array(
            'codigo' => $row['codigo'], 'descripcion' => $row['descripcion'],
            'cantidad' => floatval($row['cantidad']), 'precio' => floatval($row['precio']),
            'total_precio' => floatval($row['total_precio']),
            'tipo_impuesto' => floatval($row['tipo_impuesto']),
            'valor_impuesto' => floatval($row['valor_impuesto']),
            'unidad' => $row['unidad']
        );
    }

    $payload = array(
        'token' => $ECF_TOKEN,
        'empresa' => array('nombre'=>$empresa['nombre'],'rnc'=>$empresa['rnc'],
            'direccion'=>$empresa['direccion'],'telefono'=>$empresa['telefono'],'email'=>$empresa['email']),
        'cliente' => array('nombre'=>$cliente['nombre'],'cedula'=>$cliente['cedula'],
            'telefono'=>$cliente['telefono'],'direccion'=>$cliente['direccion'],'tipo_ncf'=>$cliente['tipo_ncf']),
        'venta' => array('id_venta'=>$venta['id_venta'],'fecha'=>$venta['fecha'],'hora'=>$venta['hora'],
            'ncf'=>$venta['ncf'],'tipo_ncf'=>$venta['tipo_ncf'],'total'=>floatval($venta['total']),
            'impuesto'=>floatval($venta['impuesto']),'descuento'=>floatval($venta['descuento']),
            'precio'=>floatval($venta['precio']),'termino'=>$venta['termino'],
            'efectivo'=>floatval($venta['efectivo']),'tarjeta'=>floatval($venta['monto_tarjeta']),
            'cheque'=>floatval($venta['cheque']),'moneda'=>$venta['moneda']),
        'items' => $items
    );

    $resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

    // Backoff exponencial: cada reintento espera más
    $intervalo = $ECF_RETRY_INTERVAL * pow(2, min($intentos - 1, 5));

    if ($resultado['exito']) {
        $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
        $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
        $trackid    = mysql_real_escape_string($resultado['trackid']);
        $mensaje    = mysql_real_escape_string($resultado['mensaje']);

        mysql_query("UPDATE ventas SET estado_dgi='$estado_dgi', codigo_seguridad_dgi='$codigo_seg',
            trackid_dgi='$trackid', fecha_dgi=NOW(), mensaje_dgi='$mensaje' WHERE id_venta='$id_venta'");
        mysql_query("UPDATE ecf_cola SET estado='Enviado', ultimo_intento=NOW() WHERE id='$cola[id]'");
        $exitosos++;
    } else {
        $error = mysql_real_escape_string($resultado['error']);
        if ($intentos >= $cola['max_intentos']) {
            mysql_query("UPDATE ecf_cola SET estado='Agotado', intentos='$intentos', 
                ultimo_intento=NOW(), error_msg='$error' WHERE id='$cola[id]'");
            mysql_query("UPDATE ventas SET estado_dgi='Error', mensaje_dgi='Reintentos agotados: $error' WHERE id_venta='$id_venta'");
        } else {
            mysql_query("UPDATE ecf_cola SET intentos='$intentos', ultimo_intento=NOW(), 
                proximo_intento=DATE_ADD(NOW(), INTERVAL $intervalo SECOND), error_msg='$error' WHERE id='$cola[id]'");
        }
        $fallidos++;
    }
    $procesados++;
}

// Incluir la función enviar_a_gateway (misma que en enviar_ecf.php)
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);
    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($json)));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIME
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response === false || $http_code != 200) {
        return array(
            'exito' => false,
            'error' => $curl_error ? $curl_error : "HTTP $http_code"
        );
    }

    $data = json_decode($response, true);
    if (!$data || !isset($data['exito'])) {
        return array('exito' => false, 'error' => 'Respuesta inválida del gateway');
    }

    return $data;
}
?>
```

#### `ecf/reintentar_ecf.php` — Script de reintentos (cron o manual)

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

// Obtener facturas pendientes cuyo próximo intento ya pasó
$sql = mysql_query("SELECT * FROM ecf_cola 
    WHERE estado='Pendiente' 
    AND proximo_intento <= NOW() 
    AND intentos < max_intentos 
    ORDER BY fecha_creacion ASC 
    LIMIT 20") or die(mysql_error());

$procesados = 0;
$exitosos = 0;
$fallidos = 0;

while ($cola = mysql_fetch_assoc($sql)) {
    $id_venta = $cola['id_venta'];
    $intentos = $cola['intentos'] + 1;

    // Reutilizar la lógica de enviar_ecf.php via include interno
    // Obtener datos de la venta
    $sql_v = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'");
    if (mysql_num_rows($sql_v) == 0) {
        mysql_query("UPDATE ecf_cola SET estado='Error', error_msg='Venta no encontrada' WHERE id='$cola[id]'");
        continue;
    }
    $venta = mysql_fetch_assoc($sql_v);

    // Si ya fue firmada por otro medio, marcar como completada
    if ($venta['estado_dgi'] == 'Aceptado' || $venta['estado_dgi'] == 'Firmado') {
        mysql_query("UPDATE ecf_cola SET estado='Enviado' WHERE id='$cola[id]'");
        continue;
    }

    // Reconstruir y enviar (misma lógica que enviar_ecf.php)
    $sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1");
    $empresa = mysql_fetch_assoc($sql_empresa);

    $nombre_cliente = mysql_real_escape_string($venta['cliente']);
    $sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1");
    $cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
        'nombre' => $venta['cliente'], 'cedula' => '', 'telefono' => '', 
        'direccion' => '', 'tipo_ncf' => $venta['tipo_ncf']
    );

    $sql_det = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta'");
    $items = array();
    while ($row = mysql_fetch_assoc($sql_det)) {
        $items[] = array(
            'codigo' => $row['codigo'], 'descripcion' => $row['descripcion'],
            'cantidad' => floatval($row['cantidad']), 'precio' => floatval($row['precio']),
            'total_precio' => floatval($row['total_precio']),
            'tipo_impuesto' => floatval($row['tipo_impuesto']),
            'valor_impuesto' => floatval($row['valor_impuesto']),
            'unidad' => $row['unidad']
        );
    }

    $payload = array(
        'token' => $ECF_TOKEN,
        'empresa' => array('nombre'=>$empresa['nombre'],'rnc'=>$empresa['rnc'],
            'direccion'=>$empresa['direccion'],'telefono'=>$empresa['telefono'],'email'=>$empresa['email']),
        'cliente' => array('nombre'=>$cliente['nombre'],'cedula'=>$cliente['cedula'],
            'telefono'=>$cliente['telefono'],'direccion'=>$cliente['direccion'],'tipo_ncf'=>$cliente['tipo_ncf']),
        'venta' => array('id_venta'=>$venta['id_venta'],'fecha'=>$venta['fecha'],'hora'=>$venta['hora'],
            'ncf'=>$venta['ncf'],'tipo_ncf'=>$venta['tipo_ncf'],'total'=>floatval($venta['total']),
            'impuesto'=>floatval($venta['impuesto']),'descuento'=>floatval($venta['descuento']),
            'precio'=>floatval($venta['precio']),'termino'=>$venta['termino'],
            'efectivo'=>floatval($venta['efectivo']),'tarjeta'=>floatval($venta['monto_tarjeta']),
            'cheque'=>floatval($venta['cheque']),'moneda'=>$venta['moneda']),
        'items' => $items
    );

    $resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

    // Backoff exponencial: cada reintento espera más
    $intervalo = $ECF_RETRY_INTERVAL * pow(2, min($intentos - 1, 5));

    if ($resultado['exito']) {
        $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
        $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
        $trackid    = mysql_real_escape_string($resultado['trackid']);
        $mensaje    = mysql_real_escape_string($resultado['mensaje']);

        mysql_query("UPDATE ventas SET estado_dgi='$estado_dgi', codigo_seguridad_dgi='$codigo_seg',
            trackid_dgi='$trackid', fecha_dgi=NOW(), mensaje_dgi='$mensaje' WHERE id_venta='$id_venta'");
        mysql_query("UPDATE ecf_cola SET estado='Enviado', ultimo_intento=NOW() WHERE id='$cola[id]'");
        $exitosos++;
    } else {
        $error = mysql_real_escape_string($resultado['error']);
        if ($intentos >= $cola['max_intentos']) {
            mysql_query("UPDATE ecf_cola SET estado='Agotado', intentos='$intentos', 
                ultimo_intento=NOW(), error_msg='$error' WHERE id='$cola[id]'");
            mysql_query("UPDATE ventas SET estado_dgi='Error', mensaje_dgi='Reintentos agotados: $error' WHERE id_venta='$id_venta'");
        } else {
            mysql_query("UPDATE ecf_cola SET intentos='$intentos', ultimo_intento=NOW(), 
                proximo_intento=DATE_ADD(NOW(), INTERVAL $intervalo SECOND), error_msg='$error' WHERE id='$cola[id]'");
        }
        $fallidos++;
    }
    $procesados++;
}

// Incluir la función enviar_a_gateway (misma que en enviar_ecf.php)
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);
    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($json)));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIME
```


## License: unknown
https://github.com/Shahab1/Messenger-PHP/blob/3a27d0a6d820826ac7c8a123f6f36c5f2af116ec/class/BotApp.class.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response === false || $http_code != 200) {
        return array(
            'exito' => false,
            'error' => $curl_error ? $curl_error : "HTTP $http_code"
        );
    }

    $data = json_decode($response, true);
    if (!$data || !isset($data['exito'])) {
        return array('exito' => false, 'error' => 'Respuesta inválida del gateway');
    }

    return $data;
}
?>
```

#### `ecf/reintentar_ecf.php` — Script de reintentos (cron o manual)

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

// Obtener facturas pendientes cuyo próximo intento ya pasó
$sql = mysql_query("SELECT * FROM ecf_cola 
    WHERE estado='Pendiente' 
    AND proximo_intento <= NOW() 
    AND intentos < max_intentos 
    ORDER BY fecha_creacion ASC 
    LIMIT 20") or die(mysql_error());

$procesados = 0;
$exitosos = 0;
$fallidos = 0;

while ($cola = mysql_fetch_assoc($sql)) {
    $id_venta = $cola['id_venta'];
    $intentos = $cola['intentos'] + 1;

    // Reutilizar la lógica de enviar_ecf.php via include interno
    // Obtener datos de la venta
    $sql_v = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'");
    if (mysql_num_rows($sql_v) == 0) {
        mysql_query("UPDATE ecf_cola SET estado='Error', error_msg='Venta no encontrada' WHERE id='$cola[id]'");
        continue;
    }
    $venta = mysql_fetch_assoc($sql_v);

    // Si ya fue firmada por otro medio, marcar como completada
    if ($venta['estado_dgi'] == 'Aceptado' || $venta['estado_dgi'] == 'Firmado') {
        mysql_query("UPDATE ecf_cola SET estado='Enviado' WHERE id='$cola[id]'");
        continue;
    }

    // Reconstruir y enviar (misma lógica que enviar_ecf.php)
    $sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1");
    $empresa = mysql_fetch_assoc($sql_empresa);

    $nombre_cliente = mysql_real_escape_string($venta['cliente']);
    $sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1");
    $cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
        'nombre' => $venta['cliente'], 'cedula' => '', 'telefono' => '', 
        'direccion' => '', 'tipo_ncf' => $venta['tipo_ncf']
    );

    $sql_det = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta'");
    $items = array();
    while ($row = mysql_fetch_assoc($sql_det)) {
        $items[] = array(
            'codigo' => $row['codigo'], 'descripcion' => $row['descripcion'],
            'cantidad' => floatval($row['cantidad']), 'precio' => floatval($row['precio']),
            'total_precio' => floatval($row['total_precio']),
            'tipo_impuesto' => floatval($row['tipo_impuesto']),
            'valor_impuesto' => floatval($row['valor_impuesto']),
            'unidad' => $row['unidad']
        );
    }

    $payload = array(
        'token' => $ECF_TOKEN,
        'empresa' => array('nombre'=>$empresa['nombre'],'rnc'=>$empresa['rnc'],
            'direccion'=>$empresa['direccion'],'telefono'=>$empresa['telefono'],'email'=>$empresa['email']),
        'cliente' => array('nombre'=>$cliente['nombre'],'cedula'=>$cliente['cedula'],
            'telefono'=>$cliente['telefono'],'direccion'=>$cliente['direccion'],'tipo_ncf'=>$cliente['tipo_ncf']),
        'venta' => array('id_venta'=>$venta['id_venta'],'fecha'=>$venta['fecha'],'hora'=>$venta['hora'],
            'ncf'=>$venta['ncf'],'tipo_ncf'=>$venta['tipo_ncf'],'total'=>floatval($venta['total']),
            'impuesto'=>floatval($venta['impuesto']),'descuento'=>floatval($venta['descuento']),
            'precio'=>floatval($venta['precio']),'termino'=>$venta['termino'],
            'efectivo'=>floatval($venta['efectivo']),'tarjeta'=>floatval($venta['monto_tarjeta']),
            'cheque'=>floatval($venta['cheque']),'moneda'=>$venta['moneda']),
        'items' => $items
    );

    $resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

    // Backoff exponencial: cada reintento espera más
    $intervalo = $ECF_RETRY_INTERVAL * pow(2, min($intentos - 1, 5));

    if ($resultado['exito']) {
        $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
        $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
        $trackid    = mysql_real_escape_string($resultado['trackid']);
        $mensaje    = mysql_real_escape_string($resultado['mensaje']);

        mysql_query("UPDATE ventas SET estado_dgi='$estado_dgi', codigo_seguridad_dgi='$codigo_seg',
            trackid_dgi='$trackid', fecha_dgi=NOW(), mensaje_dgi='$mensaje' WHERE id_venta='$id_venta'");
        mysql_query("UPDATE ecf_cola SET estado='Enviado', ultimo_intento=NOW() WHERE id='$cola[id]'");
        $exitosos++;
    } else {
        $error = mysql_real_escape_string($resultado['error']);
        if ($intentos >= $cola['max_intentos']) {
            mysql_query("UPDATE ecf_cola SET estado='Agotado', intentos='$intentos', 
                ultimo_intento=NOW(), error_msg='$error' WHERE id='$cola[id]'");
            mysql_query("UPDATE ventas SET estado_dgi='Error', mensaje_dgi='Reintentos agotados: $error' WHERE id_venta='$id_venta'");
        } else {
            mysql_query("UPDATE ecf_cola SET intentos='$intentos', ultimo_intento=NOW(), 
                proximo_intento=DATE_ADD(NOW(), INTERVAL $intervalo SECOND), error_msg='$error' WHERE id='$cola[id]'");
        }
        $fallidos++;
    }
    $procesados++;
}

// Incluir la función enviar_a_gateway (misma que en enviar_ecf.php)
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);
    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($json)));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIME
```


## License: unknown
https://github.com/Cress4code/botmusique/blob/11bc0f1b8bb588808a2132c6aaed752914587988/wp-content/themes/botarcep/core/admin/Sonnerieperso.php

```
Now I have a complete understanding of your system. Here's the architecture design for both environments:

---

## Arquitectura: Facturación Electrónica con GAE Digital

### Flujo General

```
POS (PHP 5.6)  →  Servidor Gateway (PHP 8.x)  →  GAE Digital API  →  DGII
     ↑                                                                    |
     └────────────── respuesta (código seguridad + estado) ←──────────────┘
```

---

### 1. Cambios en Base de Datos

Se necesitan nuevas columnas en `ventas` y una tabla de cola para reintentos:

```sql
-- Nuevos campos en ventas para e-CF
ALTER TABLE ventas 
  ADD COLUMN estado_dgi VARCHAR(50) DEFAULT 'Pendiente',
  ADD COLUMN codigo_seguridad_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN trackid_dgi VARCHAR(100) DEFAULT '',
  ADD COLUMN fecha_dgi DATETIME DEFAULT NULL,
  ADD COLUMN mensaje_dgi TEXT;

-- Cola de reintentos para cuando no hay internet
CREATE TABLE ecf_cola (
  id INT AUTO_INCREMENT PRIMARY KEY,
  id_venta INT NOT NULL,
  intentos INT DEFAULT 0,
  max_intentos INT DEFAULT 10,
  ultimo_intento DATETIME DEFAULT NULL,
  proximo_intento DATETIME DEFAULT NULL,
  estado VARCHAR(25) DEFAULT 'Pendiente',  -- Pendiente, Enviado, Error, Agotado
  error_msg TEXT,
  fecha_creacion DATETIME DEFAULT NOW(),
  UNIQUE KEY (id_venta)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

---

### 2. Entorno PHP 5.6 (PC Local del POS)

Se crean estos archivos dentro de `/var/www/html/DAVID/ecf/`:

#### `ecf/config_ecf.php` — Configuración

```php
<?php
// URL del servidor gateway PHP 8.x
$ECF_GATEWAY_URL = "https://TU-SERVIDOR-PHP8.com/ecf_gateway/";

// Token de autenticación entre los dos entornos
$ECF_TOKEN = "TOKEN_SECRETO_AQUI";

// Tiempo máximo de espera en segundos para cURL
$ECF_TIMEOUT = 30;

// Intervalo mínimo entre reintentos (segundos)
$ECF_RETRY_INTERVAL = 300; // 5 minutos

// Máximo de reintentos
$ECF_MAX_REINTENTOS = 10;
?>
```

#### `ecf/enviar_ecf.php` — Se llama después de grabar la venta

Este archivo se invocará desde [pos/grabar.php](pos/grabar.php) justo antes del `echo 1`. Recibe `id_venta` por GET.

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

$id_venta = intval($_GET['id_venta']);
if ($id_venta <= 0) {
    echo json_encode(array("error" => "ID venta inválido"));
    return;
}

// Obtener datos de la venta
$sql_venta = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'") or die(mysql_error());
if (mysql_num_rows($sql_venta) == 0) {
    echo json_encode(array("error" => "Venta no encontrada"));
    return;
}
$venta = mysql_fetch_assoc($sql_venta);

// Obtener datos de la empresa
$sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1") or die(mysql_error());
$empresa = mysql_fetch_assoc($sql_empresa);

// Obtener datos del cliente
$nombre_cliente = mysql_real_escape_string($venta['cliente']);
$sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1") or die(mysql_error());
$cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
    'nombre' => $venta['cliente'],
    'cedula' => '',
    'telefono' => '',
    'direccion' => '',
    'tipo_ncf' => $venta['tipo_ncf']
);

// Obtener detalle de la venta
$sql_detalle = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta' AND estado='Cerrada'") or die(mysql_error());
$items = array();
while ($row = mysql_fetch_assoc($sql_detalle)) {
    $items[] = array(
        'codigo'         => $row['codigo'],
        'descripcion'    => $row['descripcion'],
        'cantidad'       => floatval($row['cantidad']),
        'precio'         => floatval($row['precio']),
        'total_precio'   => floatval($row['total_precio']),
        'costo'          => floatval($row['costo']),
        'tipo_impuesto'  => floatval($row['tipo_impuesto']),
        'valor_impuesto' => floatval($row['valor_impuesto']),
        'unidad'         => $row['unidad']
    );
}

// Obtener datos del NCF
$prefijo_ncf = $venta['tipo_ncf'];
$sql_ncf = mysql_query("SELECT * FROM ncf WHERE tipo='$prefijo_ncf' LIMIT 1");
$ncf_data = ($sql_ncf && mysql_num_rows($sql_ncf) > 0) ? mysql_fetch_assoc($sql_ncf) : array();

// Construir payload
$payload = array(
    'token'   => $ECF_TOKEN,
    'empresa' => array(
        'nombre'    => $empresa['nombre'],
        'rnc'       => $empresa['rnc'],
        'direccion' => $empresa['direccion'],
        'telefono'  => $empresa['telefono'],
        'email'     => $empresa['email']
    ),
    'cliente' => array(
        'nombre'    => $cliente['nombre'],
        'cedula'    => $cliente['cedula'],
        'telefono'  => $cliente['telefono'],
        'direccion' => $cliente['direccion'],
        'tipo_ncf'  => $cliente['tipo_ncf']
    ),
    'venta' => array(
        'id_venta'   => $venta['id_venta'],
        'fecha'      => $venta['fecha'],
        'hora'       => $venta['hora'],
        'ncf'        => $venta['ncf'],
        'tipo_ncf'   => $venta['tipo_ncf'],
        'total'      => floatval($venta['total']),
        'impuesto'   => floatval($venta['impuesto']),
        'descuento'  => floatval($venta['descuento']),
        'precio'     => floatval($venta['precio']),
        'termino'    => $venta['termino'],
        'efectivo'   => floatval($venta['efectivo']),
        'tarjeta'    => floatval($venta['monto_tarjeta']),
        'cheque'     => floatval($venta['cheque']),
        'moneda'     => $venta['moneda']
    ),
    'items' => $items
);

// Enviar vía cURL al gateway PHP 8.x
$resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

if ($resultado['exito']) {
    // Actualizar venta con respuesta de DGII
    $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
    $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
    $trackid    = mysql_real_escape_string($resultado['trackid']);
    $mensaje    = mysql_real_escape_string($resultado['mensaje']);

    mysql_query("UPDATE ventas SET 
        estado_dgi='$estado_dgi',
        codigo_seguridad_dgi='$codigo_seg',
        trackid_dgi='$trackid',
        fecha_dgi=NOW(),
        mensaje_dgi='$mensaje'
        WHERE id_venta='$id_venta'") or die(mysql_error());

    echo json_encode(array("exito" => true, "estado" => $estado_dgi, "codigo_seguridad" => $codigo_seg));
} else {
    // Sin internet o error: encolar para reintentos
    $error_msg = mysql_real_escape_string($resultado['error']);
    
    mysql_query("INSERT INTO ecf_cola (id_venta, intentos, ultimo_intento, proximo_intento, estado, error_msg)
        VALUES ('$id_venta', 1, NOW(), DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND), 'Pendiente', '$error_msg')
        ON DUPLICATE KEY UPDATE intentos=intentos+1, ultimo_intento=NOW(), 
        proximo_intento=DATE_ADD(NOW(), INTERVAL $ECF_RETRY_INTERVAL SECOND),
        error_msg='$error_msg'") or die(mysql_error());

    mysql_query("UPDATE ventas SET estado_dgi='Pendiente', mensaje_dgi='$error_msg' WHERE id_venta='$id_venta'");

    echo json_encode(array("exito" => false, "error" => $resultado['error'], "encolado" => true));
}

/**
 * Envía datos al gateway PHP 8.x vía cURL
 */
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);

    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($json)
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($response === false || $http_code != 200) {
        return array(
            'exito' => false,
            'error' => $curl_error ? $curl_error : "HTTP $http_code"
        );
    }

    $data = json_decode($response, true);
    if (!$data || !isset($data['exito'])) {
        return array('exito' => false, 'error' => 'Respuesta inválida del gateway');
    }

    return $data;
}
?>
```

#### `ecf/reintentar_ecf.php` — Script de reintentos (cron o manual)

```php
<?php
include_once("../php/config.php");
include_once("config_ecf.php");

// Obtener facturas pendientes cuyo próximo intento ya pasó
$sql = mysql_query("SELECT * FROM ecf_cola 
    WHERE estado='Pendiente' 
    AND proximo_intento <= NOW() 
    AND intentos < max_intentos 
    ORDER BY fecha_creacion ASC 
    LIMIT 20") or die(mysql_error());

$procesados = 0;
$exitosos = 0;
$fallidos = 0;

while ($cola = mysql_fetch_assoc($sql)) {
    $id_venta = $cola['id_venta'];
    $intentos = $cola['intentos'] + 1;

    // Reutilizar la lógica de enviar_ecf.php via include interno
    // Obtener datos de la venta
    $sql_v = mysql_query("SELECT * FROM ventas WHERE id_venta='$id_venta'");
    if (mysql_num_rows($sql_v) == 0) {
        mysql_query("UPDATE ecf_cola SET estado='Error', error_msg='Venta no encontrada' WHERE id='$cola[id]'");
        continue;
    }
    $venta = mysql_fetch_assoc($sql_v);

    // Si ya fue firmada por otro medio, marcar como completada
    if ($venta['estado_dgi'] == 'Aceptado' || $venta['estado_dgi'] == 'Firmado') {
        mysql_query("UPDATE ecf_cola SET estado='Enviado' WHERE id='$cola[id]'");
        continue;
    }

    // Reconstruir y enviar (misma lógica que enviar_ecf.php)
    $sql_empresa = mysql_query("SELECT * FROM empresa LIMIT 1");
    $empresa = mysql_fetch_assoc($sql_empresa);

    $nombre_cliente = mysql_real_escape_string($venta['cliente']);
    $sql_cliente = mysql_query("SELECT * FROM clientes WHERE nombre='$nombre_cliente' LIMIT 1");
    $cliente = (mysql_num_rows($sql_cliente) > 0) ? mysql_fetch_assoc($sql_cliente) : array(
        'nombre' => $venta['cliente'], 'cedula' => '', 'telefono' => '', 
        'direccion' => '', 'tipo_ncf' => $venta['tipo_ncf']
    );

    $sql_det = mysql_query("SELECT * FROM detalle_venta WHERE folio='$id_venta'");
    $items = array();
    while ($row = mysql_fetch_assoc($sql_det)) {
        $items[] = array(
            'codigo' => $row['codigo'], 'descripcion' => $row['descripcion'],
            'cantidad' => floatval($row['cantidad']), 'precio' => floatval($row['precio']),
            'total_precio' => floatval($row['total_precio']),
            'tipo_impuesto' => floatval($row['tipo_impuesto']),
            'valor_impuesto' => floatval($row['valor_impuesto']),
            'unidad' => $row['unidad']
        );
    }

    $payload = array(
        'token' => $ECF_TOKEN,
        'empresa' => array('nombre'=>$empresa['nombre'],'rnc'=>$empresa['rnc'],
            'direccion'=>$empresa['direccion'],'telefono'=>$empresa['telefono'],'email'=>$empresa['email']),
        'cliente' => array('nombre'=>$cliente['nombre'],'cedula'=>$cliente['cedula'],
            'telefono'=>$cliente['telefono'],'direccion'=>$cliente['direccion'],'tipo_ncf'=>$cliente['tipo_ncf']),
        'venta' => array('id_venta'=>$venta['id_venta'],'fecha'=>$venta['fecha'],'hora'=>$venta['hora'],
            'ncf'=>$venta['ncf'],'tipo_ncf'=>$venta['tipo_ncf'],'total'=>floatval($venta['total']),
            'impuesto'=>floatval($venta['impuesto']),'descuento'=>floatval($venta['descuento']),
            'precio'=>floatval($venta['precio']),'termino'=>$venta['termino'],
            'efectivo'=>floatval($venta['efectivo']),'tarjeta'=>floatval($venta['monto_tarjeta']),
            'cheque'=>floatval($venta['cheque']),'moneda'=>$venta['moneda']),
        'items' => $items
    );

    $resultado = enviar_a_gateway($payload, $ECF_GATEWAY_URL, $ECF_TIMEOUT);

    // Backoff exponencial: cada reintento espera más
    $intervalo = $ECF_RETRY_INTERVAL * pow(2, min($intentos - 1, 5));

    if ($resultado['exito']) {
        $estado_dgi = mysql_real_escape_string($resultado['estado_dgi']);
        $codigo_seg = mysql_real_escape_string($resultado['codigo_seguridad']);
        $trackid    = mysql_real_escape_string($resultado['trackid']);
        $mensaje    = mysql_real_escape_string($resultado['mensaje']);

        mysql_query("UPDATE ventas SET estado_dgi='$estado_dgi', codigo_seguridad_dgi='$codigo_seg',
            trackid_dgi='$trackid', fecha_dgi=NOW(), mensaje_dgi='$mensaje' WHERE id_venta='$id_venta'");
        mysql_query("UPDATE ecf_cola SET estado='Enviado', ultimo_intento=NOW() WHERE id='$cola[id]'");
        $exitosos++;
    } else {
        $error = mysql_real_escape_string($resultado['error']);
        if ($intentos >= $cola['max_intentos']) {
            mysql_query("UPDATE ecf_cola SET estado='Agotado', intentos='$intentos', 
                ultimo_intento=NOW(), error_msg='$error' WHERE id='$cola[id]'");
            mysql_query("UPDATE ventas SET estado_dgi='Error', mensaje_dgi='Reintentos agotados: $error' WHERE id_venta='$id_venta'");
        } else {
            mysql_query("UPDATE ecf_cola SET intentos='$intentos', ultimo_intento=NOW(), 
                proximo_intento=DATE_ADD(NOW(), INTERVAL $intervalo SECOND), error_msg='$error' WHERE id='$cola[id]'");
        }
        $fallidos++;
    }
    $procesados++;
}

// Incluir la función enviar_a_gateway (misma que en enviar_ecf.php)
function enviar_a_gateway($payload, $url, $timeout) {
    $json = json_encode($payload);
    $ch = curl_init($url . "recibir.php");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($json)));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIME
```

