diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fcd00ed..7480bae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## [0.103.2](https://github.com/juspay/hyperswitch-web/compare/v0.103.1...v0.103.2) (2024-11-26) + +## [0.103.1](https://github.com/juspay/hyperswitch-web/compare/v0.103.0...v0.103.1) (2024-11-25) + + +### Bug Fixes + +* crypto currency payment method ([#810](https://github.com/juspay/hyperswitch-web/issues/810)) ([c3aed42](https://github.com/juspay/hyperswitch-web/commit/c3aed42b9f218b8c44e2574f3032081cd7a45999)) + +# [0.103.0](https://github.com/juspay/hyperswitch-web/compare/v0.102.9...v0.103.0) (2024-11-21) + + +### Features + +* added paypal tabs flow support for billing details ([#792](https://github.com/juspay/hyperswitch-web/issues/792)) ([a7c7d19](https://github.com/juspay/hyperswitch-web/commit/a7c7d191aa3df3de3293f8dade14c0448415a913)) + +## [0.102.9](https://github.com/juspay/hyperswitch-web/compare/v0.102.8...v0.102.9) (2024-11-20) + +## [0.102.8](https://github.com/juspay/hyperswitch-web/compare/v0.102.7...v0.102.8) (2024-11-19) + +## [0.102.7](https://github.com/juspay/hyperswitch-web/compare/v0.102.6...v0.102.7) (2024-11-19) + +## [0.102.6](https://github.com/juspay/hyperswitch-web/compare/v0.102.5...v0.102.6) (2024-11-19) + ## [0.102.5](https://github.com/juspay/hyperswitch-web/compare/v0.102.4...v0.102.5) (2024-11-19) ## [0.102.4](https://github.com/juspay/hyperswitch-web/compare/v0.102.3...v0.102.4) (2024-11-14) diff --git a/package-lock.json b/package-lock.json index 0e27eb88..35703023 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orca-payment-page", - "version": "0.102.5", + "version": "0.103.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orca-payment-page", - "version": "0.102.5", + "version": "0.103.2", "hasInstallScript": true, "dependencies": { "@glennsl/rescript-fetch": "^0.2.0", @@ -3597,40 +3597,40 @@ } }, "node_modules/@sentry-internal/feedback": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.119.0.tgz", - "integrity": "sha512-om8TkAU5CQGO8nkmr7qsSBVkP+/vfeS4JgtW3sjoTK0fhj26+DljR6RlfCGWtYQdPSP6XV7atcPTjbSnsmG9FQ==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.120.0.tgz", + "integrity": "sha512-+nU2PXMAyrYyK64PlfxXyRZ+LIl6IWAcdnBeX916WqOJy2WWmtdOrAX8muVwLVIXHzp1EMG1nEZgtpL/Vr2XKQ==", "dependencies": { - "@sentry/core": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry/core": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.119.0.tgz", - "integrity": "sha512-NL02VQx6ekPxtVRcsdp1bp5Tb5w6vnfBKSIfMKuDRBy5A10Uc3GSoy/c3mPyHjOxB84452A+xZSx6bliEzAnuA==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.120.0.tgz", + "integrity": "sha512-ZEFZBP+Jxmy/8IY7IZDZVPqAJ6pPxAFo1lNTd8xfpbno3WAtHw0FLewLfjrFt0zfIgCk8EXj4PW355zRP3C2NQ==", "dependencies": { - "@sentry/core": "7.119.0", - "@sentry/replay": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry/core": "7.120.0", + "@sentry/replay": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry-internal/tracing": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.119.0.tgz", - "integrity": "sha512-oKdFJnn+56f0DHUADlL8o9l8jTib3VDLbWQBVkjD9EprxfaCwt2m8L5ACRBdQ8hmpxCEo4I8/6traZ7qAdBUqA==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.0.tgz", + "integrity": "sha512-VymJoIGMV0PcTJyshka9uJ1sKpR7bHooqW5jTEr6g0dYAwB723fPXHjVW+7SETF7i5+yr2KMprYKreqRidKyKA==", "dependencies": { - "@sentry/core": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry/core": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=8" @@ -3645,18 +3645,18 @@ } }, "node_modules/@sentry/browser": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.119.0.tgz", - "integrity": "sha512-WwmW1Y4D764kVGeKmdsNvQESZiAn9t8LmCWO0ucBksrjL2zw9gBPtOpRcO6l064sCLeSxxzCN+kIxhRm1gDFEA==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.120.0.tgz", + "integrity": "sha512-2hRE3QPLBBX+qqZEHY2IbJv4YvfXY7m/bWmNjN15phyNK3oBcm2Pa8ZiKUYrk8u/4DCEGzNUlhOmFgaxwSfpNw==", "dependencies": { - "@sentry-internal/feedback": "7.119.0", - "@sentry-internal/replay-canvas": "7.119.0", - "@sentry-internal/tracing": "7.119.0", - "@sentry/core": "7.119.0", - "@sentry/integrations": "7.119.0", - "@sentry/replay": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry-internal/feedback": "7.120.0", + "@sentry-internal/replay-canvas": "7.120.0", + "@sentry-internal/tracing": "7.120.0", + "@sentry/core": "7.120.0", + "@sentry/integrations": "7.120.0", + "@sentry/replay": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=8" @@ -3817,25 +3817,25 @@ } }, "node_modules/@sentry/core": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.119.0.tgz", - "integrity": "sha512-CS2kUv9rAJJEjiRat6wle3JATHypB0SyD7pt4cpX5y0dN5dZ1JrF57oLHRMnga9fxRivydHz7tMTuBhSSwhzjw==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.0.tgz", + "integrity": "sha512-uTc2sUQ0heZrMI31oFOHGxjKgw16MbV3C2mcT7qcrb6UmSGR9WqPOXZhnVVuzPWCnQ8B5IPPVdynK//J+9/m6g==", "dependencies": { - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/integrations": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.119.0.tgz", - "integrity": "sha512-OHShvtsRW0A+ZL/ZbMnMqDEtJddPasndjq+1aQXw40mN+zeP7At/V1yPZyFaURy86iX7Ucxw5BtmzuNy7hLyTA==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.0.tgz", + "integrity": "sha512-/Hs9MgSmG4JFNyeQkJ+MWh/fxO/U38Pz0VSH3hDrfyCjI8vH9Vz9inGEQXgB9Ke4eH8XnhsQ7xPnM27lWJts6g==", "dependencies": { - "@sentry/core": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0", + "@sentry/core": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0", "localforage": "^1.8.1" }, "engines": { @@ -3843,14 +3843,14 @@ } }, "node_modules/@sentry/react": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.119.0.tgz", - "integrity": "sha512-cf8Cei+qdSA26gx+IMAuc/k44PeBImNzIpXi3930SLhUe44ypT5OZ/44L6xTODHZzTIyMSJPduf59vT2+eW9yg==", - "dependencies": { - "@sentry/browser": "7.119.0", - "@sentry/core": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.120.0.tgz", + "integrity": "sha512-YTzmTRO9a2ZIdZiiT3Ob4h8/wLDEDC24qrUqomrYHG8Rcj+9EHjTqQQmoB8ARw9Kh0SrIzR5jbDK7C8JO6jzCQ==", + "dependencies": { + "@sentry/browser": "7.120.0", + "@sentry/core": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0", "hoist-non-react-statics": "^3.3.2" }, "engines": { @@ -3861,33 +3861,33 @@ } }, "node_modules/@sentry/replay": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.119.0.tgz", - "integrity": "sha512-BnNsYL+X5I4WCH6wOpY6HQtp4MgVt0NVlhLUsEyrvMUiTs0bPkDBrulsgZQBUKJsbOr3l9nHrFoNVB/0i6WNLA==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.120.0.tgz", + "integrity": "sha512-wV9fIYwNtMvFOHQB5eSm+kCorRXsX5+v1DxyTC8Lee1hfzcUQ2Wvqh75VktpXuM9TeZE8h7aQ4Wo4qCgTUdtvA==", "dependencies": { - "@sentry-internal/tracing": "7.119.0", - "@sentry/core": "7.119.0", - "@sentry/types": "7.119.0", - "@sentry/utils": "7.119.0" + "@sentry-internal/tracing": "7.120.0", + "@sentry/core": "7.120.0", + "@sentry/types": "7.120.0", + "@sentry/utils": "7.120.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/types": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.119.0.tgz", - "integrity": "sha512-27qQbutDBPKGbuJHROxhIWc1i0HJaGLA90tjMu11wt0E4UNxXRX+UQl4Twu68v4EV3CPvQcEpQfgsViYcXmq+w==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.0.tgz", + "integrity": "sha512-3mvELhBQBo6EljcRrJzfpGJYHKIZuBXmqh0y8prh03SWE62pwRL614GIYtd4YOC6OP1gfPn8S8h9w3dD5bF5HA==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.119.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.119.0.tgz", - "integrity": "sha512-ZwyXexWn2ZIe2bBoYnXJVPc2esCSbKpdc6+0WJa8eutXfHq3FRKg4ohkfCBpfxljQGEfP1+kfin945lA21Ka+A==", + "version": "7.120.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.0.tgz", + "integrity": "sha512-XZsPcBHoYu4+HYn14IOnhabUZgCF99Xn4IdWn8Hjs/c+VPtuAVDhRTsfPyPrpY3OcN8DgO5fZX4qcv/6kNbX1A==", "dependencies": { - "@sentry/types": "7.119.0" + "@sentry/types": "7.120.0" }, "engines": { "node": ">=8" @@ -5800,9 +5800,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "engines": { "node": ">= 0.6" @@ -5923,9 +5923,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -7105,9 +7105,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -7115,7 +7115,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8318,9 +8318,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", diff --git a/package.json b/package.json index 3d92ca51..96b13326 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.102.5", + "version": "0.103.2", "main": "index.js", "private": true, "dependencies": { diff --git a/src/Components/NicknamePaymentInput.res b/src/Components/NicknamePaymentInput.res index e0df0bdd..bb4bf762 100644 --- a/src/Components/NicknamePaymentInput.res +++ b/src/Components/NicknamePaymentInput.res @@ -1,19 +1,50 @@ @react.component -let make = (~paymentType: CardThemeType.mode, ~value, ~setValue) => { - let {config, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) +let make = (~paymentType: CardThemeType.mode) => { + open RecoilAtoms + open Utils + + let (nickName, setNickName) = Recoil.useRecoilState(userCardNickName) + let {localeString} = Recoil.useRecoilValueFromAtom(configAtom) + + let validateNickname = val => { + let isValid = Some(val === "" || !(val->isDigitLimitExceeded(~digit=2))) + let errorString = + val !== "" && val->isDigitLimitExceeded(~digit=2) ? localeString.invalidNickNameError : "" + + (isValid, errorString) + } + + let setNickNameState = (val, prevState: RecoilAtomTypes.field) => { + let (isValid, errorString) = val->validateNickname + { + ...prevState, + value: val, + isValid, + errorString, + } + } let onChange = ev => { - let val = ReactEvent.Form.target(ev)["value"] - setValue(_ => val) + let val: string = ReactEvent.Form.target(ev)["value"] + setNickName(prev => setNickNameState(val, prev)) + } + + let onBlur = ev => { + let val: string = ReactEvent.Focus.target(ev)["value"] + setNickName(prev => setNickNameState(val, prev)) } - } diff --git a/src/PaymentElement.res b/src/PaymentElement.res index c4580a64..d5fd4364 100644 --- a/src/PaymentElement.res +++ b/src/PaymentElement.res @@ -17,6 +17,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod layout, customerPaymentMethods, displaySavedPaymentMethods, + sdkHandleConfirmPayment, } = Recoil.useRecoilValueFromAtom(optionAtom) let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom) let optionAtomValue = Recoil.useRecoilValueFromAtom(optionAtom) @@ -24,10 +25,8 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod let (sessions, setSessions) = React.useState(_ => Dict.make()->JSON.Encode.object) let (paymentOptions, setPaymentOptions) = React.useState(_ => []) let (walletOptions, setWalletOptions) = React.useState(_ => []) - let {sdkHandleConfirmPayment} = Recoil.useRecoilValueFromAtom(optionAtom) - - let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady) - let isGPayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isGooglePayReady) + let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady) + let isGPayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady) let (paymentMethodListValue, setPaymentMethodListValue) = Recoil.useRecoilState( PaymentUtils.paymentMethodListValue, @@ -158,6 +157,11 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod googlePayThirdPartySessionObj.sessionsToken, Gpay, ) + let { + paypalToken, + isPaypalSDKFlow, + isPaypalRedirectFlow, + } = PayPalHelpers.usePaymentMethodExperience(~paymentMethodListValue, ~sessionObj) React.useEffect(() => { switch paymentMethodList { @@ -355,6 +359,27 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod | _ => React.null }} + | PayPal => + + {switch paypalToken { + | OtherTokenOptional(optToken) => + switch (optToken, isPaypalSDKFlow, isPaypalRedirectFlow) { + | (Some(_token), true, _) => { + loggerState.setLogInfo( + ~value="PayPal Invoke SDK Flow in Tabs", + ~eventName=PAYPAL_SDK_FLOW, + ) + React.null + } + | (_, _, true) => + | _ => React.null + } + | _ => + + + + }} + | _ => diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index ee447637..7af2f4b2 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -21,7 +21,7 @@ let make = ( let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) - let (nickname, setNickname) = React.useState(_ => "") + let nickname = Recoil.useRecoilValueFromAtom(RecoilAtoms.userCardNickName) let ( isCardValid, @@ -145,7 +145,7 @@ let make = ( ~cardHolderName="", ~cvcNumber, ~cardBrand=cardNetwork, - ~nickname, + ~nickname=nickname.value, ) let banContactBody = PaymentBody.bancontactBody() let cardBody = if isCustomerAcceptanceRequired { @@ -154,12 +154,20 @@ let make = ( defaultCardBody } if confirm.doSubmit { - let validFormat = - (isBancontact || - (isCVCValid->Option.getOr(false) && + // * Sending card expiry to handle cases where the card expires before the use date. + messageParentWindow([("expiryDate", cardExpiry->JSON.Encode.string)]) + + let isCardDetailsValid = + isCVCValid->Option.getOr(false) && isCardValid->Option.getOr(false) && isCardSupported->Option.getOr(false) && - isExpiryValid->Option.getOr(false))) && areRequiredFieldsValid + isExpiryValid->Option.getOr(false) + + let isNicknameValid = nickname.value === "" || nickname.isValid->Option.getOr(false) + + let validFormat = + (isBancontact || isCardDetailsValid) && isNicknameValid && areRequiredFieldsValid + if validFormat && (showFields || isBancontact) { intent( ~bodyArr={ @@ -339,7 +347,7 @@ let make = ( - + diff --git a/src/Payments/PayPal.res b/src/Payments/PayPal.res index f7edbb5f..32e27aaa 100644 --- a/src/Payments/PayPal.res +++ b/src/Payments/PayPal.res @@ -11,7 +11,7 @@ module Loader = { let payPalIcon = @react.component -let make = () => { +let make = (~paymentType, ~walletOptions) => { let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let (paypalClicked, setPaypalClicked) = React.useState(_ => false) let sdkHandleIsThere = Recoil.useRecoilValueFromAtom(isPaymentButtonHandlerProvidedAtom) @@ -19,6 +19,8 @@ let make = () => { let options = Recoil.useRecoilValueFromAtom(optionAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(areOneClickWalletsRendered) let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) + let isWallet = walletOptions->Array.includes("paypal") + let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make()) let (_, _, labelType) = options.wallets.style.type_ let _label = switch labelType { @@ -56,12 +58,16 @@ let make = () => { let (connectors, _) = paymentMethodListValue->PaymentUtils.getConnectors(Wallets(Paypal(Redirect))) let body = PaymentBody.paypalRedirectionBody(~connectors) - - let modifiedPaymentBody = PaymentUtils.appendedCustomerAcceptance( + let basePaymentBody = PaymentUtils.appendedCustomerAcceptance( ~isGuestCustomer, ~paymentType=paymentMethodListValue.payment_type, ~body, ) + let modifiedPaymentBody = if isWallet { + basePaymentBody + } else { + basePaymentBody->Utils.mergeAndFlattenToTuples(requiredFieldsBody) + } intent( ~bodyArr=modifiedPaymentBody, @@ -88,24 +94,61 @@ let make = () => { None }) - + let useSubmitCallback = (~isWallet) => { + let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid) + let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty) + let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + let {iframeId} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + + React.useCallback((ev: Window.event) => { + if !isWallet { + let json = ev.data->Utils.safeParse + let confirm = json->Utils.getDictFromJson->ConfirmType.itemToObjMapper + if confirm.doSubmit && areRequiredFieldsValid && !areRequiredFieldsEmpty { + onPaypalClick(ev) + } else if areRequiredFieldsEmpty { + Utils.postFailedSubmitResponse( + ~errortype="validation_error", + ~message=localeString.enterFieldsText, + ) + } else if !areRequiredFieldsValid { + Utils.postFailedSubmitResponse( + ~errortype="validation_error", + ~message=localeString.enterValidDetailsText, + ) + } + } + }, (areRequiredFieldsValid, areRequiredFieldsEmpty, isWallet, iframeId)) + } + + let submitCallback = useSubmitCallback(~isWallet) + Utils.useSubmitPaymentData(submitCallback) + + if isWallet { + + } else { + + } } let default = make diff --git a/src/Payments/PayPal.resi b/src/Payments/PayPal.resi index 47fa1ac2..fcd18f44 100644 --- a/src/Payments/PayPal.resi +++ b/src/Payments/PayPal.resi @@ -1,2 +1,2 @@ @react.component -let default: unit => React.element +let default: (~paymentType: CardThemeType.mode, ~walletOptions: array) => React.element diff --git a/src/Payments/PayPalHelpers.res b/src/Payments/PayPalHelpers.res new file mode 100644 index 00000000..dc405d12 --- /dev/null +++ b/src/Payments/PayPalHelpers.res @@ -0,0 +1,31 @@ +open PaymentMethodsRecord +open SessionsType + +type paypalExperienceData = { + paypalToken: optionalTokenType, + isPaypalSDKFlow: bool, + isPaypalRedirectFlow: bool, +} + +let usePaymentMethodExperience = (~paymentMethodListValue, ~sessionObj: sessions) => { + let paypalPaymentMethodExperience = React.useMemo(() => { + getPaymentExperienceTypeFromPML( + ~paymentMethodList=paymentMethodListValue, + ~paymentMethodName="wallet", + ~paymentMethodType="paypal", + ) + }, [paymentMethodListValue]) + + let paypalToken = React.useMemo( + () => getPaymentSessionObj(sessionObj.sessionsToken, Paypal), + [sessionObj], + ) + let isPaypalSDKFlow = paypalPaymentMethodExperience->Array.includes(InvokeSDK) + let isPaypalRedirectFlow = paypalPaymentMethodExperience->Array.includes(RedirectToURL) + + { + paypalToken, + isPaypalSDKFlow, + isPaypalRedirectFlow, + } +} diff --git a/src/Payments/PaymentMethodsRecord.res b/src/Payments/PaymentMethodsRecord.res index 15cbfee7..2a3c30e3 100644 --- a/src/Payments/PaymentMethodsRecord.res +++ b/src/Payments/PaymentMethodsRecord.res @@ -534,6 +534,13 @@ let paymentMethodsFields = [ fields: [InfoElement], miniIcon: None, }, + { + paymentMethodName: "paypal", + icon: Some(icon("paypal", ~size=21, ~width=25)), + displayName: "Paypal", + fields: [], + miniIcon: None, + }, { paymentMethodName: "local_bank_transfer_transfer", fields: [InfoElement], diff --git a/src/Payments/PaymentMethodsWrapper.res b/src/Payments/PaymentMethodsWrapper.res index cb4cf062..b9d2585b 100644 --- a/src/Payments/PaymentMethodsWrapper.res +++ b/src/Payments/PaymentMethodsWrapper.res @@ -40,17 +40,15 @@ let make = (~paymentType: CardThemeType.mode, ~paymentMethodName: string) => { let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(areRequiredFieldsValid) let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(areRequiredFieldsEmpty) - let complete = areRequiredFieldsValid - React.useEffect(() => { - setFieldComplete(_ => complete) + setFieldComplete(_ => areRequiredFieldsValid) None - }, [complete]) + }, [areRequiredFieldsValid]) let empty = areRequiredFieldsEmpty UtilityHooks.useHandlePostMessages( - ~complete, + ~complete=areRequiredFieldsValid, ~empty, ~paymentType=paymentMethodDetails.paymentMethodName, ) @@ -59,7 +57,7 @@ let make = (~paymentType: CardThemeType.mode, ~paymentMethodName: string) => { let json = ev.data->safeParse let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper if confirm.doSubmit { - if complete { + if areRequiredFieldsValid { let countryCode = Country.getCountry(paymentMethodName) ->Array.filter(item => item.countryName == country) @@ -105,7 +103,10 @@ let make = (~paymentType: CardThemeType.mode, ~paymentMethodName: string) => { paymentMethodName, isManualRetryEnabled, phoneNumber.value, - (selectedBank, currency, requiredFieldsBody), + selectedBank, + currency, + requiredFieldsBody, + areRequiredFieldsValid, )) useSubmitPaymentData(submitCallback)
{ let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue) let sessionObj = React.useMemo(() => itemToObjMapper(dict, Others), [dict]) - let paypalToken = React.useMemo( - () => getPaymentSessionObj(sessionObj.sessionsToken, Paypal), - [sessionObj], - ) - let paypalPaymentMethodExperience = React.useMemo(() => { - PaymentMethodsRecord.getPaymentExperienceTypeFromPML( - ~paymentMethodList=paymentMethodListValue, - ~paymentMethodName="wallet", - ~paymentMethodType="paypal", - ) - }, [paymentMethodListValue]) + + let { + paypalToken, + isPaypalSDKFlow, + isPaypalRedirectFlow, + } = PayPalHelpers.usePaymentMethodExperience(~paymentMethodListValue, ~sessionObj) + let gPayToken = getPaymentSessionObj(sessionObj.sessionsToken, Gpay) let applePaySessionObj = itemToObjMapper(dict, ApplePayObject) let applePayToken = getPaymentSessionObj(applePaySessionObj.sessionsToken, ApplePay) @@ -70,8 +66,6 @@ let make = (~sessions, ~walletOptions, ~paymentType) => { let {clientSecret} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) - let isPaypalSDKFlow = paypalPaymentMethodExperience->Array.includes(InvokeSDK) - let isPaypalRedirectFlow = paypalPaymentMethodExperience->Array.includes(RedirectToURL)
{walletOptions @@ -113,12 +107,12 @@ let make = (~sessions, ~walletOptions, ~paymentType) => { | OtherTokenOptional(optToken) => switch (optToken, isPaypalSDKFlow, isPaypalRedirectFlow) { | (Some(token), true, _) => - | (_, _, true) => + | (_, _, true) => | _ => React.null } | _ => - + }} diff --git a/src/Payments/PazeTypes.res b/src/Payments/PazeTypes.res index 1452ee62..133f1748 100644 --- a/src/Payments/PazeTypes.res +++ b/src/Payments/PazeTypes.res @@ -36,8 +36,9 @@ type complete = { transactionValue: transactionValue, } +type canCheckoutReturnType = {consumerPresent: bool} type digitalWalletSdk = { - canCheckout: canCheckout => promise, + canCheckout: canCheckout => promise, checkout: checkout => promise, complete: complete => promise, initialize: initialize => promise, diff --git a/src/Payments/PazeWallet.res b/src/Payments/PazeWallet.res index 9008954d..6f70bce8 100644 --- a/src/Payments/PazeWallet.res +++ b/src/Payments/PazeWallet.res @@ -41,12 +41,12 @@ let make = () => { Console.log2("PAZE --- init completed", val) - let consumerPresent = await digitalWalletSdk.canCheckout({ + let canCheckout = await digitalWalletSdk.canCheckout({ emailAddress: emailAddress, }) Console.log("PAZE --- canCheckout completed") - Console.log2("PAZE --- consumerPresent: ", consumerPresent) + Console.log2("PAZE --- canCheckout: ", canCheckout.consumerPresent) let transactionValue = { transactionAmount, @@ -59,16 +59,16 @@ let make = () => { payloadTypeIndicator: "PAYMENT", } - let checkoutResponse = await digitalWalletSdk.checkout({ + let _ = await digitalWalletSdk.checkout({ acceptedPaymentCardNetworks: ["VISA", "MASTERCARD"], - emailAddress, + emailAddress: canCheckout.consumerPresent ? emailAddress : "", sessionId, actionCode: "START_FLOW", transactionValue, shippingPreference: "ALL", }) - Console.log2("PAZE --- Checkout Response Object: ", checkoutResponse) + Console.log("PAZE --- digitalWalletSdk.checkout completed") let completeObj = { transactionOptions, @@ -80,7 +80,7 @@ let make = () => { let completeResponse = await digitalWalletSdk.complete(completeObj) - Console.log2("PAZE --- Complete Response Object: ", completeResponse) + Console.log("PAZE --- digitalWalletSdk.complete completed") messageParentWindow([ ("fullscreen", false->JSON.Encode.bool), diff --git a/src/Types/PaymentModeType.res b/src/Types/PaymentModeType.res index 14d29b39..074f872f 100644 --- a/src/Types/PaymentModeType.res +++ b/src/Types/PaymentModeType.res @@ -19,6 +19,7 @@ type payment = | GooglePay | ApplePay | Boleto + | PayPal | NONE let paymentMode = str => { @@ -43,6 +44,7 @@ let paymentMode = str => { | "google_pay" => GooglePay | "apple_pay" => ApplePay | "boleto" => Boleto + | "paypal" => PayPal | _ => NONE } } diff --git a/src/Utilities/DynamicFieldsUtils.res b/src/Utilities/DynamicFieldsUtils.res index 0f3c5685..8714428c 100644 --- a/src/Utilities/DynamicFieldsUtils.res +++ b/src/Utilities/DynamicFieldsUtils.res @@ -25,6 +25,7 @@ let dynamicFieldsEnabledPaymentMethods = [ "bacs", "pay_bright", "multibanco_transfer", + "paypal", ] let getName = (item: PaymentMethodsRecord.required_fields, field: RecoilAtomTypes.field) => { @@ -289,6 +290,7 @@ let useRequiredFieldsEmptyAndValid = ( cardExpiry, cvcNumber, bankAccountNumber, + cryptoCurrencyNetworks, )) } diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res index b1c071b6..4a4c8a92 100644 --- a/src/Utilities/PaymentUtils.res +++ b/src/Utilities/PaymentUtils.res @@ -10,6 +10,7 @@ let paymentListLookupNew = ( ~areAllGooglePayRequiredFieldsPrefilled, ~isGooglePayReady, ~shouldDisplayApplePayInTabs, + ~shouldDisplayPayPalInTabs, ) => { let pmList = list->PaymentMethodsRecord.buildFromPaymentList let walletsList = [] @@ -36,6 +37,10 @@ let paymentListLookupNew = ( walletToBeDisplayedInTabs->Array.push("apple_pay") } + if shouldDisplayPayPalInTabs { + walletToBeDisplayedInTabs->Array.push("paypal") + } + if ( !paymentMethodListValue.collect_billing_details_from_wallets && !areAllGooglePayRequiredFieldsPrefilled && @@ -305,6 +310,32 @@ let getIsKlarnaSDKFlow = sessions => { } } +let usePaypalFlowStatus = (~sessions, ~paymentMethodListValue) => { + open Utils + + let sessionObj = + sessions + ->getDictFromJson + ->SessionsType.itemToObjMapper(Others) + + let { + paypalToken, + isPaypalSDKFlow, + isPaypalRedirectFlow, + } = PayPalHelpers.usePaymentMethodExperience(~paymentMethodListValue, ~sessionObj) + + let isPaypalTokenExist = switch paypalToken { + | OtherTokenOptional(optToken) => + switch optToken { + | Some(_) => true + | _ => false + } + | _ => false + } + + (isPaypalSDKFlow, isPaypalRedirectFlow, isPaypalTokenExist) +} + let useGetPaymentMethodList = (~paymentOptions, ~paymentType, ~sessions) => { open Utils let methodslist = Recoil.useRecoilValueFromAtom(RecoilAtoms.paymentMethodList) @@ -332,9 +363,20 @@ let useGetPaymentMethodList = (~paymentOptions, ~paymentType, ~sessions) => { ~paymentMethodType="google_pay", ) + let areAllPaypalRequiredFieldsPreFilled = useAreAllRequiredFieldsPrefilled( + ~paymentMethodListValue, + ~paymentMethod="wallet", + ~paymentMethodType="paypal", + ) + let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady) let isGooglePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isGooglePayReady) + let (isPaypalSDKFlow, isPaypalRedirectFlow, isPaypalTokenExist) = usePaypalFlowStatus( + ~sessions, + ~paymentMethodListValue, + ) + React.useMemo(() => { switch methodslist { | Loaded(paymentlist) => @@ -347,16 +389,26 @@ let useGetPaymentMethodList = (~paymentOptions, ~paymentType, ~sessions) => { !areAllApplePayRequiredFieldsPrefilled && isApplePayReady + let isShowPaypal = optionAtomValue.wallets.payPal === Auto + + let shouldDisplayPayPalInTabs = + isShowPaypal && + !paymentMethodListValue.collect_billing_details_from_wallets && + !areAllPaypalRequiredFieldsPreFilled && + isPaypalRedirectFlow && + (!isPaypalSDKFlow || !isPaypalTokenExist) + let (wallets, otherOptions) = plist->paymentListLookupNew( ~order=paymentOrder, - ~isShowPaypal=optionAtomValue.wallets.payPal === Auto, + ~isShowPaypal, ~isShowKlarnaOneClick=optionAtomValue.wallets.klarna === Auto, ~isKlarnaSDKFlow, ~paymentMethodListValue=plist, ~areAllGooglePayRequiredFieldsPrefilled, ~isGooglePayReady, ~shouldDisplayApplePayInTabs, + ~shouldDisplayPayPalInTabs, ) let klarnaPaymentMethodExperience = PaymentMethodsRecord.getPaymentExperienceTypeFromPML( diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res index 6ecce38e..5281dc20 100644 --- a/src/Utilities/RecoilAtoms.res +++ b/src/Utilities/RecoilAtoms.res @@ -50,6 +50,7 @@ let userPhoneNumber = Recoil.atom( countryCode: "", }, ) +let userCardNickName = Recoil.atom("userCardNickName", defaultFieldValues) let isGooglePayReady = Recoil.atom("isGooglePayReady", false) let isApplePayReady = Recoil.atom("isApplePayReady", false) let userCountry = Recoil.atom("userCountry", "") diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index c5e82b2d..745478b0 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -1451,3 +1451,10 @@ let handleIframePostMessageForWallets = (msg, componentName, mountedIframeRef) = mountedIframeRef->Window.iframePostMessage(msg) } } + +let isDigitLimitExceeded = (val, ~digit) => { + switch val->String.match(%re("/\d/g")) { + | Some(matches) => matches->Array.length > digit + | None => false + } +} diff --git a/webpack.common.js b/webpack.common.js index c3cea6c8..18a9015f 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -50,7 +50,6 @@ const getEnvironmentDomain = (prodDomain, integDomain, defaultDomain) => { const backendDomain = getEnvironmentDomain("checkout", "dev", "beta"); const confirmDomain = getEnvironmentDomain("api", "integ-api", "sandbox"); -const logDomain = getEnvironmentDomain("api", "integ-api", "sandbox"); const backendEndPoint = envBackendUrl || `https://${backendDomain}.hyperswitch.io/api`; @@ -58,8 +57,7 @@ const backendEndPoint = const confirmEndPoint = envBackendUrl || `https://${confirmDomain}.hyperswitch.io`; -const logEndpoint = - envLoggingUrl || `https://${logDomain}.hyperswitch.io/logs/sdk`; +const logEndpoint = envLoggingUrl; const loggingLevel = "DEBUG"; const maxLogsPushedPerEventName = 100;