Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions lib/utils/payment_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:convert';
import 'package:http/http.dart' as http;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical | ⚡ Quick win
What's happening:

  • 🏗️ [ARCHITECTURE] Violation of Single Responsibility Principle: This component is handling both payment processing and API calls, also lacks input validation.

Recommendation:

  • Apply the suggested fix below
- final String apiKey = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc';
Suggested change
final String apiKey;
🤖 Prompt for AI Agents
There is a critical issue in `lib/utils/payment_helper.dart` at line 3.

## Issue
🏗️ [ARCHITECTURE] Violation of Single Responsibility Principle: This component is handling both payment processing and API calls, also lacks input validation.

## Current Code

final String apiKey = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc';


## Suggested Fix

final String apiKey;


## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 3. Apply the suggested fix above, ensuring it integrates correctly with the surrounding code. Verify the fix doesn't break any existing functionality.

/// Payment processing helper
class PaymentHelper {
final String apiKey = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc'; // hardcoded secret

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical
What's happening:

  • 🚨 SECURITY ALERT: Potential Generic Secret exposed. Please revoke and remove this immediately.

Recommendation:

  • Move secrets to environment variables or a secure vault
🤖 Prompt for AI Agents
There is a critical issue in `lib/utils/payment_helper.dart` at line 6.

## Issue
🚨 SECURITY ALERT: Potential Generic Secret exposed. Please revoke and remove this immediately.

## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 6. Verify the fix doesn't break any existing functionality.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Remove hardcoded secret API key from source code.

This exposes a live Stripe secret key in version control, allowing anyone with repository access to make unauthorized payment operations. This violates security best practices and the coding guidelines.

Pass the API key via environment variables, secure storage, or dependency injection at runtime.

🔒 Proposed fix to inject API key
 class PaymentHelper {
-  final String apiKey = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc'; // hardcoded secret
+  final String apiKey;
   final String baseUrl;

-  PaymentHelper({required this.baseUrl});
+  PaymentHelper({required this.baseUrl, required this.apiKey});

Then pass the key from a secure source (e.g., const String.fromEnvironment('STRIPE_API_KEY') or a secrets manager).

As per coding guidelines: "Never hardcode API keys in source code."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
final String apiKey = 'sk_live_4eC39HqLyjWDarjtT1zdp7dc'; // hardcoded secret
class PaymentHelper {
final String apiKey;
final String baseUrl;
PaymentHelper({required this.baseUrl, required this.apiKey});
🧰 Tools
🪛 Betterleaks (1.3.1)

[high] 6-6: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/utils/payment_helper.dart` at line 6, The code exposes a hardcoded Stripe
secret in the variable apiKey in payment_helper.dart; remove the literal and
instead read the key from a secure source (environment variable or injected
secret) and pass it into the payment helper via dependency injection or a
constructor parameter; specifically replace references to the top-level apiKey
with a runtime-supplied value (e.g., load from Platform.environment, const
String.fromEnvironment('STRIPE_API_KEY'), or a secrets manager/dotenv) and
update any functions or classes that rely on apiKey to accept the key as an
argument (e.g., constructor parameter or method param) so the secret is never
stored in source.

final String baseUrl;

PaymentHelper({required this.baseUrl});

/// Process a payment - no input validation
Future<Map<String, dynamic>> processPayment(String userId, double amount) async {
// Bug: no null check on amount, negative amounts allowed
final response = await http.post(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Major | ⚡ Quick win
What's happening:

  • 🏗️ [ARCHITECTURE] Lack of proper abstraction and high coupling: The PaymentHelper class directly uses http package for making API calls.

Recommendation:

  • Apply the suggested fix below
- final response = await http.post(
Suggested change
final response = await http.post(
final response = await PaymentApiClient.post(
🤖 Prompt for AI Agents
There is a high issue in `lib/utils/payment_helper.dart` at line 14.

## Issue
🏗️ [ARCHITECTURE] Lack of proper abstraction and high coupling: The PaymentHelper class directly uses http package for making API calls.

## Current Code

final response = await http.post(


## Suggested Fix

final response = await PaymentApiClient.post(


## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 14. Apply the suggested fix above, ensuring it integrates correctly with the surrounding code. Verify the fix doesn't break any existing functionality.

Uri.parse('$baseUrl/payments'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
},
body: jsonEncode({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical
What's happening:

  • 🚨 [SECURITY VULNERABILITY] CWE-20: Improper Input Validation. The amount parameter is sent without checking that it is positive, allowing negative payments which could be exploited for unauthorized refunds or free credits.

Recommendation:

  • Apply the suggested fix below
- body: jsonEncode({
        'user_id': userId,
        'amount': amount, // allows negative amounts = free money
        'currency': 'usd',
      }),
Suggested change
body: jsonEncode({
if (amount <= 0) { throw ArgumentError('Amount must be positive'); }
body: jsonEncode({
'user_id': userId,
'amount': amount,
'currency': 'usd',
}),
🤖 Prompt for AI Agents
There is a critical issue in `lib/utils/payment_helper.dart` at line 20.

## Issue
🚨 [SECURITY VULNERABILITY] CWE-20: Improper Input Validation. The amount parameter is sent without checking that it is positive, allowing negative payments which could be exploited for unauthorized refunds or free credits.

## Current Code

body: jsonEncode({
'user_id': userId,
'amount': amount, // allows negative amounts = free money
'currency': 'usd',
}),


## Suggested Fix

if (amount <= 0) { throw ArgumentError('Amount must be positive'); }
body: jsonEncode({
'user_id': userId,
'amount': amount,
'currency': 'usd',
}),


## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 20. Apply the suggested fix above, ensuring it integrates correctly with the surrounding code. Verify the fix doesn't break any existing functionality.

'user_id': userId,
'amount': amount, // allows negative amounts = free money
'currency': 'usd',
}),
);

if (response.statusCode == 200) {
return jsonDecode(response.body);
}
// Bug: swallows all errors silently, returns empty map
return {};
Comment on lines +12 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Validate payment amount and handle errors properly.

Two serious issues here:

  1. Negative amounts allowed – Attackers or buggy clients can submit negative amounts, potentially crediting funds instead of charging.
  2. Silent error swallowing – Returning {} on failure hides payment errors from callers, leading to incorrect state (e.g., order confirmed but payment failed).
🛠️ Proposed fix with validation and error handling
   Future<Map<String, dynamic>> processPayment(String userId, double amount) async {
-    // Bug: no null check on amount, negative amounts allowed
+    if (amount <= 0) {
+      throw ArgumentError('Amount must be positive');
+    }
+    if (userId.isEmpty) {
+      throw ArgumentError('userId cannot be empty');
+    }
+
+    final http.Response response;
+    try {
-    final response = await http.post(
+      response = await http.post(
         Uri.parse('$baseUrl/payments'),
         headers: {
           'Authorization': 'Bearer $apiKey',
           'Content-Type': 'application/json',
         },
         body: jsonEncode({
           'user_id': userId,
-          'amount': amount, // allows negative amounts = free money
+          'amount': amount,
           'currency': 'usd',
         }),
-      );
+      ).timeout(const Duration(seconds: 30));
+    } catch (e) {
+      throw Exception('Payment request failed: $e');
+    }

     if (response.statusCode == 200) {
       return jsonDecode(response.body);
     }
-    // Bug: swallows all errors silently, returns empty map
-    return {};
+    throw Exception('Payment failed with status ${response.statusCode}: ${response.body}');
   }

As per coding guidelines: "Implement mechanisms to gracefully handle errors across the application" and "Ensure proper asynchronous error handling by adding try-catch blocks for Future operations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/utils/payment_helper.dart` around lines 12 - 31, processPayment currently
accepts null/negative amounts and swallows HTTP errors; add upfront validation
to ensure the amount parameter is non-null and greater than 0 (e.g., throw
ArgumentError or return a failed result) before creating the request, then wrap
the HTTP call in a try-catch and handle non-200 response codes by returning or
throwing a descriptive error (include response.statusCode and response.body) and
log the error using your logger; reference the processPayment function, the
http.post call, baseUrl/apiKey usage, and the response.statusCode/response.body
when implementing these changes.

}

/// Refund payment - builds URL unsafely
Future<void> refundPayment(String transactionId) async {
// Injection: user input directly in URL
final url = '$baseUrl/refund?tx=$transactionId&force=true';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor | ⚡ Quick win
What's happening:

  • 🚨 [SECURITY VULNERABILITY] CWE-74: Improper Neutralization of Special Elements used in an URL. Directly embedding transactionId into the query string can allow an attacker to inject additional parameters or manipulate the request.

Recommendation:

  • Apply the suggested fix below
- final url = '$baseUrl/refund?tx=$transactionId&force=true';
Suggested change
final url = '$baseUrl/refund?tx=$transactionId&force=true';
final uri = Uri.parse('$baseUrl/refund').replace(queryParameters: {'tx': transactionId, 'force': 'true'});
await http.post(uri);
🤖 Prompt for AI Agents
There is a medium issue in `lib/utils/payment_helper.dart` at line 37.

## Issue
🚨 [SECURITY VULNERABILITY] CWE-74: Improper Neutralization of Special Elements used in an URL. Directly embedding transactionId into the query string can allow an attacker to inject additional parameters or manipulate the request.

## Current Code

final url = '$baseUrl/refund?tx=$transactionId&force=true';


## Suggested Fix

final uri = Uri.parse('$baseUrl/refund').replace(queryParameters: {'tx': transactionId, 'force': 'true'});
await http.post(uri);


## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 37. Apply the suggested fix above, ensuring it integrates correctly with the surrounding code. Verify the fix doesn't break any existing functionality.

await http.post(Uri.parse(url));
// Bug: ignores response status entirely
}
Comment on lines +35 to +40

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: URL injection vulnerability and missing response validation.

Two issues:

