Diseña un flujo de pagos móviles: tokenización, alcance de PCI y manejo de fallos
Un recorrido senior sobre cómo un cliente móvil puede aceptar pagos con tarjeta sin tocar jamás el número real — campos tokenizados del SDK, 3-D Secure, reintentos idempotentes y reconciliación.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Diseña un flujo de pagos móviles: tokenización, alcance de PCI y manejo de fallos
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Aclarar los requisitos
Antes de dibujar cualquier caja, definiría qué tipo de pago es este, porque agregar un flujo de pagos esconde problemas muy distintos entre sí. ¿Es una compra única sin tarjeta presente, o hay que guardar una tarjeta para suscripciones y recompras con un toque? ¿Qué mercados? — porque la PSD2 de la UE exige Autenticación Reforzada de Cliente mediante 3-D Secure 2 en la mayoría de las transacciones, mientras que una app solo para EE.UU. suele poder evitar esa fricción. ¿Necesitamos soportar Apple Pay y Google Pay además de la entrada de tarjeta cruda? ¿Es un solo comercio cobrando, o un marketplace que necesita dividir los fondos entre una comisión de plataforma y el pago a un vendedor? ¿Y cuál es la historia de reembolsos y disputas, ya que los contracargos tocan la misma arquitectura desde el otro extremo? Esas respuestas deciden si estoy diseñando un cliente delgado alrededor del SDK de un procesador o un sistema mucho más pesado de libro contable y pagos divididos.
#La regla que le da forma a todo: que el PAN nunca toque tus servidores
La decisión arquitectónica más importante en cualquier flujo de pagos móviles es que el número real de la tarjeta — el PAN — jamás debe llegar al backend propio de la app, ni siquiera permanecer en la memoria de la app más tiempo del necesario para entregárselo al SDK de un procesador. Cada procesador importante (Stripe, Braintree, Adyen) ofrece SDKs móviles que renderizan ellos mismos los campos de tarjeta, ya sea como componentes nativos seguros o como una vista web alojada por el procesador, y esos campos tokenizan la tarjeta directamente contra la infraestructura certificada en PCI del procesador, por TLS. La app nunca ve el PAN — solo recibe de vuelta un token opaco de un solo uso o una referencia de método de pago reutilizable. Esa única decisión de diseño es lo que reduce la carga de cumplimiento PCI DSS propia desde una auditoría SAQ D completa (imagina alojar tú mismo los datos de tarjeta) hasta una autoevaluación SAQ A o SAQ A-EP, porque la ruta de los datos de tarjeta nunca toca infraestructura que tú controlas.
#Arquitectura: entra un token, sale un cobro
Con esa restricción en su lugar, el flujo se ve así: el cliente móvil renderiza el campo seguro de tarjeta del procesador y llama a su SDK para tokenizar, recibiendo de vuelta un token de método de pago. La app envía ese token — nunca el PAN — a su propio backend junto con el monto y una clave de idempotencia generada del lado del cliente. El backend llama a la API del lado del servidor del procesador para crear un intento de pago usando ese token, y es el procesador quien decide si la transacción necesita un desafío 3-D Secure. Si lo necesita, el backend le devuelve un client secret a la app, que le pide al SDK del procesador que presente la UI del desafío (confirmación biométrica o un OTP, muchas veces dentro de una vista web que gestiona el propio SDK). Una vez que el emisor confirma, el procesador liquida el cobro y dispara un webhook hacia el backend — y ese webhook, no la suposición optimista del cliente móvil, es la verdadera fuente de verdad sobre si el dinero realmente se movió.
#Apple Pay y Google Pay como atajo
Dondequiera que la plataforma lo permita, empujaría a los usuarios hacia Apple Pay o Google Pay antes que la entrada cruda de tarjeta, porque ambos evitan el problema del PAN de manera aún más completa. El elemento seguro del dispositivo almacena un número de cuenta del dispositivo en lugar del PAN real, la autenticación biométrica ocurre enteramente en el dispositivo, y el comercio recibe un token de pago cifrado que el procesador descifra — la app en sí nunca tiene acceso a ningún dato equivalente a la tarjeta. También implica menos fricción para el usuario, lo cual afecta directamente la conversión, así que desde el punto de vista de producto suele ser la ruta por defecto y no una opción secundaria escondida en configuración.
#Idempotencia y manejo de fallos
Las redes móviles se caen a mitad de una petición constantemente, y un pago es el único lugar donde un reintento ingenuo puede costarle dinero real a un usuario. El cliente tiene que generar una clave de idempotencia antes del primer intento y enviar la misma clave en cada reintento de ese pago lógico, de modo que si la petición efectivamente llegó al procesador y tuvo éxito, un reintento después de un timeout devuelva el resultado original en lugar de crear un segundo cobro. Modelaría el pago mismo como una pequeña máquina de estados — pending → requires_action → succeeded / failed — persistida por el procesador, y trataría al cliente móvil como un observador de ese estado y no como su dueño: ante cualquier resultado ambiguo (timeout, la app se cierra a mitad del flujo, partición de red), lo correcto es consultar el estado actual del pago por su ID de intento, no asumir que falló y dejar que el usuario toque pagar de nuevo. También persistiría localmente un pedido en curso antes de enviar la petición, de modo que si la app se cierra a mitad del pago, el siguiente arranque pueda reconciliar ese pedido pendiente contra el backend en lugar de perderle el rastro en silencio.
#Reconciliación: el libro contable es la verdad, no la app
Un pago no está terminado cuando la app muestra una pantalla de éxito — está terminado cuando el libro contable del backend y los registros del procesador coinciden. Me apoyaría principalmente en los webhooks para ese acuerdo, pero nunca exclusivamente, porque los webhooks pueden retrasarse, deduplicarse incorrectamente o perderse durante un incidente; un job de reconciliación en segundo plano que consulte al procesador por cualquier pago atascado en un estado no terminal más allá de un umbral (digamos, cinco minutos) cierra esa brecha. El libro contable en sí debería ser de solo append y de doble entrada — cada cobro, reembolso y comisión registrados como un par de asientos que se compensan — de modo que un desajuste de reconciliación esté a una consulta de distancia de ser encontrado, en lugar de aparecer tres meses después como una discrepancia financiera.
#Tradeoffs que nombraría
Construir tu propia UI de captura de tarjeta en lugar de usar los campos alojados del procesador te compra control de diseño pixel-perfect, pero arrastra a tu propia app de vuelta al alcance completo de PCI — casi nunca vale la pena. 3-D Secure reduce el fraude y la responsabilidad por contracargos, pero agrega un paso de desafío que perjudica de forma medible la conversión, así que solo lo forzaría donde la regulación lo exige (UE/Reino Unido) y dejaría que el motor de riesgo del procesador decida en el resto de los casos. Y apostar todo a la entrega de webhooks es más simple de construir que un job de reconciliación por polling, pero cambia un poco de esfuerzo de ingeniería por el riesgo de pagos atascados en silencio — para cualquier cosa que involucre dinero real, siempre pagaría ese costo de ingeniería extra.