diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt index 1fde5080edd..f9201c22bf4 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt @@ -461,7 +461,9 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor( instantDebits = InstantDebitsResult( encodedPaymentMethod = paymentMethod, last4 = url.getQueryParameter(QUERY_PARAM_LAST4), - bankName = url.getQueryParameter(QUERY_BANK_NAME) + bankName = url.getQueryParameter(QUERY_BANK_NAME), + // TODO(tillh-stripe): Pull this from the URL + eligibleForIncentive = false, ), financialConnectionsSession = null, token = null diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/domain/CreateInstantDebitsResult.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/domain/CreateInstantDebitsResult.kt index 887ee336bf2..e2c9d0ec6a2 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/domain/CreateInstantDebitsResult.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/domain/CreateInstantDebitsResult.kt @@ -58,10 +58,24 @@ internal class RealCreateInstantDebitsResult @Inject constructor( ) } + val incentiveEligibilitySessionId = elementsSessionContext?.incentiveEligibilitySession?.id + val newAccountId = consumerSession?.accountId + + val eligibleForIncentive = if (incentiveEligibilitySessionId != null && newAccountId != null) { + val eligibleIncentives = consumerRepository.updateAvailableIncentives( + sessionId = incentiveEligibilitySessionId, + consumerAccount = newAccountId, + ) + eligibleIncentives.data.isNotEmpty() + } else { + false + } + return InstantDebitsResult( encodedPaymentMethod = paymentMethod, bankName = paymentDetails.bankName, last4 = paymentDetails.last4, + eligibleForIncentive = eligibleForIncentive, ) } } diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityResult.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityResult.kt index f604d0c9088..8447f33bf61 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityResult.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityResult.kt @@ -56,5 +56,6 @@ internal sealed class FinancialConnectionsSheetActivityResult : Parcelable { internal data class InstantDebitsResult( val encodedPaymentMethod: String, val last4: String?, - val bankName: String? + val bankName: String?, + val eligibleForIncentive: Boolean, ) : Parcelable diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetForInstantDebitsContract.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetForInstantDebitsContract.kt index 1c01cd40236..4ba4807f79e 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetForInstantDebitsContract.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetForInstantDebitsContract.kt @@ -45,6 +45,7 @@ class FinancialConnectionsSheetForInstantDebitsContract : encodedPaymentMethod = instantDebits.encodedPaymentMethod, last4 = instantDebits.last4, bankName = instantDebits.bankName, + eligibleForIncentive = instantDebits.eligibleForIncentive, ) } } diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetInstantDebitsResult.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetInstantDebitsResult.kt index f7ff5b45e16..178ab2266e9 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetInstantDebitsResult.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetInstantDebitsResult.kt @@ -11,16 +11,14 @@ import kotlinx.parcelize.Parcelize sealed class FinancialConnectionsSheetInstantDebitsResult : Parcelable { /** * The customer completed the connections session. - * @param paymentMethodId The payment method id, that can be used to confirm the payment. - * @param last4 The last 4 digits of the bank account. - * @param bankName The name of the bank. */ @Parcelize @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class Completed( val encodedPaymentMethod: String, val last4: String?, - val bankName: String? + val bankName: String?, + val eligibleForIncentive: Boolean, ) : FinancialConnectionsSheetInstantDebitsResult() /** diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/model/UpdateAvailableIncentives.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/model/UpdateAvailableIncentives.kt new file mode 100644 index 00000000000..4d871b863f1 --- /dev/null +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/model/UpdateAvailableIncentives.kt @@ -0,0 +1,10 @@ +package com.stripe.android.financialconnections.model + +import com.stripe.android.model.LinkConsumerIncentive +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class UpdateAvailableIncentives( + @SerialName("data") val data: List, +) diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/ConsumerSessionRepository.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/ConsumerSessionRepository.kt index 0c32353973c..19f1e3d68ff 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/ConsumerSessionRepository.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/ConsumerSessionRepository.kt @@ -13,6 +13,7 @@ internal const val KeyConsumerSession = "ConsumerSession" @Parcelize internal data class CachedConsumerSession( + val accountId: String?, val emailAddress: String, val phoneNumber: String, val clientSecret: String, @@ -28,6 +29,7 @@ internal interface ConsumerSessionRepository : ConsumerSessionProvider { fun storeNewConsumerSession( consumerSession: ConsumerSession?, publishableKey: String?, + accountId: String? = null, ) fun updateConsumerSession( @@ -46,8 +48,9 @@ internal class RealConsumerSessionRepository @Inject constructor( override fun storeNewConsumerSession( consumerSession: ConsumerSession?, publishableKey: String?, + accountId: String?, ) { - savedStateHandle[KeyConsumerSession] = consumerSession?.toCached(publishableKey) + savedStateHandle[KeyConsumerSession] = consumerSession?.toCached(publishableKey, accountId) } override fun updateConsumerSession(consumerSession: ConsumerSession) { @@ -58,7 +61,9 @@ internal class RealConsumerSessionRepository @Inject constructor( private fun ConsumerSession.toCached( publishableKey: String?, + accountId: String? = null, ) = CachedConsumerSession( + accountId = accountId, emailAddress = emailAddress, phoneNumber = getRedactedPhoneNumber(), clientSecret = clientSecret, diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/FinancialConnectionsConsumerSessionRepository.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/FinancialConnectionsConsumerSessionRepository.kt index 80378469a0e..1a9750519dd 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/FinancialConnectionsConsumerSessionRepository.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/FinancialConnectionsConsumerSessionRepository.kt @@ -5,6 +5,7 @@ import com.stripe.android.core.frauddetection.FraudDetectionDataRepository import com.stripe.android.financialconnections.FinancialConnectionsSheet.ElementsSessionContext import com.stripe.android.financialconnections.FinancialConnectionsSheet.ElementsSessionContext.BillingDetails import com.stripe.android.financialconnections.domain.IsLinkWithStripe +import com.stripe.android.financialconnections.model.UpdateAvailableIncentives import com.stripe.android.financialconnections.repository.api.FinancialConnectionsConsumersApiService import com.stripe.android.financialconnections.repository.api.ProvideApiRequestOptions import com.stripe.android.financialconnections.utils.toConsumerBillingAddressParams @@ -69,6 +70,11 @@ internal interface FinancialConnectionsConsumerSessionRepository { billingPhone: String?, ): SharePaymentDetails + suspend fun updateAvailableIncentives( + sessionId: String, + consumerAccount: String, + ): UpdateAvailableIncentives + companion object { operator fun invoke( consumersApiService: ConsumersApiService, @@ -238,6 +244,16 @@ private class FinancialConnectionsConsumerSessionRepositoryImpl( ).getOrThrow() } + override suspend fun updateAvailableIncentives( + sessionId: String, + consumerAccount: String, + ): UpdateAvailableIncentives { + return financialConnectionsConsumersApiService.updateAvailableIncentives( + sessionId = sessionId, + consumerAccount = consumerAccount, + ) + } + private suspend fun postConsumerSession( email: String, clientSecret: String @@ -266,6 +282,10 @@ private class FinancialConnectionsConsumerSessionRepositoryImpl( signup: ConsumerSessionSignup, ) { logger.debug("SYNC_CACHE: updating local consumer session from signUp") - consumerSessionRepository.storeNewConsumerSession(signup.consumerSession, signup.publishableKey) + consumerSessionRepository.storeNewConsumerSession( + consumerSession = signup.consumerSession, + publishableKey = signup.publishableKey, + accountId = signup.accountId, + ) } } diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/api/FinancialConnectionsConsumersApiService.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/api/FinancialConnectionsConsumersApiService.kt index 489532dd3a4..17e0cb40f8f 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/api/FinancialConnectionsConsumersApiService.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/repository/api/FinancialConnectionsConsumersApiService.kt @@ -2,6 +2,7 @@ package com.stripe.android.financialconnections.repository.api import androidx.annotation.RestrictTo import com.stripe.android.core.networking.ApiRequest +import com.stripe.android.financialconnections.model.UpdateAvailableIncentives import com.stripe.android.financialconnections.network.FinancialConnectionsRequestExecutor import com.stripe.android.financialconnections.utils.filterNotNullValues import com.stripe.android.model.ConsumerSessionLookup @@ -15,6 +16,11 @@ internal interface FinancialConnectionsConsumersApiService { requestSurface: String, ): ConsumerSessionLookup + suspend fun updateAvailableIncentives( + sessionId: String, + consumerAccount: String, + ): UpdateAvailableIncentives + companion object { operator fun invoke( requestExecutor: FinancialConnectionsRequestExecutor, @@ -59,6 +65,24 @@ private class FinancialConnectionsConsumersApiServiceImpl( ) } + override suspend fun updateAvailableIncentives( + sessionId: String, + consumerAccount: String, + ): UpdateAvailableIncentives { + val request = apiRequestFactory.createPost( + consumerSessionsUrl, + apiOptions, + mapOf( + "session_id" to sessionId, + "consumer_account" to consumerAccount, + ).filterNotNullValues() + ) + return requestExecutor.execute( + request, + UpdateAvailableIncentives.serializer() + ) + } + private companion object { /** * @return `https://api.stripe.com/v1/connections/link_account_sessions/consumer_sessions` diff --git a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountForInstantDebitsResult.kt b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountForInstantDebitsResult.kt index 2136abd6d46..35624a531ff 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountForInstantDebitsResult.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountForInstantDebitsResult.kt @@ -18,7 +18,8 @@ sealed interface CollectBankAccountForInstantDebitsResult : Parcelable { val intent: StripeIntent?, val paymentMethod: PaymentMethod, val last4: String?, - val bankName: String? + val bankName: String?, + val eligibleForIncentive: Boolean, ) : CollectBankAccountForInstantDebitsResult @Parcelize @@ -51,7 +52,8 @@ internal fun CollectBankAccountResultInternal.toInstantDebitsResult(): CollectBa intent = response.intent, paymentMethod = response.instantDebitsData.paymentMethod, last4 = response.instantDebitsData.last4, - bankName = response.instantDebitsData.bankName + bankName = response.instantDebitsData.bankName, + eligibleForIncentive = response.instantDebitsData.eligibleForIncentive, ) } } diff --git a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountResultInternal.kt b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountResultInternal.kt index 714b5eedb82..74f9645a0a8 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountResultInternal.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/navigation/CollectBankAccountResultInternal.kt @@ -54,6 +54,7 @@ data class CollectBankAccountResponseInternal( data class InstantDebitsData( val paymentMethod: PaymentMethod, val last4: String?, - val bankName: String? + val bankName: String?, + val eligibleForIncentive: Boolean, ) : StripeModel } diff --git a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/ui/CollectBankAccountViewModel.kt b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/ui/CollectBankAccountViewModel.kt index 017c5c6b9a1..ecdafa96f6b 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/bankaccount/ui/CollectBankAccountViewModel.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/bankaccount/ui/CollectBankAccountViewModel.kt @@ -200,6 +200,7 @@ internal class CollectBankAccountViewModel @Inject constructor( paymentMethod = it, last4 = result.last4, bankName = result.bankName, + eligibleForIncentive = result.eligibleForIncentive, ) }, ) diff --git a/payments-model/src/main/java/com/stripe/android/model/ConsumerSessionSignup.kt b/payments-model/src/main/java/com/stripe/android/model/ConsumerSessionSignup.kt index 4e92c679b9f..4ebaeb2aaa2 100644 --- a/payments-model/src/main/java/com/stripe/android/model/ConsumerSessionSignup.kt +++ b/payments-model/src/main/java/com/stripe/android/model/ConsumerSessionSignup.kt @@ -13,6 +13,8 @@ import kotlinx.serialization.Serializable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @Serializable data class ConsumerSessionSignup( + @SerialName("account_id") + val accountId: String, @SerialName("consumer_session") val consumerSession: ConsumerSession, @SerialName("publishable_key") diff --git a/payments-model/src/main/java/com/stripe/android/model/LinkConsumerIncentive.kt b/payments-model/src/main/java/com/stripe/android/model/LinkConsumerIncentive.kt index e3364fb92f1..4958027219c 100644 --- a/payments-model/src/main/java/com/stripe/android/model/LinkConsumerIncentive.kt +++ b/payments-model/src/main/java/com/stripe/android/model/LinkConsumerIncentive.kt @@ -3,9 +3,11 @@ package com.stripe.android.model import androidx.annotation.RestrictTo import com.stripe.android.core.model.StripeModel import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @Parcelize +@Serializable data class LinkConsumerIncentive( val incentiveParams: IncentiveParams, val incentiveDisplayText: String?, @@ -13,6 +15,7 @@ data class LinkConsumerIncentive( @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @Parcelize + @Serializable data class IncentiveParams( val paymentMethod: String, ) : StripeModel diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/paymentdatacollection/ach/USBankAccountFormViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/paymentdatacollection/ach/USBankAccountFormViewModel.kt index ac00609691b..a292e6e9b27 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/paymentdatacollection/ach/USBankAccountFormViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/paymentdatacollection/ach/USBankAccountFormViewModel.kt @@ -354,6 +354,7 @@ internal class USBankAccountFormViewModel @Inject internal constructor( financialConnectionsSessionId = null, mandateText = buildMandateText(isVerifyWithMicrodeposits = false), isVerifyingWithMicrodeposits = false, + eligibleForIncentive = result.eligibleForIncentive, ) ) }