cashless2ecash

cashless2ecash: pay with cards for digital cash (experimental)
Log | Files | Refs | README

commit c65b64811bebbf8592e9f04efb14eec95a81b413
parent 7deac97ad58329565fc5fe7c9f3f600620f1d6c4
Author: Joel-Haeberli <haebu@rubigen.ch>
Date:   Sun, 12 May 2024 12:50:30 +0200

app: add summary activity

Diffstat:
Dwallee-c2ec/app/src/androidTest/java/ch/bfh/habej2/wallee_c2ec/ExampleInstrumentedTest.kt | 25-------------------------
Mwallee-c2ec/app/src/main/AndroidManifest.xml | 8++++++++
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt | 2--
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientImplementation.kt | 12+++++++-----
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt | 3++-
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt | 37++-----------------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt | 7+++++--
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt | 116++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TestTransactionScreen.kt | 9+--------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalActivity.kt | 12+++++++-----
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt | 80++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
11 files changed, 169 insertions(+), 142 deletions(-)

diff --git a/wallee-c2ec/app/src/androidTest/java/ch/bfh/habej2/wallee_c2ec/ExampleInstrumentedTest.kt b/wallee-c2ec/app/src/androidTest/java/ch/bfh/habej2/wallee_c2ec/ExampleInstrumentedTest.kt @@ -1,24 +0,0 @@ -package ch.bfh.habej2.wallee_c2ec - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("ch.bfh.habej2.wallee_c2ec", appContext.packageName) - } -} -\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/AndroidManifest.xml b/wallee-c2ec/app/src/main/AndroidManifest.xml @@ -40,6 +40,14 @@ android:label="@string/app_name" android:theme="@style/Theme.Walleec2ec"> </activity> + + + <activity + android:name=".withdrawal.SummaryActivity" + android:exported="true" + android:label="@string/app_name" + android:theme="@style/Theme.Walleec2ec"> + </activity> </application> </manifest> \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt @@ -4,7 +4,6 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button @@ -16,7 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import ch.bfh.habej2.wallee_c2ec.ui.theme.Walleec2ecTheme import ch.bfh.habej2.wallee_c2ec.withdrawal.ManageActivity -import ch.bfh.habej2.wallee_c2ec.withdrawal.TestTransactionScreen import ch.bfh.habej2.wallee_c2ec.withdrawal.WithdrawalActivity class MainActivity : ComponentActivity() { 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 @@ -32,6 +32,13 @@ class TerminalClientImplementation ( .addInterceptor(TerminalsApiBasicAuthInterceptor(config)) .build() + companion object { + fun <T> serializer(clazz: Class<T>) = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + .adapter(clazz) + } + private fun baseUrlBuilder() = config.terminalApiBaseUrl.toHttpUrl() private fun terminalsConfigUrl() = baseUrlBuilder().newBuilder() @@ -47,11 +54,6 @@ class TerminalClientImplementation ( .addPathSegment(encodedWopid) .build() - private fun <T> serializer(clazz: Class<T>) = Moshi.Builder() - .add(KotlinJsonAdapterFactory()) - .build() - .adapter(clazz) - private fun withdrawalsConfirm(encodedWopid: String) = withdrawalsByWopid(encodedWopid) .newBuilder() .addPathSegment("check") diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt @@ -9,9 +9,10 @@ import com.wallee.android.till.sdk.data.TransactionResponse class WalleeResponseHandler( private val activity: Activity, - private val model: WithdrawalViewModel ) : ResponseHandler() { + lateinit var model: WithdrawalViewModel + override fun authorizeTransactionReply(response: TransactionResponse?) { println("C2EC-TRANSACTION-RESPONSE: $response") diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt @@ -14,17 +14,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp -import ch.bfh.habej2.wallee_c2ec.client.wallee.WalleeResponseHandler import com.wallee.android.till.sdk.ApiClient import com.wallee.android.till.sdk.data.LineItem import com.wallee.android.till.sdk.data.Transaction -import com.wallee.android.till.sdk.data.TransactionCompletion import com.wallee.android.till.sdk.data.TransactionProcessingBehavior -import java.math.BigDecimal import java.util.Currency @Composable @@ -43,29 +37,20 @@ fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, clien horizontalAlignment = Alignment.CenterHorizontally ) { - if (uiState.authorizationState == AuthorizationState.AUTHORIZATION_PENDING) { + if (uiState.transactionState == TransactionState.AUTHORIZATION_PENDING) { Text(text = "Authorizing transaction...") - -// val withdrawalAmount = LineItem -// .ListBuilder( -// uiState.encodedWopid, -// BigDecimal("3.0") -// ) -// .build() - val transaction = Transaction.Builder(withdrawalAmount) .setCurrency(Currency.getInstance(uiState.currency)) .setInvoiceReference(uiState.encodedWopid) - .setMerchantReference(uiState.encodedWopid) + .setMerchantReference(uiState.transactionId) .setTransactionProcessingBehavior(TransactionProcessingBehavior.COMPLETE_IMMEDIATELY) .build() try { model.setAuthorizing() client.authorizeTransaction(transaction) - //client.completeTransaction(TransactionCompletion.Builder(transaction.lineItems).build()) } catch (e: Exception) { println("FAILED authorizing transaction ${e.message}") model.withdrawalOperationFailed() @@ -80,24 +65,6 @@ fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, clien Text(text = "abort") } - } else { - - Text( - text = "Amount authorized", - color = Color.Yellow, - modifier = Modifier.background(color = Color.Black) - ) - Text(text = "Summary") - Text(text = "Withdrawable Amount: ${uiState.amount} ${uiState.currency}") - Text(text = "Fees: 0.30 ${uiState.currency}") // TODO fees - Text(text = "Withdrawal Operation ID (QR Code):") - QRCode(qrCodeContent = uiState.encodedWopid, 2.dp) - - Button(onClick = { - activity.finish() - }) { - Text(text = "finish") - } } } } diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt @@ -84,7 +84,7 @@ class ManageActivity : ComponentActivity() { Text(text = "Execute Final Balance") } - TestTransactionScreen(activity = this@ManageActivity) + TestTransactionScreen(walleeClient) Button(onClick = { SIM_WALLET_ENABLED = !SIM_WALLET_ENABLED @@ -106,8 +106,11 @@ class ManageActivity : ComponentActivity() { } } - walleeClient = ApiClient(WalleeResponseHandler(this, WithdrawalViewModel())) + val responseHandler = WalleeResponseHandler(this) + walleeClient = ApiClient(responseHandler) walleeClient.bind(this) + val model = WithdrawalViewModel(walleeClient) + responseHandler.model = model walleeClient.checkApiServiceCompatibility() } diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt @@ -1,41 +1,73 @@ -//package ch.bfh.habej2.wallee_c2ec.withdrawal -// -//import android.os.Bundle -//import androidx.activity.ComponentActivity -//import androidx.activity.compose.setContent -//import androidx.compose.foundation.background -//import androidx.compose.material3.Button -//import androidx.compose.material3.Text -//import androidx.compose.runtime.collectAsState -//import androidx.compose.ui.Modifier -//import androidx.compose.ui.graphics.Color -//import androidx.compose.ui.unit.dp -// -//class SummaryActivity(private val model: WithdrawalViewModel): ComponentActivity() { -// -// override fun onCreate(savedInstanceState: Bundle?) { -// super.onCreate(savedInstanceState) -// -// setContent { -// -// val uiState = model.uiState.collectAsState() -// -// Text( -// text = "Amount authorized", -// color = Color.Yellow, -// modifier = Modifier.background(color = Color.Black) -// ) -// Text(text = "Summary") -// Text(text = "Withdrawable Amount: ${uiState.amount} ${uiState.currency}") -// Text(text = "Fees: 0.30 ${uiState.currency}") // TODO fees -// Text(text = "Withdrawal Operation ID (QR Code):") -// QRCode(qrCodeContent = uiState.encodedWopid, 2.dp) -// -// Button(onClick = { -// finish() -// }) { -// Text(text = "finish") -// } -// } -// } -//} -\ No newline at end of file +package ch.bfh.habej2.wallee_c2ec.withdrawal + +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import ch.bfh.habej2.wallee_c2ec.MainActivity +import ch.bfh.habej2.wallee_c2ec.client.taler.TerminalClientImplementation +import com.squareup.moshi.Json + +data class Summary( + @Json(name = "amount") val amount: Amount, + @Json(name = "fees") val fees: Amount, + @Json(name = "currency") val currency: String, + @Json(name = "encodedWopid") val encodedWopid: String +) + +class SummaryActivity: ComponentActivity() { + + companion object { + var summary: Summary = Summary( + Amount(0,0), + Amount(0,0), + "", + "" + ) + + private fun reset(): Summary = Summary( + Amount(0,0), + Amount(0,0), + "", + "" + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + + Column ( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Amount authorized", + color = Color.Yellow, + modifier = Modifier.background(color = Color.Black) + ) + Text(text = "Summary") + Text(text = "Withdrawable Amount: ${summary.amount} ${summary.currency}") + Text(text = "Fees: ${summary.fees} ${summary.currency}") + Text(text = "Withdrawal Operation ID (QR Code):") + QRCode(qrCodeContent = summary.encodedWopid, 2.dp) + + Button(onClick = { + this@SummaryActivity.startActivity(Intent(baseContext, MainActivity::class.java)) + reset() + finish() + }) { + Text(text = "finish") + } + } + } + } +} +\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TestTransactionScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TestTransactionScreen.kt @@ -15,14 +15,7 @@ import java.util.Currency import java.util.UUID @Composable -fun TestTransactionScreen(activity: Activity) { - - val walleeClient = ApiClient(WalleeResponseHandler( - activity, - WithdrawalViewModel()) - ) - walleeClient.bind(activity) - walleeClient.checkApiServiceCompatibility() +fun TestTransactionScreen(walleeClient: ApiClient) { val lineitemuuid = UUID.randomUUID().toString() val merchRef = UUID.randomUUID().toString() 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 @@ -18,7 +18,13 @@ class WithdrawalActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val model = WithdrawalViewModel() + val walleeResponseHandler = WalleeResponseHandler(this) + walleeClient = ApiClient(walleeResponseHandler) + walleeClient.bind(this) + walleeClient.checkApiServiceCompatibility() + + val model = WithdrawalViewModel(walleeClient) + walleeResponseHandler.model = model setContent { @@ -44,10 +50,6 @@ class WithdrawalActivity : ComponentActivity() { AuthorizePaymentScreen(model, this@WithdrawalActivity, walleeClient) } } - - walleeClient = ApiClient(WalleeResponseHandler(this, model)) - walleeClient.bind(this) - walleeClient.checkApiServiceCompatibility() } } 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 @@ -1,20 +1,27 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal import android.app.Activity +import android.content.Intent +import android.os.Bundle import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext 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.config.TalerTerminalConfig +import ch.bfh.habej2.wallee_c2ec.client.taler.encoding.CyptoUtils 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.withdrawal.WithdrawalViewModel.Companion.generateTransactionIdentifier import com.squareup.moshi.Json +import com.wallee.android.till.sdk.ApiClient import com.wallee.android.till.sdk.data.State +import com.wallee.android.till.sdk.data.TransactionCompletion import com.wallee.android.till.sdk.data.TransactionCompletionResponse import com.wallee.android.till.sdk.data.TransactionResponse import kotlinx.coroutines.flow.MutableStateFlow @@ -22,6 +29,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import java.io.Closeable import java.math.BigDecimal +import java.security.SecureRandom import java.util.Optional import java.util.UUID @@ -55,16 +63,20 @@ interface WithdrawalOperationState { val amountStr: String val amountError: String val currency: String - val authorizationState: AuthorizationState + val transactionState: TransactionState val transaction: TransactionResponse? val transactionCompletion: TransactionCompletionResponse? + val transactionId: String } -enum class AuthorizationState { +enum class TransactionState { AUTHORIZATION_PENDING, AUTHORIZATION_STARTED, AUTHORIZED, - AUTHORIZATION_FAILED + AUTHORIZATION_FAILED, + COMPLETING, + COMPLETION_FAILED, + COMPLETED, } private class MutableWithdrawalOperationState : WithdrawalOperationState { @@ -75,12 +87,14 @@ private class MutableWithdrawalOperationState : WithdrawalOperationState { override var amountStr: String by mutableStateOf("") override var amountError: String by mutableStateOf("") override var currency: String by mutableStateOf("") - override var authorizationState: AuthorizationState by mutableStateOf(AuthorizationState.AUTHORIZATION_PENDING) + override var transactionState: TransactionState by mutableStateOf(TransactionState.AUTHORIZATION_PENDING) override var transaction: TransactionResponse? by mutableStateOf(null) override var transactionCompletion: TransactionCompletionResponse? by mutableStateOf(null) + override val transactionId: String by derivedStateOf { generateTransactionIdentifier() } } class WithdrawalViewModel( + private val walleeClient: ApiClient, vararg closeables: Closeable ) : ViewModel(*closeables) { @@ -91,6 +105,14 @@ class WithdrawalViewModel( private val _uiState = MutableStateFlow(MutableWithdrawalOperationState()) val uiState: StateFlow<WithdrawalOperationState> = _uiState + companion object { + fun generateTransactionIdentifier(): String { + val wopid = ByteArray(32) + SecureRandom().nextBytes(wopid) + return CyptoUtils.encodeCrock(wopid) + } + } + fun chooseExchange(cfg: TalerTerminalConfig, activity: Activity) { if (exchangeSelected) { @@ -157,38 +179,49 @@ class WithdrawalViewModel( } fun setAuthorizing() { - _uiState.value.authorizationState = AuthorizationState.AUTHORIZATION_STARTED + _uiState.value.transactionState = TransactionState.AUTHORIZATION_STARTED } fun updateWalleeTransactionReply(response: TransactionResponse) { println("updating wallee transaction: $response") _uiState.value.transaction = response -// walleeClient.completeTransaction( -// TransactionCompletion.Builder(response.transaction.lineItems) -// .setReserveReference(response.reserveReference!!) -// .build()) - _uiState.value.authorizationState = + _uiState.value.transactionState = if (response.state.name == "SUCCESSFUL") - AuthorizationState.AUTHORIZED + TransactionState.AUTHORIZED else - AuthorizationState.AUTHORIZATION_FAILED + TransactionState.AUTHORIZATION_FAILED + + println("authorization state: ${_uiState.value.transactionState}") + if (_uiState.value.transactionState == TransactionState.AUTHORIZATION_FAILED) { + println("authorization failed... aborting withdrawal") + return + } - println("authorization state: ${_uiState.value.authorizationState}") + _uiState.value.transactionState = TransactionState.COMPLETING + + walleeClient.completeTransaction( + TransactionCompletion.Builder(response.transaction.lineItems) + .setCurrency(response.transaction.currency) + .setReserveReference(response.reserveReference!!) + .build() + ) } fun updateWalleeTransactionCompletion( completion: TransactionCompletionResponse, activity: Activity ) { - _uiState.value.transactionCompletion = completion if (completion.state == State.FAILED) { + println("completion of the transaction failed... aborting") + _uiState.value.transactionState = TransactionState.COMPLETION_FAILED withdrawalOperationFailed() + return } - completion.transactionCompletion.lineItems[0].id - completion.state.name + _uiState.value.transactionState = TransactionState.COMPLETED + _uiState.value.transactionCompletion = completion confirmationRequest(activity) } @@ -211,12 +244,25 @@ class WithdrawalViewModel( viewModelScope.launch { terminalClient!!.sendConfirmationRequest( _uiState.value.encodedWopid, - TerminalWithdrawalConfirmationRequest(_uiState.value.encodedWopid, Amount(0, 0)) + TerminalWithdrawalConfirmationRequest( + _uiState.value.encodedWopid, + Amount(0, 0) + ) ) { if (!it) { withdrawalOperationFailed() activity.finish() + return@sendConfirmationRequest } + SummaryActivity.summary = + Summary( + _uiState.value.amount, + Amount(0,0), + _uiState.value.currency, + _uiState.value.encodedWopid + ) + val intent = Intent(activity, SummaryActivity::class.java) + activity.startActivity(intent) } } }