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: pm auth connector integration - Plaid #461

Merged
merged 51 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
9c49e02
feat: bank account verification using plaid sdk
Sanskar2001 Jun 26, 2024
c4a8ddb
Merge branch 'main' of github.com:juspay/hyperswitch-web into pm-auth…
PritishBudhiraja Jun 27, 2024
b2a6db0
refactor: refactored script mounting logic
Sanskar2001 Jun 27, 2024
99a4e52
refactor: refactor
Sanskar2001 Jun 27, 2024
29ce30e
refactor: refactor
Sanskar2001 Jun 27, 2024
5544207
chore: added logs
Sanskar2001 Jun 27, 2024
75631c4
chore: reverted server.js changes
Sanskar2001 Jun 27, 2024
9ff9881
refactor: reverted white space changes
Sanskar2001 Jun 27, 2024
44c40e2
Merge branch 'main' into pm-auth-connector-integration
Sanskar2001 Jun 27, 2024
b679125
Merge branch 'pm-auth-connector-integration' of github.com:juspay/hyp…
PritishBudhiraja Jun 27, 2024
05417cb
feat: plaid integration added
PritishBudhiraja Jun 27, 2024
0b0d627
fix: revert formatting changes
PritishBudhiraja Jun 27, 2024
26b96aa
fix: making generic
PritishBudhiraja Jun 27, 2024
1141ecc
feat: saved cards flow integration
PritishBudhiraja Jun 28, 2024
d52b445
feat: bank account verification using plaid sdk
Sanskar2001 Jun 26, 2024
8c07c29
refactor: refactored script mounting logic
Sanskar2001 Jun 27, 2024
8204552
refactor: refactor
Sanskar2001 Jun 27, 2024
6462d12
refactor: refactor
Sanskar2001 Jun 27, 2024
92835cc
chore: added logs
Sanskar2001 Jun 27, 2024
bfd1a6b
chore: reverted server.js changes
Sanskar2001 Jun 27, 2024
8fd37df
refactor: reverted white space changes
Sanskar2001 Jun 27, 2024
6c05e09
feat: plaid integration added
PritishBudhiraja Jun 27, 2024
601771a
fix: revert formatting changes
PritishBudhiraja Jun 27, 2024
315756b
fix: making generic
PritishBudhiraja Jun 27, 2024
c3e107a
feat: saved cards flow integration
PritishBudhiraja Jun 28, 2024
6e1920b
Merge branch 'pm-auth-connector-integration' of github.com:juspay/hyp…
PritishBudhiraja Jul 1, 2024
9a96c29
fix: show saved fields
PritishBudhiraja Jul 1, 2024
cf880ba
Merge branch 'main' into pm-auth-connector-integration
PritishBudhiraja Jul 1, 2024
49b5cef
fix: added payment methods call event in priority events
Jul 1, 2024
30dc8f8
fix: fullframe plaid sdk
PritishBudhiraja Jul 1, 2024
a077507
Merge branch 'pm-auth-connector-integration' of github.com:juspay/hyp…
PritishBudhiraja Jul 1, 2024
80e96f7
fix: endpoint add in payment helpers
PritishBudhiraja Jul 1, 2024
fc0f116
fix: unstable changes
PritishBudhiraja Jul 1, 2024
0bda884
fix: plaid event listeners - stable
Jul 1, 2024
00b47dc
Merge branch 'main' into pm-auth-connector-integration
PritishBudhiraja Jul 1, 2024
fbf2ee3
fix: validations added
PritishBudhiraja Jul 1, 2024
eda140d
fix: loader and edge case handle
PritishBudhiraja Jul 2, 2024
c828ad0
Merge branch 'main' into pm-auth-connector-integration
PritishBudhiraja Jul 2, 2024
90d40e4
fix: added to option atom
PritishBudhiraja Jul 2, 2024
8215da0
Merge branch 'pm-auth-connector-integration' of github.com:juspay/hyp…
PritishBudhiraja Jul 2, 2024
226981b
fix: edge case handle
PritishBudhiraja Jul 2, 2024
59244da
Merge branch 'main' into pm-auth-connector-integration
PritishBudhiraja Jul 2, 2024
48e0d9c
fix: customer payment methods
PritishBudhiraja Jul 3, 2024
e20a514
fix: customer payment methods type fixed
Jul 3, 2024
23a12ca
refactor: customer list response
Jul 3, 2024
97e2732
refactor: customer list response
Jul 3, 2024
46b3bc4
Merge branch 'pm-auth-connector-integration' of github.com:juspay/hyp…
PritishBudhiraja Jul 3, 2024
be62193
fix: loading screen addition
PritishBudhiraja Jul 3, 2024
e972e23
fix: isexit loader added
PritishBudhiraja Jul 3, 2024
abbd838
fix: button disabled
PritishBudhiraja Jul 3, 2024
20fdc99
fix: comments addressed
PritishBudhiraja Jul 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/App.res
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ let make = () => {
| _ =>
switch fullscreenMode {
| "paymentloader" => <PaymentLoader />
| "plaidSDK" => <PlaidSDKIframe />
| "fullscreen" =>
<div id="fullscreen">
<FullScreenDivDriver />
Expand Down
39 changes: 30 additions & 9 deletions src/Components/SavedCardItem.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
module RenderSavedPaymentMethodItem = {
@react.component
let make = (~paymentItem: PaymentType.customerMethods, ~paymentMethodType) => {
switch paymentItem.paymentMethod {
| "card" =>
<div className="flex flex-col items-start">
<div> {React.string(paymentItem.card.nickname)} </div>
<div className={`PickerItemLabel flex flex-row gap-3 items-center`}>
<div className="tracking-widest"> {React.string(`****`)} </div>
<div> {React.string(paymentItem.card.last4Digits)} </div>
</div>
</div>
| "bank_debit" =>
<div className="flex flex-col items-start">
<div>
{React.string(
`${paymentMethodType->String.toUpperCase} ${paymentItem.paymentMethod->Utils.snakeToTitleCase}`,
)}
</div>
<div className={`PickerItemLabel flex flex-row gap-3 items-center`}>
<div className="tracking-widest"> {React.string(`****`)} </div>
<div> {React.string(paymentItem.bank.mask)} </div>
</div>
</div>
| _ => <div> {React.string(paymentMethodType->Utils.snakeToTitleCase)} </div>
}
}
}

@react.component
let make = (
~setPaymentToken,
Expand Down Expand Up @@ -109,15 +138,7 @@ let make = (
<div className={`PickerItemIcon mx-3 flex items-center `}> brandIcon </div>
<div className="flex flex-col">
<div className="flex items-center gap-4">
{isCard
? <div className="flex flex-col items-start">
<div> {React.string(paymentItem.card.nickname)} </div>
<div className={`PickerItemLabel flex flex-row gap-3 items-center`}>
<div className="tracking-widest"> {React.string(`****`)} </div>
<div> {React.string(paymentItem.card.last4Digits)} </div>
</div>
</div>
: <div> {React.string(paymentMethodType->Utils.snakeToTitleCase)} </div>}
<RenderSavedPaymentMethodItem paymentItem={paymentItem} paymentMethodType />
<RenderIf
condition={displayDefaultSavedPaymentIcon &&
paymentItem.defaultPaymentMethodSet}>
Expand Down
1 change: 1 addition & 0 deletions src/Components/SavedMethods.res
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ let make = (
->Array.mapWithIndex((obj, i) => {
let brandIcon = switch obj.paymentMethod {
| "wallet" => getWalletBrandIcon(obj)
| "bank_debit" => <Icon size=brandIconSize name="bank" />
| _ =>
getCardBrandIcon(
switch obj.card.scheme {
Expand Down
1 change: 0 additions & 1 deletion src/LoaderController.res
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ open Utils
let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTimestamp) => {
open RecoilAtoms

Console.log("in LoaderController")
//<...>//
let (configAtom, setConfig) = Recoil.useRecoilState(configAtom)
let (keys, setKeys) = Recoil.useRecoilState(keys)
Expand Down
63 changes: 40 additions & 23 deletions src/Payments/ACHBankDebit.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open PaymentModeType
@react.component
let make = (~paymentType: CardThemeType.mode) => {
let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)
let {displaySavedPaymentMethods} = Recoil.useRecoilValueFromAtom(optionAtom)
let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled)

let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
Expand All @@ -29,6 +30,15 @@ let make = (~paymentType: CardThemeType.mode) => {
let (state, _) = Recoil.useLoggedRecoilState(userAddressState, "state", loggerState)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)

let pmAuthMapper = React.useMemo1(
() =>
PmAuthConnectorUtils.findPmAuthAllPMAuthConnectors(paymentMethodListValue.payment_methods),
[paymentMethodListValue.payment_methods],
)

let isVerifyPMAuthConnectorConfigured =
displaySavedPaymentMethods && pmAuthMapper->Dict.get("ach")->Option.isSome

OutsideClick.useOutsideClick(
~refs=ArrayOfRef([toolTipRef]),
~isActive=openToolTip,
Expand Down Expand Up @@ -94,30 +104,37 @@ let make = (~paymentType: CardThemeType.mode) => {
}, (email, modalData, fullName, isManualRetryEnabled))
useSubmitPaymentData(submitCallback)

<div className="flex flex-col animate-slowShow" style={gridGap: themeObj.spacingGridColumn}>
<FullNamePaymentInput paymentType={paymentType} />
<EmailPaymentInput paymentType />
<div className="flex flex-col">
<AddBankAccount modalData setModalData />
<RenderIf condition={bankError->String.length > 0}>
<div
className="Error pt-1"
style={
color: themeObj.colorDangerText,
fontSize: themeObj.fontSizeSm,
alignSelf: "start",
textAlign: "left",
}>
{React.string(bankError)}
<>
<RenderIf condition={isVerifyPMAuthConnectorConfigured}>
<AddBankDetails paymentMethodType="ach" />
</RenderIf>
<RenderIf condition={!isVerifyPMAuthConnectorConfigured}>
<div className="flex flex-col animate-slowShow" style={gridGap: themeObj.spacingGridColumn}>
<FullNamePaymentInput paymentType={paymentType} />
<EmailPaymentInput paymentType />
<div className="flex flex-col">
<AddBankAccount modalData setModalData />
<RenderIf condition={bankError->String.length > 0}>
<div
className="Error pt-1"
style={
color: themeObj.colorDangerText,
fontSize: themeObj.fontSizeSm,
alignSelf: "start",
textAlign: "left",
}>
{React.string(bankError)}
</div>
</RenderIf>
</div>
</RenderIf>
</div>
<Surcharge paymentMethod="bank_debit" paymentMethodType="ach" />
<Terms mode=ACHBankDebit />
<FullScreenPortal>
<BankDebitModal setModalData />
</FullScreenPortal>
</div>
<Surcharge paymentMethod="bank_debit" paymentMethodType="ach" />
<Terms mode=ACHBankDebit />
<FullScreenPortal>
<BankDebitModal setModalData />
</FullScreenPortal>
</div>
</RenderIf>
</>
}

let default = make
104 changes: 104 additions & 0 deletions src/Payments/AddBankDetails.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module Loader = {
@react.component
let make = () => {
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
<div className="w-full flex items-center justify-center">
<div className="w-8 h-8 animate-spin" style={color: themeObj.colorTextSecondary}>
<Icon size=32 name="loader" />
</div>
</div>
}
}

@react.component
let make = (~paymentMethodType) => {
open Utils
open Promise
let {publishableKey, clientSecret, iframeId} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)
let setOptionValue = Recoil.useSetRecoilState(RecoilAtoms.optionAtom)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)
let setShowFields = Recoil.useSetRecoilState(RecoilAtoms.showCardFieldsAtom)
let (showLoader, setShowLoader) = React.useState(() => false)

let pmAuthConnectorsArr =
PmAuthConnectorUtils.findPmAuthAllPMAuthConnectors(
paymentMethodListValue.payment_methods,
)->PmAuthConnectorUtils.getAllRequiredPmAuthConnectors

React.useEffect0(() => {
let onPlaidCallback = (ev: Window.event) => {
let json = ev.data->JSON.parseExn
let dict = json->Utils.getDictFromJson
if dict->getBool("isPlaid", false) {
let publicToken = dict->getDictFromDict("data")->getString("publicToken", "")
let isExited = dict->getDictFromDict("data")->getBool("isExited", false)
setShowLoader(_ => !isExited)
if publicToken->String.length > 0 {
PaymentHelpers.callAuthExchange(
~publicToken,
~clientSecret,
~paymentMethodType,
~publishableKey,
~setOptionValue,
)
->then(_ => {
handlePostMessage([("fullscreen", false->JSON.Encode.bool)])
setShowFields(_ => false)
JSON.Encode.null->resolve
})
->catch(_ => JSON.Encode.null->resolve)
->ignore
}
}
}

Window.addEventListener("message", onPlaidCallback)
Some(
() => {
Window.removeEventListener("message", ev => onPlaidCallback(ev))
},
)
})

let submitCallback = React.useCallback((ev: Window.event) => {
let json = ev.data->JSON.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
if confirm.doSubmit {
postFailedSubmitResponse(
~errortype="validation_error",
~message="Please add Bank Details and then confirm payment with the added payment methods.",
)
}
}, [])
useSubmitPaymentData(submitCallback)

let onClickHandler = () => {
setShowLoader(_ => true)
PaymentHelpers.callAuthLink(
~publishableKey,
~clientSecret,
~iframeId,
~paymentMethodType,
~pmAuthConnectorsArr,
)->ignore
}

<button
onClick={_ => onClickHandler()}
disabled={showLoader}
style={
width: "100%",
padding: "20px",
cursor: "pointer",
borderRadius: themeObj.borderRadius,
borderColor: themeObj.borderColor,
borderWidth: "2px",
}>
{if showLoader {
<Loader />
} else {
{React.string("Add Bank Details")}
}}
</button>
}
3 changes: 3 additions & 0 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@ type paymentMethodTypes = {
bank_transfers_connectors: array<string>,
required_fields: array<required_fields>,
surcharge_details: option<surchargeDetails>,
pm_auth_connector: option<string>,
}

type methods = {
Expand Down Expand Up @@ -788,6 +789,7 @@ let defaultPaymentMethodType = {
bank_transfers_connectors: [],
required_fields: [],
surcharge_details: None,
pm_auth_connector: None,
}

let defaultList = {
Expand Down Expand Up @@ -935,6 +937,7 @@ let getPaymentMethodTypes = (dict, str) => {
paymentMethodType === "bancontact_card",
),
surcharge_details: jsonDict->getSurchargeDetails,
pm_auth_connector: getOptionString(jsonDict, "pm_auth_connector"),
}
})
}
Expand Down
78 changes: 78 additions & 0 deletions src/Payments/PlaidSDKIframe.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
@react.component
let make = () => {
open Utils

let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
let (linkToken, setLinkToken) = React.useState(_ => "")
let (isReady, setIsReady) = React.useState(_ => false)
let (pmAuthConnectorsArr, setPmAuthConnectorsArr) = React.useState(_ => [])

React.useEffect0(() => {
handlePostMessage([("iframeMountedCallback", true->JSON.Encode.bool)])
let handle = (ev: Window.event) => {
let json = ev.data->JSON.parseExn

let metaData = json->getDictFromJson->getDictFromDict("metadata")
let linkToken = metaData->getString("linkToken", "")
if linkToken->String.length > 0 {
let pmAuthConnectorArray =
metaData
->getArray("pmAuthConnectorArray")
->Array.map(ele => ele->JSON.Decode.string)

setLinkToken(_ => linkToken)
setPmAuthConnectorsArr(_ => pmAuthConnectorArray)
}
}
Window.addEventListener("message", handle)
Some(() => {Window.removeEventListener("message", handle)})
})

React.useEffect(() => {
PmAuthConnectorUtils.mountAllRequriedAuthConnectorScripts(
~pmAuthConnectorsArr,
~onScriptLoaded=authConnector => {
switch authConnector->PmAuthConnectorUtils.pmAuthNameToTypeMapper {
| PLAID => setIsReady(_ => true)
| NONE => ()
}
},
~logger,
)
None
}, [pmAuthConnectorsArr])

React.useEffect(() => {
if isReady && linkToken->String.length > 0 {
let handler = Plaid.create({
token: linkToken,
onSuccess: (publicToken, _) => {
handlePostMessage([
("isPlaid", true->JSON.Encode.bool),
("publicToken", publicToken->JSON.Encode.string),
])
},
onExit: _ => {
handlePostMessage([
("fullscreen", false->JSON.Encode.bool),
("isPlaid", true->JSON.Encode.bool),
("isExited", true->JSON.Encode.bool),
("publicToken", ""->JSON.Encode.string),
])
},
})

handler.open_()
}

None
}, (isReady, linkToken))

<div
className="PlaidIframe h-screen w-screen bg-black/40 backdrop-blur-sm m-auto"
style={
transition: "opacity .35s ease .1s,background-color 600ms linear",
opacity: "100",
}
/>
}
Loading
Loading