Skip to content

Commit

Permalink
Add Bacs support.
Browse files Browse the repository at this point in the history
  • Loading branch information
samer-stripe committed Nov 30, 2023
1 parent dba77db commit c3a734a
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 2 deletions.
4 changes: 4 additions & 0 deletions payments-ui-core/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<string name="stripe_afterpay_clearpay_message"><![CDATA[Pay in <num_installments/> interest-free payments of <installment_price/> with <img/>]]></string>
<!-- Text for back button -->
<string name="stripe_back">Back</string>
<!-- Label for Bacs name field -->
<string name="stripe_bacs_full_name">Full name</string>
<!-- Label for Bacs account number field -->
<string name="stripe_bacs_account_number">Account number</string>
<!-- BECS Debit Widget - account number field - incomplete error message -->
Expand Down Expand Up @@ -67,6 +69,8 @@
<string name="stripe_paymentsheet_buy_using_upi_id">Buy using a UPI ID</string>
<!-- Payment Method type brand name. -->
<string name="stripe_paymentsheet_payment_method_au_becs_debit">AU BECS Direct Debit</string>
<!-- Payment Method type brand name. -->
<string name="stripe_paymentsheet_payment_method_bacs_debit">Bacs Direct Debit</string>
<!-- Title for credit card number entry field -->
<string name="stripe_paymentsheet_payment_method_card">Card</string>
<!-- Label for Konbini Payment Method -->
Expand Down
5 changes: 5 additions & 0 deletions payments-ui-core/src/main/assets/lpms.json
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,11 @@
}
}
},
{
"type": "bacs_debit",
"async": true,
"fields": []
},
{
"type": "revolut_pay",
"async": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stripe.android.ui.core.elements

import androidx.annotation.RestrictTo
import androidx.annotation.StringRes
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import com.stripe.android.ui.core.R
Expand All @@ -23,6 +24,9 @@ data class ContactInformationSpec(
val collectEmail: Boolean = true,
@SerialName("collect_phone")
val collectPhone: Boolean = true,
@SerialName("name_label")
@StringRes
val nameLabel: Int = R.string.stripe_name_on_card
) : FormItemSpec() {
override val apiPath: IdentifierSpec = IdentifierSpec()

Expand All @@ -31,7 +35,7 @@ data class ContactInformationSpec(
SimpleTextElement(
controller = SimpleTextFieldController(
textFieldConfig = SimpleTextFieldConfig(
label = R.string.stripe_name_on_card,
label = nameLabel,
capitalization = KeyboardCapitalization.Words,
keyboard = KeyboardType.Text
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -46,7 +47,10 @@ import com.stripe.android.paymentsheet.forms.UpiRequirement
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.AddressSpec
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
Expand Down Expand Up @@ -468,6 +472,29 @@ class LpmRepository constructor(
requirement = AuBecsDebitRequirement,
formSpec = LayoutSpec(sharedDataSpec.fields)
)
PaymentMethod.Type.BacsDebit.code -> {
val localFields = listOf(
ContactInformationSpec(
collectPhone = false,
nameLabel = R.string.stripe_bacs_full_name
),
BacsDebitBankAccountSpec(),
AddressSpec(),
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)
)
}
PaymentMethod.Type.USBankAccount.code -> {
val pmo = stripeIntent.getPaymentMethodOptions()[PaymentMethod.Type.USBankAccount.code]
val verificationMethod = (pmo as? Map<*, *>)?.get("verification_method") as? String
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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)
)
}

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
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
)

Expand Down Expand Up @@ -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 -> {}
Expand Down Expand Up @@ -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 -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,14 @@ internal class PlaygroundTestDriver(
}.isSuccess
}
}

is AuthorizeAction.Bacs.Confirm -> {}
is AuthorizeAction.Bacs.ModifyDetails -> {
buyButton.apply {
waitProcessingComplete()
isEnabled()
isDisplayed()
}
}
null -> {}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
)
Expand Down
Loading

0 comments on commit c3a734a

Please sign in to comment.