commit 62cce7fac9bc1a2dfad440ea126d6dacd5e0af1d
parent be0bcae390ac5f978db5999a367c9c6497d4f10b
Author: Joel-Haeberli <haebu@rubigen.ch>
Date: Wed, 1 May 2024 14:14:50 +0200
installation instructions
Diffstat:
8 files changed, 211 insertions(+), 54 deletions(-)
diff --git a/c2ec/install/build_app.sh b/c2ec/install/build_app.sh
@@ -1,14 +1,15 @@
#!/bin/bash
-if [ "$#" -ne 1 ]; then
- echo "Usage: $0 <source-root>"
+if [ "$#" -ne 2 ]; then
+ echo "Usage: $0 <source-root> <install-dir>"
exit 1
fi
REPO_ROOT=$1
+INSTALL_DIR=$2
build_c2ec() {
- go build $REPO_ROOT
+ go build -o $INSTALL_DIR $REPO_ROOT
if [ $? -ne 0 ]; then
echo "Failed to build C2EC using Go"
exit 1
@@ -16,6 +17,3 @@ build_c2ec() {
}
build_c2ec
-if [ $? -ne 0 ]; then
- exit 1
-fi
diff --git a/c2ec/install/installation_notes.md b/c2ec/install/installation_notes.md
@@ -7,6 +7,7 @@ how to install exchange and C2EC
- Debian
- git
- postgres >= 15.6
+- go ([installing Go](https://go.dev/doc/install))
- it's a good idea to read [Exchange Operator Manual](https://docs.taler.net/taler-exchange-manual.html) first
## Required Exchange binaries
@@ -22,37 +23,63 @@ To allow the withdrawal of Taler, I will need following binaries:
## Setup Commands
-`sudo echo "deb [signed-by=/etc/apt/keyrings/taler-systems.gpg] https://deb.taler.net/apt/debian bookworm main" > /etc/apt/sources.list.d/taler.list`
+```bash
-`sudo wget -O /etc/apt/keyrings/taler-systems.gpg https://taler.net/taler-systems.gpg`
+# Add taler repo and install
+sudo echo "deb [signed-by=/etc/apt/keyrings/taler-systems.gpg] https://deb.taler.net/apt/debian bookworm main" > /etc/apt/sources.list.d/taler.list`
+sudo wget -O /etc/apt/keyrings/taler-systems.gpg https://taler.net/taler-systems.gpg
+sudo apt update
+sudo apt install taler-exchange
-`sudo apt update`
+# install git
+sudo apt install git
-`sudo apt install taler-exchange`
+# download Go and install
+export GO_TAR=go1.22.2.linux-amd64.tar.gz
+sudo wget -O $GO_TAR https://go.dev/dl/$GO_TAR
+sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf $GO_TAR
+echo 'export PATH=$PATH:/usr/local/go/bin' >> $HOME/.profile
+source $HOME/.profile
+go version
+```
## Configure
+### Getting the source
-### Preparing the database
-
-`sudo passwd postgres`
-
-`su postgres`
-
-`psql`
-
-SQL `CREATE DATABASE c2ec;`
-
-SQL `CREATE USER c2ec_admin WITH ENCRYPTED PASSWORD [..]` -> keepass
-
-SQL `GRANT ALL PRIVILEGES ON DATABASE c2ec TO c2ec_admin;`
-
-``
-
-For CLI (managing terminals and providers):
-SQL `CREATE USER c2ec_operator WITH ENCRYPTED PASSWORD [..]` -> keepass
-
+`git clone https://git.taler.net/cashless2ecash.git`
+### Preparing the database
-For the API (handling withdrawals):
-SQL `CREATE USER c2ec_api WITH ENCRYPTED PASSWORD [..]` -> keepass
-\ No newline at end of file
+```bash
+sudo passwd postgres # change default password of postgres user
+su postgres
+psql
+```
+
+```sql
+CREATE DATABASE c2ec;
+CREATE USER c2ec_admin WITH ENCRYPTED PASSWORD [..]; -- keepass
+GRANT ALL PRIVILEGES ON DATABASE c2ec TO c2ec_admin;
+--For CLI (managing terminals and providers):
+CREATE USER c2ec_operator WITH ENCRYPTED PASSWORD [..]; -- keepass
+--For the API (handling withdrawals):
+CREATE USER c2ec_api WITH ENCRYPTED PASSWORD [..]; -- keepass
+```
+
+exit psql, change back to normal user (type 2x 'exit')
+
+### Setting up c2ec schmema
+find src directory of cashless2ecash, find c2ec/install/setup_db.sh
+
+```bash
+export PGHOST=localhost`
+export PGPORT=5432
+./setup_db.sh c2ec_admin [PASSWORD OBFUSCATED] c2ec ./..
+```
+
+### Building the app
+
+```bash
+./build_app.sh ./.. $HOME
+```
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClient.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClient.kt
@@ -0,0 +1,26 @@
+package ch.bfh.habej2.wallee_c2ec.client.taler
+
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.BankWitdrawalOperationStatus
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalApiConfig
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalConfirmationRequest
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetup
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetupResponse
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.WithdrawalOperationStatus
+import java.util.Optional
+
+interface TerminalClient {
+
+ fun terminalsConfig(): Optional<TerminalApiConfig>
+
+ fun setupWithdrawal(setupReq: TerminalWithdrawalSetup): Optional<TerminalWithdrawalSetupResponse>
+
+ fun retrieveWithdrawalStatus(
+ wopid: String,
+ longPollMs: Int,
+ oldState: WithdrawalOperationStatus = WithdrawalOperationStatus.PENDING
+ ): Optional<BankWitdrawalOperationStatus>
+
+ fun sendConfirmationRequest(encodedWopid: String, confirmationRequest: TerminalWithdrawalConfirmationRequest)
+
+ fun abortWithdrawal(encodedWopid: String)
+}
+\ No newline at end of file
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientImplementation.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientImplementation.kt
@@ -1,6 +1,7 @@
package ch.bfh.habej2.wallee_c2ec.client.taler
import ch.bfh.habej2.wallee_c2ec.client.taler.config.TalerTerminalConfig
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.BankWitdrawalOperationStatus
import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalApiConfig
import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalConfirmationRequest
import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetup
@@ -9,11 +10,9 @@ import ch.bfh.habej2.wallee_c2ec.client.taler.model.WithdrawalOperationStatus
import com.squareup.moshi.Moshi
import okhttp3.HttpUrl
import okhttp3.Interceptor
-import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
-import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.util.Optional
@@ -25,7 +24,7 @@ class TerminalClientException(
class TerminalClientImplementation (
private val config: TalerTerminalConfig
-) {
+): TerminalClient {
private val client: OkHttpClient =
OkHttpClient.Builder()
@@ -60,7 +59,7 @@ class TerminalClientImplementation (
.addPathSegment("abort")
.build()
- fun terminalsConfig(): Optional<TerminalApiConfig> {
+ override fun terminalsConfig(): Optional<TerminalApiConfig> {
val req = Request.Builder()
.get()
.url(terminalsConfigUrl())
@@ -69,7 +68,7 @@ class TerminalClientImplementation (
return parseOrEmpty(response)
}
- fun setupWithdrawal(setupReq: TerminalWithdrawalSetup): Optional<TerminalWithdrawalSetupResponse> {
+ override fun setupWithdrawal(setupReq: TerminalWithdrawalSetup): Optional<TerminalWithdrawalSetupResponse> {
val reqBody = serializer(TerminalWithdrawalSetup::class.java)
.toJson(setupReq)
@@ -82,11 +81,11 @@ class TerminalClientImplementation (
return parseOrEmpty(response)
}
- fun retrieveWithdrawalStatus(
+ override fun retrieveWithdrawalStatus(
wopid: String,
longPollMs: Int,
- oldState: WithdrawalOperationStatus = WithdrawalOperationStatus.PENDING
- ): Optional<TerminalWithdrawalSetup> {
+ oldState: WithdrawalOperationStatus
+ ): Optional<BankWitdrawalOperationStatus> {
val req = Request.Builder()
.get()
@@ -101,7 +100,10 @@ class TerminalClientImplementation (
return parseOrEmpty(response)
}
- fun sendConfirmationRequest(encodedWopid: String, confirmationRequest: TerminalWithdrawalConfirmationRequest) {
+ override fun sendConfirmationRequest(
+ encodedWopid: String,
+ confirmationRequest: TerminalWithdrawalConfirmationRequest
+ ) {
val reqBody = serializer(TerminalWithdrawalConfirmationRequest::class.java)
.toJson(confirmationRequest)
@@ -116,7 +118,7 @@ class TerminalClientImplementation (
}
}
- fun abortWithdrawal(encodedWopid: String) {
+ override fun abortWithdrawal(encodedWopid: String) {
val req = Request.Builder()
.delete()
.url(withdrawalsAbort(encodedWopid))
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientMock.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientMock.kt
@@ -0,0 +1,80 @@
+package ch.bfh.habej2.wallee_c2ec.client.taler
+
+import ch.bfh.habej2.wallee_c2ec.client.taler.encoding.CyptoUtils.encodeCrock
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.BankWitdrawalOperationStatus
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalApiConfig
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalConfirmationRequest
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetup
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetupResponse
+import ch.bfh.habej2.wallee_c2ec.client.taler.model.WithdrawalOperationStatus
+import ch.bfh.habej2.wallee_c2ec.withdrawal.Amount
+import java.security.SecureRandom
+import java.util.Optional
+
+class TerminalClientMock: TerminalClient {
+
+ override fun terminalsConfig() =
+ Optional.of(
+ TerminalApiConfig(
+ "taler-terminal",
+ "0:0:0",
+ "Wallee",
+ "CHF",
+ "wallee-transaction"
+ )
+ )
+
+ override fun setupWithdrawal(setupReq: TerminalWithdrawalSetup) =
+ Optional.of(
+ TerminalWithdrawalSetupResponse(
+ mockWopidOrReservePubKey()
+ )
+ )
+
+ override fun retrieveWithdrawalStatus(
+ wopid: String,
+ longPollMs: Int,
+ oldState: WithdrawalOperationStatus
+ ): Optional<BankWitdrawalOperationStatus> {
+
+ Thread.sleep(longPollMs.toLong())
+ return Optional.of(
+ BankWitdrawalOperationStatus(
+ WithdrawalOperationStatus.SELECTED,
+ Amount(10,0),
+ Amount(10,0),
+ Amount(0,0),
+ Amount(0,0),
+ "wallee-transaction",
+ "http://mock.com/api",
+ "http://mock.com/api",
+ "",
+ Array(0) {""},
+ mockWopidOrReservePubKey(),
+ "payto://IBAN/CH1111111111111",
+ aborted = false,
+ selectionDone = true,
+ transferDone = false
+ )
+ )
+ }
+
+ override fun sendConfirmationRequest(
+ encodedWopid: String,
+ confirmationRequest: TerminalWithdrawalConfirmationRequest
+ ) {
+ println("sending confirmation request")
+ }
+
+ override fun abortWithdrawal(encodedWopid: String) {
+ println("aborting withdrawal")
+ }
+
+ private fun mockWopidOrReservePubKey(): String {
+
+ val wopid = ByteArray(32)
+ SecureRandom().nextBytes(wopid)
+ return encodeCrock(wopid)
+
+ }
+}
+\ No newline at end of file
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AmountScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AmountScreen.kt
@@ -1,5 +1,6 @@
package ch.bfh.habej2.wallee_c2ec.withdrawal
+import android.annotation.SuppressLint
import android.app.Activity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardOptions
@@ -8,9 +9,11 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
+@SuppressLint("StateFlowValueCalledInComposition")
@Composable
fun AmountScreen(model: WithdrawalViewModel, navigateToWhenAmountEntered: () -> Unit) {
@@ -20,30 +23,28 @@ fun AmountScreen(model: WithdrawalViewModel, navigateToWhenAmountEntered: () ->
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text(text = "present card, trigger payment")
+ Text(model.uiState.value.amountError, color = Color.Red)
TextField(
- value = "",
+ value = model.uiState.value.amountInput,
onValueChange = {
- println(it)
- //model.updateAmount(it)
+ model.validateInputOrEmptyField(it)
},
label = { Text(text = "Enter amount") },
- placeholder = { Text(text = "amount") },
keyboardOptions = KeyboardOptions(
autoCorrect = false,
keyboardType = KeyboardType.Number
)
)
- Button(onClick = {
+ Button(onClick = {
val success = model.setupWithdrawal()
if (!success) {
activity.finish()
}
model.withdrawalOperationFailed()
navigateToWhenAmountEntered()
- }) {
+ }, enabled = (model.uiState.value.amount.value > 0 || model.uiState.value.amount.frac > 0)) {
Text(text = "pay")
}
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalActivity.kt
@@ -21,7 +21,6 @@ class WithdrawalActivity : ComponentActivity() {
setContent {
-
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "chooseExchangeScreen") {
diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt
@@ -8,7 +8,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import ch.bfh.habej2.wallee_c2ec.client.taler.TerminalClient
import ch.bfh.habej2.wallee_c2ec.client.taler.TerminalClientImplementation
+import ch.bfh.habej2.wallee_c2ec.client.taler.TerminalClientMock
import ch.bfh.habej2.wallee_c2ec.client.taler.config.TalerTerminalConfig
import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalConfirmationRequest
import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetup
@@ -26,7 +28,12 @@ data class Amount(
val value: Int,
val frac: Int
) {
- fun toBigDecimal(): BigDecimal = BigDecimal("$value.$frac")
+
+ // fun toBigDecimal(): BigDecimal = BigDecimal("$value.$frac")
+
+ override fun toString(): String {
+ return "$value.$frac"
+ }
}
@Stable
@@ -34,7 +41,9 @@ interface WithdrawalOperationState{
val requestUid: String
val exchangeBankIntegrationApiUrl: String
val encodedWopid: String
+ val amountInput: String
val amount: Amount
+ val amountError: String
val currency: String
val payed: Boolean
val transaction: TransactionCompletionResponse?
@@ -44,7 +53,9 @@ private class MutableWithdrawalOperationState: WithdrawalOperationState {
override val requestUid: String by derivedStateOf{UUID.randomUUID().toString()}
override var exchangeBankIntegrationApiUrl: String by mutableStateOf("")
override var encodedWopid: String by mutableStateOf("")
+ override var amountInput: String by mutableStateOf("")
override var amount: Amount by mutableStateOf(Amount(0,0))
+ override var amountError: String by mutableStateOf("")
override var currency: String by mutableStateOf("")
override var payed: Boolean by mutableStateOf(false)
override var transaction: TransactionCompletionResponse? by mutableStateOf(null)
@@ -54,13 +65,13 @@ class WithdrawalViewModel(
vararg closeables: Closeable
) : ViewModel(*closeables) {
- private var terminalClient: TerminalClientImplementation? = null
+ private var terminalClient: TerminalClient? = null
private val _uiState = MutableStateFlow(MutableWithdrawalOperationState())
val uiState: StateFlow<WithdrawalOperationState> = _uiState
fun exchangeUpdated(activity: Activity, cfg: TalerTerminalConfig) {
- terminalClient = TerminalClientImplementation(cfg)
+ terminalClient = TerminalClientMock() // TerminalClientImplementation(cfg)
_uiState.value = MutableWithdrawalOperationState() // reset withdrawal operation
val optionalApiCfg = terminalClient!!.terminalsConfig()
if (!optionalApiCfg.isPresent) {
@@ -86,8 +97,20 @@ class WithdrawalViewModel(
return true
}
+ fun validateInputOrEmptyField(amount: String) {
+
+ val validAmount = parseAmount(amount)
+ if (validAmount.isPresent) {
+ _uiState.value.amountError = ""
+ _uiState.value.amount = validAmount.get()
+ } else {
+ _uiState.value.amountInput = ""
+ _uiState.value.amountError = "invalid amount (format: X[.X])"
+ }
+ }
+
fun updateAmount(amount: String) {
- _uiState.value.amount = parseAmount(amount).orElse(null)
+ _uiState.value.amount = parseAmount(amount).orElse(Amount(0,0))
}
private fun updateCurrency(currency: String) {