summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-07-28 17:16:57 -0300
committerTorsten Grote <t@grobox.de>2020-07-28 17:16:57 -0300
commitbc35e8924e652c323001f62f6781657545fa378f (patch)
tree2a40b28b28c2abe0aed6ca36e2c2bcaa9019c577
parent8eb241ccce345a35b05a6335d11306465220f66d (diff)
downloadtaler-android-bc35e8924e652c323001f62f6781657545fa378f.tar.gz
taler-android-bc35e8924e652c323001f62f6781657545fa378f.tar.bz2
taler-android-bc35e8924e652c323001f62f6781657545fa378f.zip
[pos] adapt history to new v1 API
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt19
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt2
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt46
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt6
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Response.kt7
-rw-r--r--merchant-terminal/build.gradle3
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt8
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt62
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt34
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt3
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt11
-rw-r--r--taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt2
12 files changed, 113 insertions, 90 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 e995724b..db375863 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -16,7 +16,6 @@
package net.taler.merchantlib
-import android.util.Log
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.features.json.JsonFeature
@@ -25,8 +24,6 @@ import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.post
-import io.ktor.client.statement.HttpResponse
-import io.ktor.client.statement.readBytes
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpHeaders.Authorization
import io.ktor.http.contentType
@@ -64,14 +61,16 @@ class MerchantApi(private val httpClient: HttpClient) {
suspend fun deleteOrder(
merchantConfig: MerchantConfig,
orderId: String
- ): Response<HttpResponse> = response {
- val resp = httpClient.delete(merchantConfig.urlFor("private/orders/$orderId")) {
+ ): Response<Unit> = response {
+ httpClient.delete(merchantConfig.urlFor("private/orders/$orderId")) {
header(Authorization, "ApiKey ${merchantConfig.apiKey}")
- } as HttpResponse
- // TODO remove when the API call was fixed
- Log.e("TEST", "status: ${resp.status.value}")
- Log.e("TEST", String(resp.readBytes()))
- resp
+ } as Unit
+ }
+
+ suspend fun getOrderHistory(merchantConfig: MerchantConfig): Response<OrderHistory> = response {
+ httpClient.get(merchantConfig.urlFor("private/orders")) {
+ header(Authorization, "ApiKey ${merchantConfig.apiKey}")
+ } as OrderHistory
}
}
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
index a01624ed..a8d113e1 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
@@ -31,7 +31,7 @@ data class MerchantConfig(
fun urlFor(endpoint: String): String {
val sb = StringBuilder(baseUrl)
if (sb.last() != '/') sb.append('/')
- sb.append("instances/$instance/")
+ instance?.let { sb.append("instances/$it/") }
sb.append(endpoint)
return sb.toString()
}
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
new file mode 100644
index 00000000..718bde53
--- /dev/null
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.merchantlib
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import net.taler.common.Amount
+import net.taler.common.Timestamp
+
+@Serializable
+data class OrderHistory(
+ val orders: List<OrderHistoryEntry>
+)
+
+@Serializable
+data class OrderHistoryEntry(
+ // order ID of the transaction related to this entry.
+ @SerialName("order_id")
+ val orderId: String,
+
+ // when the order was created
+ val timestamp: Timestamp,
+
+ // the amount of money the order is for
+ val amount: Amount,
+
+ // the summary of the order
+ val summary: String,
+
+ // 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 a6e74d64..4854a80e 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
@@ -48,11 +48,11 @@ sealed class CheckPaymentResponse {
override fun deserialize(decoder: Decoder): CheckPaymentResponse {
val input = decoder as JsonInput
val tree = input.decodeJson() as JsonObject
- val paid = tree.getPrimitive("paid").boolean
-// return if (paid) decoder.json.fromJson(Paid.serializer(), tree)
+ val orderStatus = tree.getPrimitive("order_status").content
+// return if (orderStatus == "paid") decoder.json.fromJson(Paid.serializer(), tree)
// else decoder.json.fromJson(Unpaid.serializer(), tree)
// manual parsing due to https://github.com/Kotlin/kotlinx.serialization/issues/576
- return if (paid) Paid(
+ return if (orderStatus == "paid") Paid(
refunded = tree.getPrimitive("refunded").boolean
) else Unpaid(
talerPayUri = tree.getPrimitive("taler_pay_uri").content
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
index 1b49d786..9aefa5f8 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
@@ -16,8 +16,11 @@
package net.taler.merchantlib
+import android.util.Log
import io.ktor.client.call.receive
import io.ktor.client.features.ClientRequestException
+import io.ktor.client.features.ResponseException
+import io.ktor.client.features.ServerResponseException
import kotlinx.serialization.Serializable
class Response<out T> private constructor(
@@ -29,6 +32,7 @@ class Response<out T> private constructor(
return try {
success(request())
} catch (e: Throwable) {
+ Log.e("merchant-lib", "Error", e)
failure(e)
}
}
@@ -63,10 +67,11 @@ class Response<out T> private constructor(
private suspend fun getFailureString(failure: Failure): String = when (failure.exception) {
is ClientRequestException -> getExceptionString(failure.exception)
+ is ServerResponseException -> getExceptionString(failure.exception)
else -> failure.exception.toString()
}
- private suspend fun getExceptionString(e: ClientRequestException): String {
+ private suspend fun getExceptionString(e: ResponseException): String {
return try {
val error: Error = e.response.receive()
"Error ${error.code}: ${error.hint}"
diff --git a/merchant-terminal/build.gradle b/merchant-terminal/build.gradle
index 1cec0c59..44998929 100644
--- a/merchant-terminal/build.gradle
+++ b/merchant-terminal/build.gradle
@@ -73,9 +73,6 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
- // JSON parsing and serialization
- implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2"
-
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'org.robolectric:robolectric:4.3.1'
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
index b62c5505..905738b6 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
@@ -20,9 +20,6 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.android.volley.toolbox.Volley
-import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.getDefaultHttpClient
import net.taler.merchantpos.config.ConfigManager
@@ -35,9 +32,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
private val httpClient = getDefaultHttpClient()
private val api = MerchantApi(httpClient)
- private val mapper = ObjectMapper()
- .registerModule(KotlinModule())
- .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
private val queue = Volley.newRequestQueue(app)
val orderManager = OrderManager(app)
@@ -45,7 +39,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
addConfigurationReceiver(orderManager)
}
val paymentManager = PaymentManager(app, configManager, viewModelScope, api)
- val historyManager = HistoryManager(configManager, queue, mapper)
+ val historyManager = HistoryManager(configManager, viewModelScope, api)
val refundManager = RefundManager(configManager, queue)
override fun onCleared() {
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 24c7a0c8..cb770960 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
@@ -19,40 +19,22 @@ package net.taler.merchantpos.history
import androidx.annotation.UiThread
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.Listener
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonProperty
-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 kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import net.taler.merchantlib.MerchantApi
+import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.config.ConfigManager
-import net.taler.merchantpos.config.MerchantRequest
-import org.json.JSONObject
-
-data class HistoryItem(
- @JsonProperty("order_id")
- val orderId: String,
- val amount: Amount,
- val summary: String,
- val timestamp: Timestamp
-) {
- @get:JsonIgnore
- val time = timestamp.ms
-}
sealed class HistoryResult {
- object Error : HistoryResult()
- class Success(val items: List<HistoryItem>) : HistoryResult()
+ class Error(val msg: String) : HistoryResult()
+ class Success(val items: List<OrderHistoryEntry>) : HistoryResult()
}
class HistoryManager(
private val configManager: ConfigManager,
- private val queue: RequestQueue,
- private val mapper: ObjectMapper
+ private val scope: CoroutineScope,
+ private val api: MerchantApi
) {
private val mIsLoading = MutableLiveData(false)
@@ -65,29 +47,17 @@ class HistoryManager(
internal fun fetchHistory() {
mIsLoading.value = true
val merchantConfig = configManager.merchantConfig!!
- val params = mapOf("instance" to merchantConfig.instance!!)
- val req = MerchantRequest(GET, merchantConfig, "history", params, null,
- Listener { onHistoryResponse(it) },
- LogErrorListener { onHistoryError() })
- queue.add(req)
- }
-
- @UiThread
- private fun onHistoryResponse(body: JSONObject) {
- mIsLoading.value = false
- val items = arrayListOf<HistoryItem>()
- val historyJson = body.getJSONArray("history")
- for (i in 0 until historyJson.length()) {
- val historyItem: HistoryItem = mapper.readValue(historyJson.getString(i))
- items.add(historyItem)
+ scope.launch(Dispatchers.IO) {
+ api.getOrderHistory(merchantConfig).handle(::onHistoryError) {
+ mIsLoading.postValue(false)
+ mItems.postValue(HistoryResult.Success(it.orders))
+ }
}
- mItems.value = HistoryResult.Success(items)
}
- @UiThread
- private fun onHistoryError() {
+ private fun onHistoryError(msg: String) = scope.launch(Dispatchers.Main) {
mIsLoading.value = false
- mItems.value = HistoryResult.Error
+ mItems.value = 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 6da3dd24..25805dca 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,6 +20,8 @@ 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
@@ -31,21 +33,22 @@ 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_SHORT
+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.*
+import java.util.ArrayList
private interface RefundClickListener {
- fun onRefundClicked(item: HistoryItem)
+ fun onRefundClicked(item: OrderHistoryEntry)
}
/**
@@ -87,7 +90,7 @@ class MerchantHistoryFragment : Fragment(), RefundClickListener {
})
historyManager.items.observe(viewLifecycleOwner, Observer { result ->
when (result) {
- is HistoryResult.Error -> onError()
+ is HistoryResult.Error -> onError(result.msg)
is HistoryResult.Success -> historyListAdapter.setData(result.items)
}.exhaustive
})
@@ -95,18 +98,18 @@ class MerchantHistoryFragment : Fragment(), RefundClickListener {
override fun onStart() {
super.onStart()
- if (model.configManager.merchantConfig?.instance == null) {
+ if (model.configManager.merchantConfig?.baseUrl == null) {
navigate(actionGlobalMerchantSettings())
} else {
historyManager.fetchHistory()
}
}
- private fun onError() {
- Snackbar.make(requireView(), R.string.error_network, LENGTH_SHORT).show()
+ private fun onError(msg: String) {
+ Snackbar.make(requireView(), msg, LENGTH_LONG).show()
}
- override fun onRefundClicked(item: HistoryItem) {
+ override fun onRefundClicked(item: OrderHistoryEntry) {
refundManager.startRefund(item)
navigate(actionNavHistoryToRefundFragment())
}
@@ -116,7 +119,7 @@ class MerchantHistoryFragment : Fragment(), RefundClickListener {
private class HistoryItemAdapter(private val listener: RefundClickListener) :
Adapter<HistoryItemViewHolder>() {
- private val items = ArrayList<HistoryItem>()
+ private val items = ArrayList<OrderHistoryEntry>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryItemViewHolder {
val v =
@@ -130,7 +133,7 @@ private class HistoryItemAdapter(private val listener: RefundClickListener) :
holder.bind(items[position])
}
- fun setData(items: List<HistoryItem>) {
+ fun setData(items: List<OrderHistoryEntry>) {
this.items.clear()
this.items.addAll(items)
this.notifyDataSetChanged()
@@ -144,13 +147,18 @@ private class HistoryItemAdapter(private val listener: RefundClickListener) :
private val orderIdView: TextView = v.findViewById(R.id.orderIdView)
private val refundButton: ImageButton = v.findViewById(R.id.refundButton)
- fun bind(item: HistoryItem) {
+ 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.time.toRelativeTime(v.context)
- refundButton.setOnClickListener { listener.onRefundClicked(item) }
+ 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/history/RefundFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
index eb3d46da..17d78f61 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
@@ -33,6 +33,7 @@ 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
@@ -65,7 +66,7 @@ class RefundFragment : Fragment() {
})
}
- private fun onRefundButtonClicked(item: HistoryItem) {
+ private fun onRefundButtonClicked(item: OrderHistoryEntry) {
val inputAmount = try {
Amount.fromString(item.amount.currency, amountInputView.text.toString())
} catch (e: AmountParserException) {
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 da642d63..7f9b4c5a 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
@@ -25,6 +25,7 @@ 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
@@ -36,7 +37,7 @@ sealed class RefundResult {
object AlreadyRefunded : RefundResult()
class Success(
val refundUri: String,
- val item: HistoryItem,
+ val item: OrderHistoryEntry,
val amount: Amount,
val reason: String
) : RefundResult()
@@ -51,14 +52,14 @@ class RefundManager(
val TAG = RefundManager::class.java.simpleName
}
- var toBeRefunded: HistoryItem? = null
+ var toBeRefunded: OrderHistoryEntry? = null
private set
private val mRefundResult = MutableLiveData<RefundResult>()
internal val refundResult: LiveData<RefundResult> = mRefundResult
@UiThread
- internal fun startRefund(item: HistoryItem) {
+ internal fun startRefund(item: OrderHistoryEntry) {
toBeRefunded = item
mRefundResult.value = null
}
@@ -70,7 +71,7 @@ class RefundManager(
}
@UiThread
- internal fun refund(item: HistoryItem, amount: Amount, reason: String) {
+ internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) {
val merchantConfig = configManager.merchantConfig!!
val refundRequest = mapOf(
"order_id" to item.orderId,
@@ -89,7 +90,7 @@ class RefundManager(
@UiThread
private fun onRefundResponse(
json: JSONObject,
- item: HistoryItem,
+ item: OrderHistoryEntry,
amount: Amount,
reason: String
) {
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
index c07127a7..b891ef70 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
@@ -82,8 +82,10 @@ data class ContractMerchant(
val name: String
)
+@Serializable
@JsonInclude(NON_EMPTY)
class Timestamp(
+ @SerialName("t_ms")
@JsonProperty("t_ms")
val ms: Long
)