67faa1a266
El original lo dice en su propio texto ("los objetos disponibles son los mismos
de la tienda") y lo confirma su JS, que usa #store-list y .store-add-button: el
regalo NO tiene catálogo propio, es la tienda con el correo a otro personaje.
Nosotros teníamos una tabla aparte (home_item) con 8 objetos y precios en euros.
Ahora send-gift monta StoreBrowser en modo `gift`: mismo catálogo (3068 ítems,
PD/PV), mismo carrito con cantidades y las cuatro formas de pago. Antes NO tenía
Stripe: solo SumUp, PD y PV.
Dos pasos, como el original: primero #char-select-div (personaje de origen,
destino, confirmación y token) y solo al pulsar "Mostrar Regalos" aparece el
catálogo. Se borran SendGiftForm, /api/gift/checkout y lib/gift (ya no los usa
nadie); la tabla home_item se queda en la BD, sin usar.
Tres cosas que el usuario pidió y que estaban mal:
- El formulario va con `noValidate`: los avisos los damos nosotros en rojo
(#show-gif-response), no el navegador.
- "Mostrar Regalos" ahora valida contra el SERVIDOR (que el personaje de origen
sea tuyo, que el destino exista y que el token sea correcto). Antes elegías
objetos para descubrir al final que el destino no existía.
- BUG REAL: el nombre del destino distinguía mayúsculas. `characters.name` es
`utf8mb4_bin`, así que "innakh" NO encontraba a "Innakh" (comprobado: 0
resultados; con COLLATE, 1). `findCharacterByName` busca sin distinguir y
devuelve el nombre CANÓNICO, que es el que va al comando SOAP.
La validación vive en lib/gift-check y la usan las dos rutas: /api/gift/check (el
botón) y /api/gift/send, que revalida porque el cliente puede saltarse el paso 1.
También se quita del texto "El pago se realiza mediante SumUp": el original no lo
dice y además ya era falso con cuatro formas de pago.
Verificado: innakh / INNAKH / InNaKh resuelven a "Innakh"; token malo y destino
inexistente salen en rojo sin pasar al catálogo. Con 500 PD de saldo de prueba,
2 copias de un ítem de 200 pasan el cobro (deliveryFailed por el worldserver
caído, con su reembolso) y 3 dan insufficientPd: el servidor cobra por copias.
Datos de prueba borrados.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37 lines
1.6 KiB
TypeScript
37 lines
1.6 KiB
TypeScript
import { getGameCharacters, findCharacterByName } from './characters'
|
|
import { checkSecurityToken } from './security-token'
|
|
import { isValidCharName } from './store'
|
|
|
|
/**
|
|
* Comprueba que un regalo se puede enviar: personaje de origen tuyo, personaje
|
|
* de destino existente y token de seguridad correcto.
|
|
*
|
|
* La usan las DOS rutas: `/api/gift/check` (el botón "Mostrar Regalos", que
|
|
* valida antes de enseñar el catálogo, como el original) y `/api/gift/send`, que
|
|
* vuelve a validarlo porque el cliente puede saltarse el primer paso.
|
|
*
|
|
* Devuelve el nombre CANÓNICO del destino: el usuario puede escribirlo en
|
|
* mayúsculas, minúsculas o mezcladas, y al correo tiene que ir el de la BD.
|
|
*/
|
|
export async function checkGift(
|
|
accountId: number,
|
|
body: { source?: string; destination?: string; security_token?: string },
|
|
): Promise<{ source: string; destination: string } | { error: string }> {
|
|
const source = String(body.source ?? '').trim()
|
|
const destination = String(body.destination ?? '').trim()
|
|
const token = String(body.security_token ?? '').trim()
|
|
|
|
if (!source || !destination || !token) return { error: 'missingFields' }
|
|
if (!isValidCharName(destination)) return { error: 'invalidDestination' }
|
|
|
|
const chars = await getGameCharacters(accountId)
|
|
if (!chars.find((c) => c.name === source)) return { error: 'invalidSource' }
|
|
|
|
const dest = await findCharacterByName(destination)
|
|
if (!dest) return { error: 'destinationNotFound' }
|
|
|
|
if (!(await checkSecurityToken(accountId, token))) return { error: 'invalidToken' }
|
|
|
|
return { source, destination: dest.name }
|
|
}
|