From 3fe1c6e4315075957c6882f2878878d2976303f6 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Fri, 2 Aug 2024 12:47:26 +0200 Subject: [PATCH 01/25] refactor existing openPaymentInputView method --- .../sdk/ui/payment/PaymentInputViewHelper.kt | 75 +++++++------------ 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt index 9358320daf..c1f5f8c162 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt @@ -20,61 +20,42 @@ object PaymentInputViewHelper { @JvmStatic fun openPaymentInputView(context: Context, paymentMethod: PaymentMethod?, projectId: String) { if (KeyguardUtils.isDeviceSecure()) { - val project = Snabble.getProjectById(projectId) - val acceptedOriginTypes = project?.paymentMethodDescriptors - ?.firstOrNull { it.paymentMethod == paymentMethod }?.acceptedOriginTypes.orEmpty() + val project = Snabble.getProjectById(projectId) ?: return + if (paymentMethod == null) { + Logger.e("Payment method requires no credentials or is unsupported") + return + } + val acceptedOriginTypes = project.paymentMethodDescriptors + .firstOrNull { it.paymentMethod == paymentMethod }?.acceptedOriginTypes.orEmpty() val useDatatrans = acceptedOriginTypes.any { it == "datatransAlias" || it == "datatransCreditCardAlias" } val usePayone = acceptedOriginTypes.any { it == "payonePseudoCardPAN" } + val useFiserv = acceptedOriginTypes.any { it == "ipgHostedDataID" } val activity = UIUtils.getHostFragmentActivity(context) val args = Bundle() - if (project != null) { - if (useDatatrans && paymentMethod != null) { - Datatrans.registerCard(activity, project, paymentMethod) - } else if (usePayone && paymentMethod != null) { - Payone.registerCard(activity, project, paymentMethod, Snabble.formPrefillData) - } else { - when (paymentMethod) { - PaymentMethod.VISA -> { - args.putString(CreditCardInputView.ARG_PROJECT_ID, projectId) - args.putSerializable(CreditCardInputView.ARG_PAYMENT_TYPE, PaymentMethod.VISA) - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) - } - - PaymentMethod.AMEX -> { - args.putString(CreditCardInputView.ARG_PROJECT_ID, projectId) - args.putSerializable(CreditCardInputView.ARG_PAYMENT_TYPE, PaymentMethod.AMEX) - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) - } - - PaymentMethod.MASTERCARD -> { - args.putString(CreditCardInputView.ARG_PROJECT_ID, projectId) - args.putSerializable(CreditCardInputView.ARG_PAYMENT_TYPE, PaymentMethod.MASTERCARD) - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) - } - - PaymentMethod.GIROPAY -> { - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_GIROPAY_INPUT) - } - - PaymentMethod.DE_DIRECT_DEBIT -> { - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_SEPA_CARD_INPUT) - } - - PaymentMethod.PAYONE_SEPA -> { - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_PAYONE_SEPA) - } + when { + useDatatrans -> Datatrans.registerCard(activity, project, paymentMethod) + usePayone -> Payone.registerCard(activity, project, paymentMethod, Snabble.formPrefillData) + useFiserv -> { + args.putString(CreditCardInputView.ARG_PROJECT_ID, projectId) + args.putSerializable(CreditCardInputView.ARG_PAYMENT_TYPE, paymentMethod) + SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) + } + paymentMethod == PaymentMethod.EXTERNAL_BILLING -> { + args.putString(ARG_PROJECT_ID, projectId) + SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_EXTERNAL_BILLING, args) + } - PaymentMethod.EXTERNAL_BILLING -> { - args.putString(ARG_PROJECT_ID, projectId) - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_EXTERNAL_BILLING, args) - } + else -> { + val event = when (paymentMethod) { + PaymentMethod.GIROPAY -> SnabbleUI.Event.SHOW_GIROPAY_INPUT + PaymentMethod.DE_DIRECT_DEBIT -> SnabbleUI.Event.SHOW_SEPA_CARD_INPUT + PaymentMethod.PAYONE_SEPA -> SnabbleUI.Event.SHOW_PAYONE_SEPA + else -> null + } ?: return - else -> { - Logger.e("Payment method requires no credentials or is unsupported") - } - } + SnabbleUI.executeAction(context, event, args) } } } else { From 043a36a9290dae28b40e75eca9be0d3376e65f09 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Mon, 5 Aug 2024 08:04:52 +0200 Subject: [PATCH 02/25] add simple ui implementation without any validation --- .../shoppingcart/utils/TextFieldManager.kt | 5 + .../payment/telecash/UserDetailsFragment.kt | 18 +++ .../ui/payment/telecash/UserDetailsScreen.kt | 152 ++++++++++++++++++ .../ui/payment/telecash/domain/UserDetails.kt | 16 ++ .../ui/payment/telecash/widget/TextInput.kt | 72 +++++++++ 5 files changed, 263 insertions(+) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt diff --git a/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/utils/TextFieldManager.kt b/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/utils/TextFieldManager.kt index f5cca1b0ed..32d402a833 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/utils/TextFieldManager.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/cart/shoppingcart/utils/TextFieldManager.kt @@ -1,6 +1,7 @@ package io.snabble.sdk.ui.cart.shoppingcart.utils import androidx.compose.runtime.Composable +import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -15,6 +16,10 @@ internal class TextFieldManager( focusManager.clearFocus() keyboardController?.hide() } + + fun moveFocusToNext() { + focusManager.moveFocus(FocusDirection.Next) + } } @Composable diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt new file mode 100644 index 0000000000..777fcf7149 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt @@ -0,0 +1,18 @@ +package io.snabble.sdk.ui.payment.telecash + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.Fragment + +class UserDetailsFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + ComposeView(requireContext()).apply { + setContent { + UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = {}, showError = false) + } + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt new file mode 100644 index 0000000000..a397f830a3 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt @@ -0,0 +1,152 @@ +package io.snabble.sdk.ui.payment.telecash + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager +import io.snabble.sdk.ui.payment.telecash.domain.Address +import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import io.snabble.sdk.ui.payment.telecash.widget.TextInput + +@Composable +fun UserDetailsScreen( + onSendAction: (UserDetails) -> Unit, + onErrorProcessed: () -> Unit, + showError: Boolean, + isLoading: Boolean, +) { + var name by remember { mutableStateOf("") } + var phoneNumber by remember { mutableStateOf("") } + var email by remember { mutableStateOf("") } + var street by remember { mutableStateOf("") } + var zip by remember { mutableStateOf("") } + var city by remember { mutableStateOf("") } + var state by remember { mutableStateOf("") } + var country by remember { mutableStateOf("") } + + val textFieldManager = rememberTextFieldManager() + + val createUserDetails: () -> UserDetails = { + UserDetails( + name = name, + phoneNumber = phoneNumber, + email = email, + address = Address( + street = street, zip = zip, city = city, state = state, country = country + ), + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + TextInput( + value = name, + onValueChanged = { + name = it + }, + label = "Name", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = phoneNumber, + onValueChanged = { + phoneNumber = it + }, + label = "Handynummer", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = email, + onValueChanged = { + email = it + }, + label = "Email", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = street, + onValueChanged = { + street = it + }, + label = "Straße", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = zip, + onValueChanged = { + zip = it + }, + label = "Postleitzahl", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = city, + onValueChanged = { + city = it + }, + label = "Stadt", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = state, + onValueChanged = { + state = it + }, + label = "Bundesland", + keyboardActions = KeyboardActions( + onNext = { textFieldManager.moveFocusToNext() } + ) + ) + TextInput( + value = country, + onValueChanged = { + country = it + }, + label = "Country", + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send), + keyboardActions = KeyboardActions( + onNext = { + textFieldManager.clearFocusAndHideKeyboard() + onSendAction(createUserDetails()) + } + ) + ) + Button(onClick = { onSendAction(createUserDetails()) }) { + Text("Senden") + } + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt new file mode 100644 index 0000000000..4e558f0912 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt @@ -0,0 +1,16 @@ +package io.snabble.sdk.ui.payment.telecash.domain + +data class UserDetails( + val name: String, + val phoneNumber: String, + val email: String, + val address: Address, +) + +data class Address( + val street: String, + val zip: String, + val city: String, + val state: String, + val country: String +) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt new file mode 100644 index 0000000000..a25746fe43 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt @@ -0,0 +1,72 @@ +package io.snabble.sdk.ui.payment.telecash.widget + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun TextInput( + modifier: Modifier = Modifier, + value: String, + label: String, + isError: Boolean = false, + onValueChanged: (String) -> Unit, + keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + keyboardActions: KeyboardActions, +) { + var textFieldValue by remember { + mutableStateOf(TextFieldValue(text = value)) + } + Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) { + OutlinedTextField( + modifier = modifier, + value = textFieldValue, + onValueChange = { + onValueChanged(it.text) + textFieldValue = it + }, + textStyle = MaterialTheme.typography.bodyLarge, + label = { + Text( + text = label, + fontSize = 17.sp + ) + }, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + isError = isError, + maxLines = 1, + singleLine = true, + colors = TextFieldDefaults.defaultColors(), + ) + } +} + +@Composable +fun TextFieldDefaults.defaultColors() = colors( + focusedContainerColor = MaterialTheme.colorScheme.background, + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedIndicatorColor = MaterialTheme.colorScheme.primary, + unfocusedIndicatorColor = MaterialTheme.colorScheme.onSurface, + focusedLabelColor = MaterialTheme.colorScheme.primary, + errorContainerColor = MaterialTheme.colorScheme.background, + errorCursorColor = Color.Red, + errorIndicatorColor = Color.Red, + errorLabelColor = Color.Red +) From 55af7f57eeb2c474501f3552cb5180f01b2b3fde Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Mon, 5 Aug 2024 08:05:25 +0200 Subject: [PATCH 03/25] add base construct for required impl's --- .../telecash/data/TelecashRemoteDataSource.kt | 15 +++++++++++++ .../telecash/data/TelecashRepositoryImpl.kt | 12 +++++++++++ .../payment/telecash/data/UserDetailsDto.kt | 21 +++++++++++++++++++ .../telecash/domain/TelecashRepository.kt | 8 +++++++ 4 files changed, 56 insertions(+) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt new file mode 100644 index 0000000000..28f664d1a0 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt @@ -0,0 +1,15 @@ +package io.snabble.sdk.ui.payment.telecash.data + +import java.net.URL + +interface TelecashRemoteDataSource { + + suspend fun preAuth(userDetails: UserDetailsDto): URL +} + +class TelecashRemoteDataSourceImpl : TelecashRemoteDataSource { + + override suspend fun preAuth(userDetails: UserDetailsDto): URL { + TODO("Not yet implemented") + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt new file mode 100644 index 0000000000..cd483aae71 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -0,0 +1,12 @@ +package io.snabble.sdk.ui.payment.telecash.data + +import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository +import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import java.net.URL + +class TelecashRepositoryImpl : TelecashRepository { + + override fun preAuth(userDetails: UserDetails): URL { + TODO("Not yet implemented") + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt new file mode 100644 index 0000000000..5b869fb550 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt @@ -0,0 +1,21 @@ +package io.snabble.sdk.ui.payment.telecash.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UserDetailsDto( + @SerialName("name") val name: String, + @SerialName("phoneNumber") val phoneNumber: String, + @SerialName("email") val email: String, + @SerialName("address") val address: AddressDto, +) + +@Serializable +data class AddressDto( + @SerialName("street") val street: String, + @SerialName("zip") val zip: String, + @SerialName("city") val city: String, + @SerialName("state") val state: String, + @SerialName("country") val country: String +) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt new file mode 100644 index 0000000000..2a9e6c1f16 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.telecash.domain + +import java.net.URL + +interface TelecashRepository { + + fun preAuth(userDetails: UserDetails): URL +} From 17082fed153d0ff994da8c156e623527cade01be Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Mon, 5 Aug 2024 10:27:12 +0200 Subject: [PATCH 04/25] impl base flow for pre auth --- .../ui/payment/telecash/TelecashViewModel.kt | 23 ++++++ .../payment/telecash/UserDetailsFragment.kt | 7 +- .../telecash/data/TelecashRemoteDataSource.kt | 74 ++++++++++++++++++- .../telecash/data/TelecashRepositoryImpl.kt | 30 ++++++-- .../telecash/domain/TelecashRepository.kt | 5 +- 5 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt new file mode 100644 index 0000000000..cd082d900d --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -0,0 +1,23 @@ +package io.snabble.sdk.ui.payment.telecash + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.extensions.xx +import io.snabble.sdk.ui.payment.telecash.data.TelecashRemoteDataSourceImpl +import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl +import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import kotlinx.coroutines.launch + +class TelecashViewModel( + private val handle: SavedStateHandle +) : ViewModel() { + + fun preuAuth(userDetails: UserDetails) { + viewModelScope.launch { + val paymentMethod = handle.get("paymentType") ?: return@launch + TelecashRepositoryImpl(TelecashRemoteDataSourceImpl()).preAuth(userDetails, paymentMethod).xx() + } + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt index 777fcf7149..354fa66de2 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt @@ -6,13 +6,18 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels class UserDetailsFragment : Fragment() { + val viewModel: TelecashViewModel by viewModels() + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(requireContext()).apply { setContent { - UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = {}, showError = false) + UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = { + viewModel.preuAuth(it) + }, showError = false) } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt index 28f664d1a0..b5bac9761e 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt @@ -1,15 +1,81 @@ package io.snabble.sdk.ui.payment.telecash.data -import java.net.URL +import com.google.gson.annotations.SerializedName +import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.Snabble +import io.snabble.sdk.utils.GsonHolder +import okhttp3.Call +import okhttp3.Callback +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import java.io.IOException +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine interface TelecashRemoteDataSource { - suspend fun preAuth(userDetails: UserDetailsDto): URL + suspend fun preAuth(userDetails: UserDetailsDto, paymentMethod: PaymentMethod): Result } class TelecashRemoteDataSourceImpl : TelecashRemoteDataSource { - override suspend fun preAuth(userDetails: UserDetailsDto): URL { - TODO("Not yet implemented") + override suspend fun preAuth(userDetails: UserDetailsDto, paymentMethod: PaymentMethod): Result { + val project = Snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId")) + val authUrl = project.paymentMethodDescriptors + .firstOrNull { it.paymentMethod == paymentMethod } + ?.links + ?.get("tokenization") + ?: return Result.failure(Exception("Missing pre-auth Url")) + + val requestBody = GsonHolder.get().toJson(userDetails).toRequestBody("application/json".toMediaType()) + + val request: Request = Request.Builder() + .url(Snabble.absoluteUrl(authUrl.href)) + .post(requestBody) + .build() + + return project.okHttpClient.post(request) + } + + private suspend fun OkHttpClient.post(request: Request) = suspendCoroutine> { + newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + it.resume(Result.failure(e)) + } + + override fun onResponse(call: Call, response: Response) { + when { + response.isSuccessful -> { + val body = response.body?.string() + val preAuthData: PreAuthData? = GsonHolder.get().fromJson(body, PreAuthData::class.java) + if (preAuthData == null) { + it.resume(Result.failure(Exception("Missing content"))) + } else { + it.resume(Result.success(preAuthData)) + } + } + + else -> { + it.resume(Result.failure(Exception(response.message))) + } + } + } + }) } } + +data class PreAuthData( + @SerializedName("links") val links: Links +) + +data class Links( + @SerializedName("self") val deleteUrl: Link, + @SerializedName("tokenizationForm") val formUrl: Link +) + +data class Link( + @SerializedName("href") val href: String +) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt index cd483aae71..8d496072c5 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -1,12 +1,32 @@ package io.snabble.sdk.ui.payment.telecash.data +import io.snabble.sdk.PaymentMethod import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import io.snabble.sdk.ui.payment.telecash.domain.UserDetails -import java.net.URL -class TelecashRepositoryImpl : TelecashRepository { +class TelecashRepositoryImpl( + private val remoteDataSource: TelecashRemoteDataSource +) : TelecashRepository { - override fun preAuth(userDetails: UserDetails): URL { - TODO("Not yet implemented") - } + override suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result = + remoteDataSource.preAuth(userDetails.toDto(), paymentMethod) + .map { PreAuthInformation(it.links.formUrl.href, it.links.deleteUrl.href) } } + +private fun UserDetails.toDto() = UserDetailsDto( + name = name, + phoneNumber = phoneNumber, + email = email, + address = AddressDto( + street = address.street, + zip = address.zip, + city = address.city, + state = address.state, + country = address.country + ), +) + +data class PreAuthInformation( + val formUrl: String, + val deleteUrl: String +) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt index 2a9e6c1f16..926b69b64f 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt @@ -1,8 +1,9 @@ package io.snabble.sdk.ui.payment.telecash.domain -import java.net.URL +import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.ui.payment.telecash.data.PreAuthInformation interface TelecashRepository { - fun preAuth(userDetails: UserDetails): URL + suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result? } From 31bd5a7f9e052692fb5cc46c320eab1741927a6a Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Mon, 5 Aug 2024 11:42:18 +0200 Subject: [PATCH 05/25] WIP: complete flow --- .../sdk/ui/payment/CreditCardInputFragment.kt | 2 +- .../sdk/ui/payment/CreditCardInputView.java | 20 +++++++++------- .../creditcard/data/CreditCardUrlBuilder.kt | 15 ++---------- .../ui/payment/telecash/TelecashViewModel.kt | 12 ++++++++-- .../payment/telecash/UserDetailsFragment.kt | 24 ++++++++++++++++--- .../telecash/data/TelecashRepositoryImpl.kt | 2 +- 6 files changed, 46 insertions(+), 29 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt index 66b76dbfc7..071b36ebe9 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt @@ -28,6 +28,6 @@ open class CreditCardInputFragment : BaseFragment( override fun onActualViewCreated(view: View, savedInstanceState: Bundle?) { val v = view as CreditCardInputView - v.load(projectId, paymentMethod) +// v.load(projectId, paymentMethod) } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java index 7f7f664217..8a5bc954b0 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; @@ -56,6 +57,7 @@ public class CreditCardInputView extends RelativeLayout { private PaymentMethod paymentType; private String projectId; + private String url; private TextView threeDHint; private boolean isLoaded; @@ -162,10 +164,9 @@ private void setProject() { } } - public void load(String projectId, PaymentMethod paymentType) { - this.projectId = projectId; + public void load(PaymentMethod paymentType, String url) { this.paymentType = paymentType; - + this.url = Snabble.getInstance().absoluteUrl(url); inflateView(); } @@ -191,7 +192,8 @@ private Project getProject() { private void loadUrl() { CreditCardUrlBuilder builder = new CreditCardUrlBuilder(); - String url = builder.createUrlFor(projectId, paymentType); + String url = builder.createUrlFor(paymentType, this.url); + Log.d("xx", "loadUrl: " + url); webView.loadUrl(url); } @@ -340,10 +342,10 @@ public void preAuthInfo(final String totalCharge, final String currency) { Dispatch.mainThread(() -> { isLoaded = true; Project project = getProject(); - String companyName = project.getName(); - if (project.getCompany() != null && project.getCompany().name != null) { - companyName = project.getCompany().name; - } +// String companyName = project.getName(); +// if (project.getCompany() != null && project.getCompany().name != null) { +// companyName = project.getCompany().name; +// } NumberFormat numberFormat = NumberFormat.getCurrencyInstance(); numberFormat.setCurrency(Currency.getInstance(currency)); BigDecimal chargeTotal = new BigDecimal(totalCharge); @@ -351,7 +353,7 @@ public void preAuthInfo(final String totalCharge, final String currency) { threeDHint.setText( resources.getString(R.string.Snabble_CC_3dsecureHint_retailerWithPrice, numberFormat.format(chargeTotal), - companyName) + "companyName") ); }); } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt index 3a29e87694..dc55105488 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt @@ -1,13 +1,11 @@ package io.snabble.sdk.ui.payment.creditcard.data -import android.net.Uri import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble class CreditCardUrlBuilder { - fun createUrlFor(projectId: String, paymentType: PaymentMethod): String { - val builder = Uri.Builder() + fun createUrlFor(paymentType: PaymentMethod, path: String): String { val paymentMethod = when (paymentType) { PaymentMethod.MASTERCARD -> "mastercard" PaymentMethod.AMEX -> "amex" @@ -15,16 +13,7 @@ class CreditCardUrlBuilder { else -> "visa" } val appUserId = Snabble.userPreferences.appUser?.id - val authority = Snabble.endpointBaseUrl.substringAfter("https://") - return builder.scheme("https") - .authority(authority) - .appendPath(projectId) - .appendPath("telecash") - .appendPath("form") - .appendQueryParameter("platform", "android") - .appendQueryParameter("appUserID", appUserId) - .appendQueryParameter("paymentMethod", paymentMethod) - .build().toString() + return "$path&platform=android&appUserID=$appUserId&paymentMethod=$paymentMethod" } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index cd082d900d..42d43f2b30 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -4,20 +4,28 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.extensions.xx import io.snabble.sdk.ui.payment.telecash.data.TelecashRemoteDataSourceImpl import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch class TelecashViewModel( private val handle: SavedStateHandle ) : ViewModel() { + val _uiState = MutableStateFlow("") + val uiState = _uiState.asStateFlow() + fun preuAuth(userDetails: UserDetails) { viewModelScope.launch { val paymentMethod = handle.get("paymentType") ?: return@launch - TelecashRepositoryImpl(TelecashRemoteDataSourceImpl()).preAuth(userDetails, paymentMethod).xx() + val result = TelecashRepositoryImpl(TelecashRemoteDataSourceImpl()).preAuth(userDetails, paymentMethod) + result.onSuccess { pre -> + _uiState.update { pre.formUrl } + } } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt index 354fa66de2..cd9b9855af 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt @@ -5,8 +5,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.viewinterop.AndroidView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.extensions.xx +import io.snabble.sdk.ui.payment.CreditCardInputView class UserDetailsFragment : Fragment() { @@ -15,9 +20,22 @@ class UserDetailsFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(requireContext()).apply { setContent { - UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = { - viewModel.preuAuth(it) - }, showError = false) + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + + when { + uiState.isEmpty() -> + UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = { + viewModel.preuAuth(it) + }, showError = false) + + uiState.isNotEmpty() -> AndroidView( + factory = { context -> + CreditCardInputView(context) + .apply { load(PaymentMethod.VISA, uiState.xx()) } + } + ) + + } } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt index 8d496072c5..3261188a9c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -8,7 +8,7 @@ class TelecashRepositoryImpl( private val remoteDataSource: TelecashRemoteDataSource ) : TelecashRepository { - override suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result = + override suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result = remoteDataSource.preAuth(userDetails.toDto(), paymentMethod) .map { PreAuthInformation(it.links.formUrl.href, it.links.deleteUrl.href) } } From 4e66d791bf9856cea39468292f0a5211be46a92b Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Mon, 5 Aug 2024 11:42:41 +0200 Subject: [PATCH 06/25] WIP: Add new navigation target --- .../main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt | 2 +- kotlin-sample/src/main/res/navigation/mobile_navigation.xml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt b/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt index a38824f89c..be7e92be34 100644 --- a/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt +++ b/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt @@ -40,7 +40,7 @@ fun setUpUiEvents(activity: AppCompatActivity, navController: NavController, bot activity, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT ) { _, args -> - navController.navigate(R.id.navigation_credit_card_input, args) + navController.navigate(R.id.user_details, args) } SnabbleUI.setUiAction( activity, diff --git a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml index f566bd61d2..3bde5f9de0 100644 --- a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml +++ b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml @@ -169,6 +169,11 @@ android:name="io.snabble.sdk.ui.payment.CreditCardInputFragment" android:label="Enter credit card" /> + + Date: Mon, 5 Aug 2024 16:34:57 +0200 Subject: [PATCH 07/25] Some small refactorings and renamings --- .../sdk/ui/payment/CreditCardInputView.java | 9 +-- .../creditcard/data/CreditCardUrlBuilder.kt | 33 ++++++---- .../ui/payment/telecash/TelecashViewModel.kt | 37 ++++++++--- .../payment/telecash/UserDetailsFragment.kt | 17 ++--- .../ui/payment/telecash/UserDetailsScreen.kt | 12 ++++ .../{UserDetailsDto.kt => CustomerInfoDto.kt} | 2 +- .../telecash/data/TelecashRemoteDataSource.kt | 63 ++++++++++++------- .../telecash/data/TelecashRepositoryImpl.kt | 20 +++--- .../telecash/domain/TelecashRepository.kt | 6 +- 9 files changed, 128 insertions(+), 71 deletions(-) rename ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/{UserDetailsDto.kt => CustomerInfoDto.kt} (95%) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java index 8a5bc954b0..ca609ec930 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java @@ -6,7 +6,6 @@ import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; @@ -37,7 +36,7 @@ import io.snabble.sdk.ui.R; import io.snabble.sdk.ui.SnabbleUI; import io.snabble.sdk.ui.payment.creditcard.data.CreditCardInfo; -import io.snabble.sdk.ui.payment.creditcard.data.CreditCardUrlBuilder; +import io.snabble.sdk.ui.payment.creditcard.data.SnabbleCreditCardUrlCreator; import io.snabble.sdk.ui.telemetry.Telemetry; import io.snabble.sdk.ui.utils.UIUtils; import io.snabble.sdk.utils.Dispatch; @@ -191,10 +190,8 @@ private Project getProject() { } private void loadUrl() { - CreditCardUrlBuilder builder = new CreditCardUrlBuilder(); - String url = builder.createUrlFor(paymentType, this.url); - Log.d("xx", "loadUrl: " + url); - webView.loadUrl(url); + final String formUrl = SnabbleCreditCardUrlCreator.createCreditCardUrlFor(paymentType, url); + webView.loadUrl(formUrl); } private void authenticateAndSave(final CreditCardInfo creditCardInfo) { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt index dc55105488..91ec3aac82 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt @@ -1,19 +1,28 @@ +@file:JvmName("SnabbleCreditCardUrlCreator") package io.snabble.sdk.ui.payment.creditcard.data +import android.net.Uri import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble +import io.snabble.sdk.extensions.xx -class CreditCardUrlBuilder { - - fun createUrlFor(paymentType: PaymentMethod, path: String): String { - val paymentMethod = when (paymentType) { - PaymentMethod.MASTERCARD -> "mastercard" - PaymentMethod.AMEX -> "amex" - PaymentMethod.VISA -> "visa" - else -> "visa" - } - val appUserId = Snabble.userPreferences.appUser?.id - - return "$path&platform=android&appUserID=$appUserId&paymentMethod=$paymentMethod" +fun createCreditCardUrlFor(paymentType: PaymentMethod, url: String): String { + val paymentMethod = when (paymentType) { + PaymentMethod.MASTERCARD -> "mastercard" + PaymentMethod.AMEX -> "amex" + PaymentMethod.VISA -> "visa" + else -> "visa" } + val appUserId = Snabble.userPreferences.appUser?.id + return Uri.parse(url) + .buildUpon() + .appendQueryParameter(PARAM_KEY_PLATFORM, "android") + .appendQueryParameter(PARAM_KEY_ADD_USER_ID, appUserId) + .appendQueryParameter(PARAM_KEY_PAYMENT_METHOD, paymentMethod) + .build() + .toString().xx("Final URL:") } + +private const val PARAM_KEY_PLATFORM = "platform" +private const val PARAM_KEY_ADD_USER_ID = "appUserID" +private const val PARAM_KEY_PAYMENT_METHOD = "paymentMethod" diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 42d43f2b30..bd3fa5c048 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -2,29 +2,46 @@ package io.snabble.sdk.ui.payment.telecash import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.telecash.data.TelecashRemoteDataSourceImpl import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl +import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import io.snabble.sdk.ui.payment.telecash.domain.UserDetails import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -class TelecashViewModel( - private val handle: SavedStateHandle +internal class TelecashViewModel( + private val telecashRepo: TelecashRepository = TelecashRepositoryImpl(), + private val savedStateHandle: SavedStateHandle ) : ViewModel() { - val _uiState = MutableStateFlow("") - val uiState = _uiState.asStateFlow() + private val _uiState = MutableStateFlow("") + val uiState: StateFlow = _uiState.asStateFlow() - fun preuAuth(userDetails: UserDetails) { + fun sendUserData(userDetails: UserDetails) { viewModelScope.launch { - val paymentMethod = handle.get("paymentType") ?: return@launch - val result = TelecashRepositoryImpl(TelecashRemoteDataSourceImpl()).preAuth(userDetails, paymentMethod) - result.onSuccess { pre -> - _uiState.update { pre.formUrl } + val paymentMethod = savedStateHandle.get("paymentType") ?: return@launch + val result = telecashRepo.sendUserData(userDetails, paymentMethod) + result.onSuccess { info -> + _uiState.update { info.formUrl } + } + // TBI: On error? + } + } + + companion object { + + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val savedStateHandle = createSavedStateHandle() + TelecashViewModel(savedStateHandle = savedStateHandle) } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt index cd9b9855af..ce66a2b7a3 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt @@ -10,31 +10,32 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.extensions.xx import io.snabble.sdk.ui.payment.CreditCardInputView class UserDetailsFragment : Fragment() { - val viewModel: TelecashViewModel by viewModels() + private val viewModel: TelecashViewModel by viewModels { TelecashViewModel.Factory } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(requireContext()).apply { setContent { - val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value // TBI: It's currently the url when { uiState.isEmpty() -> - UserDetailsScreen(onErrorProcessed = {}, isLoading = false, onSendAction = { - viewModel.preuAuth(it) - }, showError = false) + UserDetailsScreen( + onErrorProcessed = {}, + isLoading = false, + onSendAction = { viewModel.sendUserData(it) }, + showError = false + ) uiState.isNotEmpty() -> AndroidView( factory = { context -> CreditCardInputView(context) - .apply { load(PaymentMethod.VISA, uiState.xx()) } + .apply { load(PaymentMethod.VISA, uiState) } } ) - } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt index a397f830a3..def55d4b04 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp +import io.snabble.sdk.BuildConfig import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager import io.snabble.sdk.ui.payment.telecash.domain.Address import io.snabble.sdk.ui.payment.telecash.domain.UserDetails @@ -40,6 +41,17 @@ fun UserDetailsScreen( var state by remember { mutableStateOf("") } var country by remember { mutableStateOf("") } + if (BuildConfig.DEBUG) { + name = "Max Mustermann" + phoneNumber = "+491729973186" + email = "max.mustermann@example.com123" + street = "Fakestr. 123" + zip = "12345" + city = "Bonn" + state = "NRW" + country = "DE" + } + val textFieldManager = rememberTextFieldManager() val createUserDetails: () -> UserDetails = { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt similarity index 95% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt index 5b869fb550..5724f657ef 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/UserDetailsDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class UserDetailsDto( +data class CustomerInfoDto( @SerialName("name") val name: String, @SerialName("phoneNumber") val phoneNumber: String, @SerialName("email") val email: String, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt index b5bac9761e..03c9544ea6 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt @@ -1,5 +1,8 @@ package io.snabble.sdk.ui.payment.telecash.data +import android.util.Log +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException import com.google.gson.annotations.SerializedName import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble @@ -9,73 +12,87 @@ import okhttp3.Callback import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import java.io.IOException import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -interface TelecashRemoteDataSource { +internal interface TelecashRemoteDataSource { - suspend fun preAuth(userDetails: UserDetailsDto, paymentMethod: PaymentMethod): Result + suspend fun sendUserData(customerInfo: CustomerInfoDto, paymentMethod: PaymentMethod): Result } -class TelecashRemoteDataSourceImpl : TelecashRemoteDataSource { +internal class TelecashRemoteDataSourceImpl( + val gson: Gson = GsonHolder.get(), +) : TelecashRemoteDataSource { - override suspend fun preAuth(userDetails: UserDetailsDto, paymentMethod: PaymentMethod): Result { + override suspend fun sendUserData( + customerInfo: CustomerInfoDto, + paymentMethod: PaymentMethod + ): Result { val project = Snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId")) - val authUrl = project.paymentMethodDescriptors + + val customerInfoPostUrl = project.paymentMethodDescriptors .firstOrNull { it.paymentMethod == paymentMethod } ?.links ?.get("tokenization") - ?: return Result.failure(Exception("Missing pre-auth Url")) - - val requestBody = GsonHolder.get().toJson(userDetails).toRequestBody("application/json".toMediaType()) + ?.href + ?.let(Snabble::absoluteUrl) + ?: return Result.failure(Exception("Missing link to send customer info to")) + val requestBody: RequestBody = gson.toJson(customerInfo).toRequestBody("application/json".toMediaType()) val request: Request = Request.Builder() - .url(Snabble.absoluteUrl(authUrl.href)) + .url(customerInfoPostUrl) .post(requestBody) .build() return project.okHttpClient.post(request) } - private suspend fun OkHttpClient.post(request: Request) = suspendCoroutine> { + private suspend fun OkHttpClient.post(request: Request) = suspendCoroutine> { newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - it.resume(Result.failure(e)) - } override fun onResponse(call: Call, response: Response) { when { response.isSuccessful -> { val body = response.body?.string() - val preAuthData: PreAuthData? = GsonHolder.get().fromJson(body, PreAuthData::class.java) - if (preAuthData == null) { - it.resume(Result.failure(Exception("Missing content"))) + val creditCardAuthData: CreditCardAuthData? = try { + gson.fromJson(body, CreditCardAuthData::class.java) + } catch (e: JsonSyntaxException) { + Log.e("Telecash", "Error parsing pre-registration response", e) + null + } + + val result = if (creditCardAuthData == null) { + Result.failure(Exception("Missing content")) } else { - it.resume(Result.success(preAuthData)) + Result.success(creditCardAuthData) } + it.resume(result) } - else -> { - it.resume(Result.failure(Exception(response.message))) - } + else -> it.resume(Result.failure(Exception(response.message))) } } + + override fun onFailure(call: Call, e: IOException) { + it.resume(Result.failure(e)) + } }) } } -data class PreAuthData( +internal data class CreditCardAuthData( @SerializedName("links") val links: Links ) -data class Links( +internal data class Links( @SerializedName("self") val deleteUrl: Link, @SerializedName("tokenizationForm") val formUrl: Link ) -data class Link( +internal data class Link( @SerializedName("href") val href: String ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt index 3261188a9c..021598e300 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -4,16 +4,20 @@ import io.snabble.sdk.PaymentMethod import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import io.snabble.sdk.ui.payment.telecash.domain.UserDetails -class TelecashRepositoryImpl( - private val remoteDataSource: TelecashRemoteDataSource +internal class TelecashRepositoryImpl( + private val remoteDataSource: TelecashRemoteDataSource = TelecashRemoteDataSourceImpl() ) : TelecashRepository { - override suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result = - remoteDataSource.preAuth(userDetails.toDto(), paymentMethod) - .map { PreAuthInformation(it.links.formUrl.href, it.links.deleteUrl.href) } + override suspend fun sendUserData( + userDetails: UserDetails, + paymentMethod: PaymentMethod + ): Result = + remoteDataSource + .sendUserData(userDetails.toDto(), paymentMethod) + .map { CreditCardAdditionInfo(it.links.formUrl.href, it.links.deleteUrl.href) } } -private fun UserDetails.toDto() = UserDetailsDto( +private fun UserDetails.toDto() = CustomerInfoDto( name = name, phoneNumber = phoneNumber, email = email, @@ -26,7 +30,7 @@ private fun UserDetails.toDto() = UserDetailsDto( ), ) -data class PreAuthInformation( +data class CreditCardAdditionInfo( val formUrl: String, - val deleteUrl: String + val preAuthDeleteUrl: String ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt index 926b69b64f..d64269573f 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt @@ -1,9 +1,9 @@ package io.snabble.sdk.ui.payment.telecash.domain import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.telecash.data.PreAuthInformation +import io.snabble.sdk.ui.payment.telecash.data.CreditCardAdditionInfo -interface TelecashRepository { +internal interface TelecashRepository { - suspend fun preAuth(userDetails: UserDetails, paymentMethod: PaymentMethod): Result? + suspend fun sendUserData(userDetails: UserDetails, paymentMethod: PaymentMethod): Result } From 4116b303808c83e4d4effdcf9d108fe8b7f67bbc Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Mon, 5 Aug 2024 17:35:44 +0200 Subject: [PATCH 08/25] Improvement for the UI and i18n --- .../main/res/navigation/mobile_navigation.xml | 4 +- .../telecash/CustomerInfoInputFragment.kt | 61 ++++++++++ ...lsScreen.kt => CustomerInfoInputScreen.kt} | 109 ++++++++++-------- .../ui/payment/telecash/TelecashViewModel.kt | 6 +- .../payment/telecash/UserDetailsFragment.kt | 42 ------- .../telecash/data/TelecashRepositoryImpl.kt | 8 +- .../{UserDetails.kt => CustomerInfo.kt} | 2 +- .../telecash/domain/TelecashRepository.kt | 2 +- ui/src/main/res/values-de/strings.xml | 9 ++ ui/src/main/res/values/strings.xml | 9 ++ 10 files changed, 152 insertions(+), 100 deletions(-) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/telecash/{UserDetailsScreen.kt => CustomerInfoInputScreen.kt} (55%) delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/{UserDetails.kt => CustomerInfo.kt} (92%) diff --git a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml index 3bde5f9de0..44cbd976bb 100644 --- a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml +++ b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml @@ -171,8 +171,8 @@ + android:name="io.snabble.sdk.ui.payment.telecash.CustomerInfoInputFragment" + android:label="" /> ("paymentTyp") + ?: kotlin.run { requireActivity().finish(); return } + + (requireActivity() as? AppCompatActivity)?.supportActionBar?.title = + PaymentMethodMetaDataHelper(requireContext()).labelFor(paymentMethod) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + ComposeView(inflater.context).apply { + setContent { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value // TBI: It's currently only the url + + when { + uiState.isEmpty() -> + ThemeWrapper { + CustomerInfoInputScreen( + onErrorProcessed = {}, + isLoading = false, + onSendAction = { viewModel.sendUserData(it) }, + showError = false, + onBackNavigationClick = { activity?.onBackPressed() } + ) + } + + uiState.isNotEmpty() -> AndroidView( + factory = { context -> + CreditCardInputView(context) + .apply { load(paymentMethod, uiState) } + } + ) + } + } + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt similarity index 55% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index def55d4b04..c53baa4f7e 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -3,6 +3,7 @@ package io.snabble.sdk.ui.payment.telecash import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions @@ -10,6 +11,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -17,20 +19,24 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp import io.snabble.sdk.BuildConfig +import io.snabble.sdk.ui.R import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager import io.snabble.sdk.ui.payment.telecash.domain.Address -import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo import io.snabble.sdk.ui.payment.telecash.widget.TextInput @Composable -fun UserDetailsScreen( - onSendAction: (UserDetails) -> Unit, +fun CustomerInfoInputScreen( + onSendAction: (CustomerInfo) -> Unit, onErrorProcessed: () -> Unit, showError: Boolean, isLoading: Boolean, + onBackNavigationClick: () -> Unit, ) { var name by remember { mutableStateOf("") } var phoneNumber by remember { mutableStateOf("") } @@ -54,8 +60,8 @@ fun UserDetailsScreen( val textFieldManager = rememberTextFieldManager() - val createUserDetails: () -> UserDetails = { - UserDetails( + val createCustomerInfo: () -> CustomerInfo = { + CustomerInfo( name = name, phoneNumber = phoneNumber, email = email, @@ -68,97 +74,106 @@ fun UserDetailsScreen( Column( modifier = Modifier .fillMaxSize() - .padding(horizontal = 16.dp) + .padding(16.dp) .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { TextInput( + modifier = Modifier.fillMaxWidth(), value = name, - onValueChanged = { - name = it - }, - label = "Name", + onValueChanged = { name = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_fullName), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = phoneNumber, - onValueChanged = { - phoneNumber = it - }, - label = "Handynummer", + onValueChanged = { phoneNumber = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_phoneNumber), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = email, - onValueChanged = { - email = it - }, - label = "Email", + onValueChanged = { email = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_email), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } - ) + ), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = street, - onValueChanged = { - street = it - }, - label = "Straße", + onValueChanged = { street = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_street), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } - ) + ), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = zip, - onValueChanged = { - zip = it - }, - label = "Postleitzahl", + onValueChanged = { zip = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_zip), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } - ) + ), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = city, - onValueChanged = { - city = it - }, - label = "Stadt", + onValueChanged = { city = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_city), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } - ) + ), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = state, - onValueChanged = { - state = it - }, - label = "Bundesland", + onValueChanged = { state = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_state), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } - ) + ), + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( + modifier = Modifier.fillMaxWidth(), value = country, - onValueChanged = { - country = it - }, - label = "Country", - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send), + onValueChanged = { country = it }, + label = stringResource(R.string.Snabble_Payment_CustomerInfo_country), + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Words, + imeAction = ImeAction.Send + ), keyboardActions = KeyboardActions( onNext = { textFieldManager.clearFocusAndHideKeyboard() - onSendAction(createUserDetails()) + onSendAction(createCustomerInfo()) } ) ) - Button(onClick = { onSendAction(createUserDetails()) }) { - Text("Senden") + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { onSendAction(createCustomerInfo()) } + ) { + Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) + } + TextButton( + modifier = Modifier.fillMaxWidth(), + onClick = onBackNavigationClick + ) { + Text(text = stringResource(R.string.Snabble_cancel)) } } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index bd3fa5c048..358844078b 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -10,7 +10,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import io.snabble.sdk.PaymentMethod import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository -import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -25,10 +25,10 @@ internal class TelecashViewModel( private val _uiState = MutableStateFlow("") val uiState: StateFlow = _uiState.asStateFlow() - fun sendUserData(userDetails: UserDetails) { + fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { val paymentMethod = savedStateHandle.get("paymentType") ?: return@launch - val result = telecashRepo.sendUserData(userDetails, paymentMethod) + val result = telecashRepo.sendUserData(customerInfo, paymentMethod) result.onSuccess { info -> _uiState.update { info.formUrl } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt deleted file mode 100644 index ce66a2b7a3..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/UserDetailsFragment.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.snabble.sdk.ui.payment.telecash - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.viewinterop.AndroidView -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.CreditCardInputView - -class UserDetailsFragment : Fragment() { - - private val viewModel: TelecashViewModel by viewModels { TelecashViewModel.Factory } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - ComposeView(requireContext()).apply { - setContent { - val uiState = viewModel.uiState.collectAsStateWithLifecycle().value // TBI: It's currently the url - - when { - uiState.isEmpty() -> - UserDetailsScreen( - onErrorProcessed = {}, - isLoading = false, - onSendAction = { viewModel.sendUserData(it) }, - showError = false - ) - - uiState.isNotEmpty() -> AndroidView( - factory = { context -> - CreditCardInputView(context) - .apply { load(PaymentMethod.VISA, uiState) } - } - ) - } - } - } -} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt index 021598e300..63ce463a47 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -2,22 +2,22 @@ package io.snabble.sdk.ui.payment.telecash.data import io.snabble.sdk.PaymentMethod import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository -import io.snabble.sdk.ui.payment.telecash.domain.UserDetails +import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo internal class TelecashRepositoryImpl( private val remoteDataSource: TelecashRemoteDataSource = TelecashRemoteDataSourceImpl() ) : TelecashRepository { override suspend fun sendUserData( - userDetails: UserDetails, + customerInfo: CustomerInfo, paymentMethod: PaymentMethod ): Result = remoteDataSource - .sendUserData(userDetails.toDto(), paymentMethod) + .sendUserData(customerInfo.toDto(), paymentMethod) .map { CreditCardAdditionInfo(it.links.formUrl.href, it.links.deleteUrl.href) } } -private fun UserDetails.toDto() = CustomerInfoDto( +private fun CustomerInfo.toDto() = CustomerInfoDto( name = name, phoneNumber = phoneNumber, email = email, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt similarity index 92% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt index 4e558f0912..9917b6fc1f 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/UserDetails.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt @@ -1,6 +1,6 @@ package io.snabble.sdk.ui.payment.telecash.domain -data class UserDetails( +data class CustomerInfo( val name: String, val phoneNumber: String, val email: String, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt index d64269573f..2022427498 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt @@ -5,5 +5,5 @@ import io.snabble.sdk.ui.payment.telecash.data.CreditCardAdditionInfo internal interface TelecashRepository { - suspend fun sendUserData(userDetails: UserDetails, paymentMethod: PaymentMethod): Result + suspend fun sendUserData(customerInfo: CustomerInfo, paymentMethod: PaymentMethod): Result } diff --git a/ui/src/main/res/values-de/strings.xml b/ui/src/main/res/values-de/strings.xml index 67b1a806d0..fce6a3c3a6 100644 --- a/ui/src/main/res/values-de/strings.xml +++ b/ui/src/main/res/values-de/strings.xml @@ -101,6 +101,15 @@ Abbrechen nicht möglich Bei der Verarbeitung deiner Kreditkarte ist ein Fehler aufgetreten Läuft ab: %s + Stadt + Land + E-Mail-Adresse + Vor- und Nachname + Weiter + Telefonnummer + Bundesland + Straße inkl. Hausnummer + Postleitzahl Möchtest Du diese Zahlungsmethode wirklich entfernen? Zahlungsmethode hinzufügen Noch keine Zahlungsmethode hinterlegt. diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index a391f53dc7..9dea4b3080 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -101,6 +101,15 @@ Error cancelling payment There was an error processing your credit card Expires: %s + City + Country + Email + Full name + Continue + Phone number + State + Street + ZIP code Are you sure you want to remove this payment method? Add payment method You don\'t have any payment methods added yet. From e5c79c3a1164e59fceb0f4983f333dc9c074aaf0 Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 08:40:25 +0200 Subject: [PATCH 09/25] Fix several issues including UI --- kotlin-sample/src/main/AndroidManifest.xml | 2 +- .../telecash/CustomerInfoInputFragment.kt | 6 +++--- .../telecash/CustomerInfoInputScreen.kt | 19 +++++++++---------- .../telecash/data/TelecashRemoteDataSource.kt | 8 +++++--- .../ui/payment/telecash/widget/TextInput.kt | 9 +++++++-- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/kotlin-sample/src/main/AndroidManifest.xml b/kotlin-sample/src/main/AndroidManifest.xml index 17fdabf6c9..820680dbd2 100644 --- a/kotlin-sample/src/main/AndroidManifest.xml +++ b/kotlin-sample/src/main/AndroidManifest.xml @@ -34,7 +34,7 @@ + android:windowSoftInputMode="adjustResize" /> ("paymentTyp") - ?: kotlin.run { requireActivity().finish(); return } + paymentMethod = arguments?.serializableExtra(CreditCardInputView.ARG_PAYMENT_TYPE) + ?: kotlin.run { activity?.onBackPressed(); return } (requireActivity() as? AppCompatActivity)?.supportActionBar?.title = PaymentMethodMetaDataHelper(requireContext()).labelFor(paymentMethod) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index c53baa4f7e..d67a3d34f1 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -2,7 +2,6 @@ package io.snabble.sdk.ui.payment.telecash import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -22,6 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import io.snabble.sdk.BuildConfig import io.snabble.sdk.ui.R @@ -73,7 +73,7 @@ fun CustomerInfoInputScreen( Column( modifier = Modifier - .fillMaxSize() + .fillMaxWidth() .padding(16.dp) .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(16.dp), @@ -95,6 +95,10 @@ fun CustomerInfoInputScreen( label = stringResource(R.string.Snabble_Payment_CustomerInfo_phoneNumber), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Phone, + imeAction = ImeAction.Next ) ) TextInput( @@ -105,7 +109,6 @@ fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), - keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -115,7 +118,6 @@ fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), - keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -125,7 +127,6 @@ fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), - keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -135,7 +136,6 @@ fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), - keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -145,7 +145,6 @@ fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), - keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -153,11 +152,11 @@ fun CustomerInfoInputScreen( onValueChanged = { country = it }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_country), keyboardOptions = KeyboardOptions( - capitalization = KeyboardCapitalization.Words, - imeAction = ImeAction.Send + imeAction = ImeAction.Send, + capitalization = KeyboardCapitalization.Words ), keyboardActions = KeyboardActions( - onNext = { + onSend = { textFieldManager.clearFocusAndHideKeyboard() onSendAction(createCustomerInfo()) } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt index 03c9544ea6..c194edb297 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt @@ -6,6 +6,7 @@ import com.google.gson.JsonSyntaxException import com.google.gson.annotations.SerializedName import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble +import io.snabble.sdk.ui.SnabbleUI import io.snabble.sdk.utils.GsonHolder import okhttp3.Call import okhttp3.Callback @@ -25,21 +26,22 @@ internal interface TelecashRemoteDataSource { } internal class TelecashRemoteDataSourceImpl( - val gson: Gson = GsonHolder.get(), + private val snabble: Snabble = Snabble, + private val gson: Gson = GsonHolder.get(), ) : TelecashRemoteDataSource { override suspend fun sendUserData( customerInfo: CustomerInfoDto, paymentMethod: PaymentMethod ): Result { - val project = Snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId")) + val project = snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId")) val customerInfoPostUrl = project.paymentMethodDescriptors .firstOrNull { it.paymentMethod == paymentMethod } ?.links ?.get("tokenization") ?.href - ?.let(Snabble::absoluteUrl) + ?.let(snabble::absoluteUrl) ?: return Result.failure(Exception("Missing link to send customer info to")) val requestBody: RequestBody = gson.toJson(customerInfo).toRequestBody("application/json".toMediaType()) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt index a25746fe43..3b35438c28 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt @@ -15,7 +15,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -27,11 +29,14 @@ fun TextInput( label: String, isError: Boolean = false, onValueChanged: (String) -> Unit, - keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + keyboardOptions: KeyboardOptions = KeyboardOptions( + imeAction = ImeAction.Next, + capitalization = KeyboardCapitalization.Words + ), keyboardActions: KeyboardActions, ) { var textFieldValue by remember { - mutableStateOf(TextFieldValue(text = value)) + mutableStateOf(TextFieldValue(text = value, selection = TextRange(value.length, value.length))) } Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) { OutlinedTextField( From 299a150fb99da430fd3e66486287dac9e1b3a521 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Tue, 6 Aug 2024 10:23:20 +0200 Subject: [PATCH 10/25] fix bug due to missing project id --- .../snabble/sdk/ui/payment/CreditCardInputView.java | 13 +++++++------ .../payment/telecash/CustomerInfoInputFragment.kt | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java index ca609ec930..3831b773c7 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java @@ -163,8 +163,9 @@ private void setProject() { } } - public void load(PaymentMethod paymentType, String url) { + public void load(String projectId, PaymentMethod paymentType, String url) { this.paymentType = paymentType; + this.projectId = projectId; this.url = Snabble.getInstance().absoluteUrl(url); inflateView(); } @@ -339,10 +340,10 @@ public void preAuthInfo(final String totalCharge, final String currency) { Dispatch.mainThread(() -> { isLoaded = true; Project project = getProject(); -// String companyName = project.getName(); -// if (project.getCompany() != null && project.getCompany().name != null) { -// companyName = project.getCompany().name; -// } + String companyName = project.getName(); + if (project.getCompany() != null && project.getCompany().name != null) { + companyName = project.getCompany().name; + } NumberFormat numberFormat = NumberFormat.getCurrencyInstance(); numberFormat.setCurrency(Currency.getInstance(currency)); BigDecimal chargeTotal = new BigDecimal(totalCharge); @@ -350,7 +351,7 @@ public void preAuthInfo(final String totalCharge, final String currency) { threeDHint.setText( resources.getString(R.string.Snabble_CC_3dsecureHint_retailerWithPrice, numberFormat.format(chargeTotal), - "companyName") + companyName) ); }); } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt index 2096b55bb6..6093b23d8b 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.Snabble import io.snabble.sdk.ui.payment.CreditCardInputView import io.snabble.sdk.ui.payment.PaymentMethodMetaDataHelper import io.snabble.sdk.ui.utils.ThemeWrapper @@ -52,7 +53,7 @@ class CustomerInfoInputFragment : Fragment() { uiState.isNotEmpty() -> AndroidView( factory = { context -> CreditCardInputView(context) - .apply { load(paymentMethod, uiState) } + .apply { load(Snabble.checkedInProject.value?.id,paymentMethod, uiState) } } ) } From 3502b06c60ae98b98067040010f2a988e104509d Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Tue, 6 Aug 2024 12:12:32 +0200 Subject: [PATCH 11/25] add error handling --- .../telecash/CustomerInfoInputFragment.kt | 12 +-- .../telecash/CustomerInfoInputScreen.kt | 73 +++++++++++++------ .../ui/payment/telecash/TelecashViewModel.kt | 29 ++++++-- ui/src/main/res/values-de/strings.xml | 1 + ui/src/main/res/values/strings.xml | 1 + 5 files changed, 82 insertions(+), 34 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt index 6093b23d8b..df90b72461 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt @@ -39,21 +39,21 @@ class CustomerInfoInputFragment : Fragment() { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value // TBI: It's currently only the url when { - uiState.isEmpty() -> + uiState.formUrl == null -> ThemeWrapper { CustomerInfoInputScreen( - onErrorProcessed = {}, - isLoading = false, + onErrorProcessed = { viewModel.errorHandled() }, + isLoading = uiState.isLoading, onSendAction = { viewModel.sendUserData(it) }, - showError = false, + showError = uiState.showError, onBackNavigationClick = { activity?.onBackPressed() } ) } - uiState.isNotEmpty() -> AndroidView( + else -> AndroidView( factory = { context -> CreditCardInputView(context) - .apply { load(Snabble.checkedInProject.value?.id,paymentMethod, uiState) } + .apply { load(Snabble.checkedInProject.value?.id, paymentMethod, uiState.formUrl) } } ) } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index d67a3d34f1..a33e3b5da5 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -18,12 +19,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import io.snabble.sdk.BuildConfig import io.snabble.sdk.ui.R import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager import io.snabble.sdk.ui.payment.telecash.domain.Address @@ -47,17 +48,6 @@ fun CustomerInfoInputScreen( var state by remember { mutableStateOf("") } var country by remember { mutableStateOf("") } - if (BuildConfig.DEBUG) { - name = "Max Mustermann" - phoneNumber = "+491729973186" - email = "max.mustermann@example.com123" - street = "Fakestr. 123" - zip = "12345" - city = "Bonn" - state = "NRW" - country = "DE" - } - val textFieldManager = rememberTextFieldManager() val createCustomerInfo: () -> CustomerInfo = { @@ -82,7 +72,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = name, - onValueChanged = { name = it }, + onValueChanged = { + name = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_fullName), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -91,7 +84,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = phoneNumber, - onValueChanged = { phoneNumber = it }, + onValueChanged = { + phoneNumber = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_phoneNumber), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -104,7 +100,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = email, - onValueChanged = { email = it }, + onValueChanged = { + email = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_email), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -113,7 +112,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = street, - onValueChanged = { street = it }, + onValueChanged = { + street = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_street), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -122,7 +124,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = zip, - onValueChanged = { zip = it }, + onValueChanged = { + zip = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_zip), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -131,7 +136,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = city, - onValueChanged = { city = it }, + onValueChanged = { + city = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_city), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -140,7 +148,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = state, - onValueChanged = { state = it }, + onValueChanged = { + state = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_state), keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } @@ -149,7 +160,10 @@ fun CustomerInfoInputScreen( TextInput( modifier = Modifier.fillMaxWidth(), value = country, - onValueChanged = { country = it }, + onValueChanged = { + country = it + if (showError) onErrorProcessed() + }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_country), keyboardOptions = KeyboardOptions( imeAction = ImeAction.Send, @@ -162,11 +176,24 @@ fun CustomerInfoInputScreen( } ) ) - Button( + Column( modifier = Modifier.fillMaxWidth(), - onClick = { onSendAction(createCustomerInfo()) } + verticalArrangement = Arrangement.spacedBy(4.dp), ) { - Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { onSendAction(createCustomerInfo()) }, + enabled = !isLoading + ) { + Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) + } + if (showError) { + Text( + stringResource(R.string.Snabble_Payment_CustomerInfo_error), + style = MaterialTheme.typography.bodyMedium.copy(color = Color.Red), + modifier = Modifier.padding(horizontal = 16.dp) + ) + } } TextButton( modifier = Modifier.fillMaxWidth(), diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 358844078b..2c83427601 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -9,8 +9,8 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import io.snabble.sdk.PaymentMethod import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl -import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo +import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -22,20 +22,32 @@ internal class TelecashViewModel( private val savedStateHandle: SavedStateHandle ) : ViewModel() { - private val _uiState = MutableStateFlow("") - val uiState: StateFlow = _uiState.asStateFlow() + private val _uiState = MutableStateFlow(UiState()) + val uiState: StateFlow = _uiState.asStateFlow() fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } val paymentMethod = savedStateHandle.get("paymentType") ?: return@launch val result = telecashRepo.sendUserData(customerInfo, paymentMethod) result.onSuccess { info -> - _uiState.update { info.formUrl } + _uiState.update { + it.copy( + isLoading = false, + formUrl = info.formUrl, + deletePreAuthUrl = it.deletePreAuthUrl + ) + } + }.onFailure { + _uiState.update { it.copy(isLoading = false, showError = true) } } - // TBI: On error? } } + fun errorHandled() { + _uiState.update { it.copy(showError = false) } + } + companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { @@ -46,3 +58,10 @@ internal class TelecashViewModel( } } } + +data class UiState( + val isLoading: Boolean = false, + val formUrl: String? = null, + val deletePreAuthUrl: String? = null, + val showError: Boolean = false +) diff --git a/ui/src/main/res/values-de/strings.xml b/ui/src/main/res/values-de/strings.xml index fce6a3c3a6..8fc6e56c89 100644 --- a/ui/src/main/res/values-de/strings.xml +++ b/ui/src/main/res/values-de/strings.xml @@ -104,6 +104,7 @@ Stadt Land E-Mail-Adresse + Bitte überprüfe deine Eingaben und versuche es erneut Vor- und Nachname Weiter Telefonnummer diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 9dea4b3080..466654a6c0 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -104,6 +104,7 @@ City Country Email + Please check your entries and try again Full name Continue Phone number From 5b3ea480f53825a067a430d8835f463220eadc85 Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 12:26:43 +0200 Subject: [PATCH 12/25] WIP: Add country and state json and integrate it in viewmodel --- ui/src/main/assets/countriesAndStates.json | 2261 +++++++++++++++++ .../telecash/CustomerInfoInputFragment.kt | 3 +- .../ui/payment/telecash/TelecashViewModel.kt | 30 +- .../telecash/TelecashViewModelFactory.kt | 33 + .../data/CountryItemsRepositoryImpl.kt | 12 + .../country/LocalCountryItemsDataSource.kt | 8 + .../LocalCountryItemsDataSourceImpl.kt | 39 + .../telecash/data/dto/country/CountryDto.kt | 8 + .../telecash/data/dto/country/StateDto.kt | 5 + .../telecash/domain/CountryItemsRepository.kt | 8 + .../domain/model/country/CountryItem.kt | 5 + .../domain/model/country/StateItem.kt | 12 + 12 files changed, 2405 insertions(+), 19 deletions(-) create mode 100644 ui/src/main/assets/countriesAndStates.json create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt diff --git a/ui/src/main/assets/countriesAndStates.json b/ui/src/main/assets/countriesAndStates.json new file mode 100644 index 0000000000..d0d50f199e --- /dev/null +++ b/ui/src/main/assets/countriesAndStates.json @@ -0,0 +1,2261 @@ +[ + { + "code": "AF" + }, + { + "code": "AL" + }, + { + "code": "DZ" + }, + { + "code": "AS" + }, + { + "code": "AD" + }, + { + "code": "AO" + }, + { + "code": "AI" + }, + { + "code": "AQ" + }, + { + "code": "AG" + }, + { + "code": "AR", + "states": [ + { + "code" : "B", + "name" : "Buenos Aires" + }, + { + "code" : "K", + "name" : "Catamarca" + }, + { + "code" : "H", + "name" : "Chaco" + }, + { + "code" : "U", + "name" : "Chubut" + }, + { + "code" : "C", + "name" : "Ciudad Autónoma de Buenos Aires" + }, + { + "code" : "W", + "name" : "Corrientes" + }, + { + "code" : "X", + "name" : "Córdoba" + }, + { + "code" : "E", + "name" : "Entre Ríos" + }, + { + "code" : "P", + "name" : "Formosa" + }, + { + "code" : "Y", + "name" : "Jujuy" + }, + { + "code" : "L", + "name" : "La Pampa" + }, + { + "code" : "F", + "name" : "La Rioja" + }, + { + "code" : "M", + "name" : "Mendoza" + }, + { + "code" : "N", + "name" : "Misiones" + }, + { + "code" : "Q", + "name" : "Neuquén" + }, + { + "code" : "R", + "name" : "Río Negro" + }, + { + "code" : "A", + "name" : "Salta" + }, + { + "code" : "J", + "name" : "San Juan" + }, + { + "code" : "D", + "name" : "San Luis" + }, + { + "code" : "Z", + "name" : "Santa Cruz" + }, + { + "code" : "S", + "name" : "Santa Fe" + }, + { + "code" : "G", + "name" : "Santiago del Estero" + }, + { + "code" : "V", + "name" : "Tierra del Fuego" + }, + { + "code" : "T", + "name" : "Tucumán" + } + ] + }, + { + "code": "AM" + }, + { + "code": "AW" + }, + { + "code": "AU" + }, + { + "code": "AT" + }, + { + "code": "AZ" + }, + { + "code": "BS" + }, + { + "code": "BH" + }, + { + "code": "BD" + }, + { + "code": "BB" + }, + { + "code": "BY" + }, + { + "code": "BE" + }, + { + "code": "BZ" + }, + { + "code": "BJ" + }, + { + "code": "BM" + }, + { + "code": "BT" + }, + { + "code": "BO" + }, + { + "code": "BQ" + }, + { + "code": "BA" + }, + { + "code": "BW" + }, + { + "code": "BV" + }, + { + "code": "BR", + "states": [ + { + "code" : "AC", + "name" : "Acre" + }, + { + "code" : "AL", + "name" : "Alagoas" + }, + { + "code" : "AP", + "name" : "Amapá" + }, + { + "code" : "AM", + "name" : "Amazonas" + }, + { + "code" : "BA", + "name" : "Bahia" + }, + { + "code" : "CE", + "name" : "Ceará" + }, + { + "code" : "DF", + "name" : "Distrito Federal" + }, + { + "code" : "ES", + "name" : "Espírito Santo" + }, + { + "code" : "GO", + "name" : "Goiás" + }, + { + "code" : "MA", + "name" : "Maranhão" + }, + { + "code" : "MT", + "name" : "Mato Grosso" + }, + { + "code" : "MS", + "name" : "Mato Grosso do Sul" + }, + { + "code" : "MG", + "name" : "Minas Gerais" + }, + { + "code" : "PR", + "name" : "Paraná" + }, + { + "code" : "PB", + "name" : "Paraíba" + }, + { + "code" : "PA", + "name" : "Pará" + }, + { + "code" : "PE", + "name" : "Pernambuco" + }, + { + "code" : "PI", + "name" : "Piauí" + }, + { + "code" : "RN", + "name" : "Rio Grande do Norte" + }, + { + "code" : "RS", + "name" : "Rio Grande do Sul" + }, + { + "code" : "RJ", + "name" : "Rio de Janeiro" + }, + { + "code" : "RO", + "name" : "Rondônia" + }, + { + "code" : "RR", + "name" : "Roraima" + }, + { + "code" : "SC", + "name" : "Santa Catarina" + }, + { + "code" : "SE", + "name" : "Sergipe" + }, + { + "code" : "SP", + "name" : "São Paulo" + }, + { + "code" : "TO", + "name" : "Tocantins" + } + ] + }, + { + "code": "IO" + }, + { + "code": "BN" + }, + { + "code": "BG" + }, + { + "code": "BF" + }, + { + "code": "BI" + }, + { + "code": "KH" + }, + { + "code": "CM" + }, + { + "code": "CA", + "states": [ + { + "code" : "AB", + "name" : "Alberta" + }, + { + "code" : "BC", + "name" : "British Columbia" + }, + { + "code" : "MB", + "name" : "Manitoba" + }, + { + "code" : "NB", + "name" : "New Brunswick" + }, + { + "code" : "NL", + "name" : "Newfoundland and Labrador" + }, + { + "code" : "NT", + "name" : "Northwest Territories" + }, + { + "code" : "NS", + "name" : "Nova Scotia" + }, + { + "code" : "NU", + "name" : "Nunavut" + }, + { + "code" : "ON", + "name" : "Ontario" + }, + { + "code" : "PE", + "name" : "Prince Edward Island" + }, + { + "code" : "QC", + "name" : "Quebec" + }, + { + "code" : "SK", + "name" : "Saskatchewan" + }, + { + "code" : "YT", + "name" : "Yukon" + } + ] + }, + { + "code": "CV" + }, + { + "code": "KY" + }, + { + "code": "CF" + }, + { + "code": "TD" + }, + { + "code": "CL" + }, + { + "code": "CN", + "states": [ + { + "code" : "11", + "name" : "Beijing" + }, + { + "code" : "43", + "name" : "Hunan" + }, + { + "code" : "12", + "name" : "Tianjin" + }, + { + "code" : "44", + "name" : "Guangdong" + }, + { + "code" : "13", + "name" : "Hebei" + }, + { + "code" : "45", + "name" : "Guangxi" + }, + { + "code" : "14", + "name" : "Shanxi" + }, + { + "code" : "46", + "name" : "Hainan" + }, + { + "code" : "15", + "name" : "Nei Mongol (mn), (Inner Mongolia)" + }, + { + "code" : "50", + "name" : "Chongqing" + }, + { + "code" : "21", + "name" : "Liaoning" + }, + { + "code" : "51", + "name" : "Sichuan" + }, + { + "code" : "22", + "name" : "Jilin" + }, + { + "code" : "52", + "name" : "Guizhou" + }, + { + "code" : "23", + "name" : "Heilongjiang" + }, + { + "code" : "53", + "name" : "Yunnan" + }, + { + "code" : "31", + "name" : "Shanghai" + }, + { + "code" : "54", + "name" : "Xizang (Tibet)" + }, + { + "code" : "32", + "name" : "Jiangsu" + }, + { + "code" : "61", + "name" : "Shaanxi" + }, + { + "code" : "33", + "name" : "Zhejiang" + }, + { + "code" : "62", + "name" : "Gansu" + }, + { + "code" : "34", + "name" : "Anhui" + }, + { + "code" : "63", + "name" : "Qinghai" + }, + { + "code" : "35", + "name" : "Fujian" + }, + { + "code" : "64", + "name" : "Ningxia" + }, + { + "code" : "36", + "name" : "Jiangxi" + }, + { + "code" : "65", + "name" : "Xinjiang" + }, + { + "code" : "37", + "name" : "Shandong" + }, + { + "code" : "71", + "name" : "Taiwan" + }, + { + "code" : "41", + "name" : "Henan" + }, + { + "code" : "91", + "name" : "Hong Kong (en) Xianggang (zh)" + }, + { + "code" : "42", + "name" : "Hubei" + }, + { + "code" : "92", + "name" : "Macao (en) Aomen (zh)" + } + ] + }, + { + "code": "CW" + }, + { + "code": "CX" + }, + { + "code": "CC" + }, + { + "code": "CO" + }, + { + "code": "KM" + }, + { + "code": "CG" + }, + { + "code": "CK" + }, + { + "code": "CR" + }, + { + "code": "CI" + }, + { + "code": "HR" + }, + { + "code": "CU" + }, + { + "code": "CY" + }, + { + "code": "CZ" + }, + { + "code": "DK" + }, + { + "code": "DJ" + }, + { + "code": "DM" + }, + { + "code": "DO" + }, + { + "code": "TL" + }, + { + "code": "EC" + }, + { + "code": "EG" + }, + { + "code": "SV" + }, + { + "code": "GQ" + }, + { + "code": "ER" + }, + { + "code": "EE" + }, + { + "code": "ET" + }, + { + "code": "FK" + }, + { + "code": "FO" + }, + { + "code": "FJ" + }, + { + "code": "FI" + }, + { + "code": "FR" + }, + { + "code": "GF" + }, + { + "code": "PF" + }, + { + "code": "TF" + }, + { + "code": "GA" + }, + { + "code": "GM" + }, + { + "code": "GE" + }, + { + "code": "DE" + }, + { + "code": "GH" + }, + { + "code": "GI" + }, + { + "code": "GR" + }, + { + "code": "GL" + }, + { + "code": "GD" + }, + { + "code": "GP" + }, + { + "code": "GU" + }, + { + "code": "GT" + }, + { + "code": "GN" + }, + { + "code": "GW" + }, + { + "code": "GY" + }, + { + "code": "HT" + }, + { + "code": "HM" + }, + { + "code": "HN" + }, + { + "code": "HK" + }, + { + "code": "HU" + }, + { + "code": "IS" + }, + { + "code": "IN", + "states": [ + { + "code" : "AN", + "name" : "Andaman and Nicobar Islands" + }, + { + "code" : "AP", + "name" : "Andhra Pradesh" + }, + { + "code" : "AR", + "name" : "Arunachal Pradesh" + }, + { + "code" : "AS", + "name" : "Assam" + }, + { + "code" : "BR", + "name" : "Bihar" + }, + { + "code" : "CH", + "name" : "Chandigarh" + }, + { + "code" : "CT", + "name" : "Chhattisgarh" + }, + { + "code" : "DN", + "name" : "Dadra and Nagar Haveli" + }, + { + "code" : "DD", + "name" : "Daman and Diu" + }, + { + "code" : "DL", + "name" : "Delhi" + }, + { + "code" : "GA", + "name" : "Goa" + }, + { + "code" : "GJ", + "name" : "Gujarat" + }, + { + "code" : "HR", + "name" : "Haryana" + }, + { + "code" : "HP", + "name" : "Himachal Pradesh" + }, + { + "code" : "JK", + "name" : "Jammu and Kashmir" + }, + { + "code" : "JH", + "name" : "Jharkhand" + }, + { + "code" : "KA", + "name" : "Karnataka" + }, + { + "code" : "KL", + "name" : "Kerala" + }, + { + "code" : "LD", + "name" : "Lakshadweep" + }, + { + "code" : "MP", + "name" : "Madhya Pradesh" + }, + { + "code" : "MH", + "name" : "Maharashtra" + }, + { + "code" : "MN", + "name" : "Manipur" + }, + { + "code" : "ML", + "name" : "Meghalaya" + }, + { + "code" : "MZ", + "name" : "Mizoram" + }, + { + "code" : "NL", + "name" : "Nagaland" + }, + { + "code" : "OR", + "name" : "Odisha" + }, + { + "code" : "PY", + "name" : "Puducherry" + }, + { + "code" : "PB", + "name" : "Punjab" + }, + { + "code" : "RJ", + "name" : "Rajasthan" + }, + { + "code" : "SK", + "name" : "Sikkim" + }, + { + "code" : "TN", + "name" : "Tamil Nadu" + }, + { + "code" : "TG", + "name" : "Telangana" + }, + { + "code" : "TR", + "name" : "Tripura" + }, + { + "code" : "UP", + "name" : "Uttar Pradesh" + }, + { + "code" : "UT", + "name" : "Uttarakhand" + }, + { + "code" : "WB", + "name" : "West Bengal" + } + ] + }, + { + "code": "ID", + "states": [ + { + "code" : "AC", + "name" : "Aceh, Aceh" + }, + { + "code" : "BA", + "name" : "Bali, Bali" + }, + { + "code" : "BB", + "name" : "Bangka Belitung, Bangka-Belitung Islands" + }, + { + "code" : "BT", + "name" : "Banten, Banten" + }, + { + "code" : "BE", + "name" : "Bengkulu, Bengkulu" + }, + { + "code" : "GO", + "name" : "Gorontalo, Gorontalo" + }, + { + "code" : "JK", + "name" : "Jakarta Raya, Jakarta" + }, + { + "code" : "JA", + "name" : "Jambi, Jambi" + }, + { + "code" : "JB", + "name" : "Jawa Barat, West Java" + }, + { + "code" : "JT", + "name" : "Jawa Tengah, Central Java" + }, + { + "code" : "JI", + "name" : "Jawa Timur, East Java" + }, + { + "code" : "KB", + "name" : "Kalimantan Baratm, West Kalimantan" + }, + { + "code" : "KS", + "name" : "Kalimantan Selatan, South Kalimantan" + }, + { + "code" : "KT", + "name" : "Kalimantan Tengah, Central Kalimantan" + }, + { + "code" : "KI", + "name" : "Kalimantan Timur, East Kalimantan" + }, + { + "code" : "KU", + "name" : "Kalimantan Utara, North Kalimantan" + }, + { + "code" : "KR", + "name" : "Kepulauan Riau, Riau Islands" + }, + { + "code" : "LA", + "name" : "Lampung, Lampung" + }, + { + "code" : "MU", + "name" : "Maluku Utara, North Maluku" + }, + { + "code" : "MA", + "name" : "Maluku, Maluku" + }, + { + "code" : "NB", + "name" : "Nusa Tenggara Barat, West Nusa Tenggara" + }, + { + "code" : "NT", + "name" : "Nusa Tenggara Timur, East Nusa Tenggara" + }, + { + "code" : "PB", + "name" : "Papua Barat, West Papua" + }, + { + "code" : "PA", + "name" : "Papua, Papua" + }, + { + "code" : "RI", + "name" : "Riau, Riau" + }, + { + "code" : "SR", + "name" : "Sulawesi Barat, West Sulawesi" + }, + { + "code" : "SN", + "name" : "Sulawesi Selatan, South Sulawesi" + }, + { + "code" : "ST", + "name" : "Sulawesi Tengah, Central Sulawesi" + }, + { + "code" : "SG", + "name" : "Sulawesi Tenggara, Southeast Sulawesi" + }, + { + "code" : "SA", + "name" : "Sulawesi Utara, North Sulawesi" + }, + { + "code" : "SB", + "name" : "Sumatera Barat, West Sumatra" + }, + { + "code" : "SS", + "name" : "Sumatera Selatan, South Sumatra" + }, + { + "code" : "SU", + "name" : "Sumatera Utara, North Sumatra" + }, + { + "code" : "YO", + "name" : "Yogyakarta, Yogyakarta" + } + ] + }, + { + "code": "IR" + }, + { + "code": "IQ" + }, + { + "code": "IE" + }, + { + "code": "IL" + }, + { + "code": "IT" + }, + { + "code": "JM" + }, + { + "code": "JP", + "states": [ + { + "code" : "23", + "name" : "Aiti (Aichi)" + }, + { + "code" : "05", + "name" : "Akita" + }, + { + "code" : "02", + "name" : "Aomori" + }, + { + "code" : "38", + "name" : "Ehime" + }, + { + "code" : "21", + "name" : "Gihu (Gifu)" + }, + { + "code" : "10", + "name" : "Gunma" + }, + { + "code" : "34", + "name" : "Hirosima (Hiroshima)" + }, + { + "code" : "01", + "name" : "Hokkaidô (Hokkaido)" + }, + { + "code" : "18", + "name" : "Hukui (Fukui)" + }, + { + "code" : "40", + "name" : "Hukuoka (Fukuoka)" + }, + { + "code" : "07", + "name" : "Hukusima (Fukushima)" + }, + { + "code" : "28", + "name" : "Hyôgo (Hyogo)" + }, + { + "code" : "08", + "name" : "Ibaraki" + }, + { + "code" : "17", + "name" : "Isikawa (Ishikawa)" + }, + { + "code" : "03", + "name" : "Iwate" + }, + { + "code" : "37", + "name" : "Kagawa" + }, + { + "code" : "46", + "name" : "Kagosima (Kagoshima)" + }, + { + "code" : "14", + "name" : "Kanagawa" + }, + { + "code" : "43", + "name" : "Kumamoto" + }, + { + "code" : "26", + "name" : "Kyôto (Kyoto)" + }, + { + "code" : "39", + "name" : "Kôti (Kochi)" + }, + { + "code" : "24", + "name" : "Mie" + }, + { + "code" : "04", + "name" : "Miyagi" + }, + { + "code" : "45", + "name" : "Miyazaki" + }, + { + "code" : "20", + "name" : "Nagano" + }, + { + "code" : "42", + "name" : "Nagasaki" + }, + { + "code" : "29", + "name" : "Nara" + }, + { + "code" : "15", + "name" : "Niigata" + }, + { + "code" : "33", + "name" : "Okayama" + }, + { + "code" : "47", + "name" : "Okinawa" + }, + { + "code" : "41", + "name" : "Saga" + }, + { + "code" : "11", + "name" : "Saitama" + }, + { + "code" : "25", + "name" : "Siga (Shiga)" + }, + { + "code" : "32", + "name" : "Simane (Shimane)" + }, + { + "code" : "22", + "name" : "Sizuoka (Shizuoka)" + }, + { + "code" : "12", + "name" : "Tiba (Chiba)" + }, + { + "code" : "36", + "name" : "Tokusima (Tokushima)" + }, + { + "code" : "09", + "name" : "Totigi (Tochigi)" + }, + { + "code" : "31", + "name" : "Tottori" + }, + { + "code" : "16", + "name" : "Toyama" + }, + { + "code" : "13", + "name" : "Tôkyô (Tokyo)" + }, + { + "code" : "30", + "name" : "Wakayama" + }, + { + "code" : "06", + "name" : "Yamagata" + }, + { + "code" : "35", + "name" : "Yamaguti (Yamaguchi)" + }, + { + "code" : "19", + "name" : "Yamanasi (Yamanashi)" + }, + { + "code" : "44", + "name" : "Ôita (Oita)" + }, + { + "code" : "27", + "name" : "Ôsaka (Osaka)" + } + ] + }, + { + "code": "JO" + }, + { + "code": "KZ" + }, + { + "code": "KE" + }, + { + "code": "KI" + }, + { + "code": "KP" + }, + { + "code": "KR" + }, + { + "code": "KW" + }, + { + "code": "KG" + }, + { + "code": "LA" + }, + { + "code": "LV" + }, + { + "code": "LB" + }, + { + "code": "LS" + }, + { + "code": "LR" + }, + { + "code": "LY" + }, + { + "code": "LI" + }, + { + "code": "LT" + }, + { + "code": "LU" + }, + { + "code": "MO" + }, + { + "code": "MG" + }, + { + "code": "MW" + }, + { + "code": "MY" + }, + { + "code": "MV" + }, + { + "code": "ML" + }, + { + "code": "MT" + }, + { + "code": "MH" + }, + { + "code": "MQ" + }, + { + "code": "MR" + }, + { + "code": "MU" + }, + { + "code": "YT" + }, + { + "code": "MX", + "states": [ + { + "code" : "AGU", + "name" : "Aguascalientes" + }, + { + "code" : "BCN", + "name" : "Baja California" + }, + { + "code" : "BCS", + "name" : "Baja California Sur" + }, + { + "code" : "CAM", + "name" : "Campeche" + }, + { + "code" : "CHP", + "name" : "Chiapas" + }, + { + "code" : "CHH", + "name" : "Chihuahua" + }, + { + "code" : "COA", + "name" : "Coahuila" + }, + { + "code" : "COL", + "name" : "Colima" + }, + { + "code" : "DIF", + "name" : "Distrito Federal" + }, + { + "code" : "DUR", + "name" : "Durango" + }, + { + "code" : "GUA", + "name" : "Guanajuato" + }, + { + "code" : "GRO", + "name" : "Guerrero" + }, + { + "code" : "HID", + "name" : "Hidalgo" + }, + { + "code" : "JAL", + "name" : "Jalisco" + }, + { + "code" : "MIC", + "name" : "Michoacán" + }, + { + "code" : "MOR", + "name" : "Morelos" + }, + { + "code" : "MEX", + "name" : "México" + }, + { + "code" : "NAY", + "name" : "Nayarit" + }, + { + "code" : "NLE", + "name" : "Nuevo León" + }, + { + "code" : "OAX", + "name" : "Oaxaca" + }, + { + "code" : "PUE", + "name" : "Puebla" + }, + { + "code" : "QUE", + "name" : "Querétaro" + }, + { + "code" : "ROO", + "name" : "Quintana Roo" + }, + { + "code" : "SLP", + "name" : "San Luis Potosí" + }, + { + "code" : "SIN", + "name" : "Sinaloa" + }, + { + "code" : "SON", + "name" : "Sonora" + }, + { + "code" : "TAB", + "name" : "Tabasco" + }, + { + "code" : "TAM", + "name" : "Tamaulipas" + }, + { + "code" : "TLA", + "name" : "Tlaxcala" + }, + { + "code" : "VER", + "name" : "Veracruz" + }, + { + "code" : "YUC", + "name" : "Yucatán" + }, + { + "code" : "ZAC", + "name" : "Zacatecas" + } + ] + }, + { + "code": "FM" + }, + { + "code": "MD" + }, + { + "code": "MC" + }, + { + "code": "MN" + }, + { + "code": "MS" + }, + { + "code": "MA" + }, + { + "code": "MZ" + }, + { + "code": "MM" + }, + { + "code": "NA" + }, + { + "code": "NR" + }, + { + "code": "NP" + }, + { + "code": "NL" + }, + { + "code": "NC" + }, + { + "code": "NZ" + }, + { + "code": "NI" + }, + { + "code": "NE" + }, + { + "code": "NG" + }, + { + "code": "NU" + }, + { + "code": "NF" + }, + { + "code": "MP" + }, + { + "code": "NO" + }, + { + "code": "OM" + }, + { + "code": "PK" + }, + { + "code": "PW" + }, + { + "code": "PA" + }, + { + "code": "PG" + }, + { + "code": "PY" + }, + { + "code": "PE" + }, + { + "code": "PH" + }, + { + "code": "PN" + }, + { + "code": "PL" + }, + { + "code": "PT" + }, + { + "code": "PR" + }, + { + "code": "QA" + }, + { + "code": "MK" + }, + { + "code": "RE" + }, + { + "code": "RO" + }, + { + "code": "RU" + }, + { + "code": "RW" + }, + { + "code": "KN" + }, + { + "code": "LC" + }, + { + "code": "VC" + }, + { + "code": "WS" + }, + { + "code": "SM" + }, + { + "code": "ST" + }, + { + "code": "SA" + }, + { + "code": "SN" + }, + { + "code": "SC" + }, + { + "code": "SL" + }, + { + "code": "SG" + }, + { + "code": "SK" + }, + { + "code": "SI" + }, + { + "code": "SB" + }, + { + "code": "SO" + }, + { + "code": "SX" + }, + { + "code": "ZA" + }, + { + "code": "GS" + }, + { + "code": "ES" + }, + { + "code": "LK" + }, + { + "code": "SH" + }, + { + "code": "PM" + }, + { + "code": "SD" + }, + { + "code": "SR" + }, + { + "code": "SJ" + }, + { + "code": "SZ" + }, + { + "code": "SE" + }, + { + "code": "CH" + }, + { + "code": "SY" + }, + { + "code": "TW" + }, + { + "code": "TJ" + }, + { + "code": "TZ" + }, + { + "code": "TH", + "states": [ + { + "code" : "37", + "name" : "Amnat Charoen" + }, + { + "code" : "15", + "name" : "Ang Thong" + }, + { + "code" : "38", + "name" : "Bueng Kan" + }, + { + "code" : "31", + "name" : "Buri Ram" + }, + { + "code" : "24", + "name" : "Chachoengsao" + }, + { + "code" : "18", + "name" : "Chai Nat" + }, + { + "code" : "36", + "name" : "Chaiyaphum" + }, + { + "code" : "22", + "name" : "Chanthaburi" + }, + { + "code" : "50", + "name" : "Chiang Mai" + }, + { + "code" : "57", + "name" : "Chiang Rai" + }, + { + "code" : "20", + "name" : "Chon Buri" + }, + { + "code" : "86", + "name" : "Chumphon" + }, + { + "code" : "46", + "name" : "Kalasin" + }, + { + "code" : "62", + "name" : "Kamphaeng Phet" + }, + { + "code" : "71", + "name" : "Kanchanaburi" + }, + { + "code" : "40", + "name" : "Khon Kaen" + }, + { + "code" : "81", + "name" : "Krabi" + }, + { + "code" : "10", + "name" : "Krung Thep Maha Nakhon [Bangkok]" + }, + { + "code" : "52", + "name" : "Lampang" + }, + { + "code" : "51", + "name" : "Lamphun" + }, + { + "code" : "42", + "name" : "Loei" + }, + { + "code" : "16", + "name" : "Lop Buri" + }, + { + "code" : "58", + "name" : "Mae Hong Son" + }, + { + "code" : "44", + "name" : "Maha Sarakham" + }, + { + "code" : "49", + "name" : "Mukdahan" + }, + { + "code" : "26", + "name" : "Nakhon Nayok" + }, + { + "code" : "73", + "name" : "Nakhon Pathom" + }, + { + "code" : "48", + "name" : "Nakhon Phanom" + }, + { + "code" : "30", + "name" : "Nakhon Ratchasima" + }, + { + "code" : "60", + "name" : "Nakhon Sawan" + }, + { + "code" : "80", + "name" : "Nakhon Si Thammarat" + }, + { + "code" : "55", + "name" : "Nan" + }, + { + "code" : "96", + "name" : "Narathiwat" + }, + { + "code" : "39", + "name" : "Nong Bua Lam Phu" + }, + { + "code" : "43", + "name" : "Nong Khai" + }, + { + "code" : "12", + "name" : "Nonthaburi" + }, + { + "code" : "13", + "name" : "Pathum Thani" + }, + { + "code" : "94", + "name" : "Pattani" + }, + { + "code" : "82", + "name" : "Phangnga" + }, + { + "code" : "93", + "name" : "Phatthalung" + }, + { + "code" : "S", + "name" : "Phatthaya" + }, + { + "code" : "56", + "name" : "Phayao" + }, + { + "code" : "67", + "name" : "Phetchabun" + }, + { + "code" : "76", + "name" : "Phetchaburi" + }, + { + "code" : "66", + "name" : "Phichit" + }, + { + "code" : "65", + "name" : "Phitsanulok" + }, + { + "code" : "14", + "name" : "Phra Nakhon Si Ayutthaya" + }, + { + "code" : "54", + "name" : "Phrae" + }, + { + "code" : "83", + "name" : "Phuket" + }, + { + "code" : "25", + "name" : "Prachin Buri" + }, + { + "code" : "77", + "name" : "Prachuap Khiri Khan" + }, + { + "code" : "85", + "name" : "Ranong" + }, + { + "code" : "70", + "name" : "Ratchaburi" + }, + { + "code" : "21", + "name" : "Rayong" + }, + { + "code" : "45", + "name" : "Roi Et" + }, + { + "code" : "27", + "name" : "Sa Kaeo" + }, + { + "code" : "47", + "name" : "Sakon Nakhon" + }, + { + "code" : "11", + "name" : "Samut Prakan" + }, + { + "code" : "74", + "name" : "Samut Sakhon" + }, + { + "code" : "75", + "name" : "Samut Songkhram" + }, + { + "code" : "19", + "name" : "Saraburi" + }, + { + "code" : "91", + "name" : "Satun" + }, + { + "code" : "33", + "name" : "Si Sa Ket" + }, + { + "code" : "17", + "name" : "Sing Buri" + }, + { + "code" : "90", + "name" : "Songkhla" + }, + { + "code" : "64", + "name" : "Sukhothai" + }, + { + "code" : "72", + "name" : "Suphan Buri" + }, + { + "code" : "84", + "name" : "Surat Thani" + }, + { + "code" : "32", + "name" : "Surin" + }, + { + "code" : "63", + "name" : "Tak" + }, + { + "code" : "92", + "name" : "Trang" + }, + { + "code" : "23", + "name" : "Trat" + }, + { + "code" : "34", + "name" : "Ubon Ratchathani" + }, + { + "code" : "41", + "name" : "Udon Thani" + }, + { + "code" : "61", + "name" : "Uthai Thani" + }, + { + "code" : "53", + "name" : "Uttaradit" + }, + { + "code" : "95", + "name" : "Yala" + }, + { + "code" : "35", + "name" : "Yasothon" + } + ] + }, + { + "code": "TG" + }, + { + "code": "TK" + }, + { + "code": "TO" + }, + { + "code": "TT" + }, + { + "code": "TN" + }, + { + "code": "TR" + }, + { + "code": "TM" + }, + { + "code": "TC" + }, + { + "code": "TV" + }, + { + "code": "UG" + }, + { + "code": "UA" + }, + { + "code": "AE" + }, + { + "code": "GB" + }, + { + "code": "US", + "states": [ + { + "code" : "AL", + "name" : "Alabama" + }, + { + "code" : "AK", + "name" : "Alaska" + }, + { + "code" : "AS", + "name" : "American Samoa" + }, + { + "code" : "AZ", + "name" : "Arizona" + }, + { + "code" : "AR", + "name" : "Arkansas" + }, + { + "code" : "CA", + "name" : "California" + }, + { + "code" : "CO", + "name" : "Colorado" + }, + { + "code" : "CT", + "name" : "Connecticut" + }, + { + "code" : "DE", + "name" : "Delaware" + }, + { + "code" : "DC", + "name" : "District of Columbia" + }, + { + "code" : "FL", + "name" : "Florida" + }, + { + "code" : "GA", + "name" : "Georgia" + }, + { + "code" : "GU", + "name" : "Guam" + }, + { + "code" : "HI", + "name" : "Hawaii" + }, + { + "code" : "ID", + "name" : "Idaho" + }, + { + "code" : "IL", + "name" : "Illinois" + }, + { + "code" : "IN", + "name" : "Indiana" + }, + { + "code" : "IA", + "name" : "Iowa" + }, + { + "code" : "KS", + "name" : "Kansas" + }, + { + "code" : "KY", + "name" : "Kentucky" + }, + { + "code" : "LA", + "name" : "Louisiana" + }, + { + "code" : "ME", + "name" : "Maine" + }, + { + "code" : "MD", + "name" : "Maryland" + }, + { + "code" : "MA", + "name" : "Massachusetts" + }, + { + "code" : "MI", + "name" : "Michigan" + }, + { + "code" : "MN", + "name" : "Minnesota" + }, + { + "code" : "MS", + "name" : "Mississippi" + }, + { + "code" : "MO", + "name" : "Missouri" + }, + { + "code" : "MT", + "name" : "Montana" + }, + { + "code" : "NE", + "name" : "Nebraska" + }, + { + "code" : "NV", + "name" : "Nevada" + }, + { + "code" : "NH", + "name" : "New Hampshire" + }, + { + "code" : "NJ", + "name" : "New Jersey" + }, + { + "code" : "NM", + "name" : "New Mexico" + }, + { + "code" : "NY", + "name" : "New York" + }, + { + "code" : "NC", + "name" : "North Carolina" + }, + { + "code" : "ND", + "name" : "North Dakota" + }, + { + "code" : "MP", + "name" : "Northern Mariana Islands" + }, + { + "code" : "OH", + "name" : "Ohio" + }, + { + "code" : "OK", + "name" : "Oklahoma" + }, + { + "code" : "OR", + "name" : "Oregon" + }, + { + "code" : "PA", + "name" : "Pennsylvania" + }, + { + "code" : "PR", + "name" : "Puerto Rico" + }, + { + "code" : "RI", + "name" : "Rhode Island" + }, + { + "code" : "SC", + "name" : "South Carolina" + }, + { + "code" : "SD", + "name" : "South Dakota" + }, + { + "code" : "TN", + "name" : "Tennessee" + }, + { + "code" : "TX", + "name" : "Texas" + }, + { + "code" : "UM", + "name" : "United States Minor" + }, + { + "code" : "UT", + "name" : "Utah" + }, + { + "code" : "VT", + "name" : "Vermont" + }, + { + "code" : "VI", + "name" : "Virgin Islands, U.S." + }, + { + "code" : "VA", + "name" : "Virginia" + }, + { + "code" : "WA", + "name" : "Washington" + }, + { + "code" : "WV", + "name" : "West Virginia" + }, + { + "code" : "WI", + "name" : "Wisconsin" + }, + { + "code" : "WY", + "name" : "Wyoming" + } + ] + }, + { + "code": "UY" + }, + { + "code": "UZ" + }, + { + "code": "VU" + }, + { + "code": "VA" + }, + { + "code": "VE" + }, + { + "code": "VN" + }, + { + "code": "VG" + }, + { + "code": "VI" + }, + { + "code": "WF" + }, + { + "code": "YE" + }, + { + "code": "CD" + }, + { + "code": "ZM" + }, + { + "code": "ZW" + } +] diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt index 2096b55bb6..88e9dd8c98 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt @@ -1,5 +1,6 @@ package io.snabble.sdk.ui.payment.telecash +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -18,7 +19,7 @@ import io.snabble.sdk.ui.utils.serializableExtra class CustomerInfoInputFragment : Fragment() { - private val viewModel: TelecashViewModel by viewModels { TelecashViewModel.Factory } + private val viewModel: TelecashViewModel by viewModels { TelecashViewModelFactory(requireContext()) } private lateinit var paymentMethod: PaymentMethod diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 358844078b..9ab54f3318 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -2,15 +2,13 @@ package io.snabble.sdk.ui.payment.telecash import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl -import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository +import io.snabble.sdk.extensions.xx +import io.snabble.sdk.ui.payment.CreditCardInputView +import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo +import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -18,16 +16,22 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch internal class TelecashViewModel( - private val telecashRepo: TelecashRepository = TelecashRepositoryImpl(), + private val telecashRepo: TelecashRepository, + private val countryItemsRepo: CountryItemsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val _uiState = MutableStateFlow("") val uiState: StateFlow = _uiState.asStateFlow() + init { + countryItemsRepo.loadCountryItems().xx("Countries") + } + fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { - val paymentMethod = savedStateHandle.get("paymentType") ?: return@launch + val paymentMethod = + savedStateHandle.get(CreditCardInputView.ARG_PAYMENT_TYPE) ?: return@launch val result = telecashRepo.sendUserData(customerInfo, paymentMethod) result.onSuccess { info -> _uiState.update { info.formUrl } @@ -35,14 +39,4 @@ internal class TelecashViewModel( // TBI: On error? } } - - companion object { - - val Factory: ViewModelProvider.Factory = viewModelFactory { - initializer { - val savedStateHandle = createSavedStateHandle() - TelecashViewModel(savedStateHandle = savedStateHandle) - } - } - } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt new file mode 100644 index 0000000000..361d60c7d7 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt @@ -0,0 +1,33 @@ +package io.snabble.sdk.ui.payment.telecash + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.CreationExtras +import io.snabble.sdk.ui.payment.telecash.data.CountryItemsRepositoryImpl +import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl +import io.snabble.sdk.ui.payment.telecash.data.country.LocalCountryItemsDataSourceImpl +import io.snabble.sdk.utils.GsonHolder + +class TelecashViewModelFactory(private val context: Context) : ViewModelProvider.Factory { + + override fun create(modelClass: Class, extras: CreationExtras): T { + if (!modelClass.isAssignableFrom(TelecashViewModel::class.java)) { + throw IllegalArgumentException("Unable to construct viewmodel") + } + + val savedStateHandle = extras.createSavedStateHandle() + @Suppress("UNCHECKED_CAST") + return TelecashViewModel( + telecashRepo = TelecashRepositoryImpl(), + countryItemsRepo = CountryItemsRepositoryImpl( + localCountryItemsDataSource = LocalCountryItemsDataSourceImpl( + assetManager = context.assets, + gson = GsonHolder.get() + ) + ), + savedStateHandle = savedStateHandle + ) as T + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt new file mode 100644 index 0000000000..7588d79803 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt @@ -0,0 +1,12 @@ +package io.snabble.sdk.ui.payment.telecash.data + +import io.snabble.sdk.ui.payment.telecash.data.country.LocalCountryItemsDataSource +import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem + +class CountryItemsRepositoryImpl( + private val localCountryItemsDataSource: LocalCountryItemsDataSource, +) : CountryItemsRepository { + + override fun loadCountryItems(): List = localCountryItemsDataSource.loadCountries() +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt new file mode 100644 index 0000000000..715f91f9b3 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.telecash.data.country + +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem + +interface LocalCountryItemsDataSource { + + fun loadCountries(): List +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt new file mode 100644 index 0000000000..dca4dd4c6f --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt @@ -0,0 +1,39 @@ +package io.snabble.sdk.ui.payment.telecash.data.country + +import android.content.res.AssetManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.tegut.tbox.account.details.domain.model.country.StateItem +import io.snabble.sdk.ui.payment.telecash.data.dto.country.CountryDto +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import java.util.Locale + +class LocalCountryItemsDataSourceImpl( + private val assetManager: AssetManager, + private val gson: Gson, +) : LocalCountryItemsDataSource { + + override fun loadCountries(): List { + val typeToken = object : TypeToken>() {}.type + return gson.fromJson>( + assetManager.open(COUNTRIES_AND_STATES_FILE).reader(), + typeToken + ) + .map { (countryCode, states) -> + CountryItem( + displayName = countryCode.displayName, + code = countryCode, + stateItems = states?.map { StateItem.from(it) } + ) + } + .sortedBy { it.displayName } + } + + companion object { + + private const val COUNTRIES_AND_STATES_FILE = "countriesAndStates.json" + } +} + +val String.displayName: String + get() = Locale("", this).displayName diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt new file mode 100644 index 0000000000..ada1a6150a --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.telecash.data.dto.country + +import com.google.gson.annotations.SerializedName + +data class CountryDto( + @SerializedName("code") val countryCode: String, + @SerializedName("states") val states: List? = null, +) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt new file mode 100644 index 0000000000..e958d587aa --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt @@ -0,0 +1,5 @@ +package io.snabble.sdk.ui.payment.telecash.data.dto.country + +import com.google.gson.annotations.SerializedName + +class StateDto(@SerializedName("name") val displayName: String, @SerializedName("code") val code: String) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt new file mode 100644 index 0000000000..a29c71f13b --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.telecash.domain + +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem + +interface CountryItemsRepository { + + fun loadCountryItems(): List +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt new file mode 100644 index 0000000000..fd6cc69fe8 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt @@ -0,0 +1,5 @@ +package io.snabble.sdk.ui.payment.telecash.domain.model.country + +import com.tegut.tbox.account.details.domain.model.country.StateItem + +data class CountryItem(val displayName: String, val code: String, val stateItems: List?) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt new file mode 100644 index 0000000000..841c0baa81 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt @@ -0,0 +1,12 @@ +package com.tegut.tbox.account.details.domain.model.country + +import io.snabble.sdk.ui.payment.telecash.data.dto.country.StateDto + +data class StateItem(val displayName: String, val code: String) { + companion object { + fun from(stateDto: StateDto) = StateItem( + displayName = stateDto.displayName, + code = stateDto.code + ) + } +} From 08258de3ee0b49bb1df12071c3f3ee1bfaabb423 Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 13:11:04 +0200 Subject: [PATCH 13/25] Add Country and State picker --- .../telecash/CustomerInfoInputFragment.kt | 4 +- .../telecash/CustomerInfoInputScreen.kt | 51 +++----- .../ui/payment/telecash/TelecashViewModel.kt | 10 +- .../telecash/widget/CountrySelectionMenu.kt | 123 ++++++++++++++++++ .../payment/telecash/widget/DropDownMenu.kt | 111 ++++++++++++++++ ui/src/main/res/values-de/strings.xml | 1 + ui/src/main/res/values/strings.xml | 1 + 7 files changed, 261 insertions(+), 40 deletions(-) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt index 9a9e6b1925..48b63ddb4b 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt @@ -1,6 +1,5 @@ package io.snabble.sdk.ui.payment.telecash -import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -37,7 +36,7 @@ class CustomerInfoInputFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(inflater.context).apply { setContent { - val uiState = viewModel.uiState.collectAsStateWithLifecycle().value // TBI: It's currently only the url + val uiState: UiState = viewModel.uiState.collectAsStateWithLifecycle().value when { uiState.formUrl == null -> @@ -47,6 +46,7 @@ class CustomerInfoInputFragment : Fragment() { isLoading = uiState.isLoading, onSendAction = { viewModel.sendUserData(it) }, showError = uiState.showError, + countryItems = uiState.countryItems, onBackNavigationClick = { activity?.onBackPressed() } ) } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index a33e3b5da5..a103a2e340 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization @@ -29,6 +28,8 @@ import io.snabble.sdk.ui.R import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager import io.snabble.sdk.ui.payment.telecash.domain.Address import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import io.snabble.sdk.ui.payment.telecash.widget.CountrySelectionMenu import io.snabble.sdk.ui.payment.telecash.widget.TextInput @Composable @@ -37,6 +38,7 @@ fun CustomerInfoInputScreen( onErrorProcessed: () -> Unit, showError: Boolean, isLoading: Boolean, + countryItems: List?, onBackNavigationClick: () -> Unit, ) { var name by remember { mutableStateOf("") } @@ -141,41 +143,25 @@ fun CustomerInfoInputScreen( if (showError) onErrorProcessed() }, label = stringResource(R.string.Snabble_Payment_CustomerInfo_city), - keyboardActions = KeyboardActions( - onNext = { textFieldManager.moveFocusToNext() } - ), - ) - TextInput( - modifier = Modifier.fillMaxWidth(), - value = state, - onValueChanged = { - state = it - if (showError) onErrorProcessed() - }, - label = stringResource(R.string.Snabble_Payment_CustomerInfo_state), - keyboardActions = KeyboardActions( - onNext = { textFieldManager.moveFocusToNext() } - ), - ) - TextInput( - modifier = Modifier.fillMaxWidth(), - value = country, - onValueChanged = { - country = it - if (showError) onErrorProcessed() - }, - label = stringResource(R.string.Snabble_Payment_CustomerInfo_country), keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Send, + imeAction = ImeAction.Done, capitalization = KeyboardCapitalization.Words ), keyboardActions = KeyboardActions( - onSend = { - textFieldManager.clearFocusAndHideKeyboard() - onSendAction(createCustomerInfo()) - } + onDone = { textFieldManager.clearFocusAndHideKeyboard() } ) ) + CountrySelectionMenu( + countryItems = countryItems, + selectedCountryCode = country, + selectedStateCode = null, + onCountrySelected = { (_, countryCode), stateItem -> + country = countryCode + state = stateItem?.code.orEmpty() + if (showError) onErrorProcessed() + } + ) + Column( modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp), @@ -190,8 +176,9 @@ fun CustomerInfoInputScreen( if (showError) { Text( stringResource(R.string.Snabble_Payment_CustomerInfo_error), - style = MaterialTheme.typography.bodyMedium.copy(color = Color.Red), - modifier = Modifier.padding(horizontal = 16.dp) + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodyMedium ) } } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 49192a04ab..9b1f6ae967 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -4,11 +4,11 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.extensions.xx import io.snabble.sdk.ui.payment.CreditCardInputView import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,16 +17,13 @@ import kotlinx.coroutines.launch internal class TelecashViewModel( private val telecashRepo: TelecashRepository, - private val countryItemsRepo: CountryItemsRepository, + countryItemsRepo: CountryItemsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { - private val _uiState = MutableStateFlow(UiState()) + private val _uiState = MutableStateFlow(UiState(countryItems = countryItemsRepo.loadCountryItems())) val uiState: StateFlow = _uiState.asStateFlow() - init { - countryItemsRepo.loadCountryItems().xx("Countries") - } fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { @@ -56,6 +53,7 @@ internal class TelecashViewModel( data class UiState( val isLoading: Boolean = false, val formUrl: String? = null, + val countryItems: List, val deletePreAuthUrl: String? = null, val showError: Boolean = false ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt new file mode 100644 index 0000000000..9711d918b8 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt @@ -0,0 +1,123 @@ +package io.snabble.sdk.ui.payment.telecash.widget + +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.sp +import com.tegut.tbox.account.details.domain.model.country.StateItem +import io.snabble.sdk.ui.R +import io.snabble.sdk.ui.payment.telecash.data.country.displayName +import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import java.util.Locale + +@Composable +fun CountrySelectionMenu( + modifier: Modifier = Modifier, + countryItems: List?, + selectedCountryCode: String? = null, + selectedStateCode: String? = null, + onCountrySelected: (CountryItem, StateItem?) -> Unit, +) { + var showCountryList by remember { mutableStateOf(false) } + var dismissCountryList by remember { mutableStateOf(true) } + + var showStateList by remember { mutableStateOf(false) } + var dismissStateList by remember { mutableStateOf(true) } + + var currentCountryItem by remember { + mutableStateOf( + selectedCountryCode?.let { countryCode -> countryItems?.firstOrNull { it.code == countryCode } } + ?: countryItems.loadDefaultCountry() + .also { country -> onCountrySelected(country, null) } + ) + } + + var currentStateItem by remember { + mutableStateOf( + selectedStateCode?.let { stateCode -> + currentCountryItem.stateItems?.firstOrNull { it.code == stateCode } + } + ) + } + + fun validateStateItem() { + if (currentCountryItem.stateItems?.contains(currentStateItem) != true) { + currentStateItem = null + } + } + + DropDownMenu( + modifier = modifier, + isExpanded = showCountryList && !dismissCountryList, + onExpand = { + showCountryList = !showCountryList + dismissCountryList = false + }, + onDismiss = { dismissCountryList = true }, + label = stringResource(id = R.string.Snabble_Payment_CustomerInfo_country), + value = currentCountryItem.displayName, + menuItems = countryItems + ) { country -> + DropdownMenuItem( + text = { + Text( + text = country.displayName, + style = MaterialTheme.typography.bodyLarge, + fontSize = 17.sp + ) + }, + onClick = { + currentCountryItem = country + validateStateItem() + onCountrySelected(country, currentStateItem) + showCountryList = false + } + ) + } + if (currentCountryItem.stateItems != null) { + DropDownMenu( + modifier = modifier, + isExpanded = showStateList && !dismissStateList, + onExpand = { + showStateList = !showStateList + dismissStateList = false + }, + onDismiss = { dismissStateList = true }, + label = stringResource(id = R.string.Snabble_Payment_CustomerInfo_state), + value = currentStateItem?.displayName + ?: stringResource(id = R.string.Snabble_Payment_CustomerInfo_stateSelect), + menuItems = currentCountryItem.stateItems + ) { state -> + DropdownMenuItem( + text = { + Text( + text = state.displayName, + style = MaterialTheme.typography.bodyLarge, + fontSize = 17.sp + ) + }, + onClick = { + currentStateItem = state + onCountrySelected(currentCountryItem, state) + showStateList = false + } + ) + } + } +} + +private fun List?.loadDefaultCountry(): CountryItem = + this?.firstOrNull { it.displayName == Locale.getDefault().country.displayName } + ?: this?.firstOrNull { it.code == Locale.GERMANY.displayCountry } + ?: CountryItem( + displayName = Locale.GERMANY.country.displayName, + code = Locale.GERMANY.country, + stateItems = null + ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt new file mode 100644 index 0000000000..c7930115b7 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt @@ -0,0 +1,111 @@ +package io.snabble.sdk.ui.payment.telecash.widget + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material.icons.filled.KeyboardArrowUp +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.PopupProperties + +@Composable +fun DropDownMenu( + modifier: Modifier = Modifier, + isExpanded: Boolean, + onExpand: () -> Unit, + onDismiss: () -> Unit, + label: String, + value: String, + menuItems: List?, + content: @Composable (T) -> Unit, +) { + var maxWidth by remember { mutableStateOf(IntSize.Zero) } + val density = LocalDensity.current + Column( + modifier = Modifier + .fillMaxWidth() + .then(modifier), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Box { + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .clickable { onExpand() } + .onGloballyPositioned { + maxWidth = it.size + }, + value = value, + onValueChange = {}, + textStyle = MaterialTheme.typography.bodyLarge, + label = { + Text( + text = label, + fontSize = 17.sp + ) + }, + colors = TextFieldDefaults.colors( + disabledContainerColor = MaterialTheme.colorScheme.background, + disabledTextColor = MaterialTheme.colorScheme.onSurface, + disabledLabelColor = MaterialTheme.colorScheme.onSurface, + disabledTrailingIconColor = MaterialTheme.colorScheme.onSurface + ), + trailingIcon = { + Icon( + if (isExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown, + contentDescription = "" + ) + }, + readOnly = true, + enabled = false + ) + } + DropdownMenu( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .height(300.dp) + .widthIn(min = (maxWidth.width / density.density).dp), + expanded = isExpanded, + onDismissRequest = onDismiss, + properties = PopupProperties( + clippingEnabled = false, + dismissOnClickOutside = true + ) + ) { + menuItems?.forEach { selectedItem -> + content(selectedItem) + HorizontalDivider( + thickness = Dp.Hairline, + color = Color.LightGray + ) + } + } + } +} diff --git a/ui/src/main/res/values-de/strings.xml b/ui/src/main/res/values-de/strings.xml index 8fc6e56c89..558083fdf1 100644 --- a/ui/src/main/res/values-de/strings.xml +++ b/ui/src/main/res/values-de/strings.xml @@ -109,6 +109,7 @@ Weiter Telefonnummer Bundesland + Bitte wählen Straße inkl. Hausnummer Postleitzahl Möchtest Du diese Zahlungsmethode wirklich entfernen? diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 466654a6c0..e085da7df9 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -109,6 +109,7 @@ Continue Phone number State + Pick one Street ZIP code Are you sure you want to remove this payment method? From db9ab24831486108e4b93ae57f9ec41cdd624d09 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Tue, 6 Aug 2024 13:25:41 +0200 Subject: [PATCH 14/25] fix label color --- .../java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt index 3b35438c28..56ada93e7d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt @@ -71,6 +71,7 @@ fun TextFieldDefaults.defaultColors() = colors( unfocusedIndicatorColor = MaterialTheme.colorScheme.onSurface, focusedLabelColor = MaterialTheme.colorScheme.primary, errorContainerColor = MaterialTheme.colorScheme.background, + unfocusedLabelColor = MaterialTheme.colorScheme.onSurface, errorCursorColor = Color.Red, errorIndicatorColor = Color.Red, errorLabelColor = Color.Red From af7b465039a3bb513da41e497f6a97c306373812 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Tue, 6 Aug 2024 13:40:03 +0200 Subject: [PATCH 15/25] enable only if all required inputs are given --- .../sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt | 3 +-- .../sdk/ui/payment/telecash/CustomerInfoInputScreen.kt | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt index 91ec3aac82..2033b74c20 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt @@ -4,7 +4,6 @@ package io.snabble.sdk.ui.payment.creditcard.data import android.net.Uri import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble -import io.snabble.sdk.extensions.xx fun createCreditCardUrlFor(paymentType: PaymentMethod, url: String): String { val paymentMethod = when (paymentType) { @@ -20,7 +19,7 @@ fun createCreditCardUrlFor(paymentType: PaymentMethod, url: String): String { .appendQueryParameter(PARAM_KEY_ADD_USER_ID, appUserId) .appendQueryParameter(PARAM_KEY_PAYMENT_METHOD, paymentMethod) .build() - .toString().xx("Final URL:") + .toString() } private const val PARAM_KEY_PLATFORM = "platform" diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index a103a2e340..cb11b89d9c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -52,6 +52,8 @@ fun CustomerInfoInputScreen( val textFieldManager = rememberTextFieldManager() + val isEnabled = listOf(name, phoneNumber, email, street, zip, city, country).all { it.isNotEmpty() } + val createCustomerInfo: () -> CustomerInfo = { CustomerInfo( name = name, @@ -169,7 +171,7 @@ fun CustomerInfoInputScreen( Button( modifier = Modifier.fillMaxWidth(), onClick = { onSendAction(createCustomerInfo()) }, - enabled = !isLoading + enabled = !isLoading && isEnabled ) { Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) } From e36493b878a2e04f6a136a7fabfb8c5d24188a64 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Tue, 6 Aug 2024 13:45:37 +0200 Subject: [PATCH 16/25] adjust scopes --- .../sdk/ui/payment/telecash/CustomerInfoInputScreen.kt | 2 +- .../io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt | 2 +- .../ui/payment/telecash/data/CountryItemsRepositoryImpl.kt | 2 +- .../snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt | 4 ++-- .../telecash/data/country/LocalCountryItemsDataSource.kt | 2 +- .../telecash/data/country/LocalCountryItemsDataSourceImpl.kt | 4 ++-- .../sdk/ui/payment/telecash/data/dto/country/CountryDto.kt | 2 +- .../sdk/ui/payment/telecash/data/dto/country/StateDto.kt | 2 +- .../sdk/ui/payment/telecash/domain/CountryItemsRepository.kt | 2 +- .../ui/payment/telecash/domain/model/country/CountryItem.kt | 4 +--- .../ui/payment/telecash/domain/model/country/StateItem.kt | 5 +++-- .../sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt | 4 ++-- .../snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt | 2 +- .../io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt | 4 ++-- 14 files changed, 20 insertions(+), 21 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt index cb11b89d9c..052b04d9ba 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt @@ -33,7 +33,7 @@ import io.snabble.sdk.ui.payment.telecash.widget.CountrySelectionMenu import io.snabble.sdk.ui.payment.telecash.widget.TextInput @Composable -fun CustomerInfoInputScreen( +internal fun CustomerInfoInputScreen( onSendAction: (CustomerInfo) -> Unit, onErrorProcessed: () -> Unit, showError: Boolean, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 9b1f6ae967..b391f55bf4 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -50,7 +50,7 @@ internal class TelecashViewModel( } } -data class UiState( +internal data class UiState( val isLoading: Boolean = false, val formUrl: String? = null, val countryItems: List, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt index 7588d79803..f02ff0daeb 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt @@ -4,7 +4,7 @@ import io.snabble.sdk.ui.payment.telecash.data.country.LocalCountryItemsDataSour import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem -class CountryItemsRepositoryImpl( +internal class CountryItemsRepositoryImpl( private val localCountryItemsDataSource: LocalCountryItemsDataSource, ) : CountryItemsRepository { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt index 5724f657ef..edcedd8149 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class CustomerInfoDto( +internal data class CustomerInfoDto( @SerialName("name") val name: String, @SerialName("phoneNumber") val phoneNumber: String, @SerialName("email") val email: String, @@ -12,7 +12,7 @@ data class CustomerInfoDto( ) @Serializable -data class AddressDto( +internal data class AddressDto( @SerialName("street") val street: String, @SerialName("zip") val zip: String, @SerialName("city") val city: String, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt index 715f91f9b3..12f61e4e88 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt @@ -2,7 +2,7 @@ package io.snabble.sdk.ui.payment.telecash.data.country import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem -interface LocalCountryItemsDataSource { +internal interface LocalCountryItemsDataSource { fun loadCountries(): List } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt index dca4dd4c6f..b8e9d4f95d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt @@ -3,12 +3,12 @@ package io.snabble.sdk.ui.payment.telecash.data.country import android.content.res.AssetManager import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import com.tegut.tbox.account.details.domain.model.country.StateItem +import io.snabble.sdk.ui.payment.telecash.domain.model.country.StateItem import io.snabble.sdk.ui.payment.telecash.data.dto.country.CountryDto import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem import java.util.Locale -class LocalCountryItemsDataSourceImpl( +internal class LocalCountryItemsDataSourceImpl( private val assetManager: AssetManager, private val gson: Gson, ) : LocalCountryItemsDataSource { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt index ada1a6150a..2f8be4b36d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt @@ -2,7 +2,7 @@ package io.snabble.sdk.ui.payment.telecash.data.dto.country import com.google.gson.annotations.SerializedName -data class CountryDto( +internal data class CountryDto( @SerializedName("code") val countryCode: String, @SerializedName("states") val states: List? = null, ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt index e958d587aa..9563feee8c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt @@ -2,4 +2,4 @@ package io.snabble.sdk.ui.payment.telecash.data.dto.country import com.google.gson.annotations.SerializedName -class StateDto(@SerializedName("name") val displayName: String, @SerializedName("code") val code: String) +internal class StateDto(@SerializedName("name") val displayName: String, @SerializedName("code") val code: String) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt index a29c71f13b..b952aaa24b 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt @@ -2,7 +2,7 @@ package io.snabble.sdk.ui.payment.telecash.domain import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem -interface CountryItemsRepository { +internal interface CountryItemsRepository { fun loadCountryItems(): List } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt index fd6cc69fe8..25594ca64a 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt @@ -1,5 +1,3 @@ package io.snabble.sdk.ui.payment.telecash.domain.model.country -import com.tegut.tbox.account.details.domain.model.country.StateItem - -data class CountryItem(val displayName: String, val code: String, val stateItems: List?) +internal data class CountryItem(val displayName: String, val code: String, val stateItems: List?) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt index 841c0baa81..983f19b39a 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt @@ -1,9 +1,10 @@ -package com.tegut.tbox.account.details.domain.model.country +package io.snabble.sdk.ui.payment.telecash.domain.model.country import io.snabble.sdk.ui.payment.telecash.data.dto.country.StateDto -data class StateItem(val displayName: String, val code: String) { +internal data class StateItem(val displayName: String, val code: String) { companion object { + fun from(stateDto: StateDto) = StateItem( displayName = stateDto.displayName, code = stateDto.code diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt index 9711d918b8..0bab6ca92d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt @@ -11,14 +11,14 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.sp -import com.tegut.tbox.account.details.domain.model.country.StateItem +import io.snabble.sdk.ui.payment.telecash.domain.model.country.StateItem import io.snabble.sdk.ui.R import io.snabble.sdk.ui.payment.telecash.data.country.displayName import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem import java.util.Locale @Composable -fun CountrySelectionMenu( +internal fun CountrySelectionMenu( modifier: Modifier = Modifier, countryItems: List?, selectedCountryCode: String? = null, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt index c7930115b7..7491f2db37 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.PopupProperties @Composable -fun DropDownMenu( +internal fun DropDownMenu( modifier: Modifier = Modifier, isExpanded: Boolean, onExpand: () -> Unit, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt index 56ada93e7d..aa4b9e02da 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun TextInput( +internal fun TextInput( modifier: Modifier = Modifier, value: String, label: String, @@ -64,7 +64,7 @@ fun TextInput( } @Composable -fun TextFieldDefaults.defaultColors() = colors( +private fun TextFieldDefaults.defaultColors() = colors( focusedContainerColor = MaterialTheme.colorScheme.background, unfocusedContainerColor = MaterialTheme.colorScheme.background, focusedIndicatorColor = MaterialTheme.colorScheme.primary, From 31b25115ff8e558686aab200621b5203bf44f1fa Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 14:48:11 +0200 Subject: [PATCH 17/25] Set state to null if empty --- .../io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt | 1 - .../io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt | 2 +- .../sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index b391f55bf4..0282a023ee 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -24,7 +24,6 @@ internal class TelecashViewModel( private val _uiState = MutableStateFlow(UiState(countryItems = countryItemsRepo.loadCountryItems())) val uiState: StateFlow = _uiState.asStateFlow() - fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { _uiState.update { it.copy(isLoading = true) } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt index edcedd8149..3f8627154d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt @@ -16,6 +16,6 @@ internal data class AddressDto( @SerialName("street") val street: String, @SerialName("zip") val zip: String, @SerialName("city") val city: String, - @SerialName("state") val state: String, + @SerialName("state") val state: String?, @SerialName("country") val country: String ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt index 63ce463a47..c95bfac700 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt @@ -25,7 +25,7 @@ private fun CustomerInfo.toDto() = CustomerInfoDto( street = address.street, zip = address.zip, city = address.city, - state = address.state, + state = address.state.ifEmpty { null }, country = address.country ), ) From 74cf79d7fbfbff8f84122b1eb5ed8864aa2dc1f6 Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 15:24:37 +0200 Subject: [PATCH 18/25] Add PreAuthDelete Request --- .../sdk/ui/payment/CreditCardInputView.java | 26 ++++++++++++++++--- .../telecash/CustomerInfoInputFragment.kt | 9 ++++++- .../ui/payment/telecash/TelecashViewModel.kt | 22 +++++++++------- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java index 3831b773c7..fd6f3a8b12 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.math.BigDecimal; import java.text.NumberFormat; import java.util.Currency; @@ -42,6 +43,7 @@ import io.snabble.sdk.utils.Dispatch; import io.snabble.sdk.utils.Logger; import io.snabble.sdk.utils.SimpleActivityLifecycleCallbacks; +import okhttp3.Request; public class CreditCardInputView extends RelativeLayout { public static final String ARG_PROJECT_ID = "projectId"; @@ -56,7 +58,8 @@ public class CreditCardInputView extends RelativeLayout { private PaymentMethod paymentType; private String projectId; - private String url; + private String formUrl; + private String deleteUrl; private TextView threeDHint; private boolean isLoaded; @@ -163,10 +166,11 @@ private void setProject() { } } - public void load(String projectId, PaymentMethod paymentType, String url) { + public void load(String projectId, PaymentMethod paymentType, String formUrl, String deletePreAuthUrl) { this.paymentType = paymentType; this.projectId = projectId; - this.url = Snabble.getInstance().absoluteUrl(url); + this.formUrl = Snabble.getInstance().absoluteUrl(formUrl); + this.deleteUrl = Snabble.getInstance().absoluteUrl(deletePreAuthUrl); inflateView(); } @@ -191,7 +195,7 @@ private Project getProject() { } private void loadUrl() { - final String formUrl = SnabbleCreditCardUrlCreator.createCreditCardUrlFor(paymentType, url); + final String formUrl = SnabbleCreditCardUrlCreator.createCreditCardUrlFor(paymentType, this.formUrl); webView.loadUrl(formUrl); } @@ -257,9 +261,23 @@ private void save(CreditCardInfo info) { } private void finish() { + deletePreAuth(); SnabbleUI.executeAction(getContext(), SnabbleUI.Event.GO_BACK); } + private void deletePreAuth() { + Dispatch.io(() -> { + final Request request = new Request.Builder().url(deleteUrl).delete().build(); + try { + final Project project = getProject(); + if (project != null) { + project.getOkHttpClient().newCall(request).execute(); + } + } catch (final IOException ignored) { + } + }); + } + private void finishWithError(String failReason) { String errorMessage = getContext().getString(R.string.Snabble_Payment_CreditCard_error); if (failReason != null) { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt index 48b63ddb4b..620933bc74 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt @@ -54,7 +54,14 @@ class CustomerInfoInputFragment : Fragment() { else -> AndroidView( factory = { context -> CreditCardInputView(context) - .apply { load(Snabble.checkedInProject.value?.id, paymentMethod, uiState.formUrl) } + .apply { + load( + Snabble.checkedInProject.value?.id, + paymentMethod, + uiState.formUrl, + uiState.deletePreAuthUrl + ) + } } ) } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt index 0282a023ee..349c7bcfc9 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt @@ -30,17 +30,19 @@ internal class TelecashViewModel( val paymentMethod = savedStateHandle.get(CreditCardInputView.ARG_PAYMENT_TYPE) ?: return@launch val result = telecashRepo.sendUserData(customerInfo, paymentMethod) - result.onSuccess { info -> - _uiState.update { - it.copy( - isLoading = false, - formUrl = info.formUrl, - deletePreAuthUrl = it.deletePreAuthUrl - ) + result + .onSuccess { info -> + _uiState.update { + it.copy( + isLoading = false, + formUrl = info.formUrl, + deletePreAuthUrl = info.preAuthDeleteUrl + ) + } + } + .onFailure { + _uiState.update { it.copy(isLoading = false, showError = true) } } - }.onFailure { - _uiState.update { it.copy(isLoading = false, showError = true) } - } } } From f09319ab29507be852b9b75c1293141fe7a2643a Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Tue, 6 Aug 2024 16:03:37 +0200 Subject: [PATCH 19/25] Refactor name Telecash to Fiserv and moved files --- .../main/res/navigation/mobile_navigation.xml | 7 +--- ui/src/main/AndroidManifest.xml | 2 +- .../main/java/io/snabble/sdk/ui/SnabbleUI.kt | 4 +-- .../sdk/ui/payment/CreditCardInputActivity.kt | 17 ---------- .../sdk/ui/payment/CreditCardInputFragment.kt | 33 ------------------- .../sdk/ui/payment/PaymentInputViewHelper.kt | 5 +-- .../CustomerInfoInputScreen.kt | 12 +++---- .../ui/payment/fiserv/FiservInputActivity.kt | 16 +++++++++ .../FiservInputFragment.kt} | 11 +++---- .../FiservInputView.java} | 12 +++---- .../FiservViewModel.kt} | 19 +++++------ .../FiservViewModelFactory.kt} | 16 ++++----- .../fiserv/data/CountryItemsRepositoryImpl.kt | 12 +++++++ .../data/CustomerInfoDto.kt | 2 +- .../data/FiservRemoteDataSource.kt} | 11 +++---- .../data/FiservRepositoryImpl.kt} | 18 +++++----- .../country/LocalCountryItemsDataSource.kt | 8 +++++ .../LocalCountryItemsDataSourceImpl.kt | 8 ++--- .../data/dto/country/CountryDto.kt | 2 +- .../data/dto/country/StateDto.kt | 2 +- .../fiserv/domain/CountryItemsRepository.kt | 8 +++++ .../domain/CustomerInfo.kt | 2 +- .../payment/fiserv/domain/FiservRepository.kt | 9 +++++ .../domain/model/country/CountryItem.kt | 2 +- .../domain/model/country/StateItem.kt | 4 +-- .../widget/CountrySelectionMenu.kt | 8 ++--- .../widget/DropDownMenu.kt | 2 +- .../{telecash => fiserv}/widget/TextInput.kt | 2 +- .../data/CountryItemsRepositoryImpl.kt | 12 ------- .../country/LocalCountryItemsDataSource.kt | 8 ----- .../telecash/domain/CountryItemsRepository.kt | 8 ----- .../telecash/domain/TelecashRepository.kt | 9 ----- .../snabble_fragment_cardinput_creditcard.xml | 6 ---- .../snabble_fragment_cardinput_fiserv.xml | 5 +++ ui/src/main/res/values-de/strings.xml | 4 +-- 35 files changed, 132 insertions(+), 174 deletions(-) delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputActivity.kt delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/CustomerInfoInputScreen.kt (95%) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash/CustomerInfoInputFragment.kt => fiserv/FiservInputFragment.kt} (88%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{CreditCardInputView.java => fiserv/FiservInputView.java} (97%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash/TelecashViewModel.kt => fiserv/FiservViewModel.kt} (71%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash/TelecashViewModelFactory.kt => fiserv/FiservViewModelFactory.kt} (62%) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/data/CustomerInfoDto.kt (92%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash/data/TelecashRemoteDataSource.kt => fiserv/data/FiservRemoteDataSource.kt} (91%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash/data/TelecashRepositoryImpl.kt => fiserv/data/FiservRepositoryImpl.kt} (58%) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/data/country/LocalCountryItemsDataSourceImpl.kt (79%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/data/dto/country/CountryDto.kt (77%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/data/dto/country/StateDto.kt (73%) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/domain/CustomerInfo.kt (84%) create mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/domain/model/country/CountryItem.kt (63%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/domain/model/country/StateItem.kt (65%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/widget/CountrySelectionMenu.kt (94%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/widget/DropDownMenu.kt (98%) rename ui/src/main/java/io/snabble/sdk/ui/payment/{telecash => fiserv}/widget/TextInput.kt (98%) delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt delete mode 100644 ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt delete mode 100644 ui/src/main/res/layout/snabble_fragment_cardinput_creditcard.xml create mode 100644 ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml diff --git a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml index 44cbd976bb..3dc10c1f47 100644 --- a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml +++ b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml @@ -164,14 +164,9 @@ android:name="io.snabble.sdk.ui.payment.PayoneInputFragment" android:label="Payone" /> - - - startActivity(context, CreditCardInputActivity::class.java, args, canGoBack = false) + startActivity(context, FiservInputActivity::class.java, args, canGoBack = false) SHOW_PAYONE_INPUT -> startActivity(context, PayoneInputActivity::class.java, args, canGoBack = false) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputActivity.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputActivity.kt deleted file mode 100644 index 267578ef5c..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputActivity.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.snabble.sdk.ui.payment - -import androidx.fragment.app.Fragment -import io.snabble.sdk.ui.BaseFragmentActivity - -class CreditCardInputActivity : BaseFragmentActivity() { - companion object { - const val ARG_PROJECT_ID = CreditCardInputView.ARG_PROJECT_ID - const val ARG_PAYMENT_TYPE = CreditCardInputView.ARG_PAYMENT_TYPE - } - - override fun onCreateFragment(): Fragment { - val fragment = CreditCardInputFragment() - fragment.arguments = intent.extras - return fragment - } -} \ No newline at end of file diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt deleted file mode 100644 index 071b36ebe9..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputFragment.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.snabble.sdk.ui.payment - -import android.os.Bundle -import android.view.View -import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.BaseFragment -import io.snabble.sdk.ui.R -import io.snabble.sdk.ui.utils.serializableExtra - -open class CreditCardInputFragment : BaseFragment( - layoutResId = R.layout.snabble_fragment_cardinput_creditcard, - waitForProject = false -) { - companion object { - const val ARG_PROJECT_ID = CreditCardInputView.ARG_PROJECT_ID - const val ARG_PAYMENT_TYPE = CreditCardInputView.ARG_PAYMENT_TYPE - } - - var projectId: String? = null - var paymentMethod: PaymentMethod? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - projectId = arguments?.getString(ARG_PROJECT_ID, null) - paymentMethod = arguments?.serializableExtra(ARG_PAYMENT_TYPE) - } - - override fun onActualViewCreated(view: View, savedInstanceState: Bundle?) { - val v = view as CreditCardInputView -// v.load(projectId, paymentMethod) - } -} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt index c1f5f8c162..99451cca7d 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt @@ -11,6 +11,7 @@ import io.snabble.sdk.payment.PaymentCredentials import io.snabble.sdk.ui.R import io.snabble.sdk.ui.SnabbleUI import io.snabble.sdk.ui.payment.externalbilling.ExternalBillingFragment.Companion.ARG_PROJECT_ID +import io.snabble.sdk.ui.payment.fiserv.FiservInputView import io.snabble.sdk.ui.utils.KeyguardUtils import io.snabble.sdk.ui.utils.UIUtils import io.snabble.sdk.utils.Logger @@ -38,8 +39,8 @@ object PaymentInputViewHelper { useDatatrans -> Datatrans.registerCard(activity, project, paymentMethod) usePayone -> Payone.registerCard(activity, project, paymentMethod, Snabble.formPrefillData) useFiserv -> { - args.putString(CreditCardInputView.ARG_PROJECT_ID, projectId) - args.putSerializable(CreditCardInputView.ARG_PAYMENT_TYPE, paymentMethod) + args.putString(FiservInputView.ARG_PROJECT_ID, projectId) + args.putSerializable(FiservInputView.ARG_PAYMENT_TYPE, paymentMethod) SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) } paymentMethod == PaymentMethod.EXTERNAL_BILLING -> { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt similarity index 95% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt index 052b04d9ba..18a5d97f87 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash +package io.snabble.sdk.ui.payment.fiserv import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -26,11 +26,11 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import io.snabble.sdk.ui.R import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager -import io.snabble.sdk.ui.payment.telecash.domain.Address -import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem -import io.snabble.sdk.ui.payment.telecash.widget.CountrySelectionMenu -import io.snabble.sdk.ui.payment.telecash.widget.TextInput +import io.snabble.sdk.ui.payment.fiserv.domain.Address +import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem +import io.snabble.sdk.ui.payment.fiserv.widget.CountrySelectionMenu +import io.snabble.sdk.ui.payment.fiserv.widget.TextInput @Composable internal fun CustomerInfoInputScreen( diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt new file mode 100644 index 0000000000..c9f8a177e5 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt @@ -0,0 +1,16 @@ +package io.snabble.sdk.ui.payment.fiserv + +import androidx.fragment.app.Fragment +import io.snabble.sdk.ui.BaseFragmentActivity + +class FiservInputActivity : BaseFragmentActivity() { + + override fun onCreateFragment(): Fragment = FiservInputFragment() + .apply { arguments = intent.extras } + + companion object { + + const val ARG_PROJECT_ID = FiservInputView.ARG_PROJECT_ID + const val ARG_PAYMENT_TYPE = FiservInputView.ARG_PAYMENT_TYPE + } +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt similarity index 88% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt index 620933bc74..62f44d05b7 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/CustomerInfoInputFragment.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash +package io.snabble.sdk.ui.payment.fiserv import android.os.Bundle import android.view.LayoutInflater @@ -12,21 +12,20 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble -import io.snabble.sdk.ui.payment.CreditCardInputView import io.snabble.sdk.ui.payment.PaymentMethodMetaDataHelper import io.snabble.sdk.ui.utils.ThemeWrapper import io.snabble.sdk.ui.utils.serializableExtra -class CustomerInfoInputFragment : Fragment() { +class FiservInputFragment : Fragment() { - private val viewModel: TelecashViewModel by viewModels { TelecashViewModelFactory(requireContext()) } + private val viewModel: FiservViewModel by viewModels { FiservViewModelFactory(requireContext()) } private lateinit var paymentMethod: PaymentMethod override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - paymentMethod = arguments?.serializableExtra(CreditCardInputView.ARG_PAYMENT_TYPE) + paymentMethod = arguments?.serializableExtra(FiservInputView.ARG_PAYMENT_TYPE) ?: kotlin.run { activity?.onBackPressed(); return } (requireActivity() as? AppCompatActivity)?.supportActionBar?.title = @@ -53,7 +52,7 @@ class CustomerInfoInputFragment : Fragment() { else -> AndroidView( factory = { context -> - CreditCardInputView(context) + FiservInputView(context) .apply { load( Snabble.checkedInProject.value?.id, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java similarity index 97% rename from ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java index fd6f3a8b12..87629bab09 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/CreditCardInputView.java +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment; +package io.snabble.sdk.ui.payment.fiserv; import android.annotation.SuppressLint; import android.app.Activity; @@ -45,7 +45,7 @@ import io.snabble.sdk.utils.SimpleActivityLifecycleCallbacks; import okhttp3.Request; -public class CreditCardInputView extends RelativeLayout { +public class FiservInputView extends RelativeLayout { public static final String ARG_PROJECT_ID = "projectId"; public static final String ARG_PAYMENT_TYPE = "paymentType"; @@ -63,15 +63,15 @@ public class CreditCardInputView extends RelativeLayout { private TextView threeDHint; private boolean isLoaded; - public CreditCardInputView(Context context) { + public FiservInputView(Context context) { super(context); } - public CreditCardInputView(Context context, AttributeSet attrs) { + public FiservInputView(Context context, AttributeSet attrs) { super(context, attrs); } - public CreditCardInputView(Context context, AttributeSet attrs, int defStyleAttr) { + public FiservInputView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @@ -381,7 +381,7 @@ public void fail(String failReason) { @JavascriptInterface public void abort() { - Dispatch.mainThread(CreditCardInputView.this::finish); + Dispatch.mainThread(FiservInputView.this::finish); } @JavascriptInterface diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt similarity index 71% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt index 349c7bcfc9..19eea0b4d5 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt @@ -1,22 +1,21 @@ -package io.snabble.sdk.ui.payment.telecash +package io.snabble.sdk.ui.payment.fiserv import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.CreditCardInputView -import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository -import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo -import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import io.snabble.sdk.ui.payment.fiserv.domain.CountryItemsRepository +import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo +import io.snabble.sdk.ui.payment.fiserv.domain.FiservRepository +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -internal class TelecashViewModel( - private val telecashRepo: TelecashRepository, +internal class FiservViewModel( + private val fiservRepo: FiservRepository, countryItemsRepo: CountryItemsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { @@ -28,8 +27,8 @@ internal class TelecashViewModel( viewModelScope.launch { _uiState.update { it.copy(isLoading = true) } val paymentMethod = - savedStateHandle.get(CreditCardInputView.ARG_PAYMENT_TYPE) ?: return@launch - val result = telecashRepo.sendUserData(customerInfo, paymentMethod) + savedStateHandle.get(FiservInputView.ARG_PAYMENT_TYPE) ?: return@launch + val result = fiservRepo.sendUserData(customerInfo, paymentMethod) result .onSuccess { info -> _uiState.update { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt similarity index 62% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt index 361d60c7d7..d43bc4e93c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/TelecashViewModelFactory.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt @@ -1,26 +1,26 @@ -package io.snabble.sdk.ui.payment.telecash +package io.snabble.sdk.ui.payment.fiserv import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras -import io.snabble.sdk.ui.payment.telecash.data.CountryItemsRepositoryImpl -import io.snabble.sdk.ui.payment.telecash.data.TelecashRepositoryImpl -import io.snabble.sdk.ui.payment.telecash.data.country.LocalCountryItemsDataSourceImpl +import io.snabble.sdk.ui.payment.fiserv.data.CountryItemsRepositoryImpl +import io.snabble.sdk.ui.payment.fiserv.data.FiservRepositoryImpl +import io.snabble.sdk.ui.payment.fiserv.data.country.LocalCountryItemsDataSourceImpl import io.snabble.sdk.utils.GsonHolder -class TelecashViewModelFactory(private val context: Context) : ViewModelProvider.Factory { +class FiservViewModelFactory(private val context: Context) : ViewModelProvider.Factory { override fun create(modelClass: Class, extras: CreationExtras): T { - if (!modelClass.isAssignableFrom(TelecashViewModel::class.java)) { + if (!modelClass.isAssignableFrom(FiservViewModel::class.java)) { throw IllegalArgumentException("Unable to construct viewmodel") } val savedStateHandle = extras.createSavedStateHandle() @Suppress("UNCHECKED_CAST") - return TelecashViewModel( - telecashRepo = TelecashRepositoryImpl(), + return FiservViewModel( + fiservRepo = FiservRepositoryImpl(), countryItemsRepo = CountryItemsRepositoryImpl( localCountryItemsDataSource = LocalCountryItemsDataSourceImpl( assetManager = context.assets, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt new file mode 100644 index 0000000000..d0c153bb93 --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt @@ -0,0 +1,12 @@ +package io.snabble.sdk.ui.payment.fiserv.data + +import io.snabble.sdk.ui.payment.fiserv.data.country.LocalCountryItemsDataSource +import io.snabble.sdk.ui.payment.fiserv.domain.CountryItemsRepository +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem + +internal class CountryItemsRepositoryImpl( + private val localCountryItemsDataSource: LocalCountryItemsDataSource, +) : CountryItemsRepository { + + override fun loadCountryItems(): List = localCountryItemsDataSource.loadCountries() +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt similarity index 92% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt index 3f8627154d..312cd1c2cf 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CustomerInfoDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.data +package io.snabble.sdk.ui.payment.fiserv.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt similarity index 91% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt index c194edb297..f449ba715c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRemoteDataSource.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.data +package io.snabble.sdk.ui.payment.fiserv.data import android.util.Log import com.google.gson.Gson @@ -6,7 +6,6 @@ import com.google.gson.JsonSyntaxException import com.google.gson.annotations.SerializedName import io.snabble.sdk.PaymentMethod import io.snabble.sdk.Snabble -import io.snabble.sdk.ui.SnabbleUI import io.snabble.sdk.utils.GsonHolder import okhttp3.Call import okhttp3.Callback @@ -20,15 +19,15 @@ import java.io.IOException import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -internal interface TelecashRemoteDataSource { +internal interface FiservRemoteDataSource { suspend fun sendUserData(customerInfo: CustomerInfoDto, paymentMethod: PaymentMethod): Result } -internal class TelecashRemoteDataSourceImpl( +internal class FiservRemoteDataSourceImpl( private val snabble: Snabble = Snabble, private val gson: Gson = GsonHolder.get(), -) : TelecashRemoteDataSource { +) : FiservRemoteDataSource { override suspend fun sendUserData( customerInfo: CustomerInfoDto, @@ -63,7 +62,7 @@ internal class TelecashRemoteDataSourceImpl( val creditCardAuthData: CreditCardAuthData? = try { gson.fromJson(body, CreditCardAuthData::class.java) } catch (e: JsonSyntaxException) { - Log.e("Telecash", "Error parsing pre-registration response", e) + Log.e("Fiserv", "Error parsing pre-registration response", e) null } diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt similarity index 58% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt index c95bfac700..ea5339e0fc 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/TelecashRepositoryImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt @@ -1,20 +1,20 @@ -package io.snabble.sdk.ui.payment.telecash.data +package io.snabble.sdk.ui.payment.fiserv.data import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.telecash.domain.TelecashRepository -import io.snabble.sdk.ui.payment.telecash.domain.CustomerInfo +import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo +import io.snabble.sdk.ui.payment.fiserv.domain.FiservRepository -internal class TelecashRepositoryImpl( - private val remoteDataSource: TelecashRemoteDataSource = TelecashRemoteDataSourceImpl() -) : TelecashRepository { +internal class FiservRepositoryImpl( + private val remoteDataSource: FiservRemoteDataSource = FiservRemoteDataSourceImpl() +) : FiservRepository { override suspend fun sendUserData( customerInfo: CustomerInfo, paymentMethod: PaymentMethod - ): Result = + ): Result = remoteDataSource .sendUserData(customerInfo.toDto(), paymentMethod) - .map { CreditCardAdditionInfo(it.links.formUrl.href, it.links.deleteUrl.href) } + .map { FiservCardRegisterUrls(it.links.formUrl.href, it.links.deleteUrl.href) } } private fun CustomerInfo.toDto() = CustomerInfoDto( @@ -30,7 +30,7 @@ private fun CustomerInfo.toDto() = CustomerInfoDto( ), ) -data class CreditCardAdditionInfo( +data class FiservCardRegisterUrls( val formUrl: String, val preAuthDeleteUrl: String ) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt new file mode 100644 index 0000000000..5f4b7d5eeb --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.fiserv.data.country + +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem + +internal interface LocalCountryItemsDataSource { + + fun loadCountries(): List +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt similarity index 79% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt index b8e9d4f95d..53c32201a4 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSourceImpl.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt @@ -1,11 +1,11 @@ -package io.snabble.sdk.ui.payment.telecash.data.country +package io.snabble.sdk.ui.payment.fiserv.data.country import android.content.res.AssetManager import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import io.snabble.sdk.ui.payment.telecash.domain.model.country.StateItem -import io.snabble.sdk.ui.payment.telecash.data.dto.country.CountryDto -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.StateItem +import io.snabble.sdk.ui.payment.fiserv.data.dto.country.CountryDto +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem import java.util.Locale internal class LocalCountryItemsDataSourceImpl( diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt similarity index 77% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt index 2f8be4b36d..7cb9c5b703 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/CountryDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.data.dto.country +package io.snabble.sdk.ui.payment.fiserv.data.dto.country import com.google.gson.annotations.SerializedName diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt similarity index 73% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt index 9563feee8c..333651b935 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/dto/country/StateDto.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.data.dto.country +package io.snabble.sdk.ui.payment.fiserv.data.dto.country import com.google.gson.annotations.SerializedName diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt new file mode 100644 index 0000000000..bb81032f3d --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt @@ -0,0 +1,8 @@ +package io.snabble.sdk.ui.payment.fiserv.domain + +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem + +internal interface CountryItemsRepository { + + fun loadCountryItems(): List +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt similarity index 84% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt index 9917b6fc1f..8afc10340e 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CustomerInfo.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.domain +package io.snabble.sdk.ui.payment.fiserv.domain data class CustomerInfo( val name: String, diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt new file mode 100644 index 0000000000..7bd9a5129e --- /dev/null +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt @@ -0,0 +1,9 @@ +package io.snabble.sdk.ui.payment.fiserv.domain + +import io.snabble.sdk.PaymentMethod +import io.snabble.sdk.ui.payment.fiserv.data.FiservCardRegisterUrls + +internal interface FiservRepository { + + suspend fun sendUserData(customerInfo: CustomerInfo, paymentMethod: PaymentMethod): Result +} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt similarity index 63% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt index 25594ca64a..c29020cb6c 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/CountryItem.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt @@ -1,3 +1,3 @@ -package io.snabble.sdk.ui.payment.telecash.domain.model.country +package io.snabble.sdk.ui.payment.fiserv.domain.model.country internal data class CountryItem(val displayName: String, val code: String, val stateItems: List?) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt similarity index 65% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt index 983f19b39a..ea2fe103dd 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/model/country/StateItem.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt @@ -1,6 +1,6 @@ -package io.snabble.sdk.ui.payment.telecash.domain.model.country +package io.snabble.sdk.ui.payment.fiserv.domain.model.country -import io.snabble.sdk.ui.payment.telecash.data.dto.country.StateDto +import io.snabble.sdk.ui.payment.fiserv.data.dto.country.StateDto internal data class StateItem(val displayName: String, val code: String) { companion object { diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt similarity index 94% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt index 0bab6ca92d..73146a94c8 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/CountrySelectionMenu.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.widget +package io.snabble.sdk.ui.payment.fiserv.widget import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.MaterialTheme @@ -11,10 +11,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.sp -import io.snabble.sdk.ui.payment.telecash.domain.model.country.StateItem +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.StateItem import io.snabble.sdk.ui.R -import io.snabble.sdk.ui.payment.telecash.data.country.displayName -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem +import io.snabble.sdk.ui.payment.fiserv.data.country.displayName +import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem import java.util.Locale @Composable diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt similarity index 98% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt index 7491f2db37..25a2c425cc 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/DropDownMenu.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.widget +package io.snabble.sdk.ui.payment.fiserv.widget import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt similarity index 98% rename from ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt rename to ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt index aa4b9e02da..5f5a40ccbc 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/widget/TextInput.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt @@ -1,4 +1,4 @@ -package io.snabble.sdk.ui.payment.telecash.widget +package io.snabble.sdk.ui.payment.fiserv.widget import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt deleted file mode 100644 index f02ff0daeb..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/CountryItemsRepositoryImpl.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.snabble.sdk.ui.payment.telecash.data - -import io.snabble.sdk.ui.payment.telecash.data.country.LocalCountryItemsDataSource -import io.snabble.sdk.ui.payment.telecash.domain.CountryItemsRepository -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem - -internal class CountryItemsRepositoryImpl( - private val localCountryItemsDataSource: LocalCountryItemsDataSource, -) : CountryItemsRepository { - - override fun loadCountryItems(): List = localCountryItemsDataSource.loadCountries() -} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt deleted file mode 100644 index 12f61e4e88..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/data/country/LocalCountryItemsDataSource.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.snabble.sdk.ui.payment.telecash.data.country - -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem - -internal interface LocalCountryItemsDataSource { - - fun loadCountries(): List -} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt deleted file mode 100644 index b952aaa24b..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/CountryItemsRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.snabble.sdk.ui.payment.telecash.domain - -import io.snabble.sdk.ui.payment.telecash.domain.model.country.CountryItem - -internal interface CountryItemsRepository { - - fun loadCountryItems(): List -} diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt deleted file mode 100644 index 2022427498..0000000000 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/telecash/domain/TelecashRepository.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.snabble.sdk.ui.payment.telecash.domain - -import io.snabble.sdk.PaymentMethod -import io.snabble.sdk.ui.payment.telecash.data.CreditCardAdditionInfo - -internal interface TelecashRepository { - - suspend fun sendUserData(customerInfo: CustomerInfo, paymentMethod: PaymentMethod): Result -} diff --git a/ui/src/main/res/layout/snabble_fragment_cardinput_creditcard.xml b/ui/src/main/res/layout/snabble_fragment_cardinput_creditcard.xml deleted file mode 100644 index f1feb61099..0000000000 --- a/ui/src/main/res/layout/snabble_fragment_cardinput_creditcard.xml +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml b/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml new file mode 100644 index 0000000000..e28d2e0d23 --- /dev/null +++ b/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml @@ -0,0 +1,5 @@ + + diff --git a/ui/src/main/res/values-de/strings.xml b/ui/src/main/res/values-de/strings.xml index 558083fdf1..63495323eb 100644 --- a/ui/src/main/res/values-de/strings.xml +++ b/ui/src/main/res/values-de/strings.xml @@ -108,7 +108,7 @@ Vor- und Nachname Weiter Telefonnummer - Bundesland + Staat Bitte wählen Straße inkl. Hausnummer Postleitzahl @@ -230,7 +230,7 @@ Ablaufjahr (JJJJ) Bitte fülle das Formular vollständig aus. Nachname - Bundesland + Staat Bitte Staat auswählen Straße inkl. Hausnummer Postleitzahl From 96bf691aabb97321578e97fa842882a95bc99b73 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Wed, 7 Aug 2024 09:48:50 +0200 Subject: [PATCH 20/25] update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfac37b717..7baf399f7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ All notable changes to this project will be documented in this file. ### Removed ### Fixed +## [0.75.0] +### Changed +* ui: update fiserv 3D's flow. The user now has to enter additional information to add a credit card. + * instead of using the CreditCardInput it is now required to use the FiservInput instead. ## [0.74.0] ### Added * core: add new and update existing user agent headers From 297e864d083cb36504d679097bd79cb0a7484ccf Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Wed, 7 Aug 2024 10:49:01 +0200 Subject: [PATCH 21/25] Use AnimatedVisibility instead of a boolean --- .../snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt index 18a5d97f87..1239133fdb 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt @@ -1,5 +1,6 @@ package io.snabble.sdk.ui.payment.fiserv +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -175,7 +176,7 @@ internal fun CustomerInfoInputScreen( ) { Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) } - if (showError) { + AnimatedVisibility(visible = showError) { Text( stringResource(R.string.Snabble_Payment_CustomerInfo_error), modifier = Modifier.padding(horizontal = 16.dp), From 299220e8617b0ffdef16a0c36f879e30b2ebb7da Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Wed, 7 Aug 2024 10:52:55 +0200 Subject: [PATCH 22/25] Change KeyboardType for email and zip to None --- .../io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt index 1239133fdb..8acebd6c08 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt @@ -113,6 +113,7 @@ internal fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) ) TextInput( modifier = Modifier.fillMaxWidth(), @@ -137,6 +138,7 @@ internal fun CustomerInfoInputScreen( keyboardActions = KeyboardActions( onNext = { textFieldManager.moveFocusToNext() } ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) ) TextInput( modifier = Modifier.fillMaxWidth(), From efe3ec1b5115aab5156ec889d6a35788cddc13e5 Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Wed, 7 Aug 2024 11:05:28 +0200 Subject: [PATCH 23/25] add state as required field if countries contains a list of states --- .../sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt index 8acebd6c08..6757a2147e 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt @@ -53,7 +53,10 @@ internal fun CustomerInfoInputScreen( val textFieldManager = rememberTextFieldManager() - val isEnabled = listOf(name, phoneNumber, email, street, zip, city, country).all { it.isNotEmpty() } + val isRequiredStateSet = + if (!countryItems?.firstOrNull { it.code == country }?.stateItems.isNullOrEmpty()) state.isNotEmpty() else true + val areRequiredFieldsSet = + listOf(name, phoneNumber, email, street, zip, city, country).all { it.isNotEmpty() } && isRequiredStateSet val createCustomerInfo: () -> CustomerInfo = { CustomerInfo( @@ -174,7 +177,7 @@ internal fun CustomerInfoInputScreen( Button( modifier = Modifier.fillMaxWidth(), onClick = { onSendAction(createCustomerInfo()) }, - enabled = !isLoading && isEnabled + enabled = !isLoading && areRequiredFieldsSet ) { Text(stringResource(R.string.Snabble_Payment_CustomerInfo_next)) } From 4369f8631350ca65bc6fa753bf57a8d8cdfa234c Mon Sep 17 00:00:00 2001 From: Fabian Bender Date: Wed, 7 Aug 2024 11:21:20 +0200 Subject: [PATCH 24/25] rename ui event to match action --- .../java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt | 4 ++-- kotlin-sample/src/main/res/navigation/mobile_navigation.xml | 2 +- ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt | 6 +++--- .../io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt b/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt index be7e92be34..bf9a1559c8 100644 --- a/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt +++ b/kotlin-sample/src/main/java/io/snabble/sdk/sample/SnabbleUiEventHandler.kt @@ -38,9 +38,9 @@ fun setUpUiEvents(activity: AppCompatActivity, navController: NavController, bot } SnabbleUI.setUiAction( activity, - SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT + SnabbleUI.Event.SHOW_FISERV_INPUT ) { _, args -> - navController.navigate(R.id.user_details, args) + navController.navigate(R.id.navigation_fiserv_input, args) } SnabbleUI.setUiAction( activity, diff --git a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml index 3dc10c1f47..dc745cb487 100644 --- a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml +++ b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml @@ -165,7 +165,7 @@ android:label="Payone" /> diff --git a/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt b/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt index e8acf5b04b..b060d44cca 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt @@ -19,7 +19,7 @@ import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_BARCODE_SEARCH import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_CHECKOUT import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_CHECKOUT_DONE import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_COUPON_DETAILS -import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT +import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_FISERV_INPUT import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_EXTERNAL_BILLING import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_GIROPAY_INPUT import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_PAYMENT_CREDENTIALS_LIST @@ -66,7 +66,7 @@ object SnabbleUI { SHOW_BARCODE_SEARCH, SHOW_SEPA_CARD_INPUT, SHOW_PAYONE_SEPA, - SHOW_CREDIT_CARD_INPUT, + SHOW_FISERV_INPUT, SHOW_PAYONE_INPUT, SHOW_GIROPAY_INPUT, SHOW_EXTERNAL_BILLING, @@ -174,7 +174,7 @@ object SnabbleUI { canGoBack = true ) - SHOW_CREDIT_CARD_INPUT -> + SHOW_FISERV_INPUT -> startActivity(context, FiservInputActivity::class.java, args, canGoBack = false) SHOW_PAYONE_INPUT -> startActivity(context, PayoneInputActivity::class.java, args, canGoBack = false) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt index 99451cca7d..60d42a5ff7 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt @@ -41,7 +41,7 @@ object PaymentInputViewHelper { useFiserv -> { args.putString(FiservInputView.ARG_PROJECT_ID, projectId) args.putSerializable(FiservInputView.ARG_PAYMENT_TYPE, paymentMethod) - SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_CREDIT_CARD_INPUT, args) + SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_FISERV_INPUT, args) } paymentMethod == PaymentMethod.EXTERNAL_BILLING -> { args.putString(ARG_PROJECT_ID, projectId) From e5029e8f6ca60ed5796b6f7d9460d60bfd63c457 Mon Sep 17 00:00:00 2001 From: Christian Maier Date: Wed, 7 Aug 2024 11:32:08 +0200 Subject: [PATCH 25/25] Small improvements --- .../sdk/ui/payment/fiserv/FiservViewModel.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt index 19eea0b4d5..39ff761eeb 100644 --- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt +++ b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt @@ -17,19 +17,21 @@ import kotlinx.coroutines.launch internal class FiservViewModel( private val fiservRepo: FiservRepository, countryItemsRepo: CountryItemsRepository, - private val savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle ) : ViewModel() { private val _uiState = MutableStateFlow(UiState(countryItems = countryItemsRepo.loadCountryItems())) val uiState: StateFlow = _uiState.asStateFlow() + private val paymentMethod = savedStateHandle.get(FiservInputView.ARG_PAYMENT_TYPE) + fun sendUserData(customerInfo: CustomerInfo) { viewModelScope.launch { - _uiState.update { it.copy(isLoading = true) } - val paymentMethod = - savedStateHandle.get(FiservInputView.ARG_PAYMENT_TYPE) ?: return@launch - val result = fiservRepo.sendUserData(customerInfo, paymentMethod) - result + paymentMethod ?: return@launch + + _uiState.update { it.copy(isLoading = true, showError = false) } + + fiservRepo.sendUserData(customerInfo, paymentMethod) .onSuccess { info -> _uiState.update { it.copy(