Skip to content

Commit

Permalink
Add payment terms text to payment view, open links in device's defaul…
Browse files Browse the repository at this point in the history
…t browser
  • Loading branch information
mliikanen committed Jul 4, 2023
1 parent acd3080 commit 02fea04
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 26 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ agp = "8.1.0-beta04"
coil = "2.4.0"
desugar_jdk_libs = "2.0.3"
flipper = "0.199.0"
html-text = "1.3.1"
kotlin = "1.8.21"
ksp = "1.8.21-1.0.11"
kotlintest-runner-junit5 = "3.4.2"
Expand Down Expand Up @@ -43,6 +44,7 @@ coil-test = { module = "io.coil-kt:coil-test", version.ref = "coil" }
flipper = { module = "com.facebook.flipper:flipper", version.ref = "flipper" }
flipper-network-plugin = { module = "com.facebook.flipper:flipper-network-plugin", version.ref = "flipper" }
flipper-noop = { module = "com.facebook.flipper:flipper-noop", version.ref = "flipper" }
html-text = { module = "de.charlex.compose:html-text", version.ref = "html-text" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
Expand Down
1 change: 1 addition & 0 deletions payment-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ dependencies {
implementation(libs.compose.runtime.livedata)
implementation(libs.ui.tooling.preview)
implementation(libs.material3)
implementation(libs.html.text)

implementation(libs.coil)
implementation(libs.coil.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.decode.SvgDecoder
import coil.request.ImageRequest
import de.charlex.compose.HtmlText
import fi.paytrail.paymentsdk.model.PaymentMethod
import fi.paytrail.paymentsdk.model.PaymentMethodGroup
import fi.paytrail.sdk.apiclient.models.PaymentMethodProvider
Expand All @@ -42,10 +43,16 @@ fun PaymentProviders(
modifier: Modifier = Modifier,
viewModel: PaymentViewModel,
) {
val providers = viewModel.paymentProviderListing.observeAsState(emptyList()).value
val providers = viewModel.paymentMethodGroups.observeAsState(emptyList()).value
val terms = viewModel.paymentTerms.observeAsState("").value

if (providers.isNotEmpty()) {
PaymentProviderListing(modifier, providers, viewModel::startPayment)
PaymentProviderListing(
modifier = modifier,
terms = terms,
groups = providers,
onPaymentMethodSelected = viewModel::startPayment,
)
} else {
NoPaymentProvidersAvailable(modifier)
}
Expand All @@ -55,16 +62,19 @@ fun PaymentProviders(
@Composable
private fun PaymentProviderListing(
modifier: Modifier = Modifier,
terms: String?,
groups: List<PaymentMethodGroup>,
onPaymentMethodSelected: (PaymentMethod) -> Unit = {},
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 8.dp)
.padding(start = 8.dp, end = 8.dp, top = 16.dp, bottom = 32.dp)
.verticalScroll(rememberScrollState()),
) {
Spacer(modifier = Modifier.height(8.dp))
if (terms != null) {
PaytrailTerms(terms)
}
for (group in groups) {
key(group) {
PaymentGroupHeader(group)
Expand All @@ -82,10 +92,27 @@ private fun PaymentProviderListing(
Spacer(modifier = Modifier.height(8.dp))
}
}
Spacer(modifier = Modifier.height(32.dp))
}
}

@Preview
@Composable
fun PreviewPaytrailTerms() {
PaytrailTerms(
terms = "By selecting a payment method, you agree to our <a href=\\\"https://www.paytrail.com/kuluttaja/maksupalveluehdot\\\" target=\\\"_blank\\\">payment service terms & conditions</a>",
)
}

@Composable
fun PaytrailTerms(terms: String) {
// TODO: Consider adding a composition-local LocalUriHandler to open
// the terms in a WebView instead of platform default browser.
HtmlText(
modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 16.dp),
text = terms,
)
}

@Composable
fun PaymentGroupHeader(group: PaymentMethodGroup) {
Text(group.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import fi.paytrail.paymentsdk.model.PaymentMethod
import fi.paytrail.paymentsdk.model.PaymentMethodGroup
import fi.paytrail.paymentsdk.model.PaytrailApiErrorResponse
Expand All @@ -24,6 +26,8 @@ import fi.paytrail.sdk.apiclient.apis.PaymentsApi
import fi.paytrail.sdk.apiclient.infrastructure.ApiClient
import fi.paytrail.sdk.apiclient.models.ErrorResponse.Companion.deserialize
import fi.paytrail.sdk.apiclient.models.PaymentRequest
import fi.paytrail.sdk.apiclient.models.PaymentRequestResponse
import retrofit2.Response

class PaymentViewModel(
val paymentRequest: PaymentRequest,
Expand All @@ -35,26 +39,57 @@ class PaymentViewModel(
).createService(PaymentsApi::class.java)
}

val paymentProviderListing = liveData {
val createPaymentResponse: LiveData<Response<PaymentRequestResponse>> = liveData {
try {
val result = api.createPayment(paymentRequest = paymentRequest)
if (result.isSuccessful) {
val body = result.body()
emit(api.createPayment(paymentRequest = paymentRequest))
} catch (e: Exception) {
Log.i("PaymentViewModel", "Error in loading payment providers", e)
paymentError.postValue(e)
}
}

val providers = body?.providers ?: emptyList()
val groups = body?.groups ?: emptyList()
val paymentTerms: LiveData<String> = createPaymentResponse.map {
if (it.isSuccessful) {
it.body()?.terms ?: ""
} else {
""
}
}

val paymentMethodGroups: LiveData<List<PaymentMethodGroup>> =
createPaymentResponse.map { result ->
try {
if (result.isSuccessful) {
val body = result.body()

val providers = body?.providers ?: emptyList()
val groups = body?.groups ?: emptyList()

emit(
groups.map { groupData ->
PaymentMethodGroup(
paymentMethodGroup = groupData,
paymentMethods = providers
.filter { provider -> provider.group.value == groupData.id.value }
.map { PaymentMethod(it) },
)
},
)
} else {
}
} else {
emptyList()
}
} catch (e: Exception) {
Log.i("PaymentViewModel", "Error in loading payment providers", e)
paymentError.postValue(e)
emptyList()
}
}

val selectedPaymentProvider = MutableLiveData<PaymentMethod?>(null)
private val paymentWebViewRedirect = MutableLiveData<PaytrailPaymentRedirect>()
private val paymentError = MutableLiveData<Exception>()

private val apiErrorResponse = createPaymentResponse.switchMap { result ->
liveData {
if (!result.isSuccessful) {
val errorBody = result.errorBody()?.string()
val errorResponse = errorBody?.let {
try {
Expand All @@ -64,25 +99,16 @@ class PaymentViewModel(
null
}
}
apiErrorResponse.postValue(
emit(
PaytrailApiErrorResponse(
code = result.code(),
errorBody = errorBody,
errorResponse = errorResponse,
),
)
emit(emptyList())
}
} catch (e: Exception) {
Log.i("PaymentViewModel", "Error in loading payment providers", e)
paymentError.postValue(e)
}
}

val selectedPaymentProvider = MutableLiveData<PaymentMethod?>(null)
private val paymentWebViewRedirect = MutableLiveData<PaytrailPaymentRedirect>()
private val paymentError = MutableLiveData<Exception>()
private val apiErrorResponse = MutableLiveData<PaytrailApiErrorResponse>()
private val paymentCanceled = MutableLiveData(false)

fun startPayment(provider: PaymentMethod) {
Expand Down Expand Up @@ -143,7 +169,7 @@ class PaymentViewModel(
}
}

addSource(paymentProviderListing) {
addSource(paymentMethodGroups) {
// TODO: This needs improving; we need to be looking at the create_payment
// request & response, and set state accordingly (loading/ok/error)
paymentProvidersLoaded = true
Expand Down

0 comments on commit 02fea04

Please sign in to comment.