209 lines
13 KiB
PHP
Executable File
209 lines
13 KiB
PHP
Executable File
<?php if (!isset($isModuleJs)): ?>
|
|
|
|
<!-- MODULE 4: OXXO -->
|
|
<section id="section-pagos-oxxo" 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=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>
|
|
|
|
<!-- CONFIG CONTAINER -->
|
|
<div class="config-container">
|
|
<div class="form-group" style="position: relative;">
|
|
<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>
|
|
<div id="oxxoResults" class="search-results"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- PLACEHOLDER STATE (Initially Visible) -->
|
|
<div id="oxxoModulePlaceholder" class="placeholder-state">
|
|
<span class="placeholder-icon">🏪</span>
|
|
<p>Selecciona un cliente para generar una ficha OXXO Pay</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="oxxoDetailContainer" style="display: none; margin-top: 2rem;">
|
|
<div class="stripe-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: start;">
|
|
<!-- Left Column: Form & Client Info -->
|
|
<div class="card" style="height: 100%; display: flex; flex-direction: column;">
|
|
<!-- Client Header -->
|
|
<div style="border-bottom: 1px solid var(--border); padding-bottom: 1rem; margin-bottom: 1.5rem;">
|
|
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 0.5rem;">
|
|
<img src="?action=image&file=client.webp" class="client-header-icon" style="width: 32px; height: 32px; border-radius: 50%;">
|
|
<h3 style="margin: 0; font-size: 1.1rem; color: var(--text-main);"><span id="oxxoClientName">Nombre del Cliente</span></h3>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
<p id="oxxoClientIdDisplay" style="color: var(--text-muted); margin: 0; font-size: 0.9rem;">ID: #0</p>
|
|
<span class="badge" id="oxxoBalanceBadge" style="background: #fee2e2; color: #ef4444; font-size: 0.8rem; padding: 2px 8px; border-radius: 4px;">Saldo: $0.00</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- OXXO Form -->
|
|
<div style="flex: 1; display: flex; flex-direction: column; justify-content: center;">
|
|
<div style="text-align: center; margin-bottom: 1.5rem;">
|
|
<img src="?action=image&file=oxxo-logo.png" style="max-width: 100px; height: auto;">
|
|
</div>
|
|
|
|
<div class="form-group" style="margin-bottom: 1.5rem;">
|
|
<label style="font-weight: 500; margin-bottom: 0.5rem; display: block; color: var(--text-main);">Monto a Cobrar</label>
|
|
<div class="input-group-unified">
|
|
<span class="input-prefix">$</span>
|
|
<input type="number" id="oxxoAmount" class="form-control" placeholder="0.00">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="actions-row" style="display: flex; flex-direction: column; gap: 1rem;">
|
|
<button id="btnCreateOxxoIntent" class="btn btn-primary" style="width: 100%; justify-content: center; background-color: #E20613; border-color: #E20613; color: white; display: flex; align-items: center; gap: 10px; padding: 12px; border-radius: 8px; font-weight: 600; font-size: 1rem; transition: all 0.2s;">
|
|
Generar Ficha OXXO Pay
|
|
</button>
|
|
<a id="btnOxxoCrm" href="#" target="_blank" class="btn btn-uniform" style="text-align: center; display: flex; justify-content: center; width: 100%; padding: 12px; border-radius: 8px; font-size: 1rem;">
|
|
<img src="?action=image&file=crm.webp" class="icon-crm" style="vertical-align: middle; margin-right: 5px; width: 16px;"> Ver en CRM
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column: Ficha Preview -->
|
|
<div class="card" id="oxxoFichaContainer" style="height: 100%; min-height: 450px; display: flex; align-items: center; justify-content: center; background: var(--bg-body); border: 1px dashed var(--border); position: relative;">
|
|
<div id="oxxoPlaceholder" style="text-align: center; color: var(--text-muted);">
|
|
<img src="?action=image&file=oxxo-payments.png" style="max-width: 80px; opacity: 0.5; margin-bottom: 1rem; filter: grayscale(1);">
|
|
<h4 style="margin: 0; font-weight: 500;">Vista Previa</h4>
|
|
<p style="margin: 5px 0 0 0; font-size: 0.9rem;">La ficha generada aparecerá aquí</p>
|
|
</div>
|
|
<div id="oxxoResult" style="display:none; width: 100%; height: 100%;">
|
|
<!-- Generated Ficha will be injected here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- OXXO HISTORY CONTAINER -->
|
|
<div id="oxxoHistoryContainer" style="display: none; margin-top: 2rem;">
|
|
<div class="card">
|
|
<h3 class="section-title" style="margin-bottom: 1rem;">Historial de Fichas OXXO (Últimas 5)</h3>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" id="oxxoHistoryTable">
|
|
<thead>
|
|
<tr>
|
|
<th>ID Pago</th>
|
|
<th>Fecha</th>
|
|
<th>Monto</th>
|
|
<th>Ref. OXXO</th>
|
|
<th>Estatus</th>
|
|
<th>Ficha</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="6" style="text-align:center">Seleccione un cliente...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// 3. OXXO SEARCH
|
|
let selectedOxxoClient = null;
|
|
setupSearch('oxxoSearch', 'oxxoResults', 'search_stripe', async (partialClient) => {
|
|
const res = await fetch(`${window.SIIP_OXXO_PATH || ''}?action=get_stripe_details&id=${partialClient.id}`);
|
|
const data = await res.json();
|
|
selectedOxxoClient = data;
|
|
|
|
document.getElementById('oxxoClientName').textContent = data.fullName;
|
|
document.getElementById('oxxoClientIdDisplay').textContent = `ID: #${data.id}`;
|
|
document.getElementById('oxxoBalanceBadge').textContent = `Saldo: $${parseFloat(data.accountOutstanding||0).toFixed(2)}`;
|
|
document.getElementById('oxxoAmount').value = data.accountOutstanding > 0 ? data.accountOutstanding : '';
|
|
document.getElementById('btnOxxoCrm').href = `${store.publicUrl}/client/${data.id}`;
|
|
|
|
// Reset View
|
|
document.getElementById('oxxoPlaceholder').style.display = 'block'; // This is the PREVIEW placeholder (keep as is)
|
|
document.getElementById('oxxoModulePlaceholder').style.display = 'none'; // Hide MODULE placeholder
|
|
document.getElementById('oxxoResult').style.display = 'none';
|
|
document.getElementById('oxxoResult').innerHTML = '';
|
|
|
|
document.getElementById('oxxoDetailContainer').style.display = 'block';
|
|
|
|
// Load History
|
|
if (data.stripeCustomerId) {
|
|
document.getElementById('oxxoHistoryContainer').style.display = 'block';
|
|
loadOxxoHistory(data.stripeCustomerId);
|
|
} else {
|
|
document.getElementById('oxxoHistoryContainer').style.display = 'none';
|
|
}
|
|
});
|
|
|
|
document.getElementById('btnCreateOxxoIntent').onclick = async () => {
|
|
if (!selectedOxxoClient) return;
|
|
const amt = parseFloat(document.getElementById('oxxoAmount').value);
|
|
if (!amt || amt < 10) return showToast('Mínimo 10 MXN', true);
|
|
|
|
const btn = document.getElementById('btnCreateOxxoIntent');
|
|
const original = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner" style="border: 2px solid #fff; border-top: 2px solid transparent; width: 16px; height: 16px; border-radius: 50%; display: inline-block; animation: spin 1s linear infinite; margin-right: 8px;"></span> Procesando...';
|
|
|
|
const fd = new FormData();
|
|
fd.append('action', 'create_oxxo_intent');
|
|
fd.append('clientId', selectedOxxoClient.id);
|
|
fd.append('amount', amt);
|
|
|
|
try {
|
|
const res = await fetch(`${window.SIIP_OXXO_PATH || ''}?`, {
|
|
method: 'POST',
|
|
body: fd
|
|
});
|
|
const d = await res.json();
|
|
if (d.success) {
|
|
const url = d.data.voucher_image_url || `?action=image&file=${d.data.voucher_filename}`;
|
|
|
|
// Hide Placeholder
|
|
document.getElementById('oxxoPlaceholder').style.display = 'none';
|
|
|
|
// Show Result
|
|
const resDiv = document.getElementById('oxxoResult');
|
|
resDiv.style.display = 'flex';
|
|
resDiv.innerHTML = `
|
|
<div style="text-align:center; width: 100%; animation: fadeIn 0.5s ease-out;">
|
|
<div style="background: #dcfce7; color: #15803d; width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 1rem;">
|
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
|
</div>
|
|
<h3 style="color:var(--text-main); margin-bottom: 0.5rem;">¡Ficha Generada!</h3>
|
|
<p style="color:var(--text-muted); margin-bottom: 1.5rem;">Referencia OXXO Pay</p>
|
|
|
|
<div style="background: white; padding: 10px; border-radius: 8px; border: 1px solid var(--border); display: inline-block; margin-bottom: 1.5rem;">
|
|
<p style="font-size:1.4rem; font-weight:bold; letter-spacing: 2px; margin: 0; color: #000;">${d.data.oxxo_reference}</p>
|
|
</div>
|
|
|
|
<img src="${url}" style="max-width:100%; height:auto; display: block; margin: 0 auto 1.5rem; border:1px solid var(--border); border-radius:8px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
|
|
|
<div style="display: flex; gap: 10px; justify-content: center;">
|
|
<a href="${url}" target="_blank" class="btn btn-primary" style="display: inline-flex; align-items: center; gap: 8px;">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
|
|
Descargar Ficha
|
|
</a>
|
|
</div>
|
|
</div>`;
|
|
} else showToast(d.error, true);
|
|
} catch (e) {
|
|
console.error(e);
|
|
showToast('Error de conexión', true);
|
|
}
|
|
btn.disabled = false;
|
|
btn.innerHTML = original;
|
|
};
|
|
});
|
|
<?php endif; ?>
|