Skip to content

Commit 6a83cba

Browse files
cttsai-stripecodex
andcommitted
Generalize automatic-tax billing address primitives (MOBILESDK-4667)
Committed-By-Agent: codex Co-authored-by: codex <noreply@openai.com>
1 parent 90e0ce1 commit 6a83cba

2 files changed

Lines changed: 67 additions & 40 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.stripe.android.ui.core.elements
2+
3+
import androidx.annotation.RestrictTo
4+
import com.stripe.android.uicore.elements.IdentifierSpec
5+
6+
/**
7+
* Billing address fields required in addition to country for automatic tax calculation.
8+
* Countries absent from this map require country only.
9+
*
10+
* Source: https://docs.stripe.com/tax/customer-locations
11+
*/
12+
private val additionalAutomaticTaxFieldsByCountry: Map<String, Set<IdentifierSpec>> = mapOf(
13+
"CA" to setOf(IdentifierSpec.PostalCode),
14+
"GB" to setOf(IdentifierSpec.PostalCode),
15+
"IN" to setOf(IdentifierSpec.PostalCode),
16+
"PR" to setOf(IdentifierSpec.Line1, IdentifierSpec.City, IdentifierSpec.PostalCode),
17+
"US" to setOf(IdentifierSpec.Line1, IdentifierSpec.City, IdentifierSpec.State, IdentifierSpec.PostalCode),
18+
)
19+
20+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
21+
fun automaticTaxRequiredFields(countryCode: String): Set<IdentifierSpec> {
22+
return additionalAutomaticTaxFieldsByCountry[countryCode].orEmpty()
23+
}

payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/CardBillingAddressElement.kt renamed to payments-ui-core/src/main/java/com/stripe/android/ui/core/elements/BillingAddressElement.kt

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,20 @@ import com.stripe.android.uicore.utils.mapAsStateFlow
3030
import kotlinx.coroutines.flow.StateFlow
3131

