saldo stripe conseguido
This commit is contained in:
parent
28b4324406
commit
aa42284cc5
3238
data/plugin.log
3238
data/plugin.log
File diff suppressed because it is too large
Load Diff
BIN
img/webp/account-balance.webp
Normal file
BIN
img/webp/account-balance.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
img/webp/delete.webp
Executable file
BIN
img/webp/delete.webp
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
img/webp/edit.webp
Normal file
BIN
img/webp/edit.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
286
public.php
286
public.php
@ -157,6 +157,30 @@ if (isset($_GET['action'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($_GET['action'] === 'get_stripe_history') {
|
||||||
|
if (ob_get_level()) ob_end_clean();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$stripeCustomerId = $_GET['stripeCustomerId'] ?? '';
|
||||||
|
if (!$stripeCustomerId) {
|
||||||
|
echo json_encode(['error' => 'Missing stripeCustomerId']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$history = $stripeService->getLastPayments($stripeCustomerId);
|
||||||
|
$balance = $stripeService->getCustomerCashBalance($stripeCustomerId);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'history' => $history,
|
||||||
|
'cashBalance' => $balance
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($_GET['action'] === 'search_stripe') {
|
if ($_GET['action'] === 'search_stripe') {
|
||||||
$q = $_GET['q'] ?? '';
|
$q = $_GET['q'] ?? '';
|
||||||
echo json_encode($stripeService->searchClients($q));
|
echo json_encode($stripeService->searchClients($q));
|
||||||
@ -172,7 +196,7 @@ if (isset($_GET['action'])) {
|
|||||||
// Image Handler
|
// Image Handler
|
||||||
if (ob_get_level()) ob_end_clean();
|
if (ob_get_level()) ob_end_clean();
|
||||||
$filename = basename($_GET['file'] ?? $_GET['name'] ?? '');
|
$filename = basename($_GET['file'] ?? $_GET['name'] ?? '');
|
||||||
$paths = [__DIR__ . '/img/' . $filename, __DIR__ . '/vouchers_oxxo/' . $filename];
|
$paths = [__DIR__ . '/img/' . $filename, __DIR__ . '/img/webp/' . $filename, __DIR__ . '/vouchers_oxxo/' . $filename];
|
||||||
$finalPath = null;
|
$finalPath = null;
|
||||||
foreach ($paths as $p) {
|
foreach ($paths as $p) {
|
||||||
if (file_exists($p)) {
|
if (file_exists($p)) {
|
||||||
@ -622,6 +646,58 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
z-index: 4000;
|
z-index: 4000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UNIFORM BUTTONS */
|
||||||
|
.btn-uniform {
|
||||||
|
background-color: var(--primary) !important;
|
||||||
|
color: white !important;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-uniform:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ICONS */
|
||||||
|
.icon-btn {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-crm {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-action {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-action:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-header-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
object-fit: contain;
|
||||||
|
margin-right: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
#toast.show {
|
#toast.show {
|
||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
}
|
}
|
||||||
@ -894,9 +970,13 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
|
|
||||||
<div id="paymentsContainer" class="card" style="display: none;">
|
<div id="paymentsContainer" class="card" style="display: none;">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 10px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 10px;">
|
||||||
<h3 id="selectedClientName" style="margin: 0;">Pagos del Cliente</h3>
|
<h3 style="margin: 0; display: flex; align-items: center;">
|
||||||
|
<img src="?action=image&file=client.webp" class="client-header-icon"> <span id="selectedClientName">Pagos del Cliente</span>
|
||||||
|
</h3>
|
||||||
<div style="display: flex; gap: 8px;">
|
<div style="display: flex; gap: 8px;">
|
||||||
<a id="btnNotifCrm" href="#" target="_blank" class="btn btn-secondary" style="height: 38px;">Ver en CRM</a>
|
<a id="btnNotifCrm" href="#" target="_blank" class="btn btn-uniform" style="height: 38px;">
|
||||||
|
<img src="?action=image&file=crm.webp" class="icon-crm"> Ver en CRM
|
||||||
|
</a>
|
||||||
<button class="btn btn-secondary" onclick="refreshClientPayments()" style="height: 38px;">
|
<button class="btn btn-secondary" onclick="refreshClientPayments()" style="height: 38px;">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M23 4v6h-6" />
|
<path d="M23 4v6h-6" />
|
||||||
@ -948,8 +1028,12 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
<!-- HEADER: CLIENT INFO & BALANCE -->
|
<!-- HEADER: CLIENT INFO & BALANCE -->
|
||||||
<div class="client-header-grid">
|
<div class="client-header-grid">
|
||||||
<div class="client-info">
|
<div class="client-info">
|
||||||
<h3 id="stripeClientName">Nombre del Cliente</h3>
|
<h3>
|
||||||
<p id="stripeClientIdDisplay" style="color: var(--text-muted);">ID: #0</p>
|
<img src="?action=image&file=client.webp" class="client-header-icon"> <span id="stripeClientName">Nombre del Cliente</span>
|
||||||
|
</h3>
|
||||||
|
<p style="color: var(--text-muted); margin: 0;">
|
||||||
|
<span id="stripeClientIdDisplay" style="margin-right: 15px;">ID: #0</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="client-balance">
|
<div class="client-balance">
|
||||||
<span class="label">Saldo Actual</span>
|
<span class="label">Saldo Actual</span>
|
||||||
@ -993,12 +1077,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions-row">
|
<div class="actions-row">
|
||||||
<a id="btnVerEnCrm" href="#" target="_blank" class="btn btn-secondary btn-lg">
|
<a id="btnVerEnCrm" href="#" target="_blank" class="btn btn-uniform btn-lg">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<img src="?action=image&file=crm.webp" class="icon-crm">
|
||||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
|
||||||
<polyline points="15 3 21 3 21 9"></polyline>
|
|
||||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
|
||||||
</svg>
|
|
||||||
Ver en CRM
|
Ver en CRM
|
||||||
</a>
|
</a>
|
||||||
<button id="btnCreateIntent" class="btn btn-primary btn-lg">
|
<button id="btnCreateIntent" class="btn btn-primary btn-lg">
|
||||||
@ -1011,37 +1091,67 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- MODULE 4: OXXO -->
|
<div id="stripeHistoryContainer" style="display:none; margin-top: 2rem; border-top: 1px solid var(--border); padding-top: 1.5rem;">
|
||||||
<section id="section-pagos-oxxo" class="section-view">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
||||||
<div class="card">
|
<h4 class="section-title" style="margin-bottom: 0;">Historial de Pagos (Últimos 10)</h4>
|
||||||
<div style="margin-bottom: 1.5rem;">
|
<div id="stripeCashBalanceBadge" style="display:none; align-items: center; background-color: #f1f5f9; color: var(--text-main); padding: 5px 12px; border-radius: 8px; font-size: 0.9em; font-weight: 600; border: 1px solid transparent;">
|
||||||
<h2 style="margin: 0; display: flex; align-items: center; gap: 10px;">
|
<img src="?action=image&file=account-balance.webp" style="width: 32px; height: 32px; margin-right: 8px;">
|
||||||
<img src="?action=get_image&name=oxxo-payments.png" style="width:64px;height:64px;"> Pagos OXXO
|
<span id="stripeCashBalanceText">Saldo Stripe: $0.00 MXN</span>
|
||||||
</h2>
|
</div>
|
||||||
<p style="color: var(--text-muted); margin: 5px 0 0 0;">🏪 Genera fichas de pago OXXO para que tus clientes paguen en tiendas de conveniencia</p>
|
|
||||||
</div>
|
</div>
|
||||||
<label>Buscar Cliente para OXXO</label>
|
<div style="overflow-x: auto;">
|
||||||
<div class="search-wrapper">
|
<table class="table" id="stripeHistoryTable" style="width: 100%; border-collapse: collapse;">
|
||||||
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<thead>
|
||||||
<circle cx="11" cy="11" r="8" />
|
<tr style="text-align: left; background: var(--bg-body);">
|
||||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
<th style="padding: 10px;">Estado</th>
|
||||||
</svg>
|
<th style="padding: 10px;">Importe</th>
|
||||||
<input type="text" id="oxxoSearch" class="form-control search-input-padded" placeholder="Buscar cliente..." autocomplete="off">
|
<th style="padding: 10px;">Descripción</th>
|
||||||
<div id="oxxoResults" class="search-results"></div>
|
<th style="padding: 10px;">Fecha</th>
|
||||||
|
<th style="padding: 10px;">ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div id="oxxoDetailContainer" class="card" style="display: none;">
|
<!-- MODULE 4: OXXO -->
|
||||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1.5rem;">
|
<section id="section-pagos-oxxo" class="section-view">
|
||||||
|
<div class="card">
|
||||||
|
<div style="margin-bottom: 1.5rem;">
|
||||||
|
<h2 style="margin: 0; display: flex; align-items: center; gap: 10px;">
|
||||||
|
<img src="?action=get_image&name=oxxo-payments.png" style="width:64px;height:64px;"> Pagos OXXO
|
||||||
|
</h2>
|
||||||
|
<p style="color: var(--text-muted); margin: 5px 0 0 0;">🏪 Genera fichas de pago OXXO para que tus clientes paguen en tiendas de conveniencia</p>
|
||||||
|
</div>
|
||||||
|
<label>Buscar Cliente para OXXO</label>
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||||
|
</svg>
|
||||||
|
<input type="text" id="oxxoSearch" class="form-control search-input-padded" placeholder="Buscar cliente..." autocomplete="off">
|
||||||
|
<div id="oxxoResults" class="search-results"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="oxxoDetailContainer" class="card" style="display: none;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1.5rem;">
|
||||||
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h3 id="oxxoClientName">Nombre</h3>
|
<h3>
|
||||||
|
<img src="?action=image&file=client.webp" class="client-header-icon"> <span id="oxxoClientName">Nombre</span>
|
||||||
|
</h3>
|
||||||
<p id="oxxoClientIdDisplay" style="color: var(--text-muted);">ID: #0</p>
|
<p id="oxxoClientIdDisplay" style="color: var(--text-muted);">ID: #0</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;">
|
<div style="text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;">
|
||||||
<span class="badge" id="oxxoBalanceBadge" style="background: #fee2e2; color: #ef4444;">Saldo: $0.00</span>
|
<span class="badge" id="oxxoBalanceBadge" style="background: #fee2e2; color: #ef4444;">Saldo: $0.00</span>
|
||||||
<a id="btnOxxoCrm" href="#" target="_blank" class="btn btn-secondary" style="padding: 5px 12px; font-size: 0.8rem;">Ver en CRM</a>
|
<a id="btnOxxoCrm" href="#" target="_blank" class="btn btn-uniform" style="padding: 5px 12px; font-size: 0.8rem;">
|
||||||
|
<img src="?action=image&file=crm.webp" class="icon-crm"> Ver en CRM
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1053,8 +1163,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
</div>
|
</div>
|
||||||
<div id="oxxoInlineResult" style="margin-top: 1.5rem; display: none;"></div>
|
<div id="oxxoInlineResult" style="margin-top: 1.5rem; display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FOOTER -->
|
<!-- FOOTER -->
|
||||||
@ -1252,6 +1362,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
<div class="status-dot ${isSuspended ? 'suspended' : 'active'}"></div>
|
<div class="status-dot ${isSuspended ? 'suspended' : 'active'}"></div>
|
||||||
<div class="client-info">
|
<div class="client-info">
|
||||||
<div class="client-header">
|
<div class="client-header">
|
||||||
|
<div class="client-header">
|
||||||
|
<img src="?action=image&file=client.webp" style="width:24px;height:24px;margin-right:5px;vertical-align:middle;">
|
||||||
<span class="client-name">${highlightMatch(name, query)}</span>
|
<span class="client-name">${highlightMatch(name, query)}</span>
|
||||||
${isSuspended ? '<span class="badge-suspended">SUSPENDIDO</span>' : ''}
|
${isSuspended ? '<span class="badge-suspended">SUSPENDIDO</span>' : ''}
|
||||||
</div>
|
</div>
|
||||||
@ -1339,7 +1451,12 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
<td>${new Date(p.createdDate).toLocaleString()}</td>
|
<td>${new Date(p.createdDate).toLocaleString()}</td>
|
||||||
<td>$${p.amount} ${p.currencyCode}</td>
|
<td>$${p.amount} ${p.currencyCode}</td>
|
||||||
<td>${p.methodName}</td>
|
<td>${p.methodName}</td>
|
||||||
<td><button class="btn btn-whatsapp" onclick="resendPayment(${p.id})">Re-enviar</button></td>
|
<td>${p.methodName}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-whatsapp" onclick="resendPayment(${p.id})">
|
||||||
|
<img src="?action=image&file=whatsapp-logo-button.png" class="icon-btn" style="margin-right:5px;filter: brightness(0) invert(1);"> Re-enviar Notificación
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
@ -1381,6 +1498,8 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
document.getElementById('stripeAmount').value = data.accountOutstanding > 0 ? data.accountOutstanding : '';
|
document.getElementById('stripeAmount').value = data.accountOutstanding > 0 ? data.accountOutstanding : '';
|
||||||
document.getElementById('btnVerEnCrm').href = `${store.publicUrl}/client/${data.id}`;
|
document.getElementById('btnVerEnCrm').href = `${store.publicUrl}/client/${data.id}`;
|
||||||
document.getElementById('stripeDetailContainer').style.display = 'block';
|
document.getElementById('stripeDetailContainer').style.display = 'block';
|
||||||
|
if (data.stripeCustomerId) loadStripeHistory(data.stripeCustomerId);
|
||||||
|
else document.getElementById('stripeHistoryContainer').style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('btnCreateIntent').onclick = async () => {
|
document.getElementById('btnCreateIntent').onclick = async () => {
|
||||||
@ -1476,7 +1595,15 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
// --- INSTALLER MODAL LOGIC ---
|
// --- INSTALLER MODAL LOGIC ---
|
||||||
function renderTable() {
|
function renderTable() {
|
||||||
const tbody = document.querySelector('#installersTable tbody');
|
const tbody = document.querySelector('#installersTable tbody');
|
||||||
tbody.innerHTML = store.installers.map((inst, i) => `<tr><td>#${inst.id}</td><td>${inst.nombre}</td><td>${inst.whatsapp}</td><td><button class="btn btn-secondary" onclick="editInstaller(${i})">✎</button> <button class="btn btn-secondary" style="color:red" onclick="deleteInstaller(${i})">🗑</button></td></tr>`).join('');
|
tbody.innerHTML = store.installers.map((inst, i) => `<tr>
|
||||||
|
<td>#${inst.id}</td>
|
||||||
|
<td>${inst.nombre}</td>
|
||||||
|
<td>${inst.whatsapp}</td>
|
||||||
|
<td>
|
||||||
|
<img src="?action=image&file=edit.webp" class="icon-action" onclick="editInstaller(${i})" title="Editar">
|
||||||
|
<img src="?action=image&file=delete.webp" class="icon-action" style="margin-left:10px" onclick="deleteInstaller(${i})" title="Borrar">
|
||||||
|
</td>
|
||||||
|
</tr>`).join('');
|
||||||
}
|
}
|
||||||
renderTable();
|
renderTable();
|
||||||
|
|
||||||
@ -1519,6 +1646,93 @@ $installersData = json_decode($config['installersDataWhatsApp'] ?? '{"instalador
|
|||||||
renderTable();
|
renderTable();
|
||||||
document.getElementById('modalOverlay').style.display = 'none';
|
document.getElementById('modalOverlay').style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function loadStripeHistory(customerId) {
|
||||||
|
const container = document.getElementById('stripeHistoryContainer');
|
||||||
|
const tbody = document.querySelector('#stripeHistoryTable tbody');
|
||||||
|
const balanceBadge = document.getElementById('stripeCashBalanceBadge');
|
||||||
|
|
||||||
|
container.style.display = 'block';
|
||||||
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center; padding: 20px; color: var(--text-muted);">Cargando historial...</td></tr>';
|
||||||
|
balanceBadge.style.display = 'none';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`?action=get_stripe_history&stripeCustomerId=${customerId}`);
|
||||||
|
const dataRaw = await response.json();
|
||||||
|
|
||||||
|
// Compatibility handling (array vs object)
|
||||||
|
const data = Array.isArray(dataRaw) ? dataRaw : (dataRaw.history || []);
|
||||||
|
const cashBalance = (dataRaw.cashBalance !== undefined && dataRaw.cashBalance !== null) ? parseFloat(dataRaw.cashBalance) : null;
|
||||||
|
|
||||||
|
// Display Cash Balance
|
||||||
|
if (cashBalance !== null) {
|
||||||
|
balanceBadge.style.display = 'inline-flex';
|
||||||
|
const balanceText = document.getElementById('stripeCashBalanceText');
|
||||||
|
if (balanceText) {
|
||||||
|
balanceText.textContent = 'Saldo Stripe: $' + cashBalance.toFixed(2) + ' MXN';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cashBalance > 0) {
|
||||||
|
balanceBadge.style.backgroundColor = '#dcfce7'; // Green
|
||||||
|
balanceBadge.style.color = '#15803d';
|
||||||
|
balanceBadge.style.border = '1px solid #bbf7d0';
|
||||||
|
} else {
|
||||||
|
balanceBadge.style.backgroundColor = '#f1f5f9'; // Gray
|
||||||
|
balanceBadge.style.color = 'var(--text-main)';
|
||||||
|
balanceBadge.style.border = '1px solid transparent';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataRaw.error) {
|
||||||
|
tbody.innerHTML = `<tr><td colspan="5" style="color:var(--danger); text-align:center; padding: 10px;">Error: ${dataRaw.error}</td></tr>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center; padding: 20px; color: var(--text-muted);">No hay pagos recientes.</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.innerHTML = data.map(payment => {
|
||||||
|
const date = new Date(payment.created * 1000).toLocaleString();
|
||||||
|
const statusBadge = getStatusBadge(payment.status);
|
||||||
|
return `
|
||||||
|
<tr style="border-bottom: 1px solid var(--border);">
|
||||||
|
<td style="padding: 10px;">${statusBadge}</td>
|
||||||
|
<td style="padding: 10px; font-weight: 500;">$${payment.amount.toFixed(2)} ${payment.currency}</td>
|
||||||
|
<td style="padding: 10px; color: var(--text-muted);">${payment.description}</td>
|
||||||
|
<td style="padding: 10px; color: var(--text-muted); font-size: 0.9em;">${date}</td>
|
||||||
|
<td style="padding: 10px;"><small style="color: var(--text-muted); font-family: monospace;">${payment.id}</small></td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
tbody.innerHTML = `<tr><td colspan="5" style="color:var(--danger); text-align:center; padding: 10px;">Error de conexión</td></tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusBadge(status) {
|
||||||
|
let color = 'var(--text-muted)';
|
||||||
|
let bg = '#f1f5f9';
|
||||||
|
let label = status;
|
||||||
|
|
||||||
|
if (status === 'succeeded') {
|
||||||
|
color = '#15803d';
|
||||||
|
bg = '#dcfce7';
|
||||||
|
label = 'Exitoso';
|
||||||
|
} else if (status === 'requires_payment_method' || status === 'requires_action') {
|
||||||
|
color = '#b45309';
|
||||||
|
bg = '#fef3c7';
|
||||||
|
label = 'Pendiente';
|
||||||
|
} else if (status === 'canceled') {
|
||||||
|
color = '#b91c1c';
|
||||||
|
bg = '#fee2e2';
|
||||||
|
label = 'Cancelado';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<span style="background-color: ${bg}; color: ${color}; padding: 4px 10px; border-radius: 9999px; font-size: 0.75rem; font-weight: 600; text-transform: capitalize;">${label}</span>`;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@ -109,6 +109,9 @@ class Plugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
$userInput = file_get_contents('php://input');
|
$userInput = file_get_contents('php://input');
|
||||||
|
|
||||||
|
// DEBUG TEMPORAL
|
||||||
|
|
||||||
$this->logger->debug('Payload recibido: ' . $userInput . PHP_EOL);
|
$this->logger->debug('Payload recibido: ' . $userInput . PHP_EOL);
|
||||||
|
|
||||||
if (! $userInput) {
|
if (! $userInput) {
|
||||||
|
|||||||
@ -141,6 +141,56 @@ class PaymentIntentService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLastPayments($stripeCustomerId, $limit = 10)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$collection = $this->stripeClient->paymentIntents->all([
|
||||||
|
'customer' => $stripeCustomerId,
|
||||||
|
'limit' => $limit,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($collection->data as $payment) {
|
||||||
|
$description = $payment->description ?? $payment->metadata['description'] ?? 'Pago Stripe';
|
||||||
|
$result[] = [
|
||||||
|
'id' => $payment->id,
|
||||||
|
'amount' => $payment->amount / 100,
|
||||||
|
'currency' => strtoupper($payment->currency),
|
||||||
|
'status' => $payment->status,
|
||||||
|
'created' => $payment->created,
|
||||||
|
'description' => $description
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->log("Error fetching last payments: " . $e->getMessage());
|
||||||
|
return ['error' => $e->getMessage()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomerCashBalance($stripeCustomerId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$balanceObj = $this->stripeClient->customers->retrieveCashBalance(
|
||||||
|
$stripeCustomerId,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
$amount = $balanceObj->available['mxn'] ?? 0;
|
||||||
|
return $amount / 100;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Fallback or old api version check
|
||||||
|
try {
|
||||||
|
$customer = $this->stripeClient->customers->retrieve(
|
||||||
|
$stripeCustomerId,
|
||||||
|
['expand' => ['cash_balance']]
|
||||||
|
);
|
||||||
|
return ($customer->cash_balance->available['mxn'] ?? 0) / 100;
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function log($message)
|
private function log($message)
|
||||||
{
|
{
|
||||||
if ($this->logger) {
|
if ($this->logger) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user