/* * This file is part of GNU Taler * (C) 2020 Taler Systems S.A. * * GNU Taler is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 3, or (at your option) any later version. * * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * GNU Taler; see the file COPYING. If not, see */ package net.taler.wallet.withdraw import android.util.Log import androidx.lifecycle.MutableLiveData import net.taler.common.Amount import net.taler.wallet.TAG import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails import org.json.JSONObject sealed class WithdrawStatus { data class Loading(val talerWithdrawUri: String) : WithdrawStatus() data class TermsOfServiceReviewRequired( val talerWithdrawUri: String, val exchange: String, val tosText: String, val tosEtag: String, val amount: Amount, val fee: Amount ) : WithdrawStatus() data class ReceivedDetails( val talerWithdrawUri: String, val exchange: String, val amount: Amount, val fee: Amount ) : WithdrawStatus() data class Withdrawing(val talerWithdrawUri: String) : WithdrawStatus() object Success : WithdrawStatus() data class Error(val message: String?) : WithdrawStatus() } class WithdrawManager(private val walletBackendApi: WalletBackendApi) { val withdrawStatus = MutableLiveData() val testWithdrawalInProgress = MutableLiveData(false) var exchangeFees: ExchangeFees? = null private set fun withdrawTestkudos() { testWithdrawalInProgress.value = true walletBackendApi.sendRequest("withdrawTestkudos", null) { _, _ -> testWithdrawalInProgress.postValue(false) } } fun getWithdrawalInfo(talerWithdrawUri: String) { val args = JSONObject().apply { put("talerWithdrawUri", talerWithdrawUri) } withdrawStatus.value = WithdrawStatus.Loading(talerWithdrawUri) walletBackendApi.sendRequest("getWithdrawDetailsForUri", args) { isError, result -> if (isError) { Log.e(TAG, "Error getWithdrawDetailsForUri ${result.toString(4)}") val message = if (result.has("message")) result.getString("message") else null withdrawStatus.postValue(WithdrawStatus.Error(message)) return@sendRequest } Log.v(TAG, "got getWithdrawDetailsForUri result") val status = withdrawStatus.value if (status !is WithdrawStatus.Loading) { Log.v(TAG, "ignoring withdrawal info result, not loading.") return@sendRequest } val wi = result.getJSONObject("bankWithdrawDetails") val suggestedExchange = wi.getString("suggestedExchange") // We just use the suggested exchange, in the future there will be // a selection dialog. getWithdrawalInfoWithExchange(talerWithdrawUri, suggestedExchange) } } private fun getWithdrawalInfoWithExchange(talerWithdrawUri: String, selectedExchange: String) { val args = JSONObject().apply { put("talerWithdrawUri", talerWithdrawUri) put("selectedExchange", selectedExchange) } walletBackendApi.sendRequest("getWithdrawDetailsForUri", args) { isError, result -> if (isError) { Log.e(TAG, "Error getWithdrawDetailsForUri ${result.toString(4)}") val message = if (result.has("message")) result.getString("message") else null withdrawStatus.postValue(WithdrawStatus.Error(message)) return@sendRequest } Log.v(TAG, "got getWithdrawDetailsForUri result (with exchange details)") val status = withdrawStatus.value if (status !is WithdrawStatus.Loading) { Log.w(TAG, "ignoring withdrawal info result, not loading.") return@sendRequest } val wi = result.getJSONObject("bankWithdrawDetails") val amount = Amount.fromJsonObject(wi.getJSONObject("amount")) val ei = result.getJSONObject("exchangeWithdrawDetails") val termsOfServiceAccepted = ei.getBoolean("termsOfServiceAccepted") exchangeFees = ExchangeFees.fromExchangeWithdrawDetailsJson(ei) val withdrawFee = Amount.fromJsonObject(ei.getJSONObject("withdrawFee")) val overhead = Amount.fromJsonObject(ei.getJSONObject("overhead")) val fee = withdrawFee + overhead if (!termsOfServiceAccepted) { val exchange = ei.getJSONObject("exchangeInfo") val tosText = exchange.getString("termsOfServiceText") val tosEtag = exchange.optString("termsOfServiceLastEtag", "undefined") withdrawStatus.postValue( WithdrawStatus.TermsOfServiceReviewRequired( status.talerWithdrawUri, selectedExchange, tosText, tosEtag, amount, fee ) ) } else { withdrawStatus.postValue( ReceivedDetails( status.talerWithdrawUri, selectedExchange, amount, fee ) ) } } } fun acceptWithdrawal(talerWithdrawUri: String, selectedExchange: String) { val args = JSONObject() args.put("talerWithdrawUri", talerWithdrawUri) args.put("selectedExchange", selectedExchange) withdrawStatus.value = WithdrawStatus.Withdrawing(talerWithdrawUri) walletBackendApi.sendRequest("acceptWithdrawal", args) { isError, result -> if (isError) { Log.v(TAG, "got acceptWithdrawal error result: ${result.toString(4)}") return@sendRequest } Log.v(TAG, "got acceptWithdrawal result") val status = withdrawStatus.value if (status !is WithdrawStatus.Withdrawing) { Log.w(TAG, "ignoring acceptWithdrawal result, invalid state: $status") return@sendRequest } withdrawStatus.postValue(WithdrawStatus.Success) } } /** * Accept the currently displayed terms of service. */ fun acceptCurrentTermsOfService() { val s = withdrawStatus.value check(s is WithdrawStatus.TermsOfServiceReviewRequired) val args = JSONObject().apply { put("exchangeBaseUrl", s.exchange) put("etag", s.tosEtag) } walletBackendApi.sendRequest("acceptExchangeTermsOfService", args) { isError, result -> if (isError) { Log.e(TAG, "Error acceptExchangeTermsOfService ${result.toString(4)}") return@sendRequest } val status = ReceivedDetails(s.talerWithdrawUri, s.exchange, s.amount, s.fee) withdrawStatus.postValue(status) } } }