Skip to content

Commit

Permalink
Added Support for BitCash, NetCash and Paidy (#52)
Browse files Browse the repository at this point in the history
* Added Support for BitCash, NetCash and Paidy

* Added Support for webMoney

* Added Support for webMoney

* ktlint fixup
  • Loading branch information
AmniX authored Oct 17, 2024
1 parent 5333771 commit f4ea74d
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ internal data class KomojuPaymentScreen(private val sdkConfiguration: KomojuSDK.
onNetCashDisplayDataChange = screenViewModel::onNetCashDisplayDataChange,
webMoneyDisplayData = uiState.webMoneyDisplayData,
onWebMoneyDisplayDataChange = screenViewModel::onWebMoneyDisplayDataChange,
paidyDisplayData = uiState.paidyDisplayData,
onPaidyDisplayDataChange = screenViewModel::onPaidyDisplayDataChange,
onPaymentRequested = screenViewModel::onPaymentRequested,
)
}
Expand All @@ -114,6 +116,8 @@ internal data class KomojuPaymentScreen(private val sdkConfiguration: KomojuSDK.
}
if (uiState.selectedPaymentMethod is PaymentMethod.CreditCard &&
sdkConfiguration.inlinedProcessing.not() &&
uiState.isLoading ||
uiState.selectedPaymentMethod !is PaymentMethod.CreditCard &&
uiState.isLoading
) {
Box(
Expand All @@ -128,12 +132,19 @@ internal data class KomojuPaymentScreen(private val sdkConfiguration: KomojuSDK.
}
if (inlineWebViewURL != null) {
InlinedWebView(
modifier = Modifier.background(Color.White).align(Alignment.CenterEnd).animateContentSize().then(
when {
shouldShowFullScreenWebView -> Modifier.fillMaxSize()
else -> Modifier.width(Dp.Hairline).fillMaxHeight()
},
),
modifier = Modifier
.background(Color.White)
.align(Alignment.CenterEnd)
.animateContentSize()
.then(
when {
shouldShowFullScreenWebView -> Modifier.fillMaxSize()
else ->
Modifier
.width(Dp.Hairline)
.fillMaxHeight()
},
),
url = inlineWebViewURL,
onDone = {
screenViewModel.onInlinedDeeplinkCaptured(it)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.komoju.android.sdk.ui.screens.payment

import androidx.core.text.isDigitsOnly
import cafe.adriel.voyager.core.model.screenModelScope
import com.komoju.android.sdk.KomojuSDK
import com.komoju.android.sdk.navigation.RouterStateScreenModel
Expand All @@ -16,6 +17,7 @@ import com.komoju.android.sdk.utils.isValidEmail
import com.komoju.mobile.sdk.entities.Payment
import com.komoju.mobile.sdk.entities.PaymentMethod
import com.komoju.mobile.sdk.entities.PaymentRequest
import com.komoju.mobile.sdk.entities.PaymentStatus
import com.komoju.mobile.sdk.entities.PaymentStatus.Companion.isSuccessful
import com.komoju.mobile.sdk.entities.SecureTokenRequest
import com.komoju.mobile.sdk.entities.SecureTokenResponse.Status.ERRORED
Expand Down Expand Up @@ -96,6 +98,10 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
it.copy(webMoneyDisplayData = webMoneyDisplayData)
}

fun onPaidyDisplayDataChange(paidyDisplayData: PaidyDisplayData) = mutableState.update {
it.copy(paidyDisplayData = paidyDisplayData)
}

fun onPaymentRequested(paymentMethod: PaymentMethod) {
if (paymentMethod.validate()) {
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.LOADING)
Expand All @@ -106,9 +112,14 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
val request = paymentMethod.toPaymentRequest()
screenModelScope.launch {
komojuApi.sessions.pay(config.sessionId.orEmpty(), request).onSuccess { payment ->
mutableState.update { it.copy(isLoading = true) }
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.LOADING)
payment.handle()
if (payment is Payment.Error) {
mutableState.update { it.copy(isLoading = false) }
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.ERROR)
} else {
mutableState.update { it.copy(isLoading = true) }
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.LOADING)
payment.handle()
}
}.onFailure {
mutableState.update { it.copy(isLoading = false) }
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.ERROR)
Expand Down Expand Up @@ -159,6 +170,9 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
),
)
}
if (newState == InlinedPaymentPrimaryButtonState.ERROR) {
mutableRouter.value = Router.SetPaymentResultAndPop()
}
}

private suspend fun verifyAndProcessInlinedPayment(token: String, amount: String, currency: String) {
Expand All @@ -169,7 +183,6 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
currency = currency,
onError = {
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.ERROR)
mutableRouter.value = Router.SetPaymentResultAndPop()
},
onSuccess = {
changeInlinePaymentState(InlinedPaymentPrimaryButtonState.SUCCESS)
Expand All @@ -192,17 +205,91 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
when (this) {
is Payment.Konbini -> mutableRouter.value = Router.Replace(KomojuPaymentRoute.KonbiniAwaitingPayment(config, payment = this))
is Payment.OffSitePayment -> _offSitePaymentURL.value = redirectURL
is Payment.Completed -> mutableRouter.value = Router.SetPaymentResultAndPop(KomojuSDK.PaymentResult(isSuccessFul = status == PaymentStatus.CAPTURED))
else -> Unit
}
}

