Files
NightSpire/web-next/lib/paid-services.ts
T
Inna 548fca16e6 Añade gold y transfer: patrón de pago unificado con fulfill + metadata
- lib/stripe.ts: createCheckoutSession acepta metadata; claimPaidCheckout la
  recupera de la sesión Stripe y la devuelve.
- lib/paid-services.ts: config unificado con fulfill(character, meta) + extraFields.
  gold -> UPDATE characters money (BD directa, no SOAP), transfer -> SOAP
  .char changeaccount. Los 5 simples usan soapFulfill.
- checkout [service]: lee extraFields -> metadata, precio depende de metadata (gold),
  valida precio>0. service-success llama cfg.fulfill(name, metadata).
- prices.ts: getTransferPrice, getGoldOptions/goldPriceFor (home_goldprice).
- GoldForm (personaje + cantidad) y TransferForm (personaje + destino); páginas
  /gold y /transfer. Catálogos Paid.gold/transfer.

Verificado: /gold /transfer redirigen a login, checkout 401 sin sesión, home OK.
NOTA: transfer no valida aún el token de seguridad ni reglas AC (nivel>=55, DK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 23:32:12 +00:00

86 lines
2.6 KiB
TypeScript

import { executeSoapCommand } from './soap'
import { db, DB } from './db'
import {
getRenamePrice,
getCustomizePrice,
getChangeRacePrice,
getChangeFactionPrice,
getLevelUpPrice,
getTransferPrice,
goldPriceFor,
} from './prices'
type Meta = Record<string, string>
export interface PaidServiceConfig {
price: (meta: Meta) => Promise<number>
productName: (character: string, meta: Meta) => string
fulfill: (character: string, meta: Meta) => Promise<boolean>
extraFields: string[] // campos (además de character) que el form envía y se guardan en metadata
}
async function soapFulfill(command: string): Promise<boolean> {
return (await executeSoapCommand(command)) !== null
}
async function addGold(character: string, goldAmount: number): Promise<boolean> {
try {
await db(DB.characters).query('UPDATE characters SET money = money + ? WHERE name = ?', [
goldAmount * 10000,
character,
])
return true
} catch {
return false
}
}
export const PAID_SERVICES: Record<string, PaidServiceConfig> = {
rename: {
price: () => getRenamePrice(),
productName: (c) => `Renombrar personaje: ${c}`,
fulfill: (c) => soapFulfill(`.char rename ${c}`),
extraFields: [],
},
customize: {
price: () => getCustomizePrice(),
productName: (c) => `Personalizar personaje: ${c}`,
fulfill: (c) => soapFulfill(`.char customi ${c}`),
extraFields: [],
},
'change-race': {
price: () => getChangeRacePrice(),
productName: (c) => `Cambiar raza: ${c}`,
fulfill: (c) => soapFulfill(`.char changerace ${c}`),
extraFields: [],
},
'change-faction': {
price: () => getChangeFactionPrice(),
productName: (c) => `Cambiar facción: ${c}`,
fulfill: (c) => soapFulfill(`.char changef ${c}`),
extraFields: [],
},
'level-up': {
price: () => getLevelUpPrice(),
productName: (c) => `Subir a nivel 80: ${c}`,
fulfill: (c) => soapFulfill(`.char level ${c} 80`),
extraFields: [],
},
gold: {
price: (m) => goldPriceFor(Number(m.gold_amount)),
productName: (c, m) => `Aumentar el Oro: ${m.gold_amount} al Personaje: ${c}`,
fulfill: (c, m) => addGold(c, Number(m.gold_amount)),
extraFields: ['gold_amount'],
},
transfer: {
price: () => getTransferPrice(),
productName: (c, m) => `Transferir ${c} a la cuenta ${m.destination_account}`,
fulfill: (c, m) => soapFulfill(`.char changeaccount ${m.destination_account} ${c}`),
extraFields: ['destination_account'],
},
}
export function getPaidService(slug: string): PaidServiceConfig | null {
return PAID_SERVICES[slug] ?? null
}