From edde2f78abc3a4393f79afe3ca4c761deb08dcc1 Mon Sep 17 00:00:00 2001 From: adevopg Date: Tue, 14 Jul 2026 14:14:08 +0000 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20/trans-history=20(Historial=20de?= =?UTF-8?q?=20transacciones):=20solo=20Stripe=20y=20SumUp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dos cajas por pasarela (Stripe mode test/live, SumUp mode sumup) desde home_stripelog, con logo, nota y tabla ID/Estado/Concepto/Fecha/Cantidad. Estado PAID (fulfilled=1) / PENDING. Se omiten dLocal, Crypto y PayPal del diseño original. lib/trans-history.ts (getPaymentTransactions). Django era stub. Co-Authored-By: Claude Opus 4.8 (1M context) --- web-next/app/[locale]/trans-history/page.tsx | 144 +++++++++++++++++++ web-next/lib/trans-history.ts | 38 +++++ 2 files changed, 182 insertions(+) create mode 100644 web-next/app/[locale]/trans-history/page.tsx create mode 100644 web-next/lib/trans-history.ts diff --git a/web-next/app/[locale]/trans-history/page.tsx b/web-next/app/[locale]/trans-history/page.tsx new file mode 100644 index 0000000..4a729b1 --- /dev/null +++ b/web-next/app/[locale]/trans-history/page.tsx @@ -0,0 +1,144 @@ +import type { Metadata } from 'next' +import { setRequestLocale } from 'next-intl/server' +import { redirect } from '@/i18n/navigation' +import { getSession } from '@/lib/session' +import { getPaymentTransactions, type PaymentTx } from '@/lib/trans-history' +import { PageShell } from '@/components/PageShell' +import { ServiceBox } from '@/components/ServiceBox' + +export const dynamic = 'force-dynamic' + +export const metadata: Metadata = { title: 'Historial de transacciones' } + +/** Fecha en formato DD-MM-YYYY HH:MM:SS. */ +function fmt(d: Date): string { + const p = (n: number) => String(n).padStart(2, '0') + return `${p(d.getDate())}-${p(d.getMonth() + 1)}-${d.getFullYear()} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}` +} + +function StatusBadge({ status }: { status: PaymentTx['status'] }) { + return {status} +} + +/** Caja de una pasarela con su tabla de transacciones (o "No hay movimientos"). */ +function PlatformBox({ + name, + logo, + note, + txs, +}: { + name: string + logo: string + note: string + txs: PaymentTx[] +}) { + return ( +
+
+

+ {name} {name} +

+
+
+

+ Recibirás los PD correspondientes a cada transacción sólo cuando esté aprobada y el estado sea{' '} + PAID (Pagado). +

+
+

+ Importante: {note} +

+
+ {txs.length === 0 ? ( + + + + + + +
+ No hay movimientos +
+ ) : ( + + + + + + + + + + {txs.map((t) => ( + + + + + + + + ))} + +
ID de transacciónEstadoConceptoFecha creaciónCantidad
+ {t.id} + + + + {t.concept} + + {fmt(t.createdAt)} + + {t.amount.toFixed(2)} € +
+ )} +
+
+ ) +} + +const LOGO_BASE = '/nw-themes/nw-ryu/nw-images/nw-logos' + +export default async function TransHistoryPage({ params }: { params: Promise<{ locale: string }> }) { + const { locale } = await params + setRequestLocale(locale) + + const session = await getSession() + if (!session.bnetId) redirect({ href: '/login', locale }) + if (!session.username) redirect({ href: '/select-account', locale }) + + const txs = await getPaymentTransactions(session.accountId!) + const stripe = txs.filter((t) => t.platform === 'Stripe') + const sumup = txs.filter((t) => t.platform === 'SumUp') + + return ( + + +
+

Aquí verás la información que recibimos de nuestras diferentes plataformas de pago automáticas.

+
+

¿Has tenido algún problema con alguna transacción?

+

+ Si has tenido un problema con una de las transacciones o no has recibido tus PD, puedes contactarnos en + nuestro Discord o a través del correo{' '} + contacto@ultimowow.com. +

+
+
+ +
+
+
+ +
+ ) +} diff --git a/web-next/lib/trans-history.ts b/web-next/lib/trans-history.ts new file mode 100644 index 0000000..297f2f1 --- /dev/null +++ b/web-next/lib/trans-history.ts @@ -0,0 +1,38 @@ +import type { RowDataPacket } from 'mysql2' +import { db, DB } from './db' + +/** Una transacción de pago (Stripe o SumUp) registrada en home_stripelog. */ +export interface PaymentTx { + id: string // referencia de la pasarela (session_id / checkout_reference) + platform: 'Stripe' | 'SumUp' + status: 'PAID' | 'PENDING' + createdAt: Date + amount: number // importe en € + concept: string +} + +/** + * Transacciones de pago de una cuenta desde `home_stripelog`, limitadas a las + * dos pasarelas activas: **Stripe** (mode test/live) y **SumUp** (mode sumup). + * `fulfilled` 1 -> PAID (pagado y entregado), 0 -> PENDING. Orden por fecha desc. + */ +export async function getPaymentTransactions(accountId: number, limit = 200): Promise { + if (!accountId) return [] + try { + const [rows] = await db(DB.default).query( + "SELECT session_id, product_name, amount, mode, fulfilled, timestamp FROM home_stripelog " + + "WHERE account_id = ? AND mode IN ('sumup', 'test', 'live') ORDER BY timestamp DESC LIMIT ?", + [accountId, limit], + ) + return rows.map((r) => ({ + id: String(r.session_id || ''), + platform: r.mode === 'sumup' ? 'SumUp' : 'Stripe', + status: r.fulfilled ? 'PAID' : 'PENDING', + createdAt: new Date(r.timestamp), + amount: Number(r.amount), + concept: String(r.product_name || '—'), + })) + } catch { + return [] + } +}