Question

How to Fix Incompatible Kotlin Metadata Version in Android When Updating Stripe

kotlinandroidfluttergradlestripe-payments

Question

I am upgrading Stripe in an Android project and ran into a Kotlin compatibility problem.

Originally, I was using Stripe version 14.1.1. I updated it to 16.8.0 because I need support for collecting additional payment information such as email and cardholder name, which is not available directly in com.stripe.android.view.CardMultilineWidget.

After upgrading, the build first failed because the newer Stripe version requires minSdkVersion 21 instead of 19, so I updated the app's minimum SDK version to 21.

After that, I started getting this build error:

caches/transforms-2/files-2.1/4541b0189187e0017d23bbb0afebd16a/jetified-kotlin-stdlib-common-1.5.0.jar!/META-INF/kotlin-stdlib-common.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.5.1, expected version is 1.1.15.

I tried changing the Gradle version, but the error still appears.

How can I fix this Kotlin version incompatibility in Android, and what is the correct way to collect email and cardholder name when using Stripe?

Short Answer

By the end of this page, you will understand what a Kotlin metadata version mismatch means, why it often appears after upgrading a library like Stripe, and how to fix it by aligning your Android project's Kotlin and Gradle setup. You will also learn the right way to collect email and cardholder name in a Stripe integration, instead of expecting CardMultilineWidget to provide every field automatically.

Concept

Kotlin libraries are compiled with a specific Kotlin compiler version. When your Android app depends on a library that was built with a newer Kotlin version than your project understands, the build can fail with a metadata version mismatch error.

In this case:

  • A dependency such as kotlin-stdlib-common-1.5.0 was compiled with Kotlin 1.5.x
  • Your project is still expecting something much older, shown by expected version is 1.1.15
  • That means your Android project is using an outdated Kotlin Gradle plugin or an old toolchain configuration

The important idea is this:

  • Libraries, Kotlin plugin, and Gradle tooling must be compatible with each other
  • Upgrading one dependency like Stripe may force you to upgrade Kotlin and sometimes Android Gradle Plugin too

This matters in real Android development because modern libraries often depend on newer language features, newer bytecode metadata, and newer Android requirements. If your project stays on an old Kotlin version, newer libraries cannot be understood by your compiler.

For the Stripe part of the question, CardMultilineWidget is mainly for card input. It does not automatically provide a complete checkout form with fields like customer email and cardholder name in every version or setup. In practice, developers usually collect those values using their own EditText fields and then pass them into Stripe objects such as PaymentMethodCreateParams.BillingDetails.

Mental Model

Think of Kotlin metadata like a document written in a newer file format.

  • Your Stripe library is like a file saved by a newer version of an app
  • Your project compiler is like an old app trying to open that file
  • The old app reads the file header and says, "I don't understand this format"

That is exactly what this error means.

For Stripe UI, think of CardMultilineWidget as a specialized card-entry component, not a full checkout form. It handles card details well, but fields like email and cardholder name are often your responsibility to collect separately and attach to the payment request.

Take Quiz

Syntax and Examples

1. Update Kotlin in your Android project

A common fix is to update the Kotlin version in your project-level Gradle configuration.

Example using build.gradle:

