fix(shopinbit): lock shipping country, fix billing overflow, remove T&C from payment, add done button
What changed, and why it matters
This commit tweaks the checkout flow for a third-party shopping integration called ShopInBit. It removes a 'I accept the Terms & Conditions' checkbox from the payment screen, locks the shipping country to whatever was already chosen, fixes a layout overflow on the billing form, and adds a 'View My Requests' button after payment. None of these changes look like a security fix for the wallet itself; they are mostly UI/UX adjustments to a partner service's checkout screens.
No immediate security action is required. If the removal of the Terms & Conditions acceptance step has legal or compliance implications, the project should confirm that acceptance is still obtained elsewhere in the ShopInBit flow. Otherwise, treat this as a routine UI/UX update.
Security signals we found
Removal of explicit Terms & Conditions acceptance gate before payment
Shipping country selection locked to pre-selected value, reducing user-controlled input surface
UI overflow/layout fixes that may prevent form rendering issues but are not security-relevant
Evidence from the diff
The patch modifies two Dart files in the Stack Wallet Flutter app related to the ShopInBit integration. In the payment view, it removes the _termsAccepted state, the terms URL launcher, and the associated RichText checkbox, so the Pay Now button no longer depends on explicit T&C acceptance. It also adds a _navigateToTickets helper and a PrimaryButton to return to the requests list. In the shipping view, it pre-sets the country ISO from widget.model.deliveryCountry and disables the country dropdown’s onChanged callback, effectively locking the shipping country. It also replaces a Spacer with a fixed SizedBox, increases the desktop dialog maxHeight, and wraps the dialog content in SingleChildScrollView to address a billing form overflow.
Changed components
lib/pages/shopinbit/shopinbit_payment_view.dartlib/pages/shopinbit/shopinbit_shipping_view.dartInspect captured patch +23 / −70
diff --git a/lib/pages/shopinbit/shopinbit_payment_view.dart b/lib/pages/shopinbit/shopinbit_payment_view.dart
index 8747670..2ef6505 100644
--- a/lib/pages/shopinbit/shopinbit_payment_view.dart
+++ b/lib/pages/shopinbit/shopinbit_payment_view.dart
@@ -2,12 +2,10 @@ import 'dart:async';
import 'dart:io';
import 'package:decimal/decimal.dart';
-import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
-import 'package:url_launcher/url_launcher.dart';
import '../../app_config.dart';
import '../../models/isar/models/ethereum/eth_contract.dart';
@@ -48,7 +46,6 @@ class ShopInBitPaymentView extends ConsumerStatefulWidget {
}
class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
- bool _termsAccepted = false;
bool _loading = false;
int _selectedMethod = 0;
Timer? _pollTimer;
@@ -76,8 +73,7 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
'payment_processing',
}.contains(_status);
- bool get _payNowEnabled =>
- _termsAccepted && !_isExpiredOrInvalid && !_isTerminal;
+ bool get _payNowEnabled => !_isExpiredOrInvalid && !_isTerminal;
@override
void initState() {
@@ -160,11 +156,6 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
}
}
- Future<void> _openTerms() async {
- const url = "https://api.shopinbit.com/static/policy/terms.html";
- await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
- }
-
Future<void> _checkForPayment() async {
_pollTimer?.cancel();
setState(() => _loading = true);
@@ -334,6 +325,14 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
Navigator.of(context).pop();
}
+ void _navigateToTickets() {
+ if (Util.isDesktop) {
+ Navigator.of(context, rootNavigator: true).pop();
+ } else {
+ Navigator.of(context).popUntil((route) => route.isFirst);
+ }
+ }
+
void _navigateToSendFrom({
required CryptoCurrency coin,
required Amount? amount,
@@ -709,59 +708,15 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
],
),
),
+ SizedBox(height: isDesktop ? 16 : 12),
+ PrimaryButton(
+ label: "View My Requests",
+ onPressed: _navigateToTickets,
+ ),
],
SizedBox(height: isDesktop ? 24 : 16),
// Coin list (replaces tab selector + QR + address + global button)
if (!_isExpiredOrInvalid) ...coinRows,
- SizedBox(height: isDesktop ? 16 : 12),
- GestureDetector(
- onTap: () {
- setState(() {
- _termsAccepted = !_termsAccepted;
- });
- },
- child: Container(
- color: Colors.transparent,
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(
- width: 20,
- height: 20,
- child: IgnorePointer(
- child: Checkbox(
- materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
- value: _termsAccepted,
- onChanged: (_) {},
- ),
- ),
- ),
- const SizedBox(width: 12),
- Expanded(
- child: RichText(
- text: TextSpan(
- style: isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.w500_14(context),
- children: [
- const TextSpan(text: "I accept the "),
- TextSpan(
- text: "Terms & Conditions",
- style: STextStyles.richLink(
- context,
- ).copyWith(fontSize: isDesktop ? null : 14),
- recognizer: TapGestureRecognizer()
- ..onTap = _openTerms,
- ),
- const TextSpan(text: "."),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
- ),
],
);
diff --git a/lib/pages/shopinbit/shopinbit_shipping_view.dart b/lib/pages/shopinbit/shopinbit_shipping_view.dart
index 4ad2fe4..be7ca26 100644
--- a/lib/pages/shopinbit/shopinbit_shipping_view.dart
+++ b/lib/pages/shopinbit/shopinbit_shipping_view.dart
@@ -105,6 +105,10 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
_billingCityFocusNode = FocusNode();
_billingPostalCodeFocusNode = FocusNode();
+ _selectedCountryIso = widget.model.deliveryCountry.isNotEmpty
+ ? widget.model.deliveryCountry
+ : null;
+
for (final node in [
_nameFocusNode,
_streetFocusNode,
@@ -372,15 +376,9 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
_countrySearchController.clear();
}
},
- onChanged: _loadingCountries
- ? null
- : (value) {
- setState(() {
- _selectedCountryIso = value;
- });
- },
+ onChanged: null,
hint: Text(
- _loadingCountries ? "Loading countries..." : "Country",
+ "Country",
style: isDesktop
? STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
@@ -677,7 +675,7 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
),
),
],
- const Spacer(),
+ const SizedBox(height: 24),
PrimaryButton(
label: _submitting ? "Submitting..." : "Continue to payment",
enabled: _canContinue,
@@ -689,7 +687,7 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
if (isDesktop) {
return DesktopDialog(
maxWidth: 580,
- maxHeight: 600,
+ maxHeight: 700,
child: Column(
children: [
Row(
@@ -711,7 +709,7 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
horizontal: 32,
vertical: 16,
),
- child: content,
+ child: SingleChildScrollView(child: content),
),
),
],
Why this scored 16/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.