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 }