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 Dec 15, 2023
1 parent 8df8e93 commit c947e33
Show file tree
Hide file tree
Showing 19 changed files with 310 additions and 17 deletions.
4 changes: 0 additions & 4 deletions payments-ui-core/api/payments-ui-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions payments-ui-core/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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
Expand Up @@ -10,27 +10,35 @@ 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(
initialValues: Map<IdentifierSpec, String?>
) = 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]"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<IdentifierSpec, String?>
) = 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()
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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 Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
}
}
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,15 @@ internal class PlaygroundTestDriver(
}.isSuccess
}
}

is AuthorizeAction.Bacs.Confirm -> {}
is AuthorizeAction.Bacs.ModifyDetails -> {
buyButton.apply {
scrollTo()
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 @@ -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 {
Expand Down
Loading

0 comments on commit c947e33

Please sign in to comment.