cashless2ecash

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

AuthorizePaymentScreen.kt (8832B)


      1 // This file is part of taler-cashless2ecash.
      2 // Copyright (C) 2024 Joel Häberli
      3 //
      4 // taler-cashless2ecash is free software: you can redistribute it and/or modify it
      5 // under the terms of the GNU Affero General Public License as published
      6 // by the Free Software Foundation, either version 3 of the License,
      7 // or (at your option) any later version.
      8 //
      9 // taler-cashless2ecash is distributed in the hope that it will be useful, but
     10 // WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 // Affero General Public License for more details.
     13 //
     14 // You should have received a copy of the GNU Affero General Public License
     15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 //
     17 // SPDX-License-Identifier: AGPL3.0-or-later
     18 
     19 package ch.bfh.habej2.wallee_c2ec.withdrawal
     20 
     21 import android.app.Activity
     22 import androidx.compose.foundation.layout.Arrangement
     23 import androidx.compose.foundation.layout.Column
     24 import androidx.compose.foundation.layout.Spacer
     25 import androidx.compose.foundation.layout.height
     26 import androidx.compose.foundation.layout.size
     27 import androidx.compose.material.icons.Icons
     28 import androidx.compose.material.icons.filled.DoneAll
     29 import androidx.compose.material.icons.filled.Warning
     30 import androidx.compose.material3.Icon
     31 import androidx.compose.material3.MaterialTheme
     32 import androidx.compose.material3.Text
     33 import androidx.compose.runtime.Composable
     34 import androidx.compose.runtime.LaunchedEffect
     35 import androidx.compose.runtime.collectAsState
     36 import androidx.compose.runtime.getValue
     37 import androidx.compose.ui.Alignment
     38 import androidx.compose.ui.Modifier
     39 import androidx.compose.ui.res.colorResource
     40 import androidx.compose.ui.text.font.FontWeight
     41 import androidx.compose.ui.text.style.TextAlign
     42 import androidx.compose.ui.unit.dp
     43 import ch.bfh.habej2.wallee_c2ec.R
     44 import com.wallee.android.till.sdk.ApiClient
     45 import com.wallee.android.till.sdk.data.LineItem
     46 import com.wallee.android.till.sdk.data.Transaction
     47 import com.wallee.android.till.sdk.data.TransactionCompletion
     48 import com.wallee.android.till.sdk.data.TransactionProcessingBehavior
     49 import java.util.Currency
     50 
     51 @Composable
     52 fun AuthorizePaymentScreen(
     53     model: WithdrawalViewModel,
     54     activity: Activity,
     55     client: ApiClient
     56 ) {
     57 
     58     val uiState by model.uiState.collectAsState()
     59 
     60     val withdrawalAmount = LineItem
     61         .ListBuilder(
     62             uiState.encodedWopid,
     63             uiState.amount.plus(uiState.withdrawalFees).toBigDecimal()
     64         )
     65         .build()
     66 
     67     LaunchedEffect(Unit) {
     68 
     69         println("LaunchedEffect: starting authorization")
     70 
     71         if (uiState.withdrawalState == WithdrawalState.READY_FOR_AUTHORIZATION) {
     72             val transaction = Transaction.Builder(withdrawalAmount)
     73                 .setCurrency(Currency.getInstance(uiState.currency))
     74                 .setInvoiceReference(uiState.encodedWopid)
     75                 .setMerchantReference(uiState.encodedWopid)
     76                 .setCustomText(
     77                     String.format(
     78                         activity.getString(R.string.wallee_custom_text),
     79                         uiState.currency,
     80                         uiState.amount
     81                     )
     82                 )
     83                 .setTransactionProcessingBehavior(TransactionProcessingBehavior.COMPLETE_IMMEDIATELY)
     84                 .build()
     85 
     86             try {
     87                 println("Authorizing transaction ...")
     88                 model.setAuthorizing()
     89                 client.authorizeTransaction(transaction)
     90             } catch (e: Exception) {
     91                 println("FAILED authorizing transaction ${e.message}")
     92                 model.withdrawalOperationFailed(activity)
     93                 e.printStackTrace()
     94             }
     95         }
     96     }
     97 
     98     when (uiState.withdrawalState) {
     99         WithdrawalState.UNREADY_FOR_AUTHORIZATION -> model.withdrawalOperationFailed(activity)
    100         WithdrawalState.READY_FOR_AUTHORIZATION -> WalleeProcessingTransaction(activity)
    101         WithdrawalState.AUTHORIZATION_TRIGGERED -> WalleeProcessingTransaction(activity)
    102         WithdrawalState.AUTHORIZATION_FAILED -> model.withdrawalOperationFailed(activity)
    103         WithdrawalState.E2EC_TIMEOUT -> model.withdrawalOperationFailed(activity)
    104         WithdrawalState.AUTHORIZED -> WalleeCompletingPayment(
    105             client,
    106             model,
    107             activity
    108         ) // TODO: Maybe following is not needed because completion immediately is specified: "WalleeCompletingPayment(client, model)"
    109         WithdrawalState.COMPLETION_TRIGGERED -> WalleeProcessingTransaction(activity)
    110         WithdrawalState.COMPLETED -> SuccessScreen(
    111             uiState.currency,
    112             uiState.withdrawalFees.toString(true),
    113             uiState.amount.toString(true),
    114             activity
    115         )
    116         WithdrawalState.COMPLETION_FAILED -> FailedScreen(activity)
    117     }
    118 }
    119 
    120 @Composable
    121 private fun WalleeCompletingPayment(
    122     client: ApiClient,
    123     model: WithdrawalViewModel,
    124     activity: Activity
    125 ) {
    126 
    127     val uiState by model.uiState.collectAsState()
    128 
    129     LaunchedEffect(key1 = Unit) {
    130 
    131         if (uiState.withdrawalState == WithdrawalState.AUTHORIZED) {
    132             client.completeTransaction(
    133                 TransactionCompletion.Builder(uiState.transaction!!.transaction.lineItems)
    134                     .setCurrency(uiState.transaction!!.transaction.currency)
    135                     .setReserveReference(uiState.transaction!!.reserveReference!!)
    136                     .build()
    137             )
    138             model.setCompleting()
    139         }
    140 
    141     }
    142 
    143     Spacer(modifier = Modifier.height(32.dp))
    144 
    145     Column(
    146         horizontalAlignment = Alignment.CenterHorizontally,
    147         verticalArrangement = Arrangement.spacedBy(16.dp)
    148     ) {
    149         TalerLogo()
    150 
    151         Spacer(modifier = Modifier.height(8.dp))
    152 
    153         Text(text = "Completing transaction...")
    154 
    155 
    156         Spacer(modifier = Modifier.height(10.dp))
    157         TalerButton(text = "Done", onClick = { activity.finish() })
    158     }
    159 }
    160 
    161 @Composable
    162 private fun WalleeProcessingTransaction(activity: Activity) {
    163 
    164     Spacer(modifier = Modifier.height(32.dp))
    165     Column(
    166         horizontalAlignment = Alignment.CenterHorizontally,
    167         verticalArrangement = Arrangement.spacedBy(16.dp)
    168     ) {
    169         TalerLogo()
    170 
    171         Spacer(modifier = Modifier.height(8.dp))
    172 
    173         Text(text = "Processing transaction...")
    174 
    175         Spacer(modifier = Modifier.height(10.dp))
    176         TalerButton(text = "Done", onClick = { activity.finish() })
    177     }
    178 }
    179 
    180 @Composable
    181 private fun SuccessScreen(
    182     currency: String,
    183     withdrawalFees: String,
    184     amount: String,
    185     activity: Activity
    186 ) {
    187 
    188     Spacer(modifier = Modifier.height(32.dp))
    189     Column(
    190         horizontalAlignment = Alignment.CenterHorizontally,
    191         verticalArrangement = Arrangement.spacedBy(16.dp)
    192     ) {
    193         TalerLogo()
    194 
    195         Icon(
    196             imageVector = Icons.Filled.DoneAll,
    197             contentDescription = "All steps successful",
    198             tint = colorResource(R.color.bfh_mediumgreen),
    199             modifier = Modifier.size(64.dp)
    200         )
    201 
    202         Text(
    203             text = "Hurrah! Taler's market capitalization just surged!",
    204             style = MaterialTheme.typography.titleMedium,
    205             textAlign = TextAlign.Center
    206         )
    207 
    208         Spacer(modifier = Modifier.height(8.dp))
    209 
    210         Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
    211             Text(
    212                 text = "Summary",
    213                 style = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.SemiBold)
    214             )
    215             Text(text = "Amount: ${currency} ${amount}")
    216             Text(text = "Fees: ${currency} ${withdrawalFees}")
    217         }
    218         Spacer(modifier = Modifier.height(10.dp))
    219         TalerButton(text = "Done", onClick = { activity.finish() })
    220     }
    221 }
    222 
    223 
    224 @Composable
    225 private fun FailedScreen(
    226     activity: Activity
    227 ) {
    228     Spacer(modifier = Modifier.height(32.dp))
    229 
    230     Column(
    231         horizontalAlignment = Alignment.CenterHorizontally,
    232         verticalArrangement = Arrangement.spacedBy(16.dp)
    233     ) {
    234         TalerLogo()
    235 
    236 
    237         Icon(
    238             imageVector = Icons.Filled.Warning,
    239             contentDescription = "Failed authorizing payment",
    240             tint = colorResource(R.color.bfh_red),
    241             modifier = Modifier.size(64.dp)
    242         )
    243 
    244         Text(
    245             text = "Failed to authorize payment.",
    246             style = MaterialTheme.typography.titleMedium,
    247             color = MaterialTheme.colorScheme.error,
    248             textAlign = TextAlign.Center
    249         )
    250 
    251         Spacer(modifier = Modifier.height(8.dp))
    252 
    253         Text(
    254             text = "Withdrawal aborted. Please try again.",
    255             textAlign = TextAlign.Center
    256         )
    257         Spacer(modifier = Modifier.height(8.dp))
    258         TalerButton(text = "Done", onClick = { activity.finish() })
    259     }
    260 }