026fbcb81c
Porta la tienda de ítems/transfiguración del sistema antiguo: elegir personaje → árbol de categorías (3 niveles) → carrito → enviar por correo. Se paga con el saldo PD o PV de la cuenta (cada ítem lleva su precio y moneda). - BD: home_store_category (árbol por code "1-1-1") + home_store_item (mismos item_id, nombres, iconos, precios y moneda que el original; 2947 en PD, 121 en PV). Seed en sql/store_catalog.sql, extraído del HTML real. - lib/store.ts: getStoreCatalog (árbol anidado), getStoreBalances, priceStoreCart (precios REALES de BD), purchaseStoreCart (cobro atómico PD+PV con FOR UPDATE y reembolso si el envío SOAP falla; `.send items` troceado a 12/correo). - API: GET /api/store/catalog (tras elegir personaje) y POST /api/store/send. - components/StoreBrowser.tsx: selector de personaje, árbol colapsable (ítems montados solo al abrir), carrito con totales PD/PV, enviar, modal de resultado. - página /store con la info y el título "Tienda - REINO". Enlaces de ítems e iconos por wowhead/zamimg (con tooltip y locale). El enlace ya existía en AccountTools. i18n namespace Store (es/en). CSS del árbol/carrito/modal. Verificado: catálogo E2E (750 cats/3068 ítems), cobro atómico + reembolso y detección de saldo insuficiente (cuenta 15). SOAP real sin probar (worldserver caído en este entorno). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import { getSession } from '@/lib/session'
|
|
import { getGameCharacters } from '@/lib/characters'
|
|
import { priceStoreCart, purchaseStoreCart } from '@/lib/store'
|
|
|
|
/**
|
|
* Compra de la tienda: cobra el carrito (PD/PV) y envía los ítems por correo al
|
|
* personaje elegido (uno de la cuenta). El precio se recalcula en el servidor a
|
|
* partir de las filas de `home_store_item`; nunca se confía en el cliente.
|
|
*/
|
|
export async function POST(request: Request) {
|
|
const session = await getSession()
|
|
if (!session.accountId || !session.username) {
|
|
return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 })
|
|
}
|
|
|
|
let body: { character?: string; items?: number[] } = {}
|
|
try {
|
|
body = await request.json()
|
|
} catch {
|
|
return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 })
|
|
}
|
|
|
|
const character = String(body.character ?? '')
|
|
const chars = await getGameCharacters(session.accountId)
|
|
if (!character || !chars.find((c) => c.name === character)) {
|
|
return Response.json({ success: false, error: 'invalidCharacter' })
|
|
}
|
|
|
|
const cart = await priceStoreCart(Array.isArray(body.items) ? body.items : [])
|
|
if (!cart) return Response.json({ success: false, error: 'emptyCart' })
|
|
|
|
const r = await purchaseStoreCart(session.accountId, character, cart)
|
|
if (!r.success) return Response.json({ success: false, error: r.error })
|
|
return Response.json({ success: true })
|
|
}
|