From 6df08424555cc0c9f7ecbadf0d681b33a3570564 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 16:25:19 +0800 Subject: [PATCH 1/7] update app --- README.md | 60 +++++++- .../session/cards/sdk/CardSessionsImpl.kt | 143 ++++++++++++++++++ .../session/cards/sdk/CardSessionsIos.kt | 7 - iosApp/iosApp.xcodeproj/project.pbxproj | 12 +- iosApp/iosApp/ContentView.swift | 75 +++++---- 5 files changed, 250 insertions(+), 47 deletions(-) create mode 100644 cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt delete mode 100644 cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsIos.kt diff --git a/README.md b/README.md index f5c3278..398d64c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Xendit Cards SDK -A lightweight SDK for integrating card payments into Android applications. This SDK provides secure card data collection functionality with built-in validation and 3DS support. +A lightweight SDK for integrating card payments into Android and iOS applications. This SDK provides secure card data collection functionality with built-in validation and 3DS support. ## Features @@ -12,7 +12,7 @@ A lightweight SDK for integrating card payments into Android applications. This ## Installation -### Gradle +### Android - Gradle Add the following to your app's `build.gradle.kts`: @@ -26,8 +26,7 @@ dependencies { ### Initialize the SDK -First, initialize the CardSessions instance with your Xendit public key: - +#### Android ```kotlin val cardSessions = CardSessions.create( context = context, @@ -35,8 +34,16 @@ val cardSessions = CardSessions.create( ) ``` +#### iOS +```swift +let cardSessions = CardSessionsFactory().create( + apiKey: "xnd_public_development_YOUR_KEY_HERE" +) +``` + ### Collect Card Data +#### Android ```kotlin // Collect complete card information val response = cardSessions.collectCardData( @@ -52,10 +59,27 @@ val response = cardSessions.collectCardData( ) ``` +#### iOS +```swift +// Using async/await +let response = try await cardSessions.collectCardData( + cardNumber: "4000000000001091", + expiryMonth: "12", + expiryYear: "2025", + cvn: "123", // Optional + cardholderFirstName: "John", + cardholderLastName: "Doe", + cardholderEmail: "john@example.com", + cardholderPhoneNumber: "+1234567890", + paymentSessionId: "ps-1234567890" +) +``` + ### Collect CVN Only For saved cards where you only need to collect the CVN: +#### Android ```kotlin val response = cardSessions.collectCvn( cvn = "123", @@ -63,10 +87,17 @@ val response = cardSessions.collectCvn( ) ``` -### Monitor Session State +#### iOS +```swift +let response = try await cardSessions.collectCvn( + cvn: "123", + paymentSessionId: "ps-1234567890" +) +``` -The SDK provides a StateFlow to monitor the current state of operations: +### Monitor Session State +#### Android ```kotlin cardSessions.state.collect { state -> when { @@ -77,6 +108,21 @@ cardSessions.state.collect { state -> } ``` +#### iOS +```swift +// Using Combine +cardSessions.state + .sink { state in + if state.isLoading { + // Show loading state + } else if let error = state.error { + // Handle error + } else if let response = state.cardResponse { + // Handle success + } + } +``` + ## Response Types ### Success Response @@ -107,4 +153,6 @@ The SDK automatically handles: ## Requirements - Android API level 21 or higher +- iOS 13.0 or higher +- Swift 5.5 or higher diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt new file mode 100644 index 0000000..c75f0a4 --- /dev/null +++ b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt @@ -0,0 +1,143 @@ +package com.cards.session.cards.sdk + +import com.cards.session.cards.models.CardsRequestDto +import com.cards.session.cards.models.CardsResponseDto +import com.cards.session.cards.models.DeviceFingerprint +import com.cards.session.cards.network.CardsSessionError.UNKNOWN_ERROR +import com.cards.session.cards.network.CardsSessionException +import com.cards.session.cards.network.KtorCardsClient +import com.cards.session.cards.ui.CardSessionState +import com.cards.session.network.HttpClientFactory +import com.cards.session.util.AuthTokenGenerator +import com.cardsession.sdk.CreditCardUtil +import io.ktor.client.HttpClient +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import platform.Foundation.NSLog + +internal class CardSessionsImpl private constructor( + private val apiKey: String, + private val httpClient: HttpClient +) : CardSessions { + private val TAG = "CardSessionsImpl" + private val client = KtorCardsClient(httpClient) + private val _state = MutableStateFlow(CardSessionState()) + override val state: StateFlow = _state.asStateFlow() + + override suspend fun collectCardData( + cardNumber: String, + expiryMonth: String, + expiryYear: String, + cvn: String?, + cardholderFirstName: String, + cardholderLastName: String, + cardholderEmail: String, + cardholderPhoneNumber: String, + paymentSessionId: String + ): CardsResponseDto { + _state.update { it.copy(isLoading = true, exception = null) } + + try { + // Validate card data + when { + !CreditCardUtil.isCreditCardNumberValid(cardNumber) -> { + throw IllegalArgumentException("Card number is invalid") + } + + !CreditCardUtil.isCreditCardExpirationDateValid(expiryMonth, expiryYear) -> { + throw IllegalArgumentException("Card expiration date is invalid") + } + + !CreditCardUtil.isCreditCardCVNValid(cvn) -> { + throw IllegalArgumentException("Card CVN is invalid") + } + } + + val deviceFingerprint = getFingerprint("collect_card_data") + val request = CardsRequestDto( + cardNumber = cardNumber, + expiryMonth = expiryMonth, + expiryYear = expiryYear, + cvn = cvn, + cardholderFirstName = cardholderFirstName, + cardholderLastName = cardholderLastName, + cardholderEmail = cardholderEmail, + cardholderPhoneNumber = cardholderPhoneNumber, + paymentSessionId = paymentSessionId, + device = DeviceFingerprint(deviceFingerprint) + ) + + val authToken = AuthTokenGenerator.generateAuthToken(apiKey) + val response = client.paymentWithSession(request, authToken) + _state.update { it.copy(isLoading = false, cardResponse = response) } + return response + } catch (e: CardsSessionException) { + NSLog("$TAG: API request failed: ${e.message}") + _state.update { CardSessionState(isLoading = false, exception = e) } + return CardsResponseDto(message = e.message ?: "Unknown error") + } catch (e: Exception) { + NSLog("$TAG: API request failed: ${e.message}") + _state.update { + CardSessionState( + isLoading = false, + exception = CardsSessionException(errorCode = UNKNOWN_ERROR, e.message ?: "Unknown error") + ) + } + return CardsResponseDto(message = e.message ?: "Unknown error") + } + } + + override suspend fun collectCvn( + cvn: String, + paymentSessionId: String + ): CardsResponseDto { + _state.update { it.copy(isLoading = true, exception = null) } + + try { + if (!CreditCardUtil.isCreditCardCVNValid(cvn)) { + throw IllegalArgumentException("Card CVN is invalid") + } + + val deviceFingerprint = getFingerprint("collect_cvn") + val request = CardsRequestDto( + cvn = cvn, + paymentSessionId = paymentSessionId, + device = DeviceFingerprint(deviceFingerprint) + ) + + val authToken = AuthTokenGenerator.generateAuthToken(apiKey) + val response = client.paymentWithSession(request, authToken) + _state.update { it.copy(isLoading = false, cardResponse = response) } + return response + } catch (e: CardsSessionException) { + NSLog("$TAG: API request failed: ${e.message}") + _state.update { CardSessionState(isLoading = false, exception = e) } + return CardsResponseDto(message = e.message ?: "Unknown error") + } catch (e: Exception) { + NSLog("$TAG: API request failed: ${e.message}") + _state.update { + CardSessionState( + isLoading = false, + exception = CardsSessionException(errorCode = UNKNOWN_ERROR, e.message ?: "Unknown error") + ) + } + return CardsResponseDto(message = e.message ?: "Unknown error") + } + } + + private fun getFingerprint(eventName: String): String { + // TODO: Implement iOS-specific device fingerprinting + return "" + } + + companion object { + fun create(apiKey: String): CardSessions { + return CardSessionsImpl( + apiKey = apiKey, + httpClient = HttpClientFactory().create() + ) + } + } +} \ No newline at end of file diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsIos.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsIos.kt deleted file mode 100644 index 35377bc..0000000 --- a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsIos.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.cards.session.cards.sdk - -object CardSessionsIos { - fun create(apiKey: String): CardSessions { - return CardSessionsImpl.create(apiKey) - } -} \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index d311567..fbb55b7 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -9,7 +9,8 @@ /* Begin PBXBuildFile section */ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; - 0CE1FDA22CF6FE6500E5B89D /* IOSCardSessionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE1FDA12CF6FE6500E5B89D /* IOSCardSessionViewModel.swift */; }; + 0C0CDBC02D06AD03002DC16A /* CardSessionsImpl.kt in Resources */ = {isa = PBXBuildFile; fileRef = 0C0CDBBE2D06AD03002DC16A /* CardSessionsImpl.kt */; }; + 0C0CDBC62D06DB5C002DC16A /* CardSessions.kt in Resources */ = {isa = PBXBuildFile; fileRef = 0C0CDBC52D06DB5C002DC16A /* CardSessions.kt */; }; 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; 2F9A03F732FC062E0B94E281 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E47D7C75F016D7EF416763 /* Pods_iosApp.framework */; }; 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; @@ -18,7 +19,8 @@ /* Begin PBXFileReference section */ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 0CE1FDA12CF6FE6500E5B89D /* IOSCardSessionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOSCardSessionViewModel.swift; sourceTree = ""; }; + 0C0CDBBE2D06AD03002DC16A /* CardSessionsImpl.kt */ = {isa = PBXFileReference; lastKnownFileType = text; name = CardSessionsImpl.kt; path = ../cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt; sourceTree = SOURCE_ROOT; }; + 0C0CDBC52D06DB5C002DC16A /* CardSessions.kt */ = {isa = PBXFileReference; lastKnownFileType = text; name = CardSessions.kt; path = ../cardsSdk/src/commonMain/kotlin/com/cards/session/cards/sdk/CardSessions.kt; sourceTree = SOURCE_ROOT; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; 42AF08E65F4330F0220806A5 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; }; 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -51,6 +53,7 @@ 7555FF72242A565900829871 = { isa = PBXGroup; children = ( + 0C0CDBC52D06DB5C002DC16A /* CardSessions.kt */, 7555FF7D242A565900829871 /* iosApp */, 7555FF7C242A565900829871 /* Products */, EF9E122CF5BA2B8CAD48CD1D /* Pods */, @@ -69,7 +72,7 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( - 0CE1FDA12CF6FE6500E5B89D /* IOSCardSessionViewModel.swift */, + 0C0CDBBE2D06AD03002DC16A /* CardSessionsImpl.kt */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, 7555FF82242A565900829871 /* ContentView.swift */, 7555FF8C242A565B00829871 /* Info.plist */, @@ -156,6 +159,8 @@ buildActionMask = 2147483647; files = ( 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 0C0CDBC62D06DB5C002DC16A /* CardSessions.kt in Resources */, + 0C0CDBC02D06AD03002DC16A /* CardSessionsImpl.kt in Resources */, 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -193,7 +198,6 @@ buildActionMask = 2147483647; files = ( 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, - 0CE1FDA22CF6FE6500E5B89D /* IOSCardSessionViewModel.swift in Sources */, 7555FF83242A565900829871 /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index c0b118b..a62796a 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -3,13 +3,16 @@ import cardsSdk struct ContentView: View { let appModule: AppModule - @StateObject private var viewModel: IOSCardSessionViewModel + private let cardSessions: CardSessions + @State private var isLoading = false + @State private var error: String? + @State private var cardResponse: String? init(appModule: AppModule) { self.appModule = appModule - _viewModel = StateObject(wrappedValue: IOSCardSessionViewModel( - cardsPaymentSession: appModule.cardsPaymentSession - )) + self.cardSessions = CardSessionsFactory().create( + apiKey: "xnd_public_development_1TFSZ1eyExAAhfR48LRlbTIH2WFT6jjvoLDFsjQUV3lJWuNghjCMJakP0hq0ZCV" + ) } var body: some View { @@ -21,50 +24,62 @@ struct ContentView: View { .frame(maxWidth: .infinity) Button(action: { - viewModel.onEvent(event: CardSessionEvent.CollectCardData( - cardNumber: "4242424242424242", - expiryMonth: "12/22", - expiryYear: "2026", - cvn: nil, - cardholderFirstName: "First", - cardholderLastName: "Name", - cardholderEmail: "firstname@xendit.co", - cardholderPhoneNumber: "01231245242", - paymentSessionId: "1234567890", - deviceFingerprint: "1234567890" - )) + Task { + isLoading = true + error = nil + do { + let response = try await cardSessions.collectCardData( + cardNumber: "4242424242424242", + expiryMonth: "12", + expiryYear: "2026", + cvn: nil, + cardholderFirstName: "First", + cardholderLastName: "Name", + cardholderEmail: "firstname@xendit.co", + cardholderPhoneNumber: "01231245242", + paymentSessionId: "session_id MUST be 27 chars" + ) + cardResponse = response.description + } catch { + self.error = error.localizedDescription + } + isLoading = false + } }) { Text("Collect Card Data") } Button(action: { - viewModel.onEvent(event: CardSessionEvent.CollectCvn( - cvn: "123", - paymentSessionId: "1234567890", - deviceFingerprint: "1234567890" - )) + Task { + isLoading = true + error = nil + do { + let response = try await cardSessions.collectCvn( + cvn: "123", + paymentSessionId: "1234567890" + ) + cardResponse = response.description + } catch { + self.error = error.localizedDescription + } + isLoading = false + } }) { Text("Collect CVN") } - if viewModel.state.isLoading { + if isLoading { ProgressView() - } else if let error = viewModel.state.error { + } else if let error = error { Text("Error: \(error)") .foregroundColor(.red) - } else if let response = viewModel.state.cardResponse { + } else if let response = cardResponse { Text("Card Response: \(response)") } Spacer() } .padding() - .onAppear { - viewModel.startObserving() - } - .onDisappear { - viewModel.dispose() - } } } From 49a7ce37bd2c71ba8849fd8e6024a54ec48ddd67 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:06:37 +0800 Subject: [PATCH 2/7] update logger for ios --- .../kotlin/com/cards/session/util/Logger.kt | 21 +++++++++++++++++++ .../kotlin/com/cards/session/util/Logger.kt | 21 +++++-------------- .../kotlin/com/cards/session/util/Logger.kt | 21 +++++++++++++++++++ 3 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt create mode 100644 cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt diff --git a/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt new file mode 100644 index 0000000..e0cf936 --- /dev/null +++ b/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt @@ -0,0 +1,21 @@ +package com.cards.session.util + +import io.github.aakira.napier.Napier + +actual class Logger actual constructor(private val tag: String) { + actual fun d(message: String, throwable: Throwable?) { + Napier.d(message, throwable, tag) + } + + actual fun i(message: String, throwable: Throwable?) { + Napier.i(message, throwable, tag) + } + + actual fun w(message: String, throwable: Throwable?) { + Napier.w(message, throwable, tag) + } + + actual fun e(message: String, throwable: Throwable?) { + Napier.e(message, throwable, tag) + } +} \ No newline at end of file diff --git a/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt index bd8bf95..6187cbe 100644 --- a/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt +++ b/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt @@ -2,20 +2,9 @@ package com.cards.session.util import io.github.aakira.napier.Napier -class Logger(private val tag: String) { - fun d(message: String, throwable: Throwable? = null) { - Napier.d(message, throwable, tag) - } - - fun i(message: String, throwable: Throwable? = null) { - Napier.i(message, throwable, tag) - } - - fun w(message: String, throwable: Throwable? = null) { - Napier.w(message, throwable, tag) - } - - fun e(message: String, throwable: Throwable? = null) { - Napier.e(message, throwable, tag) - } +expect class Logger(tag: String) { + fun d(message: String, throwable: Throwable? = null) + fun i(message: String, throwable: Throwable? = null) + fun w(message: String, throwable: Throwable? = null) + fun e(message: String, throwable: Throwable? = null) } \ No newline at end of file diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt new file mode 100644 index 0000000..57de16e --- /dev/null +++ b/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt @@ -0,0 +1,21 @@ +package com.cards.session.util + +import platform.Foundation.NSLog + +actual class Logger actual constructor(private val tag: String) { + actual fun d(message: String, throwable: Throwable?) { + NSLog("$tag [DEBUG]: $message${throwable?.let { "\n$it" } ?: ""}") + } + + actual fun i(message: String, throwable: Throwable?) { + NSLog("$tag [INFO]: $message${throwable?.let { "\n$it" } ?: ""}") + } + + actual fun w(message: String, throwable: Throwable?) { + NSLog("$tag [WARN]: $message${throwable?.let { "\n$it" } ?: ""}") + } + + actual fun e(message: String, throwable: Throwable?) { + NSLog("$tag [ERROR]: $message${throwable?.let { "\n$it" } ?: ""}") + } +} \ No newline at end of file From 55aec8bb00e673f50676d768d2e39a13db7530c7 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:06:47 +0800 Subject: [PATCH 3/7] update demo string --- .../src/main/java/com/cards/session/android/MainActivity.kt | 4 ++-- iosApp/iosApp/ContentView.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/androidApp/src/main/java/com/cards/session/android/MainActivity.kt b/androidApp/src/main/java/com/cards/session/android/MainActivity.kt index 17672be..24edb06 100644 --- a/androidApp/src/main/java/com/cards/session/android/MainActivity.kt +++ b/androidApp/src/main/java/com/cards/session/android/MainActivity.kt @@ -92,7 +92,7 @@ fun AppRoot() { cardholderLastName = "Name", cardholderEmail = "firstname@xendit.co", cardholderPhoneNumber = "01231245242", - paymentSessionId = "session_id MUST be 27 chars" + paymentSessionId = "ps-1234567890abcdef12345678" ) } } @@ -104,7 +104,7 @@ fun AppRoot() { scope.launch { cardSessions.collectCvn( cvn = "123", - paymentSessionId = "1234567890" + paymentSessionId = "ps-1234567890abcdef12345678" ) } } diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index a62796a..e507b0c 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -37,7 +37,7 @@ struct ContentView: View { cardholderLastName: "Name", cardholderEmail: "firstname@xendit.co", cardholderPhoneNumber: "01231245242", - paymentSessionId: "session_id MUST be 27 chars" + paymentSessionId: "ps-1234567890abcdef12345678" ) cardResponse = response.description } catch { @@ -56,7 +56,7 @@ struct ContentView: View { do { let response = try await cardSessions.collectCvn( cvn: "123", - paymentSessionId: "1234567890" + paymentSessionId: "ps-1234567890abcdef12345678" ) cardResponse = response.description } catch { From 83e316f53515735465de2d68f18a5510aca818d2 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:07:36 +0800 Subject: [PATCH 4/7] remove API key --- iosApp/iosApp/ContentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index e507b0c..4f5aa5f 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -11,7 +11,7 @@ struct ContentView: View { init(appModule: AppModule) { self.appModule = appModule self.cardSessions = CardSessionsFactory().create( - apiKey: "xnd_public_development_1TFSZ1eyExAAhfR48LRlbTIH2WFT6jjvoLDFsjQUV3lJWuNghjCMJakP0hq0ZCV" + apiKey: "API_KEY_HERE" ) } From 991fb6b3a4b29299730b9d327ea690d91e2fac3f Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:09:42 +0800 Subject: [PATCH 5/7] fix debug to staging url --- .../kotlin/com/cards/session/cards/network/NetworkConstants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/network/NetworkConstants.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/network/NetworkConstants.kt index 107e3e3..172780c 100644 --- a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/network/NetworkConstants.kt +++ b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/network/NetworkConstants.kt @@ -4,7 +4,7 @@ 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" + get() = if (isDebugBuild()) "https://api.stg.tidnex.dev/v3" else "https://api.xendit.co/v3" // please check this logic private fun isDebugBuild(): Boolean { From d56a588c1f29e7ff3870c745bd1c563ca4f39c07 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:52:46 +0800 Subject: [PATCH 6/7] change from NSLog --- .../kotlin/com/cards/session/util/Logger.kt | 1 + .../session/cards/sdk/CardSessionsImpl.kt | 13 +++++--- .../kotlin/com/cards/session/util/Logger.kt | 33 +++++++++++-------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt index 6187cbe..32695b5 100644 --- a/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt +++ b/cardsSdk/src/commonMain/kotlin/com/cards/session/util/Logger.kt @@ -7,4 +7,5 @@ expect class Logger(tag: String) { fun i(message: String, throwable: Throwable? = null) fun w(message: String, throwable: Throwable? = null) fun e(message: String, throwable: Throwable? = null) + fun debugBuild() } \ No newline at end of file diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt index c75f0a4..7073c67 100644 --- a/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt +++ b/cardsSdk/src/iosMain/kotlin/com/cards/session/cards/sdk/CardSessionsImpl.kt @@ -9,6 +9,7 @@ import com.cards.session.cards.network.KtorCardsClient import com.cards.session.cards.ui.CardSessionState import com.cards.session.network.HttpClientFactory import com.cards.session.util.AuthTokenGenerator +import com.cards.session.util.Logger import com.cardsession.sdk.CreditCardUtil import io.ktor.client.HttpClient import kotlinx.coroutines.flow.MutableStateFlow @@ -22,6 +23,7 @@ internal class CardSessionsImpl private constructor( private val httpClient: HttpClient ) : CardSessions { private val TAG = "CardSessionsImpl" + private val logger = Logger(TAG) private val client = KtorCardsClient(httpClient) private val _state = MutableStateFlow(CardSessionState()) override val state: StateFlow = _state.asStateFlow() @@ -74,11 +76,11 @@ internal class CardSessionsImpl private constructor( _state.update { it.copy(isLoading = false, cardResponse = response) } return response } catch (e: CardsSessionException) { - NSLog("$TAG: API request failed: ${e.message}") + logger.e("API request failed: ${e.message}") _state.update { CardSessionState(isLoading = false, exception = e) } return CardsResponseDto(message = e.message ?: "Unknown error") } catch (e: Exception) { - NSLog("$TAG: API request failed: ${e.message}") + logger.e("API request failed: ${e.message}") _state.update { CardSessionState( isLoading = false, @@ -112,11 +114,11 @@ internal class CardSessionsImpl private constructor( _state.update { it.copy(isLoading = false, cardResponse = response) } return response } catch (e: CardsSessionException) { - NSLog("$TAG: API request failed: ${e.message}") + logger.e("API request failed: ${e.message}") _state.update { CardSessionState(isLoading = false, exception = e) } return CardsResponseDto(message = e.message ?: "Unknown error") } catch (e: Exception) { - NSLog("$TAG: API request failed: ${e.message}") + logger.e("API request failed: ${e.message}") _state.update { CardSessionState( isLoading = false, @@ -134,10 +136,11 @@ internal class CardSessionsImpl private constructor( companion object { fun create(apiKey: String): CardSessions { + Logger("").debugBuild() return CardSessionsImpl( apiKey = apiKey, httpClient = HttpClientFactory().create() ) } } -} \ No newline at end of file +} diff --git a/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt index 57de16e..9524d49 100644 --- a/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt +++ b/cardsSdk/src/iosMain/kotlin/com/cards/session/util/Logger.kt @@ -1,21 +1,26 @@ package com.cards.session.util -import platform.Foundation.NSLog +import io.github.aakira.napier.DebugAntilog +import io.github.aakira.napier.Napier actual class Logger actual constructor(private val tag: String) { - actual fun d(message: String, throwable: Throwable?) { - NSLog("$tag [DEBUG]: $message${throwable?.let { "\n$it" } ?: ""}") - } + actual fun d(message: String, throwable: Throwable?) { + Napier.d(message = "$message${throwable?.let { "\n$it" } ?: ""}", tag = tag) + } - actual fun i(message: String, throwable: Throwable?) { - NSLog("$tag [INFO]: $message${throwable?.let { "\n$it" } ?: ""}") - } + actual fun i(message: String, throwable: Throwable?) { + Napier.i(message = "$message${throwable?.let { "\n$it" } ?: ""}", tag = tag) + } - actual fun w(message: String, throwable: Throwable?) { - NSLog("$tag [WARN]: $message${throwable?.let { "\n$it" } ?: ""}") - } + actual fun w(message: String, throwable: Throwable?) { + Napier.w(message = "$message${throwable?.let { "\n$it" } ?: ""}", tag = tag) + } - actual fun e(message: String, throwable: Throwable?) { - NSLog("$tag [ERROR]: $message${throwable?.let { "\n$it" } ?: ""}") - } -} \ No newline at end of file + actual fun e(message: String, throwable: Throwable?) { + Napier.e(message = "$message${throwable?.let { "\n$it" } ?: ""}", tag = tag) + } + + actual fun debugBuild() { + Napier.base(DebugAntilog()) + } +} \ No newline at end of file From 521d2cc9e9a1962e6846ef55b47cfe835695e777 Mon Sep 17 00:00:00 2001 From: Muz Date: Mon, 9 Dec 2024 17:53:42 +0800 Subject: [PATCH 7/7] debug --- .../src/androidMain/kotlin/com/cards/session/util/Logger.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt b/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt index e0cf936..e73df17 100644 --- a/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt +++ b/cardsSdk/src/androidMain/kotlin/com/cards/session/util/Logger.kt @@ -18,4 +18,7 @@ actual class Logger actual constructor(private val tag: String) { actual fun e(message: String, throwable: Throwable?) { Napier.e(message, throwable, tag) } + + actual fun debugBuild() { + } } \ No newline at end of file