private fun PaymentMethod.validate() = when (this) {
is PaymentMethod.CreditCard -> state.value.creditCardDisplayData.validate()
is PaymentMethod.Konbini -> state.value.konbiniDisplayData.validate(state.value.commonDisplayData)
is PaymentMethod.Paidy -> state.value.paidyDisplayData.validate()
is PaymentMethod.NetCash -> state.value.netCashDisplayData.validate()
is PaymentMethod.BitCash -> state.value.bitCashDisplayData.validate()
is PaymentMethod.WebMoney -> state.value.webMoneyDisplayData.validate()
is PaymentMethod.OffSitePayment -> true // No input required for Offsite payment
else -> false
}

private fun WebMoneyDisplayData.validate(): Boolean {
val prepaidNumberError = when {
prepaidNumber.isBlank() -> "The entered prepaid number cannot be empty"
prepaidNumber.length != 16 -> "The entered prepaid number is not valid"
else -> null
}
mutableState.update {
it.copy(
webMoneyDisplayData = it.webMoneyDisplayData.copy(
prepaidNumberError = prepaidNumberError,
),
)
}
return prepaidNumberError == null
}

private fun BitCashDisplayData.validate(): Boolean {
val idError = when {
bitCashId.isBlank() -> "The entered net cash id cannot be empty"
bitCashId.length != 16 -> "The entered net cash id is not valid"
else -> null
}
mutableState.update {
it.copy(
bitCashDisplayData = it.bitCashDisplayData.copy(
bitCashError = idError,
),
)
}
return idError == null
}
private fun NetCashDisplayData.validate(): Boolean {
val idError = when {
netCashId.isBlank() -> "The entered net cash id cannot be empty"
netCashId.length !in 16..20 -> "The entered net cash id is not valid"
else -> null
}
mutableState.update {
it.copy(
netCashDisplayData = it.netCashDisplayData.copy(
netCashError = idError,
),
)
}
return idError == null
}

private fun PaidyDisplayData.validate(): Boolean {
val fullNameError = when {
fullName.isBlank() -> "The entered name cannot be empty"
else -> null
}
val phoneNumberError = when {
phoneNumber.isBlank() -> "The entered phone number cannot be empty"
phoneNumber.length < 7 -> "The entered phone number is not valid"
phoneNumber.isDigitsOnly().not() -> "The entered phone number is not valid"
else -> null
}
mutableState.update {
it.copy(
paidyDisplayData = it.paidyDisplayData.copy(
fullNameError = fullNameError,
phoneNumberError = phoneNumberError,
),
)
}
return fullNameError == null && phoneNumberError == null
}

