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

feat: Support to handle confirm button (E2E) #198

Merged
merged 14 commits into from
Mar 11, 2024
1 change: 0 additions & 1 deletion Hyperswitch-React-Demo-App/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ app.get("/create-payment-intent", async (req, res) => {
amount: 2999,
},
],
currency: "USD",
confirm: false,
capture_method: "automatic",
authentication_type: "three_ds",
Expand Down
8 changes: 4 additions & 4 deletions Hyperswitch-React-Demo-App/src/Cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ function Cart() {
<div className="titleContainer">
<div class="title">
{" "}
<img className="logoImg" width="28px" src={logo} /> Hyperswicth
Playground App
<img className="logoImg" width="28px" src={logo} alt="" />{" "}
Hyperswitch Playground App
</div>
<div class="testMode">Test Mode</div>
</div>
Expand All @@ -19,7 +19,7 @@ function Cart() {
<div className="Item">
<div className="ItemContainer">
<div className="itemImg">
<img src={shirt} />
<img src={shirt} alt="" />
</div>
<div className="itemDetails">
<div className="name">HS Tshirt</div>
Expand All @@ -37,7 +37,7 @@ function Cart() {
<div className="Item">
<div className="ItemContainer">
<div className="itemImg">
<img src={cap} />
<img src={cap} alt="" />
</div>
<div className="itemDetails">
<div className="name">HS Cap</div>
Expand Down
8 changes: 8 additions & 0 deletions src/BrutalTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ let brutal = {
colorIconCardError: "#ff1a1a",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "#f5fb1f",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "#566186",
buttonTextColor: "#000000",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}
let brutalRules = (theme: CardThemeType.themeClass) =>
{
Expand Down
41 changes: 41 additions & 0 deletions src/CardTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ let getVariables = (str, dict, default, logger) => {
"spacingGridColumn",
"spacingGridRow",
"spacingAccordionItem",
"buttonBackgroundColor",
"buttonHeight",
"buttonWidth",
"buttonBorderRadius",
"buttonBorderColor",
"buttonTextColor",
"buttonTextFontSize",
"buttonTextFontWeight",
]
unknownKeysWarning(validKeys, json, "appearance.variables", ~logger)
{
Expand Down Expand Up @@ -244,6 +252,39 @@ let getVariables = (str, dict, default, logger) => {
~logger,
),
spacingGridRow: getWarningString(json, "spacingGridRow", default.spacingGridRow, ~logger),
buttonBackgroundColor: getWarningString(
json,
"buttonBackgroundColor",
default.spacingGridRow,
~logger,
),
buttonHeight: getWarningString(json, "buttonHeight", default.spacingGridRow, ~logger),
buttonWidth: getWarningString(json, "buttonWidth", default.spacingGridRow, ~logger),
buttonBorderRadius: getWarningString(
json,
"buttonBorderRadius",
default.spacingGridRow,
~logger,
),
buttonBorderColor: getWarningString(
json,
"buttonBorderColor",
default.spacingGridRow,
~logger,
),
buttonTextColor: getWarningString(json, "buttonTextColor", default.spacingGridRow, ~logger),
buttonTextFontSize: getWarningString(
json,
"buttonTextFontSize",
default.spacingGridRow,
~logger,
),
buttonTextFontWeight: getWarningString(
json,
"buttonTextFontWeight",
default.spacingGridRow,
~logger,
),
}
})
->Belt.Option.getWithDefault(default)
Expand Down
8 changes: 8 additions & 0 deletions src/CharcoalTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ let charcoal = {
colorIconCardError: "#fd1717",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "#000000",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "#000000",
buttonTextColor: "#ffffff",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}

let charcoalRules = theme =>
Expand Down
67 changes: 49 additions & 18 deletions src/Components/PayNowButton.res
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@send external postMessage: (Window.window, Js.Json.t, string) => unit = "postMessage"
@send external postMessage: (Window.window, JSON.t, string) => unit = "postMessage"

external eventToJson: ReactEvent.Mouse.t => Js.Json.t = "%identity"
external eventToJson: ReactEvent.Mouse.t => JSON.t = "%identity"

module Loader = {
@react.component
Expand All @@ -14,41 +14,72 @@ module Loader = {
}
}
@react.component
let make = () => {
let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let make = (
~cvcProps: CardUtils.cvcProps,
~cardProps: CardUtils.cardProps,
~expiryProps: CardUtils.expiryProps,
~selectedOption: PaymentModeType.payment,
) => {
open RecoilAtoms
let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom)
let (isDisabled, setIsDisabled) = React.useState(() => false)
let (showLoader, setShowLoader) = React.useState(() => false)
let complete = Recoil.useRecoilValueFromAtom(RecoilAtoms.fieldsComplete)
let areRequiredFieldsValidValue = Recoil.useRecoilValueFromAtom(areRequiredFieldsValid)
let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(optionAtom)

let handleOnClick = _ev => {
let (isCVCValid, _, _, _, _, _, _, _, _, _) = cvcProps
let (isCardValid, _, _, _, _, _, _, _, _, _) = cardProps
let (isExpiryValid, _, _, _, _, _, _, _, _) = expiryProps

let validFormat =
isCVCValid->Option.getOr(false) &&
isCardValid->Option.getOr(false) &&
isExpiryValid->Option.getOr(false) &&
areRequiredFieldsValidValue

let confirmPayload = sdkHandleConfirmPayment->PaymentBody.confirmPayloadForSDKButton

let handleOnClick = _ => {
setIsDisabled(_ => true)
setShowLoader(_ => true)
Utils.handleOnConfirmPostMessage(~targetOrigin="*", ())
Utils.handlePostMessage([("handleSdkConfirm", confirmPayload)])
}
React.useEffect1(() => {
setIsDisabled(_ => !complete)
React.useEffect3(() => {
if selectedOption === Card {
setIsDisabled(_ => !validFormat)
} else {
setIsDisabled(_ => !areRequiredFieldsValidValue)
}
None
}, [complete])
}, (validFormat, areRequiredFieldsValidValue, selectedOption))

<div className="flex flex-col gap-1 h-auto w-full">
<div className="flex flex-col gap-1 h-auto w-full items-center">
<button
disabled=isDisabled
onClick=handleOnClick
className={`w-full flex flex-row justify-center items-center rounded-md`}
className={`w-full flex flex-row justify-center items-center`}
style={ReactDOMStyle.make(
~backgroundColor=themeObj.colorBackground,
~height="48px",
~borderRadius=themeObj.buttonBorderRadius,
~backgroundColor=themeObj.buttonBackgroundColor,
~height=themeObj.buttonHeight,
~cursor={isDisabled ? "not-allowed" : "pointer"},
~opacity={isDisabled ? "0.6" : "1"},
~borderWidth="thin",
~borderColor=themeObj.colorPrimary,
~width=themeObj.buttonWidth,
~borderColor=themeObj.buttonBorderColor,
(),
)}>
<span id="button-text" style={ReactDOMStyle.make(~color=themeObj.colorPrimary, ())}>
<span
id="button-text"
style={ReactDOMStyle.make(
~color=themeObj.buttonTextColor,
~fontSize=themeObj.buttonTextFontSize,
~fontWeight=themeObj.buttonTextFontWeight,
(),
)}>
{if showLoader {
<Loader />
} else {
{React.string(localeString.payNowButton)}
localeString.payNowButton->React.string
}}
</span>
</button>
Expand Down
8 changes: 8 additions & 0 deletions src/DefaultTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ let default = {
colorIconCardError: "#fd1717",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "#006df9",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "#ffffff",
buttonTextColor: "#ffffff",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}
let defaultRules = theme =>
{
Expand Down
7 changes: 0 additions & 7 deletions src/Hooks/CommonHooks.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type keys = {
publishableKey: string,
iframeId: string,
parentURL: string,
sdkHandleConfirmPayment: bool,
sdkHandleOneClickConfirmPayment: bool,
}
@val @scope("document") external querySelector: string => Js.Nullable.t<element> = "querySelector"
Expand Down Expand Up @@ -86,11 +85,6 @@ let updateKeys = (dict, keyPair, setKeys) => {
...prev,
parentURL: dict->Utils.getString(key, valueStr),
})
| "sdkHandleConfirmPayment" =>
setKeys(.prev => {
...prev,
sdkHandleConfirmPayment: dict->Utils.getBool(key, valueBool(false)),
})
| "sdkHandleOneClickConfirmPayment" =>
setKeys(.prev => {
...prev,
Expand All @@ -105,6 +99,5 @@ let defaultkeys = {
publishableKey: "",
iframeId: "",
parentURL: "*",
sdkHandleConfirmPayment: false,
sdkHandleOneClickConfirmPayment: true,
}
4 changes: 2 additions & 2 deletions src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {
setList(._ => updatedState)
logger.setLogInfo(~value="SemiLoaded", ~eventName=LOADER_CHANGED, ())
}
| LoadError(x) => logger.setLogError(
| LoadError(x) =>
logger.setLogError(
~value="LoadError: " ++ x->Js.Json.stringify,
~eventName=LOADER_CHANGED,
(),
Expand Down Expand Up @@ -269,7 +270,6 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => {
("iframeId", "no-element"->Js.Json.string),
("publishableKey", ""->Js.Json.string),
("parentURL", "*"->Js.Json.string),
("sdkHandleConfirmPayment", false->Js.Json.boolean),
("sdkHandleOneClickConfirmPayment", true->Js.Json.boolean),
]->Js.Array2.forEach(keyPair => {
dict->CommonHooks.updateKeys(keyPair, setKeys)
Expand Down
8 changes: 8 additions & 0 deletions src/MidnightTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ let midnight = {
colorIconCardError: "#fd1717",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "#85d996",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "#85d996",
buttonTextColor: "#000000",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}

let midnightRules = theme =>
Expand Down
8 changes: 8 additions & 0 deletions src/NoTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ let nakedValues = {
colorIconCardError: "#fd1717",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "",
buttonTextColor: "",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}

let nakedValuesRules = _ => Js.Dict.empty()->Js.Json.object_
Expand Down
17 changes: 11 additions & 6 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let make = (
let (sessions, setSessions) = React.useState(_ => Js.Dict.empty()->Js.Json.object_)
let (paymentOptions, setPaymentOptions) = React.useState(_ => [])
let (walletOptions, setWalletOptions) = React.useState(_ => [])
let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(keys)
let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(optionAtom)

let (list, setList) = React.useState(_ => PaymentMethodsRecord.defaultList)
let (cardsContainerWidth, setCardsContainerWidth) = React.useState(_ => 0)
Expand Down Expand Up @@ -392,11 +392,16 @@ let make = (
| Accordion => <AccordionContainer paymentOptions checkoutEle />
}}
</div>
<RenderIf condition={sdkHandleConfirmPayment}>
<div className="mt-4">
<PayNowButton />
</div>
</RenderIf>
</RenderIf>
<RenderIf condition={sdkHandleConfirmPayment.handleConfirm}>
<div className="mt-4">
<PayNowButton
cvcProps
cardProps
expiryProps
selectedOption={selectedOption->PaymentModeType.paymentMode}
/>
</div>
</RenderIf>
<PoweredBy />
{switch methodslist {
Expand Down
8 changes: 8 additions & 0 deletions src/SoftTheme.res
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ let soft = {
colorIconCardError: "#fe87a1",
spacingGridColumn: "20px",
spacingGridRow: "20px",
buttonBackgroundColor: "#3c3d3e",
buttonHeight: "48px",
buttonWidth: "thin",
buttonBorderRadius: "6px",
buttonBorderColor: "#7d8fff",
buttonTextColor: "#7d8fff",
buttonTextFontSize: "16px",
buttonTextFontWeight: "500",
}

let softRules = theme =>
Expand Down
8 changes: 8 additions & 0 deletions src/Types/CardThemeType.res
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ type themeClass = {
colorIconCardError: string,
spacingGridColumn: string,
spacingGridRow: string,
buttonBackgroundColor: string,
buttonHeight: string,
buttonWidth: string,
buttonBorderRadius: string,
buttonBorderColor: string,
buttonTextColor: string,
buttonTextFontSize: string,
buttonTextFontWeight: string,
}
type appearance = {
theme: theme,
Expand Down
Loading
Loading