, ~walletOptions) => {
+ open Utils
+ open RecoilAtoms
+
+ let url = RescriptReactRouter.useUrl()
+ let isSamsungPayReady = Recoil.useRecoilValueFromAtom(isSamsungPayReady)
+ let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom)
+ let options = Recoil.useRecoilValueFromAtom(optionAtom)
+ let isManualRetryEnabled = Recoil.useRecoilValueFromAtom(isManualRetryEnabled)
+ let setIsShowOrPayUsing = Recoil.useSetRecoilState(isShowOrPayUsing)
+ let areOneClickWalletsRendered = Recoil.useSetRecoilState(areOneClickWalletsRendered)
+ let {publishableKey, iframeId} = Recoil.useRecoilValueFromAtom(keys)
+ let status = CommonHooks.useScript("https://img.mpay.samsung.com/gsmpi/sdk/samsungpay_web_sdk.js")
+ let isWallet = walletOptions->Array.includes("samsung_pay")
+ let componentName = CardUtils.getQueryParamsDictforKey(url.search, "componentName")
+ let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Samsungpay)
+
+ let (_, _, _, _, heightType) = options.wallets.style.height
+ let height = switch heightType {
+ | SamsungPay(val) => val
+ | _ => 48
+ }
+
+ let getSamsungPaymentsClient = _ =>
+ SamsungPayType.samsung({
+ environment: "PRODUCTION",
+ })
+
+ let onSamsungPaymentButtonClick = _ => {
+ loggerState.setLogInfo(
+ ~value="SamsungPay Button Clicked",
+ ~eventName=SAMSUNG_PAY,
+ ~paymentMethod="SAMSUNG_PAY",
+ )
+ SamsungPayHelpers.handleSamsungPayClicked(
+ ~sessionObj=sessionObj->Option.getOr(JSON.Encode.null)->getDictFromJson,
+ ~componentName,
+ ~iframeId,
+ ~readOnly=options.readOnly,
+ )
+ }
+
+ let buttonStyle = {
+ "onClick": onSamsungPaymentButtonClick,
+ "buttonStyle": "black",
+ "type": "buy",
+ }->Identity.anyTypeToJson
+
+ let addSamsungPayButton = _ => {
+ let paymentClient = getSamsungPaymentsClient()
+ let button = paymentClient.createButton(buttonStyle)
+ let spayWrapper = GooglePayType.getElementById(GooglePayType.document, "samsungpay-container")
+ spayWrapper.innerHTML = ""
+ spayWrapper.appendChild(button)
+ }
+
+ React.useEffect(() => {
+ if status == "ready" && isSamsungPayReady && isWallet {
+ setIsShowOrPayUsing(_ => true)
+ addSamsungPayButton()
+ }
+ None
+ }, (status, sessionObj, isSamsungPayReady))
+
+ React.useEffect0(() => {
+ let handleSamsung = (ev: Window.event) => {
+ let json = ev.data->safeParse
+ let dict = json->getDictFromJson
+ if dict->Dict.get("samsungPayResponse")->Option.isSome {
+ let metadata = dict->getJsonObjectFromDict("samsungPayResponse")
+ let getBody = SamsungPayHelpers.getSamsungPayBodyFromResponse(~sPayResponse=metadata)
+ let body = PaymentBody.samsungPayBody(
+ ~metadata=getBody.paymentMethodData->Identity.anyTypeToJson,
+ )
+
+ intent(
+ ~bodyArr=body,
+ ~confirmParam={
+ return_url: options.wallets.walletReturnUrl,
+ publishableKey,
+ },
+ ~handleUserError=false,
+ ~manualRetry=isManualRetryEnabled,
+ )
+ }
+ if dict->Dict.get("samsungPayError")->Option.isSome {
+ messageParentWindow([("fullscreen", false->JSON.Encode.bool)])
+ }
+ }
+ Window.addEventListener("message", handleSamsung)
+ Some(() => {Window.removeEventListener("message", handleSamsung)})
+ })
+
+ let isRenderSamsungPayButton = isSamsungPayReady && isWallet
+
+ React.useEffect(() => {
+ areOneClickWalletsRendered(prev => {
+ ...prev,
+ isSamsungPay: isRenderSamsungPayButton,
+ })
+ None
+ }, [isRenderSamsungPayButton])
+
+ Int.toString}px`}
+ id="samsungpay-container"
+ className={`w-full flex flex-row justify-center rounded-md [&>*]:w-full [&>button]:!bg-contain`}
+ />
+}
diff --git a/src/RenderPaymentMethods.res b/src/RenderPaymentMethods.res
index 4efbd2d8..4296496c 100644
--- a/src/RenderPaymentMethods.res
+++ b/src/RenderPaymentMethods.res
@@ -85,6 +85,7 @@ let make = (
| GooglePayElement
| PayPalElement
| ApplePayElement
+ | SamsungPayElement
| KlarnaElement
| PazeElement
| ExpressCheckoutElement
diff --git a/src/Types/CardThemeType.res b/src/Types/CardThemeType.res
index 6a0a5592..2584d8a6 100644
--- a/src/Types/CardThemeType.res
+++ b/src/Types/CardThemeType.res
@@ -14,6 +14,7 @@ type mode =
| GooglePayElement
| PayPalElement
| ApplePayElement
+ | SamsungPayElement
| KlarnaElement
| PazeElement
| ExpressCheckoutElement
@@ -104,6 +105,7 @@ let getPaymentMode = val => {
| "payPal" => PayPalElement
| "applePay" => ApplePayElement
| "paymentMethodCollect" => PaymentMethodCollectElement
+ | "samsungPay" => SamsungPayElement
| "klarna" => KlarnaElement
| "expressCheckout" => ExpressCheckoutElement
| "paze" => PazeElement
diff --git a/src/Types/PaymentModeType.res b/src/Types/PaymentModeType.res
index 074f872f..e8a06fd0 100644
--- a/src/Types/PaymentModeType.res
+++ b/src/Types/PaymentModeType.res
@@ -18,6 +18,7 @@ type payment =
| BanContactCard
| GooglePay
| ApplePay
+ | SamsungPay
| Boleto
| PayPal
| NONE
@@ -43,6 +44,7 @@ let paymentMode = str => {
| "bancontact_card" => BanContactCard
| "google_pay" => GooglePay
| "apple_pay" => ApplePay
+ | "samsung_pay" => SamsungPay
| "boleto" => Boleto
| "paypal" => PayPal
| _ => NONE
@@ -55,6 +57,7 @@ let defaultOrder = [
"google_pay",
"paypal",
"klarna",
+ "samsung_pay",
"affirm",
"afterpay_clearpay",
"ach_transfer",
diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res
index 11d4b36f..346efa08 100644
--- a/src/Types/PaymentType.res
+++ b/src/Types/PaymentType.res
@@ -60,8 +60,10 @@ type terms = {
usBankAccount: showTerms,
}
type buttonHeight = Default | Custom
-type heightType = ApplePay(int) | GooglePay(int) | Paypal(int) | Klarna(int)
+type heightType = ApplePay(int) | GooglePay(int) | Paypal(int) | Klarna(int) | SamsungPay(int)
type googlePayStyleType = Default | Buy | Donate | Checkout | Subscribe | Book | Pay | Order
+type samsungPayStyleType = Buy
+
type paypalStyleType = Paypal | Checkout | Buynow | Pay | Installment
type applePayStyleType =
| Default
@@ -81,12 +83,13 @@ type styleType =
| ApplePay(applePayStyleType)
| GooglePay(googlePayStyleType)
| Paypal(paypalStyleType)
-type styleTypeArray = (styleType, styleType, styleType)
+ | SamsungPay(samsungPayStyleType)
+type styleTypeArray = (styleType, styleType, styleType, styleType)
type theme = Dark | Light | Outline
type style = {
type_: styleTypeArray,
theme: theme,
- height: (heightType, heightType, heightType, heightType),
+ height: (heightType, heightType, heightType, heightType, heightType),
buttonRadius: int,
}
type wallets = {
@@ -96,6 +99,7 @@ type wallets = {
payPal: showType,
klarna: showType,
paze: showType,
+ samsungPay: showType,
style: style,
}
type business = {name: string}
@@ -273,9 +277,9 @@ let defaultFields = {
billingDetails: JSONObject(defaultBilling),
}
let defaultStyle = {
- type_: (ApplePay(Default), GooglePay(Default), Paypal(Paypal)),
+ type_: (ApplePay(Default), GooglePay(Default), Paypal(Paypal), SamsungPay(Buy)),
theme: Light,
- height: (ApplePay(48), GooglePay(48), Paypal(48), Klarna(48)),
+ height: (ApplePay(48), GooglePay(48), Paypal(48), Klarna(48), SamsungPay(48)),
buttonRadius: 2,
}
let defaultWallets = {
@@ -285,6 +289,7 @@ let defaultWallets = {
payPal: Auto,
klarna: Auto,
paze: Auto,
+ samsungPay: Auto,
style: defaultStyle,
}
let defaultBillingAddress = {
@@ -467,6 +472,11 @@ let getGooglePayType = str => {
GooglePay(Default)
}
}
+let getSamsungPayType = str => {
+ switch str {
+ | _ => SamsungPay(Buy)
+ }
+}
let getPayPalType = str => {
switch str {
| "check-out"
@@ -505,7 +515,7 @@ let getTypeArray = (str, logger) => {
if !Array.includes(goodVals, str) {
str->unknownPropValueWarning(goodVals, "options.wallets.style.type", ~logger)
}
- (str->getApplePayType, str->getGooglePayType, str->getPayPalType)
+ (str->getApplePayType, str->getGooglePayType, str->getPayPalType, str->getSamsungPayType)
}
let getShowDetails = (~billingDetails, ~logger) => {
@@ -688,86 +698,72 @@ let getTerms = (dict, str, logger) => {
})
->Option.getOr(defaultTerms)
}
-let getApplePayHeight = (val, logger) => {
- let val: heightType =
- val >= 45
- ? ApplePay(val)
- : {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[h>=45] - ApplePay. Value set to min",
- ~logger,
- )
- ApplePay(48)
- }
- val
-}
-let getGooglePayHeight = (val, logger) => {
- let val: heightType =
- val >= 48
- ? GooglePay(val)
- : {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[h>=48] - GooglePay. Value set to min",
- ~logger,
- )
- GooglePay(48)
- }
- val
-}
-let getPaypalHeight = (val, logger) => {
- let val: heightType =
- val < 25
- ? {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[25-55] - Paypal. Value set to min",
- ~logger,
- )
- Paypal(25)
- }
- : val > 55
- ? {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[25-55] - Paypal. Value set to max",
- ~logger,
- )
- Paypal(55)
- }
- : Paypal(val)
- val
-}
-let getKlarnaHeight = (val, logger) => {
- let val: heightType =
- val < 40
- ? {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[40-60] - Klarna. Value set to min",
- ~logger,
- )
- Klarna(40)
- }
- : val > 60
- ? {
- valueOutRangeWarning(
- val,
- "options.style.height",
- "[40-60] - Paypal. Value set to max",
- ~logger,
- )
- Klarna(60)
- }
- : Klarna(val)
- val
+let getApplePayHeight: (int, 'a) => heightType = (val, logger) => {
+ if val >= 45 {
+ ApplePay(val)
+ } else {
+ valueOutRangeWarning(
+ val,
+ "options.style.height",
+ "[h>=45] - ApplePay. Value set to min",
+ ~logger,
+ )
+ ApplePay(48)
+ }
}
+
+let getGooglePayHeight: (int, 'a) => heightType = (val, logger) => {
+ if val >= 45 {
+ GooglePay(val)
+ } else {
+ valueOutRangeWarning(
+ val,
+ "options.style.height",
+ "[h>=45] - GooglePay. Value set to min",
+ ~logger,
+ )
+ GooglePay(48)
+ }
+}
+
+let getSamsungPayHeight: (int, 'a) => heightType = (val, logger) => {
+ if val >= 45 {
+ SamsungPay(val)
+ } else {
+ valueOutRangeWarning(
+ val,
+ "options.style.height",
+ "[h>=45] - SamsungPay. Value set to min",
+ ~logger,
+ )
+ SamsungPay(48)
+ }
+}
+
+let getPaypalHeight: (int, 'a) => heightType = (val, logger) => {
+ if val < 25 {
+ valueOutRangeWarning(val, "options.style.height", "[25-55] - Paypal. Value set to min", ~logger)
+ Paypal(25)
+ } else if val > 55 {
+ valueOutRangeWarning(val, "options.style.height", "[25-55] - Paypal. Value set to max", ~logger)
+ Paypal(55)
+ } else {
+ Paypal(val)
+ }
+}
+
+let getKlarnaHeight: (int, 'a) => heightType = (val, logger) => {
+ if val < 40 {
+ valueOutRangeWarning(val, "options.style.height", "[40-60] - Klarna. Value set to min", ~logger)
+ Klarna(40)
+ } else if val > 60 {
+ valueOutRangeWarning(val, "options.style.height", "[40-60] - Paypal. Value set to max", ~logger)
+ Klarna(60)
+ } else {
+ Klarna(val)
+ }
+}
+
let getTheme = (str, logger) => {
switch str {
| "outline" => Outline
@@ -784,6 +780,7 @@ let getHeightArray = (val, logger) => {
val->getGooglePayHeight(logger),
val->getPaypalHeight(logger),
val->getKlarnaHeight(logger),
+ val->getSamsungPayHeight(logger),
)
}
let getStyle = (dict, str, logger) => {
@@ -808,7 +805,7 @@ let getWallets = (dict, str, logger) => {
->Option.flatMap(JSON.Decode.object)
->Option.map(json => {
unknownKeysWarning(
- ["applePay", "googlePay", "style", "walletReturnUrl", "payPal", "klarna"],
+ ["applePay", "googlePay", "style", "walletReturnUrl", "payPal", "klarna", "samsungPay"],
json,
"options.wallets",
~logger,
@@ -836,6 +833,10 @@ let getWallets = (dict, str, logger) => {
"options.wallets.paze",
logger,
),
+ samsungPay: getWarningString(json, "samsungPay", "auto", ~logger)->getShowType(
+ "options.wallets.samsungPay",
+ logger,
+ ),
style: getStyle(json, "style", logger),
}
})
diff --git a/src/Types/SamsungPayType.res b/src/Types/SamsungPayType.res
new file mode 100644
index 00000000..78a503db
--- /dev/null
+++ b/src/Types/SamsungPayType.res
@@ -0,0 +1,46 @@
+type client = {
+ isReadyToPay: JSON.t => promise,
+ createButton: JSON.t => Dom.element,
+ loadPaymentSheet: (JSON.t, JSON.t) => promise,
+ notify: JSON.t => unit,
+}
+
+type env = {environment: string}
+
+@new external samsung: env => client = "SamsungPay.PaymentClient"
+
+type merchant = {
+ name: string,
+ url: string,
+ countryCode: string,
+}
+type amount = {
+ option: string,
+ currency: string,
+ total: string,
+}
+type transactionDetail = {
+ orderNumber: string,
+ merchant: merchant,
+ amount: amount,
+}
+type paymentMethods = {
+ version: string,
+ serviceId: string,
+ protocol: string,
+ allowedBrands: array,
+}
+
+type threeDS = {
+ \"type": string,
+ version: string,
+ data: string,
+}
+type paymentMethodData = {
+ method: string,
+ recurring_payment: bool,
+ card_brand: string,
+ card_last4digits: string,
+ @as("3_d_s") threeDS: threeDS,
+}
+type paymentData = {paymentMethodData: paymentMethodData}
diff --git a/src/Types/SessionsType.res b/src/Types/SessionsType.res
index 07d0396c..27673c70 100644
--- a/src/Types/SessionsType.res
+++ b/src/Types/SessionsType.res
@@ -1,8 +1,8 @@
open Utils
-type wallet = Gpay | Paypal | Klarna | ApplePay | Paze | NONE
-
-type tokenCategory = ApplePayObject | GooglePayThirdPartyObject | PazeObject | Others
+type wallet = Gpay | Paypal | Klarna | ApplePay | SamsungPay | Paze | NONE
+type tokenCategory =
+ ApplePayObject | GooglePayThirdPartyObject | SamsungPayObject | PazeObject | Others
type paymentType = Wallet | Others
@@ -30,12 +30,14 @@ type tokenType =
| ApplePayToken(array)
| GooglePayThirdPartyToken(array)
| PazeToken(array)
+ | SamsungPayToken(array)
| OtherToken(array)
type optionalTokenType =
| ApplePayTokenOptional(option)
| GooglePayThirdPartyTokenOptional(option)
| PazeTokenOptional(option)
+ | SamsungPayTokenOptional(option)
| OtherTokenOptional(option)
type sessions = {
@@ -67,6 +69,7 @@ let getWallet = str => {
| "apple_pay" => ApplePay
| "paypal" => Paypal
| "klarna" => Klarna
+ | "samsung_pay" => SamsungPay
| "google_pay" => Gpay
| "paze" => Paze
| _ => NONE
@@ -124,6 +127,11 @@ let itemToObjMapper = (dict, returnType) => {
clientSecret: getString(dict, "client_secret", ""),
sessionsToken: PazeToken(getSessionsTokenJson(dict, "session_token")),
}
+ | SamsungPayObject => {
+ paymentId: getString(dict, "payment_id", ""),
+ clientSecret: getString(dict, "client_secret", ""),
+ sessionsToken: SamsungPayToken(getSessionsTokenJson(dict, "session_token")),
+ }
| Others => {
paymentId: getString(dict, "payment_id", ""),
@@ -149,5 +157,6 @@ let getPaymentSessionObj = (tokenType, val) =>
| GooglePayThirdPartyToken(arr) =>
GooglePayThirdPartyTokenOptional(getWalletFromTokenType(arr, val))
| PazeToken(arr) => PazeTokenOptional(getWalletFromTokenType(arr, val))
+ | SamsungPayToken(arr) => SamsungPayTokenOptional(getWalletFromTokenType(arr, val))
| OtherToken(arr) => OtherTokenOptional(arr->Array.find(item => item.walletName == val))
}
diff --git a/src/Utilities/ApplePayHelpers.res b/src/Utilities/ApplePayHelpers.res
index 3a342cac..cee2cef9 100644
--- a/src/Utilities/ApplePayHelpers.res
+++ b/src/Utilities/ApplePayHelpers.res
@@ -189,10 +189,9 @@ let startApplePaySession = (
let payment = event.payment
payment->callBackFunc
}
- ssn.oncancel = _ev => {
+ ssn.oncancel = _ => {
applePaySessionRef := Nullable.null
- logInfo(Console.log("Apple Pay Payment Cancelled"))
- logger.setLogInfo(
+ logger.setLogError(
~value="Apple Pay Payment Cancelled",
~eventName=APPLE_PAY_FLOW,
~paymentMethod="APPLE_PAY",
@@ -218,6 +217,7 @@ let useHandleApplePayResponse = (
let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom)
let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys)
let paymentMethodListValue = Recoil.useRecoilValueFromAtom(PaymentUtils.paymentMethodListValue)
+ let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom)
let (stateJson, setStatesJson) = React.useState(_ => JSON.Encode.null)
@@ -279,7 +279,13 @@ let useHandleApplePayResponse = (
syncPayment()
}
} catch {
- | _ => logInfo(Console.log("Error in parsing Apple Pay Data"))
+ | err =>
+ logger.setLogError(
+ ~value="Error in parsing Apple Pay Data",
+ ~eventName=APPLE_PAY_FLOW,
+ ~paymentMethod="APPLE_PAY",
+ ~internalMetadata=err->formatException->JSON.stringify,
+ )
}
}
Window.addEventListener("message", handleApplePayMessages)
diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res
index 3774f02a..9b6a9aaa 100644
--- a/src/Utilities/PaymentBody.res
+++ b/src/Utilities/PaymentBody.res
@@ -386,6 +386,18 @@ let paypalSdkBody = (~token, ~connectors) => [
),
]
+let samsungPayBody = (~metadata) => {
+ let paymentCredential = [("payment_credential", metadata)]->Utils.getJsonFromArrayOfJson
+ let spayBody = [("samsung_pay", paymentCredential)]->Utils.getJsonFromArrayOfJson
+ let paymentMethodData = [("wallet", spayBody)]->Utils.getJsonFromArrayOfJson
+
+ [
+ ("payment_method", "wallet"->JSON.Encode.string),
+ ("payment_method_type", "samsung_pay"->JSON.Encode.string),
+ ("payment_method_data", paymentMethodData),
+ ]
+}
+
let gpayBody = (~payObj: GooglePayType.paymentData, ~connectors: array) => {
let gPayBody = [
("payment_method", "wallet"->JSON.Encode.string),
diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res
index 39fb85d2..9340e664 100644
--- a/src/Utilities/PaymentHelpers.res
+++ b/src/Utilities/PaymentHelpers.res
@@ -7,6 +7,7 @@ open URLModule
let getPaymentType = paymentMethodType =>
switch paymentMethodType {
| "apple_pay" => Applepay
+ | "samsung_pay" => Samsungpay
| "google_pay" => Gpay
| "paze" => Paze
| "debit"
@@ -339,6 +340,7 @@ let rec intentCall = (
let url = makeUrl(confirmParam.return_url)
url.searchParams.set("payment_intent_client_secret", clientSecret)
url.searchParams.set("status", "failed")
+ url.searchParams.set("payment_id", clientSecret->getPaymentId)
messageParentWindow([("confirmParams", confirmParam->anyTypeToJson)])
if statusCode->String.charAt(0) !== "2" {
@@ -484,6 +486,7 @@ let rec intentCall = (
let url = makeUrl(confirmParam.return_url)
url.searchParams.set("payment_intent_client_secret", clientSecret)
+ url.searchParams.set("payment_id", clientSecret->getPaymentId)
url.searchParams.set("status", intent.status)
let handleProcessingStatus = (paymentType, sdkHandleOneClickConfirmPayment) => {
@@ -826,6 +829,7 @@ let rec intentCall = (
try {
let url = makeUrl(confirmParam.return_url)
url.searchParams.set("payment_intent_client_secret", clientSecret)
+ url.searchParams.set("payment_id", clientSecret->getPaymentId)
url.searchParams.set("status", "failed")
let exceptionMessage = err->formatException
logApi(
diff --git a/src/Utilities/PaymentHelpersTypes.res b/src/Utilities/PaymentHelpersTypes.res
index 64ff0425..614d8ad3 100644
--- a/src/Utilities/PaymentHelpersTypes.res
+++ b/src/Utilities/PaymentHelpersTypes.res
@@ -1,5 +1,14 @@
type payment =
- Card | BankTransfer | BankDebits | KlarnaRedirect | Gpay | Applepay | Paypal | Paze | Other
+ | Card
+ | BankTransfer
+ | BankDebits
+ | KlarnaRedirect
+ | Gpay
+ | Applepay
+ | Paypal
+ | Samsungpay
+ | Paze
+ | Other
type paymentIntent = (
~handleUserError: bool=?,
diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res
index 4a4c8a92..cb1c91bf 100644
--- a/src/Utilities/PaymentUtils.res
+++ b/src/Utilities/PaymentUtils.res
@@ -28,7 +28,6 @@ let paymentListLookupNew = (
"gcash",
"momo",
"touch_n_go",
- "samsung_pay",
"mifinity",
]
let otherPaymentList = []
@@ -454,6 +453,7 @@ let useGetPaymentMethodList = (~paymentOptions, ~paymentType, ~sessions) => {
areAllGooglePayRequiredFieldsPrefilled,
isApplePayReady,
isGooglePayReady,
+ showCardFormByDefault,
))
}
diff --git a/src/Utilities/RecoilAtoms.res b/src/Utilities/RecoilAtoms.res
index 5281dc20..b8438084 100644
--- a/src/Utilities/RecoilAtoms.res
+++ b/src/Utilities/RecoilAtoms.res
@@ -53,6 +53,7 @@ let userPhoneNumber = Recoil.atom(
let userCardNickName = Recoil.atom("userCardNickName", defaultFieldValues)
let isGooglePayReady = Recoil.atom("isGooglePayReady", false)
let isApplePayReady = Recoil.atom("isApplePayReady", false)
+let isSamsungPayReady = Recoil.atom("isSamsungPayReady", false)
let userCountry = Recoil.atom("userCountry", "")
let userBank = Recoil.atom("userBank", "")
let userAddressline1 = Recoil.atom("userAddressline1", defaultFieldValues)
@@ -84,6 +85,7 @@ type areOneClickWalletsRendered = {
isApplePay: bool,
isPaypal: bool,
isKlarna: bool,
+ isSamsungPay: bool,
}
let defaultAreOneClickWalletsRendered = {
@@ -91,6 +93,7 @@ let defaultAreOneClickWalletsRendered = {
isApplePay: false,
isPaypal: false,
isKlarna: false,
+ isSamsungPay: false,
}
let areOneClickWalletsRendered = Recoil.atom(
diff --git a/src/Utilities/SamsungPayHelpers.res b/src/Utilities/SamsungPayHelpers.res
new file mode 100644
index 00000000..d00e86e7
--- /dev/null
+++ b/src/Utilities/SamsungPayHelpers.res
@@ -0,0 +1,60 @@
+open SamsungPayType
+open Utils
+
+let getTransactionDetail = dict => {
+ let amountDict = dict->getDictFromDict("amount")
+ let merchantDict = dict->getDictFromDict("merchant")
+ {
+ orderNumber: dict->getString("order_number", ""),
+ amount: {
+ option: amountDict->getString("option", ""),
+ currency: amountDict->getString("currency_code", ""),
+ total: amountDict->getString("total", ""),
+ },
+ merchant: {
+ name: merchantDict->getString("name", ""),
+ countryCode: merchantDict->getString("country_code", ""),
+ url: merchantDict->getString("url", ""),
+ },
+ }
+}
+
+let handleSamsungPayClicked = (~sessionObj, ~componentName, ~iframeId, ~readOnly) => {
+ messageParentWindow([
+ ("fullscreen", true->JSON.Encode.bool),
+ ("param", "paymentloader"->JSON.Encode.string),
+ ("iframeId", iframeId->JSON.Encode.string),
+ ("componentName", componentName->JSON.Encode.string),
+ ])
+
+ if !readOnly {
+ messageParentWindow([
+ ("SamsungPayClicked", true->JSON.Encode.bool),
+ ("SPayPaymentDataRequest", getTransactionDetail(sessionObj)->Identity.anyTypeToJson),
+ ])
+ }
+}
+
+let getPaymentMethodData = dict => {
+ let threeDSDict = dict->getDictFromDict("3DS")
+
+ {
+ method: dict->getString("method", ""),
+ recurring_payment: dict->getBool("recurring_payment", false),
+ card_brand: dict->getString("card_brand", ""),
+ card_last4digits: dict->getString("card_last4digits", ""),
+ threeDS: {
+ \"type": threeDSDict->getString("type", ""),
+ version: threeDSDict->getString("version", ""),
+ data: threeDSDict->getString("data", ""),
+ },
+ }
+}
+
+let itemToObjMapper = dict => {
+ paymentMethodData: getPaymentMethodData(dict),
+}
+
+let getSamsungPayBodyFromResponse = (~sPayResponse) => {
+ sPayResponse->getDictFromJson->itemToObjMapper
+}
diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res
index 745478b0..c2c0cd32 100644
--- a/src/Utilities/Utils.res
+++ b/src/Utilities/Utils.res
@@ -764,10 +764,6 @@ let snakeToTitleCase = str => {
->Array.joinWith(" ")
}
-let logInfo = log => {
- Window.isProd ? () : log
-}
-
let formatIBAN = iban => {
let formatted = iban->String.replaceRegExp(%re(`/[^a-zA-Z0-9]/g`), "")
let countryCode = formatted->String.substring(~start=0, ~end=2)->String.toUpperCase
@@ -1285,6 +1281,7 @@ let getWalletPaymentMethod = (wallets, paymentType: CardThemeType.mode) => {
| ApplePayElement => wallets->Array.filter(item => item === "apple_pay")
| KlarnaElement => wallets->Array.filter(item => item === "klarna")
| PazeElement => wallets->Array.filter(item => item === "paze")
+ | SamsungPayElement => wallets->Array.filter(item => item === "samsung_pay")
| _ => wallets
}
}
@@ -1295,6 +1292,7 @@ let expressCheckoutComponents = [
"applePay",
"klarna",
"paze",
+ "samsungPay",
"expressCheckout",
]
@@ -1317,6 +1315,7 @@ let walletElementPaymentType: array = [
GooglePayElement,
PayPalElement,
ApplePayElement,
+ SamsungPayElement,
KlarnaElement,
PazeElement,
ExpressCheckoutElement,
diff --git a/src/hyper-loader/Elements.res b/src/hyper-loader/Elements.res
index 4eda0f49..911290ff 100644
--- a/src/hyper-loader/Elements.res
+++ b/src/hyper-loader/Elements.res
@@ -101,7 +101,7 @@ let make = (
let preMountLoaderMountedPromise = Promise.make((resolve, _reject) => {
let preMountLoaderIframeCallback = (ev: Types.event) => {
- let json = ev.data->Identity.anyTypeToJson
+ let json = ev.data->anyTypeToJson
let dict = json->getDictFromJson
if dict->Dict.get("preMountLoaderIframeMountedCallback")->Option.isSome {
resolve(true->JSON.Encode.bool)
@@ -118,7 +118,7 @@ let make = (
let onPlaidCallback = mountedIframeRef => {
(ev: Types.event) => {
- let json = ev.data->Identity.anyTypeToJson
+ let json = ev.data->anyTypeToJson
let dict = json->getDictFromJson
let isPlaidExist = dict->getBool("isPlaid", false)
if isPlaidExist {
@@ -131,7 +131,7 @@ let make = (
let onPazeCallback = mountedIframeRef => {
(event: Types.event) => {
- let json = event.data->Identity.anyTypeToJson
+ let json = event.data->anyTypeToJson
let dict = json->getDictFromJson
if dict->getBool("isPaze", false) {
let componentName = dict->getString("componentName", "payment")
@@ -143,7 +143,7 @@ let make = (
let fetchPaymentsList = (mountedIframeRef, componentType) => {
let handlePaymentMethodsLoaded = (event: Types.event) => {
- let json = event.data->Identity.anyTypeToJson
+ let json = event.data->anyTypeToJson
let dict = json->getDictFromJson
let isPaymentMethodsData = dict->getString("data", "") === "payment_methods"
if isPaymentMethodsData {
@@ -182,7 +182,11 @@ let make = (
logger.setLogInfo(~value="TrustPay Script Loading", ~eventName=TRUSTPAY_SCRIPT)
trustPayScript->Window.elementSrc(trustPayScriptURL)
trustPayScript->Window.elementOnerror(err => {
- logInfo(Console.log2("ERROR DURING LOADING TRUSTPAY APPLE PAY", err))
+ logger.setLogError(
+ ~value="ERROR DURING LOADING TRUSTPAY APPLE PAY",
+ ~eventName=TRUSTPAY_SCRIPT,
+ ~internalMetadata=err->formatException->JSON.stringify,
+ )
})
trustPayScript->Window.elementOnload(_ => {
logger.setLogInfo(~value="TrustPay Script Loaded", ~eventName=TRUSTPAY_SCRIPT)
@@ -210,7 +214,7 @@ let make = (
) => {
if !disableSavedPaymentMethods {
let handleCustomerPaymentMethodsLoaded = (event: Types.event) => {
- let json = event.data->Identity.anyTypeToJson
+ let json = event.data->anyTypeToJson
let dict = json->getDictFromJson
let isCustomerPaymentMethodsData =
dict->getString("data", "") === "customer_payment_methods"
@@ -293,6 +297,7 @@ let make = (
| "klarna"
| "expressCheckout"
| "paze"
+ | "samsungPay"
| "paymentMethodsManagement"
| "payment" => ()
| str => manageErrorWarning(UNKNOWN_KEY, ~dynamicStr=`${str} type in create`, ~logger)
@@ -539,7 +544,7 @@ let make = (
}
let handleApplePayThirdPartyFlow = (event: Types.event) => {
- let json = event.data->Identity.anyTypeToJson
+ let json = event.data->anyTypeToJson
let dict = json->getDictFromJson
switch dict->Dict.get("applePayButtonClicked") {
| Some(val) =>
@@ -702,7 +707,7 @@ let make = (
}
messageCurrentWindow([
("submitSuccessful", false->JSON.Encode.bool),
- ("error", err->Identity.anyTypeToJson),
+ ("error", err->anyTypeToJson),
("url", url->JSON.Encode.string),
])
}
@@ -727,7 +732,7 @@ let make = (
->then(json => json->handleRetrievePaymentResponse)
->catch(err => {
err->handleErrorResponse
- resolve(err->Identity.anyTypeToJson)
+ resolve(err->anyTypeToJson)
})
->ignore
->resolve
@@ -767,10 +772,10 @@ let make = (
} else {
messageCurrentWindow([
("submitSuccessful", false->JSON.Encode.bool),
- ("error", err->Identity.anyTypeToJson),
+ ("error", err->anyTypeToJson),
("url", redirectUrl),
])
- resolve(err->Identity.anyTypeToJson)
+ resolve(err->anyTypeToJson)
}
})
->finally(_ => messageCurrentWindow([("fullscreen", false->JSON.Encode.bool)]))
@@ -787,7 +792,7 @@ let make = (
let fetchSessionTokens = mountedIframeRef => {
let handleSessionTokensLoaded = (event: Types.event) => {
- let json = event.data->Identity.anyTypeToJson
+ let json = event.data->anyTypeToJson
let dict = json->getDictFromJson
let sessionTokensData = dict->getString("data", "") === "session_tokens"
if sessionTokensData {
@@ -827,17 +832,21 @@ let make = (
->Option.getOr("")
x === "google_pay" || x === "googlepay"
})
+ let samsungPayPresent = sessionsArr->Array.find(item => {
+ let walletName = item->getDictFromJson->getString("wallet_name", "")
+ walletName === "samsung_pay" || walletName === "samsungpay"
+ })
- (json, applePayPresent, googlePayPresent)->resolve
+ (json, applePayPresent, googlePayPresent, samsungPayPresent)->resolve
}
->then(res => {
- let (json, applePayPresent, googlePayPresent) = res
+ let (json, applePayPresent, googlePayPresent, samsungPayPresent) = res
if (
componentType->getIsComponentTypeForPaymentElementCreate &&
applePayPresent->Option.isSome
) {
let handleApplePayMessages = (applePayEvent: Types.event) => {
- let json = applePayEvent.data->Identity.anyTypeToJson
+ let json = applePayEvent.data->anyTypeToJson
let dict = json->getDictFromJson
let componentName = dict->getString("componentName", "payment")
@@ -930,10 +939,10 @@ let make = (
let payRequest = GooglePayType.assign(
Dict.make()->JSON.Encode.object,
- GooglePayType.baseRequest->Identity.anyTypeToJson,
+ GooglePayType.baseRequest->anyTypeToJson,
{
"allowedPaymentMethods": gpayobj.allowed_payment_methods->arrayJsonToCamelCase,
- }->Identity.anyTypeToJson,
+ }->anyTypeToJson,
)
try {
@@ -1034,13 +1043,13 @@ let make = (
"paymentDataCallbacks": {
"onPaymentDataChanged": onPaymentDataChanged,
},
- }->Identity.anyTypeToJson
+ }->anyTypeToJson
} else {
{
"environment": publishableKey->String.startsWith("pk_prd_")
? "PRODUCTION"
: "TEST",
- }->Identity.anyTypeToJson
+ }->anyTypeToJson
}
let gPayClient = GooglePayType.google(gpayClientRequest)
@@ -1054,7 +1063,7 @@ let make = (
})
->catch(err => {
logger.setLogInfo(
- ~value=err->Identity.anyTypeToJson->JSON.stringify,
+ ~value=err->anyTypeToJson->JSON.stringify,
~eventName=GOOGLE_PAY_FLOW,
~paymentMethod="GOOGLE_PAY",
~logType=DEBUG,
@@ -1122,6 +1131,122 @@ let make = (
~logType=INFO,
)
}
+ if (
+ componentType->getIsComponentTypeForPaymentElementCreate &&
+ samsungPayPresent->Option.isSome &&
+ wallets.samsungPay === Auto
+ ) {
+ let dict = json->getDictFromJson
+ let sessionObj = SessionsType.itemToObjMapper(dict, SamsungPayObject)
+ let samsungPayToken = SessionsType.getPaymentSessionObj(
+ sessionObj.sessionsToken,
+ SamsungPay,
+ )
+ let tokenObj = switch samsungPayToken {
+ | SamsungPayTokenOptional(optToken) => optToken
+ | _ => None
+ }
+
+ let sessionObject =
+ tokenObj
+ ->Option.flatMap(JSON.Decode.object)
+ ->Option.getOr(Dict.make())
+
+ let allowedBrands =
+ sessionObject
+ ->getStrArray("allowed_brands")
+ ->Array.map(str => str->String.toLowerCase)
+
+ let payRequest = {
+ "version": sessionObject->getString("version", ""),
+ "allowedBrands": allowedBrands,
+ "protocol": sessionObject->getString("protocol", ""),
+ "serviceId": sessionObject->getString("service_id", ""),
+ }->anyTypeToJson
+
+ try {
+ let samsungPayClient = SamsungPayType.samsung({
+ environment: "PRODUCTION",
+ })
+ samsungPayClient.isReadyToPay(payRequest)
+ ->then(res => {
+ let dict = res->getDictFromJson
+ let isReadyToPay = dict->getBool("result", false)
+ let msg =
+ [("isSamsungPayReady", isReadyToPay->JSON.Encode.bool)]->Dict.fromArray
+ mountedIframeRef->Window.iframePostMessage(msg)
+ resolve()
+ })
+ ->catch(err => {
+ logger.setLogError(
+ ~value=`SAMSUNG PAY not ready ${err->formatException->JSON.stringify}`,
+ ~eventName=SAMSUNG_PAY,
+ ~paymentMethod="SAMSUNG_PAY",
+ ~logType=ERROR,
+ )
+ resolve()
+ })
+ ->ignore
+
+ let handleSamsungPayMessages = (event: Types.event) => {
+ let evJson = event.data->anyTypeToJson
+ let samsungPayClicked =
+ evJson
+ ->getOptionalJsonFromJson("SamsungPayClicked")
+ ->getBoolFromOptionalJson(false)
+
+ let paymentDataRequest =
+ evJson
+ ->getOptionalJsonFromJson("SPayPaymentDataRequest")
+ ->Option.getOr(JSON.Encode.null)
+
+ if samsungPayClicked && paymentDataRequest !== JSON.Encode.null {
+ samsungPayClient.loadPaymentSheet(payRequest, paymentDataRequest)
+ ->then(json => {
+ let msg = [("samsungPayResponse", json->anyTypeToJson)]->Dict.fromArray
+ event.source->Window.sendPostMessage(msg)
+ resolve()
+ })
+ ->catch(err => {
+ logger.setLogError(
+ ~value=`SAMSUNG PAY Initialization fail ${err
+ ->formatException
+ ->JSON.stringify}`,
+ ~eventName=SAMSUNG_PAY,
+ ~paymentMethod="SAMSUNG_PAY",
+ ~logType=ERROR,
+ )
+ event.source->Window.sendPostMessage(
+ [("samsungPayError", err->anyTypeToJson)]->Dict.fromArray,
+ )
+ resolve()
+ })
+ ->ignore
+ }
+ }
+ addSmartEventListener(
+ "message",
+ handleSamsungPayMessages,
+ "onSamsungPayMessages",
+ )
+ } catch {
+ | err =>
+ logger.setLogError(
+ ~value=`SAMSUNG PAY Not Ready - ${err->formatException->JSON.stringify}`,
+ ~eventName=SAMSUNG_PAY,
+ ~paymentMethod="SAMSUNG_PAY",
+ ~logType=ERROR,
+ )
+ Console.log("Error loading Samsung Pay")
+ }
+ } else if wallets.samsungPay === Never {
+ logger.setLogInfo(
+ ~value="SAMSUNG PAY is set as never by merchant",
+ ~eventName=SAMSUNG_PAY,
+ ~paymentMethod="SAMSUNG_PAY",
+ ~logType=INFO,
+ )
+ }
json->resolve
})
diff --git a/src/hyper-loader/Hyper.res b/src/hyper-loader/Hyper.res
index eac314c9..f8750ac6 100644
--- a/src/hyper-loader/Hyper.res
+++ b/src/hyper-loader/Hyper.res
@@ -139,26 +139,25 @@ let make = (publishableKey, options: option, analyticsInfo: optionOption.getOr(JSON.Encode.null)
- ->Utils.getDictFromJson
- ->Utils.getBool("isPreloadEnabled", true)
+ ->getDictFromJson
+ ->getBool("isPreloadEnabled", true)
let shouldUseTopRedirection =
options
->Option.getOr(JSON.Encode.null)
- ->Utils.getDictFromJson
- ->Utils.getBool("shouldUseTopRedirection", false)
+ ->getDictFromJson
+ ->getBool("shouldUseTopRedirection", false)
let analyticsMetadata =
options
->Option.getOr(JSON.Encode.null)
- ->Utils.getDictFromJson
- ->Utils.getDictFromObj("analytics")
- ->Utils.getJsonObjectFromDict("metadata")
+ ->getDictFromJson
+ ->getDictFromObj("analytics")
+ ->getJsonObjectFromDict("metadata")
if isPreloadEnabled {
preloader()
}
let analyticsInfoDict =
analyticsInfo->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
- let sessionID =
- analyticsInfoDict->getString("sessionID", "hyp_" ++ Utils.generateRandomString(8))
+ let sessionID = analyticsInfoDict->getString("sessionID", "hyp_" ++ generateRandomString(8))
let sdkTimestamp = analyticsInfoDict->getString("timeStamp", Date.now()->Float.toString)
let logger = HyperLogger.make(
~sessionId=sessionID,
@@ -266,12 +265,37 @@ let make = (publishableKey, options: option, analyticsInfo: optionWindow.elementSrc(googlePayScriptURL)
googlePayScript->Window.elementOnerror(err => {
- Utils.logInfo(Console.log2("ERROR DURING LOADING GOOGLE PAY SCRIPT", err))
+ logger.setLogError(
+ ~value="ERROR DURING LOADING GOOGLE PAY SCRIPT",
+ ~eventName=GOOGLE_PAY_SCRIPT,
+ ~internalMetadata=err->formatException->JSON.stringify,
+ ~paymentMethod="GOOGLE_PAY",
+ )
})
Window.body->Window.appendChild(googlePayScript)
logger.setLogInfo(~value="GooglePay Script Loaded", ~eventName=GOOGLE_PAY_SCRIPT)
}
+ if (
+ Window.querySelectorAll(`script[src="https://img.mpay.samsung.com/gsmpi/sdk/samsungpay_web_sdk.js"]`)->Array.length === 0
+ ) {
+ let samsungPayScriptUrl = "https://img.mpay.samsung.com/gsmpi/sdk/samsungpay_web_sdk.js"
+ let samsungPayScript = Window.createElement("script")
+ samsungPayScript->Window.elementSrc(samsungPayScriptUrl)
+ samsungPayScript->Window.elementOnerror(err => {
+ logger.setLogError(
+ ~value="ERROR DURING LOADING SAMSUNG PAY SCRIPT",
+ ~eventName=SAMSUNG_PAY_SCRIPT,
+ ~internalMetadata=err->formatException->JSON.stringify,
+ ~paymentMethod="SAMSUNG_PAY",
+ )
+ })
+ Window.body->Window.appendChild(samsungPayScript)
+ samsungPayScript->Window.elementOnload(_ =>
+ logger.setLogInfo(~value="SamsungPay Script Loaded", ~eventName=SAMSUNG_PAY_SCRIPT)
+ )
+ }
+
let iframeRef = ref([])
let clientSecret = ref("")
let ephemeralKey = ref("")
diff --git a/src/hyper-log-catcher/HyperLogger.res b/src/hyper-log-catcher/HyperLogger.res
index 715ed560..a0f5851f 100644
--- a/src/hyper-log-catcher/HyperLogger.res
+++ b/src/hyper-log-catcher/HyperLogger.res
@@ -86,6 +86,9 @@ type eventName =
| EXTERNAL_TAX_CALCULATION
| POST_SESSION_TOKENS_CALL
| POST_SESSION_TOKENS_CALL_INIT
+ | PAZE_SDK_FLOW
+ | SAMSUNG_PAY_SCRIPT
+ | SAMSUNG_PAY
let eventNameToStrMapper = (eventName: eventName) => (eventName :> string)
@@ -104,9 +107,6 @@ let toSnakeCaseWithSeparator = (str, separator) => {
type maskableDetails = Email | CardDetails
type source = Loader | Elements(CardThemeType.mode) | Headless
-let logInfo = log => {
- Window.isProd ? () : log
-}
type logFile = {
timestamp: string,
diff --git a/webpack.common.js b/webpack.common.js
index c3cea6c8..18a9015f 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -50,7 +50,6 @@ const getEnvironmentDomain = (prodDomain, integDomain, defaultDomain) => {
const backendDomain = getEnvironmentDomain("checkout", "dev", "beta");
const confirmDomain = getEnvironmentDomain("api", "integ-api", "sandbox");
-const logDomain = getEnvironmentDomain("api", "integ-api", "sandbox");
const backendEndPoint =
envBackendUrl || `https://${backendDomain}.hyperswitch.io/api`;
@@ -58,8 +57,7 @@ const backendEndPoint =
const confirmEndPoint =
envBackendUrl || `https://${confirmDomain}.hyperswitch.io`;
-const logEndpoint =
- envLoggingUrl || `https://${logDomain}.hyperswitch.io/logs/sdk`;
+const logEndpoint = envLoggingUrl;
const loggingLevel = "DEBUG";
const maxLogsPushedPerEventName = 100;