Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
### PaymentSheet
* [Added][12011](https://github.com/stripe/stripe-android/pull/12011) Builder APIs for PaymentSheet appearance customization (Colors, Shapes, Typography, PrimaryButtonColors, and embedded row styles)
* [Added] opensCardScannerAutomatically configuration is now available.
* [CHANGED][12036](https://github.com/stripe/stripe-android/pull/12036) Updates the google places SDK from 3.5.0 to 5.0.0.

### Payments
* [Added][12014](https://github.com/stripe/stripe-android/pull/12014) `ConfirmationToken.PaymentMethodPreview` now includes structured `Card` field with detailed card information (brand, country, expiry, funding, last4, etc.)
Expand Down
2 changes: 1 addition & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ ext.versions = [
paparazzi : '2.0.0-alpha01',
poko : '0.18.2',
payButtonCompose : '0.1.3',
places : '3.5.0',
places : '5.0.0',
playServicesCoroutines : '1.10.1',
playServicesTfLite : '16.4.0',
playServicesWallet : '19.4.0',
Expand Down
3 changes: 2 additions & 1 deletion payments-ui-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<manifest xmlns:tools="http://schemas.android.com/tools">

<uses-sdk tools:overrideLibrary="com.google.android.libraries.places" />
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package com.stripe.android.ui.core.elements.autocomplete

import android.content.Context
import android.graphics.Typeface
import android.os.Build
import android.text.style.StyleSpan
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.AutocompleteSessionToken
import com.google.android.libraries.places.api.model.TypeFilter
import com.google.android.libraries.places.api.model.PlaceTypes
import com.google.android.libraries.places.api.net.FetchPlaceRequest
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsRequest
import com.google.android.libraries.places.api.net.PlacesClient
Expand Down Expand Up @@ -50,7 +51,7 @@ interface PlacesClientProxy {
initializer: () -> Unit = { Places.initialize(context, googlePlacesApiKey) },
errorReporter: ErrorReporter
): PlacesClientProxy {
return if (isPlacesAvailable()) {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isPlacesAvailable()) {
override ?: run {
initializer()
DefaultPlacesClientProxy(
Expand Down Expand Up @@ -102,8 +103,8 @@ internal class DefaultPlacesClientProxy(
.builder()
.setSessionToken(token)
.setQuery(query)
.setCountry(country)
.setTypeFilter(TypeFilter.ADDRESS)
.setCountries(listOf(country))
.setTypesFilter(listOf(PlaceTypes.ADDRESS))
.build()
).await()
errorReporter.report(ErrorReporter.SuccessEvent.PLACES_FIND_AUTOCOMPLETE_SUCCESS)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.stripe.android.ui.core.elements.autocomplete

import android.os.Build
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import com.google.android.libraries.places.api.model.AutocompletePrediction
Expand All @@ -21,6 +23,7 @@ import com.google.android.libraries.places.api.net.SearchByTextRequest
import com.google.android.libraries.places.api.net.SearchByTextResponse
import com.google.android.libraries.places.api.net.SearchNearbyRequest
import com.google.android.libraries.places.api.net.SearchNearbyResponse
import com.google.android.libraries.places.internal.zzmu
import com.google.common.truth.Truth.assertThat
import com.stripe.android.testing.CoroutineTestRule
import com.stripe.android.testing.FakeErrorReporter
Expand All @@ -34,6 +37,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
class PlacesClientProxyTest {
Expand All @@ -59,6 +63,25 @@ class PlacesClientProxyTest {
assertThat(client).isInstanceOf<DefaultPlacesClientProxy>()
}

@Test
@Config(sdk = [Build.VERSION_CODES.M])
fun `create returns unsupported client when run on old devices`() {
val client = PlacesClientProxy.create(
context = mock(),
googlePlacesApiKey = "abc123",
isPlacesAvailable = object : IsPlacesAvailable {
override fun invoke(): Boolean {
throw IllegalStateException("Shouldn't be called.")
}
},
clientFactory = { mock() },
initializer = { /* no-op */ },
errorReporter = FakeErrorReporter(),
)

assertThat(client).isInstanceOf<UnsupportedPlacesClientProxy>()
}

@Test
fun `create returns unsupported client when google places not available`() {
val client = PlacesClientProxy.create(
Expand Down Expand Up @@ -229,17 +252,75 @@ class PlacesClientProxyTest {
throw AssertionError("Not expected")
}

override fun zza(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little weird that these functions are obfuscated and the above aren't

p0: FindAutocompletePredictionsRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzb(
p0: FetchPlaceRequest,
p1: Int
): Task<*> {
p0: FetchPhotoRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzc(
p0: FetchResolvedPhotoUriRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzd(
p0: FindAutocompletePredictionsRequest,
p1: Int
): Task<*> {
p0: FetchPlaceRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zze(p0: LatLng): Task<*> {
throw AssertionError("Not expected")
}

override fun zzf(
p0: FindCurrentPlaceRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzh(
p0: IsOpenRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzi(
p0: SearchByTextRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzj(
p0: SearchNearbyRequest?,
p1: zzmu?
): Task<*>? {
throw AssertionError("Not expected")
}

override fun zzk() {
throw AssertionError("Not expected")
}

override fun zzl() {
throw AssertionError("Not expected")
}

override fun zzm() {
throw AssertionError("Not expected")
}
}
Expand Down
52 changes: 38 additions & 14 deletions paymentsheet-example/dependencies/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
| | | | | \--- androidx.collection:collection-ktx:1.3.0 -> 1.4.4 (c)
| | | | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0
| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| | | | | \--- com.google.guava:listenablefuture:1.0
| | | | | \--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
| | | | +--- androidx.core:core:1.1.0 -> 1.13.1
| | | | | +--- androidx.annotation:annotation:1.6.0 -> 1.9.1 (*)
| | | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
Expand Down Expand Up @@ -74,7 +74,7 @@
| | | | | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| | | | | | | | \--- androidx.tracing:tracing:1.0.0
| | | | | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| | | | | | | \--- com.google.guava:listenablefuture:1.0
| | | | | | | \--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
| | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.2.20 (*)
| | | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.10.1
| | | | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 (*)
Expand All @@ -99,7 +99,7 @@
| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.2.20 (*)
| | | | | \--- androidx.core:core-ktx:1.13.1 (c)
| | | | +--- androidx.interpolator:interpolator:1.0.0 (*)
| | | | \--- com.google.guava:listenablefuture:1.0
| | | | \--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
| | | +--- com.google.dagger:dagger:2.55
| | | | +--- jakarta.inject:jakarta.inject-api:2.0.1
| | | | +--- javax.inject:javax.inject:1
Expand Down Expand Up @@ -812,7 +812,7 @@
| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.24 (c)
| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22 -> 2.2.20 (c)
| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22 -> 1.9.24 (c)
| | | | +--- com.google.errorprone:error_prone_annotations:2.15.0
| | | | +--- com.google.errorprone:error_prone_annotations:2.15.0 -> 2.28.0
| | | | +--- androidx.activity:activity:1.8.0 -> 1.9.3 (*)
| | | | +--- androidx.annotation:annotation:1.2.0 -> 1.9.1 (*)
| | | | +--- androidx.appcompat:appcompat:1.6.1 -> 1.7.0 (*)
Expand Down Expand Up @@ -853,21 +853,21 @@
| | | | | +--- androidx.core:core:1.7.0 -> 1.13.1 (*)
| | | | | +--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | | | | +--- androidx.customview:customview-poolingcontainer:1.0.0 (*)
| | | | | \--- androidx.viewpager2:viewpager2:1.1.0-beta02 (c)
| | | | | \--- androidx.viewpager2:viewpager2:1.1.0-beta02 -> 1.1.0 (c)
| | | | +--- androidx.resourceinspection:resourceinspection-annotation:1.0.1 (*)
| | | | +--- androidx.transition:transition:1.5.0
| | | | | +--- androidx.annotation:annotation:1.2.0 -> 1.9.1 (*)
| | | | | +--- androidx.collection:collection:1.1.0 -> 1.4.4 (*)
| | | | | +--- androidx.core:core:1.13.0 -> 1.13.1 (*)
| | | | | \--- androidx.dynamicanimation:dynamicanimation:1.0.0 (*)
| | | | +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
| | | | \--- androidx.viewpager2:viewpager2:1.0.0 -> 1.1.0-beta02
| | | | \--- androidx.viewpager2:viewpager2:1.0.0 -> 1.1.0
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| | | | +--- androidx.annotation:annotation-experimental:1.3.0 -> 1.4.1 (*)
| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
| | | | +--- androidx.collection:collection:1.1.0 -> 1.4.4 (*)
| | | | +--- androidx.core:core:1.3.2 -> 1.13.1 (*)
| | | | +--- androidx.fragment:fragment:1.1.0 -> 1.8.6 (*)
| | | | \--- androidx.recyclerview:recyclerview:1.3.1-rc01 -> 1.3.2 (*)
| | | | \--- androidx.recyclerview:recyclerview:1.3.1 -> 1.3.2 (*)
| | | +--- androidx.lifecycle:lifecycle-livedata-ktx:2.8.7
| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.7 (*)
| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.7 (*)
Expand Down Expand Up @@ -1220,13 +1220,27 @@
| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 -> 1.9.24 (*)
+--- com.google.android.material:material:1.12.0 (*)
+--- com.squareup.okio:okio:3.16.4 (*)
+--- com.google.android.libraries.places:places:3.5.0
+--- com.google.android.libraries.places:places:5.0.0
| +--- androidx.appcompat:appcompat:1.0.0 -> 1.7.0 (*)
| +--- androidx.cardview:cardview:1.0.0 (*)
| +--- androidx.constraintlayout:constraintlayout:2.1.4 -> 2.2.0 (*)
| +--- androidx.exifinterface:exifinterface:1.0.0
| | \--- androidx.annotation:annotation:1.0.0 -> 1.9.1 (*)
| +--- androidx.fragment:fragment:1.1.0 -> 1.8.6 (*)
| +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 (*)
| +--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.8.7 (*)
| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
| +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 (*)
| +--- androidx.recyclerview:recyclerview:1.3.2 (*)
| +--- androidx.viewpager2:viewpager2:1.1.0 (*)
| +--- com.android.volley:volley:1.2.1
| +--- com.github.bumptech.glide:glide:4.11.0
| | +--- com.github.bumptech.glide:gifdecoder:4.11.0
| | | \--- androidx.annotation:annotation:1.0.0 -> 1.9.1 (*)
| | +--- com.github.bumptech.glide:disklrucache:4.11.0
| | +--- com.github.bumptech.glide:annotations:4.11.0
| | +--- androidx.fragment:fragment:1.0.0 -> 1.8.6 (*)
| | +--- androidx.vectordrawable:vectordrawable-animated:1.0.0 -> 1.1.0 (*)
| | \--- androidx.exifinterface:exifinterface:1.0.0 (*)
| +--- com.google.android.datatransport:transport-api:3.1.0
| | \--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| +--- com.google.android.datatransport:transport-backend-cct:3.2.0
Expand All @@ -1246,19 +1260,29 @@
| | | \--- com.google.firebase:firebase-encoders:17.0.0 (*)
| | \--- androidx.annotation:annotation:1.1.0 -> 1.9.1 (*)
| +--- com.google.android.datatransport:transport-runtime:3.2.0 (*)
| +--- com.google.android.gms:play-services-base:18.3.0 -> 18.5.0 (*)
| +--- com.google.android.gms:play-services-basement:18.3.0 -> 18.4.0 (*)
| +--- com.google.android.gms:play-services-base:18.5.0 (*)
| +--- com.google.android.gms:play-services-basement:18.4.0 (*)
| +--- com.google.android.gms:play-services-location:21.0.1
| | +--- com.google.android.gms:play-services-base:18.1.0 -> 18.5.0 (*)
| | +--- com.google.android.gms:play-services-basement:18.1.0 -> 18.4.0 (*)
| | \--- com.google.android.gms:play-services-tasks:18.0.2 -> 18.2.0 (*)
| +--- com.google.android.gms:play-services-maps:17.0.0 -> 18.0.2 (*)
| +--- com.google.android.gms:play-services-tasks:18.1.0 -> 18.2.0 (*)
| +--- com.google.android.gms:play-services-tasks:18.2.0 (*)
| +--- com.google.android.material:material:1.12.0 (*)
| +--- com.google.auto.value:auto-value-annotations:1.6.2
| +--- com.google.code.gson:gson:2.10
| +--- com.google.guava:guava:33.3.0-android
| | +--- com.google.guava:failureaccess:1.0.2
| | +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
| | +--- com.google.code.findbugs:jsr305:3.0.2
| | +--- org.checkerframework:checker-qual:3.43.0
| | \--- com.google.errorprone:error_prone_annotations:2.28.0
| +--- com.squareup.okhttp:okhttp:2.7.2
| | \--- com.squareup.okio:okio:1.6.0 -> 3.16.4 (*)
| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.0 -> 2.2.20 (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.1.0 -> 2.2.20 (*)
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 -> 1.10.1 (*)
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 -> 1.10.1 (*)
| \--- org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0 -> 1.10.1 (*)
+--- com.squareup.retrofit2:converter-kotlinx-serialization:2.11.0
| +--- com.squareup.retrofit2:retrofit:2.11.0
| | \--- com.squareup.okhttp3:okhttp:3.14.9 -> 4.12.0 (*)
Expand Down
1 change: 1 addition & 0 deletions paymentsheet-example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Copy link
Collaborator

@samer-stripe samer-stripe Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary for Google Places?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it's due to one of the transitive dependencies (glide) using runtime permissions. I assume it goes away/isn't necessary if we upgrade to 5.1.1, but that causes other issues.


<application
android:name=".ExampleApplication"
Expand Down
5 changes: 4 additions & 1 deletion paymentsheet/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-sdk tools:overrideLibrary="com.google.android.libraries.places" />

<application>
<activity
Expand Down
Loading