diff --git a/public/icons/orca.svg b/public/icons/orca.svg index 7d6d7eba7..dcdab264b 100644 --- a/public/icons/orca.svg +++ b/public/icons/orca.svg @@ -1014,19 +1014,72 @@ License) - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/src/App.res b/src/App.res index eec095614..a8d3c8c1f 100644 --- a/src/App.res +++ b/src/App.res @@ -57,6 +57,7 @@ let make = () => { switch fullscreenMode { | "paymentloader" => | "plaidSDK" => + | "pazeWallet" => | "fullscreen" =>
diff --git a/src/Components/Spinner.res b/src/Components/Spinner.res new file mode 100644 index 000000000..da0e35722 --- /dev/null +++ b/src/Components/Spinner.res @@ -0,0 +1,7 @@ +@react.component +let make = () => { + let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) +
+ +
+} diff --git a/src/PaymentMethodCollectElement.res b/src/PaymentMethodCollectElement.res index f791f9ac7..8a3679d25 100644 --- a/src/PaymentMethodCollectElement.res +++ b/src/PaymentMethodCollectElement.res @@ -297,11 +297,7 @@ let make = (~integrateError, ~logger) => {

{React.string(`${options.currency} ${options.amount}`)}

- O + O
diff --git a/src/Payments/PaymentRequestButtonElement.res b/src/Payments/PaymentRequestButtonElement.res index dc2514972..e7de70049 100644 --- a/src/Payments/PaymentRequestButtonElement.res +++ b/src/Payments/PaymentRequestButtonElement.res @@ -1,4 +1,4 @@ -type wallet = GPayWallet | PaypalWallet | ApplePayWallet | KlarnaWallet | NONE +type wallet = GPayWallet | PaypalWallet | ApplePayWallet | KlarnaWallet | PazeWallet | NONE let paymentMode = str => { switch str { | "gpay" @@ -9,6 +9,7 @@ let paymentMode = str => { | "apple_pay" => ApplePayWallet | "klarna" => KlarnaWallet + | "paze" => PazeWallet | _ => NONE } } @@ -64,7 +65,8 @@ let make = (~sessions, ~walletOptions, ~paymentType) => { Gpay, ) - let klarnaTokenObj = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Klarna) + let klarnaTokenObj = getPaymentSessionObj(sessionObj.sessionsToken, Klarna) + let pazeTokenObj = getPaymentSessionObj(sessionObj.sessionsToken, Paze) let {clientSecret} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) let isPaypalSDKFlow = paypalPaymentMethodExperience->Array.includes(InvokeSDK) @@ -137,6 +139,17 @@ let make = (~sessions, ~walletOptions, ~paymentType) => { }} + | PazeWallet => + + {switch pazeTokenObj { + | OtherTokenOptional(optToken) => + switch optToken { + | Some(token) => + | None => React.null + } + | _ => React.null + }} + | NONE => React.null } | None => React.null diff --git a/src/Payments/PazeButton.res b/src/Payments/PazeButton.res new file mode 100644 index 000000000..411beee9e --- /dev/null +++ b/src/Payments/PazeButton.res @@ -0,0 +1,83 @@ +@react.component +let make = (~token: SessionsType.token) => { + open Utils + open RecoilAtoms + let {iframeId, publishableKey, clientSecret} = Recoil.useRecoilValueFromAtom(keys) + let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom) + let options = Recoil.useRecoilValueFromAtom(optionAtom) + let (showLoader, setShowLoader) = React.useState(() => false) + let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing) + let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) + let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Paze) + let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled) + let paymentIntentID = clientSecret->Option.getOr("")->getPaymentId + + React.useEffect0(() => { + setIsShowOrPayUsing(_ => true) + None + }) + + let onClick = _ => { + setShowLoader(_ => true) + messageParentWindow([ + ("fullscreen", true->JSON.Encode.bool), + ("param", "pazeWallet"->JSON.Encode.string), + ("iframeId", iframeId->JSON.Encode.string), + ( + "metadata", + [ + ("wallet", (token.walletName :> string)->JSON.Encode.string), + ("clientId", token.clientId->JSON.Encode.string), + ("clientName", token.clientName->JSON.Encode.string), + ("clientProfileId", token.clientProfileId->JSON.Encode.string), + ("sessionId", paymentIntentID->JSON.Encode.string), + ("publishableKey", publishableKey->JSON.Encode.string), + ("emailAddress", token.email_address->JSON.Encode.string), + ("transactionAmount", token.transaction_amount->JSON.Encode.string), + ("transactionCurrencyCode", token.transaction_currency_code->JSON.Encode.string), + ]->getJsonFromArrayOfJson, + ), + ]) + } + + React.useEffect0(() => { + let onPazeCallback = (ev: Window.event) => { + let json = ev.data->safeParse + let dict = json->Utils.getDictFromJson->getDictFromDict("data") + let isPaze = dict->getBool("isPaze", false) + if isPaze { + setShowLoader(_ => false) + if dict->getOptionString("completeResponse")->Option.isSome { + let completeResponse = dict->getString("completeResponse", "") + intent( + ~bodyArr=PaymentBody.pazeBody(~completeResponse), + ~confirmParam={ + return_url: options.wallets.walletReturnUrl, + publishableKey, + }, + ~handleUserError=false, + ~manualRetry=isManualRetryEnabled, + ) + } + } + } + Window.addEventListener("message", onPazeCallback) + Some(() => Window.removeEventListener("message", ev => onPazeCallback(ev))) + }) + + +} diff --git a/src/Payments/PazeTypes.res b/src/Payments/PazeTypes.res new file mode 100644 index 000000000..1452ee620 --- /dev/null +++ b/src/Payments/PazeTypes.res @@ -0,0 +1,44 @@ +type client = { + id: string, + name: string, + profileId: string, +} +type initialize = {client: client} + +type canCheckout = {emailAddress: string} + +type transactionValue = { + transactionAmount: string, + transactionCurrencyCode: string, +} + +type transactionOptions = { + billingPreference: string, + merchantCategoryCode: string, + payloadTypeIndicator: string, +} + +type checkout = { + acceptedPaymentCardNetworks: array, + emailAddress?: string, + sessionId: string, + actionCode: string, + transactionValue: transactionValue, + shippingPreference: string, +} + +type complete = { + transactionOptions: transactionOptions, + transactionId: string, + emailAddress?: string, + sessionId: string, + transactionType: string, + transactionValue: transactionValue, +} + +type digitalWalletSdk = { + canCheckout: canCheckout => promise, + checkout: checkout => promise, + complete: complete => promise, + initialize: initialize => promise, +} diff --git a/src/Payments/PazeWallet.res b/src/Payments/PazeWallet.res new file mode 100644 index 000000000..f815f2ba2 --- /dev/null +++ b/src/Payments/PazeWallet.res @@ -0,0 +1,137 @@ +open PazeTypes + +@val @scope("window") +external digitalWalletSdk: digitalWalletSdk = "DIGITAL_WALLET_SDK" + +@react.component +let make = () => { + open Promise + open Utils + + React.useEffect0(() => { + let handle = (ev: Window.event) => { + let json = ev.data->safeParse + let metaData = json->getDictFromJson->getDictFromDict("metadata") + if metaData->getString("wallet", "") === "Paze" { + let clientId = metaData->getString("clientId", "") + let clientName = metaData->getString("clientName", "") + let clientProfileId = metaData->getString("clientProfileId", "") + let sessionId = metaData->getString("sessionId", "") + let publishableKey = metaData->getString("publishableKey", "") + let emailAddress = metaData->getString("emailAddress", "") + let transactionAmount = metaData->getString("transactionAmount", "") + let transactionCurrencyCode = metaData->getString("transactionCurrencyCode", "") + + let mountPazeSDK = () => { + let pazeScriptURL = + publishableKey->String.startsWith("pk_snd") + ? `https://sandbox.digitalwallet.earlywarning.com/web/resources/js/digitalwallet-sdk.js` + : `https://checkout.paze.com/web/resources/js/digitalwallet-sdk.js` + + let loadPazeSDK = async _ => { + try { + let val = await digitalWalletSdk.initialize({ + client: { + id: clientId, + name: clientName, + profileId: clientProfileId, + }, + }) + + Console.log2("PAZE --- init completed", val) + + let consumerPresent = await digitalWalletSdk.canCheckout({ + emailAddress: emailAddress, + }) + + Console.log("PAZE --- canCheckout completed") + Console.log2("PAZE --- consumerPresent: ", consumerPresent) + + let transactionValue = { + transactionAmount, + transactionCurrencyCode, + } + + let transactionOptions = { + billingPreference: "ALL", + merchantCategoryCode: "US", + payloadTypeIndicator: "PAYMENT", + } + + let checkoutResponse = await digitalWalletSdk.checkout({ + acceptedPaymentCardNetworks: ["VISA", "MASTERCARD"], + emailAddress, + sessionId, + actionCode: "START_FLOW", + transactionValue, + shippingPreference: "ALL", + }) + + Console.log2("PAZE --- Checkout Response Object: ", checkoutResponse) + + let completeObj = { + transactionOptions, + transactionId: "", + sessionId, + transactionType: "PURCHASE", + transactionValue, + } + + let completeResponse = await digitalWalletSdk.complete(completeObj) + + Console.log2("PAZE --- Complete Response Object: ", completeResponse) + + messageParentWindow([ + ("fullscreen", false->JSON.Encode.bool), + ("isPaze", true->JSON.Encode.bool), + ( + "completeResponse", + completeResponse + ->getDictFromJson + ->getString("completeResponse", "") + ->JSON.Encode.string, + ), + ]) + + resolve() + } catch { + | _ => + messageParentWindow([ + ("fullscreen", false->JSON.Encode.bool), + ("isPaze", true->JSON.Encode.bool), + ("flowExited", "stop"->JSON.Encode.string), + ]) + resolve() + } + } + + let pazeScript = Window.createElement("script") + pazeScript->Window.elementSrc(pazeScriptURL) + pazeScript->Window.elementOnerror(exn => { + let err = exn->Identity.anyTypeToJson->JSON.stringify + Console.log2("PAZE --- errrorrr", err) + }) + pazeScript->Window.elementOnload(_ => loadPazeSDK()->ignore) + Window.body->Window.appendChild(pazeScript) + } + + if ( + [ + clientId, + clientName, + clientProfileId, + sessionId, + transactionCurrencyCode, + ]->Array.every(x => x != "") + ) { + mountPazeSDK() + } + } + } + Window.addEventListener("message", handle) + messageParentWindow([("iframeMountedCallback", true->JSON.Encode.bool)]) + Some(() => {Window.removeEventListener("message", handle)}) + }) + +
+} diff --git a/src/Types/SessionsType.res b/src/Types/SessionsType.res index 1557ddb8d..07d0396c7 100644 --- a/src/Types/SessionsType.res +++ b/src/Types/SessionsType.res @@ -1,5 +1,8 @@ -type wallet = Gpay | Paypal | Klarna | ApplePay | NONE -type tokenCategory = ApplePayObject | GooglePayThirdPartyObject | Others +open Utils + +type wallet = Gpay | Paypal | Klarna | ApplePay | Paze | NONE + +type tokenCategory = ApplePayObject | GooglePayThirdPartyObject | PazeObject | Others type paymentType = Wallet | Others @@ -15,15 +18,24 @@ type token = { shippingAddressParameters: JSON.t, orderDetails: JSON.t, connector: string, + clientId: string, + clientName: string, + clientProfileId: string, + email_address: string, + transaction_amount: string, + transaction_currency_code: string, } type tokenType = | ApplePayToken(array) | GooglePayThirdPartyToken(array) + | PazeToken(array) | OtherToken(array) + type optionalTokenType = | ApplePayTokenOptional(option) | GooglePayThirdPartyTokenOptional(option) + | PazeTokenOptional(option) | OtherTokenOptional(option) type sessions = { @@ -43,6 +55,12 @@ let defaultToken = { shippingAddressParameters: Dict.make()->JSON.Encode.object, orderDetails: Dict.make()->JSON.Encode.object, connector: "", + clientId: "", + clientName: "", + clientProfileId: "", + email_address: "", + transaction_amount: "", + transaction_currency_code: "", } let getWallet = str => { switch str { @@ -50,12 +68,12 @@ let getWallet = str => { | "paypal" => Paypal | "klarna" => Klarna | "google_pay" => Gpay + | "paze" => Paze | _ => NONE } } -open Utils -let getSessionsToken = (dict, str) => { +let getSessionsToken = (dict, str) => dict ->Dict.get(str) ->Option.flatMap(JSON.Decode.array) @@ -74,14 +92,18 @@ let getSessionsToken = (dict, str) => { shippingAddressParameters: getJsonObjectFromDict(dict, "shipping_address_parameters"), orderDetails: getJsonObjectFromDict(dict, "order_details"), connector: getString(dict, "connector", ""), + clientId: getString(dict, "client_id", ""), + clientName: getString(dict, "client_name", ""), + clientProfileId: getString(dict, "client_profile_id", ""), + email_address: getString(dict, "email_address", ""), + transaction_amount: getString(dict, "transaction_amount", ""), + transaction_currency_code: getString(dict, "transaction_currency_code", ""), } }) }) ->Option.getOr([defaultToken]) -} -let getSessionsTokenJson = (dict, str) => { +let getSessionsTokenJson = (dict, str) => dict->Dict.get(str)->Option.flatMap(JSON.Decode.array)->Option.getOr([]) -} let itemToObjMapper = (dict, returnType) => { switch returnType { @@ -97,6 +119,12 @@ let itemToObjMapper = (dict, returnType) => { sessionsToken: GooglePayThirdPartyToken(getSessionsTokenJson(dict, "session_token")), } + | PazeObject => { + paymentId: getString(dict, "payment_id", ""), + clientSecret: getString(dict, "client_secret", ""), + sessionsToken: PazeToken(getSessionsTokenJson(dict, "session_token")), + } + | Others => { paymentId: getString(dict, "payment_id", ""), clientSecret: getString(dict, "client_secret", ""), @@ -105,27 +133,21 @@ let itemToObjMapper = (dict, returnType) => { } } -let getWalletFromTokenType = (arr, val: wallet) => { - let x = arr->Array.find(item => +let getWalletFromTokenType = (arr, val) => + arr->Array.find(item => item ->JSON.Decode.object - ->Option.flatMap(x => { - x->Dict.get("wallet_name") - }) + ->Option.flatMap(x => x->Dict.get("wallet_name")) ->Option.flatMap(JSON.Decode.string) ->Option.getOr("") ->getWallet === val ) - x -} -let getPaymentSessionObj = (tokenType: tokenType, val: wallet) => { +let getPaymentSessionObj = (tokenType, val) => switch tokenType { | ApplePayToken(arr) => ApplePayTokenOptional(getWalletFromTokenType(arr, val)) - | GooglePayThirdPartyToken(arr) => GooglePayThirdPartyTokenOptional(getWalletFromTokenType(arr, val)) - + | PazeToken(arr) => PazeTokenOptional(getWalletFromTokenType(arr, val)) | OtherToken(arr) => OtherTokenOptional(arr->Array.find(item => item.walletName == val)) } -} diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res index ca2d37baa..2a5a8e75e 100644 --- a/src/Utilities/PaymentBody.res +++ b/src/Utilities/PaymentBody.res @@ -935,6 +935,22 @@ let multibancoBody = (~email) => [ ), ] +let pazeBody = (~completeResponse) => { + open Utils + let pazeCompleteResponse = + [("complete_response", completeResponse->JSON.Encode.string)]->getJsonFromArrayOfJson + + let pazeWalletData = [("paze", pazeCompleteResponse)]->getJsonFromArrayOfJson + + let paymentMethodData = [("wallet", pazeWalletData)]->getJsonFromArrayOfJson + + [ + ("payment_method", "wallet"->JSON.Encode.string), + ("payment_method_type", "paze"->JSON.Encode.string), + ("payment_method_data", paymentMethodData), + ] +} + let getPaymentMethodType = (paymentMethod, paymentMethodType) => switch paymentMethod { | "bank_debit" => paymentMethodType->String.replace("_debit", "") diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index ef3e67fbd..4a95def13 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -8,12 +8,14 @@ type url = {searchParams: searchParams, href: string} @new external urlSearch: string => url = "URL" open LoggerUtils -type payment = Card | BankTransfer | BankDebits | KlarnaRedirect | Gpay | Applepay | Paypal | Other +type payment = + Card | BankTransfer | BankDebits | KlarnaRedirect | Gpay | Applepay | Paypal | Paze | Other let getPaymentType = paymentMethodType => switch paymentMethodType { | "apple_pay" => Applepay | "google_pay" => Gpay + | "paze" => Paze | "debit" | "credit" | "" => diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index f7a1911bb..ddff1ce67 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -128,6 +128,17 @@ let make = ( } } + let onPazeCallback = mountedIframeRef => { + (ev: Types.event) => { + let json = ev.data->Identity.anyTypeToJson + let dict = json->getDictFromJson + let isPazeExist = dict->getBool("isPaze", false) + if isPazeExist { + mountedIframeRef->Window.iframePostMessage([("data", json)]->Dict.fromArray) + } + } + } + let fetchPaymentsList = (mountedIframeRef, componentType) => { let handlePaymentMethodsLoaded = (event: Types.event) => { let json = event.data->Identity.anyTypeToJson @@ -137,6 +148,7 @@ let make = ( isTaxCalculationEnabled.contents = dict->getDictFromDict("response")->getBool("is_tax_calculation_enabled", false) addSmartEventListener("message", onPlaidCallback(mountedIframeRef), "onPlaidCallback") + addSmartEventListener("message", onPazeCallback(mountedIframeRef), "onPazeCallback") let json = dict->getJsonFromDict("response", JSON.Encode.null) let isApplePayPresent = PaymentMethodsRecord.getPaymentMethodTypeFromList(