From 8f62459d5e1de2f2d8e2a3b76ffeb5ac56e9147b Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Sun, 1 Feb 2026 14:03:19 +0530 Subject: [PATCH] fix razorpay qr credit log --- .../ui/razorpay/RazorpayQrScreen.kt | 28 +++++++++++++++-- .../ui/razorpay/RazorpayQrState.kt | 1 + .../ui/razorpay/RazorpayQrViewModel.kt | 31 ++++++++++++++++--- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrScreen.kt index e5c22c2..ce9eeab 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrScreen.kt @@ -73,10 +73,19 @@ fun RazorpayQrScreen( LaunchedEffect(propertyId, bookingId) { viewModel.loadQrList(propertyId, bookingId) } + LaunchedEffect(state.isCredited) { + if (state.isCredited) { + onBack() + } + } val isViewingQr = state.isClosed || !state.imageUrl.isNullOrBlank() - BackHandler(enabled = isViewingQr) { + val exitAndRefresh: () -> Unit = { viewModel.exitQrView() + viewModel.loadQrList(propertyId, bookingId) + } + BackHandler(enabled = isViewingQr) { + exitAndRefresh() } Scaffold( @@ -86,7 +95,7 @@ fun RazorpayQrScreen( navigationIcon = { IconButton(onClick = { if (isViewingQr) { - viewModel.exitQrView() + exitAndRefresh() } else { onBack() } @@ -98,7 +107,20 @@ fun RazorpayQrScreen( ) } ) { padding -> - if (state.isClosed) { + if (state.isCredited) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(padding), + contentAlignment = Alignment.Center + ) { + Text( + text = "Payment received", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.primary + ) + } + } else if (state.isClosed) { Box( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrState.kt b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrState.kt index 09abc7f..4b2e93a 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrState.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrState.kt @@ -10,6 +10,7 @@ data class RazorpayQrState( val currency: String? = null, val imageUrl: String? = null, val isClosed: Boolean = false, + val isCredited: Boolean = false, val paymentLink: String? = null, val qrList: List = emptyList() ) diff --git a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrViewModel.kt index 3e46219..babe3e0 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/razorpay/RazorpayQrViewModel.kt @@ -37,12 +37,14 @@ class RazorpayQrViewModel : ViewModel() { } fun exitQrView() { + stopQrEventStream() stopQrEventPolling() _state.update { it.copy( qrId = null, imageUrl = null, isClosed = false, + isCredited = false, error = null ) } @@ -121,6 +123,7 @@ class RazorpayQrViewModel : ViewModel() { if (qrId.isNullOrBlank()) return if (lastQrId == qrId && qrEventSource != null) return stopQrEventStream() + stopQrEventPolling() lastQrId = qrId val client = ApiClient.createOkHttpClient(readTimeoutSeconds = 0) val url = "${ApiConstants.BASE_URL}properties/$propertyId/bookings/$bookingId/payments/razorpay/qr/$qrId/events/stream" @@ -138,10 +141,17 @@ class RazorpayQrViewModel : ViewModel() { val event = runCatching { gson.fromJson(data, RazorpayQrEventDto::class.java) }.getOrNull() ?: return - if (isClosedStatus(event.status)) { - _state.update { it.copy(isClosed = true, imageUrl = null) } + val status = event.status?.lowercase() + val eventName = event.event?.lowercase() + if (eventName == "qr_code.credited" || status == "credited") { + _state.update { it.copy(isCredited = true, imageUrl = null) } stopQrEventStream() stopQrEventPolling() + return + } + if (isClosedStatus(status)) { + _state.update { it.copy(isClosed = true, imageUrl = null) } + stopQrEventStream() } } @@ -151,13 +161,16 @@ class RazorpayQrViewModel : ViewModel() { response: okhttp3.Response? ) { stopQrEventStream() + startQrEventPolling(propertyId, bookingId, qrId) } override fun onClosed(eventSource: EventSource) { stopQrEventStream() + startQrEventPolling(propertyId, bookingId, qrId) } } ) + // Keep polling as a fallback in case SSE is buffered or never delivers events. startQrEventPolling(propertyId, bookingId, qrId) } @@ -170,6 +183,7 @@ class RazorpayQrViewModel : ViewModel() { private fun startQrEventPolling(propertyId: String, bookingId: String, qrId: String) { if (qrPollJob?.isActive == true) return qrPollJob = viewModelScope.launch { + var delayMs = 4000L while (true) { val currentQrId = state.value.qrId if (currentQrId.isNullOrBlank() || currentQrId != qrId || state.value.isClosed) { @@ -183,16 +197,22 @@ class RazorpayQrViewModel : ViewModel() { ) val body = response.body() if (response.isSuccessful && body != null) { + if (body.any { it.event?.lowercase() == "qr_code.credited" || it.status?.lowercase() == "credited" }) { + _state.update { it.copy(isCredited = true, imageUrl = null) } + stopQrEventStream() + break + } if (body.any { isClosedStatus(it.status) }) { _state.update { it.copy(isClosed = true, imageUrl = null) } stopQrEventStream() break } } - } catch (_: Exception) { - // ignore polling errors + delayMs = 4000L + } catch (e: Exception) { + delayMs = (delayMs * 1.5).toLong().coerceAtMost(15000L) } - delay(5000) + delay(delayMs) } } } @@ -258,6 +278,7 @@ class RazorpayQrViewModel : ViewModel() { currency = item.currency, imageUrl = item.imageUrl, isClosed = status == "closed" || status == "expired" || status == "credited", + isCredited = status == "credited", error = null ) }