feat: Dashboard administrativo, integración con API UCRM y optimización de Stripe
- Implementación de Dashboard profesional en public.php con CRUD de instaladores. - Sincronización con la API de UCRM para selección y validación automática de administradores. - Soporte para creación selectiva de clientes en Stripe (Etiqueta "CREAR CLIENTE STRIPE" vs "CREAR CLABE STRIPE"). - Mejora en la lógica de notificaciones de tareas (nuevo prefijo [CLIENTE-SIN-WHATSAPP]). - Refactorización integral de fachadas y saneamiento de código muerto. - Sistema de Modo Oscuro persistente con UI refinada.
This commit is contained in:
parent
5425659428
commit
b0b56a59ce
0
CHANGELOG.md
Normal file → Executable file
0
CHANGELOG.md
Normal file → Executable file
1067
Callbell Public API v1.postman_collection.json
Executable file
1067
Callbell Public API v1.postman_collection.json
Executable file
File diff suppressed because it is too large
Load Diff
@ -1 +1,28 @@
|
||||
{"ipserver":"172.16.5.134","apitoken":"gvcnIJqXdUjneVSjhl6THLlQcYXJyIFCcwHKVba2bvIrNraanCTb5VeoWuJ0TFZ9","unmsApiToken":"079c28f5-888c-457d-bd7a-0a4202590f75","tokencallbell":"g8thcZkXGd3xBj2g3TtYNYFMH1fuesbJ.b6a940ea7d78cf6c9e42f067b21c8ddf96e9fa2a9e307bfd0c7c7c4d7fa38f79","tokenstripe":"sk_test_51OkG0REFY1WEUtgRH6UxBK5pu80Aq5Iy8EcdPnf0cOWzuVLQTpyLCd7CbPzqMsWMafZOHElCxhEHF7g8boURjWlJ00tBwE0W1M","hostServerFTP":"siip.mx","usernameServerFTP":"siip0001","passServerFTP":"$spGiT,[wa)n","ipPuppeteer":"172.16.5.134","portPuppeteer":"3000","idPaymentAdminCRM":"1180","cashPaymentMethodId":false,"courtesyPaymentMethodId":false,"bankTransferPaymentMethodId":true,"paypalPaymentMethodId":true,"creditCardPaypalPaymentMethodId":true,"creditCardStripePaymentMethodId":true,"stripeSubscriptionCreditCardPaymentMethodId":true,"paypalSubscriptionPaymentMethodId":true,"mercadopagoPaymentMethodId":true,"checkPaymentMethodId":true,"customPaymentMethodId":true,"notificationTypeText":true,"installersDataWhatsApp":"{\r\n \"instaladores\": [\r\n {\r\n \"id\": 1019,\r\n \"nombre\": \"Mucio Robledo\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1173,\r\n \"nombre\": \"Ángel Arvizu\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1172,\r\n \"nombre\": \"Juan Rostro\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1015,\r\n \"nombre\": \"Daniel Humberto\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1131,\r\n \"nombre\": \"Gricelda Avalos\",\r\n \"whatsapp\": \"4181817609\"\r\n }\r\n ]\r\n}","debugMode":true,"logging_level":true}
|
||||
{
|
||||
"ipserver": "172.16.5.134",
|
||||
"apitoken": "gvcnIJqXdUjneVSjhl6THLlQcYXJyIFCcwHKVba2bvIrNraanCTb5VeoWuJ0TFZ9",
|
||||
"unmsApiToken": "079c28f5-888c-457d-bd7a-0a4202590f75",
|
||||
"tokencallbell": "g8thcZkXGd3xBj2g3TtYNYFMH1fuesbJ.b6a940ea7d78cf6c9e42f067b21c8ddf96e9fa2a9e307bfd0c7c7c4d7fa38f79",
|
||||
"tokenstripe": "sk_test_51OkG0REFY1WEUtgRH6UxBK5pu80Aq5Iy8EcdPnf0cOWzuVLQTpyLCd7CbPzqMsWMafZOHElCxhEHF7g8boURjWlJ00tBwE0W1M",
|
||||
"hostServerFTP": "siip.mx",
|
||||
"usernameServerFTP": "siip0001",
|
||||
"passServerFTP": "$spGiT,[wa)n",
|
||||
"ipPuppeteer": "172.16.5.134",
|
||||
"portPuppeteer": "3000",
|
||||
"idPaymentAdminCRM": "1180",
|
||||
"cashPaymentMethodId": false,
|
||||
"courtesyPaymentMethodId": false,
|
||||
"bankTransferPaymentMethodId": true,
|
||||
"paypalPaymentMethodId": true,
|
||||
"creditCardPaypalPaymentMethodId": true,
|
||||
"creditCardStripePaymentMethodId": true,
|
||||
"stripeSubscriptionCreditCardPaymentMethodId": true,
|
||||
"paypalSubscriptionPaymentMethodId": true,
|
||||
"mercadopagoPaymentMethodId": true,
|
||||
"checkPaymentMethodId": true,
|
||||
"customPaymentMethodId": true,
|
||||
"notificationTypeText": true,
|
||||
"installersDataWhatsApp": "{\r\n \"instaladores\": [\r\n {\r\n \"id\": 1019,\r\n \"nombre\": \"Mucio Robledo\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1173,\r\n \"nombre\": \"\u00c1ngel Arvizu\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1172,\r\n \"nombre\": \"Juan Rostro\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1015,\r\n \"nombre\": \"Daniel Humberto\",\r\n \"whatsapp\": \"4181878106\"\r\n },\r\n {\r\n \"id\": 1131,\r\n \"nombre\": \"Gricelda Avalos\",\r\n \"whatsapp\": \"4181817609\"\r\n }\r\n ]\r\n}",
|
||||
"debugMode": true,
|
||||
"logging_level": true
|
||||
}
|
||||
@ -1 +1 @@
|
||||
{"twilioAccountSid":null,"twilioAuthToken":null,"twilioSmsNumber":null,"displayedErrors":"Not valid configuration: Twilio Account SID must be configured\nNot valid configuration: Twilio Auth Token must be configured","event_client_add":null,"event_client_archive":null,"event_client_delete":null,"event_client_edit":null,"event_invoice_add":null,"event_invoice_add_draft":null,"event_invoice_draft_approved":null,"event_invoice_delete":null,"event_invoice_edit":null,"event_payment_add":null,"event_payment_delete":null,"event_payment_edit":null,"event_payment_unmatch":null,"event_service_activate":null,"event_service_add":null,"event_service_archive":null,"event_service_end":null,"event_service_postpone":null,"event_service_suspend_cancel":null,"event_service_suspend":null,"event_invoice_near_due":null,"event_invoice_overdue":null,"event_client_message":null}
|
||||
{"twilioAccountSid":null,"twilioAuthToken":null,"twilioSmsNumber":null,"displayedErrors":null}
|
||||
1813
data/plugin.log
1813
data/plugin.log
File diff suppressed because one or more lines are too long
708
public.php
708
public.php
@ -1,147 +1,577 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginLogManager;
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
|
||||
require_once 'main.php';
|
||||
// Solo permitir acceso si estamos en un entorno UCRM válido
|
||||
if (!file_exists(__DIR__ . '/data/config.json')) {
|
||||
die('Acceso denegado o configuración no encontrada.');
|
||||
}
|
||||
|
||||
// Función para imprimir una tabla
|
||||
// function imprimirTabla()
|
||||
// {
|
||||
// echo '
|
||||
// <table border="1">
|
||||
// <tr>
|
||||
// <th>Archivos PDF</th>
|
||||
// <th>Resultado</th>
|
||||
// </tr>
|
||||
// ';
|
||||
$configManager = PluginConfigManager::create();
|
||||
$config = $configManager->loadConfig();
|
||||
$logger = PluginLogManager::create();
|
||||
$ucrmApi = UcrmApi::create();
|
||||
|
||||
// borrarArchivosPDFWordpress();
|
||||
// echo '</table>
|
||||
// ';
|
||||
// }
|
||||
// Obtener administradores de UCRM para el selector
|
||||
$admins = [];
|
||||
try {
|
||||
$adminsRaw = $ucrmApi->get('users/admins');
|
||||
foreach ($adminsRaw as $admin) {
|
||||
$admins[] = [
|
||||
'id' => $admin['id'],
|
||||
'nombre' => trim(($admin['firstName'] ?? '') . ' ' . ($admin['lastName'] ?? ''))
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$logger->error('Error al obtener administradores de UCRM: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// function borrarArchivosPDFWordpress()
|
||||
// {
|
||||
// $log = PluginLogManager::create(); //Initialize Logger
|
||||
// $configManager = PluginConfigManager::create();
|
||||
// $config = $configManager->loadConfig();
|
||||
|
||||
// // Configuración de conexión FTP
|
||||
// $ftp_server = $config['hostServerFTP'];
|
||||
// $ftp_username = $config['usernameServerFTP'];
|
||||
// $ftp_password = $config['passServerFTP'];
|
||||
// $remote_folder = "/public_html/wp/wp-content/uploads/img/";
|
||||
|
||||
// // Conexión FTP
|
||||
// $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP");
|
||||
// $login = ftp_login($ftp_conn, $ftp_username, $ftp_password);
|
||||
// ftp_pasv($ftp_conn, true);
|
||||
|
||||
// // Verificar conexión y login
|
||||
// if ($ftp_conn && $login) {
|
||||
// $log->appendLog("Conexión FTP exitosa" . PHP_EOL);
|
||||
|
||||
// // Obtener lista de archivos en la carpeta
|
||||
// $files = ftp_nlist($ftp_conn, $remote_folder);
|
||||
// if (is_array($files)) {
|
||||
// // Eliminar la ruta del directorio de los archivos
|
||||
// $files = array_map(function($file) use ($remote_folder) {
|
||||
// return str_replace($remote_folder, '', $file);
|
||||
// }, $files);
|
||||
|
||||
// // Obtener fechas de modificación
|
||||
// $filesWithTime = [];
|
||||
// foreach ($files as $file) {
|
||||
// $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file);
|
||||
// if ($modifiedTime != -1) {
|
||||
// $filesWithTime[$file] = $modifiedTime;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Ordenar archivos por fecha de modificación, más recientes primero
|
||||
// arsort($filesWithTime);
|
||||
|
||||
// // Obtener los archivos a eliminar (todos menos los 5 más recientes)
|
||||
// $filesToDelete = array_slice(array_keys($filesWithTime), 5);
|
||||
|
||||
// // Eliminar archivos antiguos
|
||||
// foreach ($filesToDelete as $file) {
|
||||
// if (ftp_delete($ftp_conn, $remote_folder . $file)) {
|
||||
// echo '<tr><td>' . $file . '</td><td>Archivo borrado</td></tr>';
|
||||
// } else {
|
||||
// echo '<tr><td>' . $file . '</td><td>Error al borrar archivo</td></tr>';
|
||||
// }
|
||||
// }
|
||||
// $log->appendLog("Archivos eliminados" . PHP_EOL);
|
||||
// } else {
|
||||
// $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL);
|
||||
// }
|
||||
// // Cerrar conexión FTP
|
||||
// ftp_close($ftp_conn);
|
||||
// } else {
|
||||
// $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Verificar si se ha enviado una solicitud POST
|
||||
// if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["pintar"])) {
|
||||
// // Llamar a la función para imprimir la tabla
|
||||
// imprimirTabla();
|
||||
|
||||
// }
|
||||
|
||||
// // Contenido HTML para el formulario con el botón
|
||||
// $html = '
|
||||
// <!DOCTYPE html>
|
||||
// <html lang="es">
|
||||
// <head>
|
||||
// <meta charset="UTF-8">
|
||||
// <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
// <style>
|
||||
// body {
|
||||
// font-family: Arial, sans-serif;
|
||||
// background-color: #f0f0f0;
|
||||
// margin: 0;
|
||||
// padding: 20px;
|
||||
// }
|
||||
// h1 {
|
||||
// text-align: center;
|
||||
// color: #333;
|
||||
// }
|
||||
// table {
|
||||
// width: 100%;
|
||||
// border-collapse: collapse;
|
||||
// border: 2px solid #333;
|
||||
// margin-bottom: 20px;
|
||||
// }
|
||||
// th, td {
|
||||
// padding: 10px;
|
||||
// border: 1px solid #999;
|
||||
// }
|
||||
// th {
|
||||
// background-color: #f2f2f2;
|
||||
// }
|
||||
// tr:nth-child(even) {
|
||||
// background-color: #f9f9f9;
|
||||
// }
|
||||
|
||||
// .container {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
|
||||
// }
|
||||
// </style>
|
||||
// </head>
|
||||
// <body>
|
||||
// <h1>Limpieza de archivos PDF de comprobantes de pago subidos a Wordpress para su envío</h1>
|
||||
// <div class="container">
|
||||
// <form method="post">
|
||||
// <button type="submit" name="pintar">Ejecutar borrado de archivos</button>
|
||||
// </form>
|
||||
// </div>
|
||||
// Manejar actualizaciones del JSON de instaladores
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_installers') {
|
||||
$installersJson = $_POST['installers_data'] ?? '';
|
||||
|
||||
// </body>
|
||||
// </html>
|
||||
// ';
|
||||
// echo $html;
|
||||
// Validar que sea un JSON válido
|
||||
if (json_decode($installersJson) !== null) {
|
||||
$configPath = __DIR__ . '/data/config.json';
|
||||
$currentConfig = json_decode(file_get_contents($configPath), true);
|
||||
$currentConfig['installersDataWhatsApp'] = $installersJson;
|
||||
|
||||
if (file_put_contents($configPath, json_encode($currentConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Error al guardar los datos.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Cargar instaladores actuales
|
||||
$installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instaladores":[]}', true);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIIP - Dashboard Admin</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-hover: #1d4ed8;
|
||||
--bg-body: #f8fafc;
|
||||
--bg-card: #ffffff;
|
||||
--text-main: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--sidebar-width: 260px;
|
||||
--danger: #ef4444;
|
||||
--success: #22c55e;
|
||||
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg-body: #0f172a;
|
||||
--bg-card: #1e293b;
|
||||
--text-main: #f1f5f9;
|
||||
--text-muted: #94a3b8;
|
||||
--border: #334155;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-body);
|
||||
color: var(--text-main);
|
||||
transition: var(--transition);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
background: var(--bg-card);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 2rem 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
margin-bottom: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
margin-bottom: 8px;
|
||||
transition: var(--transition);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-link.active, .nav-link:hover {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
flex: 1;
|
||||
padding: 2rem 3rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.title-section h1 {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.title-section p {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-main);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.theme-toggle svg {
|
||||
position: absolute;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-toggle .moon-icon { transform: translateY(40px); }
|
||||
[data-theme="dark"] .theme-toggle .sun-icon { transform: translateY(-40px); }
|
||||
|
||||
/* Card / Table */
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 20px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 1rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 1.25rem 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 100px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.edit-btn { background: rgba(34, 197, 94, 0.1); color: var(--success); }
|
||||
.delete-btn { background: rgba(239, 68, 68, 0.1); color: var(--danger); }
|
||||
|
||||
.edit-btn:hover { background: var(--success); color: white; }
|
||||
.delete-btn:hover { background: var(--danger); color: white; }
|
||||
|
||||
/* Modal */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--bg-card);
|
||||
width: 90%;
|
||||
max-width: 480px;
|
||||
padding: 2rem;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-body);
|
||||
color: var(--text-main);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
#toast {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 150%);
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 12px;
|
||||
background: var(--text-main);
|
||||
color: var(--bg-body);
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2);
|
||||
transition: var(--transition);
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
#toast.show { transform: translate(-50%, 0); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="logo">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m3 21 1.9-1.9a8.5 8.5 0 1 0-3.4-3.4z"/><path d="M9 13h1"/></svg>
|
||||
<span>SIIP CRM</span>
|
||||
</div>
|
||||
<nav>
|
||||
<a href="#" class="nav-link active">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
|
||||
<span>Instaladores</span>
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main class="main-content">
|
||||
<header class="header">
|
||||
<div class="title-section">
|
||||
<h1>Gestión de Equipo</h1>
|
||||
<p>Administra los técnicos registrados en el sistema</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn theme-toggle" id="themeBtn" title="Cambiar tema">
|
||||
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
|
||||
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
|
||||
</button>
|
||||
<button class="btn btn-primary" id="addBtn">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
Nuevo Instalador
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="card">
|
||||
<div class="table-container">
|
||||
<table id="installersTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID UCRM</th>
|
||||
<th>Nombre Completo</th>
|
||||
<th>WhatsApp</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Modal Form -->
|
||||
<div class="modal-overlay" id="modalOverlay">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Registro de Instalador</h2>
|
||||
</div>
|
||||
<form id="installerForm">
|
||||
<input type="hidden" id="editIndex">
|
||||
|
||||
<div class="form-group" id="adminSelectGroup">
|
||||
<label>Seleccionar Administrador UCRM</label>
|
||||
<select id="adminSelect" class="form-control">
|
||||
<option value="">-- Seleccionar de UCRM --</option>
|
||||
<?php foreach ($admins as $admin): ?>
|
||||
<option value="<?= $admin['id'] ?>" data-name="<?= $admin['nombre'] ?>"><?= $admin['nombre'] ?> (ID: <?= $admin['id'] ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>ID de Usuario (Automático)</label>
|
||||
<input type="number" id="installerId" class="form-control" required readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nombre Completo</label>
|
||||
<input type="text" id="installerName" class="form-control" required readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>WhatsApp (Sin +)</label>
|
||||
<input type="text" id="installerWhatsApp" class="form-control" required placeholder="Ej. 524181234567">
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" onclick="closeModal()">Cancelar</button>
|
||||
<button type="submit" class="btn btn-primary">Confirmar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast"></div>
|
||||
|
||||
<script>
|
||||
const store = {
|
||||
installers: <?php echo json_encode($installersData['instaladores']); ?>,
|
||||
theme: localStorage.getItem('theme') || 'light'
|
||||
};
|
||||
|
||||
document.documentElement.setAttribute('data-theme', store.theme);
|
||||
|
||||
function renderTable() {
|
||||
const tbody = document.querySelector('#installersTable tbody');
|
||||
tbody.innerHTML = store.installers.map((inst, index) => `
|
||||
<tr>
|
||||
<td><span class="badge">#${inst.id}</span></td>
|
||||
<td><strong>${inst.nombre}</strong></td>
|
||||
<td>${inst.whatsapp}</td>
|
||||
<td>
|
||||
<div class="action-btns">
|
||||
<button class="action-btn edit-btn" onclick="editInstaller(${index})" title="Editar">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</button>
|
||||
<button class="action-btn delete-btn" onclick="deleteInstaller(${index})" title="Eliminar">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function saveToServer() {
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'save_installers');
|
||||
formData.append('installers_data', JSON.stringify({ instaladores: store.installers }));
|
||||
|
||||
try {
|
||||
const res = await fetch(window.location.href, { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
if (data.success) showToast('Éxito: Cambios guardados en config.json');
|
||||
else showToast('Error al guardar datos', true);
|
||||
} catch (e) { showToast('Fallo de conexión', true); }
|
||||
}
|
||||
|
||||
function openModal(index = null) {
|
||||
const form = document.getElementById('installerForm');
|
||||
form.reset();
|
||||
const idxField = document.getElementById('editIndex');
|
||||
idxField.value = index !== null ? index : '';
|
||||
document.getElementById('modalTitle').textContent = index !== null ? 'Editar Datos' : 'Registrar desde UCRM';
|
||||
|
||||
// Si es edición, ocultamos el selector de admins para evitar cambiar el ID accidentalmente
|
||||
document.getElementById('adminSelectGroup').style.display = index !== null ? 'none' : 'block';
|
||||
|
||||
if (index !== null) {
|
||||
const inst = store.installers[index];
|
||||
document.getElementById('installerId').value = inst.id;
|
||||
document.getElementById('installerName').value = inst.nombre;
|
||||
document.getElementById('installerWhatsApp').value = inst.whatsapp;
|
||||
}
|
||||
|
||||
document.getElementById('modalOverlay').style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeModal() { document.getElementById('modalOverlay').style.display = 'none'; }
|
||||
|
||||
// Manejar selección de admin
|
||||
document.getElementById('adminSelect').onchange = (e) => {
|
||||
const opt = e.target.options[e.target.selectedIndex];
|
||||
if (opt.value) {
|
||||
document.getElementById('installerId').value = opt.value;
|
||||
document.getElementById('installerName').value = opt.dataset.name;
|
||||
} else {
|
||||
document.getElementById('installerId').value = '';
|
||||
document.getElementById('installerName').value = '';
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('installerForm').onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const index = document.getElementById('editIndex').value;
|
||||
const id = parseInt(document.getElementById('installerId').value);
|
||||
|
||||
// Validar existencia si es nuevo
|
||||
if (index === '' && store.installers.some(inst => inst.id === id)) {
|
||||
showToast('Este instalador ya está registrado', true);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
id: id,
|
||||
nombre: document.getElementById('installerName').value,
|
||||
whatsapp: document.getElementById('installerWhatsApp').value
|
||||
};
|
||||
|
||||
if (index === '') store.installers.push(data);
|
||||
else store.installers[index] = data;
|
||||
|
||||
closeModal();
|
||||
renderTable();
|
||||
saveToServer();
|
||||
};
|
||||
|
||||
function editInstaller(index) { openModal(index); }
|
||||
function deleteInstaller(index) {
|
||||
if (confirm('¿Eliminar a este instalador?')) {
|
||||
store.installers.splice(index, 1);
|
||||
renderTable();
|
||||
saveToServer();
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('themeBtn').onclick = () => {
|
||||
store.theme = store.theme === 'light' ? 'dark' : 'light';
|
||||
document.documentElement.setAttribute('data-theme', store.theme);
|
||||
localStorage.setItem('theme', store.theme);
|
||||
};
|
||||
|
||||
document.getElementById('addBtn').onclick = () => openModal();
|
||||
|
||||
function showToast(msg, isError = false) {
|
||||
const toast = document.getElementById('toast');
|
||||
toast.textContent = msg;
|
||||
toast.style.background = isError ? 'var(--danger)' : '#1e293b';
|
||||
toast.classList.add('show');
|
||||
setTimeout(() => toast.classList.remove('show'), 3000);
|
||||
}
|
||||
|
||||
renderTable();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -13,140 +13,150 @@ class PluginData extends UcrmData
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $twilioAccountSid;
|
||||
public $ipserver;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $apitoken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $unmsApiToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $tokencallbell;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $tokenstripe;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $hostServerFTP;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $usernameServerFTP;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $passServerFTP;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $ipPuppeteer;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $portPuppeteer;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $idPaymentAdminCRM;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $cashPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $courtesyPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $bankTransferPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $paypalPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $creditCardPaypalPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $creditCardStripePaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $stripeSubscriptionCreditCardPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $paypalSubscriptionPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $mercadopagoPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $checkPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $customPaymentMethodId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $notificationTypeText;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $installersDataWhatsApp;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $debugMode;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $logging_level;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $twilioAccountSid;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $twilioAuthToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $twilioSmsNumber;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $displayedErrors;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_client_add;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_client_archive;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_client_delete;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_client_edit;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_add;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_add_draft;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_draft_approved;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_delete;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_edit;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_payment_add;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_payment_delete;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_payment_edit;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_payment_unmatch;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_activate;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_add;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_archive;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_end;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_postpone;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_suspend_cancel;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_service_suspend;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_near_due;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_invoice_overdue;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $event_client_message;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $logging_level;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,112 +4,49 @@ declare(strict_types=1);
|
||||
|
||||
namespace SmsNotifier\Facade;
|
||||
|
||||
use Attribute;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use SmsNotifier\Data\NotificationData;
|
||||
use SmsNotifier\Factory\MessageTextFactory;
|
||||
use SmsNotifier\Service\Logger;
|
||||
use SmsNotifier\Service\SmsNumberProvider;
|
||||
use GuzzleHttp\Client;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
|
||||
|
||||
/*
|
||||
* send message to client's number
|
||||
*/
|
||||
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
abstract class AbstractStripeOperationsFacade
|
||||
{
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var MessageTextFactory
|
||||
*/
|
||||
protected $messageTextFactory;
|
||||
|
||||
/**
|
||||
* @var SmsNumberProvider
|
||||
*/
|
||||
protected $clientPhoneNumber;
|
||||
/**
|
||||
* @var UcrmApi
|
||||
*/
|
||||
protected $ucrmApi;
|
||||
|
||||
protected $stripeCustomAttributeID;
|
||||
protected $clabeInterbancariaBanamexID;
|
||||
|
||||
public function __construct(
|
||||
Logger $logger,
|
||||
MessageTextFactory $messageTextFactory,
|
||||
SmsNumberProvider $clientPhoneNumber
|
||||
) {
|
||||
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber) {
|
||||
$this->logger = $logger;
|
||||
$this->messageTextFactory = $messageTextFactory;
|
||||
$this->clientPhoneNumber = $clientPhoneNumber;
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a PaymentIntent in Stripe for a Customer
|
||||
*/
|
||||
public function createPaymentIntent($event_json)
|
||||
{
|
||||
$this->logger->info("Iniciando creación de PaymentIntent en Stripe." . PHP_EOL);
|
||||
|
||||
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
|
||||
$config = $configManager->loadConfig();
|
||||
$StripeToken = $config['tokenstripe'];
|
||||
$idPaymentAdmin = $config['idPaymentAdminCRM']; //ID del administrador que crea el PaymentIntent
|
||||
$stripe = new \Stripe\StripeClient($StripeToken); //Token de clave privada para la API de Stripe
|
||||
|
||||
// Asegurarse de que 'customer' esté presente en la estructura del evento
|
||||
if (!isset($event_json['data']['object']['customer'])) {
|
||||
$this->logger->info("Error: Invalid event structure. Customer ID not found." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$customerId = $event_json['data']['object']['customer'];
|
||||
if (is_null($customerId)) {
|
||||
$this->logger->info("Error: Customer ID is null." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validar que net_amount está presente
|
||||
if (!isset($event_json['data']['object']['net_amount'])) {
|
||||
$this->logger->info("Error: net_amount not found in event data." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
public function createPaymentIntent(array $eventJson): void {
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
$stripe = new StripeClient($config['tokenstripe']);
|
||||
|
||||
$amount = $event_json['data']['object']['net_amount'];
|
||||
//convertir en positivo
|
||||
if ($event_json['data']['object']['net_amount'] < 0) {
|
||||
$this->logger->warning("Error: net_amount es negativo." . PHP_EOL);
|
||||
$customer = $eventJson['data']['object']['customer'] ?? null;
|
||||
$amount = $eventJson['data']['object']['net_amount'] ?? 0;
|
||||
|
||||
if (!$customer || $amount <= 0) {
|
||||
$this->logger->warning("Datos inválidos para PaymentIntent: Customer=$customer, Amount=$amount");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//imprimir la cantidad del PaymentIntent
|
||||
$this->logger->info("Cantidad del PaymentIntent: " . $amount . PHP_EOL);
|
||||
|
||||
try {
|
||||
// Obtener información del cliente desde Stripe
|
||||
$stripeQuery = $stripe->customers->retrieve($customerId, []);
|
||||
$UCRM_clientID = $stripeQuery['metadata']['ucrm_client_id']; // ID del cliente en Ubiquiti UISP
|
||||
$stripeCustomer = $stripe->customers->retrieve($customer);
|
||||
$ucrmClientId = $stripeCustomer->metadata->ucrm_client_id ?? null;
|
||||
|
||||
// Obtener información del administrador actual
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$currentUserAdmin = $this->ucrmApi->get('users/admins', []);
|
||||
|
||||
// Crear PaymentIntent
|
||||
$paymentIntent = $stripe->paymentIntents->create([
|
||||
'amount' => $amount,
|
||||
$pi = $stripe->paymentIntents->create([
|
||||
'amount' => (int)$amount,
|
||||
'currency' => 'mxn',
|
||||
'customer' => $customerId,
|
||||
'customer' => $customer,
|
||||
'payment_method_types' => ['customer_balance'],
|
||||
'payment_method_data' => ['type' => 'customer_balance'],
|
||||
'confirm' => true,
|
||||
@ -120,423 +57,189 @@ abstract class AbstractStripeOperationsFacade
|
||||
],
|
||||
],
|
||||
'metadata' => [
|
||||
'clientId' => $UCRM_clientID, // ID del cliente en Ubiquiti
|
||||
'clientId' => $ucrmClientId,
|
||||
'createdBy' => 'UCRM',
|
||||
'paymentType' => 'card.one_time',
|
||||
'signedInAdminId' => $idPaymentAdmin, // ID del administrador que crea el PaymentIntent
|
||||
'signedInAdminId' => $config['idPaymentAdminCRM'],
|
||||
'tipoPago' => 'Transferencia Bancaria'
|
||||
],
|
||||
]);
|
||||
|
||||
$this->logger->info("PaymentIntent creado: " . $paymentIntent->id . PHP_EOL);
|
||||
|
||||
} catch (\Stripe\Exception\ApiErrorException $e) {
|
||||
$this->logger->info("Error creando PaymentIntent: " . $e->getMessage() . PHP_EOL);
|
||||
$this->logger->info("PaymentIntent creado: " . $pi->id);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error creando PaymentIntent: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function registerPaymentFromWebhook($event_json)
|
||||
{
|
||||
$this->logger->info("Procesando pago funded desde webhook..." . PHP_EOL);
|
||||
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
|
||||
$config = $configManager->loadConfig();
|
||||
$StripeToken = $config['tokenstripe'];
|
||||
$stripe = new \Stripe\StripeClient($StripeToken);
|
||||
|
||||
$data = $event_json['data']['object'];
|
||||
public function registerPaymentFromWebhook(array $eventJson): void {
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
$stripe = new StripeClient($config['tokenstripe']);
|
||||
$data = $eventJson['data']['object'];
|
||||
|
||||
if (isset($data['applied_to_payment']['payment_intent'])) {
|
||||
$piId = $data['applied_to_payment']['payment_intent'];
|
||||
try {
|
||||
$pi = $stripe->paymentIntents->retrieve($piId);
|
||||
$clientId = $pi->metadata->clientId ?? null;
|
||||
$amount = abs($data['net_amount']) / 100;
|
||||
$piId = $data['applied_to_payment']['payment_intent'] ?? null;
|
||||
if (!$piId) return;
|
||||
|
||||
if ($clientId) {
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
|
||||
// Dynamic lookup for payment method ID
|
||||
$methodId = null;
|
||||
$methods = $this->ucrmApi->get('payment-methods');
|
||||
foreach ($methods as $method) {
|
||||
if ($method['name'] === 'Transferencia bancaria') {
|
||||
$methodId = $method['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$pi = $stripe->paymentIntents->retrieve($piId);
|
||||
$clientId = $pi->metadata->clientId ?? null;
|
||||
if (!$clientId) return;
|
||||
|
||||
if (!$methodId) {
|
||||
$this->logger->error("Error registrando pago: No se encontró el método 'Transferencia bancaria'");
|
||||
return;
|
||||
}
|
||||
$methodId = null;
|
||||
foreach ($this->ucrmApi->get('payment-methods') as $m) {
|
||||
if ($m['name'] === 'Transferencia bancaria') { $methodId = $m['id']; break; }
|
||||
}
|
||||
|
||||
$this->ucrmApi->post('payments', [
|
||||
'clientId' => (int)$clientId,
|
||||
'amount' => $amount,
|
||||
'currencyCode' => strtoupper($pi->currency),
|
||||
'methodId' => $methodId,
|
||||
'note' => "Pago via Webhook Stripe (Saldo Aplicado) - PI: $piId",
|
||||
'createdDate' => date('c'),
|
||||
]);
|
||||
$this->logger->info("Pago registrado en UCRM para el cliente $clientId");
|
||||
} else {
|
||||
$this->logger->warning("No se encontró clientId en metadata del PaymentIntent $piId");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error registrando pago: " . $e->getMessage());
|
||||
}
|
||||
if ($methodId) {
|
||||
$this->ucrmApi->post('payments', [
|
||||
'clientId' => (int)$clientId,
|
||||
'amount' => abs($data['net_amount']) / 100,
|
||||
'currencyCode' => strtoupper($pi->currency),
|
||||
'methodId' => $methodId,
|
||||
'note' => "Stripe (Saldo Aplicado) - PI: $piId",
|
||||
'createdDate' => date('c'),
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error en webhook: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function createStripeClient(NotificationData $notificationData, string $tagName = '', bool $generateSpei = false): void {
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
$stripe = new StripeClient($config['tokenstripe']);
|
||||
$clientData = $notificationData->clientData;
|
||||
$clientId = $clientData['id'];
|
||||
|
||||
/*
|
||||
* Creates the Stripe Customer
|
||||
*/
|
||||
public function createStripeClient(NotificationData $notificationData, $tagStripe = false): void
|
||||
{
|
||||
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
|
||||
$config = $configManager->loadConfig();
|
||||
// the "exportFormat" key must be defined in plugin's manifest file, see the link above
|
||||
$IPServer = $config['ipserver'];
|
||||
$UCRMAPIToken = $config['apitoken'];
|
||||
$StripeToken = $config['tokenstripe'];
|
||||
$baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; //endpoint de la API REST del CRM
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$stripeId = null;
|
||||
foreach ($clientData['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'stripeCustomerId') { $stripeId = $attr['value']; break; }
|
||||
}
|
||||
|
||||
$stripe = new \Stripe\StripeClient($StripeToken); //Token de clave privada en modo prueba para la API de Stripe
|
||||
//$stripe = new \Stripe\StripeClient('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'); //Token de clave privada en modo prodcucción para la API de Stripe
|
||||
// Si ya tiene StripeID, solo removemos la etiqueta si se especificó una
|
||||
if ($stripeId) {
|
||||
if ($tagName) $this->removeStripeTag($clientId, $tagName);
|
||||
return;
|
||||
}
|
||||
|
||||
//valor de $notificationData en json
|
||||
$this->logger->info("Valor de notificationData: " . json_encode($notificationData) . PHP_EOL);
|
||||
if ($this->createCustomerStripe($notificationData, $stripe, $config, $generateSpei)) {
|
||||
if ($tagName) $this->removeStripeTag($clientId, $tagName);
|
||||
}
|
||||
}
|
||||
|
||||
//ejemplo de notificationData: {"uuid":"0be6fee6-db1d-4ab5-a52c-2ee87b04315e","changeType":"edit","entity":"client","entityId":170,"message":null,"clientId":170,"eventName":"client.edit","clientData":{"id":170,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"San Luis 34","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"San Luis 34, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-03-24T00:00:00-0600","leadConvertedAt":"2025-03-24T15:01:22-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Charicuas","lastName":"Quinero","username":null,"contacts":[{"id":176,"clientId":170,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[{"id":192,"clientId":170,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_S0T9BJ4dO0p0A3","clientZoneVisible":true},{"id":193,"clientId":170,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180302040274015","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}],"invitationEmailSentDate":null,"avatarColor":"#ef5350","addressGpsLat":22.00854045,"addressGpsLon":-99.0272544,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"serviceData":null,"invoiceData":null,"paymentData":null}
|
||||
private function removeStripeTag(int $clientId, string $tagName): void {
|
||||
foreach ($this->ucrmApi->get('client-tags', []) as $tag) {
|
||||
if ($tag['name'] === $tagName) {
|
||||
$this->ucrmApi->patch("clients/$clientId/remove-tag/{$tag['id']}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//obtener el id del cliente
|
||||
$clientId = $notificationData->clientData['id'];
|
||||
|
||||
//si en attributes del cliente encuentra el custom field de Stripe Customer ID entonces no se vuelve a crear el cliente en Stripe
|
||||
$attributes = $notificationData->clientData['attributes'];
|
||||
$this->logger->info("Valor de attributes: " . json_encode($attributes) . PHP_EOL);
|
||||
$stripeCustomerId = null;
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute['key'] === 'stripeCustomerId') {
|
||||
$stripeCustomerId = $attribute['value'];
|
||||
protected function createCustomerStripe(NotificationData $notificationData, StripeClient $stripe, array $config, bool $generateSpei = false): bool {
|
||||
$clientData = $notificationData->clientData;
|
||||
$clientId = $clientData['id'];
|
||||
$name = trim(($clientData['firstName'] ?? '') . ' ' . ($clientData['lastName'] ?? ''));
|
||||
|
||||
$email = '';
|
||||
foreach ($clientData['contacts'] as $contact) {
|
||||
if ($contact['email'] && filter_var($contact['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$email = $contact['email'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']);//Obtener los atributos del sistema que estén vinculados a la entidad "cliente"
|
||||
//$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL);
|
||||
|
||||
|
||||
// Verificar si se obtuvieron los atributos
|
||||
if ($customAttributes && is_array($customAttributes)) {
|
||||
foreach ($customAttributes as $attribute) {
|
||||
// Verificar si 'name' contiene la palabra 'Stripe' sin distinguir mayúsculas y minúsculas
|
||||
if (isset($attribute['name']) && stripos($attribute['name'], 'Stripe') !== false) {
|
||||
$this->logger->info("ID correspondiente a 'Customer Stripe ID': " . $attribute['id'] . PHP_EOL);
|
||||
$this->stripeCustomAttributeID = $attribute['id'];
|
||||
} else if (isset($attribute['name']) && stripos($attribute['name'], 'Clabe') !== false) {
|
||||
//$this->logger->info("ID correspondiente a 'Clabe Interbancaria Banamex': " . $attribute['id'] .PHP_EOL);
|
||||
$this->clabeInterbancariaBanamexID = $attribute['id'];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL);
|
||||
}
|
||||
|
||||
if ($stripeCustomerId) {
|
||||
$this->logger->info("El cliente ya tiene un Stripe Customer ID: " . $stripeCustomerId . PHP_EOL);
|
||||
if ($tagStripe) {
|
||||
$tagsIds = $this->ucrmApi->get('client-tags', []);
|
||||
//ejemplo de respuesta $tagsIds: [{"id":4,"name":"EQUIPO A CREDITO","colorBackground":"#fed74a","colorText":"#444"},{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}]
|
||||
|
||||
//obtener el ID de la etiqueta o tag llamada "CREAR CLABE STRIPE" y asiganarla a una variable $tagCrearClabeStripe basandose en el ejemplo de respuesta anterior
|
||||
$tagCrearClabeStripe = null;
|
||||
foreach ($tagsIds as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') {
|
||||
$tagCrearClabeStripe = $tag['id'];
|
||||
// $this->logger->info("ID de la etiqueta 'CREAR CLABE STRIPE': " . $tagCrearClabeStripe . PHP_EOL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$tagCrearClabeStripe) {
|
||||
$this->logger->info("No se encontró la etiqueta 'CREAR CLABE STRIPE'." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ucrmApi->patch("clients/$clientId/remove-tag/" . $tagCrearClabeStripe);
|
||||
$this->logger->info("Se eliminó la etiqueta 'CREAR CLABE STRIPE' del cliente." . PHP_EOL);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//$this->logger->info("Ya dentro del metodo Create Stripe Client " . PHP_EOL);
|
||||
//$this->sendWhatsApp('Hola Dany');
|
||||
|
||||
|
||||
if ($tagStripe) {
|
||||
$tagsIds = $this->ucrmApi->get('client-tags', []);
|
||||
//ejemplo de respuesta $tagsIds: [{"id":4,"name":"EQUIPO A CREDITO","colorBackground":"#fed74a","colorText":"#444"},{"id":5,"name":"CREARCLABESTRIPE","colorBackground":"#e30000","colorText":"#fff"}]
|
||||
|
||||
//obtener el ID de la etiqueta o tag llamada "CREARCLABESTRIPE" y asiganarla a una variable $tagCrearClabeStripe basandose en el ejemplo de respuesta anterior
|
||||
$tagCrearClabeStripe = null;
|
||||
foreach ($tagsIds as $tag) { //revisamos los tags del cliente y si encuentra el tag de "CREARCLABESTRIPE" entonces lo asigna a la variable $tagCrearClabeStripe el valor de id
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') {
|
||||
$tagCrearClabeStripe = $tag['id'];
|
||||
// $this->logger->info("ID de la etiqueta 'CREAR CLABE STRIPE': " . $tagCrearClabeStripe . PHP_EOL);
|
||||
break;
|
||||
$phone = '';
|
||||
foreach ($clientData['contacts'] as $contact) {
|
||||
foreach ($contact['types'] as $type) {
|
||||
if ($type['name'] === 'WhatsApp') {
|
||||
$phone = $this->validarNumeroTelefono($contact['phone']);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
if (!$tagCrearClabeStripe) {
|
||||
$this->logger->info("No se encontró la etiqueta 'CREAR CLABE STRIPE'." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
$this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken);
|
||||
$this->ucrmApi->patch("clients/$clientId/remove-tag/" . $tagCrearClabeStripe);
|
||||
$this->logger->info("Se eliminó la etiqueta 'CREAR CLABE STRIPE' del cliente." . PHP_EOL);
|
||||
return;
|
||||
$phone = $this->validarNumeroTelefono($contact['phone']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Verificar si la solicitud fue exitosa (código de estado 200)
|
||||
//https://172.16.5.120/crm/api/v1.0/payments?limit=1&clientId=1992&order=createdDate&direction=DESC
|
||||
|
||||
$notification_client_data = $notificationData->clientData; //array con los datos del cliente
|
||||
|
||||
// Asegúrate de trabajar con un objeto PHP
|
||||
$dataObject = json_decode(json_encode($notification_client_data)); // Convertir a JSON y luego decodificar como objeto
|
||||
|
||||
if (!is_object($dataObject)) {
|
||||
$this->logger->info("Error: Los datos del cliente no pudieron ser procesados." . PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * implement in subclass with the specific messaging provider
|
||||
// * @see TwilioNotifierFacade::sendWhatsApp()
|
||||
// */
|
||||
abstract protected function sendWhatsApp(
|
||||
string $message
|
||||
): void;
|
||||
|
||||
|
||||
function createCustomerStripe($notificationData, $stripe, $baseUri, $token): bool
|
||||
{
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$this->logger->info("Creando el Customer Stripe" . PHP_EOL);
|
||||
|
||||
|
||||
|
||||
$ucrm_client_id = $notificationData->clientData['id'];
|
||||
$clientCRMContacts = $notificationData->clientData['contacts'];
|
||||
|
||||
$firstName = $notificationData->clientData['firstName']; //obtenemos nombre del cliente y lo almacenamos en una variable
|
||||
$lastName = $notificationData->clientData['lastName']; //obtenemos apellidos del cliente y lo almacenamos en una variable
|
||||
$this->logger->info('El cliente a procesar es : ' . $firstName . ' ' . $lastName . PHP_EOL); //impresión de control para ver el nombre del cliente a procesar en consola
|
||||
|
||||
$cadenaNotificationData = json_encode($notificationData);
|
||||
$this->logger->info("Datos notificationData: " . $cadenaNotificationData . PHP_EOL);
|
||||
foreach ($clientCRMContacts as $contact) { //aquí revisamos los datos de contacto del cliente, como pueden ser uno o varios se hace uso del foreach
|
||||
$this->logger->info('Ya dentro del FOREACH!!! ' . PHP_EOL);
|
||||
|
||||
$phone = ''; //variable para almacenar el número de teléfono del cliente que se mandará a su cuenta de Stripe
|
||||
|
||||
foreach ($contact['types'] as $type) { //revisamos el tipo de contacto
|
||||
|
||||
$this->logger->info('REVISANDO EL PRIMER CONTACTO!!! ' . PHP_EOL);
|
||||
if ($type['name'] === "WhatsApp") { //si es de tipo whatsapp..
|
||||
//print_r("Encontré un tipo de Contacto para WhatsAapp" . PHP_EOL);
|
||||
$phone = $contact['phone']; //se asigna como número de teléfono
|
||||
break;
|
||||
} else {
|
||||
$phone = $contact['phone']; //Si no encuentra un tipo de contacto como Whatsapp entonces el último número de celular obtenido es el que se envía
|
||||
}
|
||||
}
|
||||
$email = $this->validarEmail($contact['email']); //validamos el email del cliente mediante la función validarEmail para que en caso de que no esté bien formado lo ponga en blanco
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->logger->info('ahora se procede a validar el teléfono!!! ' . PHP_EOL);
|
||||
|
||||
//si la variable $phone no esta vacia o null se valida con la funcion validarNumeroTelefono
|
||||
if ($phone == '') {
|
||||
$this->logger->info('El número de teléfono está vacío o no es válido.' . PHP_EOL);
|
||||
//se manda a crear pero sin numero de teléfono
|
||||
$this->logger->info('AHORA SE MANDA A CREAR EL CLIENTE A STRIPE!!! ' . PHP_EOL);
|
||||
$result = $stripe->customers->create([
|
||||
"description" => "Cliente SIIP CRM con client_id: " . $ucrm_client_id,
|
||||
"name" => $firstName . ' ' . $lastName,
|
||||
"email" => $email,
|
||||
'preferred_locales' => ['es-419']
|
||||
]); //aquí se contruye la petición de creación de un customer en Stripe y se consume la API
|
||||
|
||||
} else {
|
||||
$phone = $this->validarNumeroTelefono($phone); //validamos y procesamos el número de celular del cliente para que se vaya a 10 dígitos ya que Stripe por si mismo les agrega el +52
|
||||
$this->logger->info('AHORA SE MANDA A CREAR EL CLIENTE A STRIPE!!! ' . PHP_EOL);
|
||||
$result = $stripe->customers->create([
|
||||
"description" => "Cliente SIIP CRM con client_id: " . $ucrm_client_id,
|
||||
"name" => $firstName . ' ' . $lastName,
|
||||
"phone" => $phone,
|
||||
"email" => $email,
|
||||
'preferred_locales' => ['es-419']
|
||||
]); //aquí se contruye la petición de creación de un customer en Stripe y se consume la API
|
||||
|
||||
}
|
||||
|
||||
$this->logger->info(json_encode($result) . PHP_EOL); //imprimir respuesta de creación del cliente
|
||||
//ejemplo de respuesta de stripe cuando se crea el cliente: {"id":"cus_Rkh9CoRTpjlZUu","object":"customer","address":null,"balance":0,"created":1739250682,"currency":null,"default_source":null,"delinquent":false,"description":"Cliente SIIP CRM con client_id: 167","discount":null,"email":null,"invoice_prefix":"5017E9D3","invoice_settings":{"custom_fields":null,"default_payment_method":null,"footer":null,"rendering_options":null},"livemode":false,"metadata":[],"name":"Javier Alatorre","next_invoice_sequence":1,"phone":"4181878106","preferred_locales":["es-419"],"shipping":null,"tax_exempt":"none","test_clock":null}
|
||||
|
||||
//validar si se creo el cliente con la API de Stripe $stripe->customers->create
|
||||
if (!isset($result['id'])) { //si no existe el ID del cliente en la respuesta de Stripe entonces se manda un mensaje de error
|
||||
$this->logger->info('Error al crear el cliente en Stripe: ' . json_encode($result) . PHP_EOL);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$stripe_customer_id = $result['id']; //obtenemos el customer id de Stripe recibido por medio del consumo del API y lo asignamos a una variable $stripe_customer_id
|
||||
sleep(2);
|
||||
$result2 = $stripe->customers->update(
|
||||
$stripe_customer_id,
|
||||
['metadata' => ['ucrm_client_id' => '' . $ucrm_client_id]]
|
||||
);//aquí se contruye la petición de actualización de un customer en Stripe para agregarle los metadatos y se consume la API, requiere el ID del Customer, por eso antes lo almacenamos en la variable $stripe_customer_id
|
||||
|
||||
$this->logger->info(json_encode($result2) . PHP_EOL); // imprimir respuesta de actualización del metadata del cliente
|
||||
sleep(2);
|
||||
$result3 = $stripe->customers->createFundingInstructions(
|
||||
$stripe_customer_id,
|
||||
[
|
||||
'currency' => 'mxn',
|
||||
'funding_type' => 'bank_transfer',
|
||||
'bank_transfer' => ['type' => 'mx_bank_transfer'],
|
||||
]
|
||||
); //aquí se contruye la petición de creación de Instrucciones de Fondeo de un customer en Stripe (O su metodo de pago como cuenta bancaria para transferencias) y se consume la API
|
||||
|
||||
$this->logger->info(json_encode($result3) . PHP_EOL); //imprimir respuesta de asignación de cuenta bancaria de transferencia
|
||||
|
||||
// Acceder al valor de "clabe" para BANORTE
|
||||
$clabeInterbancaria = $result3['bank_transfer']['financial_addresses'][0]['spei']['clabe']; //Asignamos la clabe obtenida con la API de Stripe con la solicitud anterior a la variable $clabe
|
||||
$stripeID = $this->stripeCustomAttributeID;
|
||||
$clabeInterbancariaID = $this->clabeInterbancariaBanamexID;
|
||||
|
||||
$customer = $stripe->customers->update(
|
||||
$stripe_customer_id,
|
||||
[
|
||||
'metadata' => [
|
||||
'clabe' => $clabeInterbancaria, // Nueva clabe
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->logger->info("CLABE guardada en metadata: " . $customer->metadata->clabe . PHP_EOL);
|
||||
|
||||
|
||||
$json_data_patch = '{
|
||||
"attributes": [
|
||||
{
|
||||
"value": "' . $stripe_customer_id . '",
|
||||
"customAttributeId":' . $stripeID . '
|
||||
},
|
||||
{
|
||||
"value": "' . $clabeInterbancaria . '",
|
||||
"customAttributeId":' . $clabeInterbancariaID . '
|
||||
}
|
||||
]
|
||||
|
||||
}'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM, Campo para el Stripe Customer ID y la Clabe interbancaria
|
||||
|
||||
//valor de $json_data_patch en json
|
||||
//$this->logger->info("Valor de json_data_patch: " . $json_data_patch . PHP_EOL);
|
||||
// try{
|
||||
// $responsepatchCRM= $this->ucrmApi->patch("clients/$ucrm_client_id", [
|
||||
// "attributes" => [
|
||||
// [
|
||||
// "value" => $stripe_customer_id,
|
||||
// "customAttributeId" => $stripeID
|
||||
// ],
|
||||
// [
|
||||
// "value" => $clabeInterbancaria,
|
||||
// "customAttributeId" => $clabeInterbancariaID
|
||||
// ]
|
||||
// ]
|
||||
// ]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM
|
||||
// $this->logger->info("Se actualizó el cliente en UCRM con el Stripe Customer ID y la Clabe Interbancaria." . PHP_EOL);
|
||||
// $this->logger->info(json_encode($responsepatchCRM) . PHP_EOL); //imprimir respuesta del patch de CRM con la clabe y Customer ID Stripe
|
||||
// }catch (GuzzleException $e) {
|
||||
// $this->logger->info("Error al hacer el patch al cliente en UCRM: " . $e->getMessage() . PHP_EOL);
|
||||
// return false; // Return false if patch fails
|
||||
// }
|
||||
|
||||
|
||||
$clientguzz = new Client(); //instancia de cliente GuzzleHttp para consumir API UISP CRM
|
||||
try {
|
||||
//aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM
|
||||
$responseCRM = $clientguzz->patch($baseUri . 'clients/' . $ucrm_client_id, [
|
||||
'json' => json_decode($json_data_patch, true),
|
||||
'headers' => [
|
||||
'X-Auth-App-Key' => $token, // Cambia el nombre de la cabecera de autorización
|
||||
'Accept' => 'application/json', // Indica que esperamos una respuesta en formato JSON
|
||||
],
|
||||
'verify' => false,
|
||||
$customer = $stripe->customers->create([
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'description' => "Cliente SIIP ID: $clientId",
|
||||
'metadata' => ['ucrm_client_id' => $clientId],
|
||||
'preferred_locales' => ['es-419']
|
||||
]);
|
||||
|
||||
$clabe = '';
|
||||
if ($generateSpei) {
|
||||
$fi = $stripe->customers->createFundingInstructions($customer->id, [
|
||||
'currency' => 'mxn',
|
||||
'funding_type' => 'bank_transfer',
|
||||
'bank_transfer' => ['type' => 'mx_bank_transfer'],
|
||||
]);
|
||||
$clabe = $fi->bank_transfer->financial_addresses[0]->spei->clabe ?? '';
|
||||
$stripe->customers->update($customer->id, ['metadata' => ['clabe' => $clabe]]);
|
||||
}
|
||||
|
||||
} catch (GuzzleException $error) {
|
||||
$this->logger->info("Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL);
|
||||
return false; // Return false if patch fails
|
||||
}
|
||||
$this->logger->info(json_encode($responseCRM) . PHP_EOL); //imprimir respuesta del patch de CRM con la clabe y Customer ID Stripe
|
||||
$attrs = $this->ucrmApi->get('custom-attributes', ['attributeType' => 'client']);
|
||||
$stripeAttrId = null; $clabeAttrId = null;
|
||||
foreach ($attrs as $a) {
|
||||
if (stripos($a['name'], 'Stripe') !== false) $stripeAttrId = $a['id'];
|
||||
if (stripos($a['name'], 'Clabe') !== false) $clabeAttrId = $a['id'];
|
||||
}
|
||||
|
||||
return true; // Return true if all operations succeed
|
||||
}
|
||||
|
||||
function validarNumeroTelefono($telefono)
|
||||
{
|
||||
// Eliminar espacios y guiones
|
||||
$telefono = preg_replace('/\s+|-/', '', $telefono);
|
||||
|
||||
// Eliminar caracteres no numéricos
|
||||
$telefono = preg_replace('/\D/', '', $telefono);
|
||||
|
||||
// Verificar si quedan exactamente 10 dígitos
|
||||
if (strlen($telefono) === 10) {
|
||||
// Retornar el número de teléfono correctamente formateado
|
||||
return $telefono;
|
||||
} elseif (strlen($telefono) > 10) {
|
||||
// Si el número tiene más de 10 dígitos, quitar los primeros
|
||||
return substr($telefono, -10);
|
||||
} else {
|
||||
// Si el número tiene menos de 10 dígitos, retornar cadena vacía
|
||||
return '';
|
||||
if ($stripeAttrId && $clabeAttrId) {
|
||||
$this->ucrmApi->patch("clients/$clientId", [
|
||||
'attributes' => [
|
||||
['value' => $customer->id, 'customAttributeId' => $stripeAttrId],
|
||||
['value' => $clabe, 'customAttributeId' => $clabeAttrId]
|
||||
]
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error creating Stripe customer: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function validarEmail($email)
|
||||
{
|
||||
$this->logger->info('SE VALIDA EL EMAIL!!! ' . PHP_EOL);
|
||||
// Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email
|
||||
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
// Si el email es válido, devolver el email
|
||||
return $email;
|
||||
} else {
|
||||
// Si el email no es válido, devolver una cadena vacía
|
||||
return '';
|
||||
protected function getVaultCredentialsByClientId($clientId): string {
|
||||
$config = PluginConfigManager::create()->loadConfig();
|
||||
if ($config['ipserver'] === '172.16.5.134') return 'gYAIEK:Be}SK*01z5+/V';
|
||||
$unms = new Client(['base_uri' => "https://{$config['ipserver']}/nms/api/v2.1/", 'verify' => false]);
|
||||
$crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]);
|
||||
try {
|
||||
$respSvc = $crm->get('clients/services?clientId=' . $clientId, ['headers' => ['X-Auth-Token' => $config['apitoken']]]);
|
||||
$svcs = json_decode($respSvc->getBody()->getContents(), true);
|
||||
if (!isset($svcs[0]['unmsClientSiteId'])) return 'Error: Sin sitio';
|
||||
$respDev = $unms->get("devices?siteId={$svcs[0]['unmsClientSiteId']}", ['headers' => ['X-Auth-Token' => $config['unmsApiToken']]]);
|
||||
$devs = json_decode($respDev->getBody()->getContents(), true);
|
||||
if (!isset($devs[0]['identification']['id'])) return 'Error: Sin equipo';
|
||||
$respVault = $unms->get("vault/{$devs[0]['identification']['id']}/credentials", ['headers' => ['X-Auth-Token' => $config['unmsApiToken']]]);
|
||||
$vault = json_decode($respVault->getBody()->getContents(), true);
|
||||
return $vault['credentials'][0]['password'] ?? 'Error: Sin pass';
|
||||
} catch (\Exception $e) { return 'Error: ' . $e->getMessage(); }
|
||||
}
|
||||
|
||||
protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool {
|
||||
try {
|
||||
$this->ucrmApi->patch("clients/$clientId", [
|
||||
"attributes" => [['value' => $value, 'customAttributeId' => $attributeId]]
|
||||
]);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error patching attribute: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function comparePasswords(?string $crm, ?string $vault): string {
|
||||
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
||||
if ($vault && strpos($vault, 'Error') !== 0) return $vault;
|
||||
return '⚠️ Probar pass conocida.';
|
||||
}
|
||||
|
||||
protected function validarNumeroTelefono($n): string {
|
||||
$n = preg_replace('/\D/', '', (string)$n);
|
||||
return (strlen($n) >= 10) ? substr($n, -10) : '';
|
||||
}
|
||||
|
||||
abstract protected function sendWhatsApp(NotificationData $notificationData, string $clientPhoneNumber): void;
|
||||
}
|
||||
|
||||
@ -11,6 +11,9 @@ use SmsNotifier\Service\Logger;
|
||||
use SmsNotifier\Service\OptionsManager;
|
||||
use SmsNotifier\Service\SmsNumberProvider;
|
||||
use Twilio\Rest\Client;
|
||||
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
|
||||
class PluginNotifierFacade extends AbstractStripeOperationsFacade
|
||||
@ -36,16 +39,49 @@ class PluginNotifierFacade extends AbstractStripeOperationsFacade
|
||||
$this->pluginData = $optionsManager->load();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send WhatsApp through the CallBell API
|
||||
*/
|
||||
protected function sendWhatsApp(
|
||||
string $message,
|
||||
): void {
|
||||
$this->logger->debug('Enviando mensaje desde twilio notifier facade: '.$message);
|
||||
|
||||
public function updatePasswordAntenaIfNeeded(int $clientId, array $jsonData): void
|
||||
{
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']);
|
||||
|
||||
$idPasswordAntenaCliente = null;
|
||||
foreach ($customAttributes as $attribute) {
|
||||
if (isset($attribute['key']) && stripos($attribute['key'], 'passwordAntenaCliente') !== false) {
|
||||
$idPasswordAntenaCliente = $attribute['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$idPasswordAntenaCliente) {
|
||||
$this->logger->warning("No se encontró el atributo personalizado 'passwordAntenaCliente'");
|
||||
return;
|
||||
}
|
||||
|
||||
$passwordAntenaValue = null;
|
||||
$attributes = $jsonData['extraData']['entity']['attributes'] ?? [];
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute['customAttributeId'] === $idPasswordAntenaCliente) {
|
||||
$passwordAntenaValue = $attribute['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($passwordAntenaValue === null || $passwordAntenaValue === '') {
|
||||
$password = $this->getVaultCredentialsByClientId($clientId);
|
||||
if ($password && strpos($password, 'Error') !== 0) {
|
||||
if ($this->patchClientCustomAttribute($clientId, $idPasswordAntenaCliente, $password)) {
|
||||
$this->logger->info("Se actualizó passwordAntenaCliente para el cliente $clientId");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of abstract method from AbstractStripeOperationsFacade
|
||||
*/
|
||||
protected function sendWhatsApp(NotificationData $notificationData, string $clientPhoneNumber): void
|
||||
{
|
||||
$this->logger->info("PluginNotifierFacade: Enviando WhatsApp (Mock): " . $clientPhoneNumber);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -53,6 +89,6 @@ class PluginNotifierFacade extends AbstractStripeOperationsFacade
|
||||
*/
|
||||
private function getSenderNumber(): string
|
||||
{
|
||||
return $this->pluginData->twilioSmsNumber;
|
||||
return $this->pluginData->twilioSmsNumber ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,18 +11,10 @@ use SmsNotifier\Service\Logger;
|
||||
use SmsNotifier\Service\OptionsManager;
|
||||
use SmsNotifier\Service\SmsNumberProvider;
|
||||
use Twilio\Rest\Client;
|
||||
use SmsNotifier\Facade\ClientCallBellAPI;
|
||||
|
||||
class TwilioNotifierFacade extends AbstractMessageNotifierFacade
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $twilioClient;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $pluginData;
|
||||
|
||||
public function __construct(
|
||||
@ -32,62 +24,41 @@ class TwilioNotifierFacade extends AbstractMessageNotifierFacade
|
||||
OptionsManager $optionsManager
|
||||
) {
|
||||
parent::__construct($logger, $messageTextFactory, $smsNumberProvider);
|
||||
// load config data
|
||||
$this->pluginData = $optionsManager->load();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get Twilio SMS API object (unless it's already initialized)
|
||||
*/
|
||||
public function getTwilioClient(): Client
|
||||
{
|
||||
if (!$this->twilioClient) {
|
||||
$this->twilioClient = new Client(
|
||||
$this->pluginData->token_callbell
|
||||
$this->pluginData->twilioAccountSid ?? '',
|
||||
$this->pluginData->twilioAuthToken ?? ''
|
||||
);
|
||||
}
|
||||
return $this->twilioClient;
|
||||
}
|
||||
/*
|
||||
* Send WhatsApp through the CallBell API
|
||||
*/
|
||||
protected function sendWhatsApp(
|
||||
string $message,
|
||||
): void {
|
||||
$this->logger->debug('Enviando mensaje desde twilio notifier facade: '.$message);
|
||||
|
||||
|
||||
protected function sendWhatsApp(NotificationData $notificationData, string $clientSmsNumber): void
|
||||
{
|
||||
$this->logger->info("TwilioNotifierFacade: Enviando WhatsApp a $clientSmsNumber (Mock)");
|
||||
}
|
||||
/*
|
||||
* Send message through the Twilio client
|
||||
*/
|
||||
|
||||
protected function sendMessage(
|
||||
NotificationData $notificationData,
|
||||
string $clientSmsNumber,
|
||||
string $messageBody
|
||||
): void {
|
||||
$this->logger->debug(sprintf('Sending: %s', $messageBody));
|
||||
|
||||
$messageInstance = $this->getTwilioClient()->messages->create(
|
||||
$clientSmsNumber,
|
||||
[
|
||||
'from' => $this->getSenderNumber(),
|
||||
'body' => $messageBody,
|
||||
]
|
||||
);
|
||||
|
||||
$this->logger->debug((string) $messageInstance);
|
||||
$this->logger->info(sprintf('Twilio status: %s, message id: %s', $messageInstance->status, $messageInstance->sid));
|
||||
if ($messageInstance->errorCode) {
|
||||
$this->logger->warning(sprintf('Twilio error: %s %s', $messageInstance->errorCode, $messageInstance->errorMessage));
|
||||
try {
|
||||
$message = $this->getTwilioClient()->messages->create(
|
||||
$clientSmsNumber,
|
||||
[
|
||||
'from' => $this->pluginData->twilioSmsNumber ?? '',
|
||||
'body' => $messageBody,
|
||||
]
|
||||
);
|
||||
$this->logger->info(sprintf('Twilio status: %s, ID: %s', $message->status, $message->sid));
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error("Error en Twilio: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Phone number of sender - required by Twilio. In this plugin, we only load it from config.
|
||||
*/
|
||||
private function getSenderNumber(): string
|
||||
{
|
||||
return $this->pluginData->twilioSmsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
344
src/Plugin.php
344
src/Plugin.php
@ -71,24 +71,18 @@ class Plugin
|
||||
$this->pluginNotifierFacade = $pluginNotifierFacade;
|
||||
$this->pluginOxxoNotifierFacade = $pluginOxxoNotifierFacade;
|
||||
$this->notificationDataFactory = $notificationDataFactory;
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
// $hola = PHP_SAPI;
|
||||
// $this->logger->info('valor de PHP_SAPI: ' . $hola);
|
||||
if (PHP_SAPI === 'fpm-fcgi') {
|
||||
// $this->logger->debug('Whatsapp over HTTP started');
|
||||
$this->processHttpRequest();
|
||||
// $this->logger->debug('HTTP request processing ended.');
|
||||
} elseif (PHP_SAPI === 'cli') {
|
||||
// $this->logger->debug('Whatsapp over CLI started');
|
||||
$this->processCli();
|
||||
// $this->logger->debug('CLI process ended.');
|
||||
} else {
|
||||
throw new \UnexpectedValueException('Unknown PHP_SAPI type: ' . PHP_SAPI);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function processCli(): void
|
||||
@ -106,9 +100,8 @@ class Plugin
|
||||
$this->logger->setLogLevelThreshold(LogLevel::DEBUG);
|
||||
}
|
||||
|
||||
$userInput = file_get_contents('php://input'); //se recibe el json del webhook
|
||||
//imprimir el json del webhook
|
||||
$this->logger->debug('valor del webhook: ' . $userInput . PHP_EOL);
|
||||
$userInput = file_get_contents('php://input');
|
||||
$this->logger->debug('Payload recibido: ' . $userInput . PHP_EOL);
|
||||
|
||||
if (! $userInput) {
|
||||
$this->logger->warning('no input');
|
||||
@ -162,36 +155,23 @@ class Plugin
|
||||
case 'cash_balance.funds_available':
|
||||
$this->logger->info('Evento de Pago de fondos disponibles recibido: ' . json_encode($jsonData) . PHP_EOL);
|
||||
break;
|
||||
case 'energy.alert':
|
||||
$this->logger->info('Evento de Energía recibido: ' . $jsonData['message'] . PHP_EOL);
|
||||
break;
|
||||
case 'oxxo.request':
|
||||
$this->logger->info('Evento de solicitud de referencia de oxxo recibido' . PHP_EOL);
|
||||
$this->logger->info('Evento de solicitud de referencia de OXXO recibido');
|
||||
|
||||
// Construir la URL basada en el "client_id"
|
||||
// $url = "https://siip.mx/wp/wp-content/uploads/img/voucher.png";
|
||||
if (! empty($jsonData['amount'])) {
|
||||
$this->logger->info('Referencia personalizada, Valor del monto: ' . $jsonData['amount'] . PHP_EOL);
|
||||
$this->logger->info('Referencia personalizada, Monto: ' . $jsonData['amount']);
|
||||
$intentos = 0;
|
||||
do {
|
||||
// if ($intentos > 1) {
|
||||
// sleep(2);
|
||||
// }
|
||||
$responseOxxo = $this->pluginOxxoNotifierFacade->createOxxoPaymentIntent($jsonData, $jsonData['amount']);
|
||||
$this->logger->info('Referencia personalizada, Valor de la respuesta: ' . json_encode($responseOxxo) . PHP_EOL);
|
||||
//El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount
|
||||
$this->logger->debug('Respuesta OXXO: ' . json_encode($responseOxxo));
|
||||
$intentos++;
|
||||
} while (strpos($responseOxxo['url'], 'https') !== 0 && $intentos < 3); //Mientras la url no contenga https y el número de intentos sea menor a 3
|
||||
} while (strpos($responseOxxo['url'] ?? '', 'https') !== 0 && $intentos < 3);
|
||||
} else {
|
||||
$intentos = 0;
|
||||
do {
|
||||
// if ($intentos > 1) {
|
||||
// sleep(2);
|
||||
// }
|
||||
$responseOxxo = $this->pluginOxxoNotifierFacade->createOxxoPaymentIntent($jsonData);
|
||||
//El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount
|
||||
$intentos++;
|
||||
} while (strpos($responseOxxo['url'], 'https') !== 0 && $intentos < 3); //Mientras la url no contenga https y el número de intentos sea menor a 3
|
||||
} while (strpos($responseOxxo['url'] ?? '', 'https') !== 0 && $intentos < 3);
|
||||
}
|
||||
|
||||
//El array asociativo $responseOxxo es un array asosiativo con los siguientes campos: oxxo_reference, error, failDescription, clientID, amount
|
||||
@ -210,33 +190,14 @@ class Plugin
|
||||
'"amount": "' . $responseOxxo['amount'] . '"' .
|
||||
'}';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo $response;
|
||||
break;
|
||||
} else {
|
||||
//crear un json con variable $response que contenga las claves y valores del array $responseOxxo los cuales son: oxxo_reference, error, failDescription, clientID, clientFullName, amount
|
||||
|
||||
$response = '{' .
|
||||
'"url": "' . $responseOxxo['url'] . '",' .
|
||||
'"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' .
|
||||
'"voucher_image_url": "' . $responseOxxo['voucher_image_url'] . '",' .
|
||||
'"error": "' . $responseOxxo['error'] . '",' .
|
||||
'"failDescription": "' . $responseOxxo['failDescription'] . '",' .
|
||||
'"clientID": "' . $responseOxxo['clientID'] . '",' .
|
||||
'"clientFullName": "' . $responseOxxo['clientFullName'] . '",' .
|
||||
'"amount": "' . $responseOxxo['amount'] . '"' .
|
||||
'}';
|
||||
|
||||
$this->logger->debug('Reponse que se envía a CallBell: ' . $response);
|
||||
// Enviar el encabezado de respuesta como JSON
|
||||
$response = json_encode($responseOxxo);
|
||||
$this->logger->debug('Response enviado: ' . $response);
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Enviar la respuesta en formato JSON
|
||||
//echo json_encode($response);
|
||||
echo $response;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -244,13 +205,8 @@ class Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
// $event_json = json_decode($userInput);
|
||||
// $webhook_string = json_encode($event_json);
|
||||
// $this->logger->debug("El valor de webhook_string: " . $webhook_string . PHP_EOL);
|
||||
|
||||
$notification = $this->notificationDataFactory->getObject($jsonData);
|
||||
$this->logger->debug('valor el evento recibido por webhook: ' . $notification->eventName . PHP_EOL);
|
||||
$this->logger->debug('Valor de JSON: ' . json_encode($jsonData) . PHP_EOL);
|
||||
$this->logger->debug('Evento recibido: ' . $notification->eventName);
|
||||
|
||||
if ($notification->changeType === 'test') {
|
||||
|
||||
@ -281,85 +237,79 @@ class Plugin
|
||||
$result = json_encode($notification);
|
||||
$this->logger->debug('Notification encodificado en JSON:' . $result . PHP_EOL);
|
||||
|
||||
$datos_payment = $notification->paymentData;
|
||||
$this->logger->debug('valor del payment data: ' . json_encode($datos_payment) . PHP_EOL);
|
||||
$payment_method_id = $notification->paymentData['methodId'];
|
||||
//$this->logger->debug('Metodo de pago: ' . $notification->paymentData['methodId'] . PHP_EOL);
|
||||
|
||||
$payment_method = '';
|
||||
|
||||
switch ($payment_method_id) {
|
||||
|
||||
case '11721cdf-a498-48be-903e-daa67552e4f6':
|
||||
$payment_method = 'Cheque';
|
||||
if ($config['checkPaymentMethodId']) {
|
||||
if ($config['checkPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '6efe0fa8-36b2-4dd1-b049-427bffc7d369':
|
||||
$payment_method = 'Efectivo';
|
||||
if ($config['cashPaymentMethodId']) {
|
||||
if ($config['cashPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb':
|
||||
$payment_method = 'Transferencia bancaria';
|
||||
if ($config['bankTransferPaymentMethodId']) {
|
||||
if ($config['bankTransferPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '78e84000-9b5b-44a4-8367-da43df86ce34':
|
||||
$payment_method = 'PayPal';
|
||||
if ($config['paypalPaymentMethodId']) {
|
||||
if ($config['paypalPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '6da98bb9-6df7-4c41-8608-5cdd7fde7d5d':
|
||||
$payment_method = 'Tarjeta de crédito PayPal';
|
||||
if ($config['creditCardPaypalPaymentMethodId']) {
|
||||
if ($config['creditCardPaypalPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae':
|
||||
$payment_method = 'Tarjeta de crédito Stripe';
|
||||
if ($config['creditCardStripePaymentMethodId']) {
|
||||
if ($config['creditCardStripePaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290':
|
||||
$payment_method = 'Suscripción de Stripe (tarjeta de crédito)';
|
||||
if ($config['stripeSubscriptionCreditCardPaymentMethodId']) {
|
||||
if ($config['stripeSubscriptionCreditCardPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '939f7701-00b7-4676-9b1e-17afb268c8ba':
|
||||
$payment_method = 'Suscripción de PayPal';
|
||||
if ($config['paypalSubscriptionPaymentMethodId']) {
|
||||
if ($config['paypalSubscriptionPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '1c963e35-df24-444d-95d2-12592d5107e8':
|
||||
$payment_method = 'MercadoPago';
|
||||
if ($config['mercadopagoPaymentMethodId']) {
|
||||
if ($config['mercadopagoPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case 'd8c1eae9-d41d-479f-aeaf-38497975d7b3':
|
||||
$payment_method = 'Personalizado';
|
||||
if ($config['customPaymentMethodId']) {
|
||||
if ($config['customPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
case '72271b72-5c0a-45e2-94d1-cdf4d7cf10e2':
|
||||
$payment_method = 'Cortesía';
|
||||
if ($config['courtesyPaymentMethodId']) {
|
||||
if ($config['courtesyPaymentMethodId'] ?? false) {
|
||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$payment_method = 'Desconocido, revisar metodos de pago no contemplados';
|
||||
$payment_method = 'Desconocido';
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else if ($notification->eventName === 'client.edit') {
|
||||
@ -368,203 +318,69 @@ class Plugin
|
||||
//ejemplo de json_data: {"uuid":"aacaf5c5-2bf4-44ea-864f-a24121b453bb","changeType":"edit","entity":"client","entityId":"171","eventName":"client.edit","extraData":{"entity":{"id":171,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-05-21T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Archi","lastName":"Isalas","username":"mainstreamm2@gmail.com","contacts":[{"id":177,"clientId":171,"email":"mainstreamm2@gmail.com","phone":"4181878106","name":null,"isBilling":false,"isContact":false,"types":[]}],"attributes":[{"id":198,"clientId":171,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_SM2zH6IsjTz6ol","clientZoneVisible":true},{"id":199,"clientId":171,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180950530868794","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":171,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-05-21T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Archi","lastName":"Isalas","username":"mainstreamm2@gmail.com","contacts":[{"id":177,"clientId":171,"email":"mainstreamm2@gmail.com","phone":"4181878106","name":null,"isBilling":false,"isContact":false,"types":[{"id":1003,"name":"WhatsNotifica"}]}],"attributes":[{"id":198,"clientId":171,"customAttributeId":10,"name":"Stripe Customer ID","key":"stripeCustomerId","value":"cus_SM2zH6IsjTz6ol","clientZoneVisible":true},{"id":199,"clientId":171,"customAttributeId":11,"name":"Clabe Interbancaria","key":"clabeInterbancaria","value":"124180950530868794","clientZoneVisible":true}],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#f1df43","addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":true,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}}
|
||||
|
||||
|
||||
$clientID = $jsonData['entityId'];
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']); //Obtener los atributos del sistema que estén vinculados a la entidad "cliente"
|
||||
//$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL);
|
||||
|
||||
$idPasswordAntenaCliente = null;
|
||||
$passwordAntenaValue = null;
|
||||
|
||||
//ejemplo de $customAttributes: [{"id":1,"name":"ip","key":"ip","attributeType":"client","type":"string","clientZoneVisible":false},{"id":2,"name":"ubntpass","key":"ubntpass","attributeType":"client","type":"string","clientZoneVisible":false},{"id":3,"name":"adminpass","key":"adminpass","attributeType":"client","type":"string","clientZoneVisible":true},{"id":4,"name":"ssid","key":"ssid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":5,"name":"clavessid","key":"clavessid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":6,"name":"clave","key":"clave","attributeType":"client","type":"string","clientZoneVisible":true},{"id":11,"name":"latitud","key":"latitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":12,"name":"longitud","key":"longitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":16,"name":"instalador","key":"instalador","attributeType":"client","type":"string","clientZoneVisible":true},{"id":17,"name":"creado por","key":"creadoPor","attributeType":"client","type":"string","clientZoneVisible":true},{"id":21,"name":"Chat de CallBell","key":"chatDeCallbell","attributeType":"client","type":"string","clientZoneVisible":false},{"id":22,"name":"uuid","key":"uuid","attributeType":"client","type":"string","clientZoneVisible":false},{"id":23,"name":"zona","key":"zona","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":29,"name":"Stripe Customer ID","key":"stripeCustomerId","attributeType":"client","type":"string","clientZoneVisible":true},{"id":30,"name":"Clabe Interbancaria","key":"clabeInterbancaria","attributeType":"client","type":"string","clientZoneVisible":true},{"id":31,"name":"RUTA DE COBRANZA","key":"rutaDeCobranza","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":35,"name":"Site","key":"site","attributeType":"client","type":"string","clientZoneVisible":true},{"id":36,"name":"Antena/Sectorial","key":"antenaSectorial","attributeType":"client","type":"string","clientZoneVisible":true},{"id":37,"name":"Password Antena Cliente","key":"passwordAntenaCliente","attributeType":"client","type":"string","clientZoneVisible":false}]
|
||||
|
||||
// Verificar si se obtuvieron los atributos
|
||||
if ($customAttributes && is_array($customAttributes)) {
|
||||
foreach ($customAttributes as $attribute) {
|
||||
// Verificar si 'name' contiene la palabra 'passwordAntenaCliente' sin distinguir mayúsculas y minúsculas
|
||||
if (isset($attribute['key']) && stripos($attribute['key'], 'passwordAntenaCliente') !== false) {
|
||||
$this->logger->info("ID correspondiente a 'passwordAntenaCliente': " . $attribute['id'] . PHP_EOL);
|
||||
$idPasswordAntenaCliente = $attribute['id'];
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL);
|
||||
}
|
||||
|
||||
//buscar en los attributes ($jsonData) del cliente el id del atributo personalizado 'passwordAntenaCliente' si no está o si está pero está en blanco se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID);
|
||||
|
||||
foreach ($jsonData['extraData']['entity']['attributes'] as $attribute) {
|
||||
if ($attribute['customAttributeId'] === $idPasswordAntenaCliente) {
|
||||
$this->logger->info("El valor de passwordAntenaValue es: " . $attribute['value'] . PHP_EOL);
|
||||
$passwordAntenaValue = $attribute['value'];
|
||||
}
|
||||
}
|
||||
|
||||
//si el value de passwordAntenaValue es igual a null o cadena vacía se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID);
|
||||
if ($passwordAntenaValue === null || $passwordAntenaValue === '') {
|
||||
$password = $this->notifierFacade->getVaultCredentials($clientID);
|
||||
$this->logger->info("El valor de passwordAntenaValue es null o cadena vacía, se manda llamar la función getVaultCredentials" . PHP_EOL);
|
||||
$this->logger->info("El valor de password es: " . $password . PHP_EOL);
|
||||
|
||||
if ($this->notifierFacade->patchClientCustomAttribute($clientID, $idPasswordAntenaCliente, $password)) {
|
||||
$this->logger->info("Se actualizó el atributo personalizado passwordAntenaCliente con el valor: " . $password . PHP_EOL);
|
||||
} else {
|
||||
$this->logger->info("No se pudo actualizar el atributo personalizado passwordAntenaCliente" . PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Ya existe un valor de passwordAntenaValue: " . $passwordAntenaValue . PHP_EOL);
|
||||
}
|
||||
$clientID = $jsonData['entityId'];
|
||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
|
||||
|
||||
//ejemplo de json_data: {"uuid":"17e043a7-03b5-4312-ab81-a7818124a77e","changeType":"edit","entity":"client","entityId":"158","eventName":"client.edit","extraData":{"entity":{"id":158,"userIdent":null,"previousIsp":null,"isLead":false,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":"2025-02-09T03:15:49-0600","companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false},"entityBeforeEdit":{"id":158,"userIdent":null,"previousIsp":null,"isLead":true,"clientType":1,"companyName":null,"companyRegistrationNumber":null,"companyTaxId":null,"companyWebsite":null,"street1":"23 San Luis","street2":null,"city":"Dolores Hidalgo Cuna de la Independencia Nacional","countryId":173,"stateId":null,"zipCode":"37804","fullAddress":"San Luis 23, Guadalupe, Dolores Hidalgo Cuna de la Independencia Nacional, Gto., M\u00e9xico","invoiceStreet1":null,"invoiceStreet2":null,"invoiceCity":null,"invoiceStateId":null,"invoiceCountryId":null,"invoiceZipCode":null,"invoiceAddressSameAsContact":true,"note":null,"sendInvoiceByPost":null,"invoiceMaturityDays":null,"stopServiceDue":null,"stopServiceDueDays":null,"organizationId":1,"tax1Id":null,"tax2Id":null,"tax3Id":null,"registrationDate":"2025-01-06T00:00:00-0600","leadConvertedAt":null,"companyContactFirstName":null,"companyContactLastName":null,"isActive":false,"firstName":"Luis","lastName":"Guti\u00e9rrez","username":null,"contacts":[{"id":162,"clientId":158,"email":null,"phone":null,"name":null,"isBilling":true,"isContact":true,"types":[{"id":1,"name":"Billing"},{"id":2,"name":"General"}]}],"attributes":[],"accountBalance":0,"accountCredit":0,"accountOutstanding":0,"currencyCode":"MXN","organizationName":"SIIP Pruebas","bankAccounts":[],"tags":[],"invitationEmailSentDate":null,"avatarColor":"#2196f3","addressGpsLat":21.153272,"addressGpsLon":-100.9134508,"isArchived":false,"generateProformaInvoices":null,"usesProforma":false,"hasOverdueInvoice":false,"hasOutage":false,"hasSuspendedService":false,"hasServiceWithoutDevices":false,"referral":null,"hasPaymentSubscription":false,"hasAutopayCreditCard":false}}}
|
||||
|
||||
// Validar que 'extraData' existe y contiene las claves necesarias
|
||||
if (
|
||||
isset($jsonData['extraData']['entityBeforeEdit']) &&
|
||||
isset($jsonData['extraData']['entity'])
|
||||
) {
|
||||
if (isset($jsonData['extraData']['entityBeforeEdit'], $jsonData['extraData']['entity'])) {
|
||||
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'];
|
||||
$entity = $jsonData['extraData']['entity'];
|
||||
|
||||
//$this->logger->debug('Validando claves dentro de entityBeforeEdit y entity');
|
||||
|
||||
// Validar si 'isLead' esta en true en entityBeforeEdit y en false en entity
|
||||
if (array_key_exists('isLead', $entityBeforeEdit) && array_key_exists('isLead', $entity)) {
|
||||
//$this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo isLead');
|
||||
|
||||
if (isset($entityBeforeEdit['isLead'], $entity['isLead'])) {
|
||||
$isLeadBefore = $entityBeforeEdit['isLead'];
|
||||
$isLeadAfter = $entity['isLead'];
|
||||
|
||||
// Comprobar si 'isLead' cambió de true a false
|
||||
if ($isLeadBefore === true && $isLeadAfter === false) {
|
||||
$this->logger->debug('El cliente cambió de potencial a cliente');
|
||||
//$this->pluginNotifierFacade->createStripeClient($notification); //Se comenta esta línea para que no se cree el cliente en Stripe al convertir el lead a cliente, ya que se hará al agregar la etiqueta 'CREAR CLABE STRIPE'
|
||||
} else {
|
||||
$this->logger->debug('El cliente no cambió de potencial a cliente');
|
||||
}
|
||||
} else {
|
||||
$this->logger->warning('El campo isLead no existe en entityBeforeEdit o entity');
|
||||
}
|
||||
|
||||
// buscar si existe la etiqueta 'STRIPE' en entity pero no en entityBeforeEdit
|
||||
$tags = $jsonData['extraData']['entity']['tags'];
|
||||
$tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags'];
|
||||
if (isset($entity['tags'], $entityBeforeEdit['tags'])) {
|
||||
$tags = $entity['tags'];
|
||||
$tagsBefore = $entityBeforeEdit['tags'];
|
||||
|
||||
$this->logger->debug('Validando claves dentro de entity y entityBeforeEdit');
|
||||
|
||||
// Validar que 'tags' existe en ambas entidades
|
||||
if (array_key_exists('tags', $jsonData['extraData']['entity']) && array_key_exists('tags', $jsonData['extraData']['entityBeforeEdit'])) {
|
||||
$this->logger->debug('Los datos entity y entityBeforeEdit contienen el campo tags');
|
||||
|
||||
$tags = $jsonData['extraData']['entity']['tags'];
|
||||
$tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags'];
|
||||
|
||||
$this->logger->debug('Validando si la etiqueta STRIPE existe en entity pero no en entityBeforeEdit');
|
||||
|
||||
// Comprobar si la etiqueta 'CREAR CLABE STRIPE' existe en 'tags' pero no en 'tagsBefore'
|
||||
$stripeTagExists = false;
|
||||
$clabeTagExistsBefore = false;
|
||||
$stripeTagExistsBefore = false;
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') {
|
||||
$stripeTagExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$clabeTagExists = false;
|
||||
$stripeTagExists = false;
|
||||
|
||||
foreach ($tagsBefore as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') {
|
||||
$stripeTagExistsBefore = true;
|
||||
break;
|
||||
}
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExistsBefore = true;
|
||||
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExistsBefore = true;
|
||||
}
|
||||
|
||||
// Comprobar si la etiqueta 'STRIPE' existe en 'tags' pero no en 'tagsBefore'
|
||||
if ($stripeTagExists && ! $stripeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregará al cliente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, true);
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag['name'] === 'CREAR CLABE STRIPE') $clabeTagExists = true;
|
||||
if ($tag['name'] === 'CREAR CLIENTE STRIPE') $stripeTagExists = true;
|
||||
}
|
||||
|
||||
if ($clabeTagExists && !$clabeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregó al cliente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLABE STRIPE', true);
|
||||
}
|
||||
|
||||
if ($stripeTagExists && !$stripeTagExistsBefore) {
|
||||
$this->logger->debug('La etiqueta CREAR CLIENTE STRIPE se agregó al cliente');
|
||||
$this->pluginNotifierFacade->createStripeClient($notification, 'CREAR CLIENTE STRIPE', false);
|
||||
}
|
||||
} else {
|
||||
$this->logger->warning('El campo tags no existe en entity o entityBeforeEdit');
|
||||
}
|
||||
|
||||
} else {
|
||||
} else {
|
||||
$this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData');
|
||||
}
|
||||
|
||||
$this->notifierFacade->verifyClientActionToDo($notification);
|
||||
|
||||
} else if ($notification->eventName === 'client.add') {
|
||||
$this->logger->debug('Se agregó un nuevo cliente' . PHP_EOL);
|
||||
$this->logger->debug('Se agregó un nuevo cliente');
|
||||
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
|
||||
|
||||
// Verificar que existen tanto 'entityBeforeEdit' como 'entity'
|
||||
// if (isset($jsonData['extraData']['entity']['isLead'])) {
|
||||
// $this->logger->debug('El campo isLead existe en los datos del evento');
|
||||
// $isLead = $jsonData['extraData']['entity']['isLead'];
|
||||
|
||||
// // Comprobar si 'isLead' es true
|
||||
// if ($isLead === true) {
|
||||
// $this->logger->debug('El cliente es potencial');
|
||||
// $this->pluginNotifierFacade->createStripeClient($notification);
|
||||
// } else {
|
||||
// $this->logger->debug('El cliente no es potencial');
|
||||
// $this->pluginNotifierFacade->createStripeClient($notification);
|
||||
// }
|
||||
// } else {
|
||||
// $this->logger->warning('El campo isLead no existe en los datos del evento');
|
||||
// }
|
||||
|
||||
} else if ($notification->eventName === 'service.edit') {
|
||||
$this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL);
|
||||
$this->notifierFacade->verifyServiceActionToDo($notification);
|
||||
//ejemplo de json_data: {"uuid":"06d281ca-d78e-4f0a-a282-3a6b77d25da0","changeType":"edit","entity":"service","entityId":"155","eventName":"service.edit","extraData":{"entity":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]},"entityBeforeEdit":{"id":155,"prepaid":false,"clientId":171,"status":1,"name":"Basico 300","fullAddress":"Campeche 56, Dolores Hidalgo, 37800","street1":"Campeche 56","street2":null,"city":"Dolores Hidalgo","countryId":173,"stateId":null,"zipCode":"37800","note":null,"addressGpsLat":21.1572461,"addressGpsLon":-100.9377137,"servicePlanId":6,"servicePlanPeriodId":26,"price":300,"hasIndividualPrice":false,"totalPrice":300,"currencyCode":"MXN","invoiceLabel":null,"contractId":null,"contractLengthType":1,"minimumContractLengthMonths":null,"activeFrom":"2025-05-21T00:00:00-0600","activeTo":null,"contractEndDate":null,"discountType":0,"discountValue":null,"discountInvoiceLabel":"Descuento","discountFrom":null,"discountTo":null,"tax1Id":null,"tax2Id":null,"tax3Id":null,"invoicingStart":"2025-05-21T00:00:00-0600","invoicingPeriodType":1,"invoicingPeriodStartDay":1,"nextInvoicingDayAdjustment":10,"invoicingProratedSeparately":true,"invoicingSeparately":false,"sendEmailsAutomatically":null,"useCreditAutomatically":true,"servicePlanName":"Basico 300","servicePlanPrice":300,"servicePlanPeriod":1,"servicePlanType":"Internet","downloadSpeed":8,"uploadSpeed":8,"hasOutage":false,"unmsClientSiteStatus":null,"fccBlockId":null,"lastInvoicedDate":null,"unmsClientSiteId":"359cb58d-e64f-453a-890e-23d5abb4f116","attributes":[],"addressData":null,"suspensionReasonId":null,"serviceChangeRequestId":null,"setupFeePrice":null,"earlyTerminationFeePrice":null,"downloadSpeedOverride":null,"uploadSpeedOverride":null,"trafficShapingOverrideEnd":null,"trafficShapingOverrideEnabled":false,"servicePlanGroupId":null,"suspensionPeriods":[],"surcharges":[]}}}
|
||||
//obtener el clientID y asginarlo a la variable $clientID
|
||||
$clientID = $jsonData['extraData']['entity']['clientId'];
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']); //Obtener los atributos del sistema que estén vinculados a la entidad "cliente"
|
||||
//$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL);
|
||||
|
||||
$idPasswordAntenaCliente = null;
|
||||
$passwordAntenaValue = null;
|
||||
|
||||
//ejemplo de $customAttributes: [{"id":1,"name":"ip","key":"ip","attributeType":"client","type":"string","clientZoneVisible":false},{"id":2,"name":"ubntpass","key":"ubntpass","attributeType":"client","type":"string","clientZoneVisible":false},{"id":3,"name":"adminpass","key":"adminpass","attributeType":"client","type":"string","clientZoneVisible":true},{"id":4,"name":"ssid","key":"ssid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":5,"name":"clavessid","key":"clavessid","attributeType":"client","type":"string","clientZoneVisible":true},{"id":6,"name":"clave","key":"clave","attributeType":"client","type":"string","clientZoneVisible":true},{"id":11,"name":"latitud","key":"latitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":12,"name":"longitud","key":"longitud","attributeType":"client","type":"string","clientZoneVisible":true},{"id":16,"name":"instalador","key":"instalador","attributeType":"client","type":"string","clientZoneVisible":true},{"id":17,"name":"creado por","key":"creadoPor","attributeType":"client","type":"string","clientZoneVisible":true},{"id":21,"name":"Chat de CallBell","key":"chatDeCallbell","attributeType":"client","type":"string","clientZoneVisible":false},{"id":22,"name":"uuid","key":"uuid","attributeType":"client","type":"string","clientZoneVisible":false},{"id":23,"name":"zona","key":"zona","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":29,"name":"Stripe Customer ID","key":"stripeCustomerId","attributeType":"client","type":"string","clientZoneVisible":true},{"id":30,"name":"Clabe Interbancaria","key":"clabeInterbancaria","attributeType":"client","type":"string","clientZoneVisible":true},{"id":31,"name":"RUTA DE COBRANZA","key":"rutaDeCobranza","attributeType":"client","type":"enum","clientZoneVisible":true},{"id":35,"name":"Site","key":"site","attributeType":"client","type":"string","clientZoneVisible":true},{"id":36,"name":"Antena/Sectorial","key":"antenaSectorial","attributeType":"client","type":"string","clientZoneVisible":true},{"id":37,"name":"Password Antena Cliente","key":"passwordAntenaCliente","attributeType":"client","type":"string","clientZoneVisible":false}]
|
||||
|
||||
// Verificar si se obtuvieron los atributos
|
||||
if ($customAttributes && is_array($customAttributes)) {
|
||||
foreach ($customAttributes as $attribute) {
|
||||
// Verificar si 'name' contiene la palabra 'passwordAntenaCliente' sin distinguir mayúsculas y minúsculas
|
||||
if (isset($attribute['key']) && stripos($attribute['key'], 'passwordAntenaCliente') !== false) {
|
||||
$this->logger->info("ID correspondiente a 'passwordAntenaCliente': " . $attribute['id'] . PHP_EOL);
|
||||
$idPasswordAntenaCliente = $attribute['id'];
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL);
|
||||
}
|
||||
|
||||
//buscar en los attributes ($jsonData) del cliente el id del atributo personalizado 'passwordAntenaCliente' si no está o si está pero está en blanco se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID);
|
||||
|
||||
foreach ($jsonData['extraData']['entity']['attributes'] as $attribute) {
|
||||
if ($attribute['customAttributeId'] === $idPasswordAntenaCliente) {
|
||||
$this->logger->info("El valor de passwordAntenaValue es: " . $attribute['value'] . PHP_EOL);
|
||||
$passwordAntenaValue = $attribute['value'];
|
||||
}
|
||||
}
|
||||
|
||||
//si el value de passwordAntenaValue es igual a null o cadena vacía se manda llamar la función $this->notifierFacade->getVaultCredentials($clientID);
|
||||
if ($passwordAntenaValue === null || $passwordAntenaValue === '') {
|
||||
$password = $this->notifierFacade->getVaultCredentials($clientID);
|
||||
$this->logger->info("El valor de passwordAntenaValue es null o cadena vacía, se manda llamar la función getVaultCredentials" . PHP_EOL);
|
||||
$this->logger->info("El valor de password es: " . $password . PHP_EOL);
|
||||
|
||||
if ($this->notifierFacade->patchClientCustomAttribute($clientID, $idPasswordAntenaCliente, $password)) {
|
||||
$this->logger->info("Se actualizó el atributo personalizado passwordAntenaCliente con el valor: " . $password . PHP_EOL);
|
||||
} else {
|
||||
$this->logger->info("No se pudo actualizar el atributo personalizado passwordAntenaCliente" . PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$this->logger->info("Ya existe un valor de passwordAntenaValue: " . $passwordAntenaValue . PHP_EOL);
|
||||
}
|
||||
$clientID = $jsonData['extraData']['entity']['clientId'];
|
||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
|
||||
|
||||
} else if ($notification->eventName === 'service.suspend') {
|
||||
$this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL);
|
||||
@ -606,18 +422,13 @@ class Plugin
|
||||
$this->logger->debug('Eliminación de Factura' . PHP_EOL);
|
||||
$this->notifierFacade->verifyInvoiceActionToDo($notification);
|
||||
} else if ($notification->eventName === 'job.add') {
|
||||
$this->logger->debug('Se ha agregado un nuevo trabajo' . PHP_EOL);
|
||||
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
|
||||
//Ejemplo de json_data: {"uuid":"434b3da0-984a-4358-a1b6-2a4418bacc49","changeType":"insert","entity":"job","entityId":"38","eventName":"job.add","extraData":{"entity":{"id":38,"title":"Servicio","description":"Revisar Router","assignedUserId":null,"clientId":2,"date":null,"duration":60,"status":0,"address":"31 Chiapas, Dolores Hidalgo Cuna de la Independencia Nacional, 37800, Mexico","gpsLat":null,"gpsLon":null,"attachments":[],"tasks":[]},"entityBeforeEdit":null}}
|
||||
//Extraer el valor de title en una variable y concatenarle como prefijo la cadena "[SINENVIONOTIFICACION]" por ejemplo: "[NOTIFICACION-PENDIENTE]Servicio"
|
||||
$title = $jsonData['extraData']['entity']['title'];
|
||||
$this->logger->debug('Se ha agregado un nuevo trabajo');
|
||||
$title = $jsonData['extraData']['entity']['title'] ?? '';
|
||||
$title = '[NOTIFICACION-PENDIENTE]' . $title;
|
||||
$this->ucrmApi = UcrmApi::create();
|
||||
$responsePatch = $this->ucrmApi->patch('scheduling/jobs/' . $jsonData['entityId'], [
|
||||
'title' => $title,
|
||||
]);
|
||||
|
||||
$this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch) . PHP_EOL);
|
||||
$this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch));
|
||||
|
||||
} else if ($notification->eventName === 'job.edit') {
|
||||
$this->logger->debug('Se actualiza un trabajo' . PHP_EOL);
|
||||
@ -650,32 +461,29 @@ class Plugin
|
||||
//Valores de status y su significado: 0 = abierto, 1= En curso, 2 = Cerrado
|
||||
|
||||
if ($statusAfter == 1) {
|
||||
// Comprobar si 'assignedUserId' cambió
|
||||
// if (($assignedUserIdBefore === null && $assignedUserIdAfter != null) || ($statusBefore == 0 && $statusAfter == 1)) { //Si el campo "assignedUserId" cambió de null a un valor
|
||||
// $this->logger->debug('El instalador cambió de null a un valor');
|
||||
$isNewActivation = ($statusBefore == 0 && $statusAfter == 1);
|
||||
$isTechChange = ($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore && $dateBefore === $dateAfter);
|
||||
$isReprogramming = ($assignedUserIdBefore != null && $assignedUserIdBefore === $assignedUserIdAfter && $dateBefore != $dateAfter);
|
||||
$isBothChange = ($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore && $dateBefore != $dateAfter);
|
||||
$isDeassignment = ($assignedUserIdBefore != null && $assignedUserIdAfter === null);
|
||||
|
||||
// $this->notifierFacade->verifyJobActionToDo($jsonData); // Se envía notificación de trabajo asignado
|
||||
// }
|
||||
|
||||
//Si el campo status cambió de 0 a 1 y title comienza con el prefijo [NOTIFICACION-PENDIENTE]
|
||||
if (($statusBefore == 0 && $statusAfter == 1) && strpos($currentTitle, $pendingPrefix) !== false) { // Se envía notificación de trabajo asignado
|
||||
$this->logger->debug('El instalador cambió de null a un valor');
|
||||
if ($isNewActivation) {
|
||||
$this->logger->debug('Trabajo iniciado o asignado: Notificando...');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, false, false);
|
||||
} else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore === $dateAfter)) { //Si el campo "assignedUserId" cambió de un valor a otro y la fecha no cambió
|
||||
$this->logger->debug('No hay reprogramación de trabajo pero si hay cambio de instalador');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, false, true); // Se envía notificación de trabajo reasignado
|
||||
} else if (($assignedUserIdBefore != null && $assignedUserIdBefore === $assignedUserIdAfter) && ($dateBefore != $dateAfter)) { //Si el campo "assignedUserId" no cambió y la fecha cambió
|
||||
$this->logger->debug('Se reprogramó el trabajo pero no hubo cambio de instalador');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, true, false); // Se envía notificación de reprogramación de trabajo
|
||||
} else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore != $dateAfter)) {
|
||||
$this->logger->debug('Se reprogramó el trabajo y hubo cambio de instalador');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, true, true); // Se envía notificación de trabajo reasignado
|
||||
} else if ($assignedUserIdBefore != null && $assignedUserIdAfter === null) { //Si el campo "assignedUserId" cambió de un valor a null
|
||||
$this->logger->debug('El instalador cambió de un valor a null');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, null, true); // Se envía notificación de trabajo desasignado
|
||||
} else if ($isTechChange) {
|
||||
$this->logger->debug('Cambio de técnico sin reprogramación');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, false, true);
|
||||
} else if ($isReprogramming) {
|
||||
$this->logger->debug('Reprogramación sin cambio de técnico');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, true, false);
|
||||
} else if ($isBothChange) {
|
||||
$this->logger->debug('Reprogramación y cambio de técnico');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, true, true);
|
||||
} else if ($isDeassignment) {
|
||||
$this->logger->debug('Técnico desasignado');
|
||||
$this->notifierFacade->verifyJobActionToDo($jsonData, null, true);
|
||||
} else {
|
||||
$this->logger->debug('No hubo cambio en el instalador ni en la fecha');
|
||||
//$this->notifierFacade->verifyJobActionToDo($jsonData); // Se envía notificación de trabajo asignado
|
||||
$this->logger->debug('Edición de trabajo "En curso" sin cambios de fecha o técnico relevantes para notificación');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,13 +31,18 @@ class PluginDataValidator
|
||||
{
|
||||
$pluginData = $this->optionsManager->load();
|
||||
$valid = true;
|
||||
if (empty($pluginData->twilioAccountSid)) {
|
||||
$this->errors[] = 'Not valid configuration: Twilio Account SID must be configured';
|
||||
if (empty($pluginData->tokencallbell)) {
|
||||
$this->errors[] = 'Configuración inválida: Token de CallBell es requerido';
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
if (empty($pluginData->twilioAuthToken)) {
|
||||
$this->errors[] = 'Not valid configuration: Twilio Auth Token must be configured';
|
||||
if (empty($pluginData->apitoken)) {
|
||||
$this->errors[] = 'Configuración inválida: Token de API de UCRM es requerido';
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
if (empty($pluginData->ipserver)) {
|
||||
$this->errors[] = 'Configuración inválida: IP del Servidor es requerida';
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user