From 06c9c4abea7b9275d9f20c937c55559d8f62b0e7 Mon Sep 17 00:00:00 2001 From: Ighor Moura Date: Fri, 18 Jul 2025 17:16:48 -0400 Subject: [PATCH] adicionando enum de erros --- .idea/codeStyles/Project.xml | 35 +++ .idea/vcs.xml | 6 + app/build.gradle.kts | 7 +- .../com/example/mypos/data/AditumError.kt | 42 ++++ .../mypos/services/AditumSdkService.kt | 231 ++++++++++++++++++ .../mypos/services/PaymentApplication.kt | 84 +++++-- 6 files changed, 381 insertions(+), 24 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 app/src/main/java/com/example/mypos/data/AditumError.kt create mode 100644 app/src/main/java/com/example/mypos/services/AditumSdkService.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7643783..7f70f7e 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,40 @@ + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5670d30..5b748c3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,11 +14,15 @@ android { targetSdk = 36 versionCode = 1 versionName = "1.0" - + buildConfigField ("String", "ACTIVATION_CODE", "\"\"") + buildConfigField ("String", "APPLICATION_TOKEN", "\"\"") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { + debug { + buildConfigField ("String", "APPLICATION_TOKEN", "\"mk_MbQ92RRHEOcGFRIf9/R1A\"") + } release { isMinifyEnabled = false proguardFiles( @@ -43,6 +47,7 @@ android { dependencies { implementation(files("libs/AditumSdkIntegration-2.3.6.67824-release.aar")) + implementation("com.google.code.gson:gson:2.13.1") implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) diff --git a/app/src/main/java/com/example/mypos/data/AditumError.kt b/app/src/main/java/com/example/mypos/data/AditumError.kt new file mode 100644 index 0000000..db9f684 --- /dev/null +++ b/app/src/main/java/com/example/mypos/data/AditumError.kt @@ -0,0 +1,42 @@ +package com.example.mypos.data + +object AditumError { + const val SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE" + const val SERVICE_NOT_AVAILABLE_MESSAGE = "Failed to connect to Aditum SDK service" + + const val SERVICE_NULL = "SERVICE_NULL" + const val SERVICE_NULL_MESSAGE = "Aditum SDK service is null" + + const val INIT_RESPONSE_NULL = "INIT_RESPONSE_NULL" + const val INIT_RESPONSE_NULL_MESSAGE = "Init response is null" + + const val INIT_ERROR = "INIT_ERROR" + const val INIT_ERROR_MESSAGE = "Initialization failed" + + const val MERCHANT_DATA_NULL = "MERCHANT_DATA_NULL" + const val MERCHANT_DATA_NULL_MESSAGE = "Merchant data is null" + + const val MERCHANT_DATA_ERROR = "MERCHANT_DATA_ERROR" + const val MERCHANT_DATA_ERROR_MESSAGE = "Failed to retrieve merchant data" + + const val PAYMENT_RESPONSE_NULL = "PAYMENT_RESPONSE_NULL" + const val PAYMENT_RESPONSE_NULL_MESSAGE = "Payment response is null" + + const val PAYMENT_ERROR = "PAYMENT_ERROR" + const val PAYMENT_ERROR_MESSAGE = "Payment processing failed" + + const val INVALID_NSU = "INVALID_NSU" + const val INVALID_NSU_MESSAGE = "NSU cannot be null" + + const val CONFIRM_ERROR = "CONFIRM_ERROR" + const val CONFIRM_ERROR_MESSAGE = "Transaction confirmation failed" + + const val CANCEL_RESPONSE_NULL = "CANCEL_RESPONSE_NULL" + const val CANCEL_RESPONSE_NULL_MESSAGE = "Cancelation response is null" + + const val CANCEL_ERROR = "CANCEL_ERROR" + const val CANCEL_ERROR_MESSAGE = "Cancelation processing failed" + + const val DEACTIVATE_ERROR = "DEACTIVATE_ERROR" + const val DEACTIVATE_ERROR_MESSAGE = "Deactivation failed" +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mypos/services/AditumSdkService.kt b/app/src/main/java/com/example/mypos/services/AditumSdkService.kt new file mode 100644 index 0000000..21e319b --- /dev/null +++ b/app/src/main/java/com/example/mypos/services/AditumSdkService.kt @@ -0,0 +1,231 @@ +package com.example.mypos.services + +import android.util.Log +import br.com.aditum.data.v2.enums.InstallmentType +import br.com.aditum.data.v2.enums.PayOperationType +import br.com.aditum.data.v2.enums.PaymentType +import br.com.aditum.data.v2.model.MerchantData +import br.com.aditum.data.v2.model.PinpadMessages +import br.com.aditum.data.v2.model.cancelation.CancelationRequest +import br.com.aditum.data.v2.model.cancelation.CancelationResponse +import br.com.aditum.data.v2.model.cancelation.CancelationResponseCallback +import br.com.aditum.data.v2.model.deactivation.DeactivationResponseCallback +import br.com.aditum.data.v2.model.init.InitRequest +import br.com.aditum.data.v2.model.init.InitResponse +import br.com.aditum.data.v2.model.init.InitResponseCallback +import br.com.aditum.data.v2.model.payment.PaymentRequest +import br.com.aditum.data.v2.model.payment.PaymentResponse +import br.com.aditum.data.v2.model.payment.PaymentResponseCallback +import br.com.aditum.data.v2.model.transactions.ConfirmTransactionCallback +import com.example.mypos.BuildConfig +import com.example.mypos.data.AditumError +import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.util.UUID +class AditumSdkService(private val paymentApplication: PaymentApplication) { + companion object { + private const val TAG = "AditumSdkModule" + } + + private val gson = Gson() + private val coroutineScope = CoroutineScope(Dispatchers.IO) + + fun getName(): String = "AditumSdkModule" + + fun initAditumSdk( + applicationName: String, + applicationVersion: String, + activationCode: String?, + resolve: (Boolean) -> Unit = {}, + reject: (String, String?) -> Unit = { _, _ -> } + ) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + val pinpadMessages = PinpadMessages().apply { + mainMessage = applicationName + } + + val initRequest = InitRequest().apply { + this.pinpadMessages = pinpadMessages + this.activationCode = if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else activationCode + this.applicationName = applicationName + this.applicationVersion = applicationVersion + this.applicationToken = BuildConfig.APPLICATION_TOKEN + } + + val callback = object : InitResponseCallback.Stub() { + override fun onResponse(initResponse: InitResponse?) { + if (initResponse != null) { + resolve(initResponse.initialized) + } else { + Log.e(TAG, "onResponse - initResponse is null") + reject(AditumError.INIT_RESPONSE_NULL, AditumError.INIT_RESPONSE_NULL_MESSAGE) + } + } + } + + paymentApplication.communicationService?.init(initRequest, callback) + ?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE) + } catch (e: Exception) { + reject(AditumError.INIT_ERROR, "${AditumError.INIT_ERROR_MESSAGE}: ${e.message}") + } + } + } + + fun getMerchantData(resolve: (MerchantData) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + val merchantData: MerchantData? = paymentApplication.communicationService?.merchantData + if (merchantData != null) { + resolve(merchantData) + } else { + reject(AditumError.MERCHANT_DATA_NULL, AditumError.MERCHANT_DATA_NULL_MESSAGE) + } + } catch (e: Exception) { + reject(AditumError.MERCHANT_DATA_ERROR, "${AditumError.MERCHANT_DATA_ERROR_MESSAGE}: ${e.message}") + } + } + } + + fun pay( + amount: Int, + installments: Int, + merchantChargeId: String?, + resolve: (Any) -> Unit = {}, + reject: (String, String?) -> Unit = { _, _ -> } + ) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + val paymentRequest = PaymentRequest().apply { + operationType = PayOperationType.Authorization + paymentType = PaymentType.Credit + this.amount = amount.toLong() + this.merchantChargeId = merchantChargeId ?: UUID.randomUUID().toString() + currency = 986 + allowContactless = true + manualEntry = false + installmentType = InstallmentType.Merchant + installmentNumber = installments + } + + val callback = object : PaymentResponseCallback.Stub() { + override fun onResponse(paymentResponse: PaymentResponse?) { + if (paymentResponse != null) { + val json = gson.toJson(paymentResponse) + resolve(json) + } else { + Log.e(TAG, "onResponse - paymentResponse is null") + reject(AditumError.PAYMENT_RESPONSE_NULL, AditumError.PAYMENT_RESPONSE_NULL_MESSAGE) + } + } + } + + paymentApplication.communicationService?.pay(paymentRequest, callback) + ?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE) + } catch (e: Exception) { + reject(AditumError.PAYMENT_ERROR, "${AditumError.PAYMENT_ERROR_MESSAGE}: ${e.message}") + } + } + } + + fun confirm(nsu: String?, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + if (nsu == null) { + reject(AditumError.INVALID_NSU, AditumError.INVALID_NSU_MESSAGE) + return@launch + } + + val callback = object : ConfirmTransactionCallback.Stub() { + override fun onResponse(confirmed: Boolean) { + resolve(confirmed) + } + } + + paymentApplication.communicationService?.confirmTransaction(nsu, callback) + ?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE) + } catch (e: Exception) { + reject(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}") + } + } + } + + fun cancel(nsu: String?, isReversal: Boolean, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + if (nsu == null) { + reject(AditumError.INVALID_NSU, AditumError.INVALID_NSU_MESSAGE) + return@launch + } + + val cancelationRequest = CancelationRequest(nsu, isReversal) + + val callback = object : CancelationResponseCallback.Stub() { + override fun onResponse(cancelationResponse: CancelationResponse?) { + if (cancelationResponse != null) { + resolve(cancelationResponse.canceled) + } else { + Log.e(TAG, "onResponse - cancelationResponse is null") + reject(AditumError.CANCEL_RESPONSE_NULL, AditumError.CANCEL_RESPONSE_NULL_MESSAGE) + } + } + } + + paymentApplication.communicationService?.cancel(cancelationRequest, callback) + ?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE) + } catch (e: Exception) { + reject(AditumError.CANCEL_ERROR, "${AditumError.CANCEL_ERROR_MESSAGE}: ${e.message}") + } + } + } + + fun deactivate(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) { + coroutineScope.launch { + try { + if (!paymentApplication.ensureServiceConnected()) { + reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE) + return@launch + } + + val callback = object : DeactivationResponseCallback.Stub() { + override fun onResponse(status: Boolean) { + Log.d(TAG, "onDeactivationResponse - deactivationResponse: $status") + resolve(status) + } + } + + paymentApplication.communicationService?.deactivate(callback) + ?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE) + } catch (e: Exception) { + reject(AditumError.DEACTIVATE_ERROR, "${AditumError.DEACTIVATE_ERROR_MESSAGE}: ${e.message}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mypos/services/PaymentApplication.kt b/app/src/main/java/com/example/mypos/services/PaymentApplication.kt index f91ed1f..baa7002 100644 --- a/app/src/main/java/com/example/mypos/services/PaymentApplication.kt +++ b/app/src/main/java/com/example/mypos/services/PaymentApplication.kt @@ -8,19 +8,22 @@ import android.content.ServiceConnection import android.os.Build import android.os.IBinder import android.util.Log - import br.com.aditum.IAditumSdkService import br.com.aditum.data.v2.model.MerchantData +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.withContext class PaymentApplication : Application() { - public val TAG = PaymentApplication::class.simpleName - - public val PACKAGE_BASE_NAME: String = "br.com.aditum" - public val PACKAGE_NAME: String = PACKAGE_BASE_NAME + ".smartpostef" - public val ACTION_COMMUNICATION_SERVICE: String = PACKAGE_BASE_NAME + ".AditumSdkService" + companion object { + private const val TAG = "PaymentApplication" + private const val PACKAGE_BASE_NAME = "br.com.aditum" + const val PACKAGE_NAME = "$PACKAGE_BASE_NAME.smartpostef" + const val ACTION_COMMUNICATION_SERVICE = "$PACKAGE_BASE_NAME.AditumSdkService" + } private val _isServiceConnectedFlow = MutableStateFlow(false) val isServiceConnectedFlow: StateFlow = _isServiceConnectedFlow.asStateFlow() @@ -36,7 +39,7 @@ class PaymentApplication : Application() { get() = mAditumSdkService @Volatile - public var merchantData: MerchantData? = null + var merchantData: MerchantData? = null private val mServiceConnection = object : ServiceConnection { override fun onServiceConnected(componentName: ComponentName, service: IBinder) { @@ -59,40 +62,75 @@ class PaymentApplication : Application() { mServiceConnectionListener = listener } - public interface OnServiceConnectionListener { + interface OnServiceConnectionListener { fun onServiceConnection(serviceConnected: Boolean) } override fun onCreate() { super.onCreate() - Log.d(TAG, "onCreate") + startAditumSdkService() } override fun onTerminate() { super.onTerminate() - Log.d(TAG, "onTerminate") } - public fun startAditumSdkService() { + fun startAditumSdkService() { Log.d(TAG, "startAditumSdkService") - - val intent: Intent = Intent(ACTION_COMMUNICATION_SERVICE) - intent.setPackage(PACKAGE_NAME) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Log.d(TAG, "Android Oreo or higher: " + Build.VERSION.SDK_INT); - startForegroundService(intent); - } else { - Log.d(TAG, "Android Nougat or lower: " + Build.VERSION.SDK_INT); - startService(intent); + val intent = Intent(ACTION_COMMUNICATION_SERVICE).apply { + setPackage(PACKAGE_NAME) } - bindService(intent, mServiceConnection, (BIND_AUTO_CREATE or CONTEXT_IGNORE_SECURITY)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Log.d(TAG, "Android Oreo or higher: ${Build.VERSION.SDK_INT}") + startForegroundService(intent) + } else { + Log.d(TAG, "Android Nougat or lower: ${Build.VERSION.SDK_INT}") + startService(intent) + } + + bindService(intent, mServiceConnection, BIND_AUTO_CREATE or CONTEXT_IGNORE_SECURITY) Log.d(TAG, "startAditumSdkService - bindService") } + suspend fun ensureServiceConnected(): Boolean = withContext(Dispatchers.IO) { + if (isServiceConnected && mAditumSdkService?.asBinder()?.isBinderAlive == true) { + Log.d(TAG, "Service already connected") + return@withContext true + } + + Log.d(TAG, "Service not connected, attempting to reconnect...") + val intent = Intent(ACTION_COMMUNICATION_SERVICE).apply { + setPackage(PACKAGE_NAME) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(intent) + } else { + startService(intent) + } + + val bound = bindService( + intent, + mServiceConnection, + BIND_AUTO_CREATE or CONTEXT_IGNORE_SECURITY + ) + + if (bound) { + repeat(30) { + delay(100) + if (isServiceConnected && mAditumSdkService?.asBinder()?.isBinderAlive == true) { + Log.d(TAG, "Service reconnected successfully") + return@withContext true + } + } + } + + Log.e(TAG, "Failed to reconnect service") + return@withContext false + } private fun setServiceConnected(isConnected: Boolean) { Log.d(TAG, "setServiceConnected - isConnected: $isConnected") @@ -102,4 +140,4 @@ class PaymentApplication : Application() { mServiceConnectionListener?.onServiceConnection(mIsServiceConnected) } } -} +} \ No newline at end of file