From f4fc04d3e1d93b9c13260d61279f6af57619f393 Mon Sep 17 00:00:00 2001
From: Vrishab Srivatsa <136090360+vsrivatsa-juspay@users.noreply.github.com>
Date: Tue, 5 Mar 2024 11:28:28 +0530
Subject: [PATCH] feat(paymentmethods): boleto Payment Method Integration
(#195)
Co-authored-by: ArushKapoorJuspay <121166031+ArushKapoorJuspay@users.noreply.github.com>
---
public/icons/orca.svg | 3 +
src/App.res | 12 ++-
src/Components/InfoElement.res | 3 +-
src/LocaleString.res | 11 ++-
src/PaymentDetails.res | 5 ++
src/PaymentElement.res | 4 +
src/Payments/Boleto.res | 115 ++++++++++++++++++++++++++
src/Payments/Boleto.resi | 5 ++
src/Payments/BoletoLazy.res | 8 ++
src/Payments/PaymentMethodsRecord.res | 10 ++-
src/Payments/VoucherDisplay.res | 109 ++++++++++++++++++++++++
src/Types/PaymentConfirmTypes.res | 21 +++++
src/Types/PaymentModeType.res | 3 +
src/Utilities/PaymentBody.res | 25 ++++++
src/Utilities/PaymentHelpers.res | 34 ++++++++
src/Utilities/PaymentUtils.res | 13 ---
src/Window.res | 1 +
src/orca-log-catcher/OrcaLogger.res | 2 +
18 files changed, 364 insertions(+), 20 deletions(-)
create mode 100644 src/Payments/Boleto.res
create mode 100644 src/Payments/Boleto.resi
create mode 100644 src/Payments/BoletoLazy.res
create mode 100644 src/Payments/VoucherDisplay.res
diff --git a/public/icons/orca.svg b/public/icons/orca.svg
index 6016bc4b1..7e48c5687 100644
--- a/public/icons/orca.svg
+++ b/public/icons/orca.svg
@@ -1209,4 +1209,7 @@ License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL
+
+
+
\ No newline at end of file
diff --git a/src/App.res b/src/App.res
index 9d486cdf3..998045ae1 100644
--- a/src/App.res
+++ b/src/App.res
@@ -12,16 +12,20 @@ let make = () => {
log
})
- React.useEffect1(()=>{
- setLoggerState(._=>logger)
+ React.useEffect1(() => {
+ setLoggerState(._ => logger)
None
- },[logger])
+ }, [logger])
let renderFullscreen = {
switch fullscreenMode {
| "paymentloader" =>
- | "fullscreen" =>
+ | "fullscreen" =>
+
+
+
| "qrData" =>
+ | "voucherData" =>
| "preMountLoader" => {
let clientSecret = CardUtils.getQueryParamsDictforKey(url.search, "clientSecret")
let sessionId = CardUtils.getQueryParamsDictforKey(url.search, "sessionId")
diff --git a/src/Components/InfoElement.res b/src/Components/InfoElement.res
index dab361cec..68968e84d 100644
--- a/src/Components/InfoElement.res
+++ b/src/Components/InfoElement.res
@@ -20,7 +20,8 @@ let make = () => {
{switch selectedOption {
| ACHTransfer
| BacsTransfer
- | SepaTransfer =>
+ | SepaTransfer
+ | Boleto =>
localeString.bankDetailsText
| _ => localeString.redirectText
}->React.string}
diff --git a/src/LocaleString.res b/src/LocaleString.res
index c90354388..2fb5c49cb 100644
--- a/src/LocaleString.res
+++ b/src/LocaleString.res
@@ -61,6 +61,7 @@ type localeStrings = {
nameEmptyText: string => string,
completeNameEmptyText: string => string,
billingDetailsText: string,
+ socialSecurityNumberLabel: string,
}
let defaultLocale = {
@@ -137,6 +138,7 @@ let defaultLocale = {
nameEmptyText: str => `Please provide your ${str}`,
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
+ socialSecurityNumberLabel: "Social Security Number",
}
type locale = {localeStrings: array}
@@ -215,6 +217,7 @@ let localeStrings = [
nameEmptyText: str => `Please provide your ${str}`,
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
+ socialSecurityNumberLabel: "Social Security Number",
},
{
locale: "he",
@@ -290,6 +293,7 @@ let localeStrings = [
nameEmptyText: str => `אנא ספק את שלך ${str}`,
completeNameEmptyText: str => `אנא ספק את המלא שלך ${str}`,
billingDetailsText: `פרטי תשלום`,
+ socialSecurityNumberLabel: `מספר ביטוח לאומי`,
},
{
locale: `fr`,
@@ -364,7 +368,8 @@ let localeStrings = [
\"and": `et`,
nameEmptyText: str => `Veuillez fournir votre ${str}`,
completeNameEmptyText: str => `Veuillez fournir votre complet ${str}`,
- billingDetailsText: "Détails de la facturation",
+ billingDetailsText: `Détails de la facturation`,
+ socialSecurityNumberLabel: `Numéro de sécurité sociale`,
},
{
locale: "en-GB",
@@ -440,6 +445,7 @@ let localeStrings = [
nameEmptyText: str => `Please provide your ${str}`,
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
+ socialSecurityNumberLabel: "Social Security Number",
},
{
locale: "ar",
@@ -515,6 +521,7 @@ let localeStrings = [
nameEmptyText: str => `يرجى تقديم الخاص بك ${str}`,
completeNameEmptyText: str => `يرجى تقديم كامل الخاص بك ${str}`,
billingDetailsText: `تفاصيل الفاتورة`,
+ socialSecurityNumberLabel: `رقم الضمان الاجتماعي`,
},
{
locale: "ja",
@@ -590,6 +597,7 @@ let localeStrings = [
nameEmptyText: str => `あなたの情報を提供してください ${str}`,
completeNameEmptyText: str => `完全な情報を提供してください ${str}`,
billingDetailsText: `支払明細`,
+ socialSecurityNumberLabel: `社会保障番号`,
},
{
locale: "de",
@@ -665,5 +673,6 @@ let localeStrings = [
nameEmptyText: str => `Bitte geben Sie Ihre an ${str}`,
completeNameEmptyText: str => `Bitte geben Sie Ihr vollständiges Formular an ${str}`,
billingDetailsText: `Rechnungsdetails`,
+ socialSecurityNumberLabel: `Sozialversicherungsnummer`,
},
]
diff --git a/src/PaymentDetails.res b/src/PaymentDetails.res
index f76f7c140..a0534d357 100644
--- a/src/PaymentDetails.res
+++ b/src/PaymentDetails.res
@@ -97,4 +97,9 @@ let details = [
icon: Some(icon("bank", ~size=21)),
displayName: "BECS Debit",
},
+ {
+ type_: "boleto",
+ icon: Some(icon("boleto", ~size=19)),
+ displayName: "Boleto",
+ },
]
diff --git a/src/PaymentElement.res b/src/PaymentElement.res
index f7fb9960f..9c7b049bb 100644
--- a/src/PaymentElement.res
+++ b/src/PaymentElement.res
@@ -285,6 +285,10 @@ let make = (
| _ => React.null
}
+ | Boleto =>
+
+
+
| _ =>
diff --git a/src/Payments/Boleto.res b/src/Payments/Boleto.res
new file mode 100644
index 000000000..c86b551f6
--- /dev/null
+++ b/src/Payments/Boleto.res
@@ -0,0 +1,115 @@
+open RecoilAtoms
+open Utils
+
+let cleanSocialSecurityNumber = socialSecurityNumber =>
+ socialSecurityNumber->Js.String2.replaceByRe(%re("/\D+/g"), "")
+
+let formatSocialSecurityNumber = socialSecurityNumber => {
+ let formatted = socialSecurityNumber->cleanSocialSecurityNumber
+ let firstPart = formatted->CardUtils.slice(0, 3)
+ let secondPart = formatted->CardUtils.slice(3, 6)
+ let thirdPart = formatted->CardUtils.slice(6, 9)
+ let fourthPart = formatted->CardUtils.slice(9, 11)
+
+ if formatted->Js.String2.length <= 3 {
+ firstPart
+ } else if formatted->Js.String2.length > 3 && formatted->Js.String2.length <= 6 {
+ `${firstPart}.${secondPart}`
+ } else if formatted->Js.String2.length > 6 && formatted->Js.String2.length <= 9 {
+ `${firstPart}.${secondPart}.${thirdPart}`
+ } else {
+ `${firstPart}.${secondPart}.${thirdPart}-${fourthPart}`
+ }
+}
+
+@react.component
+let make = (~paymentType: CardThemeType.mode, ~list: PaymentMethodsRecord.list) => {
+ let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
+
+ let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom)
+ let {iframeId} = Recoil.useRecoilValueFromAtom(keys)
+
+ let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Other)
+ let setComplete = Recoil.useSetRecoilState(fieldsComplete)
+ let (socialSecurityNumber, setSocialSecurityNumber) = React.useState(_ => "")
+
+ let (socialSecurityNumberError, setSocialSecurityNumberError) = React.useState(_ => "")
+
+ let socialSecurityNumberRef = React.useRef(Js.Nullable.null)
+
+ let (complete, empty) = React.useMemo1(() => {
+ (
+ socialSecurityNumber->cleanSocialSecurityNumber->Js.String2.length == 11,
+ socialSecurityNumber->Js.String2.length == 0,
+ )
+ }, [socialSecurityNumber])
+
+ React.useEffect2(() => {
+ handlePostMessageEvents(~complete, ~empty, ~paymentType="boleto", ~loggerState)
+ None
+ }, (complete, empty))
+
+ React.useEffect1(() => {
+ setComplete(._ => complete)
+ None
+ }, [complete])
+
+ let submitCallback = React.useCallback1((ev: Window.event) => {
+ let json = ev.data->Js.Json.parseExn
+ let confirm = json->Utils.getDictFromJson->ConfirmType.itemToObjMapper
+
+ if confirm.doSubmit {
+ if complete {
+ let body = PaymentBody.boletoBody(
+ ~socialSecurityNumber=socialSecurityNumber->Js.String2.replaceByRe(%re("/\D+/g"), ""),
+ )
+ intent(
+ ~bodyArr=body,
+ ~confirmParam=confirm.confirmParams,
+ ~handleUserError=false,
+ ~iframeId,
+ (),
+ )
+ ()
+ } else {
+ postFailedSubmitResponse(~errortype="validation_error", ~message="Please enter all fields")
+ }
+ }
+ }, [socialSecurityNumber])
+ submitPaymentData(submitCallback)
+
+ let changeSocialSecurityNumber = ev => {
+ let val = ReactEvent.Form.target(ev)["value"]
+ setSocialSecurityNumberError(_ => "")
+ setSocialSecurityNumber(_ => val->formatSocialSecurityNumber)
+ }
+ let socialSecurityNumberBlur = ev => {
+ let val = ReactEvent.Focus.target(ev)["value"]->cleanSocialSecurityNumber
+ if val->Js.String2.length != 11 && val->Js.String2.length > 0 {
+ setSocialSecurityNumberError(_ => "The social security number entered is invalid.")
+ }
+ }
+
+
+}
+
+let default = make
diff --git a/src/Payments/Boleto.resi b/src/Payments/Boleto.resi
new file mode 100644
index 000000000..f7493b29b
--- /dev/null
+++ b/src/Payments/Boleto.resi
@@ -0,0 +1,5 @@
+let cleanSocialSecurityNumber: Js.String2.t => Js.String2.t
+let formatSocialSecurityNumber: Js.String2.t => Js.String2.t
+@react.component
+let make: (~paymentType: CardThemeType.mode, ~list: PaymentMethodsRecord.list) => React.element
+let default: props => React.element
diff --git a/src/Payments/BoletoLazy.res b/src/Payments/BoletoLazy.res
new file mode 100644
index 000000000..e6ef10a21
--- /dev/null
+++ b/src/Payments/BoletoLazy.res
@@ -0,0 +1,8 @@
+open LazyUtils
+
+type props = {
+ paymentType: CardThemeType.mode,
+ list: PaymentMethodsRecord.list,
+}
+
+let make: props => React.element = reactLazy(.() => import_("./Boleto.bs.js"))
diff --git a/src/Payments/PaymentMethodsRecord.res b/src/Payments/PaymentMethodsRecord.res
index 6d6104f40..e2a26faaa 100644
--- a/src/Payments/PaymentMethodsRecord.res
+++ b/src/Payments/PaymentMethodsRecord.res
@@ -485,6 +485,13 @@ let paymentMethodsFields = [
displayName: "Pix",
miniIcon: None,
},
+ {
+ paymentMethodName: "boleto",
+ icon: Some(icon("boleto", ~size=19, ~width=25)),
+ displayName: "Boleto",
+ fields: [InfoElement],
+ miniIcon: None,
+ },
]
type required_fields = {
@@ -651,7 +658,7 @@ let getPaymentDetails = (arr: array) => {
}
type paymentMethod =
- Cards | Wallets | PayLater | BankRedirect | BankTransfer | BankDebit | Crypto | NONE
+ Cards | Wallets | PayLater | BankRedirect | BankTransfer | BankDebit | Crypto | Voucher | NONE
type cardType = Credit | Debit
type paymentMethodType =
@@ -737,6 +744,7 @@ let getMethod = str => {
| "bank_transfer" => BankTransfer
| "bank_debit" => BankDebit
| "crypto" => Crypto
+ | "voucher" => Voucher
| _ => NONE
}
}
diff --git a/src/Payments/VoucherDisplay.res b/src/Payments/VoucherDisplay.res
new file mode 100644
index 000000000..2a3d6b43e
--- /dev/null
+++ b/src/Payments/VoucherDisplay.res
@@ -0,0 +1,109 @@
+open Utils
+@react.component
+let make = () => {
+ let (openModal, setOpenModal) = React.useState(_ => false)
+ let (returnUrl, setReturnUrl) = React.useState(_ => "")
+ let (downloadUrl, setDownloadUrl) = React.useState(_ => "")
+ let (reference, setReference) = React.useState(_ => "")
+ let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
+ let (downloadCounter, setDownloadCounter) = React.useState(_ => 0)
+ let (paymentMethod, setPaymentMethod) = React.useState(_ => "")
+ let (paymentIntent, setPaymentIntent) = React.useState(_ => Js.Json.null)
+ let (loader, setLoader) = React.useState(_ => true)
+ let linkRef = React.useRef(Js.Nullable.null)
+
+ React.useEffect1(() => {
+ switch linkRef.current->Js.Nullable.toOption {
+ | Some(link) => link->Window.click
+ | None => ()
+ }
+ None
+ }, [loader])
+
+ React.useEffect0(() => {
+ handlePostMessage([("iframeMountedCallback", true->Js.Json.boolean)])
+ let handle = (ev: Window.event) => {
+ let json = ev.data->Js.Json.parseExn
+ let dict = json->Utils.getDictFromJson
+ if dict->Js.Dict.get("fullScreenIframeMounted")->Belt.Option.isSome {
+ let metadata = dict->getJsonObjectFromDict("metadata")
+ let metaDataDict =
+ metadata->Js.Json.decodeObject->Belt.Option.getWithDefault(Js.Dict.empty())
+ setReturnUrl(_ => metaDataDict->getString("returnUrl", ""))
+ setDownloadUrl(_ => metaDataDict->getString("voucherUrl", ""))
+ setReference(_ => metaDataDict->getString("reference", ""))
+ setPaymentMethod(_ => metaDataDict->getString("paymentMethod", ""))
+ setPaymentIntent(_ => metaDataDict->getJsonObjectFromDict("payment_intent_data"))
+ setLoader(_ => false)
+ }
+ }
+ Window.addEventListener("message", handle)
+ Some(() => {Window.removeEventListener("message", handle)})
+ })
+
+ let closeModal = () => {
+ postSubmitResponse(~jsonData=paymentIntent, ~url=returnUrl)
+ Modal.close(setOpenModal)
+ }
+
+
+
+
+
+
+
+ {React.string("Bar Code Reference: ")}
+
+ {React.string(reference)}
+
+
+
+
+ {React.string(
+ "Please do not close until you have successfully downloaded the voucher, after which you will be automatically redirected.",
+ )}
+
+
+
+
+
+
+
+
+
+}
diff --git a/src/Types/PaymentConfirmTypes.res b/src/Types/PaymentConfirmTypes.res
index 8be8a2015..e1c7831ef 100644
--- a/src/Types/PaymentConfirmTypes.res
+++ b/src/Types/PaymentConfirmTypes.res
@@ -28,12 +28,18 @@ type redirectToUrl = {
url: string,
}
+type voucherDetails = {
+ download_url: string,
+ reference: string,
+}
+
type nextAction = {
redirectToUrl: string,
type_: string,
bank_transfer_steps_and_charges_details: option,
session_token: option,
image_data_url: option,
+ voucher_details: option,
display_to_timestamp: option,
}
type intent = {
@@ -57,6 +63,7 @@ let defaultNextAction = {
bank_transfer_steps_and_charges_details: None,
session_token: None,
image_data_url: None,
+ voucher_details: None,
display_to_timestamp: None,
}
let defaultIntent = {
@@ -106,6 +113,14 @@ let getBankTransferDetails = (dict, str) => {
}
})
}
+
+let getVoucherDetails = json => {
+ {
+ download_url: getString(json, "download_url", ""),
+ reference: getString(json, "reference", ""),
+ }
+}
+
let getNextAction = (dict, str) => {
dict
->Js.Dict.get(str)
@@ -131,6 +146,12 @@ let getNextAction = (dict, str) => {
->Belt.Option.flatMap(Js.Json.decodeNumber)
->Belt.Option.getWithDefault(0.0),
),
+ voucher_details: {
+ json
+ ->Js.Dict.get("voucher_details")
+ ->Belt.Option.flatMap(Js.Json.decodeObject)
+ ->Belt.Option.map(json => json->getVoucherDetails)
+ },
}
})
->Belt.Option.getWithDefault(defaultNextAction)
diff --git a/src/Types/PaymentModeType.res b/src/Types/PaymentModeType.res
index 48d0d17d7..75503aaab 100644
--- a/src/Types/PaymentModeType.res
+++ b/src/Types/PaymentModeType.res
@@ -18,6 +18,7 @@ type payment =
| BanContactCard
| GooglePay
| ApplePay
+ | Boleto
| NONE
let paymentMode = str => {
@@ -41,6 +42,7 @@ let paymentMode = str => {
| "bancontact_card" => BanContactCard
| "google_pay" => GooglePay
| "apple_pay" => ApplePay
+ | "boleto" => Boleto
| _ => NONE
}
}
@@ -66,4 +68,5 @@ let defaultOrder = [
"paypal",
"crypto",
"bancontact_card",
+ "boleto",
]
diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res
index 1c2cca1ff..b7a7b70e0 100644
--- a/src/Utilities/PaymentBody.res
+++ b/src/Utilities/PaymentBody.res
@@ -30,6 +30,31 @@ let bancontactBody = () => [
("payment_method_type", "bancontact_card"->Js.Json.string),
]
+let boletoBody = (~socialSecurityNumber) => [
+ ("payment_method", "voucher"->Js.Json.string),
+ ("payment_method_type", "boleto"->Js.Json.string),
+ (
+ "payment_method_data",
+ [
+ (
+ "voucher",
+ [
+ (
+ "boleto",
+ [("social_security_number", socialSecurityNumber->Js.Json.string)]
+ ->Js.Dict.fromArray
+ ->Js.Json.object_,
+ ),
+ ]
+ ->Js.Dict.fromArray
+ ->Js.Json.object_,
+ ),
+ ]
+ ->Js.Dict.fromArray
+ ->Js.Json.object_,
+ ),
+]
+
let savedCardBody = (~paymentToken, ~customerId, ~cvcNumber) => [
("payment_method", "card"->Js.Json.string),
("payment_token", paymentToken->Js.Json.string),
diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res
index 229794393..30bd4b439 100644
--- a/src/Utilities/PaymentHelpers.res
+++ b/src/Utilities/PaymentHelpers.res
@@ -284,6 +284,40 @@ let intentCall = (
("iframeId", iframeId->Js.Json.string),
("metadata", metaData->Js.Json.object_),
])
+ } else if intent.nextAction.type_ === "display_voucher_information" {
+ let voucherData = intent.nextAction.voucher_details->Belt.Option.getWithDefault({
+ download_url: "",
+ reference: "",
+ })
+ let headerObj = Js.Dict.empty()
+ headers->Js.Array2.forEach(
+ entries => {
+ let (x, val) = entries
+ Js.Dict.set(headerObj, x, val->Js.Json.string)
+ },
+ )
+ let metaData =
+ [
+ ("voucherUrl", voucherData.download_url->Js.Json.string),
+ ("reference", voucherData.reference->Js.Json.string),
+ ("returnUrl", url.href->Js.Json.string),
+ ("paymentMethod", paymentMethod->Js.Json.string),
+ ("payment_intent_data", data),
+ ]->Js.Dict.fromArray
+ handleLogging(
+ ~optLogger,
+ ~value="",
+ ~internalMetadata=metaData->Js.Json.object_->Js.Json.stringify,
+ ~eventName=DISPLAY_VOUCHER,
+ ~paymentMethod,
+ (),
+ )
+ handlePostMessage([
+ ("fullscreen", true->Js.Json.boolean),
+ ("param", `voucherData`->Js.Json.string),
+ ("iframeId", iframeId->Js.Json.string),
+ ("metadata", metaData->Js.Json.object_),
+ ])
} else if intent.nextAction.type_ == "third_party_sdk_session_token" {
let session_token = switch intent.nextAction.session_token {
| Some(token) => token->Utils.getDictFromJson
diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res
index f4ef8f1e1..fb0564cdd 100644
--- a/src/Utilities/PaymentUtils.res
+++ b/src/Utilities/PaymentUtils.res
@@ -182,19 +182,6 @@ let getConnectors = (list: PaymentMethodsRecord.list, method: connectorType) =>
| None => ([], [])
}
}
-let getPaymentDetails = (arr: array) => {
- let finalArr = []
- arr
- ->Js.Array2.map(item => {
- let optionalVal = PaymentDetails.details->Js.Array2.find(i => i.type_ == item)
- switch optionalVal {
- | Some(val) => finalArr->Js.Array2.push(val)->ignore
- | None => ()
- }
- })
- ->ignore
- finalArr
-}
let getDisplayNameAndIcon = (
customNames: PaymentType.customMethodNames,
paymentMethodName,
diff --git a/src/Window.res b/src/Window.res
index 808c7a496..4b327b81c 100644
--- a/src/Window.res
+++ b/src/Window.res
@@ -58,6 +58,7 @@ external style: Dom.element => style = "style"
@set external setTransition: (style, string) => unit = "transition"
@set external setHeight: (style, string) => unit = "height"
@send external paymentRequest: (Js.Json.t, Js.Json.t, Js.Json.t) => Js.Json.t = "PaymentRequest"
+@send external click: Dom.element => unit = "click"
let iframePostMessage = (iframeRef: Js.nullable, message) => {
switch iframeRef->Js.Nullable.toOption {
diff --git a/src/orca-log-catcher/OrcaLogger.res b/src/orca-log-catcher/OrcaLogger.res
index 9da71dd2a..c0c9671df 100644
--- a/src/orca-log-catcher/OrcaLogger.res
+++ b/src/orca-log-catcher/OrcaLogger.res
@@ -53,6 +53,7 @@ type eventName =
| REDIRECTING_USER
| DISPLAY_BANK_TRANSFER_INFO_PAGE
| DISPLAY_QR_CODE_INFO_PAGE
+ | DISPLAY_VOUCHER
| PAYMENT_METHODS_RESPONSE
| LOADER_CHANGED
@@ -109,6 +110,7 @@ let eventNameToStrMapper = eventName => {
| REDIRECTING_USER => "REDIRECTING_USER"
| DISPLAY_BANK_TRANSFER_INFO_PAGE => "DISPLAY_BANK_TRANSFER_INFO_PAGE"
| DISPLAY_QR_CODE_INFO_PAGE => "DISPLAY_QR_CODE_INFO_PAGE"
+ | DISPLAY_VOUCHER => "DISPLAY_VOUCHER"
| PAYMENT_METHODS_RESPONSE => "PAYMENT_METHODS_RESPONSE"
| LOADER_CHANGED => "LOADER_CHANGED"
}