Summary
When CreateIntentCallback.onCreateIntent returns a CreateIntentResult.Failure quickly — without suspending on any I/O — the sheet discards it. No error is shown, displayMessage never appears, and the sheet returns to the payment form as if nothing happened. The callback is never told the message was dropped.
Adding an artificial delay() before returning the Failure makes the message appear reliably. In our testing yield() and delay(1) were both too short; ~300ms was the threshold at which the message consistently rendered.
This only affects failures decided locally. Any path that awaits a network call before returning is slow enough that the problem never appears, which is likely why it isn't commonly hit. In our case we reject certain payment methods ourselves, based on details we can only inspect after the user has already chosen one — that decision needs no network call, which is exactly the fast path that gets dropped.
Impact: the user taps the primary button, returns to the sheet, and sees nothing at all. It reads as a broken app. We currently ship a 500ms delay purely to make our own error message visible, which is a timing guess against SDK internals rather than anything documented.
Code to reproduce
override suspend fun onCreateIntent(
paymentMethod: PaymentMethod,
shouldSavePaymentMethod: Boolean,
): CreateIntentResult {
// Decided locally — no suspension before returning.
if (isUnacceptable(paymentMethod)) {
return CreateIntentResult.Failure(
cause = IllegalStateException("Unsupported funding"),
displayMessage = "This card can't be used. Please choose another.",
)
}
// ...normal path: awaits a network call, and always displays correctly
}
The sheet is configured with PaymentSheet.IntentConfiguration, Mode.Setup(setupFutureUse = OnSession), and presented via presentWithIntentConfiguration.
Observed: no message, sheet returns to the form.
Expected: displayMessage is shown, as it is when the same Failure is returned after a network round trip.
Workaround: delay(500.milliseconds) immediately before returning the Failure.
Android version
Not tied to a specific OS version — it appears to be a race against the sheet's own transition animation, so a slower device makes it more likely. Reproduced consistently on our debug builds.
Impacted devices
Not device-specific.
Installation method
Gradle dependency.
Dependency Versions
kotlin: 2.4.10
stripe-android: 23.16.0
Android Gradle Plugin: 9.3.1
Gradle: 9.7.1
SDK classes
PaymentSheet, CreateIntentCallback, CreateIntentResult.Failure, PaymentSheet.IntentConfiguration
Other information
compileSdk 37, minSdk 28.
Either behaviour would be fine for us, as long as it is deterministic: queue the failure until the sheet is ready to display it, or surface a signal that it was dropped. As it stands, a correct integration can silently show the user nothing.
Summary
When
CreateIntentCallback.onCreateIntentreturns aCreateIntentResult.Failurequickly — without suspending on any I/O — the sheet discards it. No error is shown,displayMessagenever appears, and the sheet returns to the payment form as if nothing happened. The callback is never told the message was dropped.Adding an artificial
delay()before returning theFailuremakes the message appear reliably. In our testingyield()anddelay(1)were both too short; ~300ms was the threshold at which the message consistently rendered.This only affects failures decided locally. Any path that awaits a network call before returning is slow enough that the problem never appears, which is likely why it isn't commonly hit. In our case we reject certain payment methods ourselves, based on details we can only inspect after the user has already chosen one — that decision needs no network call, which is exactly the fast path that gets dropped.
Impact: the user taps the primary button, returns to the sheet, and sees nothing at all. It reads as a broken app. We currently ship a 500ms delay purely to make our own error message visible, which is a timing guess against SDK internals rather than anything documented.
Code to reproduce
The sheet is configured with
PaymentSheet.IntentConfiguration,Mode.Setup(setupFutureUse = OnSession), and presented viapresentWithIntentConfiguration.Observed: no message, sheet returns to the form.
Expected:
displayMessageis shown, as it is when the sameFailureis returned after a network round trip.Workaround:
delay(500.milliseconds)immediately before returning theFailure.Android version
Not tied to a specific OS version — it appears to be a race against the sheet's own transition animation, so a slower device makes it more likely. Reproduced consistently on our debug builds.
Impacted devices
Not device-specific.
Installation method
Gradle dependency.
Dependency Versions
SDK classes
PaymentSheet,CreateIntentCallback,CreateIntentResult.Failure,PaymentSheet.IntentConfigurationOther information
compileSdk37,minSdk28.Either behaviour would be fine for us, as long as it is deterministic: queue the failure until the sheet is ready to display it, or surface a signal that it was dropped. As it stands, a correct integration can silently show the user nothing.