From 35f7ed512ed7445362d6caee1bf60441f4ce979e Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 4 Aug 2020 09:46:38 -0300 Subject: [pos] Implement new refund API (untested since there is no wallet support) Also do a bit of code cleanup and minor refactorings This also removes the volley HTTP library which is not needed anymore --- .../taler/merchantpos/history/HistoryFragment.kt | 108 +++++++++++++++++ .../merchantpos/history/MerchantHistoryFragment.kt | 108 ----------------- .../taler/merchantpos/history/RefundFragment.kt | 109 ----------------- .../net/taler/merchantpos/history/RefundManager.kt | 134 --------------------- .../taler/merchantpos/history/RefundUriFragment.kt | 69 ----------- 5 files changed, 108 insertions(+), 420 deletions(-) create mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt delete mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt delete mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt delete mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt delete mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt (limited to 'merchant-terminal/src/main/java/net/taler/merchantpos/history') diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt new file mode 100644 index 0000000..8cc435a --- /dev/null +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt @@ -0,0 +1,108 @@ +/* + * 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.merchantpos.history + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.fragment_merchant_history.* +import net.taler.common.exhaustive +import net.taler.common.navigate +import net.taler.merchantlib.OrderHistoryEntry +import net.taler.merchantpos.MainViewModel +import net.taler.merchantpos.R +import net.taler.merchantpos.history.HistoryFragmentDirections.Companion.actionGlobalMerchantSettings +import net.taler.merchantpos.history.HistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment + +internal interface RefundClickListener { + fun onRefundClicked(item: OrderHistoryEntry) +} + +/** + * Fragment to display the merchant's payment history, received from the backend. + */ +class HistoryFragment : Fragment(), RefundClickListener { + + companion object { + const val TAG = "taler-merchant" + } + + private val model: MainViewModel by activityViewModels() + private val historyManager by lazy { model.historyManager } + private val refundManager by lazy { model.refundManager } + + private val historyListAdapter = HistoryItemAdapter(this) + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_merchant_history, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + list_history.apply { + layoutManager = LinearLayoutManager(requireContext()) + addItemDecoration(DividerItemDecoration(context, VERTICAL)) + adapter = historyListAdapter + } + + swipeRefresh.setOnRefreshListener { + Log.v(TAG, "refreshing!") + historyManager.fetchHistory() + } + historyManager.isLoading.observe(viewLifecycleOwner, Observer { loading -> + Log.v(TAG, "setting refreshing to $loading") + swipeRefresh.isRefreshing = loading + }) + historyManager.items.observe(viewLifecycleOwner, Observer { result -> + when (result) { + is HistoryResult.Error -> onError(result.msg) + is HistoryResult.Success -> historyListAdapter.setData(result.items) + }.exhaustive + }) + } + + override fun onStart() { + super.onStart() + if (model.configManager.merchantConfig?.baseUrl == null) { + navigate(actionGlobalMerchantSettings()) + } else { + historyManager.fetchHistory() + } + } + + private fun onError(msg: String) { + Snackbar.make(requireView(), msg, LENGTH_LONG).show() + } + + override fun onRefundClicked(item: OrderHistoryEntry) { + refundManager.startRefund(item) + navigate(actionNavHistoryToRefundFragment()) + } + +} diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt deleted file mode 100644 index 596b8b0..0000000 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.merchantpos.history - -import android.os.Bundle -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL -import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG -import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_merchant_history.* -import net.taler.common.exhaustive -import net.taler.common.navigate -import net.taler.merchantlib.OrderHistoryEntry -import net.taler.merchantpos.MainViewModel -import net.taler.merchantpos.R -import net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionGlobalMerchantSettings -import net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment - -internal interface RefundClickListener { - fun onRefundClicked(item: OrderHistoryEntry) -} - -/** - * Fragment to display the merchant's payment history, received from the backend. - */ -class MerchantHistoryFragment : Fragment(), RefundClickListener { - - companion object { - const val TAG = "taler-merchant" - } - - private val model: MainViewModel by activityViewModels() - private val historyManager by lazy { model.historyManager } - private val refundManager by lazy { model.refundManager } - - private val historyListAdapter = HistoryItemAdapter(this) - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_merchant_history, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - list_history.apply { - layoutManager = LinearLayoutManager(requireContext()) - addItemDecoration(DividerItemDecoration(context, VERTICAL)) - adapter = historyListAdapter - } - - swipeRefresh.setOnRefreshListener { - Log.v(TAG, "refreshing!") - historyManager.fetchHistory() - } - historyManager.isLoading.observe(viewLifecycleOwner, Observer { loading -> - Log.v(TAG, "setting refreshing to $loading") - swipeRefresh.isRefreshing = loading - }) - historyManager.items.observe(viewLifecycleOwner, Observer { result -> - when (result) { - is HistoryResult.Error -> onError(result.msg) - is HistoryResult.Success -> historyListAdapter.setData(result.items) - }.exhaustive - }) - } - - override fun onStart() { - super.onStart() - if (model.configManager.merchantConfig?.baseUrl == null) { - navigate(actionGlobalMerchantSettings()) - } else { - historyManager.fetchHistory() - } - } - - private fun onError(msg: String) { - Snackbar.make(requireView(), msg, LENGTH_LONG).show() - } - - override fun onRefundClicked(item: OrderHistoryEntry) { - refundManager.startRefund(item) - navigate(actionNavHistoryToRefundFragment()) - } - -} diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt deleted file mode 100644 index 17d78f6..0000000 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.merchantpos.history - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.annotation.StringRes -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.navigation.fragment.findNavController -import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG -import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_refund.* -import net.taler.common.Amount -import net.taler.common.AmountParserException -import net.taler.common.fadeIn -import net.taler.common.fadeOut -import net.taler.common.navigate -import net.taler.merchantlib.OrderHistoryEntry -import net.taler.merchantpos.MainViewModel -import net.taler.merchantpos.R -import net.taler.merchantpos.history.RefundFragmentDirections.Companion.actionRefundFragmentToRefundUriFragment -import net.taler.merchantpos.history.RefundResult.AlreadyRefunded -import net.taler.merchantpos.history.RefundResult.Error -import net.taler.merchantpos.history.RefundResult.PastDeadline -import net.taler.merchantpos.history.RefundResult.Success - -class RefundFragment : Fragment() { - - private val model: MainViewModel by activityViewModels() - private val refundManager by lazy { model.refundManager } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_refund, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val item = refundManager.toBeRefunded ?: throw IllegalStateException() - amountInputView.setText(item.amount.amountStr) - currencyView.text = item.amount.currency - abortButton.setOnClickListener { findNavController().navigateUp() } - refundButton.setOnClickListener { onRefundButtonClicked(item) } - - refundManager.refundResult.observe(viewLifecycleOwner, Observer { result -> - onRefundResultChanged(result) - }) - } - - private fun onRefundButtonClicked(item: OrderHistoryEntry) { - val inputAmount = try { - Amount.fromString(item.amount.currency, amountInputView.text.toString()) - } catch (e: AmountParserException) { - amountView.error = getString(R.string.refund_error_invalid_amount) - return - } - if (inputAmount > item.amount) { - amountView.error = getString(R.string.refund_error_max_amount, item.amount.amountStr) - return - } - if (inputAmount.isZero()) { - amountView.error = getString(R.string.refund_error_zero) - return - } - amountView.error = null - refundButton.fadeOut() - progressBar.fadeIn() - refundManager.refund(item, inputAmount, reasonInputView.text.toString()) - } - - private fun onRefundResultChanged(result: RefundResult?): Any = when (result) { - Error -> onError(R.string.refund_error_backend) - PastDeadline -> onError(R.string.refund_error_deadline) - AlreadyRefunded -> onError(R.string.refund_error_already_refunded) - is Success -> { - progressBar.fadeOut() - refundButton.fadeIn() - navigate(actionRefundFragmentToRefundUriFragment()) - } - null -> { // no-op - } - } - - private fun onError(@StringRes res: Int) { - Snackbar.make(requireView(), res, LENGTH_LONG).show() - progressBar.fadeOut() - refundButton.fadeIn() - } - -} diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt deleted file mode 100644 index 7f9b4c5..0000000 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.merchantpos.history - -import android.util.Log -import androidx.annotation.UiThread -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import com.android.volley.Request.Method.POST -import com.android.volley.RequestQueue -import com.android.volley.Response.Listener -import com.android.volley.VolleyError -import net.taler.common.Amount -import net.taler.merchantlib.OrderHistoryEntry -import net.taler.merchantpos.LogErrorListener -import net.taler.merchantpos.config.ConfigManager -import net.taler.merchantpos.config.MerchantRequest -import org.json.JSONObject - -sealed class RefundResult { - object Error : RefundResult() - object PastDeadline : RefundResult() - object AlreadyRefunded : RefundResult() - class Success( - val refundUri: String, - val item: OrderHistoryEntry, - val amount: Amount, - val reason: String - ) : RefundResult() -} - -class RefundManager( - private val configManager: ConfigManager, - private val queue: RequestQueue -) { - - companion object { - val TAG = RefundManager::class.java.simpleName - } - - var toBeRefunded: OrderHistoryEntry? = null - private set - - private val mRefundResult = MutableLiveData() - internal val refundResult: LiveData = mRefundResult - - @UiThread - internal fun startRefund(item: OrderHistoryEntry) { - toBeRefunded = item - mRefundResult.value = null - } - - @UiThread - internal fun abortRefund() { - toBeRefunded = null - mRefundResult.value = null - } - - @UiThread - internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) { - val merchantConfig = configManager.merchantConfig!! - val refundRequest = mapOf( - "order_id" to item.orderId, - "refund" to amount.toJSONString(), - "reason" to reason - ) - val body = JSONObject(refundRequest) - Log.d(TAG, body.toString(4)) - val req = MerchantRequest(POST, merchantConfig, "refund", null, body, - Listener { onRefundResponse(it, item, amount, reason) }, - LogErrorListener { onRefundError(it) } - ) - queue.add(req) - } - - @UiThread - private fun onRefundResponse( - json: JSONObject, - item: OrderHistoryEntry, - amount: Amount, - reason: String - ) { - if (!json.has("contract_terms")) { - Log.e(TAG, "Contract terms missing: $json") - onRefundError() - return - } - - val contractTerms = json.getJSONObject("contract_terms") - val refundDeadline = if (contractTerms.has("refund_deadline")) { - contractTerms.getJSONObject("refund_deadline").getLong("t_ms") - } else null - val autoRefund = contractTerms.has("auto_refund") - val refundUri = json.getString("taler_refund_uri") - - Log.e("TEST", "refundDeadline: $refundDeadline") - if (refundDeadline != null) Log.e( - "TEST", - "refundDeadline passed: ${System.currentTimeMillis() > refundDeadline}" - ) - Log.e("TEST", "autoRefund: $autoRefund") - Log.e("TEST", "refundUri: $refundUri") - - mRefundResult.value = RefundResult.Success(refundUri, item, amount, reason) - } - - @UiThread - private fun onRefundError(error: VolleyError? = null) { - val data = error?.networkResponse?.data - if (data != null) { - val json = JSONObject(String(data)) - if (json.has("code") && json.getInt("code") == 2602) { - mRefundResult.value = RefundResult.AlreadyRefunded - return - } - } - mRefundResult.value = RefundResult.Error - } - -} diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt deleted file mode 100644 index 1ea0959..0000000 --- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.merchantpos.history - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.navigation.fragment.findNavController -import kotlinx.android.synthetic.main.fragment_refund_uri.* -import net.taler.common.NfcManager.Companion.hasNfc -import net.taler.common.QrCodeManager.makeQrCode -import net.taler.merchantpos.MainViewModel -import net.taler.merchantpos.R - -class RefundUriFragment : Fragment() { - - private val model: MainViewModel by activityViewModels() - private val refundManager by lazy { model.refundManager } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_refund_uri, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val result = refundManager.refundResult.value - if (result !is RefundResult.Success) throw IllegalStateException() - - refundQrcodeView.setImageBitmap(makeQrCode(result.refundUri)) - - val introRes = - if (hasNfc(requireContext())) R.string.refund_intro_nfc else R.string.refund_intro - refundIntroView.setText(introRes) - - refundAmountView.text = result.amount.toString() - - refundRefView.text = - getString(R.string.refund_order_ref, result.item.orderId, result.reason) - - cancelRefundButton.setOnClickListener { findNavController().navigateUp() } - completeButton.setOnClickListener { findNavController().navigateUp() } - } - - override fun onDestroy() { - super.onDestroy() - refundManager.abortRefund() - } - -} -- cgit v1.2.3