Question
How to Fix Incompatible Kotlin Metadata Version in Android When Updating Stripe
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.0was compiled with Kotlin1.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.
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= />
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:
-
cardWidget.paymentMethodCard- Stripe reads the entered card information from the widget.
- If the card input is incomplete or invalid, this may return
null.
-
if (cardParams != null)- This is a guard clause.
- It prevents your code from creating payment parameters when card data is missing.
-
PaymentMethod.BillingDetails.Builder()- A builder object is created to hold optional billing information.
-
.setEmail("alice@example.com")- The customer email is added.
-
.setName("Alice Johnson")
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
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
minSdkVersionor Kotlin requirements
2. Payment form composition
Instead of relying on one UI widget to collect everything, production apps often:
- use
CardMultilineWidgetor 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
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
Comparisons
| Topic | Option | Best Use | Notes |
|---|---|---|---|
| Stripe card input | CardMultilineWidget | Basic card entry UI | Good for card details only; extra fields are usually separate |
| Stripe payment UI | PaymentSheet | Modern checkout flows | Higher-level UI, often easier for complete payment flows |
| Billing data collection | Built into widget | Only when supported by that component | Not every Stripe UI component collects all customer info |
| Billing data collection | Your own EditText fields | Full control over form fields | Most flexible approach |
| Kotlin build fix | Downgrade Stripe | When you must keep old tooling temporarily | Short-term workaround, not ideal long term |
Cheat Sheet
Kotlin metadata error quick fix
- Error like
Module was compiled with an incompatible version of Kotlinusually means:- a dependency was compiled with a newer Kotlin version
- your project uses an older Kotlin compiler/plugin
What to check
- Kotlin Gradle plugin version
- Android Gradle Plugin version
- Gradle wrapper version
- Library version compatibility
minSdkVersionrequirements
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
CardMultilineWidgetis mainly for card entry- Collect email/name with your own fields when needed
- Newer Stripe versions may require newer Android/Kotlin tooling
minSdkVersionchanges are part of library upgrades
Safe workflow
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.
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
- Add input fields for email and cardholder name.
- Add a
CardMultilineWidgetfor card entry. - Validate that email and name are not blank.
- Ensure card data exists before creating Stripe parameters.
- Build
PaymentMethodCreateParamswithBillingDetails.
Keep learning
Related questions
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.
Can You Extend a Data Class in Kotlin? Inheritance, Limits, and Better Alternatives
Learn why Kotlin data classes cannot be extended, what causes the component function clash, and which alternatives to use instead.
Difference Between List and Array in Kotlin
Learn the difference between List and Array in Kotlin, including mutability, size, APIs, performance, and when to use each one.