3232
/**
33-
* This is a special type of AddressElement that
34-
* removes fields from the address based on the country. It
35-
* is only intended to be used with the card payment method.
33+
* An address element that dynamically removes fields based on the selected country and collection mode.
3634
*/
3735
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
38-
class CardBillingAddressElement(
36+
open class BillingAddressElement(
3937
override val identifier: IdentifierSpec,
40-
rawValuesMap: Map<IdentifierSpec, String?> = emptyMap(),
41-
countryCodes: Set<String> = emptySet(),
42-
countryDropdownFieldController: DropdownFieldController = DropdownFieldController(
43-
CountryConfig(countryCodes),
44-
rawValuesMap[IdentifierSpec.Country]
45-
),
38+
rawValuesMap: Map<IdentifierSpec, String?>,
39+
countryCodes: Set<String>,
40+
countryElement: CountryElement,
4641
autocompleteAddressInteractorFactory: AutocompleteAddressInteractor.Factory?,
4742
sameAsShippingElement: SameAsShippingElement?,
4843
shippingValuesMap: Map<IdentifierSpec, String?>?,
49-
private val collectionConfiguration: BillingDetailsCollectionConfiguration =
50-
BillingDetailsCollectionConfiguration(),
51-
private val shouldHideCountryOnNoAddressCollection: Boolean = true,
52-
private val requiresBillingAddressForAutomaticTax: Boolean = false,
44+
private val collectionConfiguration: BillingDetailsCollectionConfiguration,
45+
private val shouldHideCountryOnNoAddressCollection: Boolean,
46+
private val requiresBillingAddressForAutomaticTax: Boolean,
5347
) : AddressFieldsElement {
5448
private val nameConfig = if (collectionConfiguration.collectName) {
5549
AddressFieldConfiguration.REQUIRED
@@ -80,7 +74,7 @@ class CardBillingAddressElement(
8074
nameConfig = nameConfig,
8175
phoneNumberConfig = phoneNumberConfig,
8276
emailConfig = emailConfig,
83-
countryDropdownFieldController = countryDropdownFieldController,
77+
countryDropdownFieldController = countryElement.controller,
8478
interactorFactory = factory,
8579
shippingValuesMap = shippingValuesMap,
8680
sameAsShippingElement = sameAsShippingElement,
@@ -95,10 +89,7 @@ class CardBillingAddressElement(
9589
phoneNumberConfig = phoneNumberConfig,
9690
emailConfig = emailConfig,
9791
),
98-
countryElement = CountryElement(
99-
identifier = IdentifierSpec.Country,
100-
controller = countryDropdownFieldController,
101-
),
92+
countryElement = countryElement,
10293
shippingValuesMap = shippingValuesMap,
10394
sameAsShippingElement = sameAsShippingElement,
10495
hideCountry = shouldHideCountryOnNoAddressCollection &&
@@ -107,7 +98,7 @@ class CardBillingAddressElement(
10798
}
10899

109100
private val addressElementSectionController = addressElement.sectionFieldErrorController()
110-
private val cardBillingAddressElementSectionErrorController =
101+
private val billingAddressElementSectionErrorController =
111102
object : SectionFieldValidationController by addressElementSectionController, SectionFieldComposable {
112103
override val validationMessage: StateFlow<FieldValidationMessage?> =
113104
addressElement.addressController
@@ -163,7 +154,7 @@ class CardBillingAddressElement(
163154
// Save for future use puts this in the controller rather than element
164155
// card and achv2 uses save for future use
165156
val hiddenIdentifiers: StateFlow<Set<IdentifierSpec>> =
166-
countryDropdownFieldController.rawFieldValue.mapAsStateFlow { countryCode ->
157+
countryElement.controller.rawFieldValue.mapAsStateFlow { countryCode ->
167158
when (collectionConfiguration.address) {
168159
BillingDetailsCollectionConfiguration.AddressCollectionMode.Never -> {
169160
FieldType.entries
@@ -203,7 +194,7 @@ class CardBillingAddressElement(
203194
override fun getFormFieldValueFlow() = addressElement.getFormFieldValueFlow()
204195

205196
override fun sectionFieldErrorController(): SectionFieldValidationController =
206-
cardBillingAddressElementSectionErrorController
197+
billingAddressElementSectionErrorController
207198

208199
override fun setRawValue(rawValuesMap: Map<IdentifierSpec, String?>) = addressElement.setRawValue(rawValuesMap)
209200

@@ -215,23 +206,36 @@ class CardBillingAddressElement(
215206
}
216207

217208
/**
218-
* Billing address fields required in addition to the country, for a Checkout Session using
219-
* automatic tax with the billing address as the tax source. Most countries only need the
220-
* country. Source: https://docs.stripe.com/tax/customer-locations
221-
*
222-
* Billing only - shipping is out of scope, since it's always collected in full for delivery
223-
* regardless of tax, so there's no omittable mode there for tax to rescue.
209+
* The card-specific name is retained for compatibility. New payment-method forms should use
210+
* [BillingAddressElement] directly.
224211
*/
225-
private val additionalAutomaticTaxFieldsByCountry: Map<String, Set<IdentifierSpec>> = mapOf(
226-
"CA" to setOf(IdentifierSpec.PostalCode),
227-
"GB" to setOf(IdentifierSpec.PostalCode),
228-
"IN" to setOf(IdentifierSpec.PostalCode),
229-
"PR" to setOf(IdentifierSpec.Line1, IdentifierSpec.City, IdentifierSpec.PostalCode),
230-
"US" to setOf(IdentifierSpec.Line1, IdentifierSpec.City, IdentifierSpec.State, IdentifierSpec.PostalCode),
212+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
213+
class CardBillingAddressElement(
214+
identifier: IdentifierSpec,
215+
rawValuesMap: Map<IdentifierSpec, String?> = emptyMap(),
216+
countryCodes: Set<String> = emptySet(),
217+
countryDropdownFieldController: DropdownFieldController = DropdownFieldController(
218+
CountryConfig(countryCodes),
219+
rawValuesMap[IdentifierSpec.Country]
220+
),
221+
autocompleteAddressInteractorFactory: AutocompleteAddressInteractor.Factory?,
222+
sameAsShippingElement: SameAsShippingElement?,
223+
shippingValuesMap: Map<IdentifierSpec, String?>?,
224+
collectionConfiguration: BillingDetailsCollectionConfiguration = BillingDetailsCollectionConfiguration(),
225+
shouldHideCountryOnNoAddressCollection: Boolean = true,
226+
requiresBillingAddressForAutomaticTax: Boolean = false,
227+
) : BillingAddressElement(
228+
identifier = identifier,
229+
rawValuesMap = rawValuesMap,
230+
countryCodes = countryCodes,
231+
countryElement = CountryElement(
232+
identifier = IdentifierSpec.Country,
233+
controller = countryDropdownFieldController,
234+
),
235+
autocompleteAddressInteractorFactory = autocompleteAddressInteractorFactory,
236+
sameAsShippingElement = sameAsShippingElement,
237+
shippingValuesMap = shippingValuesMap,
238+
collectionConfiguration = collectionConfiguration,
239+
shouldHideCountryOnNoAddressCollection = shouldHideCountryOnNoAddressCollection,
240+
requiresBillingAddressForAutomaticTax = requiresBillingAddressForAutomaticTax,
231241
)
232-
233-
private fun automaticTaxRequiredFields(countryCode: String): Set<IdentifierSpec> {
234-
// Matches the raw, non-uppercased comparison the AVS check above uses - countryCode is
235-
// already an uppercase ISO code in practice (from CountryConfig).
236-
return additionalAutomaticTaxFieldsByCountry[countryCode].orEmpty()
237-
}

0 commit comments

Comments
 (0)