Skip to content

Commit

Permalink
Merge pull request #5 from xendit/finalizing
Browse files Browse the repository at this point in the history
Ready for Android release
  • Loading branch information
smuzani authored Dec 9, 2024
2 parents 5c2ce41 + d44e9f6 commit 3183e1d
Show file tree
Hide file tree
Showing 17 changed files with 150 additions and 119 deletions.
15 changes: 8 additions & 7 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ plugins {
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.serialization)
}


android {
namespace = "com.cards.session.android"
compileSdk = 34
Expand All @@ -23,18 +21,19 @@ android {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlin {
jvmToolchain(17)
}

buildFeatures {
buildConfig = true
}

}

dependencies {
Expand Down Expand Up @@ -65,4 +64,6 @@ dependencies {

kspAndroidTest(libs.hilt.android.compiler)
androidTestImplementation(libs.hilt.testing)

coreLibraryDesugaring(libs.android.desugar)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import io.github.aakira.napier.Napier
class CardsSessionApplication : Application() {
override fun onCreate() {
super.onCreate()
// TODO check that this doesn't show in production
Napier.base(DebugAntilog())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.cards.session.cards.sdk.CardSessions
import com.cards.session.cards.sdk.create
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down
28 changes: 27 additions & 1 deletion cardsSdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,34 @@ android {
defaultConfig {
minSdk = 21
}

buildTypes {
debug {
isMinifyEnabled = false
buildConfigField("String", "BASE_URL", "\"https://api.stg.tidnex.dev/v3\"")
}
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
consumerProguardFiles("proguard-rules.pro")
buildConfigField("String", "BASE_URL", "\"https://api.xendit.co/v3\"")
}
}

compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}

buildFeatures {
buildConfig = true
}
}

dependencies {
coreLibraryDesugaring(libs.android.desugar)
}
37 changes: 37 additions & 0 deletions cardsSdk/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Keep CardSession related classes
-keep class com.cards.session.cards.models.** { *; }
-keep class com.cards.session.cards.network.** { *; }
-keep class com.cards.session.cards.sdk.** { *; }
-keep class com.cards.session.cards.ui.** { *; }

# Keep Kotlin serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt

-keepclassmembers class kotlinx.serialization.json.** {
*** Companion;
}
-keepclasseswithmembers class kotlinx.serialization.json.** {
kotlinx.serialization.KSerializer serializer(...);
}

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}

# Keep MethodHandles Lookup
-keepclasseswithmembers class java.lang.invoke.MethodHandles$Lookup {
*;
}

# Keep toString() methods
-keepclassmembers class * {
java.lang.String toString();
}

## don't warn on non-existent classes
-dontwarn java.lang.invoke.StringConcatFactory

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.cards.session.cards.network

import com.cards.session.BuildConfig

actual object NetworkConstants {
actual val BASE_URL: String
get() = BuildConfig.BASE_URL
}
Original file line number Diff line number Diff line change
@@ -1,59 +1,11 @@
package com.cards.session.cards.sdk

import android.content.Context
import com.cards.session.cards.models.CardsResponseDto
import com.cards.session.cards.ui.CardSessionState
import kotlinx.coroutines.flow.StateFlow

/**
* Core interface for card data collection functionality.
* This interface provides methods to collect card data and CVN in a platform-independent way.
*/
interface CardSessions {
/**
* Current state of the card session including loading, response and error states
*/
val state: StateFlow<CardSessionState>
actual fun CardSessions.Factory.create(apiKey: String): CardSessions {
throw IllegalStateException("On Android, use create(context, apiKey)")
}

/**
* Collects card data from the user.
* @param cardNumber Card number
* @param expiryMonth Card expiry month (2 digits)
* @param expiryYear Card expiry year (4 digits)
* @param cvn Card verification number (optional)
* @param cardholderFirstName Cardholder's first name
* @param cardholderLastName Cardholder's last name
* @param cardholderEmail Cardholder's email
* @param cardholderPhoneNumber Cardholder's phone number
* @param paymentSessionId Payment session ID from the backend
* @return CardsResponseDto representing the current state of card data collection
*/
suspend fun collectCardData(
cardNumber: String,
expiryMonth: String,
expiryYear: String,
cvn: String?,
cardholderFirstName: String,
cardholderLastName: String,
cardholderEmail: String,
cardholderPhoneNumber: String,
paymentSessionId: String
): CardsResponseDto

/**
* Collects CVN (Card Verification Number) from the user.
* @param cvn Card verification number
* @param paymentSessionId Session ID received from the backend
* @return CardsResponseDto representing the current state of CVN collection
*/
suspend fun collectCvn(
cvn: String,
paymentSessionId: String
): CardsResponseDto

companion object {
fun create(context: Context, apiKey: String): CardSessions {
return CardSessionsImpl.create(context, apiKey)
}
}
}
fun CardSessions.Factory.create(context: Context, apiKey: String): CardSessions {
return CardSessionsImpl.create(context, apiKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ internal class CardSessionsImpl private constructor(
cardholderPhoneNumber: String,
paymentSessionId: String
): CardsResponseDto {
Log.d(TAG, "Starting collectCardData")
_state.update { it.copy(isLoading = true, exception = null) }

try {
Expand All @@ -61,7 +60,6 @@ internal class CardSessionsImpl private constructor(
}

val deviceFingerprint = getFingerprint("collect_card_data")
Log.d(TAG, "Making API request")
val request = CardsRequestDto(
cardNumber = cardNumber,
expiryMonth = expiryMonth,
Expand All @@ -77,7 +75,6 @@ internal class CardSessionsImpl private constructor(

val authToken = AuthTokenGenerator.generateAuthToken(apiKey)
val response = client.paymentWithSession(request, authToken)
Log.d(TAG, "API request successful: $response")
_state.update { it.copy(isLoading = false, cardResponse = response) }
return response
} catch (e: CardsSessionException) {
Expand All @@ -100,7 +97,6 @@ internal class CardSessionsImpl private constructor(
cvn: String,
paymentSessionId: String
): CardsResponseDto {
Log.d(TAG, "Starting collectCvn")
_state.update { it.copy(isLoading = true, exception = null) }

try {
Expand All @@ -109,7 +105,6 @@ internal class CardSessionsImpl private constructor(
}

val deviceFingerprint = getFingerprint("collect_cvn")
Log.d(TAG, "Making API request for CVN")
val request = CardsRequestDto(
cvn = cvn,
paymentSessionId = paymentSessionId,
Expand All @@ -118,7 +113,6 @@ internal class CardSessionsImpl private constructor(

val authToken = AuthTokenGenerator.generateAuthToken(apiKey)
val response = client.paymentWithSession(request, authToken)
Log.d(TAG, "API request successful: $response")
_state.update { it.copy(isLoading = false, cardResponse = response) }
return response
} catch (e: CardsSessionException) {
Expand All @@ -140,15 +134,11 @@ internal class CardSessionsImpl private constructor(
private suspend fun getFingerprint(eventName: String): String =
suspendCancellableCoroutine { continuation ->
try {
Log.d(TAG, "Starting fingerprint collection for event: $eventName")
val sessionId = XenditFingerprintSDK.getSessionId()
Log.d(TAG, "Got session ID: $sessionId")

XenditFingerprintSDK.scan(
customerEventName = eventName,
customerEventID = sessionId,
onSuccess = {
Log.d(TAG, "Scan successful for event: $eventName")
continuation.resume(sessionId)
},
onError = { error ->
Expand All @@ -165,10 +155,8 @@ internal class CardSessionsImpl private constructor(

companion object {
fun create(context: Context, apiKey: String): CardSessions {
Log.d("CardSessions", "Creating new CardSessionsImpl instance")
try {
XenditFingerprintSDK.init(context, apiKey)
Log.d("CardSessions", "XenditFingerprintSDK initialized successfully")
} catch (e: Exception) {
Log.e("CardSessions", "Failed to initialize XenditFingerprintSDK", e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class KtorCardsClient(
logger.i("Making payment session request with body: $jsonString")

val response: HttpResponse = httpClient.post {
url("${NetworkConstants.STG_URL}/payment_with_session")
url("${NetworkConstants.BASE_URL}/payment_with_session")
header("Authorization", "Basic $authToken")
header("Content-Type", "application/json")
setBody(jsonString)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.cards.session.cards.network

// TODO split this
object NetworkConstants {
const val PROD_URL = "https://api.xendit.co/v3"
const val STG_URL = "https://api.stg.tidnex.dev/v3"
expect object NetworkConstants {
val BASE_URL: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.cards.session.cards.sdk

import com.cards.session.cards.models.CardsResponseDto
import com.cards.session.cards.ui.CardSessionState
import kotlinx.coroutines.flow.StateFlow

interface CardSessions {
val state: StateFlow<CardSessionState>

suspend fun collectCardData(
cardNumber: String,
expiryMonth: String,
expiryYear: String,
cvn: String?,
cardholderFirstName: String,
cardholderLastName: String,
cardholderEmail: String,
cardholderPhoneNumber: String,
paymentSessionId: String
): CardsResponseDto

suspend fun collectCvn(
cvn: String,
paymentSessionId: String
): CardsResponseDto

companion object Factory
}

expect fun CardSessions.Factory.create(apiKey: String): CardSessions
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.cards.session.cards.network

import platform.Foundation.NSBundle

actual object NetworkConstants {
actual val BASE_URL: String
get() = if (isDebugBuild()) "https://api.xendit.co/v3" else "https://api.stg.tidnex.dev/v3"

// please check this logic
private fun isDebugBuild(): Boolean {
val bundle = NSBundle.mainBundle
return bundle.infoDictionary?.get("Debug") as? Boolean ?: false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.cards.session.cards.sdk

actual fun CardSessions.Factory.create(apiKey: String): CardSessions {
return CardSessionsImpl.create(apiKey)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cards.session.cards.sdk

object CardSessionsIos {
fun create(apiKey: String): CardSessions {
return CardSessionsImpl.create(apiKey)
}
}
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ assertK = "0.26.1"
turbine = "0.7.0"
rules = "1.6.1"
napier = "2.7.1"
xenditFingerprint = "1.0.0"
xenditFingerprint = "1.0.1"
coroutines = "1.7.3"
kotlinxSerialization = "1.5.1"
desugar = "2.0.4"

[libraries]
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
Expand Down Expand Up @@ -53,6 +54,7 @@ napier = { module = "io.github.aakira:napier", version.ref = "napier" }
xendit-fingerprint = { module = "com.xendit:fingerprint", version.ref = "xenditFingerprint" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
android-desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
1 change: 1 addition & 0 deletions iosApp/iosApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct ContentView: View {
cardNumber: "4242424242424242",
expiryMonth: "12/22",
expiryYear: "2026",
cvn: nil,
cardholderFirstName: "First",
cardholderLastName: "Name",
cardholderEmail: "[email protected]",
Expand Down
Loading

0 comments on commit 3183e1d

Please sign in to comment.