Skip to content

Commit

Permalink
feat: HS-454: Support for Google pay (Bank of america) (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
prafulkoppalkar authored Dec 6, 2023
1 parent f3ee590 commit 6602c71
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Hyperswitch-React-Demo-App/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NS ?= juspaydotin
VERSION ?= v1.0.2
VERSION ?= v1.0.4
IMAGE_NAME ?= hyperswitch-web
BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
CONTAINER_NAME ?= hyperswitch-web
Expand Down
2 changes: 0 additions & 2 deletions Hyperswitch-React-Demo-App/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ app.get("/create-payment-intent", async (req, res) => {
amount: 2999,
},
],
business_country: "US",
business_label: "default",
currency: "USD",
confirm: false,
capture_method: "automatic",
Expand Down
17 changes: 17 additions & 0 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.
36 changes: 33 additions & 3 deletions src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,15 @@ let make = (
walletOptions->Js.Array2.includes("paypal") || isShowOrPayUsing
}
let dict = sessions->Utils.getDictFromJson
let klarnaSessionObj = SessionsType.itemToObjMapper(dict, Others)
let tokenObj = SessionsType.getPaymentSessionObj(klarnaSessionObj.sessionsToken, Klarna)
let sessionObj = SessionsType.itemToObjMapper(dict, Others)
let klarnaTokenObj = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Klarna)
let gPayToken = SessionsType.getPaymentSessionObj(sessionObj.sessionsToken, Gpay)
let googlePayThirdPartySessionObj = SessionsType.itemToObjMapper(dict, GooglePayThirdPartyObject)
let googlePayThirdPartyToken = SessionsType.getPaymentSessionObj(
googlePayThirdPartySessionObj.sessionsToken,
Gpay,
)

