fix(shopinbit): recover car-research invoices within the +24h grace
What changed, and why it matters
This commit fixes a business-logic bug in Stack Wallet's ShopinBit ticket screen. Previously, car-research invoices that had expired but were still within a 24-hour grace period were incorrectly treated as unpayable, so users could not resume or recover them. The change extends the payable check from the original expiration time to 24 hours after expiration, matching the documented spec. There is no direct security signal in the diff itself.
Treat as a normal functional bug fix. Review whether the 24-hour grace period is consistently enforced server-side and in any other client code paths that evaluate invoice expiration, to avoid similar UX/financial discrepancies.
Security signals we found
Business-logic correction in payment/invoice lifecycle
No input validation, cryptography, authentication, or authorization changes observed
No memory safety, injection, or secret-handling changes observed
Evidence from the diff
In lib/pages/shopinbit/shopinbit_tickets_view.dart, the payable condition for an invoice was changed from inv.expiresAt!.isAfter(DateTime.now()) to inv.expiresAt!.add(const Duration(hours: 24)).isAfter(DateTime.now()). This makes unresolved invoices remain recoverable for 24 hours past their expiresAt timestamp, unless the car-research status is already finalized. The change is a straightforward logic correction to align with the stated ShopinBit spec.
Changed components
lib/pages/shopinbit/shopinbit_tickets_view.dartShopinBit invoice recovery / payable status logicInspect captured patch +5 / −1
diff --git a/lib/pages/shopinbit/shopinbit_tickets_view.dart b/lib/pages/shopinbit/shopinbit_tickets_view.dart
index 87c9927..7b185f9 100644
--- a/lib/pages/shopinbit/shopinbit_tickets_view.dart
+++ b/lib/pages/shopinbit/shopinbit_tickets_view.dart
@@ -78,7 +78,11 @@ class _ShopInBitTicketsViewState extends ConsumerState<ShopInBitTicketsView> {
final payable =
inv.expiresAt != null &&
inv.paymentLinks.isNotEmpty &&
- (inv.expiresAt!.isAfter(DateTime.now()) ||
+ // Spec: expired unresolved invoices stay recoverable until
+ // expires_at + 24h.
+ (inv.expiresAt!
+ .add(const Duration(hours: 24))
+ .isAfter(DateTime.now()) ||
carResearchIsFinalized(inv.status, inv.additional));
if (payable) {
resumable ??= [];
Why this scored 18/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.