private fun CreditCardDisplayData.validate(): Boolean {
val fullNameOnCardError = when {
fullNameOnCard.isBlank() -> "The entered cardholder name cannot be empty"
Expand Down Expand Up @@ -256,20 +343,33 @@ internal class KomojuPaymentScreenModel(private val config: KomojuSDK.Configurat
)

private fun PaymentMethod.toPaymentRequest(): PaymentRequest = when (this) {
is PaymentMethod.BankTransfer -> TODO()
is PaymentMethod.BitCash -> TODO()
is PaymentMethod.BitCash -> PaymentRequest.BitCash(
paymentMethod = this,
bitCashId = state.value.bitCashDisplayData.bitCashId,
)
is PaymentMethod.CreditCard -> error("Credit Card needs to generate tokens first!")
is PaymentMethod.Konbini -> PaymentRequest.Konbini(
paymentMethod = this,
konbiniBrand = state.value.konbiniDisplayData.selectedKonbiniBrand!!,
email = state.value.commonDisplayData.email,
)

is PaymentMethod.NetCash -> TODO()
is PaymentMethod.Other -> TODO()
is PaymentMethod.Paidy -> TODO()
is PaymentMethod.NetCash -> PaymentRequest.NetCash(
paymentMethod = this,
netCashId = state.value.netCashDisplayData.netCashId,
)
is PaymentMethod.Other -> error("payment method is not supported!")
is PaymentMethod.Paidy -> PaymentRequest.Paidy(
paymentMethod = this,
fullName = state.value.paidyDisplayData.fullName,
phoneNumber = state.value.paidyDisplayData.phoneNumber,
)
is PaymentMethod.BankTransfer -> TODO()
is PaymentMethod.PayEasy -> TODO()
is PaymentMethod.WebMoney -> TODO()
is PaymentMethod.WebMoney -> PaymentRequest.WebMoney(
paymentMethod = this,
prepaidNumber = state.value.webMoneyDisplayData.prepaidNumber,
)
is PaymentMethod.OffSitePayment -> PaymentRequest.OffSitePaymentRequest(this)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ internal data class KomojuPaymentUIState(
val bitCashDisplayData: BitCashDisplayData = BitCashDisplayData(),
val netCashDisplayData: NetCashDisplayData = NetCashDisplayData(),
val webMoneyDisplayData: WebMoneyDisplayData = WebMoneyDisplayData(),
val paidyDisplayData: PaidyDisplayData = PaidyDisplayData(),
val inlinedCreditCardProcessingURL: String? = null,
)

internal data class CommonDisplayData(
val fullName: String = String.empty,
val lastName: String = String.empty,
val firstName: String = String.empty,
val lastNamePhonetic: String = String.empty,
Expand Down Expand Up @@ -49,8 +51,15 @@ internal data class KonbiniDisplayData(
val konbiniBrandNullError: String? = null,
)

internal data class BitCashDisplayData(val bitCashId: String = String.empty)
internal data class BitCashDisplayData(val bitCashId: String = String.empty, val bitCashError: String? = null)

internal data class NetCashDisplayData(val netCashId: String = String.empty)
internal data class NetCashDisplayData(val netCashId: String = String.empty, val netCashError: String? = null)

internal data class WebMoneyDisplayData(val prepaidNumber: String = String.empty)
internal data class WebMoneyDisplayData(val prepaidNumber: String = String.empty, val prepaidNumberError: String? = null)

internal data class PaidyDisplayData(
val fullName: String = String.empty,
val fullNameError: String? = null,
val phoneNumber: String = String.empty,
val phoneNumberError: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ internal fun BitCashForm(
onValueChange = {
onBitCashDisplayDataChange(bitCashDisplayData.copy(bitCashId = it))
},
error = bitCashDisplayData.bitCashError,
)
Spacer(modifier = Modifier.height(16.dp))
PrimaryButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ internal fun NetCashForm(
Column {
TextField(
value = netCashDisplayData.netCashId,
titleKey = "BIT_CASH_INPUT_LABEL",
placeholderKey = "BIT_CASH_INPUT_PLACEHOLDER",
titleKey = "NET_CASH_INPUT_LABEL",
placeholderKey = "NET_CASH_INPUT_PLACEHOLDER",
onValueChange = {
onNetCashDisplayDataChange(netCashDisplayData.copy(netCashId = it))
},
error = netCashDisplayData.netCashError,
)
Spacer(modifier = Modifier.height(16.dp))
PrimaryButton(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.komoju.android.sdk.ui.screens.payment.composables

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.komoju.android.sdk.ui.composables.PrimaryButton
import com.komoju.android.sdk.ui.screens.payment.PaidyDisplayData
import com.komoju.android.sdk.ui.theme.KomojuMobileSdkTheme
import com.komoju.android.sdk.ui.theme.LocalI18nTexts
import com.komoju.mobile.sdk.entities.PaymentMethod

@Composable
internal fun PaidyForm(paidy: PaymentMethod.Paidy, paidyDisplayData: PaidyDisplayData, onPaidyDisplayDataChange: (PaidyDisplayData) -> Unit, onPayButtonClicked: () -> Unit) {
Column {
TextField(
value = paidyDisplayData.fullName,
titleKey = "FULL_NAME",
placeholderKey = "FULL_NAME_PLACEHOLDER",
onValueChange = {
onPaidyDisplayDataChange(paidyDisplayData.copy(fullName = it))
},
error = paidyDisplayData.fullNameError,
)
TextField(
value = paidyDisplayData.phoneNumber,
titleKey = "TELEPHONE_NUMBER",
placeholderKey = "TELEPHONE_NUMBER_PLACEHOLDER",
keyboardType = KeyboardType.Phone,
onValueChange = {
onPaidyDisplayDataChange(paidyDisplayData.copy(phoneNumber = it))
},
error = paidyDisplayData.phoneNumberError,
)
PrimaryButton(LocalI18nTexts.current["CONTINUE_TO_PAIDY"], modifier = Modifier.fillMaxWidth().padding(all = 16.dp), onPayButtonClicked)
}
}

@Composable
@Preview(showBackground = true)
private fun PaidyFormPreview() {
val paidy = PaymentMethod.Paidy(
displayName = "Paidy",
hashedGateway = "hashedGateway",
exchangeRate = 1.0,
currency = "JPY",
amount = "100",
additionalFields = emptyList(),
isOffsite = true,
)

var paidyDisplayData by remember {
mutableStateOf(PaidyDisplayData())
}
KomojuMobileSdkTheme {
PaidyForm(
paidy,
paidyDisplayData = paidyDisplayData,
onPaidyDisplayDataChange = {
paidyDisplayData = it
},
onPayButtonClicked = {
},
)
}
}
Loading

0 comments on commit f4ea74d

Please sign in to comment.