From 0b09cc7ef226d2d3b09b6106de60aac6ae0e7313 Mon Sep 17 00:00:00 2001 From: Kuntimaddi Manideep Date: Thu, 26 Feb 2026 21:31:59 +0530 Subject: [PATCH 1/3] feat: Implement PaymentWidget and integrate into MainActivity layout --- .../main/kotlin/io/hyperswitch/react/Utils.kt | 4 +- .../io/hyperswitch/view/PaymentWidget.kt | 126 ++++++++++++ .../io/hyperswitch/demoapp/MainActivity.kt | 35 ++++ demo-app/src/main/res/drawable/card_bg.xml | 10 + .../src/main/res/layout/main_activity.xml | 194 +++++++++++------- .../paymentsession/LaunchOptions.kt | 10 +- 6 files changed, 303 insertions(+), 76 deletions(-) create mode 100644 app/src/main/kotlin/io/hyperswitch/view/PaymentWidget.kt create mode 100644 demo-app/src/main/res/drawable/card_bg.xml diff --git a/app/src/main/kotlin/io/hyperswitch/react/Utils.kt b/app/src/main/kotlin/io/hyperswitch/react/Utils.kt index 3af110d7..28c5413a 100644 --- a/app/src/main/kotlin/io/hyperswitch/react/Utils.kt +++ b/app/src/main/kotlin/io/hyperswitch/react/Utils.kt @@ -35,7 +35,7 @@ class Utils { // Check if React Native fragment exists or if request has changed if (reactNativeFragmentSheet == null || areBundlesNotEqual(request, lastRequest, context)) { lastRequest = request - val newReactNativeFragmentSheet = ReactFragment.Builder() + val newReactNativeFragmentSheet = HyperFragment.Builder() .setComponentName("hyperSwitch") .setLaunchOptions(getLaunchOptions(request, message, context)) .setFabricEnabled(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) @@ -45,7 +45,7 @@ class Utils { transaction.show(reactNativeFragmentSheet).commitAllowingStateLoss() } } else { - val reactNativeFragmentCard = ReactFragment.Builder() + val reactNativeFragmentCard = HyperFragment.Builder() .setComponentName("hyperSwitch") .setLaunchOptions(getLaunchOptions(request, message, context)) .setFabricEnabled(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) diff --git a/app/src/main/kotlin/io/hyperswitch/view/PaymentWidget.kt b/app/src/main/kotlin/io/hyperswitch/view/PaymentWidget.kt new file mode 100644 index 00000000..d8349374 --- /dev/null +++ b/app/src/main/kotlin/io/hyperswitch/view/PaymentWidget.kt @@ -0,0 +1,126 @@ +package io.hyperswitch.view + +import android.app.Application +import android.content.Context +import android.widget.FrameLayout +import androidx.fragment.app.FragmentActivity +import io.hyperswitch.BuildConfig +import io.hyperswitch.payments.Callback +import io.hyperswitch.paymentsession.LaunchOptions +import io.hyperswitch.paymentsheet.PaymentSheet +import io.hyperswitch.react.HyperFragment +import io.hyperswitch.react.ReactNativeController + +enum class WidgetType { + PAYMENT_SHEET, CARD, EXPRESS_CHECKOUT, GOOGLE_PAY, PAYPAL +} + +class PaymentWidget : FrameLayout { + + private var configuration: PaymentSheet.Configuration? = null + private var callback: Callback? = null + private var publishableKey: String? = null + private var profileId: String? = null + + private var launchOptions = LaunchOptions(context, BuildConfig.VERSION_NAME) + + private var widgetType = WidgetType.PAYMENT_SHEET + + + constructor(context: Context?) : super(context!!) { + } + + fun initWidget(publishableKey: String) { + initWidget(publishableKey, this.profileId ?: "") + } + + fun initWidget( + publishableKey: String, profileId: String + ) { + initWidget( + context.applicationContext as Application, + WidgetType.PAYMENT_SHEET, + publishableKey, + profileId + ) + } + + fun setWidgetType(widgetType: WidgetType){ + this.widgetType = widgetType + } + fun initWidget( + application: Application, + type: WidgetType, + publishableKey: String, + profileId: String, + ) { + this.widgetType = type + this.publishableKey = publishableKey + this.profileId = profileId + ReactNativeController.initialize(application) + } + + fun configuration(configuration: PaymentSheet.Configuration) { + this.configuration = configuration + } + + fun onPaymentResult(callback: Callback) { + this.callback = callback + } + + private fun getFragmentActivity(): FragmentActivity? { + var ctx = context + while (ctx is android.content.ContextWrapper) { + if (ctx is FragmentActivity) { + return ctx + } + ctx = ctx.baseContext + } + return null + } + + private fun getWidgetType(widgetType: WidgetType?): String { + return when (widgetType) { + WidgetType.PAYMENT_SHEET -> "widgetPaymentSheet" + WidgetType.CARD -> "card" + WidgetType.EXPRESS_CHECKOUT -> "expressCheckout" + WidgetType.GOOGLE_PAY -> "google_pay" + WidgetType.PAYPAL -> "paypal" +// WidgetType.SAVED_PAYMENTS -> "widgetSavedPayments" + else -> "widgetPaymentSheet" + } + } + + fun showWidget(clientSecret: String, callback: Callback){ + + } + + + fun showWidget(clientSecret: String) { + val activity = getFragmentActivity() + ?: throw IllegalStateException("PaymentWidget must be attached to a FragmentActivity") + + if (id == NO_ID) { + id = generateViewId() + } + if(clientSecret == "null"){ + throw Exception("Client Secret cannot be null") + } + + clientSecret.let { + val fragment = HyperFragment.Builder( + ).setComponentName("hyperSwitch") + .setLaunchOptions( + launchOptions.getBundle( + clientSecret, + configuration, + getWidgetType(widgetType) + ) + ) + .build() + + activity.supportFragmentManager.beginTransaction().replace(this.id, fragment) + .commitAllowingStateLoss() + } + } +} \ No newline at end of file diff --git a/demo-app/src/main/kotlin/io/hyperswitch/demoapp/MainActivity.kt b/demo-app/src/main/kotlin/io/hyperswitch/demoapp/MainActivity.kt index 1e2c9480..ea740347 100644 --- a/demo-app/src/main/kotlin/io/hyperswitch/demoapp/MainActivity.kt +++ b/demo-app/src/main/kotlin/io/hyperswitch/demoapp/MainActivity.kt @@ -8,6 +8,7 @@ import android.text.TextWatcher import android.util.Log import android.view.View import android.widget.EditText +import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import com.github.kittinunf.fuel.Fuel.reset @@ -24,6 +25,10 @@ import org.json.JSONObject import androidx.core.content.edit import androidx.core.graphics.toColorInt import io.hyperswitch.HyperInterface +import io.hyperswitch.react.ReactNativeController +import io.hyperswitch.view.PaymentWidget +import io.hyperswitch.view.ReactNativeWidget +import io.hyperswitch.view.WidgetType class MainActivity : AppCompatActivity(), HyperInterface { lateinit var ctx: AppCompatActivity @@ -36,6 +41,8 @@ class MainActivity : AppCompatActivity(), HyperInterface { private lateinit var paymentSession: PaymentSession private lateinit var editText: EditText + private lateinit var widget: PaymentWidget + private fun fetchNetceteraApiKey() { reset().get("$serverUrl/netcetera-sdk-api-key").responseString(object : Handler { override fun success(value: String?) { @@ -153,6 +160,7 @@ class MainActivity : AppCompatActivity(), HyperInterface { * * */ + widget.showWidget(paymentIntentClientSecret) paymentSession = PaymentSession(ctx, publishableKey) /** @@ -228,6 +236,19 @@ class MainActivity : AppCompatActivity(), HyperInterface { } }) + widget = PaymentWidget(this) + widget.initWidget( + publishableKey = publishableKey, + ) + widget.setWidgetType(WidgetType.CARD) + widget.configuration(getCustomisations()) + widget.onPaymentResult{ + it -> + Log.i("Result", it.toString()) + } + val container = findViewById(R.id.widgetContainer) + container.addView(widget) + /** * * Merchant API call to get Client Secret @@ -255,6 +276,20 @@ class MainActivity : AppCompatActivity(), HyperInterface { startActivity(intent) } + // or + +// val widget = findViewById(R.id.paymentWidget) +// widget.initWidget( +// type = PAYMENT_SHEET, +// publishableKey = publishableKey, +// profileId = profileId, +// clientSecret = paymentIntentClientSecret +// ) +// widget.configuration(paymentSheetConfig) +// widget.onResult(callback) +// widget.show() + + } private fun setStatus(error: String) { diff --git a/demo-app/src/main/res/drawable/card_bg.xml b/demo-app/src/main/res/drawable/card_bg.xml new file mode 100644 index 00000000..cfbf4c62 --- /dev/null +++ b/demo-app/src/main/res/drawable/card_bg.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/demo-app/src/main/res/layout/main_activity.xml b/demo-app/src/main/res/layout/main_activity.xml index 7be6a45f..e90b57d2 100644 --- a/demo-app/src/main/res/layout/main_activity.xml +++ b/demo-app/src/main/res/layout/main_activity.xml @@ -1,90 +1,144 @@ + android:background="#F3F4F6"> + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> - - -