fix: handle pending payment response from server correctly in ui

This commit is contained in:
2026-06-06 14:44:54 +02:00
parent 0f360067d1
commit 29de5c5086
@@ -20,6 +20,7 @@ import com.bitcointxoko.gudariwallet.util.SendInputType
import com.bitcointxoko.gudariwallet.util.WalletConstants
import com.bitcointxoko.gudariwallet.util.lnurlProtocolError
import com.bitcointxoko.gudariwallet.util.parseApiError
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@@ -87,26 +88,18 @@ class SendViewModel(
val decoded = _sendState.value as? SendState.Bolt11Decoded
val amountSats = decoded?.amountSats ?: 0L
_sendState.value = SendState.Paying
Log.d(TAG, "SEND [PAY BOLT11 START] Sending invoice to server")
runCatching { repo.payBolt11(bolt11) }
.onSuccess { response ->
val feeSats = runCatching {
repo.getPaymentDetail(response.paymentHash).details?.feeSat
}.getOrNull()
_sendState.value = SendState.PaymentSent(
amountSats = amountSats,
feeSats = feeSats,
paymentHash = response.paymentHash,
pendingRecord = buildPendingRecord(
paymentHash = response.paymentHash,
amountSats = amountSats,
feeSats = feeSats,
bolt11 = decoded?.bolt11,
memo = decoded?.memo,
pubkey = decoded?.payee,
alias = decoded?.nodeAlias
)
handlePaymentResult(
paymentHash = response.paymentHash,
amountSats,
bolt11 = decoded?.bolt11,
memo = decoded?.memo,
pubkey = decoded?.payee,
alias = decoded?.nodeAlias,
strings
)
balanceVm.refreshBalance()
}
.onFailure { e ->
Log.e(TAG, "SEND [PAY BOLT11 ERROR] ${e.message}")
@@ -117,6 +110,97 @@ class SendViewModel(
}
}
private suspend fun handlePaymentResult(
paymentHash : String,
amountSats : Long,
bolt11 : String?,
memo : String?,
pubkey : String?,
alias : String?,
strings : SendStrings,
) {
val detail = runCatching {
repo.getPaymentDetail(paymentHash)
}.getOrNull()
when {
// ── Settled ──────────────────────────────────────────────────────────
detail?.paid == true -> {
val feeSats = detail.details?.feeSat
_sendState.value = SendState.PaymentSent(
amountSats = amountSats,
feeSats = feeSats,
paymentHash = paymentHash,
pendingRecord = buildPendingRecord(paymentHash, amountSats, feeSats, bolt11, memo, pubkey, alias)
)
balanceVm.refreshBalance()
}
// ── Hold invoice — still pending ──────────────────────────────────
detail?.details?.status == "pending" || detail?.details?.pending == true -> {
// Stay in SendState.Paying and start polling (see Part 2)
pollUntilSettled(paymentHash, amountSats, bolt11, memo, pubkey, alias, strings)
}
// ── Failed or unknown ─────────────────────────────────────────────
else -> {
_sendState.value = SendState.Error(strings.paymentFailed)
}
}
}
private suspend fun pollUntilSettled(
paymentHash : String,
amountSats : Long,
bolt11 : String?,
memo : String?,
pubkey : String?,
alias : String?,
strings : SendStrings,
) {
// SendState.Paying is already set — the UI already shows the
// hourglass/slow-payment message after 10 s, so no UI change needed here.
val pollIntervalMs = 5_000L
val maxAttempts = 120 // 10 minutes total
repeat(maxAttempts) { attempt ->
delay(pollIntervalMs)
val detail = runCatching { repo.getPaymentDetail(paymentHash) }.getOrNull()
when {
detail?.paid == true -> {
val feeSats = detail.details?.feeSat
_sendState.value = SendState.PaymentSent(
amountSats = amountSats,
feeSats = feeSats,
paymentHash = paymentHash,
pendingRecord = buildPendingRecord(paymentHash, amountSats, feeSats, bolt11, memo, pubkey, alias)
)
balanceVm.refreshBalance()
return // done
}
// Still pending — keep polling
detail?.details?.status == "pending" || detail?.details?.pending == true -> {
Log.d(TAG, "HOLD INVOICE [POLL $attempt] still pending — $paymentHash")
}
// Failed (status changed to "failed" or detail is gone)
else -> {
Log.w(TAG, "HOLD INVOICE [POLL $attempt] payment failed — $paymentHash")
_sendState.value = SendState.Error(strings.paymentFailed)
return
}
}
}
// Timed out — leave in Paying so the user can dismiss manually
// (the existing 10-second slow-payment UI already handles this gracefully)
Log.w(TAG, "HOLD INVOICE [POLL TIMEOUT] $paymentHash — leaving in Paying state")
}
fun payLnurl(
rawRes : LnurlScanResponse,
lnurl : String,