Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

callback for postal code complete #4424

Merged
merged 4 commits into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions payments-core/api/payments-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -5671,6 +5671,7 @@ public abstract interface class com/stripe/android/view/CardInputListener {
public abstract fun onCvcComplete ()V
public abstract fun onExpirationComplete ()V
public abstract fun onFocusChange (Lcom/stripe/android/view/CardInputListener$FocusField;)V
public abstract fun onPostalCodeComplete ()V
}

public final class com/stripe/android/view/CardInputListener$FocusField : java/lang/Enum {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,11 @@ interface CardInputListener {
* the user edits the CVC.
*/
fun onCvcComplete()

/**
* Called when a valid postal code has been entered.
* May be called multiple times, if the user edits the field.
* If the [CardWidget] is not collecting US card, any non-empty postal is considered valid.
*/
fun onPostalCodeComplete()
}
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,12 @@ class CardInputWidget @JvmOverloads constructor(
}
}

postalCodeEditText.setAfterTextChangedListener {
if (isPostalRequired() && postalCodeEditText.hasValidPostal()) {
cardInputListener?.onPostalCodeComplete()
}
}

cardNumberEditText.completionCallback = {
scrollEnd()
cardInputListener?.onCardComplete()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ class CardMultilineWidget @JvmOverloads constructor(
}
}

private fun isPostalRequired() =
(postalCodeRequired || usZipCodeRequired) && shouldShowPostalCode

/**
* A [PaymentMethodCreateParams.Card] representing the card details if all fields are valid;
* otherwise `null`
Expand Down Expand Up @@ -367,6 +370,12 @@ class CardMultilineWidget @JvmOverloads constructor(
cvcEditText.shouldShowError = false
}

postalCodeEditText.setAfterTextChangedListener {
if (isPostalRequired() && postalCodeEditText.hasValidPostal()) {
cardInputListener?.onPostalCodeComplete()
}
}

adjustViewForPostalCodeAttribute(shouldShowPostalCode)

cardNumberEditText.updateLengthFilter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ class PostalCodeEditText @JvmOverloads constructor(
US
}

/**
* Returns if the postal is valid. If config is not US, any non-empty postal is valid.
*/
internal fun hasValidPostal() =
config == Config.US && ZIP_CODE_PATTERN.matcher(fieldText)
.matches() || config == Config.Global && fieldText.isNotEmpty()

private companion object {
private const val MAX_LENGTH_US = 5

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,48 @@ internal class CardInputWidgetTest {
)
}

@Test
fun usZipCodeRequired_whenFalse_shouldNotCallOnPostalCodeComplete() {
cardInputWidget.usZipCodeRequired = false
postalCodeEditText.setText(POSTAL_CODE_VALUE)
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(0)
}

@Test
fun usZipCodeRequired_whenTrue_withValidZip_shouldCallOnPostalCodeComplete() {
cardInputWidget.usZipCodeRequired = true
postalCodeEditText.setText(POSTAL_CODE_VALUE)
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(1)
}

@Test
fun postalCodeEnabled_whenFalse_shouldNotCallOnPostalCodeComplete() {
cardInputWidget.postalCodeEnabled = false
postalCodeEditText.setText("123")
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(0)
}

@Test
fun usZipCodeRequired_whenTrue_withInvalidZip_shouldNotCallOnPostalCodeComplete() {
cardInputWidget.usZipCodeRequired = true
postalCodeEditText.setText("1234")
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(0)
}

@Test
fun postalCode_whenTrue_withNonEmptyZip_shouldCallOnPostalCodeComplete() {
cardInputWidget.postalCodeRequired = true
postalCodeEditText.setText("1234")
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(1)
}

@Test
fun postalCode_whenTrue_withEmptyZip_shouldNotCallOnPostalCodeComplete() {
cardInputWidget.postalCodeRequired = true
postalCodeEditText.setText("")
assertThat(cardInputListener.onPostalCodeCompleteCalls).isEqualTo(0)
}

