send-gift: rediseño al original — es la tienda, pero regalando

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>
This commit is contained in:
2026-07-15 12:45:01 +00:00
parent 2c78489e4d
commit 67faa1a266
15 changed files with 533 additions and 642 deletions
+19 -3
View File
@@ -763,7 +763,7 @@
"intro": "The <s>Send gift</s> tool lets you send gifts to your friends.",
"explain1": "When you choose which character sends the gift and the character that will receive it, you will be able to see the list of items available to send as a gift.",
"explain2": "The available items are the same as in the store, and you can send several items at once.",
"explain3": "When you use the SEND GIFT button, a mail will be sent to the destination character with the selected items, indicating who gave it. Payment is made through <s>SumUp</s>.",
"explain3": "When you use the SEND GIFT button, a mail will be sent to the destination character with the selected items, indicating who gave it.",
"securityTokenHint": "If you don't have a Security Token yet, you can request it <link>here</link>.",
"noteIrreversible": "Please make sure you have entered the correct character name, as this action cannot be reversed once performed.",
"noCharacters": "You don't have any characters available.",
@@ -1965,7 +1965,12 @@
"invalidCharacter": "Invalid character.",
"emptyCart": "Your cart is empty.",
"generic": "An error occurred. Please try again.",
"amountTooLow": "The minimum amount to pay by card is {min} €."
"amountTooLow": "The minimum amount to pay by card is {min} €.",
"missingFields": "Fill in the source character, the target one and the security token.",
"invalidDestination": "The target character name is not valid.",
"invalidSource": "The source character does not belong to your account.",
"destinationNotFound": "The target character does not exist.",
"invalidToken": "The security token is not valid."
},
"newsTitle": "News",
"newsIntro": "List of additions made based on the community's suggestions in our forum.",
@@ -1976,6 +1981,17 @@
"paySumUp": "Card (SumUp)",
"minCard": "Minimum {min} €",
"confirmCard": "Pay {eur} € by card and send the items to {character}?",
"amountTooLow": "The minimum amount to pay by card is {min} €."
"amountTooLow": "The minimum amount to pay by card is {min} €.",
"gift": {
"choosePlayer": "Choose the character that will send the gift",
"destPlaceholder": "Target character name",
"confirmPlaceholder": "Confirm target character name",
"tokenPlaceholder": "Security token",
"show": "Show Gifts",
"showing": "Showing gifts",
"errMissing": "Fill in the source character, the target one and the security token.",
"errConfirm": "The target character name does not match the confirmation.",
"successMsg": "Gift sent to {character}! They will receive it in their in-game mail."
}
}
}
+19 -3
View File
@@ -763,7 +763,7 @@
"intro": "La herramienta <s>Enviar regalo</s> te permite enviar regalos a tus amigos.",
"explain1": "Al elegir desde qué personaje se enviará el regalo y el personaje que lo recibirá, podrás ver la lista de objetos disponibles para enviar como regalo.",
"explain2": "Los objetos disponibles son los mismos de la tienda y puedes enviar varios objetos a la vez.",
"explain3": "Al usar el botón ENVIAR REGALO se enviará un correo al personaje destino con los ítems seleccionados, indicando quién lo ha regalado. El pago se realiza mediante <s>SumUp</s>.",
"explain3": "Al usar el botón ENVIAR REGALO se enviará un correo al personaje destino con los ítems seleccionados, indicando quién lo ha regalado.",
"securityTokenHint": "Si aún no tienes Token de seguridad, puedes solicitarlo <link>aquí</link>.",
"noteIrreversible": "Por favor, asegúrate de haber escrito el nombre del personaje correcto ya que esta acción no es reversible una vez realizada.",
"noCharacters": "No tienes personajes disponibles.",
@@ -1965,7 +1965,12 @@
"invalidCharacter": "El personaje no es válido.",
"emptyCart": "Tu carrito está vacío.",
"generic": "Ha ocurrido un error. Inténtalo de nuevo.",
"amountTooLow": "El importe mínimo para pagar con tarjeta es {min} €."
"amountTooLow": "El importe mínimo para pagar con tarjeta es {min} €.",
"missingFields": "Completa el personaje de origen, el de destino y el token de seguridad.",
"invalidDestination": "El nombre del personaje de destino no es válido.",
"invalidSource": "El personaje de origen no pertenece a tu cuenta.",
"destinationNotFound": "El personaje de destino no existe.",
"invalidToken": "El token de seguridad no es válido."
},
"newsTitle": "Novedades",
"newsIntro": "Lista de adiciones realizadas en base a las sugerencias de la comunidad en nuestro foro.",
@@ -1976,6 +1981,17 @@
"paySumUp": "Tarjeta (SumUp)",
"minCard": "Mínimo {min} €",
"confirmCard": "¿Pagar {eur} € con tarjeta y enviar los objetos a {character}?",
"amountTooLow": "El importe mínimo para pagar con tarjeta es {min} €."
"amountTooLow": "El importe mínimo para pagar con tarjeta es {min} €.",
"gift": {
"choosePlayer": "Escoge el personaje que enviará el regalo",
"destPlaceholder": "Nombre del personaje destino",
"confirmPlaceholder": "Confirmar nombre del personaje destino",
"tokenPlaceholder": "Token de seguridad",
"show": "Mostrar Regalos",
"showing": "Mostrando regalos",
"errMissing": "Completa el personaje de origen, el de destino y el token de seguridad.",
"errConfirm": "El nombre del personaje destino no coincide con la confirmación.",
"successMsg": "¡Regalo enviado a {character}! Lo recibirá en su correo del juego."
}
}
}