Compare commits

...

14 Commits

Author SHA1 Message Date
5425659428 antes de la limpieza y refacorización del código 2025-12-24 02:03:41 -06:00
0e37fd153f feat(stripe-sync): implementar resolución dinámica de métodos de pago y corregir validación API
- Se agregó 'getPaymentMethodIdByName' para buscar automáticamente el ID de "Transferencia bancaria" por nombre, asegurando portabilidad entre servidores UISP.
- Se implementó el manejo del webhook 'customer_cash_balance_transaction.created' para el registro automático de pagos fondeados.
- Fix: Se corrigió error 422 en la API de UCRM forzando el cast de 'clientId' a integer y 'methodId' a string (GUID).
- Se actualizó la documentación (README/CHANGELOG) con instrucciones de configuración de webhooks.
2025-12-23 14:50:57 -06:00
506615e911 Se agrega nuevo campo en los ajustes del plugin para agregar la API KEY del UNMS con el ID 'unmsApiToken' 2025-10-20 15:13:21 -06:00
2e21e09c2c Corrección de bug para poder utilizar la dirección ip del servicio Puppeteer dentro del código para referencias de OXXO PAGO, también se agregó un campo de configuración para el servicio de Puppeteer así como el código para utilizar dicho campo. 2025-08-01 08:47:33 -06:00
a0f286969a Actualización que ajusta los permisos de acceso a la API del CRM y NMS, además se realizaron cambios en el menú de ajustes para agregar el campo del toke de la api de nms y el campo del id del administrador para pagos en linea con Stripe, además se hizo el ajuste para el bug que no permitía realizar intenciones de pago cuando la cantidad trae signo de pesos 2025-07-31 15:23:49 -06:00
c9e2466353 Solucionado el bug que impedía regresar en la respuesta el monto total hacia el bot de CallBell, lo que hacía que mostrara siempre Cantidad cero, esto se realizó en el archivo AbstractOxxoOperationsFacade.php 2025-06-11 11:07:59 -06:00
8d8a1ec648 Modificaciones para enviar referencias de OXXO PAGO por medio de imagen 2025-06-10 02:46:31 -06:00
c73a51bbf2 Se modificó la información para el envío de notificaciones a llos instaladores en el flujo de trabajo para la desasignación de tareas que hacía que no se viera correctamente la información 2025-06-05 12:01:03 -06:00
5374054289 Cambios para quitar la hora de los mensajes que se envían a los clientes, corrección en la función getVaultCredentials para poder enviar el mensaje con la contraseña al instalador 2025-06-02 17:45:28 -06:00
e899945ca3 Versión 2.8.2 Rutina de envio de mensajes con plantillas de utilidad refactorizado, además tipo de pago 'applied_to_payment' en en analisis de webhooks de Stripe 2025-05-27 15:57:43 -06:00
e34b3ec0f8 Versión 2.8.0 README ACTUALIZADO 2025-05-21 21:39:50 -06:00
3a39a53da6 Versión 2.8.0 2025-05-21 21:31:09 -06:00
7cb26fe735 Se agregó una función que permite recuperar una contraseña de antena en función del ID del cliente y enviarla por mensaje de notificación al instalador para tareas que se le asignen, además de modificaron los IDs de plantillas de mensajes y se eleiminó el campo de dirección para la notioficación de tarea o servicio del instalador. 2025-05-10 11:21:43 -06:00
9a3af1f3d4 Version 2.7.1 con la opción de generar clabes interbancarias por medio de una etiqueta con nombre 'CREARCLABESTRIPE' y además permite crear las clabes de manera automática cuando el cliente pasa de potencial a cliente regular, se corrige un error que permitía generar de manera indefinida clabes para un cliente, además también se agregó la eliminación de la etiqueta 'CREARCLABESTRIPE' de manera automática cuando termina de generar una clabe interbancaria y cliente de Sripe y también valida si ya tiene uno creado con anterioridad para descartar solicitudes hechas a clientes que ya posean dichos datos 2025-03-25 07:46:08 -06:00
15 changed files with 4191 additions and 18816 deletions

3
.gitignore vendored
View File

@ -1,5 +1,8 @@
*.pdf *.pdf
*.log *.log
*.png *.png
*.jpeg
.vscode/ .vscode/
*.zip *.zip
vouchers_oxxo/
comprobantes/

61
CHANGELOG.md Normal file
View File

@ -0,0 +1,61 @@
# CHANGELOG - siip-whatsapp-notifications
## VERSIÓN 2.9.3 - 23-12-2025
### 🟢 Novedades
1⃣ Resolución dinámica del ID del método de pago ("Transferencia bancaria") mediante consulta a la API de UISP, mejorando la portabilidad del plugin entre distintos servidores.
2⃣ Implementación de registro de pago automático desde Webhook Stripe para eventos de tipo `customer_cash_balance_transaction.created` (Saldo aplicado).
### 🟡 Bugs Resueltos
1⃣ Se corrigió el error de validación de la API de UCRM (422) mediante el cast explícito de `clientId` a integer y el uso de `methodId` como string.
## VERSIÓN 2.9.2
### 🟡 Bugs Resueltos
1⃣ Se solucionó un bug que impedía obtener la contraseñas de la bóveda, ya que el response de la API cambió en la última actualización y la esstructura nueva impedía acceder al dato del password
## VERSIÓN 2.8.8
### 🟡 Bugs Resueltos
1⃣ Se solucionó un bug que impedía al BOT del CallBell mostrar el monto de la referencia de OXXO en el mensaje donde se le entrega el voucher al cliente, para el caso donde el cliente elegía crear su referencia con la CANTIDAD TOTAL.
## VERSIÓN 2.8.7
### 🟢 Novedades
1⃣ Ahora las referencias de **OXXO Pago** han cambiado, en lugar de enviarse la URL o link de pago al cliente será la imagen del código de barras y la información que aparece en el link directamente en el mensaje, de esta manera será más cómodo para el cliente tener la imagen en su chat a tener que abrir una URL o link externo.
### 🔵 Mejoras
1⃣ Se modificaron nodos del bot ***OXXO_BOT*** para poder adaptar esta actualización correctamente.
2⃣ Mejoras en el código fuente del flujo de trabajo para las referencias de OXXO PAGO.
## VERSIÓN 2.8.6
### 🔵 Mejoras
1⃣ Se modificó la información para el envío de notificaciones a llos instaladores en el flujo de trabajo para la desasignación de tareas que hacía que no se viera correctamente la información
## VERSIÓN 2.8.5
### 🔵 Mejoras
1⃣ Se modificó la información para el envío de notificaciones a los clientes para su visita técnica: ahora ya no se envían las horas en que serán las visitas, SOLO LA FECHA.
2⃣ Se crearon nuevas plantillas de tipo utilidad y se adaptaron al flujo de trabajo en el código.
### 🟡 Bugs Resueltos
1⃣ Se soluciono el bug que impedía enviar notificaciones a los instaladores para nuevas tareas.
## VERSIÓN 2.8.2
### 🟡 Bugs Resueltos
1⃣ No se enviaban las notificaciones de las tareas al instalador. Se cambió la plantilla de CallBell o WhatsApp con 3 variables en lugar de 8.
2⃣ Se agregó un nuevo tipo de pago ("applied_to_payment") en las propiedades de los Webhooks recibidos mediante Stripe por concepto de transferencias bancarias. Ya que sólo se revisaba el tipo de pago "funded" y eso hacía que no enviara los comprobantes de pago a los clientes para todos los casos.
## VERSIÓN 2.8.1
### 🟡 Bugs Resueltos
1⃣ No se enviaban las notificaciones de las tareas al instalador.
## VERSIÓN 2.8.0
### 🟢 Novedades
1⃣ Envío de contraseña de antena en el mensaje que se manda al instalador cuando se le asigna una tarea/servicio.
2⃣ Para clientes nuevos o que no tengan el campo personalizado de “Password Antena Cliente” al actualizarlos se les asignará ese campo o cuando se les actualice su servicio.
3⃣ Ahora se puede modificar tantas veces sea necesaria una tarea o servicio como su fecha o el instalador mientras la tarea permanezca en estado “Abiertos” SIN que se envíen notificaciones a los clientes o instaladores.
4⃣ Se agregó un prefijo al título de la tarea/servicio agendado cuando recién se da de alta para identificar que esta no ha sido notificada aún y se pueden realizar ajustes.
### 🔵 Mejoras
1⃣ Se modificó el flujo de trabajo para el envío de notificaciones a los clientes para su visita técnica, ahora son cuatro distintos tipos de flujo, anteriormente tres.
2⃣ Se reemplazaron algunas plantillas de mensajes de CallBell.
3⃣ Se renombró el uso de la etiqueta “CREARCLABESTRIPE” por “CREAR CLABE STRIPE” para un mejor entendimiento de su uso.
### 🟡 Bugs Resueltos
1⃣ Envío de notificaciones a los clientes y a los instaladores al CERRAR una tarea/servicio (Se mandaban mensajes de asignación y de visita del técnico ) haciendo no posible cerrar las tareas.

245
README.md
View File

