Wiki · Devlog · Stripe y Firebase

Integración Stripe y Firebase

La monetización para un juego de navegador es un problema técnico pequeño-pero-complicado. Aquí está cómo CONTRABAND maneja las compras con Stripe, verificación con Firebase y sincronización cross-device vía Firestore.

Los requisitos

Los jugadores pueden comprar cuatro epílogos pagados ($4,99 cada uno), un bundle ($12,99) y cosméticos ocasionales ($2,99). Requisitos:

La arquitectura

Cuatro Vercel Functions manejan el flujo de pago completo:

Código total entre estas cuatro funciones: aproximadamente 280 líneas de Node.js. Comparten un módulo diminuto de inicialización Stripe/Firebase admin (~20 líneas).

El modelo de datos

Firestore tiene una sola colección: users. Cada documento clasificado por UID de Firebase contiene:

Las compras de invitado van a una colección separada guestEntitlements, clasificada por un token generado por cliente en la primera visita. Cuando un invitado se registra, link-purchase.js mueve sus entitlements al documento de usuario autenticado.

El flujo del cliente

Flujo de compra del cliente:

  1. El usuario hace clic en "Comprar Epílogo" en la UI del juego.
  2. El cliente llama a checkout.js con productId y estado de auth actual (UID si está conectado, token de invitado sino).
  3. checkout.js crea la sesión de Stripe, devuelve URL.
  4. El cliente redirige a la página de pago alojada por Stripe.
  5. El jugador ingresa detalles de pago, envía.
  6. Stripe redirige de vuelta al juego con éxito/cancelación.
  7. El webhook de Stripe se dispara server-side, escribe entitlement a Firestore.
  8. El cliente re-ejecuta verify-purchase.js al regresar, obtiene entitlements actualizados, cachea a localStorage.
  9. El contenido del epílogo se desbloquea en el juego.

El viaje de ida y vuelta es aproximadamente 8-12 segundos desde clic en botón hasta desbloqueo, la mayor parte del cual es la UI propia de Stripe. El trabajo server-side es de sub-500ms.

Por qué localStorage como caché

El juego no siempre puede alcanzar Firestore. Un jugador en una mala conexión, un avión o un entorno de navegador sandboxed puede fallar al sincronizar entitlements. Cachear entitlements confirmados por servidor a localStorage significa que el juego corre correctamente offline mientras el jugador haya sincronizado exitosamente alguna vez.

El modelo de seguridad es: localStorage es confiado por el cliente para lectura (mostrar UI), pero cualquier escritura debe ser confirmada por servidor. Un usuario malicioso que modifica localStorage obtiene una UI local falsamente desbloqueada, pero no se renderiza contenido real porque el contenido real del epílogo se carga desde un endpoint de servidor que verifica Firestore antes de servirlo. El caché localStorage es una optimización de UX, no una autoridad.

Trampas comunes

Lo que aprendí

Stripe + Firebase + Vercel es genuinamente el camino más corto a un sistema de monetización funcional para un juego de navegador. El coste total de infraestructura es <$10/mes a cualquier escala razonable. El código es lo suficientemente corto para que un solo desarrollador lo mantenga.

El insight arquitectónico clave es el patrón caché-con-fuente-autoritativa. Firestore es la fuente de verdad; localStorage es caché; el cliente se confía a sí mismo para UI pero al servidor para acceso real de contenido. Este patrón escala de un jugador pagador a decenas de miles sin cambiar fundamentalmente.