← Volver al blog

Integrar pagos con Stripe en la web de un cliente: lo que no cuenta la documentación

Daniel García·

Ilustración del artículo: Integrar pagos con Stripe en la web de un cliente: lo que no cuenta la documentación

Hace un par de meses un cliente que vende menaje de cocina online nos escribió un viernes a las seis de la tarde: “necesitamos tener Stripe funcionando el lunes, ya anunciamos la fecha de lanzamiento por email a toda la lista”. Dicho así suena a tarea de una tarde: tienes la API, tienes la documentación, enchufas las claves y listo. No fue así. Lo que nos comió el fin de semana no fue el checkout, fueron los webhooks.

Integrar pagos con Stripe en una tienda pequeña no es difícil técnicamente: el SDK es bueno, y Checkout o Payment Element cubren el 90% de los casos sin que escribas una línea de HTML de formulario de tarjeta. La parte que nadie cuenta es que el pago “funciona” en el navegador mucho antes de que tu backend sepa que ha pasado algo; si confías solo en lo que devuelve el frontend, tarde o temprano vas a tener pedidos cobrados que no se marcan como pagados en tu base de datos.

El problema concreto fue este: configuramos el webhook de checkout.session.completed para marcar el pedido como pagado y disparar el email de confirmación. Funcionó perfecto en pruebas. En producción, Stripe reintenta el mismo evento si tu endpoint no responde 200 a tiempo, y el nuestro, los primeros días, tardaba de más porque mandaba el email de forma síncrona antes de responder. Resultado: un cliente recibió tres emails de confirmación para un solo pedido, y en los logs vimos el mismo event.id procesado dos veces porque no guardábamos en ningún sitio los ids ya vistos. Nada roto de cara al dinero, Stripe no cobra dos veces por eso, pero sí de cara a la credibilidad: el dueño de la tienda nos llamó preocupado pensando que había cobrado de más.

El lío no es el checkout, son los webhooks

La solución fue aburrida, y así tiene que ser: guardar el event.id en una tabla con índice único antes de procesar nada, responder 200 en milisegundos, y mover el email a una cola en segundo plano. Me leí la política de reintentos de Stripe, hasta tres días insistiendo con un evento fallido, para entender que un endpoint lento es indistinguible de uno caído desde su lado. Si tu integración no es idempotente desde el primer día, vas a tener este bug tarde o temprano; no es una posibilidad remota, es casi un cuándo.

El alcance PCI que nadie firma en el contrato

Lo otro que descolocó al cliente fue el cumplimiento PCI DSS. Él asumía que, como usábamos Stripe, “el cumplimiento lo lleva Stripe”. Es cierto a medias: si el número de tarjeta nunca toca tu servidor, tu alcance se reduce al cuestionario SAQ A, el más corto que existe. Pero en su proveedor anterior, un TPV bancario de toda la vida, alguien le había prometido integrar el formulario de tarjeta directamente en la web “para que no se note que sales a otra página”, y eso habría disparado el alcance a un SAQ D con auditoría anual. Tuvimos que explicarle, con calculadora en mano, que el ahorro visual no compensaba el coste de cumplimiento. Al final usamos Payment Element, tokenizado por Stripe.js, para que la pantalla se quede dentro de su web sin que el dato de la tarjeta pase nunca por nuestro servidor.

Esto tiene que ver con algo de lo que ya hablamos al presupuestar un proyecto web: el cliente paga por lo que ve, y el riesgo que no ve es justo el que más tarde explota. Un paralelismo parecido al de la dependencia que tumbó un checkout entero: en los dos casos el fallo no estaba en la lógica de negocio que todo el mundo revisa, sino en la tubería que se da por hecho que siempre funciona.

Seguimos usando Stripe en casi todos los proyectos de ecommerce que llevamos, y lo seguiría recomendando sin dudarlo. Pero cada vez que alguien me pide que esté listo para el lunes, pienso en esos tres emails duplicados y en la cara que puso el cliente al teléfono. ¿Cuántas integraciones de pago en producción ahí fuera no son realmente idempotentes y simplemente no les ha tocado aún el reintento que lo destape?

stripepagosintegracionesecommerce