fix: Corrección de URL de API y mejoras de log en webhook (v1.3.0)
Descripción: - Se corrigió la URL base de la API de UCRM (se añadió el prefijo /crm). - Se mejoró el procesamiento de transacciones de saldo (Cash Balance) en el webhook. - Se habilitó el logging detallado de Stripe y UCRM en data/plugin.log para mejor diagnóstico.
This commit is contained in:
parent
7e4a535038
commit
070fb757d2
@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# 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
|
## [1.2.0] - 23-12-2025
|
||||||
### Added
|
### Added
|
||||||
- Resolución dinámica del ID del método de pago ("Transferencia bancaria") para mayor portabilidad entre sistemas UISP.
|
- Resolución dinámica del ID del método de pago ("Transferencia bancaria") para mayor portabilidad entre sistemas UISP.
|
||||||
|
|||||||
11
README.md
11
README.md
@ -1,6 +1,6 @@
|
|||||||
# SIIP - Generador de Payment Intents Stripe
|
# SIIP - Generador de Payment Intents Stripe
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
@ -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).
|
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.
|
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
|
## Notas
|
||||||
- El plugin valida que el monto sea mayor a $10.00 MXN.
|
- 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.
|
- 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.
|
**Copyright**: © 2024 SIIP Internet. Todos los derechos reservados.
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
"displayName": "SIIP - Generador de Payment Intents Stripe",
|
"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",
|
"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",
|
"url": "https://siip.mx",
|
||||||
"version": "1.1.0",
|
"version": "1.3.0",
|
||||||
"ucrmVersionCompliancy": {
|
"ucrmVersionCompliancy": {
|
||||||
"min": "1.0.0",
|
"min": "1.0.0",
|
||||||
"max": null
|
"max": null
|
||||||
|
|||||||
79
public.php
79
public.php
@ -15,11 +15,11 @@ $configManager = PluginConfigManager::create();
|
|||||||
$config = $configManager->loadConfig();
|
$config = $configManager->loadConfig();
|
||||||
|
|
||||||
$ipServer = $config['ipServer'] ?? '';
|
$ipServer = $config['ipServer'] ?? '';
|
||||||
$baseUrl = 'https://' . $ipServer;
|
$baseUrl = 'https://' . $ipServer . '/crm';
|
||||||
$apiUcrmKey = $config['apiTokenUcrm'] ?? '';
|
$apiUcrmKey = $config['apiTokenUcrm'] ?? '';
|
||||||
$stripeApiKey = $config['apiTokenStripe'] ?? '';
|
$stripeApiKey = $config['apiTokenStripe'] ?? '';
|
||||||
|
|
||||||
$service = new PaymentIntentService($baseUrl, $apiUcrmKey, $stripeApiKey);
|
$service = new PaymentIntentService($baseUrl, $apiUcrmKey, $stripeApiKey, $log);
|
||||||
|
|
||||||
// --- ROUTER ---
|
// --- ROUTER ---
|
||||||
$action = $_GET['action'] ?? 'view';
|
$action = $_GET['action'] ?? 'view';
|
||||||
@ -111,38 +111,57 @@ if ($action === 'webhook') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$log->appendLog("Webhook Received: " . $event->type);
|
$log->appendLog("Webhook Received: " . $event->type);
|
||||||
|
$log->appendLog("Webhook Payload (Raw): " . $payload);
|
||||||
|
|
||||||
if ($event->type == 'payment_intent.succeeded') {
|
try {
|
||||||
$paymentIntent = $event->data->object;
|
if ($event->type == 'payment_intent.succeeded') {
|
||||||
$clientId = $paymentIntent->metadata->clientId ?? null;
|
$paymentIntent = $event->data->object;
|
||||||
$amount = $paymentIntent->amount / 100;
|
$clientId = $paymentIntent->metadata->clientId ?? null;
|
||||||
$currency = $paymentIntent->currency;
|
$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);
|
|
||||||
|
|
||||||
if ($paymentIntent) {
|
if ($clientId) {
|
||||||
$clientId = $paymentIntent->metadata->clientId ?? null;
|
$notes = "Pago procesado via Stripe PaymentIntent: " . $paymentIntent->id;
|
||||||
$amount = abs($txn->net_amount) / 100;
|
$res = $service->registerPayment($clientId, $amount, $currency, $notes);
|
||||||
$currency = $txn->currency;
|
$log->appendLog("Payment Register UCRM ($clientId) for PI {$paymentIntent->id}: " . ($res ? 'Success' : 'Fail'));
|
||||||
|
} else {
|
||||||
if ($clientId) {
|
$log->appendLog("Warning: No clientId found in metadata for PI: " . $paymentIntent->id);
|
||||||
$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'));
|
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);
|
http_response_code(200);
|
||||||
|
|||||||
@ -13,12 +13,14 @@ class PaymentIntentService
|
|||||||
private $stripeApiKey;
|
private $stripeApiKey;
|
||||||
private $httpClient;
|
private $httpClient;
|
||||||
private $stripeClient;
|
private $stripeClient;
|
||||||
|
private $logger;
|
||||||
|
|
||||||
public function __construct($ucrmApiUrl, $ucrmApiKey, $stripeApiKey)
|
public function __construct($ucrmApiUrl, $ucrmApiKey, $stripeApiKey, $logger = null)
|
||||||
{
|
{
|
||||||
$this->ucrmApiUrl = rtrim($ucrmApiUrl, '/');
|
$this->ucrmApiUrl = rtrim($ucrmApiUrl, '/');
|
||||||
$this->ucrmApiKey = $ucrmApiKey;
|
$this->ucrmApiKey = $ucrmApiKey;
|
||||||
$this->stripeApiKey = $stripeApiKey;
|
$this->stripeApiKey = $stripeApiKey;
|
||||||
|
$this->logger = $logger;
|
||||||
|
|
||||||
$this->httpClient = new Client([
|
$this->httpClient = new Client([
|
||||||
'base_uri' => $this->ucrmApiUrl . '/api/v1.0/',
|
'base_uri' => $this->ucrmApiUrl . '/api/v1.0/',
|
||||||
@ -26,7 +28,7 @@ class PaymentIntentService
|
|||||||
'X-Auth-App-Key' => $this->ucrmApiKey,
|
'X-Auth-App-Key' => $this->ucrmApiKey,
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
],
|
],
|
||||||
'verify' => false, // Initial script had verify false, keeping it but risky in prod without caution
|
'verify' => false,
|
||||||
'timeout' => 10,
|
'timeout' => 10,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -182,11 +184,11 @@ class PaymentIntentService
|
|||||||
try {
|
try {
|
||||||
$methodId = $this->getPaymentMethodIdByName('Transferencia bancaria');
|
$methodId = $this->getPaymentMethodIdByName('Transferencia bancaria');
|
||||||
if (!$methodId) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->httpClient->post('payments', [
|
$response = $this->httpClient->post('payments', [
|
||||||
'json' => [
|
'json' => [
|
||||||
'clientId' => (int)$clientId,
|
'clientId' => (int)$clientId,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
@ -196,13 +198,25 @@ class PaymentIntentService
|
|||||||
'createdDate' => date('c'),
|
'createdDate' => date('c'),
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->log("UCRM Payment Registration Response: " . $response->getStatusCode() . " - " . $response->getBody());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
error_log("Payment Registration Error: " . $e->getMessage());
|
$this->log("Payment Registration Error: " . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function log($message)
|
||||||
|
{
|
||||||
|
if ($this->logger) {
|
||||||
|
$this->logger->appendLog($message);
|
||||||
|
} else {
|
||||||
|
error_log($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getPaymentMethodIdByName($name)
|
private function getPaymentMethodIdByName($name)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
4
vendor/composer/installed.php
vendored
4
vendor/composer/installed.php
vendored
@ -5,7 +5,7 @@
|
|||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c8061c49feee889369f3892945eeba3269987143',
|
'reference' => '7e4a5350383f1e7179ecb80a2036c1fd1b890256',
|
||||||
'name' => '__root__',
|
'name' => '__root__',
|
||||||
'dev' => false,
|
'dev' => false,
|
||||||
),
|
),
|
||||||
@ -16,7 +16,7 @@
|
|||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c8061c49feee889369f3892945eeba3269987143',
|
'reference' => '7e4a5350383f1e7179ecb80a2036c1fd1b890256',
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/guzzle' => array(
|
'guzzlehttp/guzzle' => array(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user