Cómo integrar Flutter Stripe paso a paso en Android

- 👤 Andrés Cruz

🇺🇸 In english

Cómo integrar Flutter Stripe paso a paso en Android

Integrar Stripe en Flutter parece sencillo… hasta que Android te recuerda que vive en su propio universo de Gradle, Kotlin y temas Material. Cuando yo empecé, el plugin ni siquiera inicializaba y me lanzaba el típico error genérico de “The plugin failed to initialize”. Desde entonces he ido perfeccionando un proceso claro, ordenado y, sobre todo, probado.

Aquí te dejo la guía que me habría ahorrado horas de compilación fallida.

Veremos los pasos para instalar el paquete de flutter stripe en Flutter y poder emplear la API de Stripe dentro de la app en Flutter.

Instalamos la dependencia que vamos a usar mediante:

pubspec.yaml

dependencies:
 ***
 flutter_stripe:

La página oficial:

https://pub.dev/packages/flutter_stripe

Requisitos previos y versiones necesarias (Gradle, Kotlin y Android)

Antes de instalar ningún paquete, te recomiendo verificar que tu proyecto cumple con las versiones mínimas. En mi caso, la mayoría de errores vinieron simplemente porque el proyecto usaba Gradle 7.x y un Kotlin obsoleto.

Versión mínima de Android, Gradle y Kotlin

  • Stripe requiere:
  • Android 5.0 (API 21+)
  • Kotlin 1.8.0 o superior
  • Android Gradle Plugin 8+
  • Gradle 8.x
  • Uso de Theme.AppCompat o Theme.MaterialComponents

Cambio de versión del gradle-wrapper al nivel 8

Es importante acotar que el plugin requiere de varias configuraciones, específicamente emplea el nivel 8 de Gradle lo cual puede conllevar aplicar varias configuraciones adicionales.

Los pasos para inicializar el plugin anterior son los siguientes:

Use Android 5.0 (API level 21) and above.
Use Kotlin version 1.8.0 and above: example
Requires Android Gradle plugin 8 and higher
Using a descendant of Theme.AppCompat for your activity: example, example night theme
Using an up-to-date Android gradle build tools version: example and an up-to-date gradle version accordingly: example
Using FlutterFragmentActivity instead of FlutterActivity in MainActivity.kt: example
Add the following rules to your proguard-rules.pro file: example

Lo anterior, es un extractor de lo mostrado en la documentación oficial.

Puede ser que tu proyecto no tenga las últimas versiones de Gradle, así que, debes de verificar y corregir las mismas en caso de que sea necesario; para ello para ello, modificamos de:

android/gradle/wrapper/gradle-wrapper.properties

distributionUrl=https\://services.gradle.org/distributions/gradle-7.x.x-all.zip

A alguna versión 8 disponible, para ello, puedes ingresar a la página anterior:

https://services.gradle.org/distributions

Y copiar alguna de la versión 8, por ejemplo:

android/gradle/wrapper/gradle-wrapper.properties

distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip

Ajustes en Kotlin y Android Gradle Plugin (AGP)

También debemos de subir la versión de Kotlin:

android/settings.gradle

plugins {
   ***
   id "org.jetbrains.kotlin.android" version "1.8.0" apply false
}

En caso de que no configures algunos de los pasos anteriores, al implementar el widget para realizar el pago como veremos en el siguiente apartado, verás un error como el siguiente por pantalla:

Stripe exception: PlatformException(flutter_stripe initialization failed, The plugin failed to initialize: Your theme isn't set to use Theme.AppCompat or Theme.MaterialComponents. Please make sure you follow all the steps detailed inside the README: https://github.com/flutter-stripe/ flutter_stripe#android If you continue to have trouble, follow this discussion to get some support https://github.com/flutter-stripe/ flutter_stripe/discussions/538, null, null

Este error es genérico y va a dar generalmente si te equivocas en cualquiera de los pasos necesarios para configurar el plugin en un proyecto.

Otros cambios o configuraciones esenciales en Android

Agregamos las siguientes reglas:

android\app\proguard-rules.pro

-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivity$g
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider

Instalación del paquete flutter_stripe

El plugin oficial está aquí: https://pub.dev/packages/flutter_stripe

En tu pubspec.yaml:

dependencies:
 flutter_stripe:

Dependencia en pubspec.yaml

Asegúrate de usar la última versión, porque Stripe actualiza con frecuencia.

Configuración de Theme.AppCompat o MaterialComponents

Stripe necesita que tu tema Android derive de:

Theme.AppCompat

o

Theme.MaterialComponents

Una de las primeras veces que intenté integrar el plugin, recibí este mensaje:

“Your theme isn't set to use Theme.AppCompat or Theme.MaterialComponents…”

Era simplemente que mi tema heredaba de uno antiguo.

Migración obligatoria a FlutterFragmentActivity

En tu MainActivity.kt:

import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
}

Errores comunes al inicializar el plugin y cómo resolverlos

Los errores típicos que me encontré:

  • “flutter_stripe initialization failed”
  • “Your theme isn’t set to use Theme.AppCompat…”
  • “The plugin failed to initialize…”

Generalmente se deben a:

  • versiones antiguas de Gradle
  • Kotlin desactualizado
  • falta de FlutterFragmentActivity
  • tema incorrecto
  • reglas Proguard faltantes

Inicialización de Stripe en Flutter

Una vez la parte Android está sólida, ya puedes trabajar en Dart.

Configurar claves públicas

Usa tu publishable key:

Stripe.publishableKey = 'pk_test_xxxxxx';