@Test
fun `setCvcLabel is not reset when card number entered`() {
cardInputWidget.setCvcLabel("123")
Expand Down Expand Up @@ -1578,6 +1620,7 @@ internal class CardInputWidgetTest {
var cardCompleteCalls = 0
var expirationCompleteCalls = 0
var cvcCompleteCalls = 0
var onPostalCodeCompleteCalls = 0

override fun onFocusChange(focusField: CardInputListener.FocusField) {
focusedFields.add(focusField)
Expand All @@ -1594,6 +1637,10 @@ internal class CardInputWidgetTest {
override fun onCvcComplete() {
cvcCompleteCalls++
}

override fun onPostalCodeComplete() {
onPostalCodeCompleteCalls++
}
}

private companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,35 +330,42 @@ internal class CardMultilineWidgetTest {
@Test
fun paymentMethodCreateParams_whenPostalCodeIsRequiredAndValueIsBlank_returnsNull() {
cardMultilineWidget.setShouldShowPostalCode(true)
cardMultilineWidget.setCardInputListener(fullCardListener)
cardMultilineWidget.postalCodeRequired = true

fullGroup.cardNumberEditText.setText(VISA_WITH_SPACES)
fullGroup.expiryDateEditText.append("12")
fullGroup.expiryDateEditText.append("50")
fullGroup.cvcEditText.append(CVC_VALUE_COMMON)
fullGroup.postalCodeEditText.setText("")

assertThat(cardMultilineWidget.paymentMethodCreateParams)
.isNull()
verify(fullCardListener, never()).onPostalCodeComplete()
}

@Test
fun paymentMethodCreateParams_whenPostalCodeIsRequiredAndValueIsNotBlank_returnsNotNull() {
cardMultilineWidget.setShouldShowPostalCode(true)
cardMultilineWidget.postalCodeRequired = false
cardMultilineWidget.setCardInputListener(fullCardListener)
cardMultilineWidget.postalCodeRequired = true

fullGroup.cardNumberEditText.setText(VISA_WITH_SPACES)
fullGroup.expiryDateEditText.append("12")
fullGroup.expiryDateEditText.append("50")
fullGroup.cvcEditText.append(CVC_VALUE_COMMON)
fullGroup.postalCodeEditText.setText("1234")

assertThat(cardMultilineWidget.paymentMethodCreateParams)
.isNotNull()
verify(fullCardListener).onPostalCodeComplete()
}

@Test
fun paymentMethodCreateParams_whenPostalCodeIsNotRequiredAndValueIsBlank_returnsNotNull() {
cardMultilineWidget.setShouldShowPostalCode(true)
cardMultilineWidget.postalCodeRequired = false
cardMultilineWidget.setCardInputListener(fullCardListener)

fullGroup.cardNumberEditText.setText(VISA_WITH_SPACES)
fullGroup.expiryDateEditText.append("12")
Expand All @@ -367,6 +374,7 @@ internal class CardMultilineWidgetTest {

assertThat(cardMultilineWidget.paymentMethodCreateParams)
.isNotNull()
verify(fullCardListener, never()).onPostalCodeComplete()
}

@Test
Expand Down Expand Up @@ -586,6 +594,9 @@ internal class CardMultilineWidgetTest {
verify(noZipCardListener).onFocusChange(CardInputListener.FocusField.ExpiryDate)
assertThat(noZipGroup.expiryDateEditText.hasFocus())
.isTrue()

verify(fullCardListener, never()).onPostalCodeComplete()
verify(noZipCardListener, never()).onPostalCodeComplete()
}

@Test
Expand All @@ -606,6 +617,9 @@ internal class CardMultilineWidgetTest {
verify(noZipCardListener).onFocusChange(CardInputListener.FocusField.Cvc)
assertThat(noZipGroup.cvcEditText.hasFocus())
.isTrue()

verify(fullCardListener, never()).onPostalCodeComplete()
verify(noZipCardListener, never()).onPostalCodeComplete()
}

@Test
Expand All @@ -630,6 +644,9 @@ internal class CardMultilineWidgetTest {
verify(noZipCardListener, never()).onFocusChange(CardInputListener.FocusField.PostalCode)
assertThat(noZipGroup.cvcEditText.hasFocus())
.isTrue()

verify(fullCardListener, never()).onPostalCodeComplete()
verify(noZipCardListener, never()).onPostalCodeComplete()
}

@Test
Expand All @@ -646,6 +663,10 @@ internal class CardMultilineWidgetTest {
.isTrue()
assertThat(fullGroup.cardNumberEditText.text?.toString())
.isEqualTo(VISA_WITH_SPACES.take(VISA_WITH_SPACES.length - 1))

verify(fullCardListener, never()).onPostalCodeComplete()

verify(fullCardListener, never()).onPostalCodeComplete()
}

@Test
Expand All @@ -662,6 +683,8 @@ internal class CardMultilineWidgetTest {
.isTrue()
assertThat(noZipGroup.cardNumberEditText.text?.toString())
.isEqualTo(VISA_WITH_SPACES.take(VISA_WITH_SPACES.length - 1))

verify(noZipCardListener, never()).onPostalCodeComplete()
}

@Test
Expand Down Expand Up @@ -694,6 +717,9 @@ internal class CardMultilineWidgetTest {
.isTrue()
assertThat(noZipGroup.expiryDateEditText.fieldText)
.isEqualTo("12/5")

verify(fullCardListener, never()).onPostalCodeComplete()
verify(noZipCardListener, never()).onPostalCodeComplete()
}

@Test
Expand Down Expand Up @@ -805,6 +831,7 @@ internal class CardMultilineWidgetTest {
@Test
fun usZipCodeRequired_whenFalse_shouldSetPostalCodeHint() {
cardMultilineWidget.usZipCodeRequired = false
cardMultilineWidget.setCardInputListener(fullCardListener)
assertThat(cardMultilineWidget.postalInputLayout.hint)
.isEqualTo("Postal code")

Expand All @@ -826,11 +853,14 @@ internal class CardMultilineWidgetTest {
.build()
)
)

verify(fullCardListener, never()).onPostalCodeComplete()
}

@Test
fun usZipCodeRequired_whenTrue_withInvalidZipCode_shouldReturnNullCard() {
cardMultilineWidget.usZipCodeRequired = true
cardMultilineWidget.setCardInputListener(fullCardListener)
assertThat(cardMultilineWidget.postalInputLayout.hint)
.isEqualTo("ZIP Code")

Expand All @@ -843,11 +873,14 @@ internal class CardMultilineWidgetTest {
fullGroup.postalCodeEditText.setText("1234")
assertThat(cardMultilineWidget.cardParams)
.isNull()

verify(fullCardListener, never()).onPostalCodeComplete()
}

@Test
fun usZipCodeRequired_whenTrue_withValidZipCode_shouldReturnNotNullCard() {
cardMultilineWidget.usZipCodeRequired = true
cardMultilineWidget.setCardInputListener(fullCardListener)
assertThat(cardMultilineWidget.postalInputLayout.hint)
.isEqualTo("ZIP Code")

Expand All @@ -872,6 +905,8 @@ internal class CardMultilineWidgetTest {
.build()
)
)

verify(fullCardListener).onPostalCodeComplete()
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ internal class CardDataCollectionFragment<ViewModelType : BaseSheetViewModel<*>>
// move to first field when CVC is complete
billingAddressView.focusFirstField()
}

override fun onPostalCodeComplete() {}
})

sheetViewModel.processing.observe(viewLifecycleOwner) { isProcessing ->
Expand Down