
¿No aparecen los meses sin intereses en Stripe? La trampa de Automatic Payment Methods y cómo arreglarla
Si tus meses sin intereses de Stripe están activados en el Dashboard pero nunca aparecen en el Payment Element, casi siempre es la trampa de Automatic Payment Methods (stripe-js #454): los métodos dinámicos no muestran MSI. Define payment_method_options[card][installments][enabled]=true explícito en el PaymentIntent. Luego confirma MXN, tarjeta de crédito mexicana y el monto mínimo por plan.
El síntoma: MSI activado en el Dashboard, nada en el Payment Element
Si tus meses sin intereses de Stripe están activados en el Dashboard pero nunca aparecen en el Payment Element, casi siempre es la trampa de Automatic Payment Methods (stripe-js #454): los métodos de pago dinámicos no muestran MSI. La solución es definir payment_method_options[card][installments][enabled]=true de forma explícita en el PaymentIntent. Después confirma que la moneda sea MXN, que la tarjeta sea de crédito mexicana y que el monto llegue al mínimo por plan.
Este bug lo he depurado en producción con dinero real moviéndose, y siempre empieza igual: activas MSI en el Dashboard, ves los planes configurados (3, 6, 9, 12, 18, 24 meses), pruebas un checkout con una tarjeta de crédito mexicana en MXN por un monto grande, y el selector de meses sin intereses simplemente no está. No hay error, no hay warning, no hay nada en la consola. El Payment Element renderiza tarjeta normal y punto.
La confusión es lógica: la doc de Stripe da a entender que los métodos de pago dinámicos “descubren” automáticamente lo que tienes habilitado, y MSI está habilitado. Así que la reacción natural es dudar de tu tarjeta o de tu monto. Casi siempre no es eso. Es que el PaymentIntent se creó con Automatic Payment Methods y esa ruta no renderiza el selector de installments.
La causa raíz: Automatic Payment Methods vs configuración explícita de MSI (stripe-js #454)
Aquí está la contradicción, y viene de la propia documentación de Stripe. Por un lado, Stripe te empuja fuerte hacia Automatic Payment Methods (métodos de pago dinámicos): creas el PaymentIntent sin listar métodos, y el Payment Element decide qué mostrar según tu configuración del Dashboard, la moneda y el monto. Es la ruta recomendada para casi todo. Por otro lado, meses sin intereses no se materializa por esa vía dinámica: el selector de installments no se renderiza a menos que declares payment_method_options[card][installments][enabled]=true en el PaymentIntent.
Esto está documentado como el issue stripe-js #454 (abierto el 2023-06-02, y sigue mordiendo). El resumen del comportamiento: MSI activado en el Dashboard + PaymentIntent con métodos dinámicos = el selector de meses no aparece, ni en web ni en móvil. La doc implica que los métodos dinámicos exponen installments solos; en la práctica tienes que ponerlo explícito.
El modelo mental que uso es este: Automatic Payment Methods decide qué métodos de pago mostrar (tarjeta, OXXO, SPEI si aplica). Pero MSI no es un método de pago distinto — es una opción sobre la tarjeta. Y esa opción vive en payment_method_options[card][installments], un lugar que la ruta dinámica no toca. Por eso hay que activarlo a mano.
Automatic Payment Methods vs configuración explícita de installments
Solo Automatic Payment Methods
- automatic_payment_methods.enabled = true
- MSI activado en el Dashboard
- El Payment Element NO renderiza el selector de meses
- El comprador ve tarjeta normal, sin MSI
Con installments explícito
- automatic_payment_methods.enabled = true (opcional)
- payment_method_options[card][installments][enabled] = true
- El Payment Element renderiza el selector de meses
- El comprador elige 3/6/9/12/18/24 según monto
Si vienes de una integración limpia con el Payment Element, el patrón base lo cubro en cómo integrar Stripe en tu SaaS. Esta trampa es el detalle mexicano que esa guía no necesita, pero que aquí lo es todo.
El arreglo: payment_method_options[card][installments][enabled]=true
El arreglo es una línea en la creación del PaymentIntent. Antes (roto — MSI activado en el Dashboard pero solo con métodos dinámicos):
// ANTES — MSI no aparece
const paymentIntent = await stripe.paymentIntents.create({
amount: 150000, // MXN 1,500.00 en centavos
currency: "mxn",
automatic_payment_methods: { enabled: true },
});
Después (arreglado — installments explícito en el PaymentIntent):
// DESPUÉS — MSI aparece en el Payment Element
const paymentIntent = await stripe.paymentIntents.create({
amount: 150000, // MXN 1,500.00 en centavos
currency: "mxn",
automatic_payment_methods: { enabled: true },
payment_method_options: {
card: {
installments: { enabled: true },
},
},
});
El campo clave es payment_method_options.card.installments.enabled. Con eso, Stripe evalúa el monto contra los mínimos por plan que tengas configurados, verifica que la moneda sea mxn, y expone los planes elegibles. El Payment Element del lado del cliente entonces sí renderiza el selector de meses sin intereses.
Un detalle importante: el amount va en centavos. 150000 son MXN 1,500.00, no mil quinientos pesos elevados. Un error clásico es mandar 1500 creyendo que son pesos — eso son MXN 15.00, muy por debajo del mínimo de cualquier plan, y de nuevo no aparece nada. La doc oficial de mx-installments es la referencia canónica para este campo.
¿Sigue sin aparecer? Monto por debajo del mínimo por plan
Ya pusiste installments.enabled=true y todavía no aparece. La siguiente causa más común es el monto. Y aquí está el error de expectativa: el mínimo NO es un monto plano de MXN 300 para todo. El mínimo es por plan, y escala con el número de meses.
Los mínimos verificados de Stripe son:
Monto mínimo por plan de meses sin intereses (Stripe, MXN)
Lo que esto significa en la práctica: un cargo de MXN 500 califica para el plan de 3 meses (mínimo 300) pero no para 6, 9, 12, 18 ni 24. Así que el comprador vería solo la opción de 3 meses — no un error, sino una lista corta. Y un cargo de MXN 250 no califica para ningún plan, así que el selector no aparece del todo, aunque tu código esté perfecto.
Estos mínimos los puedes sobrescribir en el Dashboard si tu negocio lo requiere. Pero el default es el de la tabla, y si estás probando con montos chicos “para no gastar”, te vas a topar con esto. Prueba siempre con un monto que rebase el mínimo del plan más largo que quieras ver — MXN 2,400 o más te garantiza que los seis planes sean elegibles por monto.
Trampas de moneda, tipo de tarjeta y emisor
Aunque tengas installments.enabled=true y un monto sobrado, hay tres condiciones más que tienen que cumplirse. Meses sin intereses es un producto muy específico de México, y Stripe lo restringe con precisión.
Moneda: tiene que ser MXN. MSI solo funciona con currency: "mxn". Si tu PaymentIntent está en USD (aunque el comprador esté en México), no hay meses sin intereses. Punto. Si tienes un catálogo multi-moneda, este es un culpable silencioso: la misma tarjeta, el mismo monto, pero en la moneda equivocada, y desaparece el selector.
Tarjeta: crédito mexicana de consumo, no débito ni corporativa. MSI es un producto de crédito: el comprador difiere el pago en mensualidades y el comercio absorbe el costo de financiamiento. Por eso solo aplica a tarjetas de crédito mexicanas de consumo. Una tarjeta de débito no muestra MSI. Una tarjeta corporativa tampoco. Si estás probando con una tarjeta de débito de tu banco, ese es el problema, no tu código.
Emisor: tiene que ser un banco soportado, y ojo con Banamex. La tarjeta tiene que venir de un emisor soportado (BBVA, Santander, Amex, Banamex, entre otros). Y hay un caso concreto que confunde: Banamex solo soporta 3, 6, 9 y 12 meses — no ofrece 18 ni 24. Así que si tu comprador tiene Banamex y esperas ver el plan de 24 meses, nunca va a estar, sin importar el monto. No es un bug: es que ese emisor no ofrece esos plazos.
Las cuatro condiciones que Stripe evalúa
Si estás comparando Stripe contra otras pasarelas para MSI en México, escribí una comparación en pasarelas de pago en México, y el detalle de MSI por API en las tres (Stripe, Conekta, Mercado Pago) lo desgloso en el hermano sobre meses sin intereses por API.
Checklist de depuración: las 7 cosas que verificar en orden
Cuando MSI no aparece, no adivines. Ve en orden. La causa número uno está hasta arriba por una razón: es la que más gente pasa por alto.
Depura MSI en orden — de la causa más común a la más rara
- 1. ¿Está installments explícito en el PaymentIntent?payment_method_options[card][installments][enabled]=true — la causa #1 (stripe-js #454)
- 2. ¿La moneda es MXN?currency: 'mxn' — USD u otra moneda no muestra MSI
- 3. ¿El monto rebasa el mínimo del plan?300/600/900/1,200/1,800/2,400 para 3/6/9/12/18/24, en centavos en el amount
- 4. ¿La tarjeta es de crédito mexicana de consumo?Débito y corporativa no muestran MSI
- 5. ¿El emisor está soportado?BBVA, Santander, Amex, Banamex, etc. — verifica el banco de la tarjeta
- 6. ¿Esperas un plan que el emisor no ofrece?Banamex solo da 3/6/9/12 — 18 y 24 nunca aparecen con Banamex
- 7. ¿MSI está activado en el Dashboard?Condición necesaria pero NO suficiente — sin el paso 1 no basta
El paso 7 es donde mucha gente empieza — y donde no debería. Tener MSI activado en el Dashboard es necesario, pero no suficiente. Es el paso 1, la configuración explícita en el PaymentIntent, el que realmente hace que el selector se renderice. Si solo revisas el Dashboard y confirmas que “MSI está prendido”, te vas a quedar dando vueltas.
Cómo confirmarlo en modo de prueba
Puedes reproducir todo esto en modo de prueba antes de tocar una tarjeta real. Crea el PaymentIntent con las claves de test y con installments.enabled=true, un monto en MXN por arriba del mínimo, y móntalo en el Payment Element:
// Modo de prueba — reproduce MSI end to end
const paymentIntent = await stripe.paymentIntents.create({
amount: 240000, // MXN 2,400.00 — rebasa el mínimo de los 6 planes
currency: "mxn",
automatic_payment_methods: { enabled: true },
payment_method_options: {
card: {
installments: { enabled: true },
},
},
});
// Usa el client_secret en el Payment Element y confirma con una
// tarjeta de crédito de prueba de MX de un emisor soportado.
Con MXN 2,400.00 rebasas el mínimo de los seis planes, así que si tu tarjeta de prueba y su emisor los soportan, deberías ver la lista completa (menos los plazos que el emisor no ofrezca). Si bajas el monto a, digamos, MXN 500.00 (amount: 50000), verás cómo la lista se recorta a solo 3 meses — así confirmas en vivo que el filtrado por mínimo funciona. Consulta la referencia de aceptar un pago con meses sin intereses para los detalles del flujo de confirmación.
Una nota de disciplina: prueba primero la ausencia del selector con un monto chico, luego su presencia con un monto grande. Ver los dos comportamientos te da certeza de que el filtro por monto es lo que estás observando, no otra cosa.
Dónde encaja MSI en tu stack: hacerlo por API, webhooks y conciliación
MSI cambia qué se le ofrece al comprador y quién absorbe la comisión de financiamiento — el comercio. Lo que no cambia es tu plomería de webhooks ni de liquidación. El PaymentIntent que se confirma con meses sin intereses genera los mismos eventos que cualquier otro pago con tarjeta, y tú los procesas igual.
Eso significa que toda la ingeniería estándar de pasarela sigue aplicando sin cambios:
- Verifica la firma de los webhooks sobre los bytes crudos exactos. Si estás peleando con eso, tengo una guía dedicada en verificar la firma del webhook de Stripe.
- Sé idempotente: deduplica por
event.idy responde 2xx rápido. El comparativo de estrategias está en webhooks vs polling vs API. - Concilia: el evento verificado es tu única fuente de verdad, no lo que ves en el frontend.
Lo único conceptualmente nuevo con MSI es que el comercio come la comisión de financiamiento, así que tu modelo de márgenes tiene que contemplar ese costo por transacción. Pero tu handler de payment_intent.succeeded no distingue un pago MSI de uno de contado en su lógica de plomería — el diferimiento es entre el banco y el comprador, no algo que tú liquides.
Si además emites CFDI, el pago con MSI se factura como cualquier otro: automatiza el timbrado desde el evento verificado, como explico en facturación CFDI automática desde Stripe / Mercado Pago. Y si quieres el panorama completo de MSI por API en las tres pasarelas grandes de México, el hermano sobre meses sin intereses por API cubre Stripe, Conekta y Mercado Pago lado a lado.
La referencia oficial para todo lo de installments en México es docs.stripe.com/payments/mx-installments — verifica ahí los mínimos y emisores soportados cuando integres, porque esos detalles los actualiza Stripe y no conviene depender de la memoria.