buildscript {
    ext.kotlin_version = '1.5.31'
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.4'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

If you use the plugins block:

plugins {
    id 'com.android.application' version '7.0.4' apply false
    id 'org.jetbrains.kotlin.android' version '1.5.31' apply false
}

The exact versions depend on your project, but the key is that your Kotlin plugin must be new enough to understand the Kotlin version used by Stripe and its dependencies.

2. Add Stripe dependency

dependencies {
    implementation 'com.stripe:stripe-android:16.8.0'
}

3. Collect email and cardholder name yourself

A typical Android layout might include your own inputs:

<EditText
    android:id="@+id/emailInput"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Email" />

<EditText
    android:id="@+id/nameInput"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Cardholder name" />

<com.stripe.android.view.CardMultilineWidget
    android:id="@+id/cardInputWidget"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

Then in Kotlin:

val cardWidget = findViewById<CardMultilineWidget>(R.id.cardInputWidget)
val emailInput = findViewById<EditText>(R.id.emailInput)
val nameInput = findViewById<EditText>(R.id.nameInput)

val cardParams = cardWidget.paymentMethodCard

if (cardParams != null) {
    val billingDetails = PaymentMethod.BillingDetails.Builder()
        .setEmail(emailInput.text.toString())
        .setName(nameInput.text.toString())
        .build()

    val paymentMethodParams = PaymentMethodCreateParams.create(
        card = cardParams,
        billingDetails = billingDetails
    )

    // Use paymentMethodParams with Stripe API flow
}

What this example shows

  • CardMultilineWidget collects card data
  • Your own text fields collect email and name
  • BillingDetails combines those values with the card details
  • This is the usual pattern in real Stripe integrations

Step by Step Execution

Consider this Kotlin code:

val cardParams = cardWidget.paymentMethodCard

if (cardParams != null) {
    val billingDetails = PaymentMethod.BillingDetails.Builder()
        .setEmail("alice@example.com")
        .setName("Alice Johnson")
        .build()

    val paymentMethodParams = PaymentMethodCreateParams.create(
        card = cardParams,
        billingDetails = billingDetails
    )
}

Here is what happens step by step:

  1. cardWidget.paymentMethodCard

    • Stripe reads the entered card information from the widget.
    • If the card input is incomplete or invalid, this may return null.
  2. if (cardParams != null)

    • This is a guard clause.
    • It prevents your code from creating payment parameters when card data is missing.
  3. PaymentMethod.BillingDetails.Builder()

    • A builder object is created to hold optional billing information.
  4. .setEmail("alice@example.com")

    • The customer email is added.
  5. .setName("Alice Johnson")

    • The cardholder name is added.
  6. .build()

    • The builder creates a BillingDetails object.
  7. PaymentMethodCreateParams.create(...)

    • Stripe combines the card data and billing details into one object.
    • This object is ready to send in your payment flow.

The main lesson is that the card widget and the customer identity fields are often separate pieces that you combine yourself.

Real World Use Cases

When this Kotlin compatibility issue appears

This kind of error commonly appears when:

  • Upgrading a library like Stripe, Retrofit, Firebase, or Room
  • Copying code into an older Android project
  • Opening a legacy codebase that still uses old Kotlin or Gradle versions
  • Mixing dependencies compiled with very different toolchain versions

When collecting email and cardholder name is useful

In real apps, these fields are commonly needed for:

  • Checkout screens: showing who is paying
  • Billing receipts: sending confirmation emails
  • Fraud prevention: attaching billing identity information
  • Saved payment methods: labeling cards with customer info
  • Customer support: linking payments to a user account

Example scenarios

  • An e-commerce app asks for email for receipts and cardholder name for billing
  • A subscription app collects email before creating a payment method
  • A donation app uses card details from Stripe and name/email from its own form fields
Take Quiz

Real Codebase Usage

In real Android codebases, developers usually handle this problem in two separate layers.

1. Tooling alignment

Teams keep these versions compatible:

  • Kotlin Gradle plugin
  • Android Gradle Plugin
  • Gradle wrapper
  • Third-party libraries like Stripe

A common maintenance pattern is:

  • upgrade one layer at a time
  • sync project
  • clean and rebuild
  • check release notes when a dependency raises minSdkVersion or Kotlin requirements

2. Payment form composition

Instead of relying on one UI widget to collect everything, production apps often:

  • use CardMultilineWidget or PaymentSheet for card/payment UI
  • use separate inputs for email, full name, phone, and address
  • validate fields before creating Stripe params
  • use guard clauses to stop submission if required values are missing

Example validation pattern:

val email = emailInput.text.toString().trim()
val name = nameInput.text.toString().trim()
val cardParams = cardWidget.paymentMethodCard

if (email.isEmpty()) return
if (name.isEmpty()) return
if (cardParams == null) return

This keeps payment code safer and easier to read.

Common project patterns

  • Guard clauses: exit early when fields are invalid
  • Validation helpers: reusable email/name validation functions
  • Configuration centralization: define Kotlin and plugin versions in one place
  • Error handling: show a clear message if card data or billing data is missing
  • Dependency consistency: avoid forcing incompatible transitive Kotlin libraries

Common Mistakes

1. Upgrading the library but not Kotlin

A very common mistake is changing Stripe to a newer version without upgrading the Kotlin plugin.

Broken setup idea:

implementation 'com.stripe:stripe-android:16.8.0'

But the project still uses a very old Kotlin version.

Fix

Update the Kotlin Gradle plugin to a compatible version.

2. Changing only Gradle wrapper and not Android/Kotlin plugins

Some developers update only one piece of the build system.

But Android builds depend on several connected tools:

  • Gradle wrapper
  • Android Gradle Plugin
  • Kotlin plugin

These must work together.

3. Expecting CardMultilineWidget to include all billing fields

Broken assumption:

val email = cardWidget.email

That API does not exist in this widget.

Fix

Create your own input fields and pass the values into BillingDetails.

4. Forgetting to clean and rebuild

Old cached artifacts can keep causing confusion.

Fix

Try:

  • Clean Project
  • Rebuild Project
  • Invalidate caches if needed

5. Ignoring minSdkVersion changes

A newer library may legitimately require a higher minimum Android version.

Fix

Read the library release notes and confirm the upgrade is acceptable for your app.

6. Sending empty billing data

Broken example:

val billingDetails = PaymentMethod.BillingDetails.Builder()
    .setEmail("")
    .setName("")
    .build()

Fix

Validate inputs before creating payment parameters.

if (email.isBlank() || name.isBlank()) {
    return
}

Comparisons

TopicOptionBest UseNotes
Stripe card inputCardMultilineWidgetBasic card entry UIGood for card details only; extra fields are usually separate
Stripe payment UIPaymentSheetModern checkout flowsHigher-level UI, often easier for complete payment flows
Billing data collectionBuilt into widgetOnly when supported by that componentNot every Stripe UI component collects all customer info
Billing data collectionYour own EditText fieldsFull control over form fieldsMost flexible approach
Kotlin build fixDowngrade StripeWhen you must keep old tooling temporarilyShort-term workaround, not ideal long term
Kotlin build fixUpgrade Kotlin/plugin/toolingBest long-term solutionUsually required for modern libraries

Upgrade vs downgrade

ApproachProsCons
Upgrade Kotlin and Android toolingSupports modern libraries and fixes compatibilityMay require more project changes
Downgrade Stripe to an older versionFaster temporary fixMisses new features and may keep old limitations

Card widget vs full form

ComponentHandles card number/expiry/CVCHandles emailHandles cardholder name
CardMultilineWidgetYesUsually noOften not enough by itself
Custom form + Stripe paramsYes, with widgetYesYes
Take Quiz

Cheat Sheet

Kotlin metadata error quick fix

  • Error like Module was compiled with an incompatible version of Kotlin usually means:
    • a dependency was compiled with a newer Kotlin version
    • your project uses an older Kotlin compiler/plugin

What to check

  1. Kotlin Gradle plugin version
  2. Android Gradle Plugin version
  3. Gradle wrapper version
  4. Library version compatibility
  5. minSdkVersion requirements

Typical fix

plugins {
    id 'org.jetbrains.kotlin.android' version '1.5.31' apply false
}

Then sync, clean, and rebuild.

Stripe data collection pattern

val cardParams = cardWidget.paymentMethodCard
val billingDetails = PaymentMethod.BillingDetails.Builder()
    .setEmail(email)
    .setName(name)
    .build()

val params = PaymentMethodCreateParams.create(cardParams!!, billingDetails)

Rules to remember

  • CardMultilineWidget is mainly for card entry
  • Collect email/name with your own fields when needed
  • Newer Stripe versions may require newer Android/Kotlin tooling
  • minSdkVersion changes are part of library upgrades

Safe workflow

  • Read library release notes
  • Upgrade tooling before or with dependency upgrades
  • Validate user input before creating payment params
  • Clean/rebuild after build configuration changes

FAQ

Why does Kotlin metadata version mismatch happen?

It happens when a dependency was compiled with a newer Kotlin version than the one your project is using.

Can I fix this by changing only the Gradle version?

Not always. You often need to update the Kotlin plugin too, and sometimes the Android Gradle Plugin and Gradle wrapper.

Does Stripe CardMultilineWidget collect email and cardholder name?

Not as a full checkout form. It mainly collects card details. Email and name are usually collected with your own input fields.

Do I have to increase minSdkVersion when upgrading Stripe?

If the newer Stripe version requires it, yes. You must meet the library's minimum Android version requirement.

Should I downgrade Stripe instead of upgrading Kotlin?

You can as a temporary workaround, but the better long-term fix is usually to align your project tooling with the newer library.

Where should I pass email and cardholder name in Stripe Android?

Usually through PaymentMethod.BillingDetails when creating PaymentMethodCreateParams.

What should I do after changing Kotlin or Gradle versions?

Sync the project, clean the build, and rebuild. If needed, invalidate caches.

Take Quiz

Mini Project

Description

Build a simple Android payment form that collects a customer's email, cardholder name, and card details. This project demonstrates the real-world pattern of using Stripe for card entry while keeping billing fields in your own UI. It also reinforces the idea that library upgrades may require compatible Kotlin and Android build configuration.

Goal

Create a form that validates email, name, and card details, then builds Stripe PaymentMethodCreateParams correctly.

Requirements

  1. Add input fields for email and cardholder name.
  2. Add a CardMultilineWidget for card entry.
  3. Validate that email and name are not blank.
  4. Ensure card data exists before creating Stripe parameters.
  5. Build PaymentMethodCreateParams with BillingDetails.
Take Quiz

Keep learning

Related questions

View more