gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] 01/02: [pos] improve payment processing


From: gnunet
Subject: [taler-taler-android] 01/02: [pos] improve payment processing
Date: Mon, 03 Aug 2020 18:46:53 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository taler-android.

commit 5b1163311192e9adf15ef3d626c72812e638f90c
Author: Torsten Grote <t@grobox.de>
AuthorDate: Mon Aug 3 13:31:26 2020 -0300

    [pos] improve payment processing
    
    - cancel orders that have been abandoned and will not be paid
    - show unpaid orders in history (in case one makes it through)
    - set deadlines when creating orders in case it helps with enabling
      refunds
---
 .../main/java/net/taler/merchantlib/MerchantApi.kt |  5 +-
 .../java/net/taler/merchantlib/OrderHistory.kt     |  3 +
 .../java/net/taler/merchantlib/PostOrderRequest.kt |  6 +-
 .../merchantpos/history/HistoryItemAdapter.kt      | 88 ++++++++++++++++++++++
 .../taler/merchantpos/history/HistoryManager.kt    |  6 +-
 .../merchantpos/history/MerchantHistoryFragment.kt | 60 +--------------
 .../main/java/net/taler/merchantpos/order/Order.kt |  7 +-
 .../taler/merchantpos/payment/PaymentManager.kt    |  9 ++-
 .../merchantpos/payment/ProcessPaymentFragment.kt  |  5 ++
 merchant-terminal/src/main/res/values/strings.xml  |  1 +
 .../main/java/net/taler/common/ContractTerms.kt    |  8 +-
 11 files changed, 128 insertions(+), 70 deletions(-)

diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
index db37586..96892f5 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -29,7 +29,6 @@ import io.ktor.http.HttpHeaders.Authorization
 import io.ktor.http.contentType
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonConfiguration
-import net.taler.common.ContractTerms
 import net.taler.merchantlib.Response.Companion.response
 
 class MerchantApi(private val httpClient: HttpClient) {
@@ -40,12 +39,12 @@ class MerchantApi(private val httpClient: HttpClient) {
 
     suspend fun postOrder(
         merchantConfig: MerchantConfig,
-        contractTerms: ContractTerms
+        orderRequest: PostOrderRequest
     ): Response<PostOrderResponse> = response {
         httpClient.post(merchantConfig.urlFor("private/orders")) {
             header(Authorization, "ApiKey ${merchantConfig.apiKey}")
             contentType(Json)
-            body = PostOrderRequest(contractTerms)
+            body = orderRequest
         } as PostOrderResponse
     }
 
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
index 718bde5..b1ff5b1 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
@@ -41,6 +41,9 @@ data class OrderHistoryEntry(
     // the summary of the order
     val summary: String,
 
+    // if the order has been paid
+    val paid: Boolean,
+
     // whether some part of the order is refundable
     val refundable: Boolean
 )
diff --git 
a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
index 4854a80..783dd19 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
@@ -25,12 +25,14 @@ import kotlinx.serialization.Serializer
 import kotlinx.serialization.json.JsonInput
 import kotlinx.serialization.json.JsonObject
 import net.taler.common.ContractTerms
+import net.taler.common.Duration
 
 @Serializable
 data class PostOrderRequest(
     @SerialName("order")
-    val contractTerms: ContractTerms
-
+    val contractTerms: ContractTerms,
+    @SerialName("refund_delay")
+    val refundDelay: Duration? = null
 )
 
 @Serializable
diff --git 
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryItemAdapter.kt
 
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryItemAdapter.kt
new file mode 100644
index 0000000..25e94fb
--- /dev/null
+++ 
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryItemAdapter.kt
@@ -0,0 +1,88 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.merchantpos.history
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import net.taler.common.toRelativeTime
+import net.taler.merchantlib.OrderHistoryEntry
+import net.taler.merchantpos.R
+import net.taler.merchantpos.history.HistoryItemAdapter.HistoryItemViewHolder
+import java.util.ArrayList
+
+
+internal class HistoryItemAdapter(private val listener: RefundClickListener) :
+    Adapter<HistoryItemViewHolder>() {
+
+    private val items = ArrayList<OrderHistoryEntry>()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 
HistoryItemViewHolder {
+        val v =
+            
LayoutInflater.from(parent.context).inflate(R.layout.list_item_history, parent, 
false)
+        return HistoryItemViewHolder(v)
+    }
+
+    override fun getItemCount() = items.size
+
+    override fun onBindViewHolder(holder: HistoryItemViewHolder, position: 
Int) {
+        holder.bind(items[position])
+    }
+
+    fun setData(items: List<OrderHistoryEntry>) {
+        this.items.clear()
+        this.items.addAll(items)
+        this.notifyDataSetChanged()
+    }
+
+    internal inner class HistoryItemViewHolder(private val v: View) : 
RecyclerView.ViewHolder(v) {
+
+        private val orderSummaryView: TextView = 
v.findViewById(R.id.orderSummaryView)
+        private val orderAmountView: TextView = 
v.findViewById(R.id.orderAmountView)
+        private val orderTimeView: TextView = 
v.findViewById(R.id.orderTimeView)
+        private val orderIdView: TextView = v.findViewById(R.id.orderIdView)
+        private val refundButton: ImageButton = 
v.findViewById(R.id.refundButton)
+
+        private val orderIdColor = orderIdView.currentTextColor
+
+        fun bind(item: OrderHistoryEntry) {
+            orderSummaryView.text = item.summary
+            val amount = item.amount
+            orderAmountView.text = amount.toString()
+            orderTimeView.text = item.timestamp.ms.toRelativeTime(v.context)
+            if (item.paid) {
+                orderIdView.text = 
v.context.getString(R.string.history_ref_no, item.orderId)
+                orderIdView.setTextColor(orderIdColor)
+            } else {
+                orderIdView.text = v.context.getString(R.string.history_unpaid)
+                orderIdView.setTextColor(v.context.getColor(R.color.red))
+            }
+            if (item.refundable) {
+                refundButton.visibility = View.VISIBLE
+                refundButton.setOnClickListener { 
listener.onRefundClicked(item) }
+            } else {
+                refundButton.visibility = View.GONE
+            }
+        }
+
+    }
+
+}
diff --git 
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
 
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
index cb77096..aabe4cc 100644
--- 
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
+++ 
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
@@ -55,9 +55,9 @@ class HistoryManager(
         }
     }
 
-    private fun onHistoryError(msg: String) = scope.launch(Dispatchers.Main) {
-        mIsLoading.value = false
-        mItems.value = HistoryResult.Error(msg)
+    private fun onHistoryError(msg: String) {
+        mIsLoading.postValue(false)
+        mItems.postValue(HistoryResult.Error(msg))
     }
 
 }
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
index 25805dc..596b8b0 100644
--- 
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
+++ 
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
@@ -20,34 +20,25 @@ import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
-import android.view.View.GONE
-import android.view.View.VISIBLE
 import android.view.ViewGroup
-import android.widget.ImageButton
-import android.widget.TextView
 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 androidx.recyclerview.widget.RecyclerView.Adapter
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
 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.common.toRelativeTime
 import net.taler.merchantlib.OrderHistoryEntry
 import net.taler.merchantpos.MainViewModel
 import net.taler.merchantpos.R
-import net.taler.merchantpos.history.HistoryItemAdapter.HistoryItemViewHolder
 import 
net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionGlobalMerchantSettings
 import 
net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment
-import java.util.ArrayList
 
-private interface RefundClickListener {
+internal interface RefundClickListener {
     fun onRefundClicked(item: OrderHistoryEntry)
 }
 
@@ -115,52 +106,3 @@ class MerchantHistoryFragment : Fragment(), 
RefundClickListener {
     }
 
 }
-
-private class HistoryItemAdapter(private val listener: RefundClickListener) :
-    Adapter<HistoryItemViewHolder>() {
-
-    private val items = ArrayList<OrderHistoryEntry>()
-
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 
HistoryItemViewHolder {
-        val v =
-            
LayoutInflater.from(parent.context).inflate(R.layout.list_item_history, parent, 
false)
-        return HistoryItemViewHolder(v)
-    }
-
-    override fun getItemCount() = items.size
-
-    override fun onBindViewHolder(holder: HistoryItemViewHolder, position: 
Int) {
-        holder.bind(items[position])
-    }
-
-    fun setData(items: List<OrderHistoryEntry>) {
-        this.items.clear()
-        this.items.addAll(items)
-        this.notifyDataSetChanged()
-    }
-
-    private inner class HistoryItemViewHolder(private val v: View) : 
ViewHolder(v) {
-
-        private val orderSummaryView: TextView = 
v.findViewById(R.id.orderSummaryView)
-        private val orderAmountView: TextView = 
v.findViewById(R.id.orderAmountView)
-        private val orderTimeView: TextView = 
v.findViewById(R.id.orderTimeView)
-        private val orderIdView: TextView = v.findViewById(R.id.orderIdView)
-        private val refundButton: ImageButton = 
v.findViewById(R.id.refundButton)
-
-        fun bind(item: OrderHistoryEntry) {
-            orderSummaryView.text = item.summary
-            val amount = item.amount
-            orderAmountView.text = amount.toString()
-            orderIdView.text = v.context.getString(R.string.history_ref_no, 
item.orderId)
-            orderTimeView.text = item.timestamp.ms.toRelativeTime(v.context)
-            if (item.refundable) {
-                refundButton.visibility = VISIBLE
-                refundButton.setOnClickListener { 
listener.onRefundClicked(item) }
-            } else {
-                refundButton.visibility = GONE
-            }
-        }
-
-    }
-
-}
diff --git 
a/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt 
b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
index bb75362..4053d4b 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
@@ -18,10 +18,12 @@ package net.taler.merchantpos.order
 
 import net.taler.common.Amount
 import net.taler.common.ContractTerms
+import net.taler.common.Timestamp
 import net.taler.common.now
 import net.taler.merchantpos.config.Category
 import net.taler.merchantpos.config.ConfigProduct
 import java.net.URLEncoder
+import java.util.concurrent.TimeUnit.HOURS
 
 private const val FULFILLMENT_PREFIX = "taler://fulfillment-success/"
 
@@ -115,12 +117,15 @@ data class Order(val id: Int, val currency: String, val 
availableCategories: Map
         }
 
     fun toContractTerms(): ContractTerms {
+        val deadline = Timestamp(now() + HOURS.toMillis(1))
         return ContractTerms(
             summary = summary,
             summaryI18n = summaryI18n,
             amount = total,
             fulfillmentUrl = fulfillmentUri,
-            products = products.map { it.toContractProduct() }
+            products = products.map { it.toContractProduct() },
+            refundDeadline = deadline,
+            wireTransferDeadline = deadline
         )
     }
 
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 fc4f642..bc1e35f 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
@@ -25,13 +25,16 @@ import androidx.lifecycle.MutableLiveData
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import net.taler.common.Duration
 import net.taler.merchantlib.CheckPaymentResponse
 import net.taler.merchantlib.MerchantApi
+import net.taler.merchantlib.PostOrderRequest
 import net.taler.merchantlib.PostOrderResponse
 import net.taler.merchantpos.MainActivity.Companion.TAG
 import net.taler.merchantpos.R
 import net.taler.merchantpos.config.ConfigManager
 import net.taler.merchantpos.order.Order
+import java.util.concurrent.TimeUnit.HOURS
 import java.util.concurrent.TimeUnit.MINUTES
 import java.util.concurrent.TimeUnit.SECONDS
 
@@ -65,7 +68,11 @@ class PaymentManager(
         val merchantConfig = configManager.merchantConfig!!
         mPayment.value = Payment(order, order.summary, 
configManager.currency!!)
         scope.launch(Dispatchers.IO) {
-            val response = api.postOrder(merchantConfig, 
order.toContractTerms())
+            val request = PostOrderRequest(
+                contractTerms = order.toContractTerms(),
+                refundDelay = Duration(HOURS.toMillis(1))
+            )
+            val response = api.postOrder(merchantConfig, request)
             response.handle(::onNetworkError, ::onOrderCreated)
         }
     }
diff --git 
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt
 
b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt
index 5278a03..27ef366 100644
--- 
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt
+++ 
b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt
@@ -60,6 +60,11 @@ class ProcessPaymentFragment : Fragment() {
         }
     }
 
