aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.gradle2
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt15
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt6
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt3
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt10
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt24
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt6
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt20
-rw-r--r--merchant-terminal/src/main/res/layout/fragment_process_payment.xml155
-rw-r--r--merchant-terminal/src/main/res/layout/fragment_refund_uri.xml137
-rw-r--r--merchant-terminal/src/main/res/layout/list_item_history.xml1
-rw-r--r--merchant-terminal/src/main/res/navigation/nav_graph.xml165
-rw-r--r--merchant-terminal/src/main/res/values/strings.xml10
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt7
16 files changed, 316 insertions, 253 deletions
diff --git a/build.gradle b/build.gradle
index 57a780b..59e7f30 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.1'
+ classpath 'com.android.tools.build:gradle:3.6.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt
index d6e3747..64f6ceb 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt
@@ -47,6 +47,10 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener {
private var reallyExit = false
+ companion object {
+ val TAG = "taler-pos"
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
index 578debf..9deb042 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
@@ -16,11 +16,15 @@
package net.taler.merchantpos
+import android.util.Log
import android.view.View
import androidx.annotation.StringRes
+import com.android.volley.Response
+import com.android.volley.VolleyError
import com.google.android.material.snackbar.BaseTransientBottomBar.ANIMATION_MODE_FADE
import com.google.android.material.snackbar.BaseTransientBottomBar.Duration
import com.google.android.material.snackbar.Snackbar.make
+import net.taler.merchantpos.MainActivity.Companion.TAG
fun topSnackbar(view: View, text: CharSequence, @Duration duration: Int) {
make(view, text, duration)
@@ -32,3 +36,14 @@ fun topSnackbar(view: View, text: CharSequence, @Duration duration: Int) {
fun topSnackbar(view: View, @StringRes resId: Int, @Duration duration: Int) {
topSnackbar(view, view.resources.getText(resId), duration)
}
+
+class LogErrorListener(private val onError: (error: VolleyError) -> Any) :
+ Response.ErrorListener {
+
+ override fun onErrorResponse(error: VolleyError) {
+ val body = error.networkResponse.data?.let { String(it) }
+ Log.e(TAG, "$error $body")
+ onError.invoke(error)
+ }
+
+}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
index edb8059..171cf28 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
@@ -26,7 +26,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.volley.Request.Method.GET
import com.android.volley.RequestQueue
-import com.android.volley.Response.ErrorListener
import com.android.volley.Response.Listener
import com.android.volley.VolleyError
import com.android.volley.toolbox.JsonObjectRequest
@@ -35,6 +34,7 @@ import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import net.taler.merchantpos.LogErrorListener
import net.taler.merchantpos.R
import org.json.JSONObject
@@ -91,7 +91,7 @@ class ConfigManager(
val stringRequest = object : JsonObjectRequest(GET, config.configUrl, null,
Listener { onConfigReceived(it, configToSave) },
- ErrorListener { onNetworkError(it) }
+ LogErrorListener { onNetworkError(it) }
) {
// send basic auth header
override fun getHeaders(): MutableMap<String, String> {
@@ -117,7 +117,7 @@ class ConfigManager(
val params = mapOf("instance" to merchantConfig.instance)
val req = MerchantRequest(GET, merchantConfig, "config", params, null,
Listener { onMerchantConfigReceived(config, json, merchantConfig, it) },
- ErrorListener { onNetworkError(it) }
+ LogErrorListener { onNetworkError(it) }
)
queue.add(req)
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
index 8d95378..862dd33 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
@@ -20,6 +20,7 @@ package net.taler.merchantpos.config
import android.util.ArrayMap
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
+import net.taler.merchantpos.LogErrorListener
import org.json.JSONObject
class MerchantRequest(
@@ -29,7 +30,7 @@ class MerchantRequest(
params: Map<String, String>?,
jsonRequest: JSONObject?,
listener: Response.Listener<JSONObject>,
- errorListener: Response.ErrorListener
+ errorListener: LogErrorListener
) :
JsonObjectRequest(method, merchantConfig.urlFor(endpoint, params), jsonRequest, listener, errorListener) {
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 3aaf3a4..6b95e16 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
@@ -21,7 +21,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.volley.Request.Method.GET
import com.android.volley.RequestQueue
-import com.android.volley.Response.ErrorListener
import com.android.volley.Response.Listener
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
@@ -29,6 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import net.taler.common.Amount
import net.taler.common.Timestamp
+import net.taler.merchantpos.LogErrorListener
import net.taler.merchantpos.config.ConfigManager
import net.taler.merchantpos.config.MerchantRequest
import org.json.JSONObject
@@ -36,15 +36,11 @@ import org.json.JSONObject
data class HistoryItem(
@JsonProperty("order_id")
val orderId: String,
- @JsonProperty("amount")
- val amountStr: String,
+ val amount: Amount,
val summary: String,
val timestamp: Timestamp
) {
@get:JsonIgnore
- val amount: Amount by lazy { Amount.fromJSONString(amountStr) }
-
- @get:JsonIgnore
val time = timestamp.ms
}
@@ -72,7 +68,7 @@ class HistoryManager(
val params = mapOf("instance" to merchantConfig.instance)
val req = MerchantRequest(GET, merchantConfig, "history", params, null,
Listener { onHistoryResponse(it) },
- ErrorListener { onHistoryError() })
+ LogErrorListener { onHistoryError() })
queue.add(req)
}
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
index 7652ca4..2b85add 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
@@ -36,6 +36,7 @@ import net.taler.common.navigate
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
@@ -72,7 +73,7 @@ class RefundFragment : Fragment() {
return
}
if (inputAmount > item.amount) {
- amountView.error = getString(R.string.refund_error_max_amount, item.amountStr)
+ amountView.error = getString(R.string.refund_error_max_amount, item.amount.amountStr)
return
}
if (inputAmount.isZero()) {
@@ -88,6 +89,7 @@ class RefundFragment : Fragment() {
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()
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
index 910116e..da642d6 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
@@ -22,9 +22,10 @@ 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.ErrorListener
import com.android.volley.Response.Listener
+import com.android.volley.VolleyError
import net.taler.common.Amount
+import net.taler.merchantpos.LogErrorListener
import net.taler.merchantpos.config.ConfigManager
import net.taler.merchantpos.config.MerchantRequest
import org.json.JSONObject
@@ -32,6 +33,7 @@ import org.json.JSONObject
sealed class RefundResult {
object Error : RefundResult()
object PastDeadline : RefundResult()
+ object AlreadyRefunded : RefundResult()
class Success(
val refundUri: String,
val item: HistoryItem,
@@ -62,6 +64,12 @@ class RefundManager(
}
@UiThread
+ internal fun abortRefund() {
+ toBeRefunded = null
+ mRefundResult.value = null
+ }
+
+ @UiThread
internal fun refund(item: HistoryItem, amount: Amount, reason: String) {
val merchantConfig = configManager.merchantConfig!!
val refundRequest = mapOf(
@@ -73,7 +81,7 @@ class RefundManager(
Log.d(TAG, body.toString(4))
val req = MerchantRequest(POST, merchantConfig, "refund", null, body,
Listener { onRefundResponse(it, item, amount, reason) },
- ErrorListener { onRefundError() }
+ LogErrorListener { onRefundError(it) }
)
queue.add(req)
}
@@ -86,7 +94,7 @@ class RefundManager(
reason: String
) {
if (!json.has("contract_terms")) {
- Log.e("TEST", "json: $json")
+ Log.e(TAG, "Contract terms missing: $json")
onRefundError()
return
}
@@ -110,7 +118,15 @@ class RefundManager(
}
@UiThread
- private fun onRefundError() {
+ 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
index 1bc4002..1ea0959 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt
@@ -58,6 +58,12 @@ class RefundUriFragment : Fragment() {
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()
}
}
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 054d7cd..9138740 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
@@ -24,10 +24,11 @@ import androidx.lifecycle.MutableLiveData
import com.android.volley.Request.Method.GET
import com.android.volley.Request.Method.POST
import com.android.volley.RequestQueue
-import com.android.volley.Response.ErrorListener
import com.android.volley.Response.Listener
-import com.android.volley.VolleyError
import com.fasterxml.jackson.databind.ObjectMapper
+import net.taler.common.Timestamp
+import net.taler.common.now
+import net.taler.merchantpos.LogErrorListener
import net.taler.merchantpos.config.ConfigManager
import net.taler.merchantpos.config.MerchantRequest
import net.taler.merchantpos.order.Order
@@ -73,11 +74,12 @@ class PaymentManager(
val currency = merchantConfig.currency!!
val summary = order.summary
val summaryI18n = order.summaryI18n
-// val refundDeadline = Timestamp(System.currentTimeMillis() + HOURS.toMillis(2))
+ val now = now()
+ val deadline = Timestamp(now + MINUTES.toMillis(120))
mPayment.value = Payment(order, summary, currency)
- val fulfillmentId = "${System.currentTimeMillis()}-${order.hashCode()}"
+ val fulfillmentId = "${now}-${order.hashCode()}"
val fulfillmentUrl =
"${FULFILLMENT_PREFIX}${URLEncoder.encode(summary, "UTF-8")}#$fulfillmentId"
val body = JSONObject().apply {
@@ -88,7 +90,8 @@ class PaymentManager(
// fulfillment_url needs to be unique per order
put("fulfillment_url", fulfillmentUrl)
put("instance", "default")
-// put("refund_deadline", JSONObject(mapper.writeValueAsString(refundDeadline)))
+ put("wire_transfer_deadline", JSONObject(mapper.writeValueAsString(deadline)))
+ put("refund_deadline", JSONObject(mapper.writeValueAsString(deadline)))
put("products", order.getProductsJson())
})
}
@@ -97,7 +100,7 @@ class PaymentManager(
val req = MerchantRequest(POST, merchantConfig, "order", null, body,
Listener { onOrderCreated(it) },
- ErrorListener { onNetworkError(it) }
+ LogErrorListener { onNetworkError() }
)
queue.add(req)
}
@@ -123,7 +126,7 @@ class PaymentManager(
val req = MerchantRequest(GET, merchantConfig, "check-payment", params, null,
Listener { onPaymentChecked(it) },
- ErrorListener { onNetworkError(it) })
+ LogErrorListener { onNetworkError() })
queue.add(req)
}
@@ -141,8 +144,7 @@ class PaymentManager(
}
}
- private fun onNetworkError(volleyError: VolleyError) {
- Log.e(PaymentManager::class.java.simpleName, volleyError.toString())
+ private fun onNetworkError() {
cancelPayment()
}
diff --git a/merchant-terminal/src/main/res/layout/fragment_process_payment.xml b/merchant-terminal/src/main/res/layout/fragment_process_payment.xml
index 6cd8ea1..cb69aa2 100644
--- a/merchant-terminal/src/main/res/layout/fragment_process_payment.xml
+++ b/merchant-terminal/src/main/res/layout/fragment_process_payment.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of GNU Taler
~ (C) 2020 Taler Systems S.A.
~
@@ -16,95 +15,95 @@
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".payment.ProcessPaymentFragment">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".payment.ProcessPaymentFragment">
<ImageView
- android:id="@+id/qrcodeView"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_margin="32dp"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/guideline"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:ignore="ContentDescription"
- tools:src="@tools:sample/avatars"
- tools:visibility="visible" />
+ android:id="@+id/qrcodeView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_margin="32dp"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/guideline"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:ignore="ContentDescription"
+ tools:src="@tools:sample/avatars"
+ tools:visibility="visible" />
<ProgressBar
- android:id="@+id/progressBar"
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintBottom_toBottomOf="@+id/qrcodeView"
- app:layout_constraintEnd_toEndOf="@+id/qrcodeView"
- app:layout_constraintStart_toStartOf="@+id/qrcodeView"
- app:layout_constraintTop_toTopOf="@+id/qrcodeView" />
+ android:id="@+id/progressBar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="@+id/qrcodeView"
+ app:layout_constraintEnd_toEndOf="@+id/qrcodeView"
+ app:layout_constraintStart_toStartOf="@+id/qrcodeView"
+ app:layout_constraintTop_toTopOf="@+id/qrcodeView" />
<androidx.constraintlayout.widget.Guideline
- android:id="@+id/guideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_percent="0.54" />
+ android:id="@+id/guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.54" />
<TextView
- android:id="@+id/payIntroView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/payment_intro_nfc"
- android:textAlignment="center"
- android:textSize="24sp"
- android:visibility="invisible"
- app:layout_constraintBottom_toTopOf="@+id/amountView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_chainStyle="spread"
- tools:visibility="visible" />
+ android:id="@+id/payIntroView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/payment_intro_nfc"
+ android:textAlignment="center"
+ android:textSize="22sp"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toTopOf="@+id/amountView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="spread"
+ tools:visibility="visible" />
<TextView
- android:id="@+id/amountView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:textAppearance="@style/TextAppearance.AppCompat.Headline"
- app:layout_constraintBottom_toTopOf="@+id/orderRefView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toBottomOf="@+id/payIntroView"
- tools:text="10.49 TESTKUDOS" />
+ android:id="@+id/amountView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:textAppearance="@style/TextAppearance.AppCompat.Headline"
+ app:layout_constraintBottom_toTopOf="@+id/orderRefView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toBottomOf="@+id/payIntroView"
+ tools:text="10.49 TESTKUDOS" />
<TextView
- android:id="@+id/orderRefView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:textAlignment="center"
- android:visibility="invisible"
- app:layout_constraintBottom_toTopOf="@id/cancelPaymentButton"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toBottomOf="@+id/amountView"
- tools:text="@string/payment_order_ref"
- tools:visibility="visible" />
+ android:id="@+id/orderRefView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:textAlignment="center"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toTopOf="@id/cancelPaymentButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toBottomOf="@+id/amountView"
+ tools:text="@string/payment_order_ref"
+ tools:visibility="visible" />
<Button
- android:id="@+id/cancelPaymentButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:backgroundTint="@color/red"
- android:text="@string/payment_cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toStartOf="@+id/guideline" />
+ android:id="@+id/cancelPaymentButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:backgroundTint="@color/red"
+ android:text="@string/payment_cancel"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/merchant-terminal/src/main/res/layout/fragment_refund_uri.xml b/merchant-terminal/src/main/res/layout/fragment_refund_uri.xml
index 8447d28..c82a324 100644
--- a/merchant-terminal/src/main/res/layout/fragment_refund_uri.xml
+++ b/merchant-terminal/src/main/res/layout/fragment_refund_uri.xml
@@ -15,79 +15,92 @@
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".payment.ProcessPaymentFragment">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".payment.ProcessPaymentFragment">
<ImageView
- android:id="@+id/refundQrcodeView"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_margin="32dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/guideline"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:ignore="ContentDescription"
- tools:src="@tools:sample/avatars" />
+ android:id="@+id/refundQrcodeView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_margin="32dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/guideline"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:ignore="ContentDescription"
+ tools:src="@tools:sample/avatars" />
<androidx.constraintlayout.widget.Guideline
- android:id="@+id/guideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_percent="0.54" />
+ android:id="@+id/guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.54" />
<TextView
- android:id="@+id/refundIntroView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/refund_intro_nfc"
- android:textAlignment="center"
- android:textSize="24sp"
- app:layout_constraintBottom_toTopOf="@+id/refundAmountView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_chainStyle="spread" />
+ android:id="@+id/refundIntroView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/refund_intro_nfc"
+ android:textAlignment="center"
+ android:textSize="22sp"
+ app:layout_constraintBottom_toTopOf="@+id/refundAmountView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="spread" />
<TextView
- android:id="@+id/refundAmountView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:textAppearance="@style/TextAppearance.AppCompat.Headline"
- app:layout_constraintBottom_toTopOf="@+id/refundRefView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toBottomOf="@+id/refundIntroView"
- tools:text="10.49 TESTKUDOS" />
+ android:id="@+id/refundAmountView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:textAppearance="@style/TextAppearance.AppCompat.Headline"
+ app:layout_constraintBottom_toTopOf="@+id/refundRefView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toBottomOf="@+id/refundIntroView"
+ tools:text="10.49 TESTKUDOS" />
<TextView
- android:id="@+id/refundRefView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:textAlignment="center"
- app:layout_constraintBottom_toTopOf="@id/cancelRefundButton"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@+id/guideline"
- app:layout_constraintTop_toBottomOf="@+id/refundAmountView"
- tools:text="@string/refund_order_ref" />
+ android:id="@+id/refundRefView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:textAlignment="center"
+ app:layout_constraintBottom_toTopOf="@id/cancelRefundButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
+ app:layout_constraintTop_toBottomOf="@+id/refundAmountView"
+ tools:text="@string/refund_order_ref" />
<Button
- android:id="@+id/cancelRefundButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:backgroundTint="@color/red"
- android:text="@string/refund_abort"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toStartOf="@+id/guideline" />
+ android:id="@+id/cancelRefundButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:backgroundTint="@color/red"
+ android:text="@string/refund_abort"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/completeButton"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintStart_toEndOf="@+id/refundQrcodeView"
+ app:layout_constraintStart_toStartOf="@+id/guideline" />
+
+ <Button
+ android:id="@+id/completeButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:backgroundTint="@color/green"
+ android:text="@string/refund_complete"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/cancelRefundButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/merchant-terminal/src/main/res/layout/list_item_history.xml b/merchant-terminal/src/main/res/layout/list_item_history.xml
index fe485ba..57f85ef 100644
--- a/merchant-terminal/src/main/res/layout/list_item_history.xml
+++ b/merchant-terminal/src/main/res/layout/list_item_history.xml
@@ -88,7 +88,6 @@
android:backgroundTint="?colorPrimary"
android:contentDescription="@string/history_refund"
android:tint="?attr/colorOnPrimary"
- android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
diff --git a/merchant-terminal/src/main/res/navigation/nav_graph.xml b/merchant-terminal/src/main/res/navigation/nav_graph.xml
index 2e337f2..606f2de 100644
--- a/merchant-terminal/src/main/res/navigation/nav_graph.xml
+++ b/merchant-terminal/src/main/res/navigation/nav_graph.xml
@@ -15,123 +15,124 @@
-->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/nav_graph"
- app:startDestination="@+id/nav_order"
- tools:ignore="UnusedNavigation">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/nav_graph"
+ app:startDestination="@+id/nav_order"
+ tools:ignore="UnusedNavigation">
<fragment
- android:id="@+id/nav_order"
- android:name="net.taler.merchantpos.order.OrderFragment"
- android:label=""
- tools:layout="@layout/fragment_order">
+ android:id="@+id/nav_order"
+ android:name="net.taler.merchantpos.order.OrderFragment"
+ android:label=""
+ tools:layout="@layout/fragment_order">
<action
- android:id="@+id/action_order_to_merchantSettings"
- app:destination="@+id/nav_settings"
- app:launchSingleTop="true"
- app:popUpTo="@+id/nav_graph"
- app:popUpToInclusive="true" />
+ android:id="@+id/action_order_to_merchantSettings"
+ app:destination="@+id/nav_settings"
+ app:launchSingleTop="true"
+ app:popUpTo="@+id/nav_graph"
+ app:popUpToInclusive="true" />
<action
- android:id="@+id/action_order_self"
- app:destination="@+id/nav_order"
- app:popUpTo="@+id/nav_graph" />
+ android:id="@+id/action_order_self"
+ app:destination="@+id/nav_order"
+ app:popUpTo="@+id/nav_graph" />
<action
- android:id="@+id/action_order_to_processPayment"
- app:destination="@+id/processPayment" />
+ android:id="@+id/action_order_to_processPayment"
+ app:destination="@+id/processPayment" />
</fragment>
<fragment
- android:id="@+id/processPayment"
- android:name="net.taler.merchantpos.payment.ProcessPaymentFragment"
- android:label="@string/payment_process_label"
- tools:layout="@layout/fragment_process_payment">
+ android:id="@+id/processPayment"
+ android:name="net.taler.merchantpos.payment.ProcessPaymentFragment"
+ android:label="@string/payment_process_label"
+ tools:layout="@layout/fragment_process_payment">
<action
- android:id="@+id/action_processPayment_to_paymentSuccess"
- app:destination="@+id/paymentSuccess"
- app:popUpTo="@id/nav_order" />
+ android:id="@+id/action_processPayment_to_paymentSuccess"
+ app:destination="@+id/paymentSuccess"
+ app:popUpTo="@id/nav_order" />
</fragment>
<fragment
- android:id="@+id/nav_history"
- android:name="net.taler.merchantpos.history.MerchantHistoryFragment"
- android:label="@string/history_label"
- tools:layout="@layout/fragment_merchant_history">
+ android:id="@+id/nav_history"
+ android:name="net.taler.merchantpos.history.MerchantHistoryFragment"
+ android:label="@string/history_label"
+ tools:layout="@layout/fragment_merchant_history">
<action
- android:id="@+id/action_nav_history_to_refundFragment"
- app:destination="@id/refundFragment" />
+ android:id="@+id/action_nav_history_to_refundFragment"
+ app:destination="@id/refundFragment" />
</fragment>
<fragment
- android:id="@+id/refundFragment"
- android:name="net.taler.merchantpos.history.RefundFragment"
- android:label="@string/history_refund"
- tools:layout="@layout/fragment_refund">
+ android:id="@+id/refundFragment"
+ android:name="net.taler.merchantpos.history.RefundFragment"
+ android:label="@string/history_refund"
+ tools:layout="@layout/fragment_refund">
<action
- android:id="@+id/action_refundFragment_to_refundUriFragment"
- app:destination="@id/refundUriFragment" />
+ android:id="@+id/action_refundFragment_to_refundUriFragment"
+ app:destination="@id/refundUriFragment"
+ app:popUpTo="@id/nav_history" />
</fragment>
<fragment
- android:id="@+id/refundUriFragment"
- android:name="net.taler.merchantpos.history.RefundUriFragment"
- android:label="@string/history_refund"
- tools:layout="@layout/fragment_refund_uri" />
+ android:id="@+id/refundUriFragment"
+ android:name="net.taler.merchantpos.history.RefundUriFragment"
+ android:label="@string/history_refund"
+ tools:layout="@layout/fragment_refund_uri" />
<fragment
- android:id="@+id/nav_settings"
- android:name="net.taler.merchantpos.config.MerchantConfigFragment"
- android:label="@string/config_label"
- tools:layout="@layout/fragment_merchant_config">
+ android:id="@+id/nav_settings"
+ android:name="net.taler.merchantpos.config.MerchantConfigFragment"
+ android:label="@string/config_label"
+ tools:layout="@layout/fragment_merchant_config">
<action
- android:id="@+id/action_settings_to_order"
- app:destination="@+id/nav_order"
- app:launchSingleTop="true"
- app:popUpTo="@+id/nav_graph"
- app:popUpToInclusive="true" />
+ android:id="@+id/action_settings_to_order"
+ app:destination="@+id/nav_order"
+ app:launchSingleTop="true"
+ app:popUpTo="@+id/nav_graph"
+ app:popUpToInclusive="true" />
</fragment>
<fragment
- android:id="@+id/configFetcher"
- android:name="net.taler.merchantpos.config.ConfigFetcherFragment"
- android:label="@string/config_fetching_label"
- tools:layout="@layout/fragment_config_fetcher">
+ android:id="@+id/configFetcher"
+ android:name="net.taler.merchantpos.config.ConfigFetcherFragment"
+ android:label="@string/config_fetching_label"
+ tools:layout="@layout/fragment_config_fetcher">
<action
- android:id="@+id/action_configFetcher_to_merchantSettings"
- app:destination="@+id/nav_settings"
- app:launchSingleTop="true"
- app:popUpTo="@+id/nav_graph"
- app:popUpToInclusive="true" />
+ android:id="@+id/action_configFetcher_to_merchantSettings"
+ app:destination="@+id/nav_settings"
+ app:launchSingleTop="true"
+ app:popUpTo="@+id/nav_graph"
+ app:popUpToInclusive="true" />
<action
- android:id="@+id/action_configFetcher_to_order"
- app:destination="@+id/nav_order"
- app:launchSingleTop="true"
- app:popUpTo="@+id/nav_graph"
- app:popUpToInclusive="true" />
+ android:id="@+id/action_configFetcher_to_order"
+ app:destination="@+id/nav_order"
+ app:launchSingleTop="true"
+ app:popUpTo="@+id/nav_graph"
+ app:popUpToInclusive="true" />
</fragment>
<fragment
- android:id="@+id/paymentSuccess"
- android:name="net.taler.merchantpos.payment.PaymentSuccessFragment"
- android:label="@string/payment_received"
- tools:layout="@layout/fragment_payment_success" />
+ android:id="@+id/paymentSuccess"
+ android:name="net.taler.merchantpos.payment.PaymentSuccessFragment"
+ android:label="@string/payment_received"
+ tools:layout="@layout/fragment_payment_success" />
<action
- android:id="@+id/action_global_order"
- app:destination="@+id/nav_order"
- app:launchSingleTop="true"
- app:popUpTo="@+id/nav_graph" />
+ android:id="@+id/action_global_order"
+ app:destination="@+id/nav_order"
+ app:launchSingleTop="true"
+ app:popUpTo="@+id/nav_graph" />
<action
- android:id="@+id/action_global_merchantHistory"
- app:destination="@+id/nav_history"
- app:launchSingleTop="true" />
+ android:id="@+id/action_global_merchantHistory"
+ app:destination="@+id/nav_history"
+ app:launchSingleTop="true" />
<action
- android:id="@+id/action_global_merchantSettings"
- app:destination="@+id/nav_settings"
- app:launchSingleTop="true" />
+ android:id="@+id/action_global_merchantSettings"
+ app:destination="@+id/nav_settings"
+ app:launchSingleTop="true" />
<action
- android:id="@+id/action_global_configFetcher"
- app:destination="@+id/configFetcher"
- app:launchSingleTop="true" />
+ android:id="@+id/action_global_configFetcher"
+ app:destination="@+id/configFetcher"
+ app:launchSingleTop="true" />
</navigation>
diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml
index 756ef38..712a2fc 100644
--- a/merchant-terminal/src/main/res/values/strings.xml
+++ b/merchant-terminal/src/main/res/values/strings.xml
@@ -36,8 +36,8 @@
<string name="config_fetching_label">Fetching Configuration</string>
<string name="config_docs">Please refer to <a href="https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats">the documentation</a> for the configuration format.</string>
- <string name="payment_intro_nfc">Please scan QR Code or use NFC to pay</string>
- <string name="payment_intro">Please scan QR Code to pay</string>
+ <string name="payment_intro_nfc">Please let customer scan QR Code or use NFC to pay</string>
+ <string name="payment_intro">Please let customer scan QR Code to pay</string>
<string name="payment_cancel">Cancel Payment</string>
<string name="payment_received">Payment received</string>
<string name="payment_back_button">Continue</string>
@@ -51,14 +51,16 @@
<string name="refund_amount">Amount</string>
<string name="refund_reason">Refund reason</string>
<string name="refund_abort">Abort</string>
+ <string name="refund_complete">Received</string>
<string name="refund_confirm">Give Refund</string>
<string name="refund_error_max_amount">Greater than order amount of %s</string>
<string name="refund_error_invalid_amount">Invalid amount</string>
<string name="refund_error_zero">Needs to be positive amount</string>
<string name="refund_error_backend">Error processing refund</string>
<string name="refund_error_deadline">Refund deadline has passed</string>
- <string name="refund_intro_nfc">Please scan QR Code or use NFC to give refund</string>
- <string name="refund_intro">Please scan QR Code to give refund</string>
+ <string name="refund_error_already_refunded">Already refunded</string>
+ <string name="refund_intro_nfc">Please let customer scan QR Code or use NFC to give refund</string>
+ <string name="refund_intro">Please let customer scan QR Code to give refund</string>
<string name="refund_order_ref">Order Reference: %1$s\n\n%2$s</string>
<string name="error_network">Network Error</string>
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt b/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
index cb1622e..444caa4 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
+++ b/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
@@ -49,3 +49,10 @@ object TalerUtils {
}
}
+
+/**
+ * Returns the current time in milliseconds epoch rounded to nearest seconds.
+ */
+fun now(): Long {
+ return ((System.currentTimeMillis() + 500) / 1000) * 1000
+}