Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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,057 changes: 1,023 additions & 34 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 @@ -82,6 +82,7 @@ let make = () => {
<div id="fullscreen">
<FullScreenDivDriver />
</div>
| "upiData" => <UPIDisplay />
| "qrData" => <QRCodeDisplay />
| "3dsAuth" => <ThreeDSAuth />
| "redsys3ds" => <Redsys3ds />
Expand Down
21 changes: 21 additions & 0 deletions src/Components/ProgressBar.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@react.component
let make = (~width, ~timeRemainingValue) => {
let {themeObj} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom)

<>
<div className="w-40 h-1.5 bg-[#E5E7EB] rounded-full mb-2">
<div
className="h-full bg-[#2563EB] rounded-full transition-all duration-1000 ease-linear"
style={
width: `${width->Float.toString}%`,
}
/>
</div>
<p className="text-sm">
{React.string("Complete the payment within ")}
<span className="font-semibold" style={color: themeObj.colorText}>
{React.string(timeRemainingValue)}
</span>
</p>
</>
}
65 changes: 65 additions & 0 deletions src/Components/UPIAvailableAppsModal.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@react.component
let make = (~availableApps, ~selectedApp: option<UPITypes.appInfo>, ~setSelectedApp) => {
open RecoilAtoms

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)
let logger = Recoil.useRecoilValueFromAtom(loggerAtom)

let handleAppSelection = (app: UPITypes.appInfo) => {
setSelectedApp(_ => Some(app))
logger.setLogInfo(~value=`UPI app selected: ${app.name}`, ~eventName=PAYMENT_METHOD_CHANGED)
}

let renderAppButton = (obj: UPITypes.appInfo, i, ~appArrayLen) => {
let isSelected = switch selectedApp {
| Some(selected) => selected.packageName === obj.packageName
| None => false
}

<button
key={obj.packageName}
type_="button"
onClick={_ => handleAppSelection(obj)}
className={`flex items-center w-full p-3 transition-all `}
style={
borderBottom: i == appArrayLen - 1 ? "" : `1px solid ${themeObj.borderColor}`,
}>
<div className="flex items-center gap-3 w-full">
<input
type_="radio"
name="upi_app"
value={obj.packageName}
checked=isSelected
onChange={_ => handleAppSelection(obj)}
className="w-4 h-4"
/>
<Icon name=obj.name size=40 />
<span className="text-base font-medium" style={color: themeObj.colorText}>
{React.string(obj.name)}
</span>
</div>
</button>
}

<div>
<div className="text-center mb-6">
<h2 className="text-xl font-semibold mb-2" style={color: themeObj.colorText}>
{React.string("Select the UPI App")}
</h2>
</div>
<div className="flex flex-col max-h-64 p-2 overflow-y-auto border rounded-lg">
<RenderIf condition={availableApps->Array.length !== 0}>
{availableApps
->Array.mapWithIndex((obj, i) =>
renderAppButton(obj, i, ~appArrayLen=availableApps->Array.length)
)
->React.array}
</RenderIf>
<RenderIf condition={availableApps->Array.length === 0}>
<div className="text-center text-gray-500 py-6">
{React.string("No UPI apps are available at the moment.")}
</div>
</RenderIf>
Comment on lines +51 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use ternary operator here?

</div>
</div>
}
103 changes: 103 additions & 0 deletions src/Components/UPIButtons.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
module DoneButton = {
@react.component
let make = (~closeModal) => {
open RecoilAtoms

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)

<button
className="w-full p-3 rounded-lg font-medium"
style={
background: themeObj.colorPrimary,
color: themeObj.buttonTextColor,
}
onClick={_ => {
closeModal()->ignore
}}>
{React.string("Done")}
</button>
}
}

module TryAnotherAppButton = {
@react.component
let make = (~setCurrentScreen, ~setSelectedApp) => {
open RecoilAtoms
open UPITypes

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)

let handleBackToAppSelection = () => {
setCurrentScreen(_ => AppSelection)
setSelectedApp(_ => None)
}

<button
className="w-full p-3 rounded-lg font-medium border bg-transparent"
style={
borderColor: themeObj.colorPrimary,
color: themeObj.colorPrimary,
}
onClick={_ => handleBackToAppSelection()}>
{React.string("Try Another App")}
</button>
}
}

