diff --git a/base-v3/src/main/java/com/adyen/checkout/base/component/BasePaymentComponent.java b/base-v3/src/main/java/com/adyen/checkout/base/component/BasePaymentComponent.java index ae6e658845..89d5fa1d63 100644 --- a/base-v3/src/main/java/com/adyen/checkout/base/component/BasePaymentComponent.java +++ b/base-v3/src/main/java/com/adyen/checkout/base/component/BasePaymentComponent.java @@ -99,6 +99,7 @@ public PaymentComponentState getState() { * @param inputData {@link InputDataT} */ public final void inputDataChanged(@NonNull InputDataT inputData) { + Logger.v(TAG, "inputDataChanged"); final OutputDataT newOutputData = onInputDataChanged(inputData); if (!newOutputData.equals(mOutputData)) { mOutputData = newOutputData; diff --git a/card-base-core/src/main/java/com/adyen/checkout/card/data/CardType.java b/card-base-core/src/main/java/com/adyen/checkout/card/data/CardType.java index 2b2e62de69..50994ad2a1 100644 --- a/card-base-core/src/main/java/com/adyen/checkout/card/data/CardType.java +++ b/card-base-core/src/main/java/com/adyen/checkout/card/data/CardType.java @@ -92,14 +92,14 @@ public static List estimate(@NonNull String cardNumber) { } /** - * Get CardType from txVariant. + * Get CardType from the brand name as it appears in the Checkout API. + * @see */ @Nullable - public static CardType getCardTypeByTxVariant(@NonNull String txVariant) { - return MAPPED_BY_NAME.get(txVariant); + public static CardType getByBrandName(@NonNull String brand) { + return MAPPED_BY_NAME.get(brand); } - CardType(@NonNull String txVariant, @NonNull Pattern pattern) { mTxVariant = txVariant; mPattern = pattern; diff --git a/card-base/src/main/java/com/adyen/checkout/card/CardComponent.java b/card-base/src/main/java/com/adyen/checkout/card/CardComponent.java index cdba25f343..1e324f0399 100644 --- a/card-base/src/main/java/com/adyen/checkout/card/CardComponent.java +++ b/card-base/src/main/java/com/adyen/checkout/card/CardComponent.java @@ -15,7 +15,6 @@ import com.adyen.checkout.base.PaymentComponentProvider; import com.adyen.checkout.base.component.BasePaymentComponent; -import com.adyen.checkout.base.model.paymentmethods.InputDetail; import com.adyen.checkout.base.model.paymentmethods.PaymentMethod; import com.adyen.checkout.base.model.paymentmethods.StoredPaymentMethod; import com.adyen.checkout.base.model.payments.request.CardPaymentMethod; @@ -33,7 +32,9 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; public final class CardComponent extends BasePaymentComponent< CardConfiguration, @@ -47,6 +48,14 @@ public final class CardComponent extends BasePaymentComponent< private static final String[] PAYMENT_METHOD_TYPES = {PaymentMethodTypes.SCHEME}; private static final int BIN_VALUE_LENGTH = 6; + private static final Set NO_CVC_BRANDS; + + static { + final HashSet brandSet = new HashSet<>(); + brandSet.add(CardType.BCMC); + NO_CVC_BRANDS = Collections.unmodifiableSet(brandSet); + } + private List mFilteredSupportedCards = Collections.emptyList(); private CardInputData mStoredPaymentInputData; @@ -73,7 +82,7 @@ public CardComponent(@NonNull StoredPaymentMethod paymentMethod, @NonNull CardCo mStoredPaymentInputData.setExpiryDate(ExpiryDate.EMPTY_DATE); } - final CardType cardType = CardType.getCardTypeByTxVariant(paymentMethod.getBrand()); + final CardType cardType = CardType.getByBrandName(paymentMethod.getBrand()); if (cardType != null) { final List storedCardType = new ArrayList<>(); storedCardType.add(cardType); @@ -123,7 +132,8 @@ protected CardOutputData onInputDataChanged(@NonNull CardInputData inputData) { validateExpiryDate(inputData.getExpiryDate()), validateSecurityCode(inputData.getSecurityCode()), validateHolderName(inputData.getHolderName()), - inputData.isStorePaymentEnable() + inputData.isStorePaymentEnable(), + isCvcHidden() ); } @@ -163,7 +173,9 @@ protected CardComponentState createComponentState() { card.setNumber(outputData.getCardNumberField().getValue()); } - card.setSecurityCode(outputData.getSecurityCodeField().getValue()); + if (!isCvcHidden()) { + card.setSecurityCode(outputData.getSecurityCodeField().getValue()); + } final ExpiryDate expiryDateResult = outputData.getExpiryDateField().getValue(); @@ -243,16 +255,29 @@ private ValidatedField validateExpiryDate(@NonNull ExpiryDate expiry } private ValidatedField validateSecurityCode(@NonNull String securityCode) { - final InputDetail securityCodeInputDetail = getInputDetail("cvc"); - final boolean isRequired = securityCodeInputDetail == null || !securityCodeInputDetail.isOptional(); - if (isRequired) { + if (isCvcHidden()) { + return new ValidatedField<>(securityCode, ValidatedField.Validation.VALID); + } else { final CardType firstCardType = !mFilteredSupportedCards.isEmpty() ? mFilteredSupportedCards.get(0) : null; return CardValidationUtils.validateSecurityCode(securityCode, firstCardType); + } + } + + private boolean isCvcHidden() { + if (isStoredPaymentMethod()) { + return getConfiguration().isHideCvcStoredCard() || isBrandWithoutCvc(((StoredPaymentMethod) getPaymentMethod()).getBrand()); } else { - return new ValidatedField<>(securityCode, ValidatedField.Validation.VALID); + return getConfiguration().isHideCvc(); } } + private boolean isBrandWithoutCvc(@Nullable String brand) { + if (TextUtils.isEmpty(brand)) { + return false; + } + return NO_CVC_BRANDS.contains(CardType.getByBrandName(brand)); + } + private ValidatedField validateHolderName(@NonNull String holderName) { if (isHolderNameRequire() && TextUtils.isEmpty(holderName)) { return new ValidatedField<>(holderName, ValidatedField.Validation.INVALID); @@ -261,20 +286,6 @@ private ValidatedField validateHolderName(@NonNull String holderName) { } } - @Nullable - private InputDetail getInputDetail(@NonNull String key) { - final List details = getPaymentMethod().getDetails(); - if (details != null) { - for (InputDetail inputDetail : getPaymentMethod().getDetails()) { - if (key.equals(inputDetail.getKey())) { - return inputDetail; - } - } - } - - return null; - } - private String getBinValueFromCardNumber(String cardNumber) { return cardNumber.length() < BIN_VALUE_LENGTH ? cardNumber : cardNumber.substring(0, BIN_VALUE_LENGTH); } diff --git a/card-base/src/main/java/com/adyen/checkout/card/CardComponentProvider.java b/card-base/src/main/java/com/adyen/checkout/card/CardComponentProvider.java index 778779d22d..90f6230ab5 100644 --- a/card-base/src/main/java/com/adyen/checkout/card/CardComponentProvider.java +++ b/card-base/src/main/java/com/adyen/checkout/card/CardComponentProvider.java @@ -75,7 +75,7 @@ private CardConfiguration checkSupportedCardTypes(@NonNull PaymentMethod payment if (brands != null && !brands.isEmpty()) { supportedCardTypes = new ArrayList<>(); for (String brand : brands) { - final CardType brandType = CardType.getCardTypeByTxVariant(brand); + final CardType brandType = CardType.getByBrandName(brand); if (brandType != null) { supportedCardTypes.add(brandType); } else { diff --git a/card-base/src/main/java/com/adyen/checkout/card/CardConfiguration.java b/card-base/src/main/java/com/adyen/checkout/card/CardConfiguration.java index 832c3fa9ff..f3b833aeed 100644 --- a/card-base/src/main/java/com/adyen/checkout/card/CardConfiguration.java +++ b/card-base/src/main/java/com/adyen/checkout/card/CardConfiguration.java @@ -48,6 +48,8 @@ public class CardConfiguration extends Configuration { private final boolean mHolderNameRequire; private final List mSupportedCardTypes; private final boolean mShowStorePaymentField; + private final boolean mHideCvc; + private final boolean mHideCvcStoredCard; public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public CardConfiguration createFromParcel(@NonNull Parcel in) { @@ -67,6 +69,8 @@ public CardConfiguration[] newArray(int size) { * @param holderNameRequire If the holder name of the card should be shown as a required field. * @param showStorePaymentField If the component should show the option to store the card for later use. * @param supportCardTypes The list of supported card brands to be shown to the user. + * @param hideCvc Hides the CVC field on the payment flow so that it's not required. + * @param hideCvcStoredCard Hides the CVC field on the stored payment flow so that it's not required. */ CardConfiguration( @NonNull Locale shopperLocale, @@ -76,7 +80,10 @@ public CardConfiguration[] newArray(int size) { boolean holderNameRequire, @NonNull String shopperReference, boolean showStorePaymentField, - @NonNull List supportCardTypes) { + @NonNull List supportCardTypes, + boolean hideCvc, + boolean hideCvcStoredCard + ) { super(shopperLocale, environment, clientKey); mPublicKey = publicKey; @@ -84,6 +91,8 @@ public CardConfiguration[] newArray(int size) { mSupportedCardTypes = supportCardTypes; mShopperReference = shopperReference; mShowStorePaymentField = showStorePaymentField; + mHideCvc = hideCvc; + mHideCvcStoredCard = hideCvcStoredCard; } CardConfiguration(@NonNull Parcel in) { @@ -93,6 +102,8 @@ public CardConfiguration[] newArray(int size) { mHolderNameRequire = ParcelUtils.readBoolean(in); mSupportedCardTypes = in.readArrayList(CardType.class.getClassLoader()); mShowStorePaymentField = ParcelUtils.readBoolean(in); + mHideCvc = ParcelUtils.readBoolean(in); + mHideCvcStoredCard = ParcelUtils.readBoolean(in); } @Override @@ -103,6 +114,8 @@ public void writeToParcel(@NonNull Parcel dest, int flags) { ParcelUtils.writeBoolean(dest, mHolderNameRequire); dest.writeList(mSupportedCardTypes); ParcelUtils.writeBoolean(dest, mShowStorePaymentField); + ParcelUtils.writeBoolean(dest, mHideCvc); + ParcelUtils.writeBoolean(dest, mHideCvcStoredCard); } /** @@ -146,6 +159,16 @@ public Builder newBuilder() { return new Builder(this); } + @Nullable + public boolean isHideCvc() { + return mHideCvc; + } + + @Nullable + public boolean isHideCvcStoredCard() { + return mHideCvcStoredCard; + } + /** * Builder to create a {@link CardConfiguration}. */ @@ -157,7 +180,8 @@ public static final class Builder extends BaseConfigurationBuilder