- lib/stripe.ts: createCheckoutSession (crea sesión Stripe + StripeLog en
home_stripelog, success_url con {CHECKOUT_SESSION_ID}), isSessionPaid,
claimPaidCheckout (verifica pagado + no entregado, marca fulfilled -> evita
re-entregas; port de require_paid_stripe).
- lib/prices.ts: precios desde home_*price (rename/customize/race/faction/level).
- components/PaidServiceForm.tsx: selector + botón de pago -> redirige a la URL de
Checkout de Stripe (session.url, sin stripe.js).
- Rename: route /api/character/rename/checkout (guard + valida personaje + crea
checkout), página /rename (PaidServiceForm) y /rename/success (verifica pago y
ejecuta SOAP .char rename, i18n). STRIPE_* + NEXT_PUBLIC_STRIPE_PUBLIC_KEY en
.env.local. Namespace Rename.
Verificado: checkout sin sesión 401, /rename redirige a login, /rename/success con
session_id inválido no entrega el servicio (muestra error). Patrón para los demás
servicios de pago (cambiar precio + comando SOAP).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- lib/turnstile.ts: verifyTurnstile(token, ip) contra siteverify (fail-closed).
- components/Turnstile.tsx: widget cliente (carga el script de Cloudflare, callback
con el token). Site key por NEXT_PUBLIC_TURNSTILE_SITE_KEY; secreto server-only.
- LoginForm/RegisterForm: widget + token en el POST; submit deshabilitado hasta
resolver el captcha (si hay site key).
- Routes login/register: verifican el token antes de continuar (error captchaFailed).
- Catálogos: captchaFailed.
Verificado: POST sin token -> captchaFailed en login y registro (enforcement server).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- components/Header.tsx (server, lee sesión): marca, nav (Inicio; login/register o
account/logout según sesión), LanguageSwitcher y LogoutButton.
- components/LanguageSwitcher.tsx (cliente): cambia de idioma conservando la ruta
(next-intl usePathname/useRouter). components/LogoutButton.tsx (compartido).
- components/Footer.tsx. app/[locale]/layout.tsx envuelve children con Header/Footer
(columna min-h-screen). Se unifica el LogoutButton (se borra el de account/).
- messages: namespace Nav.
Verificado: home ES/EN con cabecera+nav+idioma+pie; enlaces locale-aware (/en/login);
textos por idioma. Al leer sesión en el header, todas las rutas pasan a dinámicas.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>