From 4b4ce136cf0e8cdd5dafdc105c9cd0fc7040cb2f Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Fri, 1 Jul 2022 11:12:07 -0700 Subject: [PATCH 1/8] createForCompose --- example/AndroidManifest.xml | 2 + example/res/values/strings.xml | 2 + .../activity/ComposeExampleActivity.kt | 7 +- .../GooglePayLauncherComposeActivity.kt | 149 ++++++++++++++++++ ...PayPaymentMethodLauncherComposeActivity.kt | 92 +++++++++++ .../example/activity/LauncherActivity.kt | 8 + .../googlepaylauncher/GooglePayLauncher.kt | 45 +++++- .../GooglePayPaymentMethodLauncher.kt | 57 +++++-- 8 files changed, 345 insertions(+), 17 deletions(-) create mode 100644 example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt create mode 100644 example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt diff --git a/example/AndroidManifest.xml b/example/AndroidManifest.xml index d5706ed2272..a60c3797091 100644 --- a/example/AndroidManifest.xml +++ b/example/AndroidManifest.xml @@ -50,7 +50,9 @@ + + diff --git a/example/res/values/strings.xml b/example/res/values/strings.xml index ce6667e5445..b1bee1245e6 100644 --- a/example/res/values/strings.xml +++ b/example/res/values/strings.xml @@ -13,8 +13,10 @@ Customer Payment Selection Customer Session Confirm payment with GooglePayLauncher + Confirm payment in Compose with GooglePayLauncher Test GooglePayLauncher Configurations Create payment method with GooglePayPaymentMethodLauncher + Create payment method in Compose with GooglePayPaymentMethodLauncher Payment Session Fragment Examples Create Payment Method diff --git a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt index e5b421d89e5..b71461f509d 100644 --- a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt @@ -33,6 +33,7 @@ import com.stripe.example.module.StripeIntentViewModel */ class ComposeExampleActivity : AppCompatActivity() { private val viewModel: StripeIntentViewModel by viewModels() + private var paymentLauncher: PaymentLauncher? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -46,7 +47,11 @@ class ComposeExampleActivity : AppCompatActivity() { val inProgress by viewModel.inProgress.observeAsState(false) val status by viewModel.status.observeAsState("") - createPaymentLauncher().let { paymentLauncher -> + if (paymentLauncher == null) { + paymentLauncher = createPaymentLauncher() + } + + paymentLauncher?.let { paymentLauncher -> Column(modifier = Modifier.padding(horizontal = 10.dp)) { if (inProgress) { LinearProgressIndicator( diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt new file mode 100644 index 00000000000..904004bc18e --- /dev/null +++ b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt @@ -0,0 +1,149 @@ +package com.stripe.example.activity + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.material.Scaffold +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import com.stripe.android.googlepaylauncher.GooglePayEnvironment +import com.stripe.android.googlepaylauncher.GooglePayLauncher +import kotlinx.coroutines.launch + +class GooglePayLauncherComposeActivity : StripeIntentActivity() { + private val googlePayConfig = GooglePayLauncher.Config( + environment = GooglePayEnvironment.Test, + merchantCountryCode = COUNTRY_CODE, + merchantName = "Widget Store", + billingAddressConfig = GooglePayLauncher.BillingAddressConfig( + isRequired = true, + format = GooglePayLauncher.BillingAddressConfig.Format.Full, + isPhoneNumberRequired = false + ), + existingPaymentMethodRequired = false + ) + + private var googlePayLauncher: GooglePayLauncher? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() + + var clientSecret by rememberSaveable { mutableStateOf("") } + var googlePayReady by rememberSaveable { mutableStateOf(false) } + var googlePayLaunched by rememberSaveable { mutableStateOf(false) } + var completed by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(Unit) { + if (clientSecret.isBlank()) { + viewModel.createPaymentIntent(COUNTRY_CODE).observe( + this@GooglePayLauncherComposeActivity + ) { result -> + result.fold( + onSuccess = { json -> + clientSecret = json.getString("secret") + }, + onFailure = { error -> + scope.launch { + scaffoldState.snackbarHostState.showSnackbar( + "Could not create PaymentIntent. ${error.message}" + ) + } + completed = true + } + ) + } + } + } + + if (googlePayLauncher == null) { + googlePayLauncher = GooglePayLauncher.createForCompose( + config = googlePayConfig, + readyCallback = { ready -> + if (!googlePayLaunched) { + googlePayReady = ready + + if (!googlePayReady) { + completed = true + } + + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") + } + } + }, + resultCallback = { result -> + when (result) { + GooglePayLauncher.Result.Completed -> { + "Successfully collected payment." + } + GooglePayLauncher.Result.Canceled -> { + "Customer cancelled Google Pay." + } + is GooglePayLauncher.Result.Failed -> { + "Google Pay failed. ${result.error.message}" + } + }.let { + scope.launch { + scaffoldState.snackbarHostState.showSnackbar(it) + completed = true + } + } + } + ) + } + + val readyToPay = googlePayReady && clientSecret.isNotBlank() && !completed + + Scaffold(scaffoldState = scaffoldState) { + Column(Modifier.fillMaxWidth()) { + if (!readyToPay && !completed) { + LinearProgressIndicator(Modifier.fillMaxWidth()) + } + + Spacer( + Modifier + .height(8.dp) + .fillMaxWidth() + ) + + AndroidView( + factory = { context -> + GooglePayButton(context) + }, + modifier = Modifier + .wrapContentWidth() + .clickable( + enabled = readyToPay, + onClick = { + googlePayLauncher?.presentForPaymentIntent(clientSecret) + googlePayLaunched = true + } + ) + ) + } + } + } + } + + private companion object { + private const val COUNTRY_CODE = "US" + } +} diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt new file mode 100644 index 00000000000..bd4d90a4528 --- /dev/null +++ b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt @@ -0,0 +1,92 @@ +package com.stripe.example.activity + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.Scaffold +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.stripe.android.googlepaylauncher.GooglePayEnvironment +import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncher +import kotlinx.coroutines.launch + +class GooglePayPaymentMethodLauncherComposeActivity : AppCompatActivity() { + private val googlePayConfig = GooglePayPaymentMethodLauncher.Config( + environment = GooglePayEnvironment.Test, + merchantCountryCode = "US", + merchantName = "Widget Store", + billingAddressConfig = GooglePayPaymentMethodLauncher.BillingAddressConfig( + isRequired = true, + format = GooglePayPaymentMethodLauncher.BillingAddressConfig.Format.Full, + isPhoneNumberRequired = false + ), + existingPaymentMethodRequired = false + ) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() + var enabled by remember { mutableStateOf(false) } + + val googlePayLauncher = GooglePayPaymentMethodLauncher.createForCompose( + config = googlePayConfig, + readyCallback = { ready -> + if (ready) { + enabled = true + } + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") + } + }, + resultCallback = { result -> + when (result) { + is GooglePayPaymentMethodLauncher.Result.Completed -> { + "Successfully created a PaymentMethod. ${result.paymentMethod}" + } + GooglePayPaymentMethodLauncher.Result.Canceled -> { + "Customer cancelled Google Pay." + } + is GooglePayPaymentMethodLauncher.Result.Failed -> { + "Google Pay failed: ${result.errorCode}: ${result.error.message}" + } + }.let { + scope.launch { + scaffoldState.snackbarHostState.showSnackbar(it) + enabled = false + } + } + } + ) + + Scaffold(scaffoldState = scaffoldState) { + AndroidView( + factory = { context -> + GooglePayButton(context) + }, + modifier = Modifier + .wrapContentWidth() + .clickable( + enabled = enabled, + onClick = { + googlePayLauncher.present( + currencyCode = "EUR", + amount = 2500 + ) + } + ) + ) + } + } + } +} diff --git a/example/src/main/java/com/stripe/example/activity/LauncherActivity.kt b/example/src/main/java/com/stripe/example/activity/LauncherActivity.kt index 83c90ccf279..18df78979c6 100644 --- a/example/src/main/java/com/stripe/example/activity/LauncherActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/LauncherActivity.kt @@ -68,6 +68,10 @@ class LauncherActivity : AppCompatActivity() { activity.getString(R.string.googlepaylauncher_example), GooglePayLauncherIntegrationActivity::class.java ), + Item( + activity.getString(R.string.googlepaycomposelauncher_example), + GooglePayLauncherComposeActivity::class.java + ), // This is for internal use so as not to confuse the user. // Item( // activity.getString(R.string.googlepayplayground_example), @@ -77,6 +81,10 @@ class LauncherActivity : AppCompatActivity() { activity.getString(R.string.googlepaypaymentmethodlauncher_example), GooglePayPaymentMethodLauncherIntegrationActivity::class.java ), + Item( + activity.getString(R.string.googlepaypaymentmethodcomposelauncher_example), + GooglePayPaymentMethodLauncherComposeActivity::class.java + ), Item( activity.getString(R.string.launch_confirm_pm_sepa_debit), ConfirmSepaDebitActivity::class.java diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt index 5123bae901c..f1833dcf797 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt @@ -2,7 +2,11 @@ package com.stripe.android.googlepaylauncher import android.os.Parcelable import androidx.activity.ComponentActivity +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.stripe.android.PaymentConfiguration @@ -276,7 +280,46 @@ class GooglePayLauncher internal constructor( fun onResult(result: Result) } - internal companion object { + companion object { internal const val PRODUCT_USAGE = "GooglePayLauncher" + + /** + * Create a [GooglePayLauncher] used for Jetpack Compose. + * + * This API uses Compose specific API [rememberLauncherForActivityResult] to register a + * [ActivityResultLauncher] into current activity, it should be called as part of Compose + * initialization path. + */ + @Composable + fun createForCompose( + config: Config, + readyCallback: ReadyCallback, + resultCallback: ResultCallback + ): GooglePayLauncher { + val repository = DefaultGooglePayRepository( + context = LocalContext.current, + environment = config.environment, + billingAddressParameters = config.billingAddressConfig.convert(), + existingPaymentMethodRequired = config.existingPaymentMethodRequired, + allowCreditCards = config.allowCreditCards + ) + + return GooglePayLauncher( + lifecycleScope = LocalLifecycleOwner.current.lifecycleScope, + config = config, + readyCallback = readyCallback, + activityResultLauncher = rememberLauncherForActivityResult( + GooglePayLauncherContract(), + resultCallback::onResult + ), + googlePayRepositoryFactory = { repository }, + PaymentAnalyticsRequestFactory( + LocalContext.current, + PaymentConfiguration.getInstance(LocalContext.current).publishableKey, + setOf(PRODUCT_USAGE) + ), + DefaultAnalyticsRequestExecutor() + ) + } } } diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt index 8b0cf1575c0..f95a9bc8c81 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt @@ -3,9 +3,12 @@ package com.stripe.android.googlepaylauncher import android.content.Context import android.os.Parcelable import androidx.activity.ComponentActivity -import androidx.activity.result.ActivityResultCaller +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IntDef +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.stripe.android.BuildConfig @@ -129,10 +132,13 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( ) : this( activity, activity.lifecycleScope, - activity, + activity.registerForActivityResult( + GooglePayPaymentMethodLauncherContract() + ) { + resultCallback.onResult(it) + }, config, - readyCallback, - resultCallback + readyCallback ) /** @@ -154,28 +160,26 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( ) : this( fragment.requireContext(), fragment.viewLifecycleOwner.lifecycleScope, - fragment, + fragment.registerForActivityResult( + GooglePayPaymentMethodLauncherContract() + ) { + resultCallback.onResult(it) + }, config, - readyCallback, - resultCallback + readyCallback ) private constructor ( context: Context, lifecycleScope: CoroutineScope, - activityResultCaller: ActivityResultCaller, + activityResultLauncher: ActivityResultLauncher, config: Config, - readyCallback: ReadyCallback, - resultCallback: ResultCallback + readyCallback: ReadyCallback ) : this( lifecycleScope, config, readyCallback, - activityResultCaller.registerForActivityResult( - GooglePayPaymentMethodLauncherContract() - ) { - resultCallback.onResult(it) - }, + activityResultLauncher, false, context, googlePayRepositoryFactory = { @@ -376,5 +380,28 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( // Error executing a network call const val NETWORK_ERROR = 3 + + /** + * Create a [GooglePayPaymentMethodLauncher] used for Jetpack Compose. + * + * This API uses Compose specific API [rememberLauncherForActivityResult] to register a + * [ActivityResultLauncher] into current activity, it should be called as part of Compose + * initialization path. + */ + @Composable + fun createForCompose( + config: Config, + readyCallback: ReadyCallback, + resultCallback: ResultCallback + ) = GooglePayPaymentMethodLauncher( + context = LocalContext.current, + lifecycleScope = LocalLifecycleOwner.current.lifecycleScope, + activityResultLauncher = rememberLauncherForActivityResult( + GooglePayPaymentMethodLauncherContract(), + resultCallback::onResult + ), + config = config, + readyCallback = readyCallback, + ) } } From c56d3e3ed1a2b60758331897b099e0e130d68629 Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 5 Jul 2022 16:53:40 -0700 Subject: [PATCH 2/8] rememberLauncher --- build.gradle | 2 +- .../activity/ComposeExampleActivity.kt | 58 +++++++------- .../GooglePayLauncherComposeActivity.kt | 76 +++++++++---------- ...PayPaymentMethodLauncherComposeActivity.kt | 2 +- payments-core/api/payments-core.api | 8 ++ payments-core/build.gradle | 2 +- .../googlepaylauncher/GooglePayLauncher.kt | 56 ++++++++------ .../GooglePayPaymentMethodLauncher.kt | 30 +++++--- .../paymentlauncher/PaymentLauncher.kt | 37 +++++++++ 9 files changed, 163 insertions(+), 108 deletions(-) diff --git a/build.gradle b/build.gradle index c4a244e0abb..e0434da71d4 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ ext { buildToolsVersion = "30.0.3" compileSdkVersion = 32 - androidxActivityVersion = '1.4.0' + androidxActivityVersion = '1.5.0' androidxAnnotationVersion = '1.4.0' androidxAppcompatVersion = '1.4.2' androidxArchCoreVersion = '2.1.0' diff --git a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt index b71461f509d..567bdb7b4fb 100644 --- a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt @@ -15,6 +15,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -33,7 +34,6 @@ import com.stripe.example.module.StripeIntentViewModel */ class ComposeExampleActivity : AppCompatActivity() { private val viewModel: StripeIntentViewModel by viewModels() - private var paymentLauncher: PaymentLauncher? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -46,37 +46,32 @@ class ComposeExampleActivity : AppCompatActivity() { fun ComposeScreen() { val inProgress by viewModel.inProgress.observeAsState(false) val status by viewModel.status.observeAsState("") + val paymentLauncher = createPaymentLauncher() - if (paymentLauncher == null) { - paymentLauncher = createPaymentLauncher() - } - - paymentLauncher?.let { paymentLauncher -> - Column(modifier = Modifier.padding(horizontal = 10.dp)) { - if (inProgress) { - LinearProgressIndicator( - modifier = Modifier.fillMaxWidth() - ) - } - Text( - stringResource(R.string.payment_auth_intro), - modifier = Modifier.padding(vertical = 5.dp) - ) - ConfirmButton( - params = confirmParams3ds1, - buttonName = R.string.confirm_with_3ds1_button, - paymentLauncher = paymentLauncher, - inProgress = inProgress + Column(modifier = Modifier.padding(horizontal = 10.dp)) { + if (inProgress) { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth() ) - ConfirmButton( - params = confirmParams3ds2, - buttonName = R.string.confirm_with_3ds2_button, - paymentLauncher = paymentLauncher, - inProgress = inProgress - ) - Divider(modifier = Modifier.padding(vertical = 5.dp)) - Text(text = status) } + Text( + stringResource(R.string.payment_auth_intro), + modifier = Modifier.padding(vertical = 5.dp) + ) + ConfirmButton( + params = confirmParams3ds1, + buttonName = R.string.confirm_with_3ds1_button, + paymentLauncher = paymentLauncher, + inProgress = inProgress + ) + ConfirmButton( + params = confirmParams3ds2, + buttonName = R.string.confirm_with_3ds2_button, + paymentLauncher = paymentLauncher, + inProgress = inProgress + ) + Divider(modifier = Modifier.padding(vertical = 5.dp)) + Text(text = status) } } @@ -85,8 +80,9 @@ class ComposeExampleActivity : AppCompatActivity() { */ @Composable fun createPaymentLauncher(): PaymentLauncher { - val settings = Settings(LocalContext.current) - return PaymentLauncher.createForCompose( + val context = LocalContext.current + val settings = remember { Settings(context) } + return PaymentLauncher.rememberLauncher( publishableKey = settings.publishableKey, stripeAccountId = settings.stripeAccountId ) { diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt index 904004bc18e..0ac92dc6adc 100644 --- a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt @@ -37,8 +37,6 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { existingPaymentMethodRequired = false ) - private var googlePayLauncher: GooglePayLauncher? = null - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -73,56 +71,50 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { } } - if (googlePayLauncher == null) { - googlePayLauncher = GooglePayLauncher.createForCompose( - config = googlePayConfig, - readyCallback = { ready -> - if (!googlePayLaunched) { - googlePayReady = ready + val googlePayLauncher = GooglePayLauncher.rememberLauncher( + config = googlePayConfig, + readyCallback = { ready -> + if (!googlePayLaunched) { + googlePayReady = ready - if (!googlePayReady) { - completed = true - } + if (!googlePayReady) { + completed = true + } - scope.launch { - scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") - } + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") } - }, - resultCallback = { result -> - when (result) { - GooglePayLauncher.Result.Completed -> { - "Successfully collected payment." - } - GooglePayLauncher.Result.Canceled -> { - "Customer cancelled Google Pay." - } - is GooglePayLauncher.Result.Failed -> { - "Google Pay failed. ${result.error.message}" - } - }.let { - scope.launch { - scaffoldState.snackbarHostState.showSnackbar(it) - completed = true - } + } + }, + resultCallback = { result -> + when (result) { + GooglePayLauncher.Result.Completed -> { + "Successfully collected payment." + } + GooglePayLauncher.Result.Canceled -> { + "Customer cancelled Google Pay." + } + is GooglePayLauncher.Result.Failed -> { + "Google Pay failed. ${result.error.message}" + } + }.let { + scope.launch { + scaffoldState.snackbarHostState.showSnackbar(it) + completed = true } } - ) - } + } + ) - val readyToPay = googlePayReady && clientSecret.isNotBlank() && !completed + val readyToPay = googlePayReady && clientSecret.isNotBlank() Scaffold(scaffoldState = scaffoldState) { Column(Modifier.fillMaxWidth()) { - if (!readyToPay && !completed) { + if (!readyToPay) { LinearProgressIndicator(Modifier.fillMaxWidth()) } - Spacer( - Modifier - .height(8.dp) - .fillMaxWidth() - ) + Spacer(Modifier.height(8.dp).fillMaxWidth()) AndroidView( factory = { context -> @@ -131,9 +123,9 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { modifier = Modifier .wrapContentWidth() .clickable( - enabled = readyToPay, + enabled = readyToPay && !completed, onClick = { - googlePayLauncher?.presentForPaymentIntent(clientSecret) + googlePayLauncher.presentForPaymentIntent(clientSecret) googlePayLaunched = true } ) diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt index bd4d90a4528..9b79090417a 100644 --- a/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt @@ -39,7 +39,7 @@ class GooglePayPaymentMethodLauncherComposeActivity : AppCompatActivity() { val scope = rememberCoroutineScope() var enabled by remember { mutableStateOf(false) } - val googlePayLauncher = GooglePayPaymentMethodLauncher.createForCompose( + val googlePayLauncher = GooglePayPaymentMethodLauncher.rememberLauncher( config = googlePayConfig, readyCallback = { ready -> if (ready) { diff --git a/payments-core/api/payments-core.api b/payments-core/api/payments-core.api index e1fc356597d..1eb21601fae 100644 --- a/payments-core/api/payments-core.api +++ b/payments-core/api/payments-core.api @@ -1106,6 +1106,7 @@ public final class com/stripe/android/googlepaylauncher/GooglePayEnvironment : j public final class com/stripe/android/googlepaylauncher/GooglePayLauncher { public static final field $stable I + public static final field Companion Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$Companion; public fun (Landroidx/activity/ComponentActivity;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ResultCallback;)V public fun (Landroidx/fragment/app/Fragment;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ResultCallback;)V public final fun presentForPaymentIntent (Ljava/lang/String;)V @@ -1144,6 +1145,10 @@ public final class com/stripe/android/googlepaylauncher/GooglePayLauncher$Billin public static fun values ()[Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$BillingAddressConfig$Format; } +public final class com/stripe/android/googlepaylauncher/GooglePayLauncher$Companion { + public final fun rememberLauncher (Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayLauncher$ResultCallback;Landroidx/compose/runtime/Composer;I)Lcom/stripe/android/googlepaylauncher/GooglePayLauncher; +} + public final class com/stripe/android/googlepaylauncher/GooglePayLauncher$Config : android/os/Parcelable { public static final field $stable I public static final field CREATOR Landroid/os/Parcelable$Creator; @@ -1348,6 +1353,7 @@ public final class com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLa public static final field DEVELOPER_ERROR I public static final field INTERNAL_ERROR I public static final field NETWORK_ERROR I + public synthetic fun (Landroid/content/Context;Lkotlinx/coroutines/CoroutineScope;Landroidx/activity/result/ActivityResultLauncher;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ReadyCallback;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Landroidx/activity/ComponentActivity;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ResultCallback;)V public fun (Landroidx/fragment/app/Fragment;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ResultCallback;)V public final fun present (Ljava/lang/String;)V @@ -1395,6 +1401,7 @@ public final class com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLa } public final class com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Companion { + public final fun rememberLauncher (Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Config;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ReadyCallback;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$ResultCallback;Landroidx/compose/runtime/Composer;I)Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher; } public final class com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher$Config : android/os/Parcelable { @@ -6868,6 +6875,7 @@ public final class com/stripe/android/payments/paymentlauncher/PaymentLauncher$C public static synthetic fun create$default (Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$Companion;Landroidx/activity/ComponentActivity;Ljava/lang/String;Ljava/lang/String;Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$PaymentResultCallback;ILjava/lang/Object;)Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher; public static synthetic fun create$default (Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$Companion;Landroidx/fragment/app/Fragment;Ljava/lang/String;Ljava/lang/String;Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$PaymentResultCallback;ILjava/lang/Object;)Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher; public final fun createForCompose (Ljava/lang/String;Ljava/lang/String;Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$PaymentResultCallback;Landroidx/compose/runtime/Composer;II)Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher; + public final fun rememberLauncher (Ljava/lang/String;Ljava/lang/String;Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher$PaymentResultCallback;Landroidx/compose/runtime/Composer;II)Lcom/stripe/android/payments/paymentlauncher/PaymentLauncher; } public abstract interface class com/stripe/android/payments/paymentlauncher/PaymentLauncher$PaymentResultCallback { diff --git a/payments-core/build.gradle b/payments-core/build.gradle index 3b3dd5f2d8b..427065ac53b 100644 --- a/payments-core/build.gradle +++ b/payments-core/build.gradle @@ -19,12 +19,12 @@ dependencies { implementation "androidx.fragment:fragment-ktx:$androidxFragmentVersion" implementation "androidx.constraintlayout:constraintlayout:$androidxConstraintlayoutVersion" implementation "androidx.activity:activity-ktx:$androidxActivityVersion" + implementation "androidx.activity:activity-compose:$androidxActivityVersion" implementation "com.google.android.gms:play-services-wallet:$playServicesWalletVersion" implementation "com.google.android.material:material:$materialVersion" implementation "com.google.dagger:dagger:$daggerVersion" kapt "com.google.dagger:dagger-compiler:$daggerVersion" - implementation "androidx.activity:activity-compose:$androidxActivityVersion" implementation 'com.google.android.instantapps:instantapps:1.1.0' // For instructions on replacing the BouncyCastle dependency used by the 3DS2 SDK, see diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt index f1833dcf797..7ab1a9b465d 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt @@ -5,6 +5,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment @@ -289,37 +290,46 @@ class GooglePayLauncher internal constructor( * This API uses Compose specific API [rememberLauncherForActivityResult] to register a * [ActivityResultLauncher] into current activity, it should be called as part of Compose * initialization path. + * The GooglePayLauncher created is remembered across recompositions. Recomposition will + * always return the value produced by composition. */ @Composable - fun createForCompose( + fun rememberLauncher( config: Config, readyCallback: ReadyCallback, resultCallback: ResultCallback ): GooglePayLauncher { - val repository = DefaultGooglePayRepository( - context = LocalContext.current, - environment = config.environment, - billingAddressParameters = config.billingAddressConfig.convert(), - existingPaymentMethodRequired = config.existingPaymentMethodRequired, - allowCreditCards = config.allowCreditCards + val activityResultLauncher = rememberLauncherForActivityResult( + GooglePayLauncherContract(), + resultCallback::onResult ) - return GooglePayLauncher( - lifecycleScope = LocalLifecycleOwner.current.lifecycleScope, - config = config, - readyCallback = readyCallback, - activityResultLauncher = rememberLauncherForActivityResult( - GooglePayLauncherContract(), - resultCallback::onResult - ), - googlePayRepositoryFactory = { repository }, - PaymentAnalyticsRequestFactory( - LocalContext.current, - PaymentConfiguration.getInstance(LocalContext.current).publishableKey, - setOf(PRODUCT_USAGE) - ), - DefaultAnalyticsRequestExecutor() - ) + val context = LocalContext.current + val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope + + return remember { + GooglePayLauncher( + lifecycleScope = lifecycleScope, + config = config, + readyCallback = readyCallback, + activityResultLauncher = activityResultLauncher, + googlePayRepositoryFactory = { + DefaultGooglePayRepository( + context = context, + environment = config.environment, + billingAddressParameters = config.billingAddressConfig.convert(), + existingPaymentMethodRequired = config.existingPaymentMethodRequired, + allowCreditCards = config.allowCreditCards + ) + }, + PaymentAnalyticsRequestFactory( + context, + PaymentConfiguration.getInstance(context).publishableKey, + setOf(PRODUCT_USAGE) + ), + DefaultAnalyticsRequestExecutor() + ) + } } } } diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt index f95a9bc8c81..e8ed0ad9451 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt @@ -7,6 +7,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IntDef import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment @@ -387,21 +388,32 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( * This API uses Compose specific API [rememberLauncherForActivityResult] to register a * [ActivityResultLauncher] into current activity, it should be called as part of Compose * initialization path. + * The GooglePayPaymentMethodLauncher created is remembered across recompositions. + * Recomposition will always return the value produced by composition. */ @Composable - fun createForCompose( + fun rememberLauncher( config: Config, readyCallback: ReadyCallback, resultCallback: ResultCallback - ) = GooglePayPaymentMethodLauncher( - context = LocalContext.current, - lifecycleScope = LocalLifecycleOwner.current.lifecycleScope, - activityResultLauncher = rememberLauncherForActivityResult( + ): GooglePayPaymentMethodLauncher { + val activityResultLauncher = rememberLauncherForActivityResult( GooglePayPaymentMethodLauncherContract(), resultCallback::onResult - ), - config = config, - readyCallback = readyCallback, - ) + ) + + val context = LocalContext.current + val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope + + return remember { + GooglePayPaymentMethodLauncher( + context = context, + lifecycleScope = lifecycleScope, + activityResultLauncher = activityResultLauncher, + config = config, + readyCallback = readyCallback, + ) + } + } } } diff --git a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt index ecaf4f307c7..870f5e5c8de 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt @@ -4,6 +4,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.fragment.app.Fragment import com.stripe.android.model.ConfirmPaymentIntentParams @@ -75,7 +76,14 @@ interface PaymentLauncher { * This API uses Compose specific API [rememberLauncherForActivityResult] to register a * [ActivityResultLauncher] into current activity, it should be called as part of Compose * initialization path. + * This method creates a new PaymentLauncher object every time it is called, even during + * recompositions. */ + @Deprecated( + "This method creates a new PaymentLauncher object every time it is called, even" + + "during recompositions.", + replaceWith = ReplaceWith("PaymentLauncher.rememberLauncher") + ) @Composable fun createForCompose( publishableKey: String, @@ -88,5 +96,34 @@ interface PaymentLauncher { callback::onPaymentResult ) ).create(publishableKey, stripeAccountId) + + /** + * Create a [PaymentLauncher] used for Jetpack Compose. + * + * This API uses Compose specific API [rememberLauncherForActivityResult] to register a + * [ActivityResultLauncher] into current activity, it should be called as part of Compose + * initialization path. + * The PaymentLauncher created is remembered across recompositions. Recomposition will + * always return the value produced by composition. + */ + @Composable + fun rememberLauncher( + publishableKey: String, + stripeAccountId: String? = null, + callback: PaymentResultCallback + ): PaymentLauncher { + val activityResultLauncher = rememberLauncherForActivityResult( + PaymentLauncherContract(), + callback::onPaymentResult + ) + val context = LocalContext.current + + return remember { + PaymentLauncherFactory( + context, + activityResultLauncher + ).create(publishableKey, stripeAccountId) + } + } } } From 4f6169b5f37714194e49dc61d7ceb773ccbe1e09 Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 5 Jul 2022 16:57:27 -0700 Subject: [PATCH 3/8] ReplaceWith --- .../stripe/android/payments/paymentlauncher/PaymentLauncher.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt index 870f5e5c8de..79bcabb2537 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt @@ -82,7 +82,7 @@ interface PaymentLauncher { @Deprecated( "This method creates a new PaymentLauncher object every time it is called, even" + "during recompositions.", - replaceWith = ReplaceWith("PaymentLauncher.rememberLauncher") + replaceWith = ReplaceWith("PaymentLauncher.rememberLauncher(publishableKey, stripeAccountId, callback)") ) @Composable fun createForCompose( From da9fed434c520463aad3c9f23da2ce084e9512ef Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Mon, 11 Jul 2022 22:56:32 -0700 Subject: [PATCH 4/8] Example --- .../activity/GooglePayLauncherComposeActivity.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt index 0ac92dc6adc..5f43a8abbcd 100644 --- a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt @@ -45,8 +45,7 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { val scope = rememberCoroutineScope() var clientSecret by rememberSaveable { mutableStateOf("") } - var googlePayReady by rememberSaveable { mutableStateOf(false) } - var googlePayLaunched by rememberSaveable { mutableStateOf(false) } + var googlePayReady by rememberSaveable { mutableStateOf(null) } var completed by rememberSaveable { mutableStateOf(false) } LaunchedEffect(Unit) { @@ -74,10 +73,10 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { val googlePayLauncher = GooglePayLauncher.rememberLauncher( config = googlePayConfig, readyCallback = { ready -> - if (!googlePayLaunched) { + if (googlePayReady == null) { googlePayReady = ready - if (!googlePayReady) { + if (!ready) { completed = true } @@ -106,11 +105,9 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { } ) - val readyToPay = googlePayReady && clientSecret.isNotBlank() - Scaffold(scaffoldState = scaffoldState) { Column(Modifier.fillMaxWidth()) { - if (!readyToPay) { + if (googlePayReady == null || clientSecret.isBlank()) { LinearProgressIndicator(Modifier.fillMaxWidth()) } @@ -123,10 +120,10 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { modifier = Modifier .wrapContentWidth() .clickable( - enabled = readyToPay && !completed, + enabled = googlePayReady == true && + clientSecret.isNotBlank() && !completed, onClick = { googlePayLauncher.presentForPaymentIntent(clientSecret) - googlePayLaunched = true } ) ) From 43ce04582a61cd72be054c450188306804442f33 Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 12 Jul 2022 12:19:40 -0700 Subject: [PATCH 5/8] changelog --- CHANGELOG.md | 4 ++++ payments-core/build.gradle | 11 +++++------ .../android/googlepaylauncher/GooglePayLauncher.kt | 5 ++--- .../GooglePayPaymentMethodLauncher.kt | 7 +++---- .../payments/paymentlauncher/PaymentLauncher.kt | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ad0719f1b0..a74fd8e7620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## X.X.X +### Payments +* [ADDED][5274](https://github.com/stripe/stripe-android/pull/5274) Create `rememberLauncher` method enabling usage of `GooglePayLauncher`, `GooglePayPaymentMethodLauncher` and `PaymentLauncher` in Compose. +* [ADDED][5274](https://github.com/stripe/stripe-android/pull/5274) Deprecate `PaymentLauncher.createForCompose` in favor of `PaymentLauncher.rememberLauncher`. + ## 20.7.0 - 2022-07-06 * This release adds additional support for Afterpay/Clearpay in PaymentSheet. diff --git a/payments-core/build.gradle b/payments-core/build.gradle index 427065ac53b..a6f59f50b68 100644 --- a/payments-core/build.gradle +++ b/payments-core/build.gradle @@ -23,22 +23,20 @@ dependencies { implementation "com.google.android.gms:play-services-wallet:$playServicesWalletVersion" implementation "com.google.android.material:material:$materialVersion" implementation "com.google.dagger:dagger:$daggerVersion" - kapt "com.google.dagger:dagger-compiler:$daggerVersion" - + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion" implementation 'com.google.android.instantapps:instantapps:1.1.0' - // For instructions on replacing the BouncyCastle dependency used by the 3DS2 SDK, see // https://github.com/stripe/stripe-android/issues/3173#issuecomment-785176608 implementation "com.stripe:stripe-3ds2-android:6.1.5" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion" + kapt "com.google.dagger:dagger-compiler:$daggerVersion" javadocDeps "androidx.annotation:annotation:$androidxAnnotationVersion" javadocDeps "androidx.appcompat:appcompat:$androidxAppcompatVersion" javadocDeps "com.google.android.material:material:$materialVersion" - testImplementation project(':financial-connections') + testImplementation project(':financial-connections') testImplementation "junit:junit:$junitVersion" testImplementation "org.mockito:mockito-core:$mockitoCoreVersion" testImplementation "org.robolectric:robolectric:$robolectricVersion" @@ -56,6 +54,7 @@ dependencies { androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "androidx.test:rules:$androidTestVersion" androidTestImplementation "androidx.test:runner:$androidTestVersion" + androidTestUtil "androidx.test:orchestrator:$androidTestVersion" ktlint "com.pinterest:ktlint:$ktlintVersion" diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt index 7ab1a9b465d..84fc5546837 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt @@ -299,14 +299,13 @@ class GooglePayLauncher internal constructor( readyCallback: ReadyCallback, resultCallback: ResultCallback ): GooglePayLauncher { + val context = LocalContext.current + val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope val activityResultLauncher = rememberLauncherForActivityResult( GooglePayLauncherContract(), resultCallback::onResult ) - val context = LocalContext.current - val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope - return remember { GooglePayLauncher( lifecycleScope = lifecycleScope, diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt index e8ed0ad9451..250f19d6e1e 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt @@ -397,21 +397,20 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( readyCallback: ReadyCallback, resultCallback: ResultCallback ): GooglePayPaymentMethodLauncher { + val context = LocalContext.current + val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope val activityResultLauncher = rememberLauncherForActivityResult( GooglePayPaymentMethodLauncherContract(), resultCallback::onResult ) - val context = LocalContext.current - val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope - return remember { GooglePayPaymentMethodLauncher( context = context, lifecycleScope = lifecycleScope, activityResultLauncher = activityResultLauncher, config = config, - readyCallback = readyCallback, + readyCallback = readyCallback ) } } diff --git a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt index 79bcabb2537..964005dde7b 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt @@ -112,11 +112,11 @@ interface PaymentLauncher { stripeAccountId: String? = null, callback: PaymentResultCallback ): PaymentLauncher { + val context = LocalContext.current val activityResultLauncher = rememberLauncherForActivityResult( PaymentLauncherContract(), callback::onPaymentResult ) - val context = LocalContext.current return remember { PaymentLauncherFactory( From b30b8dfdfb3f99d5c9ab5231a4fd383b55c22f0c Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 12 Jul 2022 12:20:14 -0700 Subject: [PATCH 6/8] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a74fd8e7620..fa387cf60e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Payments * [ADDED][5274](https://github.com/stripe/stripe-android/pull/5274) Create `rememberLauncher` method enabling usage of `GooglePayLauncher`, `GooglePayPaymentMethodLauncher` and `PaymentLauncher` in Compose. -* [ADDED][5274](https://github.com/stripe/stripe-android/pull/5274) Deprecate `PaymentLauncher.createForCompose` in favor of `PaymentLauncher.rememberLauncher`. +* [DEPRECATED][5274](https://github.com/stripe/stripe-android/pull/5274) Deprecate `PaymentLauncher.createForCompose` in favor of `PaymentLauncher.rememberLauncher`. ## 20.7.0 - 2022-07-06 * This release adds additional support for Afterpay/Clearpay in PaymentSheet. From 34fa9b30ff77084c5b5527004b35cadf7fa29472 Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 12 Jul 2022 19:39:41 -0700 Subject: [PATCH 7/8] rememberPaymentLauncher --- .../com/stripe/example/activity/ComposeExampleActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt index 567bdb7b4fb..98147a9254f 100644 --- a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt @@ -46,7 +46,7 @@ class ComposeExampleActivity : AppCompatActivity() { fun ComposeScreen() { val inProgress by viewModel.inProgress.observeAsState(false) val status by viewModel.status.observeAsState("") - val paymentLauncher = createPaymentLauncher() + val paymentLauncher = rememberPaymentLauncher() Column(modifier = Modifier.padding(horizontal = 10.dp)) { if (inProgress) { @@ -79,7 +79,7 @@ class ComposeExampleActivity : AppCompatActivity() { * Create [PaymentLauncher] in a [Composable] */ @Composable - fun createPaymentLauncher(): PaymentLauncher { + fun rememberPaymentLauncher(): PaymentLauncher { val context = LocalContext.current val settings = remember { Settings(context) } return PaymentLauncher.rememberLauncher( From e74d3aa3fb2eef916dca83fe814f6da5c29ed5dc Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" Date: Tue, 19 Jul 2022 17:25:50 -0700 Subject: [PATCH 8/8] rememberLauncher --- .../activity/ComposeExampleActivity.kt | 66 ++++--- .../GooglePayLauncherComposeActivity.kt | 174 ++++++++++-------- ...PayPaymentMethodLauncherComposeActivity.kt | 108 ++++++----- .../googlepaylauncher/GooglePayLauncher.kt | 10 +- .../GooglePayPaymentMethodLauncher.kt | 10 +- .../paymentlauncher/PaymentLauncher.kt | 2 +- 6 files changed, 223 insertions(+), 147 deletions(-) diff --git a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt index 98147a9254f..0fe7aae7569 100644 --- a/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/ComposeExampleActivity.kt @@ -48,6 +48,19 @@ class ComposeExampleActivity : AppCompatActivity() { val status by viewModel.status.observeAsState("") val paymentLauncher = rememberPaymentLauncher() + ComposeScreen( + inProgress = inProgress, + status = status, + onConfirm = { paymentLauncher.confirm(it) } + ) + } + + @Composable + private fun ComposeScreen( + inProgress: Boolean, + status: String, + onConfirm: (ConfirmPaymentIntentParams) -> Unit + ) { Column(modifier = Modifier.padding(horizontal = 10.dp)) { if (inProgress) { LinearProgressIndicator( @@ -61,13 +74,13 @@ class ComposeExampleActivity : AppCompatActivity() { ConfirmButton( params = confirmParams3ds1, buttonName = R.string.confirm_with_3ds1_button, - paymentLauncher = paymentLauncher, + onConfirm = onConfirm, inProgress = inProgress ) ConfirmButton( params = confirmParams3ds2, buttonName = R.string.confirm_with_3ds2_button, - paymentLauncher = paymentLauncher, + onConfirm = onConfirm, inProgress = inProgress ) Divider(modifier = Modifier.padding(vertical = 5.dp)) @@ -79,40 +92,43 @@ class ComposeExampleActivity : AppCompatActivity() { * Create [PaymentLauncher] in a [Composable] */ @Composable - fun rememberPaymentLauncher(): PaymentLauncher { + private fun rememberPaymentLauncher(): PaymentLauncher { val context = LocalContext.current val settings = remember { Settings(context) } return PaymentLauncher.rememberLauncher( publishableKey = settings.publishableKey, - stripeAccountId = settings.stripeAccountId - ) { - when (it) { - is PaymentResult.Completed -> { - viewModel.status.value += "\n\nPaymentIntent confirmation succeeded\n\n" - viewModel.inProgress.value = false - } - is PaymentResult.Canceled -> { - viewModel.status.value += "\n\nPaymentIntent confirmation cancelled\n\n" - viewModel.inProgress.value = false - } - is PaymentResult.Failed -> { - viewModel.status.value += "\n\nPaymentIntent confirmation failed with " + - "throwable ${it.throwable} \n\n" - viewModel.inProgress.value = false - } + stripeAccountId = settings.stripeAccountId, + callback = ::onPaymentResult + ) + } + + private fun onPaymentResult(paymentResult: PaymentResult) { + when (paymentResult) { + is PaymentResult.Completed -> { + viewModel.status.value += "\n\nPaymentIntent confirmation succeeded\n\n" + viewModel.inProgress.value = false + } + is PaymentResult.Canceled -> { + viewModel.status.value += "\n\nPaymentIntent confirmation cancelled\n\n" + viewModel.inProgress.value = false + } + is PaymentResult.Failed -> { + viewModel.status.value += "\n\nPaymentIntent confirmation failed with " + + "throwable ${paymentResult.throwable} \n\n" + viewModel.inProgress.value = false } } } @Composable - fun ConfirmButton( + private fun ConfirmButton( params: PaymentMethodCreateParams, @StringRes buttonName: Int, - paymentLauncher: PaymentLauncher, - inProgress: Boolean + inProgress: Boolean, + onConfirm: (ConfirmPaymentIntentParams) -> Unit ) { Button( - onClick = { createAndConfirmPaymentIntent(params, paymentLauncher) }, + onClick = { createAndConfirmPaymentIntent(params, onConfirm) }, modifier = Modifier .fillMaxWidth() .padding(vertical = 5.dp), @@ -124,7 +140,7 @@ class ComposeExampleActivity : AppCompatActivity() { private fun createAndConfirmPaymentIntent( params: PaymentMethodCreateParams, - paymentLauncher: PaymentLauncher + onConfirm: (ConfirmPaymentIntentParams) -> Unit ) { viewModel.createPaymentIntent("us").observe( this @@ -136,7 +152,7 @@ class ComposeExampleActivity : AppCompatActivity() { clientSecret = responseData.getString("secret"), shipping = SHIPPING ) - paymentLauncher.confirm(confirmPaymentIntentParams) + onConfirm(confirmPaymentIntentParams) } } } diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt index 5f43a8abbcd..0dfe754a968 100644 --- a/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/GooglePayLauncherComposeActivity.kt @@ -10,7 +10,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.LinearProgressIndicator import androidx.compose.material.Scaffold +import androidx.compose.material.ScaffoldState import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -41,93 +43,119 @@ class GooglePayLauncherComposeActivity : StripeIntentActivity() { super.onCreate(savedInstanceState) setContent { - val scaffoldState = rememberScaffoldState() - val scope = rememberCoroutineScope() + GooglePayLauncherScreen() + } + } + + @Composable + private fun GooglePayLauncherScreen() { + val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() - var clientSecret by rememberSaveable { mutableStateOf("") } - var googlePayReady by rememberSaveable { mutableStateOf(null) } - var completed by rememberSaveable { mutableStateOf(false) } + var clientSecret by rememberSaveable { mutableStateOf("") } + var googlePayReady by rememberSaveable { mutableStateOf(null) } + var completed by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(Unit) { - if (clientSecret.isBlank()) { - viewModel.createPaymentIntent(COUNTRY_CODE).observe( - this@GooglePayLauncherComposeActivity - ) { result -> - result.fold( - onSuccess = { json -> - clientSecret = json.getString("secret") - }, - onFailure = { error -> - scope.launch { - scaffoldState.snackbarHostState.showSnackbar( - "Could not create PaymentIntent. ${error.message}" - ) - } - completed = true + LaunchedEffect(Unit) { + if (clientSecret.isBlank()) { + viewModel.createPaymentIntent(COUNTRY_CODE).observe( + this@GooglePayLauncherComposeActivity + ) { result -> + result.fold( + onSuccess = { json -> + clientSecret = json.getString("secret") + }, + onFailure = { error -> + scope.launch { + scaffoldState.snackbarHostState.showSnackbar( + "Could not create PaymentIntent. ${error.message}" + ) } - ) - } + completed = true + } + ) } } + } - val googlePayLauncher = GooglePayLauncher.rememberLauncher( - config = googlePayConfig, - readyCallback = { ready -> - if (googlePayReady == null) { - googlePayReady = ready - - if (!ready) { - completed = true - } + val googlePayLauncher = GooglePayLauncher.rememberLauncher( + config = googlePayConfig, + readyCallback = { ready -> + if (googlePayReady == null) { + googlePayReady = ready - scope.launch { - scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") - } + if (!ready) { + completed = true } - }, - resultCallback = { result -> - when (result) { - GooglePayLauncher.Result.Completed -> { - "Successfully collected payment." - } - GooglePayLauncher.Result.Canceled -> { - "Customer cancelled Google Pay." - } - is GooglePayLauncher.Result.Failed -> { - "Google Pay failed. ${result.error.message}" - } - }.let { - scope.launch { - scaffoldState.snackbarHostState.showSnackbar(it) - completed = true - } + + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") } } - ) - - Scaffold(scaffoldState = scaffoldState) { - Column(Modifier.fillMaxWidth()) { - if (googlePayReady == null || clientSecret.isBlank()) { - LinearProgressIndicator(Modifier.fillMaxWidth()) + }, + resultCallback = { result -> + when (result) { + GooglePayLauncher.Result.Completed -> { + "Successfully collected payment." } + GooglePayLauncher.Result.Canceled -> { + "Customer cancelled Google Pay." + } + is GooglePayLauncher.Result.Failed -> { + "Google Pay failed. ${result.error.message}" + } + }.let { + scope.launch { + scaffoldState.snackbarHostState.showSnackbar(it) + completed = true + } + } + } + ) - Spacer(Modifier.height(8.dp).fillMaxWidth()) + GooglePayLauncherScreen( + scaffoldState = scaffoldState, + clientSecret = clientSecret, + googlePayReady = googlePayReady, + completed = completed, + onLaunchGooglePay = { googlePayLauncher.presentForPaymentIntent(it) } + ) + } - AndroidView( - factory = { context -> - GooglePayButton(context) - }, - modifier = Modifier - .wrapContentWidth() - .clickable( - enabled = googlePayReady == true && - clientSecret.isNotBlank() && !completed, - onClick = { - googlePayLauncher.presentForPaymentIntent(clientSecret) - } - ) - ) + @Composable + private fun GooglePayLauncherScreen( + scaffoldState: ScaffoldState, + clientSecret: String, + googlePayReady: Boolean?, + completed: Boolean, + onLaunchGooglePay: (String) -> Unit + ) { + Scaffold(scaffoldState = scaffoldState) { + Column(Modifier.fillMaxWidth()) { + if (googlePayReady == null || clientSecret.isBlank()) { + LinearProgressIndicator(Modifier.fillMaxWidth()) } + + Spacer( + Modifier + .height(8.dp) + .fillMaxWidth() + ) + + AndroidView( + factory = { context -> + GooglePayButton(context) + }, + modifier = Modifier + .wrapContentWidth() + .clickable( + enabled = googlePayReady == true && + clientSecret.isNotBlank() && !completed, + onClick = { + onLaunchGooglePay(clientSecret) + } + ) + ) } } } diff --git a/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt index 9b79090417a..1a58924c238 100644 --- a/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt +++ b/example/src/main/java/com/stripe/example/activity/GooglePayPaymentMethodLauncherComposeActivity.kt @@ -6,7 +6,9 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.Scaffold +import androidx.compose.material.ScaffoldState import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -35,58 +37,76 @@ class GooglePayPaymentMethodLauncherComposeActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContent { - val scaffoldState = rememberScaffoldState() - val scope = rememberCoroutineScope() - var enabled by remember { mutableStateOf(false) } + GooglePayPaymentMethodLauncherScreen() + } + } - val googlePayLauncher = GooglePayPaymentMethodLauncher.rememberLauncher( - config = googlePayConfig, - readyCallback = { ready -> - if (ready) { - enabled = true + @Composable + private fun GooglePayPaymentMethodLauncherScreen() { + val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() + var enabled by remember { mutableStateOf(false) } + + val googlePayLauncher = GooglePayPaymentMethodLauncher.rememberLauncher( + config = googlePayConfig, + readyCallback = { ready -> + if (ready) { + enabled = true + } + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") + } + }, + resultCallback = { result -> + when (result) { + is GooglePayPaymentMethodLauncher.Result.Completed -> { + "Successfully created a PaymentMethod. ${result.paymentMethod}" } - scope.launch { - scaffoldState.snackbarHostState.showSnackbar("Google Pay ready? $ready") + GooglePayPaymentMethodLauncher.Result.Canceled -> { + "Customer cancelled Google Pay." } - }, - resultCallback = { result -> - when (result) { - is GooglePayPaymentMethodLauncher.Result.Completed -> { - "Successfully created a PaymentMethod. ${result.paymentMethod}" - } - GooglePayPaymentMethodLauncher.Result.Canceled -> { - "Customer cancelled Google Pay." - } - is GooglePayPaymentMethodLauncher.Result.Failed -> { - "Google Pay failed: ${result.errorCode}: ${result.error.message}" - } - }.let { - scope.launch { - scaffoldState.snackbarHostState.showSnackbar(it) - enabled = false - } + is GooglePayPaymentMethodLauncher.Result.Failed -> { + "Google Pay failed: ${result.errorCode}: ${result.error.message}" + } + }.let { + scope.launch { + scaffoldState.snackbarHostState.showSnackbar(it) + enabled = false } } - ) + } + ) - Scaffold(scaffoldState = scaffoldState) { - AndroidView( - factory = { context -> - GooglePayButton(context) - }, - modifier = Modifier - .wrapContentWidth() - .clickable( - enabled = enabled, - onClick = { - googlePayLauncher.present( - currencyCode = "EUR", - amount = 2500 - ) - } - ) + GooglePayPaymentMethodLauncherScreen( + scaffoldState = scaffoldState, + enabled = enabled, + onLaunchGooglePay = { + googlePayLauncher.present( + currencyCode = "EUR", + amount = 2500 ) } + ) + } + + @Composable + private fun GooglePayPaymentMethodLauncherScreen( + scaffoldState: ScaffoldState, + enabled: Boolean, + onLaunchGooglePay: () -> Unit + ) { + Scaffold(scaffoldState = scaffoldState) { + AndroidView( + factory = { context -> + GooglePayButton(context) + }, + modifier = Modifier + .wrapContentWidth() + .clickable( + enabled = enabled, + onClick = onLaunchGooglePay + ) + ) } } } diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt index 84fc5546837..6ba57496a4e 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayLauncher.kt @@ -5,7 +5,9 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment @@ -299,6 +301,8 @@ class GooglePayLauncher internal constructor( readyCallback: ReadyCallback, resultCallback: ResultCallback ): GooglePayLauncher { + val currentReadyCallback by rememberUpdatedState(readyCallback) + val context = LocalContext.current val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope val activityResultLauncher = rememberLauncherForActivityResult( @@ -306,11 +310,13 @@ class GooglePayLauncher internal constructor( resultCallback::onResult ) - return remember { + return remember(config) { GooglePayLauncher( lifecycleScope = lifecycleScope, config = config, - readyCallback = readyCallback, + readyCallback = { + currentReadyCallback.onReady(it) + }, activityResultLauncher = activityResultLauncher, googlePayRepositoryFactory = { DefaultGooglePayRepository( diff --git a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt index 250f19d6e1e..bfe201cb43e 100644 --- a/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncher.kt @@ -7,7 +7,9 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IntDef import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment @@ -397,6 +399,8 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( readyCallback: ReadyCallback, resultCallback: ResultCallback ): GooglePayPaymentMethodLauncher { + val currentReadyCallback by rememberUpdatedState(readyCallback) + val context = LocalContext.current val lifecycleScope = LocalLifecycleOwner.current.lifecycleScope val activityResultLauncher = rememberLauncherForActivityResult( @@ -404,13 +408,15 @@ class GooglePayPaymentMethodLauncher @AssistedInject internal constructor( resultCallback::onResult ) - return remember { + return remember(config) { GooglePayPaymentMethodLauncher( context = context, lifecycleScope = lifecycleScope, activityResultLauncher = activityResultLauncher, config = config, - readyCallback = readyCallback + readyCallback = { + currentReadyCallback.onReady(it) + } ) } } diff --git a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt index 964005dde7b..9c6775864fa 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/paymentlauncher/PaymentLauncher.kt @@ -118,7 +118,7 @@ interface PaymentLauncher { callback::onPaymentResult ) - return remember { + return remember(publishableKey, stripeAccountId) { PaymentLauncherFactory( context, activityResultLauncher