Pagos · modules/billing
Scaffold de adapterStripe,
detrás de un solo contrato.
UseDeploy modela el billing como un único port IPaymentProvider. Stripe es uno de los adapters que se enchufa ahí — seleccionado por una env var, compartiendo los mismos tipos normalizados que MercadoPago y Polar, así que cambiar de proveedor es un cambio de un solo archivo.
Qué viene listo y qué completás vos
Honestidad primero: el adapter de Stripe es un scaffold, no una integración llave en mano. La selección de proveedor, la validación de config y el path del customer portal están cableados — createCustomerPortalLink importa el SDK de Stripe de forma lazy y llama a billingPortal.sessions.create. La creación de la sesión de checkout y el parseo de webhooks son stubs tipados con las llamadas exactas al SDK escritas en los comentarios. ¿Por qué scaffold y no terminarlo? Porque la base del boilerplate no trae el paquete stripe como dependencia. Lo sumás, completás los dos métodos contra la forma que ya está fijada, y cada consumidor del contrato de billing sigue funcionando sin cambios. El compromiso que asumimos ahora es la interfaz — para que tu código nunca se acople a las particularidades de Stripe.
Una env var selecciona el proveedor
El composition root construye exactamente un proveedor de pago a partir de PAYMENT_PROVIDER. Ponelo en stripe y pasá los dos secrets; la factory los valida al arrancar y lanza un error claro si falta alguno.
1 PAYMENT_PROVIDER=stripe
2 STRIPE_SECRET_KEY=sk_live_...
3 STRIPE_WEBHOOK_SECRET=whsec_...
4
5 # Then add the SDK the scaffold expects:
6 # bun add stripe (in apps/server)
El contrato de billing en el que Stripe se enchufa
Customer portal — cableado
createCustomerPortalLink importa el SDK de Stripe de forma lazy y devuelve una URL de billing portal alojado. Cero costo de SDK para deployments que usan otro proveedor.
Checkout — scaffold
createCheckoutSession devuelve un "aún no implementado" tipado hasta que cablees stripe.checkout.sessions.create contra la forma de input fijada.
Webhooks — scaffold
parseWebhook está como stub con la llamada exacta a hacer: stripe.webhooks.constructEvent(rawBody, sig, secret) mapeada a un BillingEvent normalizado.
Tipos agnósticos del proveedor
Los tipos de retorno Result<T, DomainError> son idénticos entre Stripe, MercadoPago y Polar, así que la capa de aplicación nunca ramifica según el proveedor.
Referencia de configuración
Variables de entorno
PAYMENT_PROVIDERrequeridaSet to stripe
STRIPE_SECRET_KEYrequeridaSTRIPE_WEBHOOK_SECRETrequerida
Dónde vive
apps/server/src/modules/billing/infrastructure/providers/stripe-payment-provider.tsapps/server/src/modules/billing/infrastructure/providers/index.ts
Construí el billing sobre un contrato, no sobre un proveedor.
Shippeá con Stripe, MercadoPago o Polar detrás del mismo port tipado — y nunca reescribas tu app para cambiar.