diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2409ede02..cba71aa67 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+## [0.36.1](https://github.com/juspay/hyperswitch-web/compare/v0.36.0...v0.36.1) (2024-04-04)
+
+
+### Bug Fixes
+
+* added Loader and Error Handling for TrustPay GooglePay ([#268](https://github.com/juspay/hyperswitch-web/issues/268)) ([1f082eb](https://github.com/juspay/hyperswitch-web/commit/1f082ebe2031a10129b24238f1c60fabd6f5e2e0))
+
+# [0.36.0](https://github.com/juspay/hyperswitch-web/compare/v0.35.6...v0.36.0) (2024-04-04)
+
+
+### Features
+
+* **3ds:** three DS SDK - adding logs to track milestone events ([#265](https://github.com/juspay/hyperswitch-web/issues/265)) ([ceab161](https://github.com/juspay/hyperswitch-web/commit/ceab1614e80d8cfb96ac3eea04486ebd509e0770))
+* giropay dynamic fields added ([#267](https://github.com/juspay/hyperswitch-web/issues/267)) ([ad2fa63](https://github.com/juspay/hyperswitch-web/commit/ad2fa630c639c7b246176f1d2683050a58ad3e36))
+
## [0.35.6](https://github.com/juspay/hyperswitch-web/compare/v0.35.5...v0.35.6) (2024-04-02)
diff --git a/package-lock.json b/package-lock.json
index 4eb12e8cd..101fe6213 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "orca-payment-page",
- "version": "0.35.6",
+ "version": "0.36.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index e11908492..de47c185c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "orca-payment-page",
- "version": "0.35.6",
+ "version": "0.36.1",
"main": "index.js",
"private": true,
"dependencies": {
diff --git a/src/Components/DynamicFields.res b/src/Components/DynamicFields.res
index 1784479e3..bef9f90ae 100644
--- a/src/Components/DynamicFields.res
+++ b/src/Components/DynamicFields.res
@@ -318,425 +318,421 @@ let make = (
dynamicFieldsToRenderInsideBilling->Array.length > 0 &&
(dynamicFieldsToRenderInsideBilling->Array.length > 1 || !isOnlyInfoElementPresent)
- {
- fieldsArr->Array.length > 0
- ? <>
- {dynamicFieldsToRenderOutsideBilling
- ->Array.mapWithIndex((item, index) => {
-
Int.toString}`}
- className="flex flex-col w-full place-content-between"
- style={ReactDOMStyle.make(
- ~marginTop=index !== 0 || paymentMethod === "card"
- ? themeObj.spacingGridColumn
- : "",
- ~gridColumnGap=themeObj.spacingGridRow,
- (),
- )}>
- {switch item {
- | CardNumber =>
-
- | CardExpiryMonth
- | CardExpiryYear
- | CardExpiryMonthAndYear =>
-
- | CardCvc =>
-
- | CardExpiryAndCvc =>
-
-
Array.length > 0}>
+ {<>
+ {dynamicFieldsToRenderOutsideBilling
+ ->Array.mapWithIndex((item, index) => {
+ Int.toString}`}
+ className="flex flex-col w-full place-content-between"
+ style={ReactDOMStyle.make(
+ ~marginTop=index !== 0 || paymentMethod === "card" ? themeObj.spacingGridColumn : "",
+ ~gridColumnGap=themeObj.spacingGridRow,
+ (),
+ )}>
+ {switch item {
+ | CardNumber =>
+
+ | CardExpiryMonth
+ | CardExpiryYear
+ | CardExpiryMonthAndYear =>
+
+ | CardCvc =>
+
+ | CardExpiryAndCvc =>
+
+ | Currency(currencyArr) =>
+
+ | FullName =>
+
getCustomFieldName}
+ optionalRequiredFields={Some(requiredFields)}
+ />
+ | Email
+ | InfoElement
+ | Country
+ | Bank
+ | None
+ | BillingName
+ | PhoneNumber
+ | AddressLine1
+ | AddressLine2
+ | AddressCity
+ | StateAndCity
+ | AddressPincode
+ | AddressState
+ | BlikCode
+ | SpecialField(_)
+ | CountryAndPincode(_)
+ | AddressCountry(_) => React.null
+ }}
+
+ })
+ ->React.array}
+
+
+ {React.string(localeString.billingDetailsText)}
+
+ {dynamicFieldsToRenderInsideBilling
+ ->Array.mapWithIndex((item, index) => {
+
Int.toString}`}
+ className="flex flex-col w-full place-content-between">
+ {switch item {
+ | BillingName =>
+ | Email =>
+ | PhoneNumber =>
+ | StateAndCity =>
+
+
{
+ let value = ReactEvent.Form.target(ev)["value"]
+ setCity(prev => {
+ isValid: value !== "" ? Some(true) : Some(false),
+ value,
+ errorString: value !== "" ? "" : prev.errorString,
+ })
+ }}
+ onBlur={ev => {
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setCity(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
+ paymentType
+ type_="text"
+ name="city"
+ inputRef=cityRef
+ placeholder=localeString.cityLabel
+ />
+ {switch stateJson {
+ | Some(options) =>
+ Utils.getStateNames({
+ value: country,
+ isValid: None,
+ errorString: "",
+ })}
+ />
+ | None => React.null
+ }}
+
+ | CountryAndPincode(countryArr) =>
+
+
+
{
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setPostalCode(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
+ onChange=onPostalChange
+ paymentType
+ name="postal"
+ inputRef=postalRef
+ placeholder=localeString.postalCodeLabel
+ />
+
+ | AddressLine1 =>
+
{
+ let value = ReactEvent.Form.target(ev)["value"]
+ setLine1(prev => {
+ isValid: value !== "" ? Some(true) : Some(false),
+ value,
+ errorString: value !== "" ? "" : prev.errorString,
+ })
+ }}
+ onBlur={ev => {
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setLine1(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
paymentType
- type_="tel"
- appearance=config.appearance
- maxLength=7
- inputRef=expiryRef
- placeholder="MM / YY"
+ type_="text"
+ name="line1"
+ inputRef=line1Ref
+ placeholder=localeString.line1Placeholder
+ />
+ | AddressLine2 =>
+ {
+ let value = ReactEvent.Form.target(ev)["value"]
+ setLine2(prev => {
+ isValid: value !== "" ? Some(true) : Some(false),
+ value,
+ errorString: value !== "" ? "" : prev.errorString,
+ })
+ }}
+ onBlur={ev => {
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setLine2(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
+ paymentType
+ type_="text"
+ name="line2"
+ inputRef=line2Ref
+ placeholder=localeString.line2Placeholder
+ />
+ | AddressCity =>
+ {
+ let value = ReactEvent.Form.target(ev)["value"]
+ setCity(prev => {
+ isValid: value !== "" ? Some(true) : Some(false),
+ value,
+ errorString: value !== "" ? "" : prev.errorString,
+ })
+ }}
+ onBlur={ev => {
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setCity(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
+ paymentType
+ type_="text"
+ name="city"
+ inputRef=cityRef
+ placeholder=localeString.cityLabel
/>
-
+ switch stateJson {
+ | Some(options) =>
+ Utils.getStateNames({
+ value: country,
+ isValid: None,
+ errorString: "",
+ })}
+ />
+ | None => React.null
+ }
+ | AddressPincode =>
+ {
+ let value = ReactEvent.Focus.target(ev)["value"]
+ setPostalCode(prev => {
+ ...prev,
+ isValid: Some(value !== ""),
+ })
+ }}
+ onChange=onPostalChange
paymentType
- rightIcon={CardUtils.setRightIconForCvc(
- ~cardEmpty,
- ~cardInvalid,
- ~color=themeObj.colorIconCardCvcError,
- ~cardComplete,
- )}
+ name="postal"
+ inputRef=postalRef
+ placeholder=localeString.postalCodeLabel
+ />
+ | BlikCode =>
+ | Country =>
+
-
- | Currency(currencyArr) =>
-
- | FullName =>
-
getCustomFieldName}
- optionalRequiredFields={Some(requiredFields)}
- />
- | Email
- | InfoElement
- | Country
- | Bank
- | None
- | BillingName
- | PhoneNumber
- | AddressLine1
- | AddressLine2
- | AddressCity
- | StateAndCity
- | AddressPincode
- | AddressState
- | BlikCode
- | SpecialField(_)
- | CountryAndPincode(_)
- | AddressCountry(_) => React.null
- }}
-
- })
- ->React.array}
-
-
- {React.string(localeString.billingDetailsText)}
-
- {dynamicFieldsToRenderInsideBilling
- ->Array.mapWithIndex((item, index) => {
-
Int.toString}`}
- className="flex flex-col w-full place-content-between">
- {switch item {
- | BillingName =>
- | Email =>
- | PhoneNumber =>
- | StateAndCity =>
-
-
{
- let value = ReactEvent.Form.target(ev)["value"]
- setCity(prev => {
- isValid: value !== "" ? Some(true) : Some(false),
- value,
- errorString: value !== "" ? "" : prev.errorString,
- })
- }}
- onBlur={ev => {
- let value = ReactEvent.Focus.target(ev)["value"]
- setCity(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- paymentType
- type_="text"
- name="city"
- inputRef=cityRef
- placeholder=localeString.cityLabel
- />
- {switch stateJson {
- | Some(options) =>
- Utils.getStateNames({
- value: country,
- isValid: None,
- errorString: "",
- })}
- />
- | None => React.null
- }}
-
- | CountryAndPincode(countryArr) =>
-
-
-
{
- let value = ReactEvent.Focus.target(ev)["value"]
- setPostalCode(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- onChange=onPostalChange
- paymentType
- name="postal"
- inputRef=postalRef
- placeholder=localeString.postalCodeLabel
- />
-
- | AddressLine1 =>
-
{
- let value = ReactEvent.Form.target(ev)["value"]
- setLine1(prev => {
- isValid: value !== "" ? Some(true) : Some(false),
- value,
- errorString: value !== "" ? "" : prev.errorString,
- })
- }}
- onBlur={ev => {
- let value = ReactEvent.Focus.target(ev)["value"]
- setLine1(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- paymentType
- type_="text"
- name="line1"
- inputRef=line1Ref
- placeholder=localeString.line1Placeholder
- />
- | AddressLine2 =>
- {
- let value = ReactEvent.Form.target(ev)["value"]
- setLine2(prev => {
- isValid: value !== "" ? Some(true) : Some(false),
- value,
- errorString: value !== "" ? "" : prev.errorString,
- })
- }}
- onBlur={ev => {
- let value = ReactEvent.Focus.target(ev)["value"]
- setLine2(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- paymentType
- type_="text"
- name="line2"
- inputRef=line2Ref
- placeholder=localeString.line2Placeholder
- />
- | AddressCity =>
- {
- let value = ReactEvent.Form.target(ev)["value"]
- setCity(prev => {
- isValid: value !== "" ? Some(true) : Some(false),
- value,
- errorString: value !== "" ? "" : prev.errorString,
- })
- }}
- onBlur={ev => {
- let value = ReactEvent.Focus.target(ev)["value"]
- setCity(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- paymentType
- type_="text"
- name="city"
- inputRef=cityRef
- placeholder=localeString.cityLabel
- />
- | AddressState =>
- switch stateJson {
- | Some(options) =>
- Utils.getStateNames({
- value: country,
- isValid: None,
- errorString: "",
- })}
- />
- | None => React.null
- }
- | AddressPincode =>
- {
- let value = ReactEvent.Focus.target(ev)["value"]
- setPostalCode(prev => {
- ...prev,
- isValid: Some(value !== ""),
- })
- }}
- onChange=onPostalChange
- paymentType
- name="postal"
- inputRef=postalRef
- placeholder=localeString.postalCodeLabel
- />
- | BlikCode =>
- | Country =>
-
- | AddressCountry(countryArr) =>
-
- | Bank =>
-
- | SpecialField(element) => element
- | InfoElement =>
- <>
-
- {if fieldsArr->Array.length > 1 {
- bottomElement
- } else {
-
- }}
- >
- | CardNumber
- | CardExpiryMonth
- | CardExpiryYear
- | CardExpiryMonthAndYear
- | CardCvc
- | CardExpiryAndCvc
- | Currency(_)
- | FullName
- | None => React.null
+ | AddressCountry(countryArr) =>
+
+ | Bank =>
+
+ | SpecialField(element) => element
+ | InfoElement =>
+ <>
+
+ {if fieldsArr->Array.length > 1 {
+ bottomElement
+ } else {
+
}}
-
- })
- ->React.array}
+ >
+ | CardNumber
+ | CardExpiryMonth
+ | CardExpiryYear
+ | CardExpiryMonthAndYear
+ | CardCvc
+ | CardExpiryAndCvc
+ | Currency(_)
+ | FullName
+ | None => React.null
+ }}
-
-
-
- {<>
-
- {if fieldsArr->Array.length > 1 {
- bottomElement
- } else {
-
- }}
- >}
-
-
-
-
- >
- : React.null
- }
+ })
+ ->React.array}
+
+
+
+
+ {<>
+
+ {if fieldsArr->Array.length > 1 {
+ bottomElement
+ } else {
+
+ }}
+ >}
+
+
+
+
+ >}
+
}
diff --git a/src/Payments/GPay.res b/src/Payments/GPay.res
index 03d5cc210..5cc0bbfba 100644
--- a/src/Payments/GPay.res
+++ b/src/Payments/GPay.res
@@ -151,6 +151,11 @@ let make = (
if result {
if isInvokeSDKFlow {
if isDelayedSessionToken {
+ handlePostMessage([
+ ("fullscreen", true->JSON.Encode.bool),
+ ("param", "paymentloader"->JSON.Encode.string),
+ ("iframeId", iframeId->JSON.Encode.string),
+ ])
let bodyDict = PaymentBody.gPayThirdPartySdkBody(~connectors)
processPayment(bodyDict)
} else {
diff --git a/src/Payments/PaymentMethodsRecord.res b/src/Payments/PaymentMethodsRecord.res
index 143e0413e..b77b5ddff 100644
--- a/src/Payments/PaymentMethodsRecord.res
+++ b/src/Payments/PaymentMethodsRecord.res
@@ -223,7 +223,7 @@ let paymentMethodsFields = [
paymentMethodName: "giropay",
icon: Some(icon("giropay", ~size=19, ~width=25)),
displayName: "GiroPay",
- fields: [FullName, InfoElement],
+ fields: [InfoElement],
miniIcon: None,
},
{
@@ -578,6 +578,7 @@ let dynamicFieldsEnabledPaymentMethods = [
"ideal",
"sofort",
"pix_transfer",
+ "giropay",
]
let getIsBillingField = requiredFieldType => {
diff --git a/src/Payments/PaymentMethodsWrapper.res b/src/Payments/PaymentMethodsWrapper.res
index 7ee67cc8c..2c7d1710f 100644
--- a/src/Payments/PaymentMethodsWrapper.res
+++ b/src/Payments/PaymentMethodsWrapper.res
@@ -115,7 +115,7 @@ let make = (
))
useSubmitPaymentData(submitCallback)
Utils.handlePostMessage([("fullscreen", false->JSON.Encode.bool)])
-let retrievePaymentIntent = (clientSecret, headers, ~optLogger, ~switchToCustomPod) => {
+let retrievePaymentIntent = (
+ clientSecret,
+ headers,
+ ~optLogger,
+ ~switchToCustomPod,
+ ~isForceSync=false,
+) => {
open Promise
let paymentIntentID = String.split(clientSecret, "_secret_")->Array.get(0)->Option.getOr("")
let endpoint = ApiEndpoint.getApiEndPoint()
- let uri = `${endpoint}/payments/${paymentIntentID}?client_secret=${clientSecret}`
+ let forceSync = isForceSync ? "&force_sync=true" : ""
+ let uri = `${endpoint}/payments/${paymentIntentID}?client_secret=${clientSecret}${forceSync}`
logApi(
~optLogger,
@@ -209,9 +216,15 @@ let threeDsAuth = (~clientSecret, ~optLogger, ~threeDsMethodComp, ~headers) => {
})
}
-let rec pollRetrievePaymentIntent = (clientSecret, headers, ~optLogger, ~switchToCustomPod) => {
+let rec pollRetrievePaymentIntent = (
+ clientSecret,
+ headers,
+ ~optLogger,
+ ~switchToCustomPod,
+ ~isForceSync=false,
+) => {
open Promise
- retrievePaymentIntent(clientSecret, headers, ~optLogger, ~switchToCustomPod)
+ retrievePaymentIntent(clientSecret, headers, ~optLogger, ~switchToCustomPod, ~isForceSync)
->then(json => {
let dict = json->JSON.Decode.object->Option.getOr(Dict.make())
let status = dict->getString("status", "")
@@ -220,13 +233,19 @@ let rec pollRetrievePaymentIntent = (clientSecret, headers, ~optLogger, ~switchT
resolve(json)
} else {
delay(2000)->then(_val => {
- pollRetrievePaymentIntent(clientSecret, headers, ~optLogger, ~switchToCustomPod)
+ pollRetrievePaymentIntent(
+ clientSecret,
+ headers,
+ ~optLogger,
+ ~switchToCustomPod,
+ ~isForceSync,
+ )
})
}
})
->catch(e => {
Console.log2("Unable to retrieve payment due to following error", e)
- pollRetrievePaymentIntent(clientSecret, headers, ~optLogger, ~switchToCustomPod)
+ pollRetrievePaymentIntent(clientSecret, headers, ~optLogger, ~switchToCustomPod, ~isForceSync)
})
}
diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res
index d0ade9433..b5164d21d 100644
--- a/src/orca-loader/Elements.res
+++ b/src/orca-loader/Elements.res
@@ -305,40 +305,79 @@ let make = (
paymentDataRequest->GooglePayType.jsonToPaymentRequestDataType(
googlePayThirdPartySession,
)
- let secrets = googlePayThirdPartySession->getJsonFromDict("secrets", JSON.Encode.null)
- let payment = secrets->getDictFromJson->getString("payment", "")
+ let headers = [("Content-Type", "application/json"), ("api-key", publishableKey)]
+
+ let connector =
+ googlePayThirdPartySession
+ ->Dict.get("connector")
+ ->Option.getOr(JSON.Encode.null)
+ ->JSON.Decode.string
+ ->Option.getOr("")
try {
- let trustpay = trustPayApi(secrets)
- trustpay.executeGooglePayment(payment, googlePayRequest)
- ->then(res => {
- logger.setLogInfo(
- ~value="TrustPay GooglePay Success Response",
- ~internalMetadata=res->JSON.stringify,
- ~eventName=GOOGLE_PAY_FLOW,
- ~paymentMethod="GOOGLE_PAY",
- (),
- )
- let msg = [("googlePaySyncPayment", true->JSON.Encode.bool)]->Dict.fromArray
- mountedIframeRef->Window.iframePostMessage(msg)
- resolve()
- })
- ->catch(err => {
- let exceptionMessage = err->formatException->JSON.stringify
- logger.setLogInfo(
- ~value=exceptionMessage,
- ~eventName=GOOGLE_PAY_FLOW,
- ~paymentMethod="GOOGLE_PAY",
- ~logType=ERROR,
- ~logCategory=USER_ERROR,
- (),
- )
- let msg = [("googlePaySyncPayment", true->JSON.Encode.bool)]->Dict.fromArray
- mountedIframeRef->Window.iframePostMessage(msg)
- resolve()
- })
- ->ignore
+ switch connector {
+ | "trustpay" => {
+ let secrets =
+ googlePayThirdPartySession->Utils.getJsonFromDict("secrets", JSON.Encode.null)
+
+ let payment = secrets->Utils.getDictFromJson->Utils.getString("payment", "")
+
+ let trustpay = trustPayApi(secrets)
+
+ let polling =
+ Utils.delay(2000)->then(_ =>
+ PaymentHelpers.pollRetrievePaymentIntent(
+ clientSecret,
+ headers,
+ ~optLogger=Some(logger),
+ ~switchToCustomPod,
+ ~isForceSync=true,
+ )
+ )
+ let executeGooglePayment = trustpay.executeGooglePayment(
+ payment,
+ googlePayRequest,
+ )
+ let timeOut = Utils.delay(600000)->then(_ => {
+ let errorMsg =
+ [("error", "Request Timed Out"->JSON.Encode.string)]
+ ->Dict.fromArray
+ ->JSON.Encode.object
+ reject(Exn.anyToExnInternal(errorMsg))
+ })
+
+ Promise.race([polling, executeGooglePayment, timeOut])
+ ->then(res => {
+ logger.setLogInfo(
+ ~value="TrustPay GooglePay Response",
+ ~internalMetadata=res->JSON.stringify,
+ ~eventName=GOOGLE_PAY_FLOW,
+ ~paymentMethod="GOOGLE_PAY",
+ (),
+ )
+ let msg = [("googlePaySyncPayment", true->JSON.Encode.bool)]->Dict.fromArray
+ mountedIframeRef->Window.iframePostMessage(msg)
+ resolve()
+ })
+ ->catch(err => {
+ let exceptionMessage = err->Utils.formatException->JSON.stringify
+ logger.setLogInfo(
+ ~value=exceptionMessage,
+ ~eventName=GOOGLE_PAY_FLOW,
+ ~paymentMethod="GOOGLE_PAY",
+ ~logType=ERROR,
+ ~logCategory=USER_ERROR,
+ (),
+ )
+ let msg = [("googlePaySyncPayment", true->JSON.Encode.bool)]->Dict.fromArray
+ mountedIframeRef->Window.iframePostMessage(msg)
+ resolve()
+ })
+ ->ignore
+ }
+ | _ => ()
+ }
} catch {
| err => {
let exceptionMessage = err->formatException->JSON.stringify