module AppSelectionButton = {
@react.component
let make = (
~selectedApp,
~upiUrl,
~setCurrentScreen,
~setTimeRemaining,
~timer,
~defaultTimer,
) => {
open RecoilAtoms
open UPIHelpers
open UPITypes

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)
let logger = Recoil.useRecoilValueFromAtom(loggerAtom)

let handleOpenApp = () => {
switch selectedApp {
| Some(app) => {
let appSpecificUrl = constructAppSpecificUrl(app, upiUrl)
logger.setLogInfo(
~value=`Opening UPI app: ${app.name} with URL: ${appSpecificUrl}`,
~eventName=PAYMENT_ATTEMPT,
)
UPIHelpers.openApp(appSpecificUrl)
setCurrentScreen(_ => VerificationScreen)
if timer > 0.0 {
setTimeRemaining(_ => timer)
} else {
setTimeRemaining(_ => defaultTimer)
}
}
| None => {
logger.setLogInfo(
~value=`Opening UPI with default URL: ${upiUrl}`,
~eventName=PAYMENT_ATTEMPT,
)
UPIHelpers.openApp(upiUrl)

setCurrentScreen(_ => VerificationScreen)
setTimeRemaining(_ => defaultTimer)
}
}
}

<button
className="w-full p-3 rounded-lg font-medium"
style={
background: themeObj.colorPrimary,
color: themeObj.buttonTextColor,
}
Comment on lines 93 to 98
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

onClick={_ => handleOpenApp()}>
{React.string("Proceed to pay")}
</button>
}
}
101 changes: 101 additions & 0 deletions src/Components/UPIQRCode.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module UPIQRCodeInfoElement = {
@react.component
let make = () => {
open RecoilAtoms

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)

<div className="flex flex-row items-start mt-3">
<Icon name="qr_code" size=50 className="shrink-0" />
<div className="flex flex-col ml-3 gap-3">
<span className="text-base font-medium text-start" style={color: themeObj.colorText}>
{React.string("Scan QR from your UPI app")}
</span>
<div
className="w-full text-start opacity-60"
style={
color: themeObj.colorText,
fontSize: themeObj.fontSizeLg,
fontWeight: themeObj.fontWeightLight,
}>
{React.string(
"Use any UPI app on your mobile phone like CRED, Phone Pe, Google Pay, BHIM etc",
)}
</div>
<div className="flex flex-row items-center">
<Icon name="multiple_apps" width=110 size=29 className="shrink-0" />
<div
className="w-full text-start ml-1 opacity-60"
style={
color: themeObj.colorText,
fontSize: themeObj.fontSizeLg,
fontWeight: themeObj.fontWeightLight,
}>
{React.string("& more")}
</div>
</div>
</div>
</div>
}
}

@react.component
let make = (~upiUrl, ~timer, ~timeRemaining, ~defaultTimer) => {
open RecoilAtoms

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)

let timerValueUsed = timer > 0.0 ? timer : defaultTimer

let progressBarWidth = (timerValueUsed -. timeRemaining) /. timerValueUsed *. 100.0
let timeRemainingValue = UPIHelpers.formatTime(timeRemaining)

let addInnerHtml = htmlUi => {
let element = Window.querySelector("#qr-code")
switch element->Nullable.toOption {
| Some(elem) => elem->Window.innerHTML(htmlUi)
| None =>
Console.warn(
"INTEGRATION ERROR: Div does not seem to exist on which payment element is to mount/unmount",
)
}
}

React.useEffect(() => {
switch UPIHelpers.generateQRCode(upiUrl) {
| Some(qrSvg) => addInnerHtml(qrSvg)
| None => {
let fallbackUi = `<div>
<p>
Unable to generate QR code
</p>
</div>`
addInnerHtml(fallbackUi)
}
}

None
}, [upiUrl])