@ -1,210 +1,73 @@
# SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp # SIIP - WhatsApp Notifications & Stripe Payments for UCRM
Este plugin sincroniza los clientes del sitema UISP CRM con los contactos de WhatsApp en CallBell, además procesa pagos de Stripe como las trasferencias bancarias y genera referencias de pago vía OXXO, además envía comprobantes de pago en formato imagen PNG o texto vía Whatsapp a los clientes Este plugin es una solución integral para automatizar la comunicación con clientes y la gestión de pagos en UISP/UCRM. Actúa como puente entre el CRM, Stripe y la plataforma de mensajería CallBell.
## 🚀 Funcionalidades Principales
## Configuration - **Notificaciones Dinámicas**: Envío automático de mensajes por WhatsApp para facturas, pagos, suspensiones y reactivaciones.
- **Gestión de Agenda (Jobs/Tasks)**: Notificación inteligente a técnicos e instaladores sobre nuevas tareas, reprogramaciones y desasignaciones.
- **Pagos con Stripe & OXXO**: Generación automatizada de referencias de OXXO con captura de pantalla (Puppeteer) y alojamiento en la nube (WordPress).
- **Sincronización CallBell**: Mantenimiento de la información del cliente actualizada en la plataforma de chat CallBell.
- **Resolución Dinámica**: Detección automática de métodos de pago y atributos personalizados para máxima portabilidad.
* Install the plugin into UCRM and enable it. I.e. download the plugin [zip file](https://github.com/Ubiquiti-App/UCRM-plugins/raw/master/plugins/sms-twilio/sms-twilio.zip) and upload it to UCRM in System > Plugins. ---
* Keep execution period at "don't execute automatically" - the plugin will react to webhook events.
* Set up with data which you obtain from [Twilio Console](https://twilio.com/console):
* Account SID
* Auth Token
* SMS number to send from
Note: there are two sets of credentials available, the default ("LIVE credentials") for actual use and [test credentials](https://www.twilio.com/console/project/settings) for development. ## 🏗️ Arquitectura del Sistema
* Customize the texts you wish to send to a client when an event happens El plugin utiliza un patrón de diseño basado en **Facades** para desacoplar la lógica de despacho de los servicios externos.
* Each event has its own row
* Empty row means "do not send SMS for this"
* It is possible to replace predefined variables: `%%some.variable%%`, see full list below
* If a variable is not set for a client, it is replaced with an empty string
* Save the configuration
* Enable the plugin
* Add webhook (button next to Public URL)
* Select events about which to notify clients and save the webhook endpoint
## Usage ### Componentes Clave
* In UCRM admin, go to System / Webhooks / Endpoints
* Click Test Endpoint
* Go to System / Plugins / SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp
* In the log output, you'll see `Webhook test successful.`
## Variables replaced - **Despachador Central (`Plugin.php`)**: Gestiona tanto los webhooks internos de UCRM como los externos (Stripe). Controla el estado de las notificaciones de tareas mediante prefijos en los títulos (`[NOTIFICACION-PENDIENTE]`).
- **Fachada de Mensajería (`AbstractMessageNotifierFacade`)**: Contiene la lógica de negocio para decidir qué notificar, cuándo y a quién.
- **Interfaz CallBell (`ClientCallBellAPI`)**: Encapsula las llamadas a la API de CallBell, manejando plantillas y mensajes multimedia.
- **Operaciones OXXO (`AbstractOxxoOperationsFacade`)**: Orquesta el flujo de pago: Stripe (Referencia) -> Puppeteer (Screenshot) -> WordPress (Hosting) -> CallBell (WhatsApp).
These are loaded from UCRM API, and reflect the structure returned. ---
Client variables are replaced always; payment invoice and service only with the applicable events.
### Client variables ## 🛠️ Requisitos e Integraciones
* `%%client.id%%` => 20 Para un funcionamiento óptimo, el plugin requiere:
* `%%client.userIdent%%` => '18'
* `%%client.previousIsp%%` => ''
* `%%client.isLead%%` => false
* `%%client.clientType%%` => 1
* `%%client.companyName%%` => ''
* `%%client.companyRegistrationNumber%%` => ''
* `%%client.companyTaxId%%` => ''
* `%%client.companyWebsite%%` => ''
* `%%client.street1%%` => '2544 Hillview Drive'
* `%%client.street2%%` => ''
* `%%client.city%%` => 'San Jose'
* `%%client.countryId%%` => 249
* `%%client.stateId%%` => 5
* `%%client.zipCode%%` => '95113'
* `%%client.invoiceStreet1%%` => ''
* `%%client.invoiceStreet2%%` => ''
* `%%client.invoiceCity%%` => ''
* `%%client.invoiceStateId%%` => ''
* `%%client.invoiceCountryId%%` => ''
* `%%client.invoiceZipCode%%` => ''
* `%%client.invoiceAddressSameAsContact%%` => true
* `%%client.note%%` => ''
* `%%client.sendInvoiceByPost%%` => false
* `%%client.invoiceMaturityDays%%` => 14
* `%%client.stopServiceDue%%` => true
* `%%client.stopServiceDueDays%%` => 7
* `%%client.organizationId%%` => 1
* `%%client.tax1Id%%` => 1
* `%%client.tax2Id%%` => ''
* `%%client.tax3Id%%` => ''
* `%%client.registrationDate%%` => '2016-04-26 00:00'
* `%%client.companyContactFirstName%%` => ''
* `%%client.companyContactLastName%%` => ''
* `%%client.isActive%%` => false
* `%%client.firstName%%` => 'Tyson'
* `%%client.lastName%%` => 'Doe'
* `%%client.username%%` => 'tyson.doe@example.com'
* `%%client.accountBalance%%` => 0
* `%%client.accountCredit%%` => 0
* `%%client.accountOutstanding%%` => 0
* `%%client.currencyCode%%` => 'USD'
* `%%client.organizationName%%` => 'UBNT ISP'
* `%%client.invitationEmailSentDate%%` => ''
* `%%client.avatarColor%%` => '#e53935'
* `%%client.addressGpsLat%%` => 37.401482000001
* `%%client.addressGpsLon%%` => -121.966545
* `%%client.message%%` => 'This is an example message sent from the Messaging feature.'
### Invoice variables 1. **UISP/UCRM**: Versión compatible con el SDK de Ubiquiti.
* `%%invoice.id%%` => 4 2. **Stripe API**: Token y Webhooks configurados para transacciones en tiempo real.
* `%%invoice.clientId%%` => 20 3. **CallBell API**: Token de acceso para el envío de mensajes a través de plantillas de WhatsApp.
* `%%invoice.number%%` => '2016050002' 4. **Microservicio Puppeteer**: Un contenedor Docker con la API de Puppeteer para capturar vouchers.
* `%%invoice.createdDate%%` => '2016-05-03 00:00' 5. **Hosting WordPress (FTP)**: Para alojar temporalmente las imágenes de los vouchers enviadas a los clientes.
* `%%invoice.dueDate%%` => '2016-05-17 00:00'
* `%%invoice.emailSentDate%%` => '2018-08-24 00:00'
* `%%invoice.maturityDays%%` => 14
* `%%invoice.notes%%` => ''
* `%%invoice.adminNotes%%` => ''
* `%%invoice.subtotal%%` => 7.88
* `%%invoice.discount%%` => ''
* `%%invoice.discountLabel%%` => ''
* `%%invoice.total%%` => 7.88
* `%%invoice.amountPaid%%` => 7.88
* `%%invoice.currencyCode%%` => 'USD'
* `%%invoice.status%%` => 3
* `%%invoice.invoiceTemplateId%%` => 1
* `%%invoice.organizationName%%` => 'UBNT ISP'
* `%%invoice.organizationRegistrationNumber%%` => ''
* `%%invoice.organizationTaxId%%` => ''
* `%%invoice.organizationStreet1%%` => '2580 Orchard Parkway'
* `%%invoice.organizationStreet2%%` => ''
* `%%invoice.organizationCity%%` => 'New York'
* `%%invoice.organizationStateId%%` => 1
* `%%invoice.organizationCountryId%%` => 249
* `%%invoice.organizationZipCode%%` => '10017'
* `%%invoice.organizationBankAccountName%%` => ''
* `%%invoice.organizationBankAccountField1%%` => ''
* `%%invoice.organizationBankAccountField2%%` => ''
* `%%invoice.clientFirstName%%` => 'Tyson'
* `%%invoice.clientLastName%%` => 'Doe'
* `%%invoice.clientCompanyName%%` => ''
* `%%invoice.clientCompanyRegistrationNumber%%` => ''
* `%%invoice.clientCompanyTaxId%%` => ''
* `%%invoice.clientStreet1%%` => '685 Third Avenue'
* `%%invoice.clientStreet2%%` => ''
* `%%invoice.clientCity%%` => 'New York'
* `%%invoice.clientCountryId%%` => 249
* `%%invoice.clientStateId%%` => 5
* `%%invoice.clientZipCode%%` => '10017'
* `%%invoice.uncollectible%%` => false
### Payment variables ---
* `%%payment.id%%` => 28
* `%%payment.clientId%%` => 20
* `%%payment.invoiceId%%` => ''
* `%%payment.method%%` => 2
* `%%payment.checkNumber%%` => ''
* `%%payment.createdDate%%` => '2018-08-24 11:36'
* `%%payment.amount%%` => 1
* `%%payment.currencyCode%%` => 'USD'
* `%%payment.note%%` => ''
* `%%payment.receiptSentDate%%` => ''
* `%%payment.providerName%%` => ''
* `%%payment.providerPaymentId%%` => ''
* `%%payment.providerPaymentTime%%` => ''
* `%%payment.creditAmount%%` => 0
* `%%payment.applyToInvoicesAutomatically%%` => false
### Service variables ## 🏷️ Configuración de UCRM
* `%%service.id%%` => 23
* `%%service.clientId%%` => 20
* `%%service.status%%` => 1
* `%%service.name%%` => 'Mini'
* `%%service.street1%%` => '622 Hide A Way Road'
* `%%service.street2%%` => ''
* `%%service.city%%` => 'San Jose'
* `%%service.countryId%%` => 249
* `%%service.stateId%%` => 5
* `%%service.zipCode%%` => '95135'
* `%%service.note%%` => ''
* `%%service.addressGpsLat%%` => 37.232849
* `%%service.addressGpsLon%%` => -121.752502
* `%%service.servicePlanId%%` => 1
* `%%service.servicePlanPeriodId%%` => 2
* `%%service.price%%` => 25
* `%%service.hasIndividualPrice%%` => false
* `%%service.totalPrice%%` => 25
* `%%service.currencyCode%%` => 'USD'
* `%%service.invoiceLabel%%` => ''
* `%%service.contractId%%` => ''
* `%%service.contractLengthType%%` => 1
* `%%service.minimumContractLengthMonths%%` => ''
* `%%service.activeFrom%%` => '2016-05-03T00:00:00+0000'
* `%%service.activeTo%%` => ''
* `%%service.contractEndDate%%` => ''
* `%%service.discountType%%` => 0
* `%%service.discountValue%%` => ''
* `%%service.discountInvoiceLabel%%` => ''
* `%%service.discountFrom%%` => ''
* `%%service.discountTo%%` => ''
* `%%service.tax1Id%%` => ''
* `%%service.tax2Id%%` => ''
* `%%service.tax3Id%%` => ''
* `%%service.invoicingStart%%` => '2016-05-03T00:00:00+0000'
* `%%service.invoicingPeriodType%%` => 1
* `%%service.invoicingPeriodStartDay%%` => 1
* `%%service.nextInvoicingDayAdjustment%%` => 0
* `%%service.invoicingProratedSeparately%%` => true
* `%%service.invoicingSeparately%%` => false
* `%%service.sendEmailsAutomatically%%` => false
* `%%service.useCreditAutomatically%%` => true
* `%%service.servicePlanName%%` => 'Mini'
* `%%service.servicePlanPrice%%` => 25
* `%%service.servicePlanPeriod%%` => 3
* `%%service.downloadSpeed%%` => 10
* `%%service.uploadSpeed%%` => 10
* `%%service.hasOutage%%` => true
* `%%service.stopReason%%` => 'Payments overdue'
### Contactos y Etiquetas
El plugin selecciona los números de destino basándose en el **Tipo de Contacto** definido en UCRM:
- `WhatsApp`: Recibe notificaciones y actualizaciones de datos.
- `WhatsNotifica`: Recibe solo notificaciones.
- `WhatsActualiza`: Utilizado solo para sincronizar datos con CallBell.
## Developers ### Atributos Personalizados
* This plugin is MIT-licensed and can be used by developers as a template for integrating with a different messaging solution: Es necesario configurar los siguientes atributos en UCRM:
* Create a new plugin based on this one - `stripeCustomerId`: ID del cliente en Stripe.
* Replace the TwilioNotifierFacade and any references to it with a different class which extends AbstractMessageNotifierFacade - `clabeInterbancaria`: CLABE personalizada para transferencias.
* Update libraries in composer.json as needed - `passwordAntenaCliente`: Almacena la contraseña del equipo del cliente (sincronizada con la bóveda).
* Communicate with the remote system in the sendMessage() function
---
Read more about creating your own plugin in the [Developer documentation](https://github.com/Ubiquiti-App/UCRM-plugins/blob/master/docs/index.md). ## 🔄 Flujos de Trabajo Destacados
### 📅 Notificación de Visitas Técnicas
Cuando se asigna o reprograma una tarea:
1. El plugin detecta el cambio en el `assignedUserId` o la fecha.
2. Envía un mensaje al cliente con el nombre del técnico y la fecha (sin hora, por privacidad/logística).
3. Envía un mensaje al técnico con la dirección, ubicación en Google Maps y la contraseña de la antena obtenida de la bóveda de UISP.
### 💳 Registro de Pagos por Transferencia
1. Stripe envía un webhook de saldo aplicado (`customer_cash_balance_transaction.created`).
2. El plugin resuelve dinámicamente el ID del método "Transferencia bancaria".
3. Registra el pago automáticamente en UCRM vinculado al `clientId` en los metadatos.
---
## 📝 Registro de Cambios
Para consultar la evolución del proyecto, ver el archivo [CHANGELOG.md](./CHANGELOG.md).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1 +1 @@
{"ipserver":"172.16.5.134","apitoken":"6abef18c-783d-4dd0-b530-be6e6a7bbd1d","tokencallbell":"g8thcZkXGd3xBj2g3TtYNYFMH1fuesbJ.b6a940ea7d78cf6c9e42f067b21c8ddf96e9fa2a9e307bfd0c7c7c4d7fa38f79","tokenstripe":"sk_test_51OkG0REFY1WEUtgRH6UxBK5pu80Aq5Iy8EcdPnf0cOWzuVLQTpyLCd7CbPzqMsWMafZOHElCxhEHF7g8boURjWlJ00tBwE0W1M","unmsApiToken":null,"hostServerFTP":"siip.mx","usernameServerFTP":"siip0001","passServerFTP":"$spGiT,[wa)n","cashPaymentMethodId":false,"courtesyPaymentMethodId":false,"bankTransferPaymentMethodId":true,"paypalPaymentMethodId":true,"creditCardPaypalPaymentMethodId":true,"creditCardStripePaymentMethodId":true,"stripeSubscriptionCreditCardPaymentMethodId":true,"paypalSubscriptionPaymentMethodId":true,"mercadopagoPaymentMethodId":true,"checkPaymentMethodId":true,"customPaymentMethodId":true,"notificationTypeText":false,"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\": \"Angel 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}","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\": \"Á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}

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
"displayName": "SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp", "displayName": "SIIP - Procesador de Pagos en línea con Stripe, Oxxo y Transferencia, Sincronizador de CallBell y Envío de Notificaciones y comprobantes vía WhatsApp",
"description": "Este plugin sincroniza los clientes del sistema UISP CRM con los contactos de WhatsApp en CallBell, además procesa pagos de Stripe como las trasferencias bancarias y genera referencias de pago vía OXXO, además envía comprobantes de pago en formato imagen PNG o texto vía Whatsapp a los clientes", "description": "Este plugin sincroniza los clientes del sistema UISP CRM con los contactos de WhatsApp en CallBell, además procesa pagos de Stripe como las trasferencias bancarias y genera referencias de pago vía OXXO, además envía comprobantes de pago en formato imagen PNG o texto vía Whatsapp a los clientes",
"url": "https://siip.mx/", "url": "https://siip.mx/",
"version": "2.7.0", "version": "2.9.2",
"unmsVersionCompliancy": { "unmsVersionCompliancy": {
"min": "2.1.0", "min": "2.1.0",
"max": null "max": null
@ -23,10 +23,17 @@
{ {
"key": "apitoken", "key": "apitoken",
"label": "Token de la API UCRM", "label": "Token de la API UCRM",
"description": "Token de autenticación para el uso de la API del sistema UISP UCRM. Contiene 36 caracteres, ejemplo: 3d3fa6c9-e268-6e8b-b4d5-aae394d99d7d", "description": "Token de autenticación necesario para el uso de la API del sistema UISP UCRM, se utiliza para gestionar cualquier información de los clientes. Contiene 64 caracteres y se genera desde el módulo de Ajustes del UISP CRM en la opción de 'Seguridad' y en la sección de 'Claves app'.",
"required": 1, "required": 1,
"type": "text" "type": "text"
}, },
{
"key": "unmsApiToken",
"label": "Token de la API UNMS",
"description": "Token de autenticación necesario para el uso de la API del sistema UISP UNMS, se utiliza para gestionar información de antenas u otros dispositivos de red. Contiene 34 caracteres y se genera desde el módulo de Ajustes del UISP Network en la opción de 'Usuarios' y en apartado de 'API tokens'.",
"required": 0,
"type": "text"
},
{ {
"key": "tokencallbell", "key": "tokencallbell",
"label": "Token de la API de CallBell", "label": "Token de la API de CallBell",
@ -37,17 +44,10 @@
{ {
"key": "tokenstripe", "key": "tokenstripe",
"label": "Token de la API de Stripe", "label": "Token de la API de Stripe",
"description": "Token de autenticación para el uso de la API de Stripe que maneja las funciones realacionadas con los pagos en línea. ", "description": "Token de autenticación para el uso de la API de Stripe que maneja las funciones realacionadas con los pagos en línea (Transferencia y OXXO PAGO). ",
"required": 1, "required": 1,
"type": "text" "type": "text"
}, },
{
"key": "unmsApiToken",
"label": "Token de la API UNMS",
"description": "Token API creado para este plugin en la seccion Network de UNMS, solo necesario cuando se utiliza UNMS v1",
"required": 0,
"type": "text"
},
{ {
"key": "hostServerFTP", "key": "hostServerFTP",
"label": "IP o dominio del servidor FTP", "label": "IP o dominio del servidor FTP",
@ -58,14 +58,35 @@
{ {
"key": "usernameServerFTP", "key": "usernameServerFTP",
"label": "Usuario FTP", "label": "Usuario FTP",
"description": "Nombre de usuario para inicio de sesión el servidor FTP", "description": "Nombre de usuario para inicio de sesión el servidor FTP, necesario para la carga de comprobantes de pago del sistema y su posterior envío",
"required": 1, "required": 1,
"type": "text" "type": "text"
}, },
{ {
"key": "passServerFTP", "key": "passServerFTP",
"label": "Password FTP", "label": "Password FTP",
"description": "Contraseña para inicio de sesión en el servidor FTP", "description": "Contraseña para inicio de sesión en el servidor FTP, necesario para la carga de comprobantes de pago del sistema y su posterior envío",
"required": 1,
"type": "text"
},
{
"key": "ipPuppeteer",
"label": "Dirección IP del servicio de Puppeteer",
"description": "Dirección IP del contenedor docker que ejecuta el servicio de Puppeteer para la generación de comprobantes de pago en formato imagen PNG",
"required": 1,
"type": "text"
},
{
"key": "portPuppeteer",
"label": "Puerto del servicio de Puppeteer",
"description": "Puerto del contenedor docker que ejecuta el servicio de Puppeteer, por defecto es el 3000",
"required": 1,
"type": "text"
},
{
"key": "idPaymentAdminCRM",
"label": "ID del usuario para pagos en línea",
"description": "ID del usuario administrador del CRM asigando para realizar pagos en línea con Stripe. Todos los pagos que llegan desde Stripe se asignan a este usuario. Se recomienda crear un usuario exclusivo para este fin.",
"required": 1, "required": 1,
"type": "text" "type": "text"
}, },
@ -181,18 +202,6 @@
"label": "Borrar comprobantes Wordpress", "label": "Borrar comprobantes Wordpress",
"type": "admin", "type": "admin",
"target": "iframe" "target": "iframe"
},
{
"key": "Reports",
"label": "Generador de Clabes CBM en Stripe",
"type": "admin",
"target": "iframe"
},
{
"key": "Reports",
"label": "Obtener datos del Network de Cliente",
"type": "admin",
"target": "iframe"
} }
], ],
"supportsWebhookEvents": true "supportsWebhookEvents": true

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,8 @@ use SmsNotifier\Service\SmsNumberProvider;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\RequestException;
use Ubnt\UcrmPluginSdk\Service\UcrmApi; use Ubnt\UcrmPluginSdk\Service\UcrmApi;
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
use Ubnt\UcrmPluginSdk\Service\PluginLogManager;
/* /*
@ -67,11 +69,13 @@ abstract class AbstractOxxoOperationsFacade
$integerAmount = $amount; $integerAmount = $amount;
$this->logger->info("Creando referencia del cliente para OXXO: " . PHP_EOL); $this->logger->info("Creando referencia del cliente para OXXO: " . PHP_EOL);
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); $configManager = PluginConfigManager::create();
$config = $configManager->loadConfig(); $config = $configManager->loadConfig();
$StripeToken = $config['tokenstripe']; $StripeToken = $config['tokenstripe'];
$IPServer = $config['ipserver']; $IPServer = $config['ipserver'];
$tokenCRM = $config['apitoken']; $tokenCRM = $config['apitoken'];
$ipPuppeteer = $config['ipPuppeteer'];
$portPuppeteer = $config['portPuppeteer'];
$baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/';
$this->ucrmApi = UcrmApi::create(); $this->ucrmApi = UcrmApi::create();
@ -80,6 +84,7 @@ abstract class AbstractOxxoOperationsFacade
$stripeCustomerId = null; $stripeCustomerId = null;
$clientEmail = ''; $clientEmail = '';
$clientFullName = '';
$clientGuzzleHttp = new Client([ $clientGuzzleHttp = new Client([
'base_uri' => $baseUri, 'base_uri' => $baseUri,
@ -107,6 +112,7 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $integerAmount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
@ -116,21 +122,25 @@ abstract class AbstractOxxoOperationsFacade
$this->logger->error("Timeout al obtener el cliente en CRM: " . $clientID . PHP_EOL); $this->logger->error("Timeout al obtener el cliente en CRM: " . $clientID . PHP_EOL);
//devolver un array con los campos del codigo error, descripción de la falla, clientID y amount //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount
$arrayOxxoPayment['oxxo_reference'] = ''; $arrayOxxoPayment['oxxo_reference'] = '';
$arrayOxxoPayment['url'] = '';
$arrayOxxoPayment['error'] = 'errorTimeoutGetClient'; $arrayOxxoPayment['error'] = 'errorTimeoutGetClient';
$arrayOxxoPayment['failDescription'] = 'Timeout al obtener el cliente en CRM: ' . $clientID; $arrayOxxoPayment['failDescription'] = 'Timeout al obtener el cliente en CRM: ' . $clientID;
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $integerAmount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
$this->logger->error("Error al obtener el cliente en CRM (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); $this->logger->error("Error al obtener el cliente en CRM (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL);
//devolver un array con los campos del codigo error, descripción de la falla, clientID y amount //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount
$arrayOxxoPayment['oxxo_reference'] = ''; $arrayOxxoPayment['oxxo_reference'] = '';
$arrayOxxoPayment['url'] = '';
$arrayOxxoPayment['error'] = 'errorGetClient'; $arrayOxxoPayment['error'] = 'errorGetClient';
$arrayOxxoPayment['failDescription'] = 'Error al obtener el cliente en CRM: ' . $clientID; $arrayOxxoPayment['failDescription'] = 'Error al obtener el cliente en CRM: ' . $clientID;
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $integerAmount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
@ -164,28 +174,33 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $integerAmount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
if ($amount === null) { if ($amount === null) {
$amount = abs($arrayClientCRM['accountOutstanding']); $amount = abs($arrayClientCRM['accountOutstanding']); //Monto adeudado del cliente
} else { } else {
$this->logger->info("Monto proporcionado directamente: $amount " . PHP_EOL); //la variable $amount debe ser numérica pero en ocasiones la solicitud puede traer signos o alguna letra ingresada mal por el cliente, se debe analizar para limpiar cualquier caracter que no sea numérico
if(!is_numeric($amount)) {
$amount = preg_replace('/[^\d.]/', '', $amount); // Eliminar todo lo que no sea dígito o punto decimal
}
$this->logger->debug("Monto proporcionado directamente: $amount " . PHP_EOL);
} }
if ($amount > 10) { if ($amount > 10) {
$amount = intval($amount * 100); $amountInCents = intval($amount * 100); // Convertir a centavos
try { try {
$this->logger->info("Creando referencia en Stripe por $amount para el cliente $stripeCustomerId" . PHP_EOL); $this->logger->debug("Creando referencia en Stripe por $amount para el cliente $stripeCustomerId" . PHP_EOL);
$guzzleClient = new Client([ $guzzleClient = new Client([
'timeout' => 5, 'timeout' => 5, // Timeout de 5 segundos
]); ]);
$response = $guzzleClient->post('https://api.stripe.com/v1/payment_intents', [ $response = $guzzleClient->post('https://api.stripe.com/v1/payment_intents', [
'auth' => [$StripeToken, ''], 'auth' => [$StripeToken, ''],
'form_params' => [ 'form_params' => [
'amount' => $amount, 'amount' => $amountInCents,
'currency' => 'mxn', 'currency' => 'mxn',
'payment_method_types' => ['customer_balance', 'card', 'oxxo'], 'payment_method_types' => ['customer_balance', 'card', 'oxxo'],
'description' => 'Pago de servicio de SIIP Internet', 'description' => 'Pago de servicio de SIIP Internet',
@ -216,7 +231,8 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = 'Error de conexión con Stripe: ' . $clientID; $arrayOxxoPayment['failDescription'] = 'Error de conexión con Stripe: ' . $clientID;
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} catch (\Stripe\Exception\ApiErrorException $e) { } catch (\Stripe\Exception\ApiErrorException $e) {
@ -228,19 +244,28 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = 'Error de la API de Stripe: ' . $clientID; $arrayOxxoPayment['failDescription'] = 'Error de la API de Stripe: ' . $clientID;
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->error("Error inesperado al crear PaymentIntent (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL); $this->logger->error("Error inesperado al crear PaymentIntent (Error {$e->getCode()}): " . $e->getMessage() . PHP_EOL);
//si e->getMessage incluye el mensaje: You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...) declarar una variable y ahi poner "Este cliente no tiene cuenta de Stripe"
if ($e->getMessage() == 'You must provide a customer when creating or updating a PaymentIntent with a `customer_ba (truncated...)') {
$this->logger->error("Este cliente no tiene cuenta de Stripe" . PHP_EOL);
$arrayOxxoPayment['failDescription'] = 'Este cliente no tiene cuenta de Stripe: ' . $clientID;
} else {
$this->logger->error("Error inesperado al crear PaymentIntent: " . $e->getMessage() . PHP_EOL);
$arrayOxxoPayment['failDescription'] = 'Error inesperado al crear PaymentIntent: ' . $clientID;
}
//devolver un array con los campos del codigo error, descripción de la falla, clientID y amount //devolver un array con los campos del codigo error, descripción de la falla, clientID y amount
$arrayOxxoPayment['oxxo_reference'] = ''; $arrayOxxoPayment['oxxo_reference'] = '';
$arrayOxxoPayment['url'] = ''; $arrayOxxoPayment['url'] = '';
$arrayOxxoPayment['error'] = 'errorCreatePaymentIntent'; $arrayOxxoPayment['error'] = 'errorCreatePaymentIntent';
$arrayOxxoPayment['failDescription'] = 'Error inesperado al crear PaymentIntent: ' . $clientID;
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['amount'] = $integerAmount;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
@ -258,7 +283,8 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = "Nombre/apellido inválido: ' . $firstName . ' ' . $lastName"; $arrayOxxoPayment['failDescription'] = "Nombre/apellido inválido: ' . $firstName . ' ' . $lastName";
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
@ -294,7 +320,8 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = 'Error al confirmar PaymentIntent: ' . $e->getMessage(); $arrayOxxoPayment['failDescription'] = 'Error al confirmar PaymentIntent: ' . $e->getMessage();
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
@ -305,16 +332,100 @@ abstract class AbstractOxxoOperationsFacade
$this->logger->info("Referencia OXXO: " . $oxxo_reference . PHP_EOL); $this->logger->info("Referencia OXXO: " . $oxxo_reference . PHP_EOL);
$this->logger->info("URL del recibo: " . $oxxo_receipt_url . PHP_EOL); $this->logger->info("URL del recibo: " . $oxxo_receipt_url . PHP_EOL);
$this->captureScreenshot($oxxo_receipt_url); //$this->captureScreenshot($oxxo_receipt_url);
//devolver un array con los campos de url de oxxo, descripción de la falla, clientID y amount //devolver un array con los campos de url de oxxo, descripción de la falla, clientID y amount
$arrayOxxoPayment['oxxo_reference'] = $oxxo_reference; $arrayOxxoPayment['oxxo_reference'] = $oxxo_reference;
$arrayOxxoPayment['url'] = $oxxo_receipt_url; $arrayOxxoPayment['url'] = $oxxo_receipt_url;
$arrayOxxoPayment['error'] = '';
$arrayOxxoPayment['failDescription'] = '';
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['failDescription'] = '';
$arrayOxxoPayment['error'] = '';
$this->logger->info("Referencia OXXO creada correctamente." . PHP_EOL); $this->logger->info("Referencia OXXO creada correctamente." . PHP_EOL);
// Configuración de la API de tu servicio Docker de Puppeteer
$api_url = 'http://'.$ipPuppeteer.':'.$portPuppeteer.'/screenshot'; // Asegúrate que esta URL sea accesible
// --- Datos para la petición ---
$request_data = [
'url' => $oxxo_receipt_url, // URL del recibo de OXXO
// Opcional: Si quieres recortar la imagen, descomenta y ajusta el 'clip'
'clip' => [
'x' => 325,
'y' => 30,
'width' => 550,
'height' => 550
]
];
// Nombre del archivo donde se guardará la imagen
$clientFullNameWithoutSpaces = str_replace(' ', '_', $clientFullName); // Reemplazar espacios por guiones bajos
$voucherFileName = 'voucher_'.$clientFullNameWithoutSpaces.'_' . time() . '.jpeg'; // Usamos .jpeg por defecto
$output_filename = __DIR__ . '/../../vouchers_oxxo/' . $voucherFileName; // Usamos .jpeg por defecto
$output_filename = realpath(__DIR__ . '/../../vouchers_oxxo') . '/'. $voucherFileName; // Mejor: ruta absoluta y verifica que el directorio exista
//$output_filename = '/path/to/vouchers_oxxo/voucher_' . time() . '.jpeg'; // Mejor aún: ruta absoluta y fija, si es posible
$guzzleClient = new Client([
'timeout' => 5, // Timeout de 5 segundos
]);
// Descargar el recibo de OXXO
try {
// --- Realizar la petición POST ---
$response = $guzzleClient->post($api_url, [
'json' => $request_data, // Guzzle codifica automáticamente el array a JSON y establece Content-Type: application/json
'headers' => [
'Accept' => 'image/jpeg, image/png', // Indicar que esperamos una imagen
],
// Opcional: para depuración si necesitas ver el cuerpo de la respuesta en caso de error
// 'http_errors' => false // Desactiva las excepciones para códigos de estado 4xx/5xx y maneja la respuesta manualmente
]);
// --- Procesar la respuesta ---
$statusCode = $response->getStatusCode();
$contentType = $response->getHeaderLine('Content-Type');
$this->logger->debug("Status Code: " . $statusCode);
$this->logger->debug("Content Type: " . $contentType);
if ($statusCode === 200 && str_contains($contentType, 'image/')) {
// La respuesta es una imagen y el código de estado es 200 OK
$image_content = $response->getBody()->getContents();
// *** VERIFICACIÓN IMPORTANTE: ***
if (file_put_contents($output_filename, $image_content)) {
$this->logger->debug("¡Imagen guardada exitosamente en: " . $output_filename);
$url_file = $this->UploadVoucherToWordpressByImageFileName($voucherFileName); //Carga del comprobante PDF a Wordpress para su posterior envío
$arrayOxxoPayment['voucher_image_url'] = $url_file; // Agregar la URL del archivo de imagen al array
} else {
$this->logger->error("Error: No se pudo guardar la imagen en " . $output_filename);
$this->logger->error("Ruta del archivo: " . $output_filename); // Agregado para depuración
$this->logger->error("Directorio del script: " . __DIR__); // Agregado para depuración
$this->logger->error("Error de PHP: " . error_get_last()['message']); // Agregado para depuración
}
} else {
// No es una imagen o hubo un error en el servicio
$this->logger->debug("Error: La respuesta no es una imagen o el código de estado no es 200 OK.");
$this->logger->debug("Cuerpo de la respuesta: " . $response->getBody()->getContents());
}
} catch (RequestException $e) {
// Manejo de errores de red o del servidor (ej. timeouts, 4xx, 5xx si http_errors no es false)
$this->logger->error("Error en la petición: " . $e->getMessage());
if ($e->hasResponse()) {
$this->logger->error("Cuerpo de la respuesta de error: " . $e->getResponse()->getBody()->getContents());
$this->logger->error("Código de estado HTTP de error: " . $e->getResponse()->getStatusCode());
}
} catch (Exception $e) {
// Otros errores inesperados
$this->logger->error("Error inesperado: " . $e->getMessage());
}
return $arrayOxxoPayment; return $arrayOxxoPayment;
} else { } else {
$this->logger->info("El PaymentIntent no tiene detalles de OXXO disponibles. Estado: " . $paymentIntentConfirm['status'] . PHP_EOL); $this->logger->info("El PaymentIntent no tiene detalles de OXXO disponibles. Estado: " . $paymentIntentConfirm['status'] . PHP_EOL);
@ -325,7 +436,8 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = 'El PaymentIntent no tiene detalles de OXXO disponibles. Estado: ' . $paymentIntentConfirm['status']; $arrayOxxoPayment['failDescription'] = 'El PaymentIntent no tiene detalles de OXXO disponibles. Estado: ' . $paymentIntentConfirm['status'];
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
@ -339,7 +451,8 @@ abstract class AbstractOxxoOperationsFacade
$arrayOxxoPayment['failDescription'] = 'Este cliente no tiene adeudos.'; $arrayOxxoPayment['failDescription'] = 'Este cliente no tiene adeudos.';
$arrayOxxoPayment['clientID'] = $clientID; $arrayOxxoPayment['clientID'] = $clientID;
$arrayOxxoPayment['clientFullName'] = $clientFullName; $arrayOxxoPayment['clientFullName'] = $clientFullName;
$arrayOxxoPayment['amount'] = $integerAmount; $arrayOxxoPayment['amount'] = $amount;
$arrayOxxoPayment['voucher_image_url'] = '';
return $arrayOxxoPayment; return $arrayOxxoPayment;
} }
} }
@ -362,7 +475,7 @@ abstract class AbstractOxxoOperationsFacade
function validarEmail($email) function validarEmail($email)
{ {
$this->logger->info('SE VALIDA EL EMAIL!!! ' . PHP_EOL); $this->logger->debug('SE VALIDA EL EMAIL!!! ' . PHP_EOL);
// Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email // Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email
if (filter_var($email, FILTER_VALIDATE_EMAIL)) { if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
// Si el email es válido, devolver el email // Si el email es válido, devolver el email
@ -481,37 +594,145 @@ abstract class AbstractOxxoOperationsFacade
} }
/**
* Función para capturar una pantalla de una URL usando el servicio Puppeteer.
*
* @param string $url La URL de la página web que se desea capturar.
* @return string La ruta del archivo de la captura de pantalla.
*/
function captureScreenshot($url) {
// URL del servicio Puppeteer
$puppeteerUrl = 'http://172.16.5.134:4000';
// Datos a enviar function UploadVoucherToWordpressByImageFileName($imageFileName): string
$data = http_build_query(['url' => $url]); {
// Configurar la solicitud POST $log = PluginLogManager::create(); //Initialize Logger
$options = [ $configManager = PluginConfigManager::create();
'http' => [ $config = $configManager->loadConfig();
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => $data,
],
];
// Ejecutar la solicitud // Configuración de conexión FTP
$context = stream_context_create($options); $ftp_server = $config['hostServerFTP'];
$response = file_get_contents($puppeteerUrl, false, $context); $ftp_username = $config['usernameServerFTP'];
$ftp_password = $config['passServerFTP'];
$remote_folder = "/public_html/wp/wp-content/uploads/vouchers_oxxo/";
if ($response === false) { $log->appendLog("Subiendo voucher a worpdpress " . PHP_EOL);
throw new Exception("Error al comunicarse con el servicio Puppeteer."); // Configuración de conexión FTP
// $ftp_server = "siip.mx";
// $ftp_username = "siip0001";
// $ftp_password = '$spGiT,[wa)n';
$remote_file = "/public_html/wp/wp-content/uploads/vouchers_oxxo/" . $imageFileName;
$file_to_upload = __DIR__ . '/../../vouchers_oxxo/' . $imageFileName;
$url = 'https://siip.mx/wp/wp-content/uploads/vouchers_oxxo/' . $imageFileName;
$log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL);
// 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);
// Cargar archivo
if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) {
$log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL);
$log->appendLog("La URL es: " . $url . PHP_EOL);
$this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_file);
// Cerrar conexión FTP
ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO
return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO
} else {
$log->appendLog("Error al cargar el archivo " . PHP_EOL);
ftp_close($ftp_conn);
return '';
} }
return trim($response); //SI SE DECIDE VOLVER A ELIMINAR LOS COMPROBANTES ENTONCES DESCOMENTAR ESTA PARTE DE ABAJO Y COMENTAR LA SECCIÓN DE ARRIBA
}
// 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 50 más recientes)
// $filesToDelete = array_slice(array_keys($filesWithTime), 50);
// // Eliminar archivos antiguos
// foreach ($filesToDelete as $file) {
// if (ftp_delete($ftp_conn, $remote_folder . $file)) {
// $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL);
// } else {
// $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL);
// }
// }
// $log->appendLog("Archivos eliminados" . PHP_EOL);
// ftp_close($ftp_conn);
// return $url;
// } else {
// $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL);
// ftp_close($ftp_conn);
// return $url;
// }
} else {
$log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL);
return '';
}
}
function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool
{
// 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 50 más recientes)
$filesToDelete = array_slice(array_keys($filesWithTime), 50);
// Eliminar archivos antiguos
foreach ($filesToDelete as $file) {
if (ftp_delete($ftp_conn, $remote_folder . $file)) {
$log->appendLog("Voucher eliminado de Wordpress: " . $file . PHP_EOL);
} else {
$log->appendLog('Error al borrar voucher' . $file . PHP_EOL);
}
}
$log->appendLog("Archivos eliminados" . PHP_EOL);
ftp_close($ftp_conn);
return true;
} else {
$log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL);
ftp_close($ftp_conn);
return false;
}
}
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SmsNotifier\Facade; namespace SmsNotifier\Facade;
use Attribute;
use Exception; use Exception;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use SmsNotifier\Data\NotificationData; use SmsNotifier\Data\NotificationData;
@ -57,11 +58,12 @@ abstract class AbstractStripeOperationsFacade
*/ */
public function createPaymentIntent($event_json) public function createPaymentIntent($event_json)
{ {
$this->logger->info("Evento recibido: " . json_encode($event_json) . PHP_EOL); $this->logger->info("Iniciando creación de PaymentIntent en Stripe." . PHP_EOL);
$configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create(); $configManager = \Ubnt\UcrmPluginSdk\Service\PluginConfigManager::create();
$config = $configManager->loadConfig(); $config = $configManager->loadConfig();
$StripeToken = $config['tokenstripe']; $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 $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 // Asegurarse de que 'customer' esté presente en la estructura del evento
@ -83,11 +85,21 @@ abstract class AbstractStripeOperationsFacade
} }
$amount = $event_json['data']['object']['net_amount']; $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);
return;
}
//imprimir la cantidad del PaymentIntent
$this->logger->info("Cantidad del PaymentIntent: " . $amount . PHP_EOL);
try { try {
// Obtener información del cliente desde Stripe // Obtener información del cliente desde Stripe
$stripeQuery = $stripe->customers->retrieve($customerId, []); $stripeQuery = $stripe->customers->retrieve($customerId, []);
$UCRM_clientID = $stripeQuery['metadata']['ucrm_client_id']; $UCRM_clientID = $stripeQuery['metadata']['ucrm_client_id']; // ID del cliente en Ubiquiti UISP
// Obtener información del administrador actual // Obtener información del administrador actual
$this->ucrmApi = UcrmApi::create(); $this->ucrmApi = UcrmApi::create();
@ -111,7 +123,7 @@ abstract class AbstractStripeOperationsFacade
'clientId' => $UCRM_clientID, // ID del cliente en Ubiquiti 'clientId' => $UCRM_clientID, // ID del cliente en Ubiquiti
'createdBy' => 'UCRM', 'createdBy' => 'UCRM',
'paymentType' => 'card.one_time', 'paymentType' => 'card.one_time',
'signedInAdminId' => $currentUserAdmin[0]['id'], 'signedInAdminId' => $idPaymentAdmin, // ID del administrador que crea el PaymentIntent
'tipoPago' => 'Transferencia Bancaria' 'tipoPago' => 'Transferencia Bancaria'
], ],
]); ]);
@ -123,6 +135,59 @@ abstract class AbstractStripeOperationsFacade
} }
} }
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'];
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;
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;
}
}
if (!$methodId) {
$this->logger->error("Error registrando pago: No se encontró el método 'Transferencia bancaria'");
return;
}
$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());
}
}
}
/* /*
* Creates the Stripe Customer * Creates the Stripe Customer
@ -136,19 +201,30 @@ abstract class AbstractStripeOperationsFacade
$UCRMAPIToken = $config['apitoken']; $UCRMAPIToken = $config['apitoken'];
$StripeToken = $config['tokenstripe']; $StripeToken = $config['tokenstripe'];
$baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; //endpoint de la API REST del CRM $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; //endpoint de la API REST del CRM
$this->ucrmApi = UcrmApi::create();
$stripe = new \Stripe\StripeClient($StripeToken); //Token de clave privada en modo prueba para la API de Stripe $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 //$stripe = new \Stripe\StripeClient('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'); //Token de clave privada en modo prodcucción para la API de Stripe
//valor de $notificationData en json
$this->logger->info("Valor de notificationData: " . json_encode($notificationData) . PHP_EOL);
//$this->logger->info("Ya dentro del metodo Create Stripe Client " . PHP_EOL); //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}
//$this->sendWhatsApp('Hola Dany');
$this->ucrmApi = UcrmApi::create(); //obtener el id del cliente
if ($tagStripe) { $clientId = $notificationData->clientData['id'];
$tagsIds = $this->ucrmApi->get('client-tags', ['name' => 'STRIPE']);
$this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken); //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'];
break;
} }
}
$customAttributes = $this->ucrmApi->get('custom-attributes/', ['attributeType' => 'client']);//Obtener los atributos del sistema que estén vinculados a la entidad "cliente" $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); //$this->logger->info("result del custom Attributes: " . json_encode($customAttributes) . PHP_EOL);
@ -171,6 +247,61 @@ abstract class AbstractStripeOperationsFacade
$this->logger->info("Error al obtener los atributos personalizados." . PHP_EOL); $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;
}
}
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;
}
@ -189,26 +320,8 @@ abstract class AbstractStripeOperationsFacade
} }
$this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken); $this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken);
// Acceder a "isLead" de forma segura
// $isLead = $dataObject->isLead ?? null;
// $this->logger->info("El valor de 'isLead' es: " . ($isLead ? "true" : "false") . PHP_EOL);
// if ($isLead === true) {
// $this->logger->info("El cliente es un lead, no se procesará" . PHP_EOL);
// return;
// } else {
// $this->logger->info("El cliente NO es un lead, se procesará" . PHP_EOL);
// $this->createCustomerStripe($notificationData, $stripe, $baseUri, $UCRMAPIToken);
// }
} }
// /** // /**
// * implement in subclass with the specific messaging provider // * implement in subclass with the specific messaging provider
// * @see TwilioNotifierFacade::sendWhatsApp() // * @see TwilioNotifierFacade::sendWhatsApp()
@ -220,6 +333,7 @@ abstract class AbstractStripeOperationsFacade
function createCustomerStripe($notificationData, $stripe, $baseUri, $token): bool function createCustomerStripe($notificationData, $stripe, $baseUri, $token): bool
{ {
$this->ucrmApi = UcrmApi::create();
$this->logger->info("Creando el Customer Stripe" . PHP_EOL); $this->logger->info("Creando el Customer Stripe" . PHP_EOL);
@ -343,22 +457,33 @@ abstract class AbstractStripeOperationsFacade
] ]
}'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM, Campo para el Stripe Customer ID y la Clabe interbancaria }'; //JSON para hacer patch de los custom fields del cliente en el UISCP CRM, Campo para el Stripe Customer ID y la Clabe interbancaria
// $json_data_patch = '{
// "attributes": [
// {
// "value": "' . $stripe_customer_id . '",
// "customAttributeId": 29
// },
// {
// "value": "' . $clabe . '",
// "customAttributeId": 30
// }
// ]
// }'; //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 $clientguzz = new Client(); //instancia de cliente GuzzleHttp para consumir API UISP CRM
try { 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, [ $responseCRM = $clientguzz->patch($baseUri . 'clients/' . $ucrm_client_id, [
'json' => json_decode($json_data_patch, true), 'json' => json_decode($json_data_patch, true),
'headers' => [ 'headers' => [
@ -366,7 +491,7 @@ abstract class AbstractStripeOperationsFacade
'Accept' => 'application/json', // Indica que esperamos una respuesta en formato JSON 'Accept' => 'application/json', // Indica que esperamos una respuesta en formato JSON
], ],
'verify' => false, 'verify' => false,
]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM ]);
} catch (GuzzleException $error) { } catch (GuzzleException $error) {
@ -412,4 +537,6 @@ abstract class AbstractStripeOperationsFacade
} }
} }
} }

View File

@ -1,17 +1,13 @@
<?php <?php
namespace SmsNotifier\Facade; namespace SmsNotifier\Facade;
use CURLFile;
use DateTime; use DateTime;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Imagick; use Imagick;
use Ubnt\UcrmPluginSdk\Service\PluginLogManager;
use ImagickException; use ImagickException;
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager; use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
use Ubnt\UcrmPluginSdk\Service\UcrmOptionsManager; use Ubnt\UcrmPluginSdk\Service\PluginLogManager;
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
//use SmsNotifier\Service\Logger; //use SmsNotifier\Service\Logger;
@ -56,7 +52,6 @@ class ClientCallBellAPI
public $ucrmVersion; public $ucrmVersion;
public function __construct( public function __construct(
$UCRMAPIToken, $UCRMAPIToken,
$IPServer, $IPServer,
@ -105,9 +100,6 @@ class ClientCallBellAPI
$campo2 = sprintf("📡 Su servicio está: %s 📍Su dirección es: *%s* ", $estado_service, $domicilio); $campo2 = sprintf("📡 Su servicio está: %s 📍Su dirección es: *%s* ", $estado_service, $domicilio);
$log->appendLog("Valor del campo2 " . $campo2); $log->appendLog("Valor del campo2 " . $campo2);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }";
$log->appendLog("La cadena CURL que se envia es: " . $curl_string); $log->appendLog("La cadena CURL que se envia es: " . $curl_string);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string);
@ -118,15 +110,21 @@ class ClientCallBellAPI
curl_close($ch); curl_close($ch);
} }
public function sendJobNotificationWhatsAppToClient($clientWhatsAppNumber, $jobNotificationData, $reprogramming = false, $changeInstaller = false): bool public function sendJobNotificationWhatsAppToClient($clientWhatsAppNumber, $jobNotificationData, $reprogramming, $changeInstaller): bool
{ {
$log = PluginLogManager::create(); //Initialize Logger $log = PluginLogManager::create(); //Initialize Logger
$log->appendLog("Enviando mensaje de trabajo para el cliente" . PHP_EOL); $log->appendLog("Enviando mensaje de trabajo para el cliente" . PHP_EOL);
$jsonJobNotificationData = json_encode($jobNotificationData, true); $jsonJobNotificationData = json_encode($jobNotificationData, true);
$log->appendLog("Datos de la notificación de trabajo: " . $jsonJobNotificationData . PHP_EOL); $log->appendLog("Datos de la notificación de trabajo: " . $jsonJobNotificationData . PHP_EOL);
$log->appendLog("Debugging: reprogramming = " . var_export($reprogramming, true) . ", changeInstaller = " . var_export($changeInstaller, true) . PHP_EOL);
// --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! ---
$reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
// -----------------------------------------------------------
// Puedes volver a loggear los valores para confirmar la conversión (opcional)
$log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL);
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send');
@ -137,24 +135,53 @@ class ClientCallBellAPI
'Content-Type: application/json', 'Content-Type: application/json',
]); ]);
$campo1 = sprintf('*%s*', $jobNotificationData['clientFullName']); $campo1 = '👤 ' . sprintf(' *%s*', $jobNotificationData['clientFullName']);
$campo2 = sprintf('*#%s*', $jobNotificationData['jobId']); if ($changeInstaller && !$reprogramming) {
$campo3 = sprintf('*%s*', $jobNotificationData['date']); $campo2 = sprintf('se ha hecho un cambio de técnico 👷🏻‍♂️🔄 para su visita con el folio *#️⃣%s*', $jobNotificationData['jobId']);
$campo4 = sprintf('*%s*', $jobNotificationData['installerName']); } else {
$campo2 = sprintf('*#️⃣%s*', $jobNotificationData['jobId']);
}
$campo3 = '🗓️ ' . sprintf('%s', $jobNotificationData['date']);
if ($changeInstaller && $reprogramming) {
$campo4 = 'Además se ha hecho un cambio de técnico por el siguiente 👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']);
} else {
$campo4 = '👷🏻‍♂️➡️ ' . sprintf('*%s*', $jobNotificationData['installerName']);
}
if ($reprogramming && $changeInstaller === false) { $campo5 = "asegúrese de que alguien esté presente en el domicilio 🏠 para permitir el acceso y confirme su disponibilidad.";
$log->appendLog("DEBUG: Valores antes de la estructura IF - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL);
if ($reprogramming && !$changeInstaller) {
//{{1}}, se ha reprogramado su visita técnica con el folio {{2}}
$campo3 = '🗓️➡️ ' . sprintf('%s', $jobNotificationData['date']);
$campo1_combinado = "Estimado cliente $campo1 se ha reprogramado su visita técnica con folio $campo2";
// Case: true, false
//Enviar notificación de reprogramación al cliente //Enviar notificación de reprogramación al cliente
$log->appendLog("Enviando notificación de reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $log->appendLog("Enviando notificación de reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"70579353773f4de1836d4f9b6bf6074d\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"715eed9d6f2d4d90853f25e296202e00\",\n \"optin_contact\": true\n }";
} } else if (!$reprogramming && $changeInstaller) {
if ($changeInstaller) { // Case: false, true
//Enviar notificación de cambio de instalador //Enviar notificación de cambio de instalador
$log->appendLog("Enviando notificación de cambio de instalador al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $log->appendLog("Enviando notificación de cambio de instalador al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"0d57fd210595422caf2f5999642882a3\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"d684e6fa2ba24593a86c98be1815831d\",\n \"optin_contact\": true\n }";
} else { } else if (!$reprogramming && !$changeInstaller) { // <--- Ahora este else if está correctamente encadenado
// Case: false, false
//Enviar notificación normal de visita técnica al cliente //Enviar notificación normal de visita técnica al cliente
$log->appendLog("Enviando notificación normal de visita técnica al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $log->appendLog("Enviando notificación normal de visita técnica al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"c0ef8228b50a4d9690a2e87bc11e9ab3\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"07cfbc6e394044608485b530a27177d0\",\n \"optin_contact\": true\n }";
} else if ($reprogramming && $changeInstaller) { // <--- Ahora este else if está correctamente encadenado
// Case: true, true
//Enviar notificación de cambio de instalador y reprogramación al cliente
$log->appendLog("Enviando notificación de cambio de instalador y reprogramación al cliente, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"145885d15323414f978f1e3f249c2ae1\",\n \"optin_contact\": true\n }";
} else {
// Case: true, true (la única combinación restante con booleanos)
$log->appendLog("No se encontró una opción válida para enviar la notificación (reprogramming y changeInstaller son true)." . PHP_EOL);
// Decide qué hacer aquí, ¿Quizás devolver false? ¿O manejar esta combinación?
return false; // O el manejo adecuado para true/true
} }
$log->appendLog("La cadena CURL que se envia es: " . $curl_string); $log->appendLog("La cadena CURL que se envia es: " . $curl_string);
@ -190,7 +217,8 @@ class ClientCallBellAPI
$log->appendLog("Ruta no prevista en la función." . PHP_EOL); $log->appendLog("Ruta no prevista en la función." . PHP_EOL);
return false; return false;
} }
public function sendJobNotificationWhatsAppToInstaller($installerWhatsAppNumber, $jobInstallerNotificationData, $reprogramming = false, $changeInstaller = false): bool
public function sendJobNotificationWhatsAppToInstaller($installerWhatsAppNumber, $jobInstallerNotificationData, $reprogramming, $changeInstaller): bool
{ {
$log = PluginLogManager::create(); //Initialize Logger $log = PluginLogManager::create(); //Initialize Logger
@ -198,6 +226,14 @@ class ClientCallBellAPI
$jsonJobNotificationData = json_encode($jobInstallerNotificationData, true); $jsonJobNotificationData = json_encode($jobInstallerNotificationData, true);
$log->appendLog("Datos de la notificación de tarea: " . $jsonJobNotificationData . PHP_EOL); $log->appendLog("Datos de la notificación de tarea: " . $jsonJobNotificationData . PHP_EOL);
// --- ¡AÑADE ESTAS LÍNEAS PARA CONVERTIR A BOOLEANO REAL! ---
$reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
// -----------------------------------------------------------
// Puedes volver a loggear los valores para confirmar la conversión (opcional)
$log->appendLog("DEBUG: Valores después de conversión - Reprogramming: " . var_export($reprogramming, true) . ", ChangeInstaller: " . var_export($changeInstaller, true) . PHP_EOL);
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@ -208,39 +244,61 @@ class ClientCallBellAPI
]); ]);
if (!$reprogramming && $changeInstaller) {
$campo1 = $jobInstallerNotificationData['installerName']; $campo1 = $jobInstallerNotificationData['installerName'];
if ($changeInstaller) {
$campo2 = $jobInstallerNotificationData['subjectOfChange']; $campo2 = $jobInstallerNotificationData['subjectOfChange'];
$campo3 = sprintf("#%s", $jobInstallerNotificationData['jobId']); $campo3 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']);
$campo4 = $jobInstallerNotificationData['clientFullName']; $campo4 = sprintf('*👤%s* ', $jobInstallerNotificationData['clientFullName']);
$campo5 = $jobInstallerNotificationData['additionalChangeData']; $campo5 = $jobInstallerNotificationData['additionalChangeData'];
} else { } else {
$campo2 = sprintf("*#%s*", $jobInstallerNotificationData['jobId']); $campo1 = '👷🏻‍♂️ ' . $jobInstallerNotificationData['installerName'];
$campo3 = $jobInstallerNotificationData['clientFullName']; $campo2 = sprintf("#️⃣%s", $jobInstallerNotificationData['jobId']);
$campo4 = $jobInstallerNotificationData['clientAddress']; $campo3 = '👤 *' . $jobInstallerNotificationData['clientFullName'] . '*';
$campo5 = $jobInstallerNotificationData['clientWhatsApp']; // $campo4 = $jobInstallerNotificationData['clientAddress'];
$campo6 = $jobInstallerNotificationData['date']; $campo4 = '☎️ ' . $jobInstallerNotificationData['clientWhatsApp'];
$campo7 = $jobInstallerNotificationData['jobDescription']; $campo5 = '🗓️ ' . $jobInstallerNotificationData['date'];
$campo8 = $jobInstallerNotificationData['gmapsLocation']; // $campo6 = '🛠️ ' . $jobInstallerNotificationData['jobDescription'];
// $campo7 = '📌 ' . $jobInstallerNotificationData['gmapsLocation'];
// $campo8 = '🔐 ' . $passwordAntenaCliente;
} }
if ($reprogramming && !$changeInstaller) {
$jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']);
$clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']);
$date = sprintf("🗓️➡️ %s", $jobInstallerNotificationData['date']);
$installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']);
if ($reprogramming && $changeInstaller === false) { $campo1_combinado = "$installerName se reprogramó una tarea con el folio $jobId para el cliente $clientFullName, la nueva fecha será el $date";
$campo2 = $jobInstallerNotificationData['clientWhatsApp'];
$campo3 = $jobInstallerNotificationData['gmapsLocation'];
$campo4 = $jobInstallerNotificationData['passwordAntenaCliente'];
//Enviar notificación de reprogramación //Enviar notificación de reprogramación
$log->appendLog("Enviando notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $log->appendLog("Enviando notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\", \"$campo6\", \"$campo7\", \"$campo8\"],\n \"template_uuid\": \"42152c07c67b468ba68e581c0283e22e\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }";
} else if (!$reprogramming && $changeInstaller) {
} else if ($changeInstaller) {
//Enviar notificación de cambio de instalador //Enviar notificación de cambio de instalador
$log->appendLog("Enviando notificación de cambio de instalador al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $log->appendLog("Enviando notificación de cambio de instalador al instalador anterior (desasignación), valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }";
} else if ($reprogramming && $changeInstaller) {
//Enviar notificación de cambio de instalador
$log->appendLog("Enviando notificación de cambio de instalador al instalador y notificación de reprogramación al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\"],\n \"template_uuid\": \"e1aa2b0fd3884595918f4ac2676acd29\",\n \"optin_contact\": true\n }";
} else if (!$reprogramming && !$changeInstaller) {
} else { //combinar el campo3, campo4, campo5, campo6, campo7 y campo8 en un solo campo con saltos de línea
//Enviar notificación normal de asignación de tarea $jobId = sprintf("#️⃣ *%s*", $jobInstallerNotificationData['jobId']);
$log->appendLog("Enviando notificación normal de asignación de tarea al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL); $clientFullName = sprintf("👤 *%s*", $jobInstallerNotificationData['clientFullName']);
$curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\", \"$campo3\", \"$campo4\", \"$campo5\", \"$campo6\", \"$campo7\", \"$campo8\"],\n \"template_uuid\": \"b6663394265e4bcdb215369aa9ba0f21\",\n \"optin_contact\": true\n }"; $date = sprintf("🗓️ %s", $jobInstallerNotificationData['date']);
$installerName = sprintf("👷🏻‍♂️ *%s*", $jobInstallerNotificationData['installerName']);
$campo1_combinado = "$installerName se te ha asignado una tarea con folio $jobId, del cliente $clientFullName, para el $date";
$campo2 = $jobInstallerNotificationData['clientWhatsApp'];
$campo3 = $jobInstallerNotificationData['gmapsLocation'];
$campo4 = $jobInstallerNotificationData['passwordAntenaCliente'];
$log->appendLog("Enviando notificación normal de tarea al instalador, valor de reprogramming $reprogramming y valor de changeInstaller $changeInstaller " . PHP_EOL);
$curl_string = "{\n \"to\": \"$installerWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1_combinado\", \"$campo2\", \"$campo3\", \"$campo4\"],\n \"template_uuid\": \"88eeb6420a214fd8870dd28d741021c4\",\n \"optin_contact\": true\n }";
} }
$log->appendLog("La cadena CURL que se envia es: " . $curl_string); $log->appendLog("La cadena CURL que se envia es: " . $curl_string);
@ -251,6 +309,9 @@ class ClientCallBellAPI
curl_close($ch); curl_close($ch);
// Validar la respuesta de Callbell // Validar la respuesta de Callbell
$jsonResponse = json_decode($response, true); $jsonResponse = json_decode($response, true);
@ -275,7 +336,6 @@ class ClientCallBellAPI
// Valor de retorno predeterminado en caso de que ninguna condición se cumpla // Valor de retorno predeterminado en caso de que ninguna condición se cumpla
$log->appendLog("Ruta no prevista en la función." . PHP_EOL); $log->appendLog("Ruta no prevista en la función." . PHP_EOL);
return false; return false;
} }
public function sendPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool public function sendPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool
@ -292,20 +352,17 @@ class ClientCallBellAPI
//Path base del comprobante de pago //Path base del comprobante de pago
$pdf_payment_path = ''; $pdf_payment_path = '';
$this->ucrmApi = UcrmApi::create(); $this->ucrmApi = UcrmApi::create();
$payments = $this->ucrmApi->get( $payments = $this->ucrmApi->get(
'payments/', 'payments/',
[ [
'clientId' => $notificationData->clientData['id'], 'clientId' => $notificationData->clientData['id'],
'limit' => 1, 'limit' => 1,
'direction' => 'DESC' 'direction' => 'DESC',
] ]
); );
$payment_id = $payments[0]['id']; $payment_id = $payments[0]['id'];
$payment_amount = '$' . $payments[0]['amount']; $payment_amount = '$' . $payments[0]['amount'];
//$saldo = '$' . $notificationData->clientData['accountBalance']; //$saldo = '$' . $notificationData->clientData['accountBalance'];
@ -335,8 +392,6 @@ class ClientCallBellAPI
'verify' => false, 'verify' => false,
]); ]);
try { try {
// Hacer la solicitud GET para obtener el PDF // Hacer la solicitud GET para obtener el PDF
$response = $clientGuzzleHttp->request('GET', "payments/$payment_id/pdf"); $response = $clientGuzzleHttp->request('GET', "payments/$payment_id/pdf");
@ -368,20 +423,11 @@ class ClientCallBellAPI
$log->appendLog("El archivo PDF es válido y tiene contenido: $rutaArchivo" . PHP_EOL); $log->appendLog("El archivo PDF es válido y tiene contenido: $rutaArchivo" . PHP_EOL);
$rutaImagen = __DIR__ . '/../../comprobantes/' . str_replace('.pdf', '.png', $fileNameComprobante); $rutaImagen = __DIR__ . '/../../comprobantes/' . str_replace('.pdf', '.png', $fileNameComprobante);
} catch (\Exception $e) { } catch (\Exception $e) {
$log->appendLog("Error al manejar el comprobante de pago: " . $e->getMessage() . PHP_EOL); $log->appendLog("Error al manejar el comprobante de pago: " . $e->getMessage() . PHP_EOL);
return false; return false;
} }
try { try {
$image = new Imagick(); $image = new Imagick();
$image->setResolution(300, 300); $image->setResolution(300, 300);
@ -397,16 +443,12 @@ class ClientCallBellAPI
return false; return false;
} }
$fileNameComprobanteImage = str_replace('.pdf', '.png', $fileNameComprobante); $fileNameComprobanteImage = str_replace('.pdf', '.png', $fileNameComprobante);
$url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobanteImage);//Carga del comprobante PDF a Wordpress para su posterior envío $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobanteImage); //Carga del comprobante PDF a Wordpress para su posterior envío
// $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobante);//Carga del comprobante PDF a Wordpress para su posterior envío // $url_file = $this->UploadReceiptToWordpressByImageFileName($fileNameComprobante);//Carga del comprobante PDF a Wordpress para su posterior envío
$log->appendLog("Se terminó de subir comprobante a wordpress " . PHP_EOL); $log->appendLog("Se terminó de subir comprobante a wordpress " . PHP_EOL);
//$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL);
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send');
@ -417,8 +459,6 @@ class ClientCallBellAPI
'Content-Type: application/json', 'Content-Type: application/json',
]); ]);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldoTexto\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldoTexto\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }";
$log->appendLog("La cadena CURL que se envia es: " . $curl_string); $log->appendLog("La cadena CURL que se envia es: " . $curl_string);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string); curl_setopt($ch, CURLOPT_POSTFIELDS, $curl_string);
@ -465,11 +505,10 @@ class ClientCallBellAPI
public function sendTextPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool public function sendTextPaymentNotificationWhatsApp($clientWhatsAppNumber, $notificationData): bool
{ {
$nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']); $nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']);
$folio = $notificationData->paymentData['id']; $folio = $notificationData->paymentData['id'];
$client_id = $notificationData->clientData['id']; $client_id = $notificationData->clientData['id'];
$fecha_pago = null; $fecha_pago = '';
$cantidad_pagada = $notificationData->paymentData['amount']; $cantidad_pagada = $notificationData->paymentData['amount'];
$metodo_pago = ''; $metodo_pago = '';
//$invoice_id= null; //$invoice_id= null;
@ -480,9 +519,6 @@ class ClientCallBellAPI
$texto_credito = null; $texto_credito = null;
$creditoClienteBase = $notificationData->clientData['accountCredit']; $creditoClienteBase = $notificationData->clientData['accountCredit'];
switch ($notificationData->paymentData['methodId']) { switch ($notificationData->paymentData['methodId']) {
case '11721cdf-a498-48be-903e-daa67552e4f6': case '11721cdf-a498-48be-903e-daa67552e4f6':
$metodo_pago = 'Cheque 📄'; $metodo_pago = 'Cheque 📄';
@ -517,11 +553,8 @@ class ClientCallBellAPI
default: default:
$metodo_pago = 'Desconocido, revisar metodos de pago no contemplados'; $metodo_pago = 'Desconocido, revisar metodos de pago no contemplados';
break; break;
} }
$log = PluginLogManager::create(); //Initialize Logger $log = PluginLogManager::create(); //Initialize Logger
$log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL); $log->appendLog("Eviando comprobante de pago al cliente: " . $notificationData->clientData['id'] . ' con número: ' . $clientWhatsAppNumber . PHP_EOL);
@ -533,14 +566,13 @@ class ClientCallBellAPI
//Path base del comprobante de pago //Path base del comprobante de pago
$pdf_payment_path = ''; $pdf_payment_path = '';
$this->ucrmApi = UcrmApi::create(); $this->ucrmApi = UcrmApi::create();
$payments = $this->ucrmApi->get( $payments = $this->ucrmApi->get(
'payments/', 'payments/',
[ [
'clientId' => $notificationData->clientData['id'], 'clientId' => $notificationData->clientData['id'],
'limit' => 1, 'limit' => 1,
'direction' => 'DESC' 'direction' => 'DESC',
] ]
); );
@ -549,7 +581,6 @@ class ClientCallBellAPI
$datos_paymentJsonText = json_encode($payments, true); $datos_paymentJsonText = json_encode($payments, true);
$log->appendLog("Datos traidos con payment api: " . $datos_paymentJsonText . PHP_EOL); $log->appendLog("Datos traidos con payment api: " . $datos_paymentJsonText . PHP_EOL);
// $log->appendLog("Check 1" . PHP_EOL); // $log->appendLog("Check 1" . PHP_EOL);
$paymentDate = new DateTime($fecha_pago); $paymentDate = new DateTime($fecha_pago);
@ -559,7 +590,6 @@ class ClientCallBellAPI
// Formatear la fecha como "d/m/Y g:ia" (día/mes/año hora:minutos am/pm) // Formatear la fecha como "d/m/Y g:ia" (día/mes/año hora:minutos am/pm)
$fecha_pago = $paymentDate->format('d/m/Y g:ia'); $fecha_pago = $paymentDate->format('d/m/Y g:ia');
$accountBalance = $notificationData->clientData['accountBalance']; $accountBalance = $notificationData->clientData['accountBalance'];
$saldoTexto = ''; $saldoTexto = '';
@ -581,21 +611,15 @@ class ClientCallBellAPI
if ($creditoClienteBase > 0 && empty($notificationData->paymentData['paymentCovers'])) { if ($creditoClienteBase > 0 && empty($notificationData->paymentData['paymentCovers'])) {
$texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito";
$log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL);
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"4ac9dc060cf746b6ad7f2e8acad355e0\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"4ac9dc060cf746b6ad7f2e8acad355e0\",\n \"optin_contact\": true\n }";
} else { } else {
if ($creditoPorPago > 0) { if ($creditoPorPago > 0) {
$texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito"; $texto_credito = "La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito";
$log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL); $log->appendLog("La cantidad que sobra de $creditoPorPagoFormateado se ha convertido a crédito" . PHP_EOL);
} else { } else {
$texto_credito = '_________________________'; $texto_credito = '_________________________';
} }
@ -612,10 +636,8 @@ class ClientCallBellAPI
$log->appendLog("Verificar paymentCovers " . PHP_EOL); $log->appendLog("Verificar paymentCovers " . PHP_EOL);
$log->appendLog("payment covers" . json_encode($notificationData->paymentData['paymentCovers']) . PHP_EOL); $log->appendLog("payment covers" . json_encode($notificationData->paymentData['paymentCovers']) . PHP_EOL);
if (!empty($notificationData->paymentData['paymentCovers'])) { if (!empty($notificationData->paymentData['paymentCovers'])) {
$log->appendLog('Datos del payment covers:' . PHP_EOL); $log->appendLog('Datos del payment covers:' . PHP_EOL);
@ -654,14 +676,10 @@ class ClientCallBellAPI
$log->appendLog('Numero de factura: ' . $responseInvoicesJSON['number'] . PHP_EOL); $log->appendLog('Numero de factura: ' . $responseInvoicesJSON['number'] . PHP_EOL);
$log->appendLog('TOTAL de factura: ' . $responseInvoicesJSON['total'] . PHP_EOL); $log->appendLog('TOTAL de factura: ' . $responseInvoicesJSON['total'] . PHP_EOL);
$total_factura = $responseInvoicesJSON['total']; $total_factura = $responseInvoicesJSON['total'];
} catch (\Exception $e) { } catch (\Exception $e) {
$log->appendLog("Error con un problema al obtener alguna factura del cliente: " . $e->getMessage() . PHP_EOL); $log->appendLog("Error con un problema al obtener alguna factura del cliente: " . $e->getMessage() . PHP_EOL);
return false; return false;
} }
} else { } else {
$log->appendLog("no hay datos en payment covers" . PHP_EOL); $log->appendLog("no hay datos en payment covers" . PHP_EOL);
$invoiceIds = $notificationData->paymentData['id']; $invoiceIds = $notificationData->paymentData['id'];
@ -674,13 +692,8 @@ class ClientCallBellAPI
$pagoFacturaFormateado = '$' . $amounts . ' MXN'; $pagoFacturaFormateado = '$' . $amounts . ' MXN';
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$invoiceIds\", \"$total_facturaFormateada\", \"$pagoFacturaFormateado\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"c1396a6ad3cb4192916d4ac2bfb782a5\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"John Doe\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$folio\", \"$client_id\", \"$fecha_pago\", \"$cantidad_pagadaFormateada\", \"$metodo_pago\", \"$invoiceIds\", \"$total_facturaFormateada\", \"$pagoFacturaFormateado\", \"$saldoTexto\", \"$texto_credito\"],\n \"template_uuid\": \"c1396a6ad3cb4192916d4ac2bfb782a5\",\n \"optin_contact\": true\n }";
} }
//$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL); //$log->appendLog("Entrando al metodo sendPaymentNotificationWhatsAp" . PHP_EOL);
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send'); curl_setopt($ch, CURLOPT_URL, 'https://api.callbell.eu/v1/messages/send');
@ -691,9 +704,6 @@ class ClientCallBellAPI
'Content-Type: application/json', 'Content-Type: application/json',
]); ]);
//$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }";
// $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }"; // $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\"\n },\n \"template_values\": [\"$campo1\", \"$campo2\"],\n \"template_uuid\": \"55705f1fe4e24bab80104dc2643fe11c\",\n \"optin_contact\": true\n }";
@ -742,7 +752,6 @@ class ClientCallBellAPI
public function sendOverdueNotificationWhatsApp($clientWhatsAppNumber, $notificationData): void public function sendOverdueNotificationWhatsApp($clientWhatsAppNumber, $notificationData): void
{ {
$log = PluginLogManager::create(); //Initialize Logger $log = PluginLogManager::create(); //Initialize Logger
// URL base de la API // URL base de la API
@ -752,7 +761,6 @@ class ClientCallBellAPI
//Path base del comprobante de pago //Path base del comprobante de pago
//$pdf_payment_path = ''; //$pdf_payment_path = '';
// $this->ucrmApi = UcrmApi::create(); // $this->ucrmApi = UcrmApi::create();
// $invoices = $this->ucrmApi->get( // $invoices = $this->ucrmApi->get(
// 'invoices/', // 'invoices/',
@ -786,8 +794,6 @@ class ClientCallBellAPI
// 'verify' => false, // 'verify' => false,
// ]); // ]);
// if (empty($notificationData->clientData['contacts'][0]['email'])) { // if (empty($notificationData->clientData['contacts'][0]['email'])) {
// $log->appendLog("El cliente no tiene correo" . PHP_EOL); // $log->appendLog("El cliente no tiene correo" . PHP_EOL);
// } else { // } else {
@ -815,9 +821,6 @@ class ClientCallBellAPI
// $log->appendLog("La ruta no es válida o no existe" . PHP_EOL); // $log->appendLog("La ruta no es válida o no existe" . PHP_EOL);
// } // }
$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"SIIP INTERNET\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\",\n },\n \"template_values\": [\"$nombre_cliente\", \"$saldo\"],\n \"template_uuid\": \"9e7024c0a61a4c49b382150d26888dc2\",\n \"optin_contact\": true\n }"; $curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"SIIP INTERNET\",\n \"type\": \"text\",\n \"content\": {\n \"text\": \"S/M\",\n },\n \"template_values\": [\"$nombre_cliente\", \"$saldo\"],\n \"template_uuid\": \"9e7024c0a61a4c49b382150d26888dc2\",\n \"optin_contact\": true\n }";
//$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }"; //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"57ead79cebd14902921477922403093b\",\n \"optin_contact\": true\n }";
//$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }"; //$curl_string = "{\n \"to\": \"$clientWhatsAppNumber\",\n \"from\": \"whatsapp\",\n \"type\": \"document\",\n \"content\": {\n \"text\": \"S/M\",\n \"url\": \"$url_file\"\n },\n \"template_values\": [\"$nombre_cliente\", \"$payment_amount\", \"$saldo\"],\n \"template_uuid\": \"6c0df98317b44f7b8666375a6cc8454c\",\n \"optin_contact\": true\n }";
@ -830,10 +833,8 @@ class ClientCallBellAPI
$log->appendLog("Response del CallBell: " . $response); $log->appendLog("Response del CallBell: " . $response);
curl_close($ch); curl_close($ch);
} }
public function getContactWhatsapp($cellphone_number): string public function getContactWhatsapp($cellphone_number): string
{ {
// URL de la API REST // URL de la API REST
@ -847,7 +848,7 @@ class ClientCallBellAPI
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->CallBellAPIToken, 'Authorization: Bearer ' . $this->CallBellAPIToken,
'Content-Type: application/json' 'Content-Type: application/json',
]); ]);
// Ejecutar la solicitud y obtener la respuesta // Ejecutar la solicitud y obtener la respuesta
@ -888,8 +889,6 @@ class ClientCallBellAPI
} }
} }
$log->appendLog("Dentro del proceso del patch: " . PHP_EOL); $log->appendLog("Dentro del proceso del patch: " . PHP_EOL);
$this->ucrmApi = UcrmApi::create(); $this->ucrmApi = UcrmApi::create();
$payments = $this->ucrmApi->get( $payments = $this->ucrmApi->get(
@ -897,7 +896,7 @@ class ClientCallBellAPI
[ [
'clientId' => $notificationData->clientData['id'], 'clientId' => $notificationData->clientData['id'],
'limit' => 1, 'limit' => 1,
'direction' => 'DESC' 'direction' => 'DESC',
] ]
); );
@ -909,10 +908,6 @@ class ClientCallBellAPI
//$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL); //$log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos: " . $notificationData->paymentData[0]['createdDate']. PHP_EOL);
// $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL); // $log->appendLog("Esto es lo que trae la fecha mas reciente de los pagos opcion 2: " . $payments[0]['createdDate'] . PHP_EOL);
$uuid = $response_getContactCallBell['contact']['uuid']; $uuid = $response_getContactCallBell['contact']['uuid'];
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@ -973,11 +968,8 @@ class ClientCallBellAPI
default: default:
$payment_method = 'Desconocido, revisar metodos de pago no contemplados'; $payment_method = 'Desconocido, revisar metodos de pago no contemplados';
break; break;
} }
$fecha_actual = new DateTime(); $fecha_actual = new DateTime();
$fecha_actual->modify('-6 hours'); $fecha_actual->modify('-6 hours');
$fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i");
@ -1033,15 +1025,11 @@ class ClientCallBellAPI
'"Antena/Sectorial": "' . $antenaSectorial . '"' . '"Antena/Sectorial": "' . $antenaSectorial . '"' .
'}'; '}';
$data_CRM = [
$data_CRM = array(
//"uuid" => $json_responseAPI->contact->uuid, //"uuid" => $json_responseAPI->contact->uuid,
"name" => sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), "name" => sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']),
"custom_fields" => array( "custom_fields" => [
"Cliente" => $notificationData->clientData['id'], "Cliente" => $notificationData->clientData['id'],
"Domicilio" => ($notificationData->clientData['fullAddress'] == null) ? '📍❌ Sin domicilio' : '📍 ' . $notificationData->clientData['fullAddress'], "Domicilio" => ($notificationData->clientData['fullAddress'] == null) ? '📍❌ Sin domicilio' : '📍 ' . $notificationData->clientData['fullAddress'],
"Nombre" => sprintf("👤 %s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']), "Nombre" => sprintf("👤 %s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']),
@ -1054,18 +1042,17 @@ class ClientCallBellAPI
"Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada, "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada,
"Clabe Interbancaria" => $clabeInterbancaria, "Clabe Interbancaria" => $clabeInterbancaria,
"Site" => $site, "Site" => $site,
"Antena/Sectorial" => $antenaSectorial "Antena/Sectorial" => $antenaSectorial,
) ],
); ];
$log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL);
$data_CRM2 = [
$data_CRM2 = array( "custom_fields" => [
"custom_fields" => array(
"Resumen" => $resumenClienteJSON, "Resumen" => $resumenClienteJSON,
) ],
); ];
$log->appendLog("JSON con los datos a actualizar del resumen: " . $resumenClienteJSON . PHP_EOL); $log->appendLog("JSON con los datos a actualizar del resumen: " . $resumenClienteJSON . PHP_EOL);
@ -1095,7 +1082,6 @@ class ClientCallBellAPI
// $this->deleteFileWordPressAndLocal($fileNameComprobante); // $this->deleteFileWordPressAndLocal($fileNameComprobante);
// } // }
// $json_data_patch = '{ // $json_data_patch = '{
// "attributes": [ // "attributes": [
// { // {
@ -1117,7 +1103,6 @@ class ClientCallBellAPI
// 'verify' => false, // 'verify' => false,
// ]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM // ]); //aquí se contruye la petición para hacer patch hacia el cliente en sus custom fields con la API del UISP UCRM
// } catch (GuzzleException $error) { // } catch (GuzzleException $error) {
// echo "Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL; // echo "Error al hacer el patch al CRM: " . $error->getMessage() . PHP_EOL;
// //exit(); // //exit();
@ -1129,17 +1114,13 @@ class ClientCallBellAPI
} }
} }
public function patchServiceStatusWhatsApp($response_getContactCallBell, $notificationData) public function patchServiceStatusWhatsApp($response_getContactCallBell, $notificationData)
{ {
$log = PluginLogManager::create(); //Initialize Logger $log = PluginLogManager::create(); //Initialize Logger
$log->appendLog("Actualizando estado del servicio " . PHP_EOL); $log->appendLog("Actualizando estado del servicio " . PHP_EOL);
$uuid = $response_getContactCallBell['contact']['uuid']; $uuid = $response_getContactCallBell['contact']['uuid'];
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@ -1154,18 +1135,13 @@ class ClientCallBellAPI
$log->appendLog("Nombre del cliente al que se va a actualizar el estado del servicio: " . $nombre_cliente . PHP_EOL); $log->appendLog("Nombre del cliente al que se va a actualizar el estado del servicio: " . $nombre_cliente . PHP_EOL);
$log->appendLog("UUID: " . $uuid . PHP_EOL); $log->appendLog("UUID: " . $uuid . PHP_EOL);
$fecha_actual = new DateTime(); $fecha_actual = new DateTime();
$fecha_actual->modify('-6 hours'); $fecha_actual->modify('-6 hours');
$fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i"); $fecha_actual_ajustada = $fecha_actual->format("d/m/Y H:i");
$data_CRM = [
$data_CRM = array(
"name" => $nombre_cliente, "name" => $nombre_cliente,
"custom_fields" => array( "custom_fields" => [
"Estado" => ( "Estado" => (
isset($notificationData->serviceData['status']) isset($notificationData->serviceData['status'])
&& $notificationData->serviceData['status'] == 3 && $notificationData->serviceData['status'] == 3
@ -1173,11 +1149,9 @@ class ClientCallBellAPI
(isset($notificationData->serviceData['status']) (isset($notificationData->serviceData['status'])
&& $notificationData->serviceData['status'] == 1) && $notificationData->serviceData['status'] == 1)
? '🟢 Activo' : '🚫 Finalizado'), ? '🟢 Activo' : '🚫 Finalizado'),
"Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada "Fecha Ultima Actualizacion" => '📆🔄️ ' . $fecha_actual_ajustada,
) ],
); ];
$log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL); $log->appendLog("JSON con los datos a actualizar: " . json_encode($data_CRM) . PHP_EOL);
@ -1186,8 +1160,6 @@ class ClientCallBellAPI
$response = curl_exec($ch); $response = curl_exec($ch);
$log->appendLog("Response Patch CallBell: " . $response . PHP_EOL); $log->appendLog("Response Patch CallBell: " . $response . PHP_EOL);
curl_close($ch); curl_close($ch);
} else { } else {
$log->appendLog("No hay cambios en el estado del servicio que actualizar " . PHP_EOL); $log->appendLog("No hay cambios en el estado del servicio que actualizar " . PHP_EOL);
} }
@ -1217,7 +1189,6 @@ class ClientCallBellAPI
$log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL); $log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL);
// Conexión FTP // Conexión FTP
$ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor 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); $login = ftp_login($ftp_conn, $ftp_username, $ftp_password);
@ -1231,10 +1202,10 @@ class ClientCallBellAPI
$log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL);
$log->appendLog("La URL es: " . $url . PHP_EOL); $log->appendLog("La URL es: " . $url . PHP_EOL);
$this->deleteFilesWordpressExceptLastHundred($log,$ftp_conn,$remote_file); $this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_file);
// Cerrar conexión FTP // Cerrar conexión FTP
ftp_close($ftp_conn);//COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO
return $url;//COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO
} else { } else {
$log->appendLog("Error al cargar el archivo " . PHP_EOL); $log->appendLog("Error al cargar el archivo " . PHP_EOL);
@ -1284,15 +1255,12 @@ class ClientCallBellAPI
// return $url; // return $url;
// } // }
} else { } else {
$log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL);
return ''; return '';
} }
} }
function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool
{ {
@ -1335,10 +1303,8 @@ class ClientCallBellAPI
ftp_close($ftp_conn); ftp_close($ftp_conn);
return false; return false;
} }
} }
/** /**
* /Elimina los archivos del directorio de comprobantes de pago a excepción de los últimos 100 para evitar errores de envío * /Elimina los archivos del directorio de comprobantes de pago a excepción de los últimos 100 para evitar errores de envío
* @return void * @return void
@ -1351,7 +1317,6 @@ class ClientCallBellAPI
if (!is_dir($directory)) { if (!is_dir($directory)) {
$log->appendLog('El directorio no existe.'); $log->appendLog('El directorio no existe.');
die("El directorio no existe."); die("El directorio no existe.");
} }
// Obtener la lista de archivos en el directorio // Obtener la lista de archivos en el directorio
@ -1378,5 +1343,4 @@ class ClientCallBellAPI
$log->appendLog("Hay menos de 100 archivos en el directorio. No se eliminarán archivos." . PHP_EOL); $log->appendLog("Hay menos de 100 archivos en el directorio. No se eliminarán archivos." . PHP_EOL);
} }
} }
} }

View File

@ -1,14 +1,13 @@
<?php <?php
declare(strict_types=1); declare (strict_types = 1);
namespace SmsNotifier; namespace SmsNotifier;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use SmsNotifier\Facade\TwilioNotifierFacade;
use SmsNotifier\Facade\PluginNotifierFacade; use SmsNotifier\Facade\PluginNotifierFacade;
use SmsNotifier\Facade\PluginOxxoNotifierFacade; use SmsNotifier\Facade\PluginOxxoNotifierFacade;
use SmsNotifier\Facade\TwilioNotifierFacade;
use SmsNotifier\Factory\NotificationDataFactory; use SmsNotifier\Factory\NotificationDataFactory;
use SmsNotifier\Service\Logger; use SmsNotifier\Service\Logger;
use SmsNotifier\Service\OptionsManager; use SmsNotifier\Service\OptionsManager;
@ -56,7 +55,6 @@ class Plugin
*/ */
protected $ucrmApi; protected $ucrmApi;
public function __construct( public function __construct(
Logger $logger, Logger $logger,
OptionsManager $optionsManager, OptionsManager $optionsManager,
@ -80,13 +78,13 @@ class Plugin
// $hola = PHP_SAPI; // $hola = PHP_SAPI;
// $this->logger->info('valor de PHP_SAPI: ' . $hola); // $this->logger->info('valor de PHP_SAPI: ' . $hola);
if (PHP_SAPI === 'fpm-fcgi') { if (PHP_SAPI === 'fpm-fcgi') {
//$this->logger->debug('Whatsapp over HTTP started'); // $this->logger->debug('Whatsapp over HTTP started');
$this->processHttpRequest(); $this->processHttpRequest();
//$this->logger->debug('HTTP request processing ended.'); // $this->logger->debug('HTTP request processing ended.');
} elseif (PHP_SAPI === 'cli') { } elseif (PHP_SAPI === 'cli') {
//$this->logger->debug('Whatsapp over CLI started'); // $this->logger->debug('Whatsapp over CLI started');
$this->processCli(); $this->processCli();
//$this->logger->debug('CLI process ended.'); // $this->logger->debug('CLI process ended.');
} else { } else {
throw new \UnexpectedValueException('Unknown PHP_SAPI type: ' . PHP_SAPI); throw new \UnexpectedValueException('Unknown PHP_SAPI type: ' . PHP_SAPI);
} }
@ -108,12 +106,11 @@ class Plugin
$this->logger->setLogLevelThreshold(LogLevel::DEBUG); $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'); if (! $userInput) {
if (!$userInput) {
$this->logger->warning('no input'); $this->logger->warning('no input');
return; return;
@ -121,7 +118,7 @@ class Plugin
$jsonData = @json_decode($userInput, true, 50); $jsonData = @json_decode($userInput, true, 50);
if (!isset($jsonData['uuid'])) { if (! isset($jsonData['uuid'])) {
$this->logger->info('No UUID found in the webhook data'); $this->logger->info('No UUID found in the webhook data');
//$this->logger->error('JSON error: ' . json_last_error_msg()); //$this->logger->error('JSON error: ' . json_last_error_msg());
@ -131,25 +128,50 @@ class Plugin
if ($jsonData) { if ($jsonData) {
switch ($jsonData['type']) { switch ($jsonData['type']) {
case 'customer_cash_balance_transaction.created': case 'customer_cash_balance_transaction.created':
$this->logger->info('Evento de transfencia al cliente encontrado'); if ($jsonData['data']['object']['type'] === 'funded') {
$this->logger->info('Valor del EventJSON: ' . json_encode($jsonData) . PHP_EOL); $this->logger->info('Evento de transferencia de un cliente recibido: ' . json_encode($jsonData) . PHP_EOL);
$this->pluginNotifierFacade->createPaymentIntent($jsonData); $this->pluginNotifierFacade->createPaymentIntent($jsonData);
}
if ($jsonData['data']['object']['type'] === 'applied_to_payment') {
$this->logger->info('Se aplicó el saldo en Stripe de un pago: ' . json_encode($jsonData) . PHP_EOL);
$this->pluginNotifierFacade->registerPaymentFromWebhook($jsonData);
}elseif ($jsonData['data']['object']['type'] === 'unapplied_from_payment'){
//ejemplo de json para transferencia de dinero cancelada: {"id":"evt_1RlEGgEFY1WEUtgR6Bp2DzDP","object":"event","api_version":"2023-10-16","created":1752606717,"data":{"object":{"id":"ccsbtxn_1RlEGfEFY1WEUtgRv8jAUGmE","object":"customer_cash_balance_transaction","created":1752606717,"currency":"mxn","customer":"cus_PetN1dhr4rx0kX","ending_balance":18000,"livemode":false,"net_amount":18000,"type":"unapplied_from_payment","unapplied_from_payment":{"payment_intent":"pi_3RlDPdEFY1WEUtgR1JBgNhTQ"}}},"livemode":false,"pending_webhooks":2,"request":{"id":"req_954mskVBfAI0jn","idempotency_key":"749518f6-baa0-4ae9-99e4-8029a35719aa"},"type":"customer_cash_balance_transaction.created"}
$paymentIntentId = $jsonData['data']['object']['unapplied_from_payment']['payment_intent'];
//Se canceló una transferencia de dinero, imprimir que se canceló y además el monto neto
$this->logger->warning('Evento de transferencia cancelada para el pago: ' . $paymentIntentId . PHP_EOL);
$this->logger->warning('Monto neto de la transferencia cancelada: ' . $jsonData['data']['object']['net_amount'] . PHP_EOL);
}
break;
case 'payout.failed':
$this->logger->info('Evento de transferencia fallida encontrado: ' . json_encode($jsonData) . PHP_EOL);
//imprimir detalles del fallo
$this->logger->info('Detalles del fallo: ' . json_encode($jsonData));
break;
case 'payment_intent.partially_funded':
$this->logger->info('Evento de pago parcialmente financiado encontrado: ' . json_encode($jsonData) . PHP_EOL);
//imprimir detalles del evento o pago
$this->logger->info('Detalles del evento: ' . json_encode($jsonData));
break; break;
case 'inbound_payment.payment_attempt': case 'inbound_payment.payment_attempt':
$this->logger->info('Evento de Pago de OXXO recibido'); //$this->logger->info('Evento de Pago de OXXO recibido: ' . json_encode($jsonData) . PHP_EOL);
$this->logger->info('Valor del EventJSON: ' . json_encode($jsonData) . PHP_EOL); break;
case 'cash_balance.funds_available':
$this->logger->info('Evento de Pago de fondos disponibles recibido: ' . json_encode($jsonData) . PHP_EOL);
break; break;
case 'energy.alert': case 'energy.alert':
$this->logger->info('Evento de Energía recibido: ' . $jsonData['message'] . PHP_EOL); $this->logger->info('Evento de Energía recibido: ' . $jsonData['message'] . PHP_EOL);
break; break;
case 'oxxo.request': case 'oxxo.request':
$this->logger->info('Evento de referencia de oxxo recibido' . PHP_EOL); $this->logger->info('Evento de solicitud de referencia de oxxo recibido' . PHP_EOL);
// Construir la URL basada en el "client_id" // Construir la URL basada en el "client_id"
// $url = "https://siip.mx/wp/wp-content/uploads/img/voucher.png"; // $url = "https://siip.mx/wp/wp-content/uploads/img/voucher.png";
if (!empty($jsonData['amount'])) { if (! empty($jsonData['amount'])) {
$this->logger->info('Referencia persnoalizada, Valor del monto: ' . $jsonData['amount'] . PHP_EOL); $this->logger->info('Referencia personalizada, Valor del monto: ' . $jsonData['amount'] . PHP_EOL);
$intentos = 0; $intentos = 0;
do { do {
// if ($intentos > 1) { // if ($intentos > 1) {
@ -178,12 +200,14 @@ class Plugin
//this->logger->error('Error al crear la referencia de OXXO: ' . $responseOxxo); //this->logger->error('Error al crear la referencia de OXXO: ' . $responseOxxo);
//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 //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 = '{' . $response = '{' .
'"url":' . $responseOxxo['url'] . ',' . '"url": "' . $responseOxxo['url'] . '",' .
'"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' .
'"voucher_image_url": "' . $responseOxxo['voucher_image_url'] . '",' .
'"error": "' . $responseOxxo['error'] . '",' . '"error": "' . $responseOxxo['error'] . '",' .
'"failDescription": "' . $responseOxxo['failDescription'] . '",' . '"failDescription": "' . $responseOxxo['failDescription'] . '",' .
'"clientID": "' . $responseOxxo['clientID'] . '",' . '"clientID": "' . $responseOxxo['clientID'] . '",' .
'"clientFullName": "' . $responseOxxo['clientFullName'] . '",' . '"clientFullName": "' . $responseOxxo['clientFullName'] . '",' .
'"amount": "' . $responseOxxo['amount'] . '",' . '"amount": "' . $responseOxxo['amount'] . '"' .
'}'; '}';
header('Content-Type: application/json'); header('Content-Type: application/json');
@ -195,6 +219,7 @@ class Plugin
$response = '{' . $response = '{' .
'"url": "' . $responseOxxo['url'] . '",' . '"url": "' . $responseOxxo['url'] . '",' .
'"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' . '"oxxo_reference": "' . $responseOxxo['oxxo_reference'] . '",' .
'"voucher_image_url": "' . $responseOxxo['voucher_image_url'] . '",' .
'"error": "' . $responseOxxo['error'] . '",' . '"error": "' . $responseOxxo['error'] . '",' .
'"failDescription": "' . $responseOxxo['failDescription'] . '",' . '"failDescription": "' . $responseOxxo['failDescription'] . '",' .
'"clientID": "' . $responseOxxo['clientID'] . '",' . '"clientID": "' . $responseOxxo['clientID'] . '",' .
@ -223,11 +248,6 @@ class Plugin
// $webhook_string = json_encode($event_json); // $webhook_string = json_encode($event_json);
// $this->logger->debug("El valor de webhook_string: " . $webhook_string . PHP_EOL); // $this->logger->debug("El valor de webhook_string: " . $webhook_string . PHP_EOL);
$notification = $this->notificationDataFactory->getObject($jsonData); $notification = $this->notificationDataFactory->getObject($jsonData);
$this->logger->debug('valor el evento recibido por webhook: ' . $notification->eventName . PHP_EOL); $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('Valor de JSON: ' . json_encode($jsonData) . PHP_EOL);
@ -240,6 +260,9 @@ class Plugin
$this->logger->info('Webhook test successful.'); $this->logger->info('Webhook test successful.');
return; return;
} else if ($notification->changeType === 'paperless.update') {
//imprimir el webhook json
$this->logger->info('Webhook de paperless update: ' . json_encode($jsonData) . PHP_EOL);
} }
// if (!$notification->clientId) { // if (!$notification->clientId) {
// $this->logger->warning('No client specified, cannot notify them.'); // $this->logger->warning('No client specified, cannot notify them.');
@ -263,11 +286,8 @@ class Plugin
$payment_method_id = $notification->paymentData['methodId']; $payment_method_id = $notification->paymentData['methodId'];
//$this->logger->debug('Metodo de pago: ' . $notification->paymentData['methodId'] . PHP_EOL); //$this->logger->debug('Metodo de pago: ' . $notification->paymentData['methodId'] . PHP_EOL);
$payment_method = ''; $payment_method = '';
switch ($payment_method_id) { switch ($payment_method_id) {
case '11721cdf-a498-48be-903e-daa67552e4f6': case '11721cdf-a498-48be-903e-daa67552e4f6':
@ -340,16 +360,64 @@ class Plugin
$payment_method = 'Desconocido, revisar metodos de pago no contemplados'; $payment_method = 'Desconocido, revisar metodos de pago no contemplados';
break; break;
} }
} else if ($notification->eventName === 'client.edit') { } else if ($notification->eventName === 'client.edit') {
$this->logger->debug('Se actualiza a un cliente'); $this->logger->debug('Se actualiza a un cliente');
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); $this->logger->debug('Valor de json_data: ' . json_encode($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}}} //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);
}
//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 // Validar que 'extraData' existe y contiene las claves necesarias
if ( if (
isset($jsonData['extraData']['entityBeforeEdit']) && isset($jsonData['extraData']['entityBeforeEdit']) &&
@ -358,11 +426,11 @@ class Plugin
$entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit']; $entityBeforeEdit = $jsonData['extraData']['entityBeforeEdit'];
$entity = $jsonData['extraData']['entity']; $entity = $jsonData['extraData']['entity'];
$this->logger->debug('Validando claves dentro de entityBeforeEdit y entity'); //$this->logger->debug('Validando claves dentro de entityBeforeEdit y entity');
// Validar si 'isLead' esta en true en entityBeforeEdit y en false en 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)) { if (array_key_exists('isLead', $entityBeforeEdit) && array_key_exists('isLead', $entity)) {
$this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo isLead'); //$this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo isLead');
$isLeadBefore = $entityBeforeEdit['isLead']; $isLeadBefore = $entityBeforeEdit['isLead'];
$isLeadAfter = $entity['isLead']; $isLeadAfter = $entity['isLead'];
@ -370,7 +438,7 @@ class Plugin
// Comprobar si 'isLead' cambió de true a false // Comprobar si 'isLead' cambió de true a false
if ($isLeadBefore === true && $isLeadAfter === false) { if ($isLeadBefore === true && $isLeadAfter === false) {
$this->logger->debug('El cliente cambió de potencial a cliente'); $this->logger->debug('El cliente cambió de potencial a cliente');
$this->pluginNotifierFacade->createStripeClient($notification); //$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 { } else {
$this->logger->debug('El cliente no cambió de potencial a cliente'); $this->logger->debug('El cliente no cambió de potencial a cliente');
} }
@ -378,7 +446,6 @@ class Plugin
$this->logger->warning('El campo isLead no existe en entityBeforeEdit o entity'); $this->logger->warning('El campo isLead no existe en entityBeforeEdit o entity');
} }
// buscar si existe la etiqueta 'STRIPE' en entity pero no en entityBeforeEdit // buscar si existe la etiqueta 'STRIPE' en entity pero no en entityBeforeEdit
$tags = $jsonData['extraData']['entity']['tags']; $tags = $jsonData['extraData']['entity']['tags'];
$tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags']; $tagsBefore = $jsonData['extraData']['entityBeforeEdit']['tags'];
@ -394,28 +461,26 @@ class Plugin
$this->logger->debug('Validando si la etiqueta STRIPE existe en entity pero no en entityBeforeEdit'); $this->logger->debug('Validando si la etiqueta STRIPE existe en entity pero no en entityBeforeEdit');
// Comprobar si la etiqueta 'STRIPE' existe en 'tags' pero no en 'tagsBefore' // Comprobar si la etiqueta 'CREAR CLABE STRIPE' existe en 'tags' pero no en 'tagsBefore'
$stripeTagExists = false; $stripeTagExists = false;
$stripeTagExistsBefore = false; $stripeTagExistsBefore = false;
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ($tag['name'] === 'CREARCLABESTRIPE') { if ($tag['name'] === 'CREAR CLABE STRIPE') {
$stripeTagExists = true; $stripeTagExists = true;
break; break;
} }
} }
foreach ($tagsBefore as $tag) { foreach ($tagsBefore as $tag) {
if ($tag['name'] === 'CREARCLABESTRIPE') { if ($tag['name'] === 'CREAR CLABE STRIPE') {
$stripeTagExistsBefore = true; $stripeTagExistsBefore = true;
break; break;
} }
} }
// Comprobar si la etiqueta 'STRIPE' existe en 'tags' pero no en 'tagsBefore' // Comprobar si la etiqueta 'STRIPE' existe en 'tags' pero no en 'tagsBefore'
if ($stripeTagExists && !$stripeTagExistsBefore) { if ($stripeTagExists && ! $stripeTagExistsBefore) {
$this->logger->debug('La etiqueta STRIPE se agregará al cliente'); $this->logger->debug('La etiqueta CREAR CLABE STRIPE se agregará al cliente');
$this->pluginNotifierFacade->createStripeClient($notification, true); $this->pluginNotifierFacade->createStripeClient($notification, true);
} else {
$this->logger->debug('La etiqueta STRIPE no se agregó al cliente');
} }
} else { } else {
$this->logger->warning('El campo tags no existe en entity o entityBeforeEdit'); $this->logger->warning('El campo tags no existe en entity o entityBeforeEdit');
@ -425,10 +490,8 @@ class Plugin
$this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData'); $this->logger->warning('Los datos entityBeforeEdit o entity no están presentes en extraData');
} }
$this->notifierFacade->verifyClientActionToDo($notification); $this->notifierFacade->verifyClientActionToDo($notification);
} else if ($notification->eventName === 'client.add') { } else if ($notification->eventName === 'client.add') {
$this->logger->debug('Se agregó un nuevo cliente' . PHP_EOL); $this->logger->debug('Se agregó un nuevo cliente' . PHP_EOL);
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); $this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
@ -453,6 +516,56 @@ class Plugin
} else if ($notification->eventName === 'service.edit') { } else if ($notification->eventName === 'service.edit') {
$this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL); $this->logger->debug('Se editó el servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification); $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);
}
} else if ($notification->eventName === 'service.suspend') { } else if ($notification->eventName === 'service.suspend') {
$this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL); $this->logger->debug('Se suspendió el servicio a un cliente' . PHP_EOL);
$this->notifierFacade->verifyServiceActionToDo($notification); $this->notifierFacade->verifyServiceActionToDo($notification);
@ -494,28 +607,21 @@ class Plugin
$this->notifierFacade->verifyInvoiceActionToDo($notification); $this->notifierFacade->verifyInvoiceActionToDo($notification);
} else if ($notification->eventName === 'job.add') { } else if ($notification->eventName === 'job.add') {
$this->logger->debug('Se ha agregado un nuevo trabajo' . PHP_EOL); $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'];
$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);
// Verificar que existen tanto 'assignedUserId' como 'entity'
if (isset($jsonData['extraData']['entity']['assignedUserId'])) {
$this->logger->debug('El campo assignedUserId existe en los datos del evento');
$assignedUserId = $jsonData['extraData']['entity']['assignedUserId'];
// Comprobar si 'isLead' es null
if ($assignedUserId === null) {
$this->logger->debug('El campo assignedUserId es null');
$this->pluginNotifierFacade->createStripeClient($notification);
} else {
$this->logger->debug('El campo assignedUserId no es null');
$this->notifierFacade->verifyJobActionToDo($notification);
}
} else {
$this->logger->warning('El campo assignedUserId no existe en los datos del evento');
}
} else if ($notification->eventName === 'job.edit') { } else if ($notification->eventName === 'job.edit') {
$this->logger->debug('Se actualiza un trabajo' . PHP_EOL); $this->logger->debug('Se actualiza un trabajo' . PHP_EOL);
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData)); // $this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
// Validar que 'extraData' existe y contiene las claves necesarias // Validar que 'extraData' existe y contiene las claves necesarias
if ( if (
@ -529,7 +635,7 @@ class Plugin
// Validar que 'assignedUserId' existe en ambas entidades // Validar que 'assignedUserId' existe en ambas entidades
if (array_key_exists('assignedUserId', $entityBeforeEdit) && array_key_exists('assignedUserId', $entity)) { if (array_key_exists('assignedUserId', $entityBeforeEdit) && array_key_exists('assignedUserId', $entity)) {
$this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo assignedUserId'); // $this->logger->debug('Los datos entityBeforeEdit y entity contienen el campo assignedUserId');
$assignedUserIdBefore = $entityBeforeEdit['assignedUserId']; $assignedUserIdBefore = $entityBeforeEdit['assignedUserId'];
$assignedUserIdAfter = $entity['assignedUserId']; $assignedUserIdAfter = $entity['assignedUserId'];
@ -537,25 +643,40 @@ class Plugin
$dateAfter = $entity['date']; $dateAfter = $entity['date'];
$statusBefore = $entityBeforeEdit['status']; $statusBefore = $entityBeforeEdit['status'];
$statusAfter = $entity['status']; $statusAfter = $entity['status'];
$title = $entityBeforeEdit['title'];
$pendingPrefix = '[NOTIFICACION-PENDIENTE]'; // Prefijo para trabajos pendientes de notificación
$currentTitle = $entity['title'] ?? ''; // Obtener el título actual
//Valores de status y su significado: 0 = abierto, 1= En curso, 2 = Cerrado
if ($statusAfter == 1) {
// Comprobar si 'assignedUserId' cambió // Comprobar si 'assignedUserId' cambió
if ($assignedUserIdBefore === null && $assignedUserIdAfter != null) { //Si el campo "assignedUserId" cambió de null a un valor // 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');
// $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'); $this->logger->debug('El instalador cambió de null a un valor');
$this->notifierFacade->verifyJobActionToDo($jsonData); // Se envía notificación de trabajo asignado $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ó } 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('El instalador cambió 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 $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ó } else if (($assignedUserIdBefore != null && $assignedUserIdBefore === $assignedUserIdAfter) && ($dateBefore != $dateAfter)) { //Si el campo "assignedUserId" no cambió y la fecha cambió
$this->logger->debug('El instalador no cambió y la fecha sí 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 $this->notifierFacade->verifyJobActionToDo($jsonData, true, false); // Se envía notificación de reprogramación de trabajo
} else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore != $dateAfter)) { } else if (($assignedUserIdBefore != null && $assignedUserIdAfter != $assignedUserIdBefore) && ($dateBefore != $dateAfter)) {
$this->logger->debug('El instalador cambió y la fecha sí cambió'); $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 $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 } 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->logger->debug('El instalador cambió de un valor a null');
$this->notifierFacade->verifyJobActionToDo($jsonData, null, true); // Se envía notificación de trabajo desasignado $this->notifierFacade->verifyJobActionToDo($jsonData, null, true); // Se envía notificación de trabajo desasignado
} else { } else {
$this->logger->debug('No hubo cambio en el instalador ni en la fecha'); $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
}
} }
} else { } else {
@ -567,8 +688,6 @@ class Plugin
} }
//$this->notifierFacade->update($notification); //$this->notifierFacade->update($notification);
} catch (TwilioException $exception) { } catch (TwilioException $exception) {
$this->logger->error($exception->getMessage()); $this->logger->error($exception->getMessage());

View File

@ -1 +1 @@
{"ucrmPublicUrl":"https://venus.siip.mx/crm/","ucrmLocalUrl":"http://localhost/crm/","unmsLocalUrl":"http://unms:8081/nms/","pluginPublicUrl":"https://venus.siip.mx/crm/_plugins/siip-whatsapp-notifications/public.php","pluginAppKey":"5dVI22XIa9NSK0tiDeiQ3SLDBa1UZVT7g5s/tJzi944Bygx89WIHH7I2/niVPiBK","pluginId":16} {"ucrmPublicUrl":"https://venus.siip.mx/crm/","ucrmLocalUrl":"http://localhost/crm/","unmsLocalUrl":"http://unms-api:8081/nms/","pluginPublicUrl":"https://venus.siip.mx/crm/_plugins/siip-whatsapp-notifications/public.php","pluginAppKey":"5dVI22XIa9NSK0tiDeiQ3SLDBa1UZVT7g5s/tJzi944Bygx89WIHH7I2/niVPiBK","pluginId":16}

View File

@ -5,7 +5,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '9db9febc39027c302495c041d5a28e198c035b21', 'reference' => '506615e9116829588e84ecacf5e6e28769e6dd87',
'name' => 'ucrm-plugins/sms-twilio', 'name' => 'ucrm-plugins/sms-twilio',
'dev' => false, 'dev' => false,
), ),
@ -307,7 +307,7 @@
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'reference' => '9db9febc39027c302495c041d5a28e198c035b21', 'reference' => '506615e9116829588e84ecacf5e6e28769e6dd87',
'dev_requirement' => false, 'dev_requirement' => false,
), ),
), ),