Skip to content

Commit

Permalink
Merge branch 'main' of github.com:juspay/hyperswitch-web into paze-in…
Browse files Browse the repository at this point in the history
…tegration
  • Loading branch information
PritishBudhiraja committed Oct 28, 2024
2 parents ceaba14 + bc4e296 commit 63a078f
Show file tree
Hide file tree
Showing 17 changed files with 322 additions and 66 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## [0.96.2](https://github.com/juspay/hyperswitch-web/compare/v0.96.1...v0.96.2) (2024-10-28)

## [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)


### 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)


Expand Down
84 changes: 84 additions & 0 deletions cypress-tests/cypress/e2e/cvc-checks-e2e-test.cy.ts
Original file line number Diff line number Diff line change
@@ -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<JQuery<HTMLBodyElement>>;
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');
})

})

})




2 changes: 2 additions & 0 deletions cypress-tests/cypress/support/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 13 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orca-payment-page",
"version": "0.95.3",
"version": "0.96.2",
"main": "index.js",
"private": true,
"dependencies": {
Expand All @@ -16,23 +16,22 @@
"webpack-merge": "^5.9.0"
},
"scripts": {
"build:dev": "cross-env sdkEnv=sandbox webpack --config webpack.dev.js",
"build:dev-integ": "cross-env sdkEnv=integ webpack --config webpack.dev.js",
"start": "cross-env sdkEnv=local webpack serve --config webpack.dev.js",
"build:prod": "cross-env sdkEnv=prod webpack --config webpack.common.js",
"build": "webpack --config webpack.common.js",
"build:sandbox": "cross-env sdkEnv=sandbox webpack --config webpack.common.js",
"build:integ": "cross-env sdkEnv=integ webpack --config webpack.common.js",
"test": "cd cypress-tests && npm run cypress:run",
"build:integ": "cross-env sdkEnv=integ enableLogging=false webpack --config webpack.common.js",
"build:playground": "npm run setup:playground && npm run build",
"build:prod": "cross-env sdkEnv=prod enableLogging=true webpack --config webpack.common.js",
"build:sandbox": "cross-env sdkEnv=sandbox enableLogging=true webpack --config webpack.common.js",
"deploy-to-s3": "node ./scripts/pushToS3.js",
"postinstall": "cd Hyperswitch-React-Demo-App && npm i",
"prepare": "husky install",
"re:build": "rescript",
"re:clean": "rescript clean",
"re:start": "rescript -w",
"re:format": "rescript format -all",
"start:playground": "npm run postinstall && cd Hyperswitch-React-Demo-App && node promptScript.js && npm run start",
"build:playground": "npm run postinstall && cd Hyperswitch-React-Demo-App && node promptScript.js && npm run build",
"prepare": "husky install",
"deploy-to-s3": "node ./scripts/pushToS3.js",
"postinstall": "cd Hyperswitch-React-Demo-App && npm i",
"re:start": "rescript -w",
"setup:playground": "npm run postinstall && cd Hyperswitch-React-Demo-App && node promptScript.js",
"start": "cross-env sdkEnv=local enableLogging=false webpack serve --config webpack.dev.js",
"start:playground": "npm run setup:playground && npm run start",
"test": "cd cypress-tests && npm run cypress:run",
"test:hooks": "npx eslint src/"
},
"eslintConfig": {
Expand Down
4 changes: 4 additions & 0 deletions src/CardUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,7 @@ let getEligibleCoBadgedCardSchemes = (~matchedCardSchemes, ~enabledCardSchemes)
enabledCardSchemes->Array.includes(ele->String.toLowerCase)
})
}

let getCardBrandFromStates = (cardBrand, cardScheme, showFields) => {
!showFields ? cardScheme : cardBrand
}
7 changes: 6 additions & 1 deletion src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 =>
Expand Down
3 changes: 2 additions & 1 deletion src/Payment.res
Original file line number Diff line number Diff line change
Expand Up @@ -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(_ => "")
Expand Down Expand Up @@ -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])
Expand Down
18 changes: 18 additions & 0 deletions src/Types/ApplePayTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
24 changes: 8 additions & 16 deletions src/Utilities/ApplePayHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ let startApplePaySession = (
~applePaySessionRef,
~applePayPresent,
~logger: OrcaLogger.loggerMake,
~applePayEvent: option<Types.event>=None,
~callBackFunc,
~resolvePromise: option<Core__JSON.t => unit>=None,
~resolvePromise,
~clientSecret,
~publishableKey,
~isTaxCalculationEnabled=false,
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -329,6 +320,7 @@ let handleApplePayButtonClicked = (
"isTaxCalculationEnabled",
paymentMethodListValue.is_tax_calculation_enabled->JSON.Encode.bool,
),
("componentName", componentName->JSON.Encode.string),
]
messageParentWindow(message)
}
Expand Down
9 changes: 7 additions & 2 deletions src/Utilities/PaymentHelpers.res
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ let rec intentCall = (
~counter,
~isPaymentSession=false,
~isCallbackUsedVal=?,
~componentName="payment",
) => {
open Promise
let isConfirm = uri->String.includes("/confirm")
Expand Down Expand Up @@ -470,6 +471,7 @@ let rec intentCall = (
~customPodUri,
~sdkHandleOneClickConfirmPayment,
~counter=counter + 1,
~componentName,
)
->then(
res => {
Expand Down Expand Up @@ -706,6 +708,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" => {
Expand Down Expand Up @@ -895,6 +898,7 @@ let rec intentCall = (
~sdkHandleOneClickConfirmPayment,
~counter=counter + 1,
~isPaymentSession,
~componentName,
)
->then(
res => {
Expand Down Expand Up @@ -992,8 +996,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)
Expand Down Expand Up @@ -1093,6 +1097,7 @@ let usePaymentIntent = (optLogger, paymentType) => {
~sdkHandleOneClickConfirmPayment=keys.sdkHandleOneClickConfirmPayment,
~counter=0,
~isCallbackUsedVal,
~componentName,
)
->then(val => {
intentCallback(val)
Expand Down
14 changes: 12 additions & 2 deletions src/Utilities/Utils.res
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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="*") => {
Expand Down
Loading

0 comments on commit 63a078f

Please sign in to comment.