PaymentOption.kt
/*
* This file is part of GNU Taler
* (C) 2026 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.merchantpos.paymentoptions
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import net.taler.common.Amount
import net.taler.merchantpos.R
enum class PaymentOptionStatus
{
Disabled,
Unavailable,
NotConfigured,
SignInRequired,
PermissionRequired,
NotPaired,
Disconnected,
/** Payments are available without an external device connection. */
Ready,
Connected,
}
data class PaymentDevice(
/** Stable across device status updates. */
val id: String,
val name: String? = null,
/** Fallback when [name] is absent. */
@StringRes val nameRes: Int? = null,
val serialNumber: String? = null,
val batteryPercent: Int? = null,
val connected: Boolean = false,
/** Device error; does not change payment availability. */
@StringRes val statusRes: Int? = null,
)
data class PaymentOptionState(
val id: String,
val enabled: Boolean = false,
val status: PaymentOptionStatus = PaymentOptionStatus.Disabled,
val account: String? = null,
/** Includes disconnected devices. */
val devices: List<PaymentDevice> = emptyList(),
)
class PaymentOptionActions(
val setEnabled: (Boolean) -> Unit,
val refresh: () -> Unit,
val reportFailure: () -> Unit,
)
/** Payment method implementation and settings UI. */
interface PaymentOption
{
/** Stable identifier used in preferences and navigation. */
val id: String
@get:StringRes
val nameRes: Int
@get:StringRes
val descriptionRes: Int
val icon: ImageVector
@Composable get
val enabledByDefault: Boolean get() = false
val alwaysEnabled: Boolean get() = false
/**
* Tracks the option's own connection.
* Optional displays are reported through [PaymentOptionState.devices].
*/
val tracksConnection: Boolean get() = false
/** Full logo for checkout tiles; [icon] is used in lists. */
val logo: ImageVector?
@Composable get() = null
val hasSettings: Boolean get() = false
@get:StringRes
val settingsTitleRes: Int get() = R.string.payment_option_settings_title
val chargesExternally: Boolean get() = false
/** Must be acknowledged before enabling the option. */
@get:StringRes
val activationNoticeRes: Int? get() = null
val canRefund: Boolean get() = false
/**
* Returns false if charging cannot start; otherwise calls [onResult] on the main thread.
* Persist the pending charge first: process death can prevent callback delivery.
*/
fun startCharge(
charge: ExternalCharge,
onResult: (ChargeResult) -> Unit,
): Boolean = false
/** Whether the required application credentials are configured. */
fun isConfigured(): Boolean = true
/** Called only while enabled. Exceptions become [PaymentOptionStatus.Unavailable]. */
fun probe(): PaymentOptionState = PaymentOptionState(id, true, PaymentOptionStatus.Ready)
/** Called for every option in [net.taler.merchantpos.MainActivity.onCreate], before STARTED. */
fun registerLaunchers(
activity: ComponentActivity,
manager: PaymentOptionsManager,
) = Unit
/** Called at startup and periodically while the app is in the foreground. */
suspend fun maintainSession() = Unit
/** Returns true if this option consumed the redirect. */
suspend fun handleRedirect(uri: Uri): Boolean = false
fun canCharge(state: PaymentOptionState): Boolean = state.enabled && state.status == PaymentOptionStatus.Connected
/** [transactionCode] is the reference returned by the original charge. */
suspend fun refund(
transactionCode: String?,
amount: Amount,
reason: String,
): RefundOutcome = RefundOutcome.NotSupported
/** Clear the session on merchant logout. */
fun onLogout() = Unit
@Composable
fun DetailContent(
state: PaymentOptionState,
actions: PaymentOptionActions,
) = Unit
}