  1. Injection risktransactionId is interpolated directly into the URL. A malicious input like abc&admin=true could manipulate query parameters.
  2. No response validation – Refund success/failure is ignored, leaving callers unaware of failures.
🛠️ Proposed fix with safe URL construction and response handling
   Future<void> refundPayment(String transactionId) async {
-    // Injection: user input directly in URL
-    final url = '$baseUrl/refund?tx=$transactionId&force=true';
-    await http.post(Uri.parse(url));
-    // Bug: ignores response status entirely
+    if (transactionId.isEmpty) {
+      throw ArgumentError('transactionId cannot be empty');
+    }
+
+    final uri = Uri.parse('$baseUrl/refund').replace(
+      queryParameters: {'tx': transactionId, 'force': 'true'},
+    );
+
+    final response = await http.post(
+      uri,
+      headers: {'Authorization': 'Bearer $apiKey'},
+    ).timeout(const Duration(seconds: 30));
+
+    if (response.statusCode != 200) {
+      throw Exception('Refund failed with status ${response.statusCode}: ${response.body}');
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/utils/payment_helper.dart` around lines 35 - 40, The refundPayment
function currently interpolates transactionId into the URL (injection risk) and
ignores the HTTP response; fix by building the request with Uri(queryParameters:
...) or by encoding the id via Uri.encodeQueryComponent(transactionId) and
passing a proper Uri to http.post instead of string concatenation, then await
and inspect the http.Response from http.post (check response.statusCode for
success 2xx, handle non-2xx by throwing a descriptive exception or returning an
error result and include relevant response.body/error details), and ensure any
thrown errors are propagated so callers know when refund failed; reference the
refundPayment method and the variables baseUrl, transactionId, and the http.post
call when making the changes.


/// Get payment history - no pagination, loads everything
Future<List<dynamic>> getHistory(String userId) async {
final response = await http.get(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor
What's happening:

  • 🚨 [SECURITY VULNERABILITY] CWE-400: Uncontrolled Resource Consumption. The method retrieves the entire payment history without pagination or size limits and lacks a request timeout, enabling potential denial-of-service attacks by forcing large responses or hanging connections.

Recommendation:

  • Apply the suggested fix below
- final response = await http.get(
       Uri.parse('$baseUrl/history?user=$userId'),
       headers: {'Authorization': 'Bearer $apiKey'},
     );
     // Bug: no timeout, can hang forever
// ...
Suggested change
final response = await http.get(
final uri = Uri.parse('$baseUrl/history').replace(queryParameters: {'user': userId, 'limit': '100'});
final response = await http.get(uri, headers: {'Authorization': 'Bearer $apiKey'}).timeout(const Duration(seconds: 10));
if (response.statusCode != 200) { throw HttpException('Failed to fetch history'); }
return jsonDecode(response.body);
🤖 Prompt for AI Agents
There is a medium issue in `lib/utils/payment_helper.dart` at line 44.

## Issue
🚨 [SECURITY VULNERABILITY] CWE-400: Uncontrolled Resource Consumption. The method retrieves the entire payment history without pagination or size limits and lacks a request timeout, enabling potential denial-of-service attacks by forcing large responses or hanging connections.

## Current Code

final response = await http.get(
Uri.parse('$baseUrl/history?user=$userId'),
headers: {'Authorization': 'Bearer $apiKey'},
);
// Bug: no timeout, can hang forever
// ...


## Suggested Fix

final uri = Uri.parse('$baseUrl/history').replace(queryParameters: {'user': userId, 'limit': '100'});
final response = await http.get(uri, headers: {'Authorization': 'Bearer $apiKey'}).timeout(const Duration(seconds: 10));
if (response.statusCode != 200) { throw HttpException('Failed to fetch history'); }
return jsonDecode(response.body);


## Instructions
Fix the issue in `lib/utils/payment_helper.dart` at line 44. Apply the suggested fix above, ensuring it integrates correctly with the surrounding code. Verify the fix doesn't break any existing functionality.

Uri.parse('$baseUrl/history?user=$userId'),
headers: {'Authorization': 'Bearer $apiKey'},
);
// Bug: no timeout, can hang forever
// Bug: no error handling at all
return jsonDecode(response.body);
}
Comment on lines +43 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add timeout, error handling, and safe URL construction for getHistory.

This method has multiple reliability issues:

  1. No timeout – Network issues can cause indefinite hangs.
  2. No error handling – HTTP errors or JSON parse failures crash the caller.
  3. URL injectionuserId is directly interpolated without encoding.
  4. No pagination – Loading unbounded history can exhaust memory.
🛠️ Proposed fix with timeout, error handling, and safe URL
-  Future<List<dynamic>> getHistory(String userId) async {
-    final response = await http.get(
-      Uri.parse('$baseUrl/history?user=$userId'),
+  Future<List<dynamic>> getHistory(String userId, {int limit = 100, int offset = 0}) async {
+    if (userId.isEmpty) {
+      throw ArgumentError('userId cannot be empty');
+    }
+
+    final uri = Uri.parse('$baseUrl/history').replace(
+      queryParameters: {
+        'user': userId,
+        'limit': limit.toString(),
+        'offset': offset.toString(),
+      },
+    );
+
+    final http.Response response;
+    try {
+      response = await http.get(
+        uri,
         headers: {'Authorization': 'Bearer $apiKey'},
-      );
-    // Bug: no timeout, can hang forever
-    // Bug: no error handling at all
-    return jsonDecode(response.body);
+      ).timeout(const Duration(seconds: 30));
+    } catch (e) {
+      throw Exception('Failed to fetch payment history: $e');
+    }
+
+    if (response.statusCode != 200) {
+      throw Exception('History request failed with status ${response.statusCode}');
+    }
+
+    return jsonDecode(response.body) as List<dynamic>;
   }

As per coding guidelines: "Ensure proper asynchronous error handling by adding try-catch blocks for Future operations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/utils/payment_helper.dart` around lines 43 - 51, The getHistory method
currently interpolates userId into the URL, makes an unbounded http.get call and
directly jsonDecodes the response; update getHistory to build the request URL
using Uri.https/Uri.parse with proper encoding of userId and accept optional
pagination params (e.g., limit/offset), call http.get with a
.timeout(Duration...) to avoid hangs, wrap the network and json parsing in a
try-catch and handle non-200 responses (using response.statusCode) by returning
a safe default (e.g., empty List) or throwing a descriptive exception, and catch
FormatException/SocketException/TimeoutException to avoid crashing callers;
reference the existing symbols getHistory, baseUrl, and apiKey when making these
changes.

}