Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/src/main/kotlin/io/hyperswitch/react/HyperModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.hyperswitch.paymentsession.LaunchOptions
import io.hyperswitch.paymentsession.PaymentSheetCallbackManager
import io.hyperswitch.view.BasePaymentWidget
import io.hyperswitch.payments.launcher.PaymentMethod
import io.hyperswitch.paymentsession.WidgetCallbackManager
import org.json.JSONObject

class HyperModule internal constructor(private val rct: ReactApplicationContext) :
Expand Down Expand Up @@ -156,7 +157,8 @@ class HyperModule internal constructor(private val rct: ReactApplicationContext)

// Method to exit widget payment sheet
@ReactMethod
fun exitWidgetPaymentsheet(rootTag: Int, paymentResult: String, reset: Boolean) {
fun exitWidgetPaymentsheet(rootTag: Int, sessionId : String, paymentResult: String, reset: Boolean) {
WidgetCallbackManager.executeCallback(paymentResult, sessionId)
}

// Variable to keep track of event listener count
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/io/hyperswitch/react/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
151 changes: 151 additions & 0 deletions app/src/main/kotlin/io/hyperswitch/view/PaymentWidget.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
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.paymentsession.Callback
import io.hyperswitch.paymentsession.EventCallback
import io.hyperswitch.paymentsession.LaunchOptions
import io.hyperswitch.paymentsession.WidgetCallbackManager
import io.hyperswitch.paymentsheet.PaymentSheet
import io.hyperswitch.react.HyperFragment
import io.hyperswitch.react.ReactNativeController
import java.util.UUID

enum class WidgetType {
PAYMENT_SHEET, BUTTON_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 val sessionId = UUID.randomUUID().toString()

private var launchOptions = LaunchOptions(context, BuildConfig.VERSION_NAME)

private var widgetType = WidgetType.PAYMENT_SHEET


constructor(context: Context?) : super(context!!) {}

constructor(context: Context, attrs: android.util.AttributeSet?) : super(context, attrs) {}

constructor(
context: Context, attrs: android.util.AttributeSet?, defStyleAttr: Int
) : super(context, attrs, defStyleAttr) {
}


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
val mCallback: Callback = { result ->
callback(result)
removeWidget()
}
WidgetCallbackManager.setCallback(mCallback, true, this.sessionId)
}

fun onEvent(eventCallback: EventCallback) {
WidgetCallbackManager.setEventCallback(this.sessionId, eventCallback)
}

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(): String {
return when (this.widgetType) {
WidgetType.PAYMENT_SHEET -> "widgetPaymentSheet"
WidgetType.BUTTON_SHEET -> "widgetButtonSheet"
WidgetType.CARD -> "card"
WidgetType.EXPRESS_CHECKOUT -> "expressCheckout"
WidgetType.GOOGLE_PAY -> "google_pay"
WidgetType.PAYPAL -> "paypal"
else -> "widgetPaymentSheet"
}
}

fun showWidget(clientSecret: String, callback: Callback? = null) {
val activity = getFragmentActivity()
?: throw IllegalStateException("PaymentWidget must be attached to a FragmentActivity")
if (id == NO_ID) {
id = generateViewId()
}
if (callback != null) {
this.onPaymentResult(callback)
}
if (clientSecret == "null") {
throw IllegalArgumentException("Client Secret cannot be null")
}
val fragment = HyperFragment.Builder().setComponentName("hyperSwitch").setLaunchOptions(
launchOptions.getBundle(
paymentIntentClientSecret = clientSecret,
configuration = this.configuration,
type = getWidgetType(),
sessionId = this.sessionId
)
).build()
activity.supportFragmentManager.beginTransaction().replace(this.id, fragment)
.commitAllowingStateLoss()
}

fun showWidget(clientSecret: String) {
showWidget(clientSecret, this.callback)
}

private fun removeWidget() {
val activity = getFragmentActivity() ?: return
val fragment =
activity.supportFragmentManager.findFragmentById(id) as? HyperFragment ?: return
WidgetCallbackManager.removeSession(this.sessionId)
activity.supportFragmentManager.beginTransaction().remove(fragment)
.commitAllowingStateLoss()
}
}
34 changes: 32 additions & 2 deletions demo-app/src/main/kotlin/io/hyperswitch/demoapp/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.hyperswitch.demoapp

import android.annotation.SuppressLint
import android.content.Intent
import android.util.Patterns
import android.os.Bundle
Expand All @@ -8,6 +9,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
Expand All @@ -24,6 +26,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
Expand All @@ -36,6 +42,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<String?> {
override fun success(value: String?) {
Expand Down Expand Up @@ -152,9 +160,8 @@ class MainActivity : AppCompatActivity(), HyperInterface {
* Create Payment Session Object
*
* */

widget.showWidget(paymentIntentClientSecret)
paymentSession = PaymentSession(ctx, publishableKey)

/**
*
* Initialise Payment Session
Expand Down Expand Up @@ -189,6 +196,7 @@ class MainActivity : AppCompatActivity(), HyperInterface {
ctx.runOnUiThread {
ctx.findViewById<View>(R.id.launchButton).isEnabled = true
}

}
} catch (e: JSONException) {
Log.d("Backend Response", e.toString())
Expand Down Expand Up @@ -228,6 +236,14 @@ class MainActivity : AppCompatActivity(), HyperInterface {
}
})

widget = findViewById(R.id.paymentWidget)
widget.initWidget(
publishableKey = publishableKey,
)
widget.setWidgetType(WidgetType.PAYMENT_SHEET)
widget.configuration(getCustomisations())
widget.onPaymentResult(::onPaymentSheetResult)

/**
*
* Merchant API call to get Client Secret
Expand Down Expand Up @@ -255,6 +271,20 @@ class MainActivity : AppCompatActivity(), HyperInterface {
startActivity(intent)
}

// or

// val widget = findViewById<PaymentWidget>(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) {
Expand Down
10 changes: 10 additions & 0 deletions demo-app/src/main/res/drawable/card_bg.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<corners android:radius="12dp"/>
<padding
android:left="16dp"
android:right="16dp"
android:top="16dp"
android:bottom="16dp"/>
</shape>
Loading