fix: handle pending payment response from server correctly in ui
This commit is contained in:
@@ -20,6 +20,7 @@ import com.bitcointxoko.gudariwallet.util.SendInputType
|
|||||||
import com.bitcointxoko.gudariwallet.util.WalletConstants
|
import com.bitcointxoko.gudariwallet.util.WalletConstants
|
||||||
import com.bitcointxoko.gudariwallet.util.lnurlProtocolError
|
import com.bitcointxoko.gudariwallet.util.lnurlProtocolError
|
||||||
import com.bitcointxoko.gudariwallet.util.parseApiError
|
import com.bitcointxoko.gudariwallet.util.parseApiError
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -87,26 +88,18 @@ class SendViewModel(
|
|||||||
val decoded = _sendState.value as? SendState.Bolt11Decoded
|
val decoded = _sendState.value as? SendState.Bolt11Decoded
|
||||||
val amountSats = decoded?.amountSats ?: 0L
|
val amountSats = decoded?.amountSats ?: 0L
|
||||||
_sendState.value = SendState.Paying
|
_sendState.value = SendState.Paying
|
||||||
|
Log.d(TAG, "SEND [PAY BOLT11 START] Sending invoice to server")
|
||||||
runCatching { repo.payBolt11(bolt11) }
|
runCatching { repo.payBolt11(bolt11) }
|
||||||
.onSuccess { response ->
|
.onSuccess { response ->
|
||||||
val feeSats = runCatching {
|
handlePaymentResult(
|
||||||
repo.getPaymentDetail(response.paymentHash).details?.feeSat
|
|
||||||
}.getOrNull()
|
|
||||||
_sendState.value = SendState.PaymentSent(
|
|
||||||
amountSats = amountSats,
|
|
||||||
feeSats = feeSats,
|
|
||||||
paymentHash = response.paymentHash,
|
paymentHash = response.paymentHash,
|
||||||
pendingRecord = buildPendingRecord(
|
amountSats,
|
||||||
paymentHash = response.paymentHash,
|
|
||||||
amountSats = amountSats,
|
|
||||||
feeSats = feeSats,
|
|
||||||
bolt11 = decoded?.bolt11,
|
bolt11 = decoded?.bolt11,
|
||||||
memo = decoded?.memo,
|
memo = decoded?.memo,
|
||||||
pubkey = decoded?.payee,
|
pubkey = decoded?.payee,
|
||||||
alias = decoded?.nodeAlias
|
alias = decoded?.nodeAlias,
|
||||||
|
strings
|
||||||
)
|
)
|
||||||
)
|
|
||||||
balanceVm.refreshBalance()
|
|
||||||
}
|
}
|
||||||
.onFailure { e ->
|
.onFailure { e ->
|
||||||
Log.e(TAG, "SEND [PAY BOLT11 ERROR] ${e.message}")
|
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(
|
fun payLnurl(
|
||||||
rawRes : LnurlScanResponse,
|
rawRes : LnurlScanResponse,
|
||||||
lnurl : String,
|
lnurl : String,
|
||||||
|
|||||||
Reference in New Issue
Block a user