siip-whatsapp-notifications.../views/stripe.php

271 lines
15 KiB
PHP
Executable File

<?php if (!isset($isModuleJs)): ?>
<!-- MODULE 3: STRIPE -->
<section id="section-pagos-spei" class="section-view">
<div class="card">
<div style="margin-bottom: 2rem;">
<h2 style="margin: 0; display: flex; align-items: center; gap: 10px;">
<img src="?action=get_image&name=online-payments-stripe.png" style="width:64px;height:64px;"> Pagos SPEI
</h2>
<p style="color: var(--text-muted); margin: 5px 0 0 0;">🏦 Genera referencias de transferencia bancaria para que tus clientes paguen vía SPEI</p>
</div>
<!-- CONFIG CONTAINER -->
<div class="config-container">
<div class="form-group" style="position: relative;">
<label>Buscar Cliente para Stripe</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="stripeSearch" class="form-control search-input-padded" placeholder="Buscar cliente..." autocomplete="off">
</div>
<div id="stripeResults" class="search-results"></div>
</div>
</div>
<!-- PLACEHOLDER STATE (Initially Visible) -->
<div id="stripePlaceholder" class="placeholder-state">
<span class="placeholder-icon">🏦</span>
<p>Selecciona un cliente para generar una referencia de pago SPEI</p>
</div>
</div>
<div id="stripeDetailContainer" class="card" style="display: none;">
<!-- HEADER: CLIENT INFO & BALANCE -->
<div class="client-header-grid">
<div class="client-info">
<h3>
<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 class="client-balance">
<span class="label">Saldo Actual</span>
<span class="badge" id="stripeBalanceBadge">$0.00</span>
</div>
</div>
<!-- TECHNICAL DETAILS GRID -->
<div class="details-grid">
<div class="detail-item">
<label>Stripe Customer ID</label>
<div class="value-box" id="stripeCustomerIdDisplay">-</div>
</div>
<div class="detail-item">
<label>Clabe Interbancaria</label>
<div class="value-box" id="stripeClabeDisplay">-</div>
</div>
</div>
<!-- PAYMENT INTENT FORM -->
<div>
<h4 class="section-title">Generar Intención de Pago</h4>
<div class="form-grid">
<div class="form-group">
<label>Monto a Cobrar</label>
<div class="input-group-unified">
<span class="input-prefix">$</span>
<input type="number" id="stripeAmount" class="form-control" placeholder="0.00">
</div>
</div>
<div class="form-group">
<label>Asignar a Administrador</label>
<select id="stripeAdminSelect" class="form-control">
<option value="">-- Automático (Sistema) --</option>
<?php foreach ($admins as $admin): echo "<option value='{$admin['id']}'>{$admin['nombre']}</option>";
endforeach; ?>
</select>
</div>
</div>
<div class="actions-row">
<a id="btnVerEnCrm" href="#" target="_blank" class="btn btn-uniform btn-lg">
<img src="?action=image&file=crm.webp" class="icon-crm">
Ver en CRM
</a>
<button id="btnCreateIntent" class="btn btn-primary btn-lg">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect>
<line x1="1" y1="10" x2="23" y2="10"></line>
</svg>
Generar Referencia SPEI
</button>
</div>
</div>
</div>
<div id="stripeHistoryContainer" style="display:none; margin-top: 2rem; border-top: 1px solid var(--border); padding-top: 1.5rem;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<h4 class="section-title" style="margin-bottom: 0;">Historial de Pagos (Últimos 10)</h4>
<div id="stripeCashBalanceBadge" class="balance-badge" style="display:none;">
<img src="?action=image&file=account-balance.webp" style="width: 32px; height: 32px; margin-right: 8px;">
<span id="stripeCashBalanceText">Saldo Stripe: $0.00 MXN</span>
</div>
</div>
<div style="overflow-x: auto;">
<table class="table" id="stripeHistoryTable" style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="text-align: left; background: var(--bg-body);">
<th style="padding: 10px;">Estado</th>
<th style="padding: 10px;">Importe</th>
<th style="padding: 10px;">Descripción</th>
<th style="padding: 10px;">Fecha</th>
<th style="padding: 10px;">ID</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</section>
<?php else: ?>
document.addEventListener('DOMContentLoaded', () => {
// 2. STRIPE SEARCH
let selectedStripeClient = null;
setupSearch('stripeSearch', 'stripeResults', 'search_stripe', async (partialClient) => {
const res = await fetch(`${window.SIIP_STRIPE_PATH || ''}?action=get_stripe_details&id=${partialClient.id}`);
const data = await res.json();
selectedStripeClient = data;
document.getElementById('stripeClientName').textContent = data.fullName;
document.getElementById('stripeClientIdDisplay').textContent = `ID: #${data.id}`;
document.getElementById('stripeBalanceBadge').textContent = `Saldo: $${parseFloat(data.accountOutstanding||0).toFixed(2)}`;
document.getElementById('stripeCustomerIdDisplay').textContent = data.stripeCustomerId || 'No disponible';
document.getElementById('stripeClabeDisplay').textContent = data.clabeInterbancaria || 'No disponible';
document.getElementById('stripeAmount').value = data.accountOutstanding > 0 ? data.accountOutstanding : '';
document.getElementById('btnVerEnCrm').href = `${store.publicUrl}/client/${data.id}`;
document.getElementById('stripeDetailContainer').style.display = 'block';
document.getElementById('stripePlaceholder').style.display = 'none'; // Hide placeholder
if (data.stripeCustomerId) loadStripeHistory(data.stripeCustomerId);
else document.getElementById('stripeHistoryContainer').style.display = 'none';
});
document.getElementById('btnCreateIntent').onclick = async () => {
if (!selectedStripeClient?.stripeCustomerId) return showToast('Error: Cliente sin Stripe ID', true);
const amt = parseFloat(document.getElementById('stripeAmount').value);
if (!amt || amt < 10) return showToast('Mínimo 10 MXN', true);
const btn = document.getElementById('btnCreateIntent');
btn.disabled = true;
btn.textContent = 'Procesando...';
const fd = new FormData();
fd.append('action', 'create_intent');
fd.append('clientId', selectedStripeClient.id);
fd.append('amount', amt);
fd.append('stripeCustomerId', selectedStripeClient.stripeCustomerId);
fd.append('adminId', document.getElementById('stripeAdminSelect').value || store.defaultStripeAdminId);
try {
const res = await fetch(`${window.SIIP_STRIPE_PATH || ''}?`, {
method: 'POST',
body: fd
});
const d = await res.json();
if (d.success) showStripeResult(d);
else showToast(d.error, true);
} catch (e) {
showToast('Error de conexión', true);
}
btn.disabled = false;
btn.textContent = 'Generar Referencia SPEI';
};
function showStripeResult(data) {
const c = document.getElementById('stripeResultContent');
let html = `<div style="text-align:center; margin-bottom: 20px;">
<h3 style="color:var(--success); margin-bottom: 5px;">¡Referencia Creada!</h3>
<p style="font-size: 1.1rem; color: var(--text-main);">Monto: <strong>$${data.amount} MXN</strong></p>
</div>`;
if (data.next_action?.display_bank_transfer_instructions) {
const instr = data.next_action.display_bank_transfer_instructions;
const spei = instr.financial_addresses[0]?.spei;
if (spei) {
html += `
<div style="background:#f8fafc; border: 1px solid #e2e8f0; padding:1.5rem; border-radius:12px; color: #1e293b; text-align: left;">
<div style="margin-bottom: 1rem;">
<span style="display:block; font-size: 0.8rem; color: #64748b; text-transform: uppercase; font-weight: 600;">Institución Bancaria</span>
<strong style="font-size: 1.1rem;">${spei.bank_name}</strong>
</div>
<div style="margin-bottom: 0;">
<span style="display:block; font-size: 0.8rem; color: #64748b; text-transform: uppercase; font-weight: 600;">CLABE Interbancaria</span>
<strong style="font-size: 1.3rem; letter-spacing: 1px; font-family: monospace; display: block; margin-top: 5px;">${spei.clabe}</strong>
</div>
</div>
<p style="font-size: 0.85rem; color: var(--text-muted); text-align: center; margin-top: 1.5rem;">
Realiza tu transferencia vía SPEI con estos datos. El pago se registrará automáticamente al recibirse.
</p>`;
}
} else {
html += `<div style="background:rgba(255,165,0,0.1); padding: 15px; border-radius: 8px; border: 1px solid orange; color: orange; text-align:center;">
No se encontró información de transferencia. Por favor, revisa el historial.
</div>`;
}
c.innerHTML = html;
document.getElementById('stripeResultModal').style.display = 'flex';
}
async function loadOxxoHistory(stripeCustomerId) {
const tbody = document.querySelector('#oxxoHistoryTable tbody');
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center">Cargando historial...</td></tr>';
try {
const res = await fetch(`${window.SIIP_STRIPE_PATH || ''}?action=get_oxxo_history&stripeCustomerId=${stripeCustomerId}`);
const data = await res.json();
if (data.error) {
tbody.innerHTML = `<tr><td colspan="6" style="text-align:center;color:var(--danger)">Error: ${data.error}</td></tr>`;
return;
}
if (!data.history || data.history.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center">No hay fichas recientes</td></tr>';
return;
}
tbody.innerHTML = data.history.map(p => {
let statusBadge = '';
switch (p.status) {
case 'succeeded':
statusBadge = '<span class="badge badge-success">Pagado</span>';
break;
case 'requires_action':
statusBadge = '<span class="badge badge-warning">Pendiente</span>';
break;
case 'canceled':
statusBadge = '<span class="badge badge-danger">Cancelado</span>';
break;
default:
statusBadge = `<span class="badge badge-secondary">${p.status}</span>`;
}
const date = new Date(p.created * 1000).toLocaleString();
const voucherBtn = p.voucherUrl ?
`<a href="${p.voucherUrl}" target="_blank" class="btn btn-uniform" style="padding: 4px 12px; font-size: 0.8rem;">Ver Ficha</a>` :
'-';
return `<tr>
<td><small style="font-family:monospace">${p.id.slice(-8)}</small></td>
<td>${date}</td>
<td>$${p.amount.toFixed(2)} ${p.currency}</td>
<td style="font-family:monospace">${p.oxxoNumber}</td>
<td>${statusBadge}</td>
<td>${voucherBtn}</td>
</tr>`;
}).join('');
} catch (e) {
console.error(e);
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;color:var(--danger)">Error de conexión</td></tr>';
}
}
});
<?php endif; ?>