diff --git a/web-next/app/api/store/send/route.ts b/web-next/app/api/store/send/route.ts index ecc8d07..b43e54b 100644 --- a/web-next/app/api/store/send/route.ts +++ b/web-next/app/api/store/send/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' import { getSession } from '@/lib/session' import { getGameCharacters } from '@/lib/characters' -import { priceStoreCart, purchaseStoreCart, storeEuroTotal, createStoreOrder, type StoreCartLine } from '@/lib/store' +import { priceStoreCart, purchaseStoreCart, storeEuroTotal, createStoreOrder, storeConcept, type StoreCartLine } from '@/lib/store' import { CARD_MIN_EUR } from '@/lib/store-pricing' import { createCheckoutSession } from '@/lib/stripe' import { createSumUpCheckout, sumupConfigured } from '@/lib/sumup' @@ -56,10 +56,13 @@ export async function POST(request: Request) { const site = process.env.SITE_URL || '' const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' - // Objetos comprados = suma de copias, no número de líneas: 15 copias de un - // mismo ítem son "15 objetos", no "1 objeto". - const units = cart.lines.reduce((s, l) => s + l.copies, 0) - const productName = `Compra en la tienda (${units} objeto${units === 1 ? '' : 's'})` + // Concepto con quién compra y qué: es lo único que verá la pasarela en su + // panel para identificar el pago (ver `storeConcept`). + const productName = storeConcept(cart, { + bnetEmail: session.bnetEmail, + account: session.username, + character, + }) const metadata = { service: 'store', order_ref: ref } if (provider === 'sumup') { diff --git a/web-next/lib/store.ts b/web-next/lib/store.ts index fd919ca..4d7a0be 100644 --- a/web-next/lib/store.ts +++ b/web-next/lib/store.ts @@ -197,11 +197,50 @@ export interface StoreCartLine { export interface PricedCart { // `qty` = lote que entrega CADA copia (home_store_item.quantity); // `copies` = cuántas veces se compra la línea. Total de unidades = qty*copies. - lines: { id: number; itemId: number; qty: number; currency: Currency; price: number; copies: number }[] + // `name` = nombre del catálogo; lo usa el concepto del cobro con tarjeta. + lines: { id: number; itemId: number; name: string; qty: number; currency: Currency; price: number; copies: number }[] pdTotal: number vpTotal: number } +/** + * Concepto del cobro con tarjeta: quién compra y qué. Sale en la pasarela y en + * su panel, así que es lo único que tendrán Stripe/SumUp para identificar un + * pago si hay que reclamar o devolver algo a mano. + * + * Formato: `Tienda · BNet · Cuenta · · Nombre (id) x2, …` + * + * Se recorta a `MAX_CONCEPT`: Stripe y SumUp limitan el campo (~250-255), y un + * carrito de 100 líneas se pasaría de largo. Se recortan solo los ÍTEMS, nunca + * la cabecera: quién ha pagado importa más que el listado completo, que además + * queda entero en `home_store_order`. + */ +const MAX_CONCEPT = 200 + +export function storeConcept( + cart: PricedCart, + opts: { bnetEmail?: string; account?: string; character: string }, +): string { + const head = [ + 'Tienda', + opts.bnetEmail ? `BNet ${opts.bnetEmail}` : null, + opts.account ? `Cuenta ${opts.account}` : null, + opts.character, + ] + .filter(Boolean) + .join(' · ') + const parts = cart.lines.map((l) => `${l.name} (${l.itemId})${l.copies > 1 ? ` x${l.copies}` : ''}`) + + // Se van quitando ítems por el final hasta que quepa TODO, sufijo incluido: si + // se comprueba antes de añadir el "+N más" el resultado se pasa igualmente. + const fits = (s: string) => `${head} · ${s}`.length <= MAX_CONCEPT + let list = parts.join(', ') + for (let n = parts.length - 1; n >= 0 && !fits(list); n--) { + list = n > 0 ? `${parts.slice(0, n).join(', ')}, +${parts.length - n} más` : `${parts.length} ítems` + } + return fits(list) ? `${head} · ${list}` : head +} + /** * Valida el carrito contra la BD (precios/cantidades/moneda REALES, nunca del * cliente) a partir de las filas `home_store_item.id` y las copias pedidas. @@ -223,7 +262,7 @@ export async function priceStoreCart(cart: StoreCartLine[]): Promise( - `SELECT id, item_id, quantity, currency, price FROM home_store_item WHERE id IN (${ids.map(() => '?').join(',')})`, + `SELECT id, item_id, name, quantity, currency, price FROM home_store_item WHERE id IN (${ids.map(() => '?').join(',')})`, ids, ) } catch { @@ -234,6 +273,7 @@ export async function priceStoreCart(cart: StoreCartLine[]): Promise ({ id: Number(r.id), itemId: Number(r.item_id), + name: String(r.name), qty: Number(r.quantity), currency: r.currency, price: Number(r.price),