From 44ed3a83b9686b140470575f7aa15c516cac1cc8 Mon Sep 17 00:00:00 2001 From: ArushKapoorJuspay <121166031+ArushKapoorJuspay@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:59:53 +0530 Subject: [PATCH 1/4] feat: added apple pay support inside an iframe (#743) Co-authored-by: Shiva Nandan --- src/LoaderController.res | 7 +- src/Types/ApplePayTypes.res | 18 +++++ src/Utilities/ApplePayHelpers.res | 24 ++---- src/Utilities/PaymentHelpers.res | 9 ++- src/Utilities/Utils.res | 14 +++- src/Window.res | 6 ++ src/orca-loader/Elements.res | 91 ++++++++++++++++------- src/orca-loader/Hyper.res | 74 ++++++++++++++++++ src/orca-loader/PaymentSessionMethods.res | 1 + 9 files changed, 197 insertions(+), 47 deletions(-) diff --git a/src/LoaderController.res b/src/LoaderController.res index 838cedee2..9343459f3 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -18,6 +18,8 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime let (launchTime, setLaunchTime) = React.useState(_ => 0.0) let {showCardFormByDefault, paymentMethodOrder} = optionsPayment let (_, setPaymentMethodCollectOptions) = Recoil.useRecoilState(paymentMethodCollectOptionAtom) + let url = RescriptReactRouter.useUrl() + let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName") let divRef = React.useRef(Nullable.null) @@ -148,7 +150,10 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTime React.useEffect0(() => { messageParentWindow([("iframeMounted", true->JSON.Encode.bool)]) - messageParentWindow([("applePayMounted", true->JSON.Encode.bool)]) + messageParentWindow([ + ("applePayMounted", true->JSON.Encode.bool), + ("componentName", componentName->JSON.Encode.string), + ]) logger.setLogInitiated() let updatedState: PaymentType.loadType = switch paymentMethodList { | Loading => diff --git a/src/Types/ApplePayTypes.res b/src/Types/ApplePayTypes.res index f644e938f..983f4962c 100644 --- a/src/Types/ApplePayTypes.res +++ b/src/Types/ApplePayTypes.res @@ -185,3 +185,21 @@ let getPaymentRequestFromSession = (~sessionObj, ~componentName) => { paymentRequest } + +let handleApplePayIframePostMessage = (msg, componentName, mountedIframeRef) => { + let isApplePayMessageSent = ref(false) + + let iframes = Window.querySelectorAll("iframe") + + iframes->Array.forEach(iframe => { + let iframeSrc = iframe->Window.getAttribute("src")->Option.getOr("") + if iframeSrc->String.includes(`componentName=${componentName}`) { + iframe->Js.Nullable.return->Window.iframePostMessage(msg) + isApplePayMessageSent := true + } + }) + + if !isApplePayMessageSent.contents { + mountedIframeRef->Window.iframePostMessage(msg) + } +} diff --git a/src/Utilities/ApplePayHelpers.res b/src/Utilities/ApplePayHelpers.res index 688b9d647..5c33cca71 100644 --- a/src/Utilities/ApplePayHelpers.res +++ b/src/Utilities/ApplePayHelpers.res @@ -73,9 +73,8 @@ let startApplePaySession = ( ~applePaySessionRef, ~applePayPresent, ~logger: OrcaLogger.loggerMake, - ~applePayEvent: option=None, ~callBackFunc, - ~resolvePromise: option unit>=None, + ~resolvePromise, ~clientSecret, ~publishableKey, ~isTaxCalculationEnabled=false, @@ -202,18 +201,10 @@ let startApplePaySession = ( ~eventName=APPLE_PAY_FLOW, ~paymentMethod="APPLE_PAY", ) - switch (applePayEvent, resolvePromise) { - | (Some(applePayEvent), _) => { - let msg = [("showApplePayButton", true->JSON.Encode.bool)]->Dict.fromArray - applePayEvent.source->Window.sendPostMessage(msg) - } - | (_, Some(resolvePromise)) => - handleFailureResponse( - ~message="ApplePay Session Cancelled", - ~errorType="apple_pay", - )->resolvePromise - | _ => () - } + handleFailureResponse( + ~message="ApplePay Session Cancelled", + ~errorType="apple_pay", + )->resolvePromise } ssn.begin() } @@ -250,9 +241,9 @@ let useHandleApplePayResponse = ( let json = ev.data->safeParse try { let dict = json->getDictFromJson - if dict->Dict.get("applePayProcessPayment")->Option.isSome { + if dict->Dict.get("applePayPaymentToken")->Option.isSome { let token = - dict->Dict.get("applePayProcessPayment")->Option.getOr(Dict.make()->JSON.Encode.object) + dict->Dict.get("applePayPaymentToken")->Option.getOr(Dict.make()->JSON.Encode.object) let billingContactDict = dict->getDictFromDict("applePayBillingContact") let shippingContactDict = dict->getDictFromDict("applePayShippingContact") @@ -329,6 +320,7 @@ let handleApplePayButtonClicked = ( "isTaxCalculationEnabled", paymentMethodListValue.is_tax_calculation_enabled->JSON.Encode.bool, ), + ("componentName", componentName->JSON.Encode.string), ] messageParentWindow(message) } diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index 08179118e..ef3e67fbd 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -316,6 +316,7 @@ let rec intentCall = ( ~counter, ~isPaymentSession=false, ~isCallbackUsedVal=?, + ~componentName="payment", ) => { open Promise let isConfirm = uri->String.includes("/confirm") @@ -468,6 +469,7 @@ let rec intentCall = ( ~customPodUri, ~sdkHandleOneClickConfirmPayment, ~counter=counter + 1, + ~componentName, ) ->then( res => { @@ -704,6 +706,7 @@ let rec intentCall = ( | "apple_pay" => [ ("applePayButtonClicked", true->JSON.Encode.bool), ("applePayPresent", session_token->anyTypeToJson), + ("componentName", componentName->JSON.Encode.string), ] | "google_pay" => [("googlePayThirdPartyFlow", session_token->anyTypeToJson)] | "open_banking" => { @@ -893,6 +896,7 @@ let rec intentCall = ( ~sdkHandleOneClickConfirmPayment, ~counter=counter + 1, ~isPaymentSession, + ~componentName, ) ->then( res => { @@ -990,8 +994,8 @@ let usePaymentIntent = (optLogger, paymentType) => { open RecoilAtoms open Promise let url = RescriptReactRouter.useUrl() - let paymentTypeFromUrl = - CardUtils.getQueryParamsDictforKey(url.search, "componentName")->CardThemeType.getPaymentMode + let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName") + let paymentTypeFromUrl = componentName->CardThemeType.getPaymentMode let blockConfirm = Recoil.useRecoilValueFromAtom(isConfirmBlocked) let customPodUri = Recoil.useRecoilValueFromAtom(customPodUri) let paymentMethodList = Recoil.useRecoilValueFromAtom(paymentMethodList) @@ -1091,6 +1095,7 @@ let usePaymentIntent = (optLogger, paymentType) => { ~sdkHandleOneClickConfirmPayment=keys.sdkHandleOneClickConfirmPayment, ~counter=0, ~isCallbackUsedVal, + ~componentName, ) ->then(val => { intentCallback(val) diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index 5a3b518f9..07e923f70 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -1,6 +1,7 @@ @val external document: 'a = "document" @val external window: Dom.element = "window" @val @scope("window") external iframeParent: Dom.element = "parent" +@val @scope("window") external topParent: Dom.element = "top" type event = {data: string} external dictToObj: Dict.t<'a> => {..} = "%identity" @@ -16,12 +17,21 @@ type dateTimeFormat = {resolvedOptions: unit => options} @send external postMessage: (Dom.element, JSON.t, string) => unit = "postMessage" open ErrorUtils + +let messageWindow = (window, ~targetOrigin="*", messageArr) => { + window->postMessage(messageArr->Dict.fromArray->JSON.Encode.object, targetOrigin) +} + +let messageTopWindow = (~targetOrigin="*", messageArr) => { + topParent->messageWindow(~targetOrigin, messageArr) +} + let messageParentWindow = (~targetOrigin="*", messageArr) => { - iframeParent->postMessage(messageArr->Dict.fromArray->JSON.Encode.object, targetOrigin) + iframeParent->messageWindow(~targetOrigin, messageArr) } let messageCurrentWindow = (~targetOrigin="*", messageArr) => { - window->postMessage(messageArr->Dict.fromArray->JSON.Encode.object, targetOrigin) + window->messageWindow(~targetOrigin, messageArr) } let handleOnFocusPostMessage = (~targetOrigin="*") => { diff --git a/src/Window.res b/src/Window.res index 231606624..ebb0570d6 100644 --- a/src/Window.res +++ b/src/Window.res @@ -26,6 +26,7 @@ type style external querySelector: string => Nullable.t = "querySelector" @val @scope("document") external querySelectorAll: string => array = "querySelectorAll" +@send external getAttribute: (Dom.element, string) => option = "getAttribute" type eventData = { elementType: string, @@ -44,6 +45,7 @@ external addEventListener: (string, _ => unit) => unit = "addEventListener" @val @scope("window") external removeEventListener: (string, 'ev => unit) => unit = "removeEventListener" @send external postMessage: (Dom.element, string, string) => unit = "postMessage" +@send external postMessageJSON: (Dom.element, JSON.t, string) => unit = "postMessage" @send external getElementById: (document, string) => Nullable.t = "getElementById" @get @@ -62,6 +64,10 @@ let sendPostMessage = (element, message) => { element->postMessage(message->JSON.Encode.object->JSON.stringify, GlobalVars.targetOrigin) } +let sendPostMessageJSON = (element, message) => { + element->postMessageJSON(message, GlobalVars.targetOrigin) +} + let iframePostMessage = (iframeRef: nullable, message) => { switch iframeRef->Nullable.toOption { | Some(ref) => diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index 110353b7c..a664a8750 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -21,7 +21,6 @@ let make = ( ~analyticsMetadata, ~customBackendUrl, ) => { - let applePaySessionRef = ref(Nullable.null) try { let iframeRef = [] let logger = logger->Option.getOr(OrcaLogger.defaultLoggerConfig) @@ -334,6 +333,7 @@ let make = ( let handleApplePayMounted = (event: Types.event) => { let json = event.data->anyTypeToJson let dict = json->getDictFromJson + let componentName = getString(dict, "componentName", "payment") if dict->Dict.get("applePayMounted")->Option.isSome { if wallets.applePay === Auto { @@ -341,8 +341,11 @@ let make = ( | Some(session) => try { if session.canMakePayments() { - let msg = [("applePayCanMakePayments", true->JSON.Encode.bool)]->Dict.fromArray - event.source->Window.sendPostMessage(msg) + let msg = [ + ("hyperApplePayCanMakePayments", true->JSON.Encode.bool), + ("componentName", componentName->JSON.Encode.string), + ] + messageTopWindow(msg) } else { Console.log("CANNOT MAKE PAYMENT USING APPLE PAY") logger.setLogInfo( @@ -374,6 +377,28 @@ let make = ( ~logType=INFO, ) } + } else if dict->Dict.get("applePayCanMakePayments")->Option.isSome { + let applePayCanMakePayments = getBool(dict, "applePayCanMakePayments", false) + + if applePayCanMakePayments { + try { + let msg = [("applePayCanMakePayments", true->JSON.Encode.bool)]->Dict.fromArray + + handleApplePayIframePostMessage(msg, componentName, mountedIframeRef) + } catch { + | exn => { + let exnString = exn->anyTypeToJson->JSON.stringify + + Console.log("CANNOT MAKE PAYMENT USING APPLE PAY: " ++ exnString) + logger.setLogInfo( + ~value=exnString, + ~eventName=APPLE_PAY_FLOW, + ~paymentMethod="APPLE_PAY", + ~logType=ERROR, + ) + } + } + } } } @@ -792,6 +817,8 @@ let make = ( let handleApplePayMessages = (applePayEvent: Types.event) => { let json = applePayEvent.data->Identity.anyTypeToJson let dict = json->getDictFromJson + let componentName = dict->getString("componentName", "payment") + switch ( dict->Dict.get("applePayButtonClicked"), dict->Dict.get("applePayPaymentRequest"), @@ -817,33 +844,45 @@ let make = ( ~paymentMethod="APPLE_PAY", ) - let callBackFunc = payment => { - let msg = - [ - ("applePayProcessPayment", payment.token), - ("applePayBillingContact", payment.billingContact), - ("applePayShippingContact", payment.shippingContact), - ]->Dict.fromArray - applePayEvent.source->Window.sendPostMessage(msg) - } - - ApplePayHelpers.startApplePaySession( - ~paymentRequest, - ~applePaySessionRef, - ~applePayPresent, - ~logger, - ~applePayEvent=Some(applePayEvent), - ~callBackFunc, - ~clientSecret, - ~publishableKey, - ~isTaxCalculationEnabled, - ) + let msg = [ + ("hyperApplePayButtonClicked", true->JSON.Encode.bool), + ("paymentRequest", paymentRequest), + ("applePayPresent", applePayPresent->Option.getOr(JSON.Encode.null)), + ("clientSecret", clientSecret->JSON.Encode.string), + ("publishableKey", publishableKey->JSON.Encode.string), + ("isTaxCalculationEnabled", isTaxCalculationEnabled->JSON.Encode.bool), + ("sdkSessionId", sdkSessionId->JSON.Encode.string), + ("analyticsMetadata", analyticsMetadata), + ("componentName", componentName->JSON.Encode.string), + ] + messageTopWindow(msg) } - } else { - () } | _ => () } + + if dict->Dict.get("applePayPaymentToken")->Option.isSome { + let token = dict->getJsonFromDict("applePayPaymentToken", JSON.Encode.null) + let billingContact = + dict->getJsonFromDict("applePayBillingContact", JSON.Encode.null) + let shippingContact = + dict->getJsonFromDict("applePayShippingContact", JSON.Encode.null) + + let msg = + [ + ("applePayPaymentToken", token), + ("applePayBillingContact", billingContact), + ("applePayShippingContact", shippingContact), + ]->Dict.fromArray + + handleApplePayIframePostMessage(msg, componentName, mountedIframeRef) + } + + if dict->Dict.get("showApplePayButton")->Option.isSome { + let msg = [("showApplePayButton", true->JSON.Encode.bool)]->Dict.fromArray + + handleApplePayIframePostMessage(msg, componentName, mountedIframeRef) + } } addSmartEventListener("message", handleApplePayMessages, "onApplePayMessages") diff --git a/src/orca-loader/Hyper.res b/src/orca-loader/Hyper.res index 6941d62a7..eb3d6e29e 100644 --- a/src/orca-loader/Hyper.res +++ b/src/orca-loader/Hyper.res @@ -60,6 +60,80 @@ let preloader = () => { preloadFile(~type_="script", ~href="https://js.braintreegateway.com/web/3.88.4/js/client.min.js") } +let handleHyperApplePayMounted = (event: Types.event) => { + open ApplePayTypes + let json = event.data->anyTypeToJson + let dict = json->getDictFromJson + let applePaySessionRef = ref(Nullable.null) + + let componentName = dict->getString("componentName", "payment") + + if dict->Dict.get("hyperApplePayCanMakePayments")->Option.isSome { + let msg = + [ + ("applePayCanMakePayments", true->JSON.Encode.bool), + ("componentName", componentName->JSON.Encode.string), + ] + ->Dict.fromArray + ->JSON.Encode.object + event.source->Window.sendPostMessageJSON(msg) + } else if dict->Dict.get("hyperApplePayButtonClicked")->Option.isSome { + let paymentRequest = dict->Dict.get("paymentRequest")->Option.getOr(JSON.Encode.null) + let applePayPresent = dict->Dict.get("applePayPresent") + let clientSecret = dict->getString("clientSecret", "") + let publishableKey = dict->getString("publishableKey", "") + let isTaxCalculationEnabled = dict->getBool("isTaxCalculationEnabled", false) + let sdkSessionId = dict->getString("sdkSessionId", "") + let analyticsMetadata = dict->getJsonFromDict("analyticsMetadata", JSON.Encode.null) + + let logger = OrcaLogger.make( + ~sessionId=sdkSessionId, + ~source=Loader, + ~merchantId=publishableKey, + ~metadata=analyticsMetadata, + ~clientSecret, + ) + + let callBackFunc = payment => { + let msg = + [ + ("applePayPaymentToken", payment.token), + ("applePayBillingContact", payment.billingContact), + ("applePayShippingContact", payment.shippingContact), + ("componentName", componentName->JSON.Encode.string), + ] + ->Dict.fromArray + ->JSON.Encode.object + event.source->Window.sendPostMessageJSON(msg) + } + + let resolvePromise = _ => { + let msg = + [ + ("showApplePayButton", true->JSON.Encode.bool), + ("componentName", componentName->JSON.Encode.string), + ] + ->Dict.fromArray + ->JSON.Encode.object + event.source->Window.sendPostMessageJSON(msg) + } + + ApplePayHelpers.startApplePaySession( + ~paymentRequest, + ~applePaySessionRef, + ~applePayPresent, + ~logger, + ~callBackFunc, + ~clientSecret, + ~publishableKey, + ~isTaxCalculationEnabled, + ~resolvePromise, + ) + } +} + +addSmartEventListener("message", handleHyperApplePayMounted, "onHyperApplePayMount") + let make = (publishableKey, options: option, analyticsInfo: option) => { try { let isPreloadEnabled = diff --git a/src/orca-loader/PaymentSessionMethods.res b/src/orca-loader/PaymentSessionMethods.res index 60e54b844..fb80a5614 100644 --- a/src/orca-loader/PaymentSessionMethods.res +++ b/src/orca-loader/PaymentSessionMethods.res @@ -194,6 +194,7 @@ let getCustomerSavedPaymentMethods = ( ~callBackFunc=processPayment, ~clientSecret, ~publishableKey, + ~resolvePromise, ) } From f53537abbe79d28e43805224e88dbef81a2ee2fe Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Oct 2024 10:31:51 +0000 Subject: [PATCH 2/4] chore(release): 0.96.0 [skip ci] # [0.96.0](https://github.com/juspay/hyperswitch-web/compare/v0.95.3...v0.96.0) (2024-10-23) ### Features * added apple pay support inside an iframe ([#743](https://github.com/juspay/hyperswitch-web/issues/743)) ([44ed3a8](https://github.com/juspay/hyperswitch-web/commit/44ed3a83b9686b140470575f7aa15c516cac1cc8)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba0fefc95..4e7bf7ee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [0.96.0](https://github.com/juspay/hyperswitch-web/compare/v0.95.3...v0.96.0) (2024-10-23) + + +### Features + +* added apple pay support inside an iframe ([#743](https://github.com/juspay/hyperswitch-web/issues/743)) ([44ed3a8](https://github.com/juspay/hyperswitch-web/commit/44ed3a83b9686b140470575f7aa15c516cac1cc8)) + ## [0.95.3](https://github.com/juspay/hyperswitch-web/compare/v0.95.2...v0.95.3) (2024-10-21) diff --git a/package-lock.json b/package-lock.json index ac674f2f0..7b8ec0ad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orca-payment-page", - "version": "0.95.3", + "version": "0.96.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orca-payment-page", - "version": "0.95.3", + "version": "0.96.0", "hasInstallScript": true, "dependencies": { "@glennsl/rescript-fetch": "^0.2.0", diff --git a/package.json b/package.json index d72e1f99a..cd23cd0c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.95.3", + "version": "0.96.0", "main": "index.js", "private": true, "dependencies": { From 6122d9d200b934969ee8643e598754d724a786a4 Mon Sep 17 00:00:00 2001 From: Sanskar Atrey Date: Fri, 25 Oct 2024 12:56:20 +0530 Subject: [PATCH 3/4] fix: card cvc bug fix (#748) --- .../cypress/e2e/cvc-checks-e2e-test.cy.ts | 84 +++++++++++++++++++ cypress-tests/cypress/support/utils.ts | 2 + src/CardUtils.res | 4 + src/Payment.res | 3 +- 4 files changed, 92 insertions(+), 1 deletion(-) 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..9341d889d 100644 --- a/src/CardUtils.res +++ b/src/CardUtils.res @@ -671,3 +671,7 @@ let getEligibleCoBadgedCardSchemes = (~matchedCardSchemes, ~enabledCardSchemes) enabledCardSchemes->Array.includes(ele->String.toLowerCase) }) } + +let getCardBrandFromStates = (cardBrand, cardScheme, showFields) => { + !showFields ? cardScheme : cardBrand +} diff --git a/src/Payment.res b/src/Payment.res index 9aa7ba63b..f549921df 100644 --- a/src/Payment.res +++ b/src/Payment.res @@ -18,7 +18,6 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled) let paymentToken = Recoil.useRecoilValueFromAtom(paymentTokenAtom) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) - let {iframeId} = keys let (cardNumber, setCardNumber) = React.useState(_ => "") @@ -56,6 +55,8 @@ let make = (~paymentMode, ~integrateError, ~logger) => { let (cardBrand, setCardBrand) = React.useState(_ => !showFields && isNotBancontact ? cardScheme : cardBrand ) + + let cardBrand = CardUtils.getCardBrandFromStates(cardBrand, cardScheme, showFields) let supportedCardBrands = React.useMemo(() => { paymentMethodListValue->PaymentUtils.getSupportedCardBrands }, [paymentMethodListValue]) From 627980d55a18a8ebe15ba65cc7cb2a1ccde068d0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 25 Oct 2024 07:28:14 +0000 Subject: [PATCH 4/4] chore(release): 0.96.1 [skip ci] ## [0.96.1](https://github.com/juspay/hyperswitch-web/compare/v0.96.0...v0.96.1) (2024-10-25) ### Bug Fixes * card cvc bug fix ([#748](https://github.com/juspay/hyperswitch-web/issues/748)) ([6122d9d](https://github.com/juspay/hyperswitch-web/commit/6122d9d200b934969ee8643e598754d724a786a4)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7bf7ee4..1519bc3d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.96.1](https://github.com/juspay/hyperswitch-web/compare/v0.96.0...v0.96.1) (2024-10-25) + + +### Bug Fixes + +* card cvc bug fix ([#748](https://github.com/juspay/hyperswitch-web/issues/748)) ([6122d9d](https://github.com/juspay/hyperswitch-web/commit/6122d9d200b934969ee8643e598754d724a786a4)) + # [0.96.0](https://github.com/juspay/hyperswitch-web/compare/v0.95.3...v0.96.0) (2024-10-23) diff --git a/package-lock.json b/package-lock.json index 7b8ec0ad0..b2ec9bb59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orca-payment-page", - "version": "0.96.0", + "version": "0.96.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orca-payment-page", - "version": "0.96.0", + "version": "0.96.1", "hasInstallScript": true, "dependencies": { "@glennsl/rescript-fetch": "^0.2.0", diff --git a/package.json b/package.json index cd23cd0c7..4ceafaf2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.96.0", + "version": "0.96.1", "main": "index.js", "private": true, "dependencies": {