<div
className="rounded-2xl border border-[#E5E7EB] bg-white p-8 text-center shadow-sm max-w-md mx-auto">
<h2 className="text-lg font-semibold mb-1" style={color: themeObj.colorText}>
{React.string("Pay with QR code")}
</h2>
<p className="text-sm mb-6" style={color: themeObj.colorTextSecondary}>
{React.string("Scan & pay with any UPI app")}
</p>
<div className="flex justify-center mb-6">
<div
className="bg-[#FAFAFA] p-4 rounded-2xl shadow-sm border border-[#F1F1F1]" id="qr-code"
/>
</div>
<div className="flex flex-col items-center mb-6">
<ProgressBar width=progressBarWidth timeRemainingValue />
</div>
<div className="border-t border-[#E5E7EB] pt-3 mt-4 flex text-xs text-gray-500">
<Icon name="info_circle" className="shrink-0" />
{React.string("You will be automatically redirected once the payment is done")}
</div>
</div>
}
32 changes: 32 additions & 0 deletions src/Components/UPIWaitModal.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@react.component
let make = (~timeRemaining, ~timer, ~defaultTimer) => {
open RecoilAtoms

let {themeObj} = Recoil.useRecoilValueFromAtom(configAtom)

let timerValueUsed = timer > 0.0 ? timer : defaultTimer

let progressBarWidth = (timerValueUsed -. timeRemaining) /. timerValueUsed *. 100.0
let timeRemainingValue = UPIHelpers.formatTime(timeRemaining)

<div className="w-full flex flex-col items-center justify-center h-full">
<div
className="w-full flex flex-col items-center justify-center h-full"
style={
borderBottom: `1px solid ${themeObj.borderColor}`,
}>
<Icon name="coin" size=80 className="shrink-0" />
<p className="text-lg mb-6 mt-3" style={color: themeObj.colorTextSecondary}>
{React.string("Payable amount request has been sent")}
</p>
<div className="flex flex-col items-center mb-6 mt-5">
<ProgressBar width=progressBarWidth timeRemainingValue />
</div>
</div>
<div className="w-full flex items-center justify-center rounded-lg bg-[#F5F7FA] mt-6 mb-6 py-3">
<p className="text-base text-gray-500 text-center">
{React.string("You will be automatically redirected once the payment is done")}
</p>
</div>
</div>
}
6 changes: 5 additions & 1 deletion src/PaymentElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
None
}, [savedMethods])

let (walletList, paymentOptionsList, actualList) = useGetPaymentMethodList(
let (walletList, paymentOptionsList, actualList, upiMethods) = useGetPaymentMethodList(
~paymentOptions,
~paymentType,
~sessions,
Expand Down Expand Up @@ -344,6 +344,10 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
<ReusableReactSuspense loaderComponent={loader()} componentName="SepaBankDebitLazy">
<SepaBankDebitLazy />
</ReusableReactSuspense>
| Upi =>
<ReusableReactSuspense loaderComponent={loader()} componentName="SepaBankDebitLazy">
<UPI upiMethods />
</ReusableReactSuspense>
| BacsBankDebit =>
<ReusableReactSuspense loaderComponent={loader()} componentName="BacsBankDebitLazy">
<BacsBankDebitLazy />
Expand Down
1 change: 1 addition & 0 deletions src/PaymentElementV2.res
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ let make = (~cardProps, ~expiryProps, ~cvcProps, ~paymentType: CardThemeType.mod
| Boleto
| PayPal
| EFT
| Upi
| Unknown => React.null
}}
</ErrorBoundary>
Expand Down
13 changes: 3 additions & 10 deletions src/Payments/PaymentMethodsRecord.res
Original file line number Diff line number Diff line change
Expand Up @@ -621,17 +621,10 @@ let getPaymentMethodsFields = (~localeString: LocaleStringTypes.localeStrings) =
miniIcon: None,
},
{
paymentMethodName: "upi_collect",
paymentMethodName: "upi",
icon: Some(icon("BHIM", ~size=19)),
displayName: "UPI",
fields: [InfoElement],
icon: Some(icon("bhim_upi", ~size=19)),
displayName: localeString.payment_methods_upi_collect,
miniIcon: None,
},
{
paymentMethodName: "upi_intent",
fields: [InfoElement],
icon: Some(icon("bhim_upi", ~size=19)),
displayName: "UPI Intent",
miniIcon: None,
},
{
Expand Down
Loading
Loading