+    override fun onDestroy() {
+        super.onDestroy()
+        paymentManager.cancelPayment(getString(R.string.error_cancelled))
+    }
+
     private fun onPaymentStateChanged(payment: Payment) {
         if (payment.error != null) {
             topSnackbar(requireView(), payment.error, LENGTH_LONG)
diff --git a/merchant-terminal/src/main/res/values/strings.xml 
b/merchant-terminal/src/main/res/values/strings.xml
index 4c0ba5a..f7a62da 100644
--- a/merchant-terminal/src/main/res/values/strings.xml
+++ b/merchant-terminal/src/main/res/values/strings.xml
@@ -47,6 +47,7 @@
 
     <string name="history_label">Payment history</string>
     <string name="history_ref_no" 
translatable="false">@string/payment_order_id</string>
+    <string name="history_unpaid">Unpaid</string>
     <string name="history_refund">Refund</string>
     <string name="refund_amount">Amount</string>
     <string name="refund_reason">Refund reason</string>
diff --git 
a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt 
b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
index 013427f..ab442f2 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -35,7 +35,13 @@ data class ContractTerms(
     @SerialName("fulfillment_url")
     @get:JsonProperty("fulfillment_url")
     val fulfillmentUrl: String,
-    val products: List<ContractProduct>
+    val products: List<ContractProduct>,
+    @SerialName("wire_transfer_deadline")
+    @get:JsonProperty("wire_transfer_deadline")
+    val wireTransferDeadline: Timestamp? = null,
+    @SerialName("refund_deadline")
+    @get:JsonProperty("refund_deadline")
+    val refundDeadline: Timestamp? = null
 )
 
 @JsonInclude(NON_NULL)

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]