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: added samsung pay #112

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion android
Submodule android updated 43 files
+0 −19 .github/workflows/client-core-dispatch.yml
+867 −995 app/src/main/assets/hyperswitch.bundle
+0 −8 app/src/main/java/io/hyperswitch/react/HyperModule.kt
+5 −53 app/src/main/java/io/hyperswitch/view/GooglePayButtonView.kt
+0 −16 app/src/main/res/values/theme.xml
+43 −113 build-lib.sh
+3 −38 demo-app/build.gradle
+12 −5 demo-app/src/main/AndroidManifest.xml
+5 −5 demo-app/src/main/java/io/hyperswitch/demoapp/MainActivity.kt
+71 −61 demo-app/src/main/res/layout/main_activity.xml
+6 −0 demo-app/src/main/res/values/colors.xml
+4 −0 demo-app/src/main/res/values/strings.xml
+4 −12 demo-app/src/main/res/values/styles.xml
+2 −2 gradle.properties
+1 −1 hyperswitch-gradle-plugin/build.gradle
+4 −1 hyperswitch-gradle-plugin/src/main/groovy/io/hyperswitch/HyperPlugin.groovy
+32 −1 hyperswitch-sdk-android-lite/build.gradle
+0 −0 hyperswitch-sdk-android-lite/consumer-rules.pro
+1 −1 hyperswitch-sdk-android-lite/src/main/AndroidManifest.xml
+1 −1 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/PaymentConfiguration.kt
+0 −23 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/lite/WebViewFragment.kt
+2 −2 ...h-sdk-android-lite/src/main/java/io/hyperswitch/payments/expresscheckoutlauncher/ExpressCheckoutLauncher.kt
+2 −2 ...-lite/src/main/java/io/hyperswitch/payments/expresscheckoutlauncher/ExpressCheckoutPaymentMethodLauncher.kt
+11 −8 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/googlepaylauncher/GooglePayActivity.kt
+2 −2 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/googlepaylauncher/GooglePayLauncher.kt
+2 −2 ...-sdk-android-lite/src/main/java/io/hyperswitch/payments/googlepaylauncher/GooglePayPaymentMethodLauncher.kt
+7 −14 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/googlepaylauncher/GooglePayViewModel.kt
+2 −2 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/paymentlauncher/PaymentLauncher.kt
+2 −4 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/paymentlauncher/PaymentResult.kt
+2 −2 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/paypallauncher/PayPalLauncher.kt
+2 −2 ...switch-sdk-android-lite/src/main/java/io/hyperswitch/payments/paypallauncher/PayPalPaymentMethodLauncher.kt
+62 −0 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/payments/view/GooglePayButtonView.kt
+29 −0 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/AddressElementActivityContract.kt
+20 −21 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/AddressLauncher.kt
+2 −2 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/DefaultFlowController.kt
+40 −10 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/DefaultPaymentSheetLauncher.kt
+4 −4 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/FlowControllerFactory.kt
+1 −1 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/PaymentOption.kt
+8 −12 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/PaymentSheet.kt
+9 −8 hyperswitch-sdk-android-lite/src/main/java/io/hyperswitch/paymentsheet/PaymentSheetContract.kt
+6 −3 hyperswitch-sdk-android-lite/src/main/res/values/theme.xml
+1 −0 maven/public/icons/samsung_pay.svg
+1 −1 settings.gradle
2 changes: 1 addition & 1 deletion ios
Submodule ios updated 29 files
+2 −2 hyperswitch-sdk-ios.podspec
+93 −109 hyperswitch.xcodeproj/project.pbxproj
+33 −34 hyperswitch/HyperViewModel.swift
+0 −133 hyperswitch/PMMViewController.swift
+7 −14 hyperswitch/SwiftUIView.swift
+19 −6 hyperswitch/ViewController.swift
+8 −1 hyperswitchAppClip/AppDelegate.swift
+0 −38 hyperswitchAppClip/ContentView.swift
+0 −81 hyperswitchAppClip/SwiftUIView.swift
+0 −18 hyperswitchAppClip/hyperClip.swift
+1 −3 hyperswitchSDK/Core/HyperExpressCheckout/ExpressCheckoutLauncher.swift
+93 −0 hyperswitchSDK/Core/HyperPaymentMethodManagement/PaymentMethodManagement.swift
+0 −101 hyperswitchSDK/Core/HyperPaymentMethodManagement/PaymentMethodManagementWidget.swift
+24 −1 hyperswitchSDK/Core/HyperPaymentSheet/PaymentSheetView+SwiftUI.swift
+1 −2 hyperswitchSDK/Core/HyperPaymentSheet/PaymentSheetView.swift
+11 −0 hyperswitchSDK/Core/HyperPaymentSheet/SwiftUIManager.swift
+5 −0 hyperswitchSDK/Core/HyperSession/PaymentSession+UIKit.swift
+0 −3 hyperswitchSDK/Core/NativeModule/HyperHeadless.swift
+0 −11 hyperswitchSDK/Core/NativeModule/HyperModule.m
+0 −5 hyperswitchSDK/Core/NativeModule/HyperModule.swift
+1 −1 hyperswitchSDK/Core/Resources/CodePush.plist
+983 −985 hyperswitchSDK/Core/Resources/hyperswitch.bundle
+0 −0 hyperswitchSDK/CoreLite/AppClip/ApplePayHandlerLite.swift
+1 −1 hyperswitchSDK/CoreLite/AppClip/PaymentSession+AppClip.swift
+2 −3 hyperswitchSDK/CoreLite/AppClip/PaymentSheetView+AppClip.swift
+0 −0 hyperswitchSDK/CoreLite/AppClip/WebViewController.swift
+0 −48 hyperswitchSDK/CoreLite/SwiftUI+Lite.swift
+1 −2 hyperswitchSDK/Shared/PaymentSession.swift
+1 −1 hyperswitchSDK/Shared/Version.swift
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@sentry/react-native": "^5.9.1",
"react-native-code-push": "^8.3.1",
"react-native-hyperswitch-netcetera-3ds": "^0.1.1",
"react-native-hyperswitch-samsung-pay": "^0.1.0",
"react-native-hyperswitch-scancard": "^0.3.1",
"react-native-inappbrowser-reborn": "^3.7.0",
"react-native-klarna-inapp-sdk": "^2.1.13",
Expand Down
39 changes: 38 additions & 1 deletion src/components/elements/ButtonElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ let make = (
applePayButtonColor,
buttonBorderRadius,
primaryButtonHeight,
samsungPayButtonColor,
} = ThemebasedStyle.useThemeBasedStyle()