let loader = () => {
Utils.handlePostMessageEvents(
~complete=false,
Expand All @@ -191,7 +198,7 @@ let make = (
| Card => <CardPayment cardProps expiryProps cvcProps paymentType list />
| Klarna =>
<SessionPaymentWrapper type_=Others>
{switch tokenObj {
{switch klarnaTokenObj {
| OtherTokenOptional(optToken) =>
switch optToken {
| Some(token) =>
Expand Down Expand Up @@ -237,6 +244,29 @@ let make = (
<React.Suspense fallback={loader()}>
<BecsBankDebitLazy paymentType list />
</React.Suspense>
| GooglePay =>
switch gPayToken {
| OtherTokenOptional(optToken) =>
switch googlePayThirdPartyToken {
| GooglePayThirdPartyTokenOptional(googlePayThirdPartyOptToken) =>
<React.Suspense fallback={loader()}>
<GPayLazy
paymentType
sessionObj=optToken
list
thirdPartySessionObj=googlePayThirdPartyOptToken
walletOptions
/>
</React.Suspense>
| _ =>
<React.Suspense fallback={loader()}>
<GPayLazy
paymentType sessionObj=optToken list thirdPartySessionObj=None walletOptions
/>
</React.Suspense>
}
| _ => React.null
}
| _ =>
<React.Suspense fallback={loader()}>
<PaymentMethodsWrapperLazy paymentType list paymentMethodName=selectedOption />
Expand Down
78 changes: 66 additions & 12 deletions src/Payments/GPay.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ type props = {
sessionObj: option<SessionsType.token>,
list: PaymentMethodsRecord.list,
thirdPartySessionObj: option<Js.Json.t>,
paymentType: option<CardThemeType.mode>,
walletOptions: array<string>,
}

open GooglePayType

let default = (props: props) => {
let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty())
let (loggerState, _setLoggerState) = Recoil.useRecoilState(loggerAtom)
let {iframeId} = Recoil.useRecoilValueFromAtom(keys)
let {publishableKey} = Recoil.useRecoilValueFromAtom(keys)
let {localeString} = Recoil.useRecoilValueFromAtom(configAtom)
let options = Recoil.useRecoilValueFromAtom(optionAtom)
let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Gpay)
let sync = PaymentHelpers.usePaymentSync(Some(loggerState), Gpay)
let isGPayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady)
let setIsShowOrPayUsing = Recoil.useSetRecoilState(isShowOrPayUsing)
let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid)
let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty)
let status = CommonHooks.useScript("https://pay.google.com/gp/p/js/pay.js")
let isGooglePaySDKFlow = React.useMemo1(() => {
props.sessionObj->Belt.Option.isSome
Expand All @@ -35,6 +41,7 @@ let default = (props: props) => {
| None => PaymentMethodsRecord.defaultPaymentMethodType
}

let isWallet = props.walletOptions->Js.Array2.includes("google_pay")
let paymentExperience =
googlePayPaymentMethodType.payment_experience->Js.Array2.length == 0
? PaymentMethodsRecord.RedirectToURL
Expand Down Expand Up @@ -79,11 +86,21 @@ let default = (props: props) => {
if dict->Js.Dict.get("gpayResponse")->Belt.Option.isSome {
let metadata = dict->getJsonObjectFromDict("gpayResponse")
let obj = metadata->getDictFromJson->itemToObjMapper
let body = PaymentBody.gpayBody(~payObj=obj, ~connectors)
let body = {
PaymentBody.gpayBody(~payObj=obj, ~connectors)
->Js.Dict.fromArray
->Js.Json.object_
->OrcaUtils.flattenObject(true)
->OrcaUtils.mergeTwoFlattenedJsonDicts(requiredFieldsBody)
->OrcaUtils.getArrayOfTupleFromDict
}
processPayment(body)
}
if dict->Js.Dict.get("gpayError")->Belt.Option.isSome {
Utils.handlePostMessage([("fullscreen", false->Js.Json.boolean)])
if !isWallet {
postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong")
}
}
}
Window.addEventListener("message", handle)
Expand Down Expand Up @@ -166,9 +183,10 @@ let default = (props: props) => {
React.useEffect3(() => {
if (
status == "ready" &&
(isGPayReady ||
isDelayedSessionToken ||
paymentExperience == PaymentMethodsRecord.RedirectToURL)
(isGPayReady ||
isDelayedSessionToken ||
paymentExperience == PaymentMethodsRecord.RedirectToURL) &&
isWallet
) {
addGooglePayButton()
}
Expand Down Expand Up @@ -200,15 +218,51 @@ let default = (props: props) => {
})

let isRenderGooglePayButton =
isGPayReady || paymentExperience == PaymentMethodsRecord.RedirectToURL || isDelayedSessionToken
(isGPayReady ||
paymentExperience == PaymentMethodsRecord.RedirectToURL ||
isDelayedSessionToken) && isWallet

setIsShowOrPayUsing(.prev => prev || isRenderGooglePayButton)

<RenderIf condition={isRenderGooglePayButton}>
<div
style={ReactDOMStyle.make(~height=`${height->Belt.Int.toString}px`, ())}
id="google-pay-button"
className={`w-full flex flex-row justify-center rounded-md`}
/>
</RenderIf>
let submitCallback = React.useCallback((ev: Window.event) => {
let json = ev.data->Js.Json.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
if confirm.doSubmit && areRequiredFieldsValid && !areRequiredFieldsEmpty {
handlePostMessage([
("fullscreen", true->Js.Json.boolean),
("param", "paymentloader"->Js.Json.string),
("iframeId", iframeId->Js.Json.string),
])
options.readOnly ? () : handlePostMessage([("GpayClicked", true->Js.Json.boolean)])
} else if areRequiredFieldsEmpty {
postFailedSubmitResponse(~errortype="validation_error", ~message=localeString.enterFieldsText)
} else if !areRequiredFieldsValid {
postFailedSubmitResponse(
~errortype="validation_error",
~message=localeString.enterValidDetailsText,
)
}
})
submitPaymentData(submitCallback)

{
isWallet
? <RenderIf condition={isRenderGooglePayButton}>
<div
style={ReactDOMStyle.make(~height=`${height->Belt.Int.toString}px`, ())}
id="google-pay-button"
className={`w-full flex flex-row justify-center rounded-md`}
/>
</RenderIf>
: <DynamicFields
paymentType={switch props.paymentType {
| Some(val) => val
| _ => NONE
}}
list=props.list
paymentMethod="wallet"
paymentMethodType="google_pay"
setRequiredFieldsBody
/>
}
}
2 changes: 2 additions & 0 deletions src/Payments/GPay.resi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ type props = {
sessionObj: option<SessionsType.token>,
list: PaymentMethodsRecord.list,
thirdPartySessionObj: option<Js.Json.t>,
paymentType: option<CardThemeType.mode>,
walletOptions: array<string>,
}

let default: props => React.element
2 changes: 2 additions & 0 deletions src/Payments/GPayLazy.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ external makeProps: (
~sessionObj: option<SessionsType.token>,
~list: PaymentMethodsRecord.list,
~thirdPartySessionObj: option<Js.Json.t>,
~paymentType: CardThemeType.mode,
~walletOptions: array<string>,
unit,
) => componentProps = ""

Expand Down
15 changes: 14 additions & 1 deletion src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ let paymentMethodsFields = [
displayName: "After Pay",
miniIcon: None,
},
{
paymentMethodName: "google_pay",
fields: [],
icon: Some(icon("google_pay", ~size=19, ~width=25)),
displayName: "Google Pay",
miniIcon: None,
},
{
paymentMethodName: "mb_way",
fields: [SpecialField(<PhoneNumberPaymentInput />), InfoElement],
Expand Down Expand Up @@ -526,7 +533,13 @@ let getRequiredFieldsFromJson = dict => {
}
}

let dynamicFieldsEnabledPaymentMethods = ["crypto_currency", "debit", "credit", "blik"]
let dynamicFieldsEnabledPaymentMethods = [
"crypto_currency",
"debit",
"credit",
"blik",
"google_pay",
]

let getPaymentMethodFields = (
paymentMethod,
Expand Down
15 changes: 13 additions & 2 deletions src/Payments/PaymentRequestButtonElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,20 @@ let make = (~sessions, ~walletOptions, ~list: PaymentMethodsRecord.list) => {
switch googlePayThirdPartyToken {
| GooglePayThirdPartyTokenOptional(googlePayThirdPartyOptToken) =>
<GPayLazy
sessionObj=optToken list thirdPartySessionObj=googlePayThirdPartyOptToken
paymentType=NONE
sessionObj=optToken
list
thirdPartySessionObj=googlePayThirdPartyOptToken
walletOptions
/>
| _ =>
<GPayLazy
paymentType=NONE
sessionObj=optToken
list
thirdPartySessionObj=None
walletOptions
/>
| _ => <GPayLazy sessionObj=optToken list thirdPartySessionObj=None />
}
| _ => React.null
}}
Expand Down
2 changes: 2 additions & 0 deletions src/Types/PaymentModeType.res
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type payment =
| BacsBankDebit
| BecsBankDebit
| BanContactCard
| GooglePay
| NONE

let paymentMode = str => {
Expand All @@ -37,6 +38,7 @@ let paymentMode = str => {
| "sepa_transfer" => SepaTransfer
| "bacs_transfer" => BacsTransfer
| "bancontact_card" => BanContactCard
| "google_pay" => GooglePay
| _ => NONE
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/Utilities/PaymentUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ let paymentListLookupNew = (list: PaymentMethodsRecord.list, ~order) => {
"samsung_pay",
]
let otherPaymentList = []
let googlePayFields = pmList->Js.Array2.find(item => item.paymentMethodName === "google_pay")
switch googlePayFields {
| Some(val) =>
if val.fields->Js.Array2.length > 0 {
walletToBeDisplayedInTabs->Js.Array2.push("google_pay")->ignore
}
| None => ()
}

pmList->Js.Array2.forEach(item => {
if walletToBeDisplayedInTabs->Js.Array2.includes(item.paymentMethodName) {
otherPaymentList->Js.Array2.push(item.paymentMethodName)->ignore
Expand Down

0 comments on commit 6602c71

Please sign in to comment.