From c947e330a45c184776a38dc8241fc293fc89d5f3 Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Fri, 15 Dec 2023 18:12:26 -0500 Subject: [PATCH] Add `Bacs` support. --- payments-ui-core/api/payments-ui-core.api | 4 -- payments-ui-core/res/values/strings.xml | 2 + payments-ui-core/src/main/assets/lpms.json | 5 ++ .../forms/PaymentMethodRequirements.kt | 9 +++ .../java/com/stripe/android/ui/core/FormUI.kt | 6 ++ .../core/elements/BacsDebitBankAccountSpec.kt | 16 +++-- .../ui/core/elements/BacsDebitConfirmSpec.kt | 13 ++-- .../ui/core/forms/TransformSpecToElements.kt | 2 +- .../ui/core/forms/resources/LpmRepository.kt | 43 +++++++++++++ .../java/com/stripe/android/lpm/TestBacs.kt | 60 ++++++++++++++++++ .../android/test/core/FieldPopulator.kt | 25 ++++++++ .../android/test/core/PlaygroundTestDriver.kt | 10 ++- .../android/test/core/TestParameters.kt | 12 ++++ .../stripe/android/test/core/ui/BuyButton.kt | 5 ++ .../stripe/android/test/core/ui/Selectors.kt | 61 +++++++++++++++++++ .../paymentsheet/forms/FormViewModel.kt | 2 +- .../src/test/resources/bacs_debit-support.csv | 49 +++++++++++++++ stripe-ui-core/api/stripe-ui-core.api | 1 + .../android/uicore/elements/IdentifierSpec.kt | 2 + 19 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestBacs.kt create mode 100644 paymentsheet/src/test/resources/bacs_debit-support.csv diff --git a/payments-ui-core/api/payments-ui-core.api b/payments-ui-core/api/payments-ui-core.api index 3671a875ceb..8c9e5c6d35a 100644 --- a/payments-ui-core/api/payments-ui-core.api +++ b/payments-ui-core/api/payments-ui-core.api @@ -128,10 +128,6 @@ public final class com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec$ public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } -public final class com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public final class com/stripe/android/ui/core/elements/BacsDebitConfirmSpec$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field $stable I public static final field INSTANCE Lcom/stripe/android/ui/core/elements/BacsDebitConfirmSpec$$serializer; diff --git a/payments-ui-core/res/values/strings.xml b/payments-ui-core/res/values/strings.xml index 2dbd7186560..fa1353a1950 100644 --- a/payments-ui-core/res/values/strings.xml +++ b/payments-ui-core/res/values/strings.xml @@ -67,6 +67,8 @@ Buy using a UPI ID AU BECS Direct Debit + + Bacs Direct Debit Card diff --git a/payments-ui-core/src/main/assets/lpms.json b/payments-ui-core/src/main/assets/lpms.json index 98c09df806b..78142a765bb 100644 --- a/payments-ui-core/src/main/assets/lpms.json +++ b/payments-ui-core/src/main/assets/lpms.json @@ -755,6 +755,11 @@ } } }, + { + "type": "bacs_debit", + "async": true, + "fields": [] + }, { "type": "revolut_pay", "async": false, diff --git a/payments-ui-core/src/main/java/com/stripe/android/paymentsheet/forms/PaymentMethodRequirements.kt b/payments-ui-core/src/main/java/com/stripe/android/paymentsheet/forms/PaymentMethodRequirements.kt index cb8e5e6193c..ad652935f97 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/paymentsheet/forms/PaymentMethodRequirements.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/paymentsheet/forms/PaymentMethodRequirements.kt @@ -227,6 +227,15 @@ internal val AuBecsDebitRequirement = PaymentMethodRequirements( confirmPMFromCustomer = true ) +/** + * This defines the requirements for usage as a Payment Method. + */ +internal val BacsDebitRequirement = PaymentMethodRequirements( + piRequirements = setOf(Delayed), + siRequirements = null, + confirmPMFromCustomer = null +) + internal val ZipRequirement = PaymentMethodRequirements( piRequirements = emptySet(), siRequirements = null, diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/FormUI.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/FormUI.kt index bd892db1cc8..121d4ce64af 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/FormUI.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/FormUI.kt @@ -24,6 +24,8 @@ import com.stripe.android.ui.core.elements.SaveForFutureUseElement import com.stripe.android.ui.core.elements.SaveForFutureUseElementUI import com.stripe.android.ui.core.elements.StaticTextElement import com.stripe.android.ui.core.elements.StaticTextElementUI +import com.stripe.android.uicore.elements.CheckboxFieldElement +import com.stripe.android.uicore.elements.CheckboxFieldUI import com.stripe.android.uicore.elements.FormElement import com.stripe.android.uicore.elements.IdentifierSpec import com.stripe.android.uicore.elements.OTPElement @@ -76,6 +78,10 @@ fun FormUI( hiddenIdentifiers, lastTextFieldIdentifier ) + is CheckboxFieldElement -> CheckboxFieldUI( + controller = element.controller, + enabled = enabled + ) is StaticTextElement -> StaticTextElementUI(element) is SaveForFutureUseElement -> SaveForFutureUseElementUI(enabled, element) is AfterpayClearpayHeaderElement -> AfterpayClearpayElementUI( diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec.kt index b1b67c240ab..b02ae0fa1e3 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitBankAccountSpec.kt @@ -10,6 +10,9 @@ import kotlinx.serialization.Serializable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @Serializable class BacsDebitBankAccountSpec : FormItemSpec() { + private val sortCodeIdentifier = IdentifierSpec.Generic(SORT_CODE_API_PATH) + private val accountNumberIdentifier = IdentifierSpec.Generic(ACCOUNT_NUMBER_API_PATH) + override val apiPath: IdentifierSpec = IdentifierSpec() fun transform( @@ -17,20 +20,25 @@ class BacsDebitBankAccountSpec : FormItemSpec() { ) = createSectionElement( listOf( SimpleTextElement( - IdentifierSpec.Generic("bacs_debit[sort_code]"), + sortCodeIdentifier, SimpleTextFieldController( BacsDebitSortCodeConfig(), - initialValue = initialValues[this.apiPath] + initialValue = initialValues[sortCodeIdentifier] ) ), SimpleTextElement( - IdentifierSpec.Generic("bacs_debit[account_number]"), + accountNumberIdentifier, SimpleTextFieldController( BacsDebitAccountNumberConfig(), - initialValue = initialValues[this.apiPath] + initialValue = initialValues[accountNumberIdentifier] ) ) ), R.string.stripe_bacs_bank_account_title ) + + private companion object { + const val SORT_CODE_API_PATH = "bacs_debit[sort_code]" + const val ACCOUNT_NUMBER_API_PATH = "bacs_debit[account_number]" + } } diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitConfirmSpec.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitConfirmSpec.kt index 9a435a8d4b5..53fceba361b 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitConfirmSpec.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BacsDebitConfirmSpec.kt @@ -10,19 +10,20 @@ import kotlinx.serialization.Serializable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @Serializable class BacsDebitConfirmSpec : FormItemSpec() { - override val apiPath: IdentifierSpec = IdentifierSpec( - v1 = "bacs_debit[confirmed]", - ignoreField = true - ) + override val apiPath: IdentifierSpec = IdentifierSpec.BacsDebitConfirmed - fun transform(merchantName: String) = CheckboxFieldElement( + fun transform( + merchantName: String, + initialValues: Map + ) = CheckboxFieldElement( apiPath, CheckboxFieldController( labelResource = CheckboxFieldController.LabelResource( R.string.stripe_bacs_confirm_mandate_label, merchantName ), - debugTag = "BACS_MANDATE_CHECKBOX" + debugTag = "BACS_MANDATE_CHECKBOX", + initialValue = initialValues[this.apiPath].toBoolean() ) ) } diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/TransformSpecToElements.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/TransformSpecToElements.kt index 684ff7cc8ef..ae2c8c2cecf 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/TransformSpecToElements.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/TransformSpecToElements.kt @@ -74,7 +74,7 @@ class TransformSpecToElements( is MandateTextSpec -> it.transform(merchantName) is AuBecsDebitMandateTextSpec -> it.transform(merchantName) is BacsDebitBankAccountSpec -> it.transform(initialValues) - is BacsDebitConfirmSpec -> it.transform(merchantName) + is BacsDebitConfirmSpec -> it.transform(merchantName, initialValues) is CardDetailsSectionSpec -> it.transform( context = context, cbcEligibility = cbcEligibility, diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/resources/LpmRepository.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/resources/LpmRepository.kt index bddc362884e..123588685f9 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/resources/LpmRepository.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/forms/resources/LpmRepository.kt @@ -20,6 +20,7 @@ import com.stripe.android.paymentsheet.forms.AlipayRequirement import com.stripe.android.paymentsheet.forms.AlmaRequirement import com.stripe.android.paymentsheet.forms.AmazonPayRequirement import com.stripe.android.paymentsheet.forms.AuBecsDebitRequirement +import com.stripe.android.paymentsheet.forms.BacsDebitRequirement import com.stripe.android.paymentsheet.forms.BancontactRequirement import com.stripe.android.paymentsheet.forms.BlikRequirement import com.stripe.android.paymentsheet.forms.BoletoRequirement @@ -47,6 +48,8 @@ import com.stripe.android.paymentsheet.forms.ZipRequirement import com.stripe.android.ui.core.BillingDetailsCollectionConfiguration import com.stripe.android.ui.core.R import com.stripe.android.ui.core.elements.AfterpayClearpayHeaderElement.Companion.isClearpay +import com.stripe.android.ui.core.elements.BacsDebitBankAccountSpec +import com.stripe.android.ui.core.elements.BacsDebitConfirmSpec import com.stripe.android.ui.core.elements.CardBillingSpec import com.stripe.android.ui.core.elements.CardDetailsSectionSpec import com.stripe.android.ui.core.elements.CashAppPayMandateTextSpec @@ -56,6 +59,7 @@ import com.stripe.android.ui.core.elements.FormItemSpec import com.stripe.android.ui.core.elements.LayoutSpec import com.stripe.android.ui.core.elements.LpmSerializer import com.stripe.android.ui.core.elements.MandateTextSpec +import com.stripe.android.ui.core.elements.PlaceholderSpec import com.stripe.android.ui.core.elements.SaveForFutureUseSpec import com.stripe.android.ui.core.elements.SharedDataSpec import com.stripe.android.ui.core.elements.transform @@ -468,6 +472,45 @@ class LpmRepository constructor( requirement = AuBecsDebitRequirement, formSpec = LayoutSpec(sharedDataSpec.fields) ) + PaymentMethod.Type.BacsDebit.code -> { + val localFields = listOfNotNull( + PlaceholderSpec( + apiPath = IdentifierSpec.Name, + field = PlaceholderSpec.PlaceholderField.Name + ), + PlaceholderSpec( + apiPath = IdentifierSpec.Email, + field = PlaceholderSpec.PlaceholderField.Email + ), + PlaceholderSpec( + apiPath = IdentifierSpec.Phone, + field = PlaceholderSpec.PlaceholderField.Phone + ), + BacsDebitBankAccountSpec(), + PlaceholderSpec( + apiPath = IdentifierSpec.BillingAddress, + field = PlaceholderSpec.PlaceholderField.BillingAddress + ), + BacsDebitConfirmSpec() + ) + + SupportedPaymentMethod( + code = "bacs_debit", + requiresMandate = true, + displayNameResource = R.string.stripe_paymentsheet_payment_method_bacs_debit, + iconResource = R.drawable.stripe_ic_paymentsheet_pm_bank, + lightThemeIconUrl = sharedDataSpec.selectorIcon?.lightThemePng, + darkThemeIconUrl = sharedDataSpec.selectorIcon?.darkThemePng, + tintIconOnSelection = true, + requirement = BacsDebitRequirement, + formSpec = LayoutSpec(items = sharedDataSpec.fields + localFields), + placeholderOverrideList = listOf( + IdentifierSpec.Name, + IdentifierSpec.Email, + IdentifierSpec.BillingAddress + ) + ) + } PaymentMethod.Type.USBankAccount.code -> { val pmo = stripeIntent.getPaymentMethodOptions()[PaymentMethod.Type.USBankAccount.code] val verificationMethod = (pmo as? Map<*, *>)?.get("verification_method") as? String diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestBacs.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestBacs.kt new file mode 100644 index 00000000000..b990e2aeca6 --- /dev/null +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/lpm/TestBacs.kt @@ -0,0 +1,60 @@ +package com.stripe.android.lpm + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.stripe.android.BasePlaygroundTest +import com.stripe.android.paymentsheet.example.playground.settings.AutomaticPaymentMethodsSettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.Country +import com.stripe.android.paymentsheet.example.playground.settings.CountrySettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.Currency +import com.stripe.android.paymentsheet.example.playground.settings.CurrencySettingsDefinition +import com.stripe.android.paymentsheet.example.playground.settings.DelayedPaymentMethodsSettingsDefinition +import com.stripe.android.test.core.AuthorizeAction +import com.stripe.android.test.core.TestParameters +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +internal class TestBacs : BasePlaygroundTest() { + @Test + fun testBacsWhenConfirmed() { + testDriver.confirmNewOrGuestComplete( + testParameters = createTestParameters(AuthorizeAction.Bacs.Confirm) + ) + } + + @Test + fun testBacsWhenCancelled() { + testDriver.confirmNewOrGuestComplete( + testParameters = createTestParameters(AuthorizeAction.Bacs.ModifyDetails) + ) + } + + @Test + fun testBacsWhenConfirmedInCustomFlow() { + testDriver.confirmCustomAndBuy( + testParameters = createTestParameters(AuthorizeAction.Bacs.Confirm) + ) + } + + @Test + fun testBacsWhenCancelledInCustomFlow() { + testDriver.confirmCustomAndBuy( + testParameters = createTestParameters(AuthorizeAction.Bacs.ModifyDetails) + ) + } + + private fun createTestParameters( + bacsAuthAction: AuthorizeAction.Bacs + ): TestParameters { + return TestParameters.create( + paymentMethodCode = "bacs_debit", + ) { settings -> + settings[AutomaticPaymentMethodsSettingsDefinition] = true + settings[DelayedPaymentMethodsSettingsDefinition] = true + settings[CountrySettingsDefinition] = Country.GB + settings[CurrencySettingsDefinition] = Currency.GBP + }.copy( + authorizationAction = bacsAuthAction + ) + } +} diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/FieldPopulator.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/FieldPopulator.kt index 91eb9920030..b26b94aa1df 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/FieldPopulator.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/FieldPopulator.kt @@ -14,6 +14,8 @@ import com.stripe.android.paymentsheet.example.playground.settings.DefaultBillin import com.stripe.android.test.core.ui.Selectors import com.stripe.android.ui.core.elements.AddressSpec import com.stripe.android.ui.core.elements.AuBankAccountNumberSpec +import com.stripe.android.ui.core.elements.BacsDebitBankAccountSpec +import com.stripe.android.ui.core.elements.BacsDebitConfirmSpec import com.stripe.android.ui.core.elements.BoletoTaxIdSpec import com.stripe.android.ui.core.elements.BsbSpec import com.stripe.android.ui.core.elements.CardBillingSpec @@ -99,6 +101,8 @@ internal class FieldPopulator( val cardCvc: String = "321", val auBecsBsbNumber: String = "000000", val auBecsAccountNumber: String = "000123456", + val bacsSortCode: String = "108800", + val bacsAccountNumber: String = "00012345", val boletoTaxId: String = "00000000000", ) @@ -156,6 +160,16 @@ internal class FieldPopulator( selectors.getAuAccountNumber() .assertContentDescriptionEquals(values.auBecsAccountNumber) } + is BacsDebitBankAccountSpec -> { + selectors.getBacsAccountNumber() + .assertContentDescriptionEquals(values.bacsAccountNumber) + selectors.getBacsSortCode() + .assertContentDescriptionEquals(values.bacsSortCode) + } + is BacsDebitConfirmSpec -> { + selectors.getBacsConfirmed() + .assertIsOn() + } is DropdownSpec -> {} is IbanSpec -> {} is KlarnaCountrySpec -> {} @@ -212,6 +226,17 @@ internal class FieldPopulator( performTextInput(values.auBecsAccountNumber) } } + is BacsDebitBankAccountSpec -> { + selectors.getBacsAccountNumber() + .performTextInput(values.bacsAccountNumber) + selectors.getBacsSortCode() + .performTextInput(values.bacsSortCode) + } + is BacsDebitConfirmSpec -> { + selectors.getBacsConfirmed() + .performScrollTo() + .performClick() + } is IbanSpec -> {} is KlarnaCountrySpec -> {} is CardBillingSpec -> { diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt index fcf0d3ccd39..f5610f4de92 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/PlaygroundTestDriver.kt @@ -770,7 +770,15 @@ internal class PlaygroundTestDriver( }.isSuccess } } - + is AuthorizeAction.Bacs.Confirm -> {} + is AuthorizeAction.Bacs.ModifyDetails -> { + buyButton.apply { + scrollTo() + waitProcessingComplete() + isEnabled() + isDisplayed() + } + } null -> {} } } else { diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/TestParameters.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/TestParameters.kt index 5bacc0ac565..e3a1e5f6adf 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/TestParameters.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/TestParameters.kt @@ -129,4 +129,16 @@ internal sealed interface AuthorizeAction { override fun text(checkoutMode: CheckoutMode): String = "" override val requiresBrowser: Boolean = true } + + sealed interface Bacs : AuthorizeAction { + object Confirm : Bacs { + override fun text(checkoutMode: CheckoutMode): String = "Confirm" + override val requiresBrowser: Boolean = false + } + + object ModifyDetails : Bacs { + override fun text(checkoutMode: CheckoutMode): String = "Modify details" + override val requiresBrowser: Boolean = false + } + } } diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/BuyButton.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/BuyButton.kt index ad0f15cfac1..49f76e0de7c 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/BuyButton.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/BuyButton.kt @@ -36,6 +36,11 @@ class BuyButton( .assertIsDisplayed() } + fun scrollTo() { + composeTestRule.onNode(hasTestTag(PAYMENT_SHEET_PRIMARY_BUTTON_TEST_TAG)) + .performScrollTo() + } + fun waitProcessingComplete() { composeTestRule.waitUntil(timeoutMillis = processingCompleteTimeout.inWholeMilliseconds) { runCatching { diff --git a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt index f87520ac8e3..a5681a4a6a4 100644 --- a/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt +++ b/paymentsheet-example/src/androidTest/java/com/stripe/android/test/core/ui/Selectors.kt @@ -2,7 +2,10 @@ package com.stripe.android.test.core.ui import android.content.pm.PackageManager import androidx.annotation.StringRes +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.isToggleable import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText @@ -27,6 +30,7 @@ import com.stripe.android.ui.core.elements.SAVE_FOR_FUTURE_CHECKBOX_TEST_TAG import kotlin.time.Duration.Companion.seconds import com.stripe.android.R as StripeR import com.stripe.android.core.R as CoreR +import com.stripe.android.paymentsheet.R as PaymentSheetR import com.stripe.android.ui.core.R as PaymentsUiCoreR import com.stripe.android.uicore.R as UiCoreR @@ -122,6 +126,7 @@ internal class Selectors( private val checkoutMode = testParameters.playgroundSettingsSnapshot[CheckoutModeSettingsDefinition] + @OptIn(ExperimentalTestApi::class) val authorizeAction = when (testParameters.authorizationAction) { is AuthorizeAction.AuthorizePayment -> { object : UiAutomatorText( @@ -163,6 +168,50 @@ internal class Selectors( ) {} } + is AuthorizeAction.Bacs.Confirm -> { + object : UiAutomatorText( + label = testParameters.authorizationAction.text(checkoutMode), + className = "android.widget.Button", + device = device + ) { + override fun click() { + composeTestRule.waitUntilExactlyOneExists( + hasText( + getResourceString( + PaymentSheetR.string.stripe_paymentsheet_bacs_mandate_title + ) + ) + ) + + composeTestRule.onNodeWithText( + getResourceString(PaymentSheetR.string.stripe_paymentsheet_confirm) + ).performClick() + } + } + } + + is AuthorizeAction.Bacs.ModifyDetails -> { + object : UiAutomatorText( + label = testParameters.authorizationAction.text(checkoutMode), + className = "android.widget.Button", + device = device + ) { + override fun click() { + composeTestRule.waitUntilExactlyOneExists( + hasText(getResourceString(PaymentSheetR.string.stripe_paymentsheet_bacs_mandate_title)) + ) + + composeTestRule.onNodeWithText( + getResourceString( + PaymentSheetR + .string + .stripe_paymentsheet_bacs_modify_details_button_label + ) + ).performClick() + } + } + } + else -> null } @@ -213,6 +262,18 @@ internal class Selectors( getResourceString(StripeR.string.stripe_becs_widget_account_number) ) + fun getBacsSortCode() = composeTestRule.onNodeWithText( + getResourceString(PaymentsUiCoreR.string.stripe_bacs_sort_code) + ) + + fun getBacsAccountNumber() = composeTestRule.onNodeWithText( + getResourceString(StripeR.string.stripe_becs_widget_account_number) + ) + + fun getBacsConfirmed() = composeTestRule.onNode( + isToggleable().and(hasTestTag("BACS_MANDATE_CHECKBOX")) + ) + fun getBoletoTaxId() = composeTestRule.onNodeWithText( getResourceString(PaymentsUiCoreR.string.stripe_boleto_tax_id_label) ) diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/forms/FormViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/forms/FormViewModel.kt index 5c0eeee0066..ceaf5b87147 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/forms/FormViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/forms/FormViewModel.kt @@ -69,7 +69,7 @@ internal class FormViewModel @Inject internal constructor( lpmRepository.fromCode(formArguments.paymentMethodCode) ).formSpec.items - // Cards are a special case and already contain specs based on the configuration. + // Cards & Bacs debit are a special case and already contain specs based on the configuration. if (formArguments.paymentMethodCode != PaymentMethod.Type.Card.code) { specs = specsForConfiguration( configuration = formArguments.billingDetailsCollectionConfiguration, diff --git a/paymentsheet/src/test/resources/bacs_debit-support.csv b/paymentsheet/src/test/resources/bacs_debit-support.csv new file mode 100644 index 00000000000..24b2e01fb76 --- /dev/null +++ b/paymentsheet/src/test/resources/bacs_debit-support.csv @@ -0,0 +1,49 @@ +lpm, hasCustomer, allowsDelayedPayment, intentSetupFutureUsage, intentHasShipping, intentLpms, supportCustomerSavedCard, formExists, formType, supportsAdding +bacs_debit, true, true, off_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, true, true, off_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, false, off_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, off_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, true, on_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, true, true, on_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, false, on_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, on_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, true, null, false, card/bacs_debit, false, true, oneTime, true +bacs_debit, true, true, null, false, card/eps/bacs_debit, false, true, oneTime, true +bacs_debit, true, false, null, false, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, null, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, off_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, false, true, off_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, false, off_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, off_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, on_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, false, true, on_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, false, on_session, false, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, on_session, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, null, false, card/bacs_debit, false, true, oneTime, true +bacs_debit, false, true, null, false, card/eps/bacs_debit, false, true, oneTime, true +bacs_debit, false, false, null, false, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, null, false, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, true, off_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, true, true, off_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, false, off_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, off_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, true, on_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, true, true, on_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, false, on_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, on_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, true, true, null, true, card/bacs_debit, false, true, oneTime, true +bacs_debit, true, true, null, true, card/eps/bacs_debit, false, true, oneTime, true +bacs_debit, true, false, null, true, card/bacs_debit, false, false, not available, false +bacs_debit, true, false, null, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, off_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, false, true, off_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, false, off_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, off_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, on_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, false, true, on_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, false, on_session, true, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, on_session, true, card/eps/bacs_debit, false, false, not available, false +bacs_debit, false, true, null, true, card/bacs_debit, false, true, oneTime, true +bacs_debit, false, true, null, true, card/eps/bacs_debit, false, true, oneTime, true +bacs_debit, false, false, null, true, card/bacs_debit, false, false, not available, false +bacs_debit, false, false, null, true, card/eps/bacs_debit, false, false, not available, false \ No newline at end of file diff --git a/stripe-ui-core/api/stripe-ui-core.api b/stripe-ui-core/api/stripe-ui-core.api index 1f3bf6fde30..166b910b7ce 100644 --- a/stripe-ui-core/api/stripe-ui-core.api +++ b/stripe-ui-core/api/stripe-ui-core.api @@ -136,6 +136,7 @@ public final class com/stripe/android/uicore/elements/IdentifierSpec$$serializer public final class com/stripe/android/uicore/elements/IdentifierSpec$Companion { public final fun Generic (Ljava/lang/String;)Lcom/stripe/android/uicore/elements/IdentifierSpec; + public final fun getBillingAddress ()Lcom/stripe/android/uicore/elements/IdentifierSpec; public final fun getBlik ()Lcom/stripe/android/uicore/elements/IdentifierSpec; public final fun getBlikCode ()Lcom/stripe/android/uicore/elements/IdentifierSpec; public final fun getCardBrand ()Lcom/stripe/android/uicore/elements/IdentifierSpec; diff --git a/stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/IdentifierSpec.kt b/stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/IdentifierSpec.kt index 0665658bce7..5e6aa370f79 100644 --- a/stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/IdentifierSpec.kt +++ b/stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/IdentifierSpec.kt @@ -55,6 +55,8 @@ data class IdentifierSpec( val CardExpYear = IdentifierSpec("card[exp_year]") + val BillingAddress = IdentifierSpec("billing_details[address]") + val Email = IdentifierSpec("billing_details[email]") val Phone = IdentifierSpec("billing_details[phone]")