corregido doble payment intent y reenvio de comprobantes
This commit is contained in:
parent
d2ce14a7e3
commit
24c32f6334
@ -1 +1 @@
|
|||||||
{"ipserver":"venus.siip.mx","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":"4100","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":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\": \"Á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,"minioEndpoint":"http://172.16.5.134:9002","minioPublicUrl":"https://aws-venus.siip.mx","minioAccessKey":"minioadmin","minioSecretKey":"minioadmin","minioBucket":"vouchers-oxxo","logging_level":true}
|
{"ipserver":"venus.siip.mx","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":"4100","idPaymentAdminCRM":"1180","cashPaymentMethodId":true,"courtesyPaymentMethodId":true,"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\": \"Á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,"minioEndpoint":"http://172.16.5.134:9002","minioPublicUrl":"https://aws-venus.siip.mx","minioAccessKey":"minioadmin","minioSecretKey":"minioadmin","minioBucket":"vouchers-oxxo","logging_level":true,"oxxoPayPaymentMethodId":true,"creditDebitCardPaymentMethodId":true}
|
||||||
12709
data/plugin.log
12709
data/plugin.log
File diff suppressed because one or more lines are too long
@ -189,6 +189,20 @@
|
|||||||
"required": 0,
|
"required": 0,
|
||||||
"type": "checkbox"
|
"type": "checkbox"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "oxxoPayPaymentMethodId",
|
||||||
|
"label": "Envío de Comprobante por pago de OXXO Pay",
|
||||||
|
"description": "Habilita el envío de comprobantes en formato de imagen por WhatsApp cuando el método de pago es OXXO Pay",
|
||||||
|
"required": 0,
|
||||||
|
"type": "checkbox"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "creditDebitCardPaymentMethodId",
|
||||||
|
"label": "Envío de Comprobante por pago de Tarjeta de Crédito/Débito (Genérico)",
|
||||||
|
"description": "Habilita el envío de comprobantes en formato de imagen por WhatsApp cuando el método de pago es Tarjeta de Crédito/Débito (Genérico)",
|
||||||
|
"required": 0,
|
||||||
|
"type": "checkbox"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "notificationTypeText",
|
"key": "notificationTypeText",
|
||||||
"label": "Envío de Comprobante por medio de plantilla de texto",
|
"label": "Envío de Comprobante por medio de plantilla de texto",
|
||||||
|
|||||||
@ -21,7 +21,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
protected $ucrmApi;
|
protected $ucrmApi;
|
||||||
private $systemAttributesCache = null;
|
private $systemAttributesCache = null;
|
||||||
|
|
||||||
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber) {
|
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber)
|
||||||
|
{
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->messageTextFactory = $messageTextFactory;
|
$this->messageTextFactory = $messageTextFactory;
|
||||||
$this->clientPhoneNumber = $clientPhoneNumber;
|
$this->clientPhoneNumber = $clientPhoneNumber;
|
||||||
@ -38,7 +39,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
$this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? '');
|
$this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPaymentIntent(array $eventJson): void {
|
public function createPaymentIntent(array $eventJson): void
|
||||||
|
{
|
||||||
$config = PluginConfigManager::create()->loadConfig();
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
$stripe = new StripeClient($config['tokenstripe']);
|
$stripe = new StripeClient($config['tokenstripe']);
|
||||||
|
|
||||||
@ -54,6 +56,39 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
$stripeCustomer = $stripe->customers->retrieve($customer);
|
$stripeCustomer = $stripe->customers->retrieve($customer);
|
||||||
$ucrmClientId = $stripeCustomer->metadata->ucrm_client_id ?? null;
|
$ucrmClientId = $stripeCustomer->metadata->ucrm_client_id ?? null;
|
||||||
|
|
||||||
|
// [NEW] Check for existing PENDING PaymentIntent to avoid duplicates
|
||||||
|
// Especially useful if webhook is retried or if one was already created.
|
||||||
|
$existingPIs = $stripe->paymentIntents->search([
|
||||||
|
'query' => "customer:'$customer' AND status:'requires_payment_method' AND amount>=" . ((int)$amount - 100) . " AND amount<=" . ((int)$amount + 100) . " AND metadata['tipoPago']:'Transferencia Bancaria'",
|
||||||
|
'limit' => 1
|
||||||
|
]);
|
||||||
|
// Note: Range check just in case, or exact check. Using exact check is safer if amount is precise.
|
||||||
|
// Let's use exact check for now, but sometimes small variations happen? No, Bank Transfer is exact.
|
||||||
|
// Actually, 'requires_payment_method' or 'requires_action' or 'processing'?
|
||||||
|
// If it's bank transfer funded, it might be in 'requires_confirmation' if not auto-confirmed.
|
||||||
|
// But if we are CREATING it, we want to know if we already created one that is waiting.
|
||||||
|
// If we created it with confirm=true, it should transition to succeeded immediately if funds are available (which they are, cause this event says so).
|
||||||
|
// However, IF the previous attempt failed or timed out but created the PI...
|
||||||
|
|
||||||
|
// BETTER: Check if we have processed this Event ID before?
|
||||||
|
// The method doesn't receive Event ID in the args easily (it's in $eventJson['id']).
|
||||||
|
// But we don't store Event IDs in DB.
|
||||||
|
|
||||||
|
// Let's stick to checking if there is a PI created recently (last 5 mins?) with same amount?
|
||||||
|
// Stripe Search API is powerful.
|
||||||
|
// query: "customer:'$customer' AND amount=$amount AND created>" . (time() - 300)
|
||||||
|
|
||||||
|
$fiveMinsAgo = time() - 300;
|
||||||
|
$existingPIs = $stripe->paymentIntents->search([
|
||||||
|
'query' => "customer:'$customer' AND amount=" . (int)$amount . " AND created>$fiveMinsAgo AND metadata['createdBy']:'UCRM'",
|
||||||
|
'limit' => 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (count($existingPIs->data) > 0) {
|
||||||
|
$this->logger->info("PaymentIntent duplicado evitado. Ya existe uno reciente tras el evento de fondos. ID: " . $existingPIs->data[0]->id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$pi = $stripe->paymentIntents->create([
|
$pi = $stripe->paymentIntents->create([
|
||||||
'amount' => (int)$amount,
|
'amount' => (int)$amount,
|
||||||
'currency' => 'mxn',
|
'currency' => 'mxn',
|
||||||
@ -73,14 +108,15 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
'signedInAdminId' => $config['idPaymentAdminCRM'],
|
'signedInAdminId' => $config['idPaymentAdminCRM'],
|
||||||
'tipoPago' => 'Transferencia Bancaria'
|
'tipoPago' => 'Transferencia Bancaria'
|
||||||
],
|
],
|
||||||
]);
|
], ['idempotency_key' => $eventJson['id'] ?? null]);
|
||||||
$this->logger->info("PaymentIntent creado: " . $pi->id);
|
$this->logger->info("PaymentIntent creado: " . $pi->id);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logger->error("Error creando PaymentIntent: " . $e->getMessage());
|
$this->logger->error("Error creando PaymentIntent: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerPaymentFromWebhook(array $eventJson): void {
|
public function registerPaymentFromWebhook(array $eventJson): void
|
||||||
|
{
|
||||||
$config = PluginConfigManager::create()->loadConfig();
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
$stripe = new StripeClient($config['tokenstripe']);
|
$stripe = new StripeClient($config['tokenstripe']);
|
||||||
$data = $eventJson['data']['object'];
|
$data = $eventJson['data']['object'];
|
||||||
@ -110,7 +146,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerPaymentFromIntent(array $data): void {
|
public function registerPaymentFromIntent(array $data): void
|
||||||
|
{
|
||||||
$piId = $data['id'] ?? null;
|
$piId = $data['id'] ?? null;
|
||||||
if (!$piId) return;
|
if (!$piId) return;
|
||||||
|
|
||||||
@ -135,6 +172,31 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [NEW] Check for duplicate payment (same PI ID)
|
||||||
|
try {
|
||||||
|
$existingPayments = $this->ucrmApi->get('payments', [
|
||||||
|
'clientId' => $clientId,
|
||||||
|
'limit' => 20,
|
||||||
|
'order' => 'createdDate',
|
||||||
|
'direction' => 'DESC'
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($existingPayments as $p) {
|
||||||
|
// Check note for PI ID
|
||||||
|
if (isset($p['note']) && strpos($p['note'], $piId) !== false) {
|
||||||
|
$this->logger->info("Pago duplicado detectado para PI $piId (ID existente: {$p['id']}). Omitiendo creación.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Also check duplicate by transaction ID if applicable
|
||||||
|
if (isset($p['transactionId']) && $p['transactionId'] === $piId) {
|
||||||
|
$this->logger->info("Pago duplicado detectado (Transaction ID) para PI $piId (ID existente: {$p['id']}). Omitiendo creación.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->warning("Falló la verificación de duplicados para PI $piId: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Intentar detectar Payment Method name basado en tipo
|
// Intentar detectar Payment Method name basado en tipo
|
||||||
$type = $data['payment_method_types'][0] ?? 'card';
|
$type = $data['payment_method_types'][0] ?? 'card';
|
||||||
@ -166,7 +228,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findClientIdByStripeCustomer(string $stripeCustomerId): ?int {
|
private function findClientIdByStripeCustomer(string $stripeCustomerId): ?int
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
// Nota: Esto puede ser lento si hay muchos clientes, pero es un fallback.
|
// Nota: Esto puede ser lento si hay muchos clientes, pero es un fallback.
|
||||||
// Idealmente usaríamos $this->ucrmApi->get('clients', ['customAttributeKey' => 'stripeCustomerId', ...]) si existiera ese filtro.
|
// Idealmente usaríamos $this->ucrmApi->get('clients', ['customAttributeKey' => 'stripeCustomerId', ...]) si existiera ese filtro.
|
||||||
@ -195,7 +258,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findPaymentMethodId(string $name): ?int {
|
private function findPaymentMethodId(string $name): ?int
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
$methods = $this->ucrmApi->get('payment-methods');
|
$methods = $this->ucrmApi->get('payment-methods');
|
||||||
foreach ($methods as $m) {
|
foreach ($methods as $m) {
|
||||||
@ -203,11 +267,13 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return $m['id'];
|
return $m['id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(\Exception $e) {}
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createStripeClient(NotificationData $notificationData, string $tagName, bool $generateSpei = true): void {
|
public function createStripeClient(NotificationData $notificationData, string $tagName, bool $generateSpei = true): void
|
||||||
|
{
|
||||||
$clientId = $notificationData->clientId;
|
$clientId = $notificationData->clientId;
|
||||||
if (!$clientId) return;
|
if (!$clientId) return;
|
||||||
|
|
||||||
@ -246,7 +312,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createCustomerStripe(StripeClient $stripe, array $clientCRM, bool $generateSpei): ?\Stripe\Customer {
|
protected function createCustomerStripe(StripeClient $stripe, array $clientCRM, bool $generateSpei): ?\Stripe\Customer
|
||||||
|
{
|
||||||
$clientId = $clientCRM['id'];
|
$clientId = $clientCRM['id'];
|
||||||
|
|
||||||
// Extraer email de contactos (prioridad) o username
|
// Extraer email de contactos (prioridad) o username
|
||||||
@ -324,7 +391,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return $customer;
|
return $customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getVaultCredentialsByClientId($clientId): string {
|
protected function getVaultCredentialsByClientId($clientId): string
|
||||||
|
{
|
||||||
$config = PluginConfigManager::create()->loadConfig();
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
$ipServer = $config['ipserver'] ?? '';
|
$ipServer = $config['ipserver'] ?? '';
|
||||||
|
|
||||||
@ -415,7 +483,9 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
$passVault = $vault['credentials'][0]['password'];
|
$passVault = $vault['credentials'][0]['password'];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) { continue; }
|
} catch (\Exception $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($passVault) {
|
if ($passVault) {
|
||||||
@ -429,7 +499,9 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]]
|
'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]]
|
||||||
]);
|
]);
|
||||||
$passwordValue = $newPass;
|
$passwordValue = $newPass;
|
||||||
} catch (\Exception $e) { $passwordValue = $newPass; }
|
} catch (\Exception $e) {
|
||||||
|
$passwordValue = $newPass;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$passwordValue = "⚠️ Sin antena";
|
$passwordValue = "⚠️ Sin antena";
|
||||||
}
|
}
|
||||||
@ -451,14 +523,14 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
|
|
||||||
$this->syncPasswordWithCrm((int)$clientId, $finalValue);
|
$this->syncPasswordWithCrm((int)$clientId, $finalValue);
|
||||||
return $finalValue;
|
return $finalValue;
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logger->error("Excepción en getVaultCredentialsByClientId (Cliente: $clientId): " . $e->getMessage());
|
$this->logger->error("Excepción en getVaultCredentialsByClientId (Cliente: $clientId): " . $e->getMessage());
|
||||||
return 'Error: ' . $e->getMessage();
|
return 'Error: ' . $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function syncPasswordWithCrm(int $clientId, string $passVault): void {
|
private function syncPasswordWithCrm(int $clientId, string $passVault): void
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
$clientData = $this->ucrmApi->get("clients/$clientId");
|
$clientData = $this->ucrmApi->get("clients/$clientId");
|
||||||
|
|
||||||
@ -484,7 +556,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function generateStrongPassword(int $length = 16): string {
|
protected function generateStrongPassword(int $length = 16): string
|
||||||
|
{
|
||||||
$lower = 'abcdefghijkmnopqrstuvwxyz'; // Eliminamos 'l'
|
$lower = 'abcdefghijkmnopqrstuvwxyz'; // Eliminamos 'l'
|
||||||
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Eliminamos 'I', 'O'
|
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Eliminamos 'I', 'O'
|
||||||
$digits = '23456789'; // Eliminamos '1', '0'
|
$digits = '23456789'; // Eliminamos '1', '0'
|
||||||
@ -515,7 +588,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return implode('', $pwChars);
|
return implode('', $pwChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool {
|
protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool
|
||||||
|
{
|
||||||
if ($attributeId <= 0) {
|
if ($attributeId <= 0) {
|
||||||
$this->logger->error("Intento de patchAttribute con ID inválido ($attributeId) para cliente $clientId");
|
$this->logger->error("Intento de patchAttribute con ID inválido ($attributeId) para cliente $clientId");
|
||||||
return false;
|
return false;
|
||||||
@ -537,7 +611,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveAttributeId(string $key): int {
|
private function resolveAttributeId(string $key): int
|
||||||
|
{
|
||||||
if ($this->systemAttributesCache === null) {
|
if ($this->systemAttributesCache === null) {
|
||||||
try {
|
try {
|
||||||
$this->systemAttributesCache = $this->ucrmApi->get('custom-attributes', ['attributeType' => 'client']);
|
$this->systemAttributesCache = $this->ucrmApi->get('custom-attributes', ['attributeType' => 'client']);
|
||||||
@ -556,13 +631,15 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function comparePasswords(?string $crm, ?string $vault): string {
|
protected function comparePasswords(?string $crm, ?string $vault): string
|
||||||
|
{
|
||||||
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
||||||
if ($vault && strpos($vault, 'Error') !== 0) return $vault;
|
if ($vault && strpos($vault, 'Error') !== 0) return $vault;
|
||||||
return '⚠️ Probar pass conocida.';
|
return '⚠️ Probar pass conocida.';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncStripeCustomerData(int $clientId, string $name, ?string $email): void {
|
public function syncStripeCustomerData(int $clientId, string $name, ?string $email): void
|
||||||
|
{
|
||||||
$config = PluginConfigManager::create()->loadConfig();
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
$stripe = new StripeClient($config['tokenstripe']);
|
$stripe = new StripeClient($config['tokenstripe']);
|
||||||
try {
|
try {
|
||||||
@ -583,7 +660,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function removeTagFromClient(int $clientId, string $tagName): void {
|
protected function removeTagFromClient(int $clientId, string $tagName): void
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
$client = $this->ucrmApi->get("clients/$clientId");
|
$client = $this->ucrmApi->get("clients/$clientId");
|
||||||
$targetTagId = null;
|
$targetTagId = null;
|
||||||
@ -605,7 +683,8 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function validarNumeroTelefono($n): string {
|
protected function validarNumeroTelefono($n): string
|
||||||
|
{
|
||||||
if (!$n) return '';
|
if (!$n) return '';
|
||||||
$n = preg_replace('/\D/', '', (string)$n);
|
$n = preg_replace('/\D/', '', (string)$n);
|
||||||
return (strlen($n) === 10) ? '52' . $n : $n;
|
return (strlen($n) === 10) ? '52' . $n : $n;
|
||||||
@ -665,6 +744,51 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// [NEW] 3.5. Patch Payment Method ID via Microservice
|
||||||
|
$targetMethodId = null;
|
||||||
|
|
||||||
|
if ($metadataTipoPago === 'OXXO') {
|
||||||
|
$targetMethodId = 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb'; // OXXO Pay
|
||||||
|
} elseif ($metadataTipoPago === 'Transferencia Bancaria') {
|
||||||
|
$targetMethodId = '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb'; // Transferencia Bancaria
|
||||||
|
} else {
|
||||||
|
// [NEW] Default to "Credit/Debit Card" if no specific metadata type found
|
||||||
|
$targetMethodId = '93814765-66a1-4c7d-a777-05c18fd6aab3'; // Tarjeta de crédito/débito
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($targetMethodId) {
|
||||||
|
// [NEW] Update Notification Object in Memory so the calling code knows the change
|
||||||
|
if (is_object($notificationObject) && isset($notificationObject->paymentData)) {
|
||||||
|
// Fix for "Indirect modification of overloaded property" error
|
||||||
|
// We must read the array, modify it, and write it back.
|
||||||
|
$pData = $notificationObject->paymentData;
|
||||||
|
if (is_array($pData)) {
|
||||||
|
$pData['methodId'] = $targetMethodId;
|
||||||
|
$notificationObject->paymentData = $pData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check current methodId (reuse 'payment' if available, otherwise fetch)
|
||||||
|
// Note: We fetched 'payment' in Step 3 ONLY if stripeUserId was valid.
|
||||||
|
// Safe to fetch again or reuse specific check.
|
||||||
|
$paymentCheck = $this->ucrmApi->get('payments/' . $paymentId);
|
||||||
|
|
||||||
|
if ($paymentCheck['methodId'] !== $targetMethodId) {
|
||||||
|
$this->logger->info("Payment $paymentId has wrong Method ID ({$paymentCheck['methodId']}). Patching to $targetMethodId via Microservice.");
|
||||||
|
|
||||||
|
$httpClient->patch("$microserviceBaseUrl/payments/$paymentId/method", [
|
||||||
|
'json' => ['methodId' => $targetMethodId],
|
||||||
|
'timeout' => 5
|
||||||
|
]);
|
||||||
|
$this->logger->info("Payment Method ID patched successfully.");
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->logger->error("Failed to patch Payment Method ID via microservice: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Determine Target Attribute Value
|
// 4. Determine Target Attribute Value
|
||||||
// Truth Source Priority: 1. Metadata (DB), 2. Existing Attribute, 3. Method Name (Guess)
|
// Truth Source Priority: 1. Metadata (DB), 2. Existing Attribute, 3. Method Name (Guess)
|
||||||
|
|
||||||
@ -733,7 +857,6 @@ abstract class AbstractStripeOperationsFacade
|
|||||||
} else {
|
} else {
|
||||||
$this->logger->debug("No se pudo determinar el tipoPagoStripe.");
|
$this->logger->debug("No se pudo determinar el tipoPagoStripe.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->logger->error("Error in ensureStripePaymentAttribute: " . $e->getMessage());
|
$this->logger->error("Error in ensureStripePaymentAttribute: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace SmsNotifier\Facade;
|
namespace SmsNotifier\Facade;
|
||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@ -64,9 +65,7 @@ class ClientCallBellAPI
|
|||||||
$this->CallBellAPIToken = $CallBellAPIToken;
|
$this->CallBellAPIToken = $CallBellAPIToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateContact($client_uuid)
|
public function updateContact($client_uuid) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function printPrueba($clientWhatsAppNumber, $notificationData)
|
public function printPrueba($clientWhatsAppNumber, $notificationData)
|
||||||
{
|
{
|
||||||
@ -357,17 +356,43 @@ class ClientCallBellAPI
|
|||||||
$config = PluginConfigManager::create()->loadConfig();
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
$gClient = new Client(['base_uri' => "https://{$this->IPServer}/crm/api/v1.0/", 'verify' => false]);
|
$gClient = new Client(['base_uri' => "https://{$this->IPServer}/crm/api/v1.0/", 'verify' => false]);
|
||||||
$this->ucrmApi = new UcrmApi($gClient, $this->UCRMAPIToken ?? '');
|
$this->ucrmApi = new UcrmApi($gClient, $this->UCRMAPIToken ?? '');
|
||||||
$payments = $this->ucrmApi->get(
|
$payment_id = $notificationData->paymentData['id'];
|
||||||
'payments/',
|
$payment_amount = '$' . $notificationData->paymentData['amount'];
|
||||||
[
|
|
||||||
'clientId' => $notificationData->clientData['id'],
|
|
||||||
'limit' => 1,
|
|
||||||
'direction' => 'DESC',
|
|
||||||
|
|
||||||
]
|
// We already have the payment data in $notificationData, no need to fetch 'payments/' again to get the ID.
|
||||||
);
|
// However, if we need the 'note' field for the overlay which might not be in notificationData (depending on richness),
|
||||||
|
// we should try to use what we have or fetch SPECIFICALLY this payment.
|
||||||
|
|
||||||
|
// Let's verify if 'note' is in paymentData. Usually UCRM webhook payload has it.
|
||||||
|
// But to be safe and consistent with previous logic, if we need 'note', we can fetch THIS payment.
|
||||||
|
|
||||||
|
// $payments = $this->ucrmApi->get('payments/'.$payment_id); // This would be better if we need details.
|
||||||
|
|
||||||
|
// The previous code did:
|
||||||
|
// $payments = $this->ucrmApi->get('payments/', ['clientId' => ..., 'limit' => 1 ...]);
|
||||||
|
// $payment_id = $payments[0]['id']; <-- THIS WAS THE BUG. Always getting latest.
|
||||||
|
|
||||||
|
// Fix: Use the ID passed in notificationData.
|
||||||
|
|
||||||
|
// If we need the NOTE for the Overlay (OXXO/Transfer check later in code), we should ensure we have it.
|
||||||
|
// $notificationData->paymentData usually contains 'note'.
|
||||||
|
$note = $notificationData->paymentData['note'] ?? '';
|
||||||
|
|
||||||
|
// Let's keep $payments array structure if downstream code expects it, OR refactor downstream.
|
||||||
|
// Downstream uses $payments[0]['note'].
|
||||||
|
|
||||||
|
// Let's just mock $payments[0] with our data OR fetch the correct single payment.
|
||||||
|
// Fetching single payment is safer to ensure we have the Note.
|
||||||
|
try {
|
||||||
|
$fetchedPayment = $this->ucrmApi->get('payments/' . $payment_id);
|
||||||
|
$payments = [$fetchedPayment];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Fallback if fetch fails (unlikely if ID is valid)
|
||||||
|
$payments = [$notificationData->paymentData];
|
||||||
|
}
|
||||||
|
|
||||||
|
// $payment_id is already set above.
|
||||||
|
|
||||||
$payment_id = $payments[0]['id'];
|
|
||||||
$payment_amount = '$' . $payments[0]['amount'];
|
$payment_amount = '$' . $payments[0]['amount'];
|
||||||
//$saldo = '$' . $notificationData->clientData['accountBalance'];
|
//$saldo = '$' . $notificationData->clientData['accountBalance'];
|
||||||
$nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']);
|
$nombre_cliente = sprintf("%s %s", $notificationData->clientData['firstName'], $notificationData->clientData['lastName']);
|
||||||
@ -520,7 +545,6 @@ class ClientCallBellAPI
|
|||||||
//$log->appendLog("Archivo JPG temporal eliminado." . PHP_EOL);
|
//$log->appendLog("Archivo JPG temporal eliminado." . PHP_EOL);
|
||||||
}
|
}
|
||||||
$log->appendLog("Archivos temporales (PDF/JPG) eliminados tras subida exitosa." . PHP_EOL);
|
$log->appendLog("Archivos temporales (PDF/JPG) eliminados tras subida exitosa." . PHP_EOL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$log->appendLog("Error: Falló la subida a MinIO." . PHP_EOL);
|
$log->appendLog("Error: Falló la subida a MinIO." . PHP_EOL);
|
||||||
return false;
|
return false;
|
||||||
@ -533,7 +557,6 @@ class ClientCallBellAPI
|
|||||||
$log->appendLog("Error microservicio PDF: HTTP " . $responseMs->getStatusCode() . PHP_EOL);
|
$log->appendLog("Error microservicio PDF: HTTP " . $responseMs->getStatusCode() . PHP_EOL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$log->appendLog("Excepción en flujo Microservicio/MinIO: " . $e->getMessage() . PHP_EOL);
|
$log->appendLog("Excepción en flujo Microservicio/MinIO: " . $e->getMessage() . PHP_EOL);
|
||||||
return false;
|
return false;
|
||||||
@ -1173,7 +1196,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 SE EJECUTA PATCH - No hay cambios que actualizar" . PHP_EOL);
|
$log->appendLog("NO SE EJECUTA PATCH - No hay cambios que actualizar" . PHP_EOL);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|||||||
@ -131,7 +131,6 @@ class Plugin
|
|||||||
if ($jsonData['data']['object']['type'] === 'funded') {
|
if ($jsonData['data']['object']['type'] === 'funded') {
|
||||||
$this->logger->info('Evento de transferencia de un cliente recibido: ' . 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') {
|
if ($jsonData['data']['object']['type'] === 'applied_to_payment') {
|
||||||
@ -143,7 +142,6 @@ class Plugin
|
|||||||
//Se canceló una transferencia de dinero, imprimir que se canceló y además el monto neto
|
//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('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);
|
$this->logger->warning('Monto neto de la transferencia cancelada: ' . $jsonData['data']['object']['net_amount'] . PHP_EOL);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'payout.failed':
|
case 'payout.failed':
|
||||||
@ -261,7 +259,6 @@ class Plugin
|
|||||||
// Terminar proceso hijo
|
// Terminar proceso hijo
|
||||||
exit;
|
exit;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -299,8 +296,8 @@ class Plugin
|
|||||||
$result = json_encode($notification);
|
$result = json_encode($notification);
|
||||||
$this->logger->debug('Notification encodificado en JSON:' . $result . PHP_EOL);
|
$this->logger->debug('Notification encodificado en JSON:' . $result . PHP_EOL);
|
||||||
|
|
||||||
// [NEW] Attempt to patch the payment with correct Stripe attribute if applicable
|
// [MOVED] Attempt to patch method ID only if it comes as "Stripe Credit Card" (catch-all)
|
||||||
$this->pluginNotifierFacade->ensureStripePaymentAttribute($notification);
|
//$this->pluginNotifierFacade->ensureStripePaymentAttribute($notification); (Removed from top)
|
||||||
|
|
||||||
$payment_method_id = $notification->paymentData['methodId'];
|
$payment_method_id = $notification->paymentData['methodId'];
|
||||||
$payment_method = '';
|
$payment_method = '';
|
||||||
@ -337,10 +334,31 @@ class Plugin
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae':
|
case '1dd098fa-5d63-4c8d-88b7-3c27ffbbb6ae':
|
||||||
|
// [NEW] Logic to patch method ID based on metadata
|
||||||
|
$this->pluginNotifierFacade->ensureStripePaymentAttribute($notification);
|
||||||
|
|
||||||
|
// Check if Method ID was updated in memory to OXXO or Transfer
|
||||||
|
$patchedMethodId = $notification->paymentData['methodId'];
|
||||||
|
|
||||||
|
if ($patchedMethodId === 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb') {
|
||||||
|
// It is OXXO Pay
|
||||||
|
$payment_method = 'OXXO Pay';
|
||||||
|
if ($config['oxxoPayPaymentMethodId'] ?? false) {
|
||||||
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
|
}
|
||||||
|
} elseif ($patchedMethodId === '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb') {
|
||||||
|
// It is Bank Transfer
|
||||||
|
$payment_method = 'Transferencia bancaria';
|
||||||
|
if ($config['bankTransferPaymentMethodId'] ?? false) {
|
||||||
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default: Credit Card Stripe
|
||||||
$payment_method = 'Tarjeta de crédito Stripe';
|
$payment_method = 'Tarjeta de crédito Stripe';
|
||||||
if ($config['creditCardStripePaymentMethodId'] ?? false) {
|
if ($config['creditCardStripePaymentMethodId'] ?? false) {
|
||||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290':
|
case 'b9e1e9d1-5c7b-41d2-b6b2-3e568d700290':
|
||||||
$payment_method = 'Suscripción de Stripe (tarjeta de crédito)';
|
$payment_method = 'Suscripción de Stripe (tarjeta de crédito)';
|
||||||
@ -372,11 +390,28 @@ class Plugin
|
|||||||
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb':
|
||||||
|
$payment_method = 'OXXO Pay';
|
||||||
|
if ($config['oxxoPayPaymentMethodId'] ?? false) {
|
||||||
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '4145b5f5-3bbc-45e3-8fc5-9cda970c62fb':
|
||||||
|
$payment_method = 'Transferencia bancaria';
|
||||||
|
if ($config['bankTransferPaymentMethodId'] ?? false) {
|
||||||
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '93814765-66a1-4c7d-a777-05c18fd6aab3':
|
||||||
|
$payment_method = 'Tarjeta de crédito/débito';
|
||||||
|
if ($config['creditDebitCardPaymentMethodId'] ?? false) {
|
||||||
|
$this->notifierFacade->verifyPaymentActionToDo($notification);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$payment_method = 'Desconocido';
|
$payment_method = 'Desconocido';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ($notification->eventName === 'client.edit') {
|
} else if ($notification->eventName === 'client.edit') {
|
||||||
$this->logger->info('Procesando evento client.edit para entityId: ' . ($jsonData['entityId'] ?? 'unknown'));
|
$this->logger->info('Procesando evento client.edit para entityId: ' . ($jsonData['entityId'] ?? 'unknown'));
|
||||||
$this->logger->debug('Payload completo client.edit: ' . json_encode($jsonData));
|
$this->logger->debug('Payload completo client.edit: ' . json_encode($jsonData));
|
||||||
@ -388,7 +423,7 @@ class Plugin
|
|||||||
} else {
|
} else {
|
||||||
$this->logger->info('Llamando a updatePasswordAntenaIfNeeded para cliente: ' . $clientID);
|
$this->logger->info('Llamando a updatePasswordAntenaIfNeeded para cliente: ' . $clientID);
|
||||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded((int)$clientID, $jsonData);
|
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded((int)$clientID, $jsonData);
|
||||||
$this->logger->info('Llamada finalizada exitosamente.');
|
// $this->lcdf4d7cf10e2ogger->info('Llamada finalizada exitosamente.');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->logger->error('ERROR FATAL procesando client.edit: ' . $e->getMessage());
|
$this->logger->error('ERROR FATAL procesando client.edit: ' . $e->getMessage());
|
||||||
@ -473,18 +508,15 @@ class Plugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
$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');
|
$this->logger->debug('Se agregó un nuevo cliente');
|
||||||
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
|
$this->logger->debug('Valor de json_data: ' . json_encode($jsonData));
|
||||||
|
|
||||||
} 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":[]}}}
|
//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":[]}}}
|
||||||
$clientID = $jsonData['extraData']['entity']['clientId'];
|
$clientID = $jsonData['extraData']['entity']['clientId'];
|
||||||
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
|
$this->pluginNotifierFacade->updatePasswordAntenaIfNeeded($clientID, $jsonData);
|
||||||
|
|
||||||
} 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);
|
||||||
@ -532,7 +564,6 @@ class Plugin
|
|||||||
'title' => $title,
|
'title' => $title,
|
||||||
]);
|
]);
|
||||||
$this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch));
|
$this->logger->debug('Respuesta de la API al agregar el trabajo: ' . json_encode($responsePatch));
|
||||||
|
|
||||||
} 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));
|
||||||
@ -589,14 +620,12 @@ class Plugin
|
|||||||
$this->logger->debug('Edición de trabajo "En curso" sin cambios de fecha o técnico relevantes para notificación');
|
$this->logger->debug('Edición de trabajo "En curso" sin cambios de fecha o técnico relevantes para notificación');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->logger->warning('El campo assignedUserId no existe en entityBeforeEdit o entity');
|
$this->logger->warning('El campo assignedUserId no existe en entityBeforeEdit o entity');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$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->update($notification);
|
//$this->notifierFacade->update($notification);
|
||||||
|
|||||||
32
test_microservice_patch.php
Normal file
32
test_microservice_patch.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
$config = PluginConfigManager::create()->loadConfig();
|
||||||
|
|
||||||
|
$ip = $config['ipPuppeteer'] ?? '127.0.0.1'; // Fallback
|
||||||
|
$port = $config['portPuppeteer'] ?? '4100'; // Fallback, docker-compose says 4100 host -> 4000 container
|
||||||
|
$paymentId = 907;
|
||||||
|
$oxxoMethodId = 'b01c0b35-b42c-48d9-9ad9-ea6591adfbbb';
|
||||||
|
|
||||||
|
echo "Testing Microservice Patch on Payment $paymentId to OXXO Pay ($oxxoMethodId)...\n";
|
||||||
|
|
||||||
|
$client = new Client();
|
||||||
|
try {
|
||||||
|
$url = "http://$ip:$port/payments/$paymentId/method";
|
||||||
|
echo "URL: $url\n";
|
||||||
|
|
||||||
|
$response = $client->patch($url, [
|
||||||
|
'json' => ['methodId' => $oxxoMethodId]
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo "Response Code: " . $response->getStatusCode() . "\n";
|
||||||
|
echo "Body: " . $response->getBody()->getContents() . "\n";
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo "Error: " . $e->getMessage() . "\n";
|
||||||
|
if (method_exists($e, 'getResponse') && $e->getResponse()) {
|
||||||
|
echo "Response Error: " . $e->getResponse()->getBody()->getContents() . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user