feat(shopinbit): keep polling ticket state & messages of terminal tickets
What changed, and why it matters
This commit changes the Stack Wallet app so it keeps checking (polling) the status of certain ShopInBit support tickets even after they are in a final, unchangeable state. Previously, the app stopped polling once a ticket was closed, merged, or refunded. The change removes those stop conditions. On its own, this is a behavior change rather than a direct security flaw, but it could slightly increase network traffic, battery use, and the number of API calls made to the ShopInBit server. There is no evidence in the commit that this fixes a security vulnerability or that it introduces one.
Treat this as a minor resource-usage change, not a security patch. If reviewing for security, verify that the polling logic respects server rate limits, that terminal tickets cannot be modified by the client, and that no sensitive data is leaked through repeated status requests. No immediate user action is required.
Security signals we found
Removal of resource-limiting guard conditions (terminal-state short-circuits)
Increased persistent background polling for terminal tickets
Potential for higher API request volume / battery consumption
No new input validation, auth, or cryptographic changes visible
Evidence from the diff
The patch removes two terminal-state short-circuits in the ShopInBit ticket polling logic. In shopinbit_ticket_detail.dart, the widget now restarts polling whenever the app resumes and no longer stops polling after a successful poll that finds a terminal ticket state. In shopinbit_service.dart, the service no longer skips the status/messages/details API calls when an existing ticket is already terminal. The stated commit message is a feature: ‘keep polling ticket state & messages of terminal tickets.’ The diff shows only deletions of guard conditions; no new validation, authentication, or rate-limiting logic is added. The change could lead to unnecessary background polling and repeated API requests for terminal tickets, but it does not by itself expose user data, bypass authentication, or enable remote code execution.
Changed components
lib/pages/shopinbit/shopinbit_ticket_detail.dartlib/services/shopinbit/shopinbit_service.dartShopInBit ticket polling serviceShopInBit ticket detail UIInspect captured patch +1 / −20
diff --git a/lib/pages/shopinbit/shopinbit_ticket_detail.dart b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
index 20968a0..16944a1 100644
--- a/lib/pages/shopinbit/shopinbit_ticket_detail.dart
+++ b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
@@ -12,7 +12,6 @@ import '../../providers/db/drift_provider.dart';
import '../../providers/global/shopin_bit_service_provider.dart';
import '../../services/shopinbit/src/client.dart';
import '../../services/shopinbit/src/models/message.dart';
-import '../../services/shopinbit/src/models/ticket.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/assets.dart';
import '../../utilities/logger.dart';
@@ -81,10 +80,7 @@ class _ShopInBitTicketDetailState extends ConsumerState<ShopInBitTicketDetail>
void didChangeAppLifecycleState(AppLifecycleState state) {
// Don't poll while backgrounded; resume fresh when we come back.
if (state == AppLifecycleState.resumed) {
- final ticket = ref.read(pShopInBitTicket(_id)).asData?.value;
- final terminal =
- ticket != null && TicketState.fromString(ticket.statusRaw).isTerminal;
- if (!terminal) _startPolling();
+ _startPolling();
} else {
_pollingTimer?.cancel();
}
@@ -105,13 +101,6 @@ class _ShopInBitTicketDetailState extends ConsumerState<ShopInBitTicketDetail>
}
if (!mounted) return;
- // Stop polling once the ticket reaches a terminal state; nothing about a
- // closed/merged/refunded ticket will change server-side.
- final ticket = ref.read(pShopInBitTicket(_id)).asData?.value;
- if (ticket != null && TicketState.fromString(ticket.statusRaw).isTerminal) {
- return;
- }
-
// Back off on failure (e.g. a 429), reset on success.
_pollInterval = ok
? _kBasePollInterval
diff --git a/lib/services/shopinbit/shopinbit_service.dart b/lib/services/shopinbit/shopinbit_service.dart
index 6b02485..8ed057f 100644
--- a/lib/services/shopinbit/shopinbit_service.dart
+++ b/lib/services/shopinbit/shopinbit_service.dart
@@ -182,14 +182,6 @@ class ShopInBitService {
id,
);
- // Terminal-state short-circuit: nothing about a closed/merged ticket
- // will change server-side, so skip the three API calls entirely.
- if (existing != null &&
- TicketState.fromString(existing.statusRaw).isTerminal) {
- completer.complete();
- return;
- }
-
// get status first. If it fails there is no reason to make the remaining
// two API calls
final statusResp = await client.getTicketStatus(
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.