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
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ object ReactNativeController {
private var isInitialized = false

/**
* Resolves the JavaScript bundle path using Hyper Airborne OTA if available.
* Resolves the JavaScript bundle path using Airborne OTA if available.
*
* Behavior:
* - Determines SDK environment using the publishable key
* - Reads OTA endpoint from resources based on environment
* - Reads Airborne release config endpoint from resources based on environment
* - Dynamically loads AirborneOTA via reflection (optional dependency)
* - Fetches the OTA-downloaded bundle path
* - Falls back to bundled assets if OTA is disabled, unavailable, or fails
* - Falls back to bundled assets if Airborne is disabled, unavailable, or fails
*
* @param application Application context
* @return Path to the JS bundle (OTA or bundled asset)
Expand All @@ -59,13 +59,13 @@ object ReactNativeController {
LogUtils.getEnvironment(PaymentConfiguration.Companion.publishableKey())
val airborneUrl = application.getString(
if (environment == SDKEnvironment.SANDBOX)
R.string.hyperOTASandBoxEndPoint
R.string.airborneSandBoxEndPoint
else
R.string.hyperOTAEndPoint
R.string.airborneEndPoint
)

// Ensure OTA endpoint is valid
if (airborneUrl != "hyperOTA_END_POINT_") {
// Ensure Airborne endpoint is valid (not the default placeholder)
if (airborneUrl != "airborne_END_POINT_") {
val airborneClass =
Class.forName("io.hyperswitch.airborne.AirborneOTA")

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<resources>
<string name="app_name">HyperSwitch</string>
<string name="hyperOTAEndPoint">hyperOTA_END_POINT_</string>
<string name="hyperOTASandBoxEndPoint">hyperOTA_END_POINT_</string>
<string name="airborneEndPoint">airborne_END_POINT_</string>
<string name="airborneSandBoxEndPoint">airborne_END_POINT_</string>
</resources>
2 changes: 1 addition & 1 deletion hyperswitch-sdk-android-airborne/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ rootProject.allprojects {
}

dependencies {
implementation("in.juspay:hyperotareact:0.0.1-xota.10")
implementation("in.juspay:hyperotareact:2.2.2-xota.07")
implementation project(':hyperswitch-sdk-android-logger')
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import io.hyperswitch.logs.LogCategory
import org.json.JSONException

/**
* Logger implementation for tracking HyperOTA lifecycle events and errors.
* Logger implementation for tracking Airborne OTA lifecycle events and errors.
* Extends TrackerCallback to capture OTA-related events and send them to the logging endpoint.
*
* @param sdkVersion The version of the SDK being used
* @param customLogUrl Optional custom URL for the logging endpoint
* @param publishableKey Optional merchant's publishable key for log authentication
*/
class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() {
class AirborneLogger(private val sdkVersion: String) : TrackerCallback() {

private fun createAndSendLog(
eventName: EventName,
Expand All @@ -41,55 +39,66 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() {

try {
val jsonData = JSONObject(eventData).toString()
val log = HSLog.LogBuilder().logType(level).category(LogCategory.OTA_LIFE_CYCLE)
val log = HSLog.LogBuilder().logType(level).category(LogCategory.AIRBORNE_LIFE_CYCLE)
.eventName(eventName).value(jsonData).version(this.sdkVersion)
HyperLogManager.addLog(log.build())
} catch (e: JSONException) {
} catch (_: JSONException) {
}
}
}

/**
* Tracks OTA lifecycle events with primitive or string values.
* Resolves the appropriate EventName based on the event key.
*
* Maps lifecycle event keys from the Airborne SDK to internal event names:
* - "started" / "init" → AIRBORNE_INIT
* - "end" → AIRBORNE_FINISH
* - "boot" → AIRBORNE_BOOT
* - everything else → AIRBORNE_EVENT
*/
private fun resolveEventName(key: String): EventName {
return when (key) {
"started", "init" -> EventName.AIRBORNE_INIT
"end" -> EventName.AIRBORNE_FINISH
"boot" -> EventName.AIRBORNE_BOOT
else -> EventName.AIRBORNE_EVENT
}
}

/**
* Tracks Airborne lifecycle events with primitive or string values.
*
* @param category The event category (e.g., "lifecycle")
* @param subCategory The event subcategory (e.g., "hyperota")
* @param subCategory The event subcategory (e.g., "hyperota", "hypersdk")
* @param level The log level (e.g., "info", "debug", "error")
* @param label A descriptive label for the event
* @param key The event key (e.g., "init", "end")
* @param key The event key (e.g., "started", "end", "boot")
* @param value The event value to be logged
*/
override fun track(
category: String, subCategory: String, level: String, label: String, key: String, value: Any
) {
if (category == "lifecycle") {
if (subCategory == "hyperota") {
val eventName = when (key) {
"init" -> EventName.HYPER_OTA_INIT
"end" -> EventName.HYPER_OTA_FINISH
else -> EventName.HYPER_OTA_EVENT
}
if (subCategory == "hyperota" || subCategory == "hypersdk") {
val eventName = resolveEventName(key)
createAndSendLog(eventName, level, label, category, subCategory, key, value)
} else {
createAndSendLog(
EventName.HYPER_OTA_EVENT, level, label, category, subCategory, key, value
EventName.AIRBORNE_EVENT, level, label, category, subCategory, key, value
)
}

}
// Log.i("ota-1" , "category : " + category + "\n subCategory : " + subCategory +
// "\n level : " + level + "\n label : " + label + "\n key : " + key + "\n value : " + value.toString())
}

/**
* Tracks OTA lifecycle events with JSON object values.
* Tracks Airborne lifecycle events with JSON object values.
* Extracts OTA version from the value if available and updates the log manager.
*
* @param category The event category (e.g., "lifecycle")
* @param subCategory The event subcategory (e.g., "hyperota")
* @param subCategory The event subcategory (e.g., "hyperota", "hypersdk")
* @param level The log level (e.g., "info", "debug", "error")
* @param label A descriptive label for the event
* @param key The event key (e.g., "init", "end")
* @param key The event key (e.g., "started", "end", "boot")
* @param value The JSON object containing event data
*/
override fun track(
Expand All @@ -105,27 +114,20 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() {
val otaVersion = value.getString("package_version")
HyperLogManager.setOtaVersion(otaVersion)
} catch (_: JSONException) {

}
if (subCategory == "hyperota") {
val eventName = when (key) {
"init" -> EventName.HYPER_OTA_INIT
"end" -> EventName.HYPER_OTA_FINISH
else -> EventName.HYPER_OTA_EVENT
}
if (subCategory == "hyperota" || subCategory == "hypersdk") {
val eventName = resolveEventName(key)
createAndSendLog(eventName, level, label, category, subCategory, key, value)
} else {
createAndSendLog(
EventName.HYPER_OTA_EVENT, level, label, category, subCategory, key, value
EventName.AIRBORNE_EVENT, level, label, category, subCategory, key, value
)
}
}
// Log.i("ota-2" , "category : " + category + "\n subCategory : " + subCategory +
// "\n level : " + level + "\n label : " + label + "\n key : " + key + "\n value : " + value.toString())
}

/**
* Tracks exceptions that occur during OTA lifecycle operations.
* Tracks exceptions that occur during Airborne lifecycle operations.
*
* @param category The event category (e.g., "lifecycle")
* @param subCategory The event subcategory
Expand All @@ -147,14 +149,12 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() {
try {
val jsonData = JSONObject(eventData).toString()
val log =
HSLog.LogBuilder().logType("error").category(LogCategory.OTA_LIFE_CYCLE)
.eventName(EventName.HYPER_OTA_EVENT).value(jsonData)
HSLog.LogBuilder().logType("error").category(LogCategory.AIRBORNE_LIFE_CYCLE)
.eventName(EventName.AIRBORNE_EVENT).value(jsonData)

HyperLogManager.addLog(log.build())
} catch (e: JSONException) {
} catch (_: JSONException) {
}
}
// Log.i("ota-3" , "category : " + category + "\n subCategory : " + subCategory +
// "\n level : " + "error" + "\n label : " + label + "\n description : " + description + "\n value : " + e.toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,68 @@ package io.hyperswitch.airborne

import android.content.Context
import androidx.annotation.Keep
import `in`.juspay.hyperota.LazyDownloadCallback
import `in`.juspay.hyperotareact.HyperOTAReact
import `in`.juspay.hyperotareact.HyperOTAServicesReact

@Keep
class AirborneOTA {
private lateinit var hyperOTAReact : HyperOTAReact
private lateinit var tracker : HyperOtaLogger
private lateinit var hyperOTAServicesReact: HyperOTAServicesReact
private lateinit var tracker: AirborneLogger

fun initAirborneOTA(context: Context,
sdkVersion: String,
url : String,
appId: String,
bundleName: String){
private fun initAirborneOTA(
context: Context,
sdkVersion: String,
url: String,
appId: String,
bundleName: String
) {
try {
this.tracker = HyperOtaLogger(sdkVersion)
HyperOTAReact(
this.tracker = AirborneLogger(sdkVersion)
hyperOTAServicesReact = HyperOTAServicesReact(
context,
appId,
bundleName,
sdkVersion,
url,
mapOf(
"Content-Encoding" to "br, gzip"
),
object : LazyDownloadCallback {
override fun fileInstalled(filePath: String, success: Boolean) {
}

override fun lazySplitsInstalled(success: Boolean) {
}
},
tracker,
tracker
)
}catch (e: Exception){
} catch (e: Exception) {
e.printStackTrace()
}
}

/**
* constructor for HyperOTA with full configuration.
* Constructor for AirborneOTA with full configuration.
*
* @param context The Android application context
* @param sdkVersion The version of the SDK being used
* @param configUrl The URL to fetch the OTA configuration
* @param appId The application identifier for OTA updates
* @param bundleName The name of the bundle to be downloaded and installed
*/
constructor(context: Context,
sdkVersion: String,
configUrl : String,
appId: String,
bundleName: String
){
this.initAirborneOTA(context,sdkVersion,configUrl,appId,bundleName)
constructor(
context: Context,
sdkVersion: String,
configUrl: String,
appId: String,
bundleName: String
) {
this.initAirborneOTA(context, sdkVersion, configUrl, appId, bundleName)
}

/**
* constructor for HyperOTA with default Hyperswitch configuration.
* Constructor for AirborneOTA with default Hyperswitch configuration.
* Automatically constructs the config URL, app ID, and bundle name.
*
* @param context The Android application context
* @param sdkVersion The version of the SDK being used
* @param baseUrl The base URL for the OTA service (config URL will be constructed as: baseUrl/mobile-ota/android/sdkVersion/config.json)
*/
constructor(context: Context,
sdkVersion: String,
baseUrl : String){
if(baseUrl == "") {
constructor(
context: Context,
sdkVersion: String,
baseUrl: String
) {
if (baseUrl == "") {
throw Exception("BaseURL shouldn't be empty")
}
this.initAirborneOTA(
Expand All @@ -82,11 +77,10 @@ class AirborneOTA {

fun getBundlePath(): String {
return try {
hyperOTAReact.getBundlePath().takeUnless { it.contains("ios") }
hyperOTAServicesReact.getBundlePath().takeUnless { it.contains("ios") }
?: "assets://hyperswitch.bundle"
} catch (_: Exception) {
"assets://hyperswitch.bundle"
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ enum class LogType {
}

enum class LogCategory {
API, USER_ERROR, USER_EVENT, MERCHANT_EVENT, OTA_LIFE_CYCLE
API, USER_ERROR, USER_EVENT, MERCHANT_EVENT, OTA_LIFE_CYCLE, AIRBORNE_LIFE_CYCLE
}

enum class EventName {
HYPER_OTA_INIT, HYPER_OTA_FINISH, HYPER_OTA_EVENT, CRASH_EVENT,
AIRBORNE_INIT, AIRBORNE_FINISH, AIRBORNE_BOOT, AIRBORNE_EVENT, CRASH_EVENT,
AUTHENTICATION_SESSION,
AUTHENTICATION_SESSION_INIT,
AUTHENTICATION_SESSION_RETURNED,
Expand Down
Loading