let fetchAndRedirect = AllPaymentHooks.useRedirectHook()
Expand Down Expand Up @@ -267,6 +268,37 @@ let make = (
}
}

let confirmSamsungPay = status => {
if status->ThreeDsUtils.isStatusSuccess {
let response =
status.message
->JSON.parseExn
->JSON.Decode.object
->Option.getOr(Dict.make())

let obj = response->SamsungPayType.itemToObjMapper

let payment_method_data =
[
(
walletType.payment_method,
[(walletType.payment_method_type, obj->Utils.getJsonObjectFromRecord)]
->Dict.fromArray
->JSON.Encode.object,
),
]
->Dict.fromArray
->JSON.Encode.object
processRequest(~payment_method_data, ())
} else {
setLoading(FillingDetails)
showAlert(
~errorType="warning",
~message=`Samsung Pay Error, Please try again ${status.message}`,
)
}
}

let confirmApplePay = (var: dict<JSON.t>) => {
logger(
~logType=DEBUG,
Expand Down Expand Up @@ -494,6 +526,7 @@ let make = (
},
)
}
| SAMSUNG_PAY => SamsungPayModule.presentSamsungPayPaymentSheet(confirmSamsungPay)
| _ => {
logger(
~logType=DEBUG,
Expand Down Expand Up @@ -557,9 +590,13 @@ let make = (
borderRadius=buttonBorderRadius
linearGradientColorTuple=?{switch walletType.payment_method_type_wallet {
| PAYPAL => Some(Some(paypalButonColor))
| SAMSUNG_PAY => {
Console.log2("HERE", walletType.payment_method_type)
Some(Some(samsungPayButtonColor))
}
| _ => None
}}
leftIcon=CustomIcon(<Icon name=walletType.payment_method_type width=24. height=32. />)
leftIcon=CustomIcon(<Icon name=walletType.payment_method_type width=120. height=115. />)
onPress={_ => pressHandler()}
name=walletType.payment_method_type>
{switch walletType.payment_method_type_wallet {
Expand Down
18 changes: 18 additions & 0 deletions src/components/modules/SamsungPayModule.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
open ExternalThreeDsTypes

type module_ = {
checkSamsungPayValidity: (string, statusType => unit) => unit,
presentSamsungPayPaymentSheet: (statusType => unit) => unit,
isAvailable: bool,
}

@val external require: string => module_ = "require"

let (checkSamsungPayValidity, presentSamsungPayPaymentSheet, isAvailable) = switch try {
require("react-native-hyperswitch-samsung-pay")->Some
} catch {
| _ => None
} {
| Some(mod) => (mod.checkSamsungPayValidity, mod.presentSamsungPayPaymentSheet, mod.isAvailable)
| None => ((_, _) => (), _ => (), false)
}
7 changes: 7 additions & 0 deletions src/hooks/PMListModifier.res
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,13 @@ let useListModifier = () => {
exp
}
: None
| SAMSUNG_PAY =>
exp->Option.isSome &&
SamsungPayModule.isAvailable &&
SamsungPay.val.contents == SamsungPay.Valid
? exp
: None

| PAYPAL =>
exp->Option.isSome && PaypalModule.payPalModule->Option.isSome
? exp
Expand Down
60 changes: 60 additions & 0 deletions src/hooks/SamsungPay.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
open SamsungPayType

type samsungPayWalletValidity = Checking | Valid | Invalid | Not_Started
let val = ref(Not_Started)

let isSamsungPayValid = state => {
state != Checking && state != Not_Started
}

let useSamsungPayValidityHook = () => {
let (state, setState) = React.useState(_ => val.contents)
let isSamsungPayAvailable = SamsungPayModule.isAvailable
let (allApiData, _) = React.useContext(AllApiDataContext.allApiDataContext)
let sessionToken = allApiData.sessions->getSamsungPaySessionObject

let stringifiedSessionToken =
sessionToken
->Utils.getJsonObjectFromRecord
->JSON.stringify

React.useEffect2(() => {
switch (val.contents, isSamsungPayAvailable, allApiData.sessions) {
| (_, false, _) =>
setState(_ => {
val := Invalid
Invalid
})
| (Not_Started, true, Some(_)) => {
setState(_ => {
val := Checking
Checking
})
if isSamsungPayAvailable {
SamsungPayModule.checkSamsungPayValidity(stringifiedSessionToken, status => {
if status->ThreeDsUtils.isStatusSuccess {
setState(
_ => {
val := Valid
Valid
},
)
} else {
setState(
_ => {
val := Invalid
Invalid
},
)
}
})
}
}

| (_, _, _) => ()
}->ignore
None
}, (isSamsungPayAvailable, allApiData.sessions))

state
}
6 changes: 6 additions & 0 deletions src/hooks/ThemebasedStyle.res
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ type themeBasedStyleObj = {
modalTextSizeAdjust: float,
cardTextSizeAdjust: float,
paypalButonColor: buttonColorConfig,
samsungPayButtonColor: buttonColorConfig,
applePayButtonColor: SdkTypes.applePayButtonStyle,
googlePayButtonColor: Appearance.t,
payNowButtonColor: buttonColorConfig,
Expand Down Expand Up @@ -237,6 +238,7 @@ let darkRecord = {
modalTextSizeAdjust: 0.,
cardTextSizeAdjust: 0.,
paypalButonColor: ("#ffffff", "#ffffff"),
samsungPayButtonColor: ("#000000", "#000000"),
payNowButtonTextColor: "#FFFFFF",
applePayButtonColor: #white,
googlePayButtonColor: #light,
Expand Down Expand Up @@ -314,6 +316,7 @@ let lightRecord = {
cardTextSizeAdjust: 0.,
paypalButonColor: ("#F6C657", "#F6C657"),
applePayButtonColor: #black,
samsungPayButtonColor: ("#000000", "#000000"),
googlePayButtonColor: #dark,
payNowButtonColor: ("#006DF9", "#006DF9"),
payNowButtonTextColor: "#FFFFFF",
Expand Down Expand Up @@ -390,6 +393,7 @@ let minimal = {
modalTextSizeAdjust: 0.,
cardTextSizeAdjust: 0.,
paypalButonColor: ("#ffc439", "#ffc439"),
samsungPayButtonColor: ("#000000", "#000000"),
applePayButtonColor: #black,
googlePayButtonColor: #dark,
payNowButtonColor: ("#0570de", "#0080FE"),
Expand Down Expand Up @@ -469,6 +473,7 @@ let flatMinimal = {
paypalButonColor: ("#ffc439", "#ffc439"),
applePayButtonColor: #black,
googlePayButtonColor: #dark,
samsungPayButtonColor: ("#000000", "#000000"),
payNowButtonColor: ("#0570de", "#0080FE"),
payNowButtonTextColor: "#000000",
payNowButtonBorderColor: "#000000",
Expand Down Expand Up @@ -763,6 +768,7 @@ let itemToObj = (
| None => themeObj.cardTextSizeAdjust
},
paypalButonColor: themeObj.paypalButonColor,
samsungPayButtonColor: themeObj.samsungPayButtonColor,
applePayButtonColor: applePayOverrideStyle,
googlePayButtonColor: gpayOverrideStyle,
payNowButtonColor: getStrProp(
Expand Down
3 changes: 3 additions & 0 deletions src/icons/Icon.res

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions src/routes/ParentPaymentSheet.res
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,29 @@ let make = () => {
let setConfirmButtonDataRef = React.useCallback1(confirmButtonDataRef => {
setConfirmButtonDataRef(_ => confirmButtonDataRef)
}, [setConfirmButtonDataRef])
let samsungPayValidity = SamsungPay.useSamsungPayValidityHook()

<FullScreenSheetWrapper>
{switch (allApiData.savedPaymentMethods, allApiData.additionalPMLData.paymentType) {
| (_, None)
| (Loading, _) =>
nativeProp.hyperParams.defaultView
{switch (
allApiData.savedPaymentMethods,
allApiData.additionalPMLData.paymentType,
samsungPayValidity,
) {
| (_, _, SamsungPay.Checking) => <SdkLoadingScreen />
| (_, _, SamsungPay.Not_Started) => <SdkLoadingScreen />
| (_, None, _)
| (Loading, _, _) =>
nativeProp.hyperParams.defaultView && samsungPayValidity->SamsungPay.isSamsungPayValid
? <PaymentSheet setConfirmButtonDataRef />
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we move this to PMListModifier.res as checks for other wallets are also handled there

: <SdkLoadingScreen />
| (Some(data), _) =>
| (Some(data), _, _) =>
paymentScreenType == PaymentScreenContext.SAVEDCARDSCREEN &&
data.pmList->Option.getOr([])->Array.length > 0 &&
allApiData.additionalPMLData.mandateType !== SETUP_MANDATE
? <SavedPaymentScreen setConfirmButtonDataRef savedPaymentMethordContextObj=data />
: <PaymentSheet setConfirmButtonDataRef />

| (None, _) => <PaymentSheet setConfirmButtonDataRef />
| (None, _, _) => <PaymentSheet setConfirmButtonDataRef />
}}
<GlobalConfirmButton confirmButtonDataRef />
<Space height=12. />
Expand Down
1 change: 1 addition & 0 deletions src/types/PaymentMethodListType.res
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ let flattenPaymentListArray = (plist, item) => {
| "google_pay" => GOOGLE_PAY
| "apple_pay" => APPLE_PAY
| "paypal" => PAYPAL
| "samsung_pay" => SAMSUNG_PAY
| _ => NONE
},
payment_experience: dict2
Expand Down
83 changes: 83 additions & 0 deletions src/types/SamsungPayType.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
open Utils
type payment3DS = {
\"type": string,
version: string,
data: string,
}

type paymentShippingAddress = {
shipping: Js.Json.t, // Assuming this is a complex object, we use Js.Json.t for now
email: string,
}

type paymentCredential = {
\"3_d_s": payment3DS,
card_brand: string,
// payment_currency_type: string,
// payment_last4_dpan: string,
// payment_last4_fpan: string,
card_last4digits: string,
// merchant_ref: string,
method: string,
recurring_payment: bool,
// payment_shipping_address: paymentShippingAddress,
// payment_shipping_method: string,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

How do we handle billing address requirements for a processor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are not collecting it via Samsung Pay, as it will add extra friction of filling all these details in Samsung Pay Sheet. We will either accept that in create or confirm ( via HS SDK)

type paymentMethodData = {payment_credential: paymentCredential}

let defaultSPayPaymentMethodData = {
payment_credential: {
card_brand: "",
recurring_payment: false,
card_last4digits: "",
method: "",
\"3_d_s": {
\"type": "",
version: "",
data: "",
},
},
}

let get3DSData = (dict, str) => {
dict
->Dict.get(str)
->Option.flatMap(JSON.Decode.object)
->Option.map(json => {
{
\"type": getString(json, "type", ""),
version: getString(json, "version", ""),
data: getString(json, "data", ""),
}
})
->Option.getOr({\"type": "", version: "", data: ""})
}
let getPaymentMethodData = dict => {
{
payment_credential: {
card_brand: getString(dict, "payment_card_brand", ""),
recurring_payment: getBool(dict, "recurring_payment", false),
card_last4digits: getString(dict, "payment_last4_fpan", ""),
method: getString(dict, "method", ""),
\"3_d_s": get3DSData(dict, "3DS"),
},
}
}

type paymentDataFromSPay = {paymentMethodData: paymentMethodData, email?: string}
let itemToObjMapper = dict => {
getPaymentMethodData(dict)
}

let getSamsungPaySessionObject = (sessionData: AllApiDataContext.sessions) => {
let sessionObject = switch sessionData {
| Some(sessionData) =>
sessionData
->Array.find(item => item.wallet_name == SAMSUNG_PAY)
->Option.getOr(SessionsType.defaultToken)
| _ => SessionsType.defaultToken
}

sessionObject
//TO DO order_number should not contain _
}
3 changes: 2 additions & 1 deletion src/types/SdkTypes.res
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type localeTypes =

type fontFamilyTypes = DefaultIOS | DefaultAndroid | CustomFont(string) | DefaultWeb

type payment_method_type_wallet = GOOGLE_PAY | APPLE_PAY | PAYPAL | NONE | KLARNA
type payment_method_type_wallet = GOOGLE_PAY | APPLE_PAY | PAYPAL | SAMSUNG_PAY | NONE | KLARNA
let walletNameMapper = str => {
switch str {
| "google_pay" => "Google Pay"
Expand Down Expand Up @@ -274,6 +274,7 @@ let walletTypeToStrMapper = walletType => {
| GOOGLE_PAY => "google_pay"
| APPLE_PAY => "apple_pay"
| PAYPAL => "paypal"
| SAMSUNG_PAY => "samsung_pay"
| _ => ""
}
}
Expand Down
Loading
Loading