View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012094 | Taler | wallet (Android App) | public | 2026-11-11 01:50 | 2026-11-11 01:50 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012094: Android: the payment screen does not show that an order is age-restricted (minimum_age not even decoded); the WebExtension shows "18+" (patch attached) | ||||
| Description | For an age-restricted order (contract minimum_age), the WebExtension shows a "18+" badge next to the merchant on the payment screen (and "18+ This purchase is age restricted." on the pay-template form). Android and iOS show nothing; it is the only place where the user learns that the purchase is age-restricted before paying. What the user sees (order with "minimum_age": 18, products wine and corkscrew; origin/master builds of 2026-11-11): - WebExtension: "18+" pill at the right of the merchant row (before-web-1.png). - Android: merchant, products, total -- no age information (before-android-1.png). - iOS: merchant, products, amounts -- no age information (before-ios-1.png). Reference: web screens/PaymentScreen.tsx:577-581, routes/payment-model.ts:272, screens/PaymentTemplateScreen.tsx:136-141. Android (origin/master 4c483527): taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt:39-160 does not decode minimum_age at all (the pay-template DTO does, PayTemplateDetails.kt:66-67,82-83); PromptPaymentComposable.kt:250-284 (MerchantSection) shows logo and name only. Patch (android-minimum-age.patch): ContractTerms (V0 and V1) decodes minimum_age; MerchantSection shows "Age-restricted purchase: 18+" under the merchant name. One new English string. Not in the patch: the same hint on the pay-template form. Verified: :wallet:assembleFdroidSim builds; after-android-1.png is the same order with the patch. Related: iOS counterpart. Assisted by LLM | ||||
| Steps To Reproduce | 1. Create an order with "minimum_age": 18. 2. Open its taler://pay/ link: the WebExtension shows "18+", Android and iOS do not. (Automated: wc-stack pay-uri --order-json lib/stack-examples/products-and-age.json.) | ||||
| Tags | No tags attached. | ||||
| Attached Files | android-minimum-age.patch (2,976 bytes)
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
index 1f59d332..41870125 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -53,6 +53,7 @@ sealed class ContractTerms {
abstract val exchanges: List<Exchange>
abstract val deliveryLocation: Location?
abstract val deliveryDate: Timestamp?
+ abstract val minimumAge: Int?
@Serializable
@JsonClassDiscriminator("version")
@@ -98,6 +99,9 @@ sealed class ContractTerms {
@SerialName("delivery_date")
override val deliveryDate: Timestamp? = null,
+ @SerialName("minimum_age")
+ override val minimumAge: Int? = null,
+
val amount: Amount,
@SerialName("max_fee")
@@ -150,6 +154,9 @@ sealed class ContractTerms {
@SerialName("delivery_date")
override val deliveryDate: Timestamp? = null,
+ @SerialName("minimum_age")
+ override val minimumAge: Int? = null,
+
val choices: List<ContractChoice>,
@SerialName("token_families")
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
index 3663d1d3..e5f1ca9f 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
@@ -270,6 +270,16 @@ fun MerchantSection(
style = MaterialTheme.typography.titleLarge,
)
+ // AGE RESTRICTION
+ contractTerms.minimumAge?.takeIf { it > 0 }?.let { age ->
+ Text(
+ stringResource(R.string.payment_minimum_age, age),
+ modifier = Modifier.padding(top = 8.dp),
+ style = MaterialTheme.typography.labelLarge,
+ color = MaterialTheme.colorScheme.error,
+ )
+ }
+
// MERCHANT INFO BUTTON
// TextButton(onClick = {}) {
// Icon(
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..40f44f78 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -319,6 +319,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<string name="payment_tokens_insufficient">Insufficient tokens</string>
<string name="payment_tokens_unexpected">Unexpected merchant %1$s, your token could be stolen.</string>
<string name="payment_tokens_untrusted">Merchant %1$s is untrusted, cannot spend this token.</string>
+ <string name="payment_minimum_age">Age-restricted purchase: %1$d+</string>
<string name="payment_template_error">Error creating order</string>
<string name="payment_title">Payment</string>
| ||||
| related to | 0012096 | new | iOS: the payment screen does not show that an order is age-restricted (minimumAge decoded but unused); the WebExtension shows "18+" (patch attached) |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:50 | bot-cg | New Issue | |
| 2026-11-11 01:50 | bot-cg | File Added: before-web-1.png | |
| 2026-11-11 01:50 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:50 | bot-cg | File Added: before-ios-1.png | |
| 2026-11-11 01:50 | bot-cg | File Added: after-android-1.png | |
| 2026-11-11 01:50 | bot-cg | File Added: android-minimum-age.patch |