diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt index 91672e4f..e39c6a78 100644 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.MutableLiveData import net.taler.lib.android.assertUiThread import net.taler.merchantlib.CheckPaymentResponse import net.taler.merchantlib.MerchantApi +import net.taler.merchantlib.MerchantConfig import net.taler.merchantlib.OrderHistoryEntry import net.taler.merchantlib.PostOrderRequest import net.taler.merchantpos.MainActivity.Companion.TAG @@ -91,8 +92,8 @@ class PaymentManager( order: Order, includeProducts: Boolean = true, ) = scope.launch { - val merchantConfig = configManager.merchantConfig!! - val payment = Payment(order, order.summary, configManager.currency!!) + val merchantConfig = liveConfig() ?: return@launch + val payment = Payment(order, order.summary, configManager.currency ?: return@launch) mPayment.value = payment val parts = order.toOrderParts(includeProducts) val request = @@ -235,7 +236,7 @@ class PaymentManager( private fun checkPayment(orderId: String) = scope.launch { - val merchantConfig = configManager.merchantConfig!! + val merchantConfig = liveConfig() ?: return@launch api.checkOrder(merchantConfig, orderId).handle( { error -> // don't call onNetworkError() to not cancel payment, just keep trying @@ -369,10 +370,13 @@ class PaymentManager( fun cancelPayment(error: String? = null) { stopPaymentChecks() - val merchantConfig = configManager.merchantConfig!! + // Not required: cancelling has to work even when the session is the + // thing that ended, and then there is nobody to delete the order at. + val merchantConfig = configManager.merchantConfig mPayment.value?.let { payment -> // A checkout order records cash or card already taken, so it stays for resuming. - if (!payment.paid && + if (merchantConfig != null && + !payment.paid && payment.checkoutId == null) { payment.orderId?.let { orderId -> @@ -398,7 +402,7 @@ class PaymentManager( mDeleteNeedsForce.value = false return } - val merchantConfig = configManager.merchantConfig!! + val merchantConfig = liveConfig() ?: return val orderId = mPayment.value?.orderId ?: return scope.launch { val result = api.deleteOrder(merchantConfig, orderId) @@ -419,7 +423,7 @@ class PaymentManager( @UiThread fun forceDeleteOrder() { - val merchantConfig = configManager.merchantConfig!! + val merchantConfig = liveConfig() ?: return val orderId = mPayment.value?.orderId ?: return Log.d(TAG, "Force deleting claimed order $orderId") scope.launch { @@ -437,6 +441,27 @@ class PaymentManager( mDeleteNeedsForce.value = null } + /** + * The merchant configuration, or null once the session has ended. + * + * `ConfigManager` clears it the moment a request comes back 401, and the + * payment screen polls once a second -- so between two ticks of + * [checkTimer] the field can go from a configuration to nothing, and + * every `!!` on it here was a crash on the main thread waiting for a + * token to expire. Where there is no session left there is also nothing + * useful to ask the backend, so the polling is stopped as well. + */ + private fun liveConfig(): MerchantConfig? + { + val merchantConfig = configManager.merchantConfig + if (merchantConfig == null) + { + Log.d(TAG, "no merchant configuration left; stopping payment checks") + stopPaymentChecks() + } + return merchantConfig + } + private fun stopPaymentChecks() { checkTimer.cancel()