Skip to content

Commit

Permalink
feat: paze integration (#738)
Browse files Browse the repository at this point in the history
  • Loading branch information
PritishBudhiraja authored Nov 4, 2024
1 parent 7f92c37 commit 64fab0a
Show file tree
Hide file tree
Showing 12 changed files with 437 additions and 39 deletions.
91 changes: 78 additions & 13 deletions public/icons/orca.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/App.res
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ let make = () => {
switch fullscreenMode {
| "paymentloader" => <PaymentLoader />
| "plaidSDK" => <PlaidSDKIframe />
| "pazeWallet" => <PazeWallet />
| "fullscreen" =>
<div id="fullscreen">
<FullScreenDivDriver />
Expand Down
7 changes: 7 additions & 0 deletions src/Components/Spinner.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@react.component
let make = () => {
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
<div className=" w-8 h-8 animate-spin" style={color: themeObj.colorTextSecondary}>
<Icon size=32 name="loader" />
</div>
}
6 changes: 1 addition & 5 deletions src/PaymentMethodCollectElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,7 @@ let make = (~integrateError, ~logger) => {
<p> {React.string(`${options.currency} ${options.amount}`)} </p>
</div>
<div className="flex self-start h-12 w-auto bg-white rounded-sm">
<img
className="max-h-12 w-auto max-w-21 h-auto w-auto"
src={merchantLogo}
alt="O"
/>
<img className="max-h-12 w-auto max-w-21 h-auto" src={merchantLogo} alt="O" />
</div>
</div>
<div className="lg:mx-5">
Expand Down
17 changes: 15 additions & 2 deletions src/Payments/PaymentRequestButtonElement.res
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -9,6 +9,7 @@ let paymentMode = str => {
| "apple_pay" =>
ApplePayWallet
| "klarna" => KlarnaWallet
| "paze" => PazeWallet
| _ => NONE
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -137,6 +139,17 @@ let make = (~sessions, ~walletOptions, ~paymentType) => {
}}
</SessionPaymentWrapper>

| PazeWallet =>
<SessionPaymentWrapper type_={Wallet}>
{switch pazeTokenObj {
| OtherTokenOptional(optToken) =>
switch optToken {
| Some(token) => <PazeButton token />
| None => React.null
}
| _ => React.null
}}
</SessionPaymentWrapper>
| NONE => React.null
}
| None => React.null
Expand Down
83 changes: 83 additions & 0 deletions src/Payments/PazeButton.res
Original file line number Diff line number Diff line change
@@ -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)))
})

<button
disabled={showLoader}
onClick
className={`w-full flex flex-row justify-center items-center`}
style={
borderRadius: themeObj.buttonBorderRadius,
backgroundColor: "#2B63FF",
height: themeObj.buttonHeight,
cursor: {showLoader ? "not-allowed" : "pointer"},
opacity: {showLoader ? "0.6" : "1"},
width: themeObj.buttonWidth,
border: `${themeObj.buttonBorderWidth} solid ${themeObj.buttonBorderColor}`,
}>
{showLoader ? <Spinner /> : <Icon name="paze" size=55 />}
</button>
}
44 changes: 44 additions & 0 deletions src/Payments/PazeTypes.res
Original file line number Diff line number Diff line change
@@ -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<string>,
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<JSON.t>,
checkout: checkout => promise<JSON.t>,
complete: complete => promise<JSON.t>,
initialize: initialize => promise<JSON.t>,
}
137 changes: 137 additions & 0 deletions src/Payments/PazeWallet.res
Original file line number Diff line number Diff line change
@@ -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)})
})

<div id="paze-button" className="w-full flex flex-row justify-center rounded-md h-auto" />
}
Loading

0 comments on commit 64fab0a

Please sign in to comment.