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

fix: Make CardField postal code behavior consistent on iOS and Android #989

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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
### New features

- Added a `defaultValues` prop to the `CardForm` component. Currently only accepts `countryCode`, and is Android-only. [#974](https://github.com/stripe/stripe-react-native/pull/974)
- Added the `countryCode` prop to the `CardField` component. [#989](https://github.com/stripe/stripe-react-native/pull/989)
- Added option to create a PII token (represents the details of personally identifiable information) to the `createToken` method. [#976](https://github.com/stripe/stripe-react-native/pull/976)

### Fixes

- Resolve with an Error (of type `Canceled`) if no payment option is selected in the Payment Sheet custom flow (i.e., the `x` button is clicked to close the Payment Sheet). [#975](https://github.com/stripe/stripe-react-native/pull/975)
- Fixed an issue on Android where the `complete` field in the `onCardChange` callback would incorrectly be set to `true` even if the postal code wasn't filled out. [#989](https://github.com/stripe/stripe-react-native/pull/989)
- Make `SetupIntent.lastSetupError` and `PaymentIntent.lastPaymentError` object shape consistent on iOS and Android.[#990](https://github.com/stripe/stripe-react-native/pull/990)

## 0.12.0
Expand Down
21 changes: 15 additions & 6 deletions android/src/main/java/com/reactnativestripesdk/CardFieldView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.FrameLayout
import androidx.core.os.LocaleListCompat
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.EventDispatcher
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.stripe.android.core.model.CountryCode
import com.stripe.android.core.model.CountryUtils
import com.stripe.android.databinding.CardInputWidgetBinding
import com.stripe.android.model.Address
import com.stripe.android.model.PaymentMethodCreateParams
Expand All @@ -33,6 +36,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
private var mEventDispatcher: EventDispatcher? = context.getNativeModule(UIManagerModule::class.java)?.eventDispatcher
private var dangerouslyGetFullCardDetails: Boolean = false
private var currentFocusedField: String? = null
private var isCardValid = false

init {
cardInputWidgetBinding.container.isFocusable = true
Expand Down Expand Up @@ -197,6 +201,14 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
mCardWidget.postalCodeEnabled = isEnabled
}

fun setCountryCode(countryCode: String?) {
val doesCountryUsePostalCode = CountryUtils.doesCountryUsePostalCode(
CountryCode.create(value = countryCode ?: LocaleListCompat.getAdjustedDefault()[0].country)
)
mCardWidget.postalCodeRequired = doesCountryUsePostalCode
mCardWidget.postalCodeEnabled = doesCountryUsePostalCode
}

fun getValue(): MutableMap<String, Any?> {
return cardDetails
}
Expand Down Expand Up @@ -224,7 +236,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {

private fun sendCardDetailsEvent() {
mEventDispatcher?.dispatchEvent(
CardChangedEvent(id, cardDetails, mCardWidget.postalCodeEnabled, cardParams != null, dangerouslyGetFullCardDetails))
CardChangedEvent(id, cardDetails, mCardWidget.postalCodeEnabled, isCardValid, dangerouslyGetFullCardDetails))
}

private fun setListeners() {
Expand All @@ -246,6 +258,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}

mCardWidget.setCardValidCallback { isValid, invalidFields ->
isCardValid = isValid
fun getCardValidationState(field: CardValidCallback.Fields, editTextField: StripeEditText): String {
if (invalidFields.contains(field)) {
return if (editTextField.shouldShowError) "Invalid"
Expand All @@ -263,6 +276,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
} else {
cardParams = null
cardAddress = null
sendCardDetailsEvent()
}
}

Expand All @@ -284,8 +298,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
if (splitText.size == 2) {
cardDetails["expiryYear"] = var1.toString().split("/")[1].toIntOrNull()
}

sendCardDetailsEvent()
}
})

Expand All @@ -294,7 +306,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
override fun afterTextChanged(p0: Editable?) {}
override fun onTextChanged(var1: CharSequence?, var2: Int, var3: Int, var4: Int) {
cardDetails["postalCode"] = var1.toString()
sendCardDetailsEvent()
}
})

Expand All @@ -305,7 +316,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
if (dangerouslyGetFullCardDetails) {
cardDetails["number"] = var1.toString().replace(" ", "")
}
sendCardDetailsEvent()
}
})

Expand All @@ -316,7 +326,6 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
if (dangerouslyGetFullCardDetails) {
cardDetails["cvc"] = var1.toString()
}
sendCardDetailsEvent()
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class CardFieldViewManager : SimpleViewManager<CardFieldView>() {
view.setPlaceHolders(placeholders)
}

@ReactProp(name = "countryCode")
fun setPlaceHolders(view: CardFieldView, countryCode: String?) {
view.setCountryCode(countryCode)
}

override fun createViewInstance(reactContext: ThemedReactContext): CardFieldView {
val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
val view = CardFieldView(reactContext)
Expand Down
1 change: 1 addition & 0 deletions ios/CardFieldManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

@interface RCT_EXTERN_MODULE(CardFieldManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(postalCodeEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(countryCode, NSString)
RCT_EXPORT_VIEW_PROPERTY(onCardChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onFocusChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(cardStyle, NSDictionary)
Expand Down
6 changes: 6 additions & 0 deletions ios/CardFieldView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate {
}
}

@objc var countryCode: String? {
didSet {
cardField.countryCode = countryCode
}
}

@objc var placeholders: NSDictionary = NSDictionary() {
didSet {
if let numberPlaceholder = placeholders["number"] as? String {
Expand Down
5 changes: 5 additions & 0 deletions src/components/CardField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ const CardFieldNative =
*/
export interface Props extends AccessibilityProps {
style?: StyleProp<ViewStyle>;
/** Controls if a postal code entry field can be displayed to the user. Defaults to false. If true, the type of code entry shown is controlled by the set countryCode prop. Some country codes may result in no postal code entry being shown if those countries do not commonly use postal codes. If false, no postal code entry will ever be displayed. */
postalCodeEnabled?: boolean;
/** Controls the postal code entry shown (if the postalCodeEnabled prop is set to true). Defaults to the device's default locale. */
countryCode?: string;
cardStyle?: CardFieldInput.Styles;
placeholders?: CardFieldInput.Placeholders;
autofocus?: boolean;
Expand Down Expand Up @@ -74,6 +77,7 @@ export const CardField = forwardRef<CardFieldInput.Methods, Props>(
cardStyle,
placeholders,
postalCodeEnabled,
countryCode,
...props
},
ref
Expand Down Expand Up @@ -175,6 +179,7 @@ export const CardField = forwardRef<CardFieldInput.Methods, Props>(
onCardChange={onCardChangeHandler}
onFocusChange={onFocusHandler}
postalCodeEnabled={postalCodeEnabled ?? true}
countryCode={countryCode ?? null}
cardStyle={{
backgroundColor: cardStyle?.backgroundColor,
borderColor: cardStyle?.borderColor,
Expand Down
1 change: 1 addition & 0 deletions src/types/components/CardFieldInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface NativeProps {
value?: Partial<Details>;
postalCodeEnabled?: boolean;
autofocus?: boolean;
countryCode: string | null;
onCardChange(event: NativeSyntheticEvent<Details>): void;
onFocusChange(
event: NativeSyntheticEvent<{ focusedField: FieldName | null }>
Expand Down