Crear e inicializar Stripe en main.dart

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 Stripe.publishableKey = 'pk_test_xxxxxx';
 runApp(const MyApp());
}

Cargar la PaymentSheet

await Stripe.instance.initPaymentSheet(
 paymentSheetParameters: SetupPaymentSheetParameters(
   merchantDisplayName: 'Mi Tienda',
   paymentIntentClientSecret: clientSecret,
 ),
);

Ejemplo completo de integración de pago

Ahora, podemos crear el pago que es lo más importante, para ello se emplea la clase PaymentIntent desde tu backendÑ

Tu backend debe crear un PaymentIntent y devolver clientSecret.

Ejemplo típico (Node):

const paymentIntent = await stripe.paymentIntents.create({
 amount: 1000,
 currency: 'usd',
});

Mostrar PaymentSheet en la app

await Stripe.instance.presentPaymentSheet();

Confirmar pago y manejo de estados

try {
 await Stripe.instance.presentPaymentSheet();
 print("Pago completado");
} catch (e) {
 print("Error al pagar: $e");
}

Consejos basados en experiencia personal

Cómo evitar errores genéricos del plugin

A mí me pasó varias veces: el plugin mostraba errores sin pista clara.
Aprendí esto:

  • Revisa versiones antes de añadir Stripe.
  • Asegúrate de que tu tema Android es MaterialComponents.
  • Limpia el proyecto después de actualizar versiones:
flutter clean

Qué revisar antes de compilar

  • Gradle 8.x
  • Kotlin 1.8+
  • AGP 8+
  • Tema correcto
  • FlutterFragmentActivity

Aquí puedes ver la implementación de un pago con Stripe:

Future<Map<String, dynamic>> _createPaymentIntent(
    int price, String currency, BuildContext context) async {
  try {
    Map<String, dynamic> body = {
      // Amount must be in smaller unit of currency
      // so we have multiply it by 100
      'amount': (price * 100).toString(),
      'currency': currency,
      'payment_method_types[]': 'card',
    };
    var response = await http.post(
      Uri.parse('https://api.stripe.com/v1/payment_intents'),
      headers: {
        'Authorization': 'Bearer $stripeSecret',
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: body,
    );
    // print('Payment Intent Body: ${response.body.toString()}');
    return jsonDecode(response.body.toString());
  } catch (err) {
    showToastMessage(context, 'Stripe exception: ${err.toString()}');
  }
  return {};
}

makePayment(
    int price, String tagPack, String userToken, BuildContext context,
    [String coupon = '', String currency = "usd"]) async {
  // Create payment intent data
  final paymentIntent = await _createPaymentIntent(price, currency, context);

  //*** recurso pagado
  if (await _makePayment(context, price, paymentIntent)) {
      return;
    }

    // *** compras exitosas
    final appModel = Provider.of<AppModel>(context, listen: false);

    final data = // TODO CALL API?

    showToastMessage(
        context, LocaleKeys.thankYouForPurchasingTheCourseWeHopeYouLikeIt.tr());

    // espero un segundo y redirecciono
    Timer(
        const Duration(seconds: 1),
        () => Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) =>
                    pack_index_page.IndexPage(LocaleKeys.pack.tr()))));
  }
}

Para usarlo, lo podemos configurar en un botón:

                  MaterialButton(
                    color: Theme.of(context).primaryColor,
                    textTheme: Theme.of(context).buttonTheme.textTheme,
                    onPressed: () {
                      if (_isButtonEnabled) {
                        makePayment(
                          double.parse(
                            totalPrice,
                          ).toInt(),
                          tutorialPacksModel[0].tag,
                          appModel.userToken,
                          context,
                          couponUseCoupon,
                        );
                        Future.delayed(const Duration(seconds: 5), () {
                          setState(() {
                            _isButtonEnabled = true;
                          });
                        });
                      }
                      _isButtonEnabled = false;
                    },
                    child: Text(
                      LocaleKeys.payWithStripe.tr(),
                      style: Theme.of(
                        context,
                      ).textTheme.bodyLarge!.copyWith(color: Colors.white),
                    ),
                  ),
                ],
              ),

Cómo depurar fallas de inicialización

Si te aparece un error genérico, casi siempre es por versiones desalineadas.
En mi caso, actualizar Gradle y Kotlin resolvió el 90% de problemas.

Conclusión

Integrar Stripe en Flutter no es difícil, pero Android exige precisión quirúrgica con versiones, temas y configuraciones.

Con esta guía tienes un proceso probado y mucho más completo que los tutoriales habituales.

Si sigues los pasos exactamente, la PaymentSheet funcionará sin misterios ni errores genéricos.

Preguntas frecuentes (FAQ)

  • ¿Por qué Stripe falla al inicializarse?
    • Generalmente por tema incorrecto, Gradle desactualizado o falta de FlutterFragmentActivity.
  • ¿Qué versiones mínimas requiere el plugin?
    • Android 5.0+, Gradle 8+, Kotlin 1.8+, AGP 8+.
  • ¿Stripe funciona igual en Android e iOS?
    • Sí, pero iOS no requiere tantos ajustes de infraestructura.
  • ¿Cómo probar Stripe en modo Test?
    • Usa las claves test y tarjetas de prueba proporcionadas por Stripe.

Acepto recibir anuncios de interes sobre este Blog.

Veremos los pasos para instalar el paquete de flutter stripe en Flutter y poder emplear la API de Stripe dentro de la app en Flutter con Android.

| 👤 Andrés Cruz

🇺🇸 In english