diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad5ebe..166a773 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.3.0] - 06-01-2026 +### Fixed +- Corrección de la URL base de UCRM (faltaba prefijo `/crm`). +- Mejoras de robustez en el webhook para procesar transacciones de saldo (Cash Balance). +### Added +- Logging detallado del payload de Stripe y respuestas del CRM en `data/plugin.log`. +- Inyección de logger en `PaymentIntentService` para mejor diagnóstico en producción. + ## [1.2.0] - 23-12-2025 ### Added - Resolución dinámica del ID del método de pago ("Transferencia bancaria") para mayor portabilidad entre sistemas UISP. diff --git a/README.md b/README.md index 71b77b4..2209be4 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SIIP - Generador de Payment Intents Stripe -![Version](https://img.shields.io/badge/version-1.1.0-blue.svg) +![Version](https://img.shields.io/badge/version-1.3.0-blue.svg) ![UCRM Compliancy](https://img.shields.io/badge/UCRM-v1.0.0%2B-success.svg) ![PHP](https://img.shields.io/badge/PHP-7.4%2B-777bb4.svg) ![License](https://img.shields.io/badge/license-MIT-green.svg) @@ -33,10 +33,17 @@ Este plugin permite buscar clientes en la base de datos de UISP CRM y generar in 5. Ingrese el monto a cobrar (mínimo $10.00 MXN). 6. Haga clic en "Generar Intención de Pago" y confirme la operación. +## Automatización de Pagos (Webhooks) +Para que los pagos se registren automáticamente en UCRM, debe configurar un Webhook en su Dashboard de Stripe: +1. **Endpoint URL**: `https://SU-DOMINIO/crm/plugin/siip-stripe-payment_intents/public.php?action=webhook` +2. **Eventos a Escuchar**: + - `payment_intent.succeeded` (Para pagos inmediatos con saldo). + - `customer_cash_balance_transaction.created` (Para pagos vía transferencia SPEI). + ## Notas - El plugin valida que el monto sea mayor a $10.00 MXN. - Si el cliente no tiene un "Stripe Customer ID", no se permitirá generar el pago. --- -**Versión**: 1.2.0 +**Versión**: 1.3.0 **Copyright**: © 2024 SIIP Internet. Todos los derechos reservados. diff --git a/manifest.json b/manifest.json index 9ce2143..6febaf6 100755 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "displayName": "SIIP - Generador de Payment Intents Stripe", "description": "Generador manual de Payment Intents Stripe para los clientes de UISP CRM, útil para conciliación de pagos no registrados en UISP CRM", "url": "https://siip.mx", - "version": "1.1.0", + "version": "1.3.0", "ucrmVersionCompliancy": { "min": "1.0.0", "max": null diff --git a/public.php b/public.php index 9f159d7..459ff20 100755 --- a/public.php +++ b/public.php @@ -15,11 +15,11 @@ $configManager = PluginConfigManager::create(); $config = $configManager->loadConfig(); $ipServer = $config['ipServer'] ?? ''; -$baseUrl = 'https://' . $ipServer; +$baseUrl = 'https://' . $ipServer . '/crm'; $apiUcrmKey = $config['apiTokenUcrm'] ?? ''; $stripeApiKey = $config['apiTokenStripe'] ?? ''; -$service = new PaymentIntentService($baseUrl, $apiUcrmKey, $stripeApiKey); +$service = new PaymentIntentService($baseUrl, $apiUcrmKey, $stripeApiKey, $log); // --- ROUTER --- $action = $_GET['action'] ?? 'view'; @@ -111,38 +111,57 @@ if ($action === 'webhook') { } $log->appendLog("Webhook Received: " . $event->type); + $log->appendLog("Webhook Payload (Raw): " . $payload); - if ($event->type == 'payment_intent.succeeded') { - $paymentIntent = $event->data->object; - $clientId = $paymentIntent->metadata->clientId ?? null; - $amount = $paymentIntent->amount / 100; - $currency = $paymentIntent->currency; - - if ($clientId) { - $notes = "Pago procesado via Stripe PaymentIntent: " . $paymentIntent->id; - $res = $service->registerPayment($clientId, $amount, $currency, $notes); - $log->appendLog("Payment Register UCRM ($clientId): " . ($res ? 'Success' : 'Fail')); - } - } - elseif ($event->type == 'customer_cash_balance_transaction.created') { - $txn = $event->data->object; - // Check if money was applied to a payment object - if (isset($txn->applied_to_payment) && isset($txn->applied_to_payment->payment_intent)) { - $piId = $txn->applied_to_payment->payment_intent; - $paymentIntent = $service->getPaymentIntent($piId); + try { + if ($event->type == 'payment_intent.succeeded') { + $paymentIntent = $event->data->object; + $clientId = $paymentIntent->metadata->clientId ?? null; + $amount = $paymentIntent->amount / 100; + $currency = $paymentIntent->currency; - if ($paymentIntent) { - $clientId = $paymentIntent->metadata->clientId ?? null; - $amount = abs($txn->net_amount) / 100; - $currency = $txn->currency; - - if ($clientId) { - $notes = "Pago via Transferencia (Cash Balance) aplicado a Intent: " . $piId; - $res = $service->registerPayment($clientId, $amount, $currency, $notes); - $log->appendLog("Cash Balance Payment Register UCRM ($clientId): " . ($res ? 'Success' : 'Fail')); - } + if ($clientId) { + $notes = "Pago procesado via Stripe PaymentIntent: " . $paymentIntent->id; + $res = $service->registerPayment($clientId, $amount, $currency, $notes); + $log->appendLog("Payment Register UCRM ($clientId) for PI {$paymentIntent->id}: " . ($res ? 'Success' : 'Fail')); + } else { + $log->appendLog("Warning: No clientId found in metadata for PI: " . $paymentIntent->id); + } + } + elseif ($event->type == 'customer_cash_balance_transaction.created') { + $txn = $event->data->object; + $log->appendLog("Processing Cash Balance Transaction: " . $txn->id); + + // Check if money was applied to a payment object + if (isset($txn->applied_to_payment) && isset($txn->applied_to_payment->payment_intent)) { + $piId = $txn->applied_to_payment->payment_intent; + $log->appendLog("Transaction applied to PI: " . $piId); + + $paymentIntent = $service->getPaymentIntent($piId); + + if ($paymentIntent) { + $clientId = $paymentIntent->metadata->clientId ?? null; + // In cash balance txn, net_amount is what was received (negative for withdrawals, positive for deposits/applications) + // Usually it's negative when applied to a PI, so we take abs + $amount = abs($txn->net_amount) / 100; + $currency = $txn->currency; + + if ($clientId) { + $notes = "Pago via Transferencia (Cash Balance) aplicado a Intent: " . $piId . " (Txn: " . $txn->id . ")"; + $res = $service->registerPayment($clientId, $amount, $currency, $notes); + $log->appendLog("Cash Balance Payment Register UCRM ($clientId) for PI $piId: " . ($res ? 'Success' : 'Fail')); + } else { + $log->appendLog("Error: No clientId found in metadata for PI: $piId"); + } + } else { + $log->appendLog("Error: Could not retrieve PaymentIntent $piId from Stripe"); + } + } else { + $log->appendLog("Info: Cash Balance Transaction " . $txn->id . " not directly applied to a PaymentIntent yet."); } } + } catch (\Exception $e) { + $log->appendLog("CRITICAL ERROR in Webhook Exception: " . $e->getMessage()); } http_response_code(200); diff --git a/src/PaymentIntentService.php b/src/PaymentIntentService.php index e02f741..d629c2d 100755 --- a/src/PaymentIntentService.php +++ b/src/PaymentIntentService.php @@ -13,12 +13,14 @@ class PaymentIntentService private $stripeApiKey; private $httpClient; private $stripeClient; + private $logger; - public function __construct($ucrmApiUrl, $ucrmApiKey, $stripeApiKey) + public function __construct($ucrmApiUrl, $ucrmApiKey, $stripeApiKey, $logger = null) { $this->ucrmApiUrl = rtrim($ucrmApiUrl, '/'); $this->ucrmApiKey = $ucrmApiKey; $this->stripeApiKey = $stripeApiKey; + $this->logger = $logger; $this->httpClient = new Client([ 'base_uri' => $this->ucrmApiUrl . '/api/v1.0/', @@ -26,7 +28,7 @@ class PaymentIntentService 'X-Auth-App-Key' => $this->ucrmApiKey, 'Accept' => 'application/json', ], - 'verify' => false, // Initial script had verify false, keeping it but risky in prod without caution + 'verify' => false, 'timeout' => 10, ]); @@ -182,11 +184,11 @@ class PaymentIntentService try { $methodId = $this->getPaymentMethodIdByName('Transferencia bancaria'); if (!$methodId) { - error_log("Payment Registration Error: Could not find payment method 'Transferencia bancaria'"); + $this->log("Payment Registration Error: Could not find payment method 'Transferencia bancaria'"); return false; } - $this->httpClient->post('payments', [ + $response = $this->httpClient->post('payments', [ 'json' => [ 'clientId' => (int)$clientId, 'amount' => $amount, @@ -196,13 +198,25 @@ class PaymentIntentService 'createdDate' => date('c'), ] ]); + + $this->log("UCRM Payment Registration Response: " . $response->getStatusCode() . " - " . $response->getBody()); + return true; } catch (\Exception $e) { - error_log("Payment Registration Error: " . $e->getMessage()); + $this->log("Payment Registration Error: " . $e->getMessage()); return false; } } + private function log($message) + { + if ($this->logger) { + $this->logger->appendLog($message); + } else { + error_log($message); + } + } + private function getPaymentMethodIdByName($name) { try { diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 358afa5..9928f1e 100755 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'c8061c49feee889369f3892945eeba3269987143', + 'reference' => '7e4a5350383f1e7179ecb80a2036c1fd1b890256', 'name' => '__root__', 'dev' => false, ), @@ -16,7 +16,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'c8061c49feee889369f3892945eeba3269987143', + 'reference' => '7e4a5350383f1e7179ecb80a2036c1fd1b890256', 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array(