fix(shopinbit): keep car-research expiresAt null on parse failure
What changed, and why it matters
This commit fixes a bug in the Stack Wallet app's integration with ShopinBit's car-research service. Previously, if the invoice expiration date was missing or unreadable, the app would silently replace it with the current time, making a brand-new invoice look like it had already expired. Now the app keeps the expiration date as 'unknown' instead of guessing. This is a correctness and user-experience fix, not a remote code execution or theft vulnerability.
Review any downstream code that reads CarResearchInvoice.expiresAt to ensure it safely handles the new null case (e.g., displays 'no expiration' or 'unknown' rather than crashing). No urgent security patch is required, but the change should be included in the next release because it fixes a user-facing payment bug.
Security signals we found
Data-parsing fallback changed from a default value to null
Developer comment explicitly frames the change as preventing a misleading expiration state
Affects invoice/payment state in a cryptocurrency wallet integration
Evidence from the diff
In lib/services/shopinbit/src/models/car_research.dart, the CarResearchInvoice model changed expiresAt from a non-nullable DateTime to a nullable DateTime?. The factory constructor no longer falls back to DateTime.now() when DateTime.tryParse fails on the expires_at JSON field; it now leaves expiresAt as null. The commit comment explicitly states the rationale: ‘a missing/garbled date should not make a fresh invoice look already-expired.’ This prevents false-positive expiration of BTCPay invoices used for car research deposits.
Changed components
lib/services/shopinbit/src/models/car_research.dartShopinBit car-research invoice parsingBTCPay invoice expiration displayInspect captured patch +5 / −5
diff --git a/lib/services/shopinbit/src/models/car_research.dart b/lib/services/shopinbit/src/models/car_research.dart
index 8a8a9a7..70e61b3 100644
--- a/lib/services/shopinbit/src/models/car_research.dart
+++ b/lib/services/shopinbit/src/models/car_research.dart
@@ -82,12 +82,12 @@ bool carResearchIsFinalized(String? status, String? additional) {
class CarResearchInvoice {
final String btcpayInvoice;
- final DateTime expiresAt;
+ final DateTime? expiresAt;
final Map<String, String> paymentLinks;
CarResearchInvoice({
required this.btcpayInvoice,
- required this.expiresAt,
+ this.expiresAt,
required this.paymentLinks,
});
@@ -95,9 +95,9 @@ class CarResearchInvoice {
final linksRaw = json['payment_links'] as Map<String, dynamic>? ?? {};
return CarResearchInvoice(
btcpayInvoice: json['btcpay_invoice'] as String,
- expiresAt:
- DateTime.tryParse(json['expires_at']?.toString() ?? '') ??
- DateTime.now(),
+ // Null rather than defaulting to now(): a missing/garbled date should
+ // not make a fresh invoice look already-expired.
+ expiresAt: DateTime.tryParse(json['expires_at']?.toString() ?? ''),
paymentLinks: linksRaw.map((k, v) => MapEntry(k, v as String)),
);
}
Why this scored 35/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.