From 7c5cfb784eeb2616a8807a9a085b897b88a6a3f8 Mon Sep 17 00:00:00 2001 From: Sanskar Atrey Date: Wed, 23 Oct 2024 22:12:37 +0530 Subject: [PATCH 1/3] fix: card cvc bug fix --- .../cypress/e2e/cvc-checks-e2e-test.cy.ts | 84 +++++++++++++++++++ cypress-tests/cypress/support/utils.ts | 2 + src/CardUtils.res | 14 ++++ src/Components/SavedMethods.res | 7 ++ src/Payment.res | 8 ++ src/Payments/CardPayment.res | 5 ++ src/Types/RecoilAtomTypes.res | 2 + src/Utilities/RecoilAtoms.res | 1 + 8 files changed, 123 insertions(+) create mode 100644 cypress-tests/cypress/e2e/cvc-checks-e2e-test.cy.ts diff --git a/cypress-tests/cypress/e2e/cvc-checks-e2e-test.cy.ts b/cypress-tests/cypress/e2e/cvc-checks-e2e-test.cy.ts new file mode 100644 index 000000000..883aaa5bf --- /dev/null +++ b/cypress-tests/cypress/e2e/cvc-checks-e2e-test.cy.ts @@ -0,0 +1,84 @@ +import * as testIds from "../../../src/Utilities/TestUtils.bs"; +import { getClientURL, amexTestCard, visaTestCard, createPaymentBody } from "../support/utils"; + +describe("Card CVC Checks", () => { + let getIframeBody: () => Cypress.Chainable>; + const publishableKey = Cypress.env('HYPERSWITCH_PUBLISHABLE_KEY') + const secretKey = Cypress.env('HYPERSWITCH_SECRET_KEY') + let iframeSelector = + "#orca-payment-element-iframeRef-orca-elements-payment-element-payment-element"; + + + beforeEach(() => { + getIframeBody = () => cy.iframe(iframeSelector); + cy.createPaymentIntent(secretKey, createPaymentBody).then(() => { + cy.getGlobalState("clientSecret").then((clientSecret) => { + + cy.visit(getClientURL(clientSecret, publishableKey)); + }); + + }) + }); + + + + + it("title rendered correctly", () => { + cy.contains("Hyperswitch Unified Checkout").should("be.visible"); + }); + + it("orca-payment-element iframe loaded", () => { + cy.get( + "#orca-payment-element-iframeRef-orca-elements-payment-element-payment-element" + ) + .should("be.visible") + .its("0.contentDocument") + .its("body"); + }); + + + it('user can enter 4 digit cvc in card form', () => { + getIframeBody().find(`[data-testid=${testIds.addNewCardIcon}]`).click() + getIframeBody().find(`[data-testid=${testIds.cardNoInputTestId}]`).type(amexTestCard) + getIframeBody().find(`[data-testid=${testIds.expiryInputTestId}]`).type("0444") + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).type("1234").then(() => { + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).should('have.value', '1234'); + }) + + + }) + it('user can enter 3 digit cvc on saved payment methods screen', () => { + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).type('123').then(() => { + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).should('have.value', '123'); + }) + + }) + + it('user can enter 3 digit cvc in card form', () => { + getIframeBody().find(`[data-testid=${testIds.addNewCardIcon}]`).click() + getIframeBody().find(`[data-testid=${testIds.cardNoInputTestId}]`).type(visaTestCard) + getIframeBody().find(`[data-testid=${testIds.expiryInputTestId}]`).type("0444") + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).type("123").then(() => { + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).should('have.value', '123'); + }) + }) + + it('user can enter 4 digit cvc on saved payment methods screen', () => { + cy.wait(2000) + getIframeBody() + .contains('div', '4 digit cvc test card') + .should('exist') + .trigger('click') + cy.wait(1000) + + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).type("1234").then(() => { + getIframeBody().find(`[data-testid=${testIds.cardCVVInputTestId}]`).should('have.value', '1234'); + }) + + }) + +}) + + + + diff --git a/cypress-tests/cypress/support/utils.ts b/cypress-tests/cypress/support/utils.ts index 08a516365..4d6ca4c02 100644 --- a/cypress-tests/cypress/support/utils.ts +++ b/cypress-tests/cypress/support/utils.ts @@ -116,3 +116,5 @@ export const confirmBody = { export const stripeTestCard = "4000000000003220"; export const adyenTestCard = "4917610000000000"; export const bluesnapTestCard = "4000000000001091"; +export const amexTestCard = "378282246310005" +export const visaTestCard = "4242424242424242"; diff --git a/src/CardUtils.res b/src/CardUtils.res index 02a4bd55f..c060d5eef 100644 --- a/src/CardUtils.res +++ b/src/CardUtils.res @@ -671,3 +671,17 @@ let getEligibleCoBadgedCardSchemes = (~matchedCardSchemes, ~enabledCardSchemes) enabledCardSchemes->Array.includes(ele->String.toLowerCase) }) } + +let getCardBrandFromStates = ( + sdkScreenType, + cardBrand, + cardScheme, + showFields, + isNotBancontact, +) => { + switch (sdkScreenType, showFields, isNotBancontact) { + | (RecoilAtomTypes.SAVEDCARD, _, _) => cardScheme + | (_, false, true) => cardScheme + | _ => cardBrand + } +} diff --git a/src/Components/SavedMethods.res b/src/Components/SavedMethods.res index ae2dbdf1f..dcdf6ed77 100644 --- a/src/Components/SavedMethods.res +++ b/src/Components/SavedMethods.res @@ -46,6 +46,13 @@ let make = ( let {paymentToken: paymentTokenVal, customerId} = paymentToken + let setSdKScreenType = Recoil.useSetRecoilState(RecoilAtoms.sdkScreenType) + + React.useEffect1(() => { + setSdKScreenType(_ => RecoilAtomTypes.SAVEDCARD) + None + }, []) + let bottomElement = {
{savedMethods diff --git a/src/Payment.res b/src/Payment.res index 9aa7ba63b..b2fc48ab6 100644 --- a/src/Payment.res +++ b/src/Payment.res @@ -18,6 +18,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled) let paymentToken = Recoil.useRecoilValueFromAtom(paymentTokenAtom) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) + let sdkScreenType = Recoil.useRecoilValueFromAtom(RecoilAtoms.sdkScreenType) let {iframeId} = keys @@ -56,6 +57,13 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let (cardBrand, setCardBrand) = React.useState(_ => !showFields && isNotBancontact ? cardScheme : cardBrand ) + let cardBrand = CardUtils.getCardBrandFromStates( + sdkScreenType, + cardBrand, + cardScheme, + showFields, + isNotBancontact, + ) let supportedCardBrands = React.useMemo(() => { paymentMethodListValue->PaymentUtils.getSupportedCardBrands }, [paymentMethodListValue]) diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index 27d76278a..8c14931d4 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -22,6 +22,11 @@ let make = ( let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) let (nickname, setNickname) = React.useState(_ => "") + let setSdKScreenType = Recoil.useSetRecoilState(RecoilAtoms.sdkScreenType) + React.useEffect1(() => { + setSdKScreenType(_ => RecoilAtomTypes.CARDFORM) + None + }, []) let ( isCardValid, diff --git a/src/Types/RecoilAtomTypes.res b/src/Types/RecoilAtomTypes.res index 879de0bd1..4990b2dde 100644 --- a/src/Types/RecoilAtomTypes.res +++ b/src/Types/RecoilAtomTypes.res @@ -6,6 +6,8 @@ type field = { } type load = Loading | Loaded(JSON.t) | LoadError +type screenType = CARDFORM | SAVEDCARD | NONE +let defaultScreenValues = NONE type paymentToken = { paymentToken: string, diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res index d1207655f..536462def 100644 --- a/src/Utilities/RecoilAtoms.res +++ b/src/Utilities/RecoilAtoms.res @@ -75,6 +75,7 @@ let userPixCPF = Recoil.atom("userPixCPF", defaultFieldValues) let userPixCNPJ = Recoil.atom("userPixCNPJ", defaultFieldValues) let isCompleteCallbackUsed = Recoil.atom("isCompleteCallbackUsed", false) let isPaymentButtonHandlerProvidedAtom = Recoil.atom("isPaymentButtonHandlerProvidedAtom", false) +let sdkScreenType = Recoil.atom("sdkScreenType", defaultScreenValues) type areOneClickWalletsRendered = { isGooglePay: bool, From 3d6b4da44b263569f910e0eb4500f0c4b885c06c Mon Sep 17 00:00:00 2001 From: Sanskar Atrey Date: Wed, 23 Oct 2024 23:11:45 +0530 Subject: [PATCH 2/3] refactor: applied suggestions from code review --- src/CardUtils.res | 14 ++++---------- src/Components/SavedMethods.res | 7 ------- src/Payment.res | 4 +--- src/Payments/CardPayment.res | 5 ----- src/Types/RecoilAtomTypes.res | 2 -- src/Utilities/RecoilAtoms.res | 1 - 6 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/CardUtils.res b/src/CardUtils.res index c060d5eef..bcb9c8ead 100644 --- a/src/CardUtils.res +++ b/src/CardUtils.res @@ -672,16 +672,10 @@ let getEligibleCoBadgedCardSchemes = (~matchedCardSchemes, ~enabledCardSchemes) }) } -let getCardBrandFromStates = ( - sdkScreenType, - cardBrand, - cardScheme, - showFields, - isNotBancontact, -) => { - switch (sdkScreenType, showFields, isNotBancontact) { - | (RecoilAtomTypes.SAVEDCARD, _, _) => cardScheme - | (_, false, true) => cardScheme +let getCardBrandFromStates = (cardBrand, cardScheme, showFields, isNotBancontact) => { + switch (showFields, isNotBancontact) { + | (false, _) => cardScheme + | (_, true) => cardBrand | _ => cardBrand } } diff --git a/src/Components/SavedMethods.res b/src/Components/SavedMethods.res index dcdf6ed77..ae2dbdf1f 100644 --- a/src/Components/SavedMethods.res +++ b/src/Components/SavedMethods.res @@ -46,13 +46,6 @@ let make = ( let {paymentToken: paymentTokenVal, customerId} = paymentToken - let setSdKScreenType = Recoil.useSetRecoilState(RecoilAtoms.sdkScreenType) - - React.useEffect1(() => { - setSdKScreenType(_ => RecoilAtomTypes.SAVEDCARD) - None - }, []) - let bottomElement = {
{savedMethods diff --git a/src/Payment.res b/src/Payment.res index b2fc48ab6..6b159e8be 100644 --- a/src/Payment.res +++ b/src/Payment.res @@ -18,8 +18,6 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled) let paymentToken = Recoil.useRecoilValueFromAtom(paymentTokenAtom) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) - let sdkScreenType = Recoil.useRecoilValueFromAtom(RecoilAtoms.sdkScreenType) - let {iframeId} = keys let (cardNumber, setCardNumber) = React.useState(_ => "") @@ -57,8 +55,8 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let (cardBrand, setCardBrand) = React.useState(_ => !showFields && isNotBancontact ? cardScheme : cardBrand ) + let cardBrand = CardUtils.getCardBrandFromStates( - sdkScreenType, cardBrand, cardScheme, showFields, diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index 8c14931d4..27d76278a 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -22,11 +22,6 @@ let make = ( let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) let (nickname, setNickname) = React.useState(_ => "") - let setSdKScreenType = Recoil.useSetRecoilState(RecoilAtoms.sdkScreenType) - React.useEffect1(() => { - setSdKScreenType(_ => RecoilAtomTypes.CARDFORM) - None - }, []) let ( isCardValid, diff --git a/src/Types/RecoilAtomTypes.res b/src/Types/RecoilAtomTypes.res index 4990b2dde..879de0bd1 100644 --- a/src/Types/RecoilAtomTypes.res +++ b/src/Types/RecoilAtomTypes.res @@ -6,8 +6,6 @@ type field = { } type load = Loading | Loaded(JSON.t) | LoadError -type screenType = CARDFORM | SAVEDCARD | NONE -let defaultScreenValues = NONE type paymentToken = { paymentToken: string, diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res index 536462def..d1207655f 100644 --- a/src/Utilities/RecoilAtoms.res +++ b/src/Utilities/RecoilAtoms.res @@ -75,7 +75,6 @@ let userPixCPF = Recoil.atom("userPixCPF", defaultFieldValues) let userPixCNPJ = Recoil.atom("userPixCNPJ", defaultFieldValues) let isCompleteCallbackUsed = Recoil.atom("isCompleteCallbackUsed", false) let isPaymentButtonHandlerProvidedAtom = Recoil.atom("isPaymentButtonHandlerProvidedAtom", false) -let sdkScreenType = Recoil.atom("sdkScreenType", defaultScreenValues) type areOneClickWalletsRendered = { isGooglePay: bool, From a2622b2ce5a7e2a5148cb73f43bdc7ff845e0649 Mon Sep 17 00:00:00 2001 From: Sanskar Atrey Date: Fri, 25 Oct 2024 12:32:56 +0530 Subject: [PATCH 3/3] refactor: applied suggestions --- src/CardUtils.res | 8 ++------ src/Payment.res | 7 +------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/CardUtils.res b/src/CardUtils.res index bcb9c8ead..9341d889d 100644 --- a/src/CardUtils.res +++ b/src/CardUtils.res @@ -672,10 +672,6 @@ let getEligibleCoBadgedCardSchemes = (~matchedCardSchemes, ~enabledCardSchemes) }) } -let getCardBrandFromStates = (cardBrand, cardScheme, showFields, isNotBancontact) => { - switch (showFields, isNotBancontact) { - | (false, _) => cardScheme - | (_, true) => cardBrand - | _ => cardBrand - } +let getCardBrandFromStates = (cardBrand, cardScheme, showFields) => { + !showFields ? cardScheme : cardBrand } diff --git a/src/Payment.res b/src/Payment.res index 6b159e8be..f549921df 100644 --- a/src/Payment.res +++ b/src/Payment.res @@ -56,12 +56,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => { !showFields && isNotBancontact ? cardScheme : cardBrand ) - let cardBrand = CardUtils.getCardBrandFromStates( - cardBrand, - cardScheme, - showFields, - isNotBancontact, - ) + let cardBrand = CardUtils.getCardBrandFromStates(cardBrand, cardScheme, showFields) let supportedCardBrands = React.useMemo(() => { paymentMethodListValue->PaymentUtils.getSupportedCardBrands }, [paymentMethodListValue])