Skip to content

[HEL-4841] | Example project - Introduced new module for the example project#72

Draft
koyinusa wants to merge 1 commit into
mainfrom
feature/HEL-4841-exampleProject
Draft

[HEL-4841] | Example project - Introduced new module for the example project#72
koyinusa wants to merge 1 commit into
mainfrom
feature/HEL-4841-exampleProject

Conversation

@koyinusa

@koyinusa koyinusa commented Mar 9, 2026

Copy link
Copy Markdown
Collaborator

Note

Medium Risk
Medium risk because it changes iOS build configuration and upgrades CocoaPods/RevenueCat dependencies, which can break builds or runtime linking. Dart changes are confined to the example app’s initialization and demo UI flows.

Overview
The example app now uses the new helium_stripe module: main.dart initializes Helium via HeliumStripe.initializeWithStripe using Stripe-related .env keys, and HomePage adds a Stripe demo section (sync user, check entitlements, create portal session, reset entitlements) plus updates the upsell trigger to stripe.

On iOS, CocoaPods configuration is refreshed: adds the Helium pod, bumps purchases_flutter/PurchasesHybridCommon/RevenueCat versions, adds missing pods (e.g., integration_test, path_provider_foundation), adds the [CP] Embed Pods Frameworks build phase, and removes committed SwiftPM Package.resolved files.

Written by Cursor Bugbot for commit 37596cb. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Integrated Stripe payment processing with entitlement management capabilities
    • Added UI controls for Stripe operations: user sync, entitlement checks, customer portal sessions, and entitlement resets
  • Refactor

    • Migrated payment system initialization with updated configuration parameters

@koyinusa koyinusa marked this pull request as draft March 9, 2026 15:32
@coderabbitai

coderabbitai Bot commented Mar 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR integrates Stripe payment processing into the Flutter example application, replacing the previous HeliumFlutter setup. Changes include iOS Xcode project build phase updates, Dart initialization migration from HeliumFlutter to HeliumStripe with new configuration parameters, and expanded UI with Stripe-specific user management and entitlement controls.

Changes

Cohort / File(s) Summary
iOS Build Configuration
example/ios/Runner.xcodeproj/project.pbxproj
Adds new [CP] Embed Pods Frameworks build phase to handle framework embedding, and updates XCLocalSwiftPackageReference from path-based to simplified reference for FlutterGeneratedPluginSwiftPackage.
Package Resolution
example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved, example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
Deletes Swift Package Manager resolution state for helium-swift and purchases-ios-spm packages, clearing pinned versions and metadata.
Dart App Initialization
example/lib/main.dart
Replaces HeliumFlutter initialization with HeliumStripe initialization, removing HeliumFlutter imports and adding Stripe configuration parameters (publishable key, merchant identifier, name, management URL).
UI & Dependency Management
example/lib/presentation/home_page.dart, example/pubspec.yaml
Adds helium_stripe dependency and introduces new Stripe UI section with controls for user management, entitlement checking, portal session creation, and entitlement reset; updates presentUpsell trigger parameter from 'sdk_test' to 'stripe'.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related PRs

Suggested reviewers

  • salami

Poem

🐰 Hop! A Stripe integration, so fine,
Payment flows renewed, the config realigned,
HeliumFlutter fades, HeliumStripe shines bright,
Merchants and keys dance in the iOS night! 💳✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions 'Introduced new module' but the actual changes primarily involve replacing HeliumFlutter with HeliumStripe integration across the example project, which is the main objective. Consider revising the title to specifically mention 'Integrate Stripe payment integration in example project' or similar to accurately reflect the primary changes made.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/HEL-4841-exampleProject

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

},
child: Text('Open RevenueCat Paywall'),
),
SizedBox(height: 16),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reset Helium loses Stripe integration after reinitialization

Medium Severity

The "Reset Helium" button re-initializes Helium via _heliumFlutterPlugin.initialize() with only apiKey, while the app's startup now uses HeliumStripe.initializeWithStripe() with full Stripe configuration. After pressing reset, Stripe integration (publishable key, merchant ID, etc.) is lost, and the newly added Stripe buttons (set user, check entitlement, portal session, reset entitlements) would fail or behave incorrectly.

Additional Locations (1)

Fix in Cursor Fix in Web

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
example/lib/main.dart (1)

35-39: 🧹 Nitpick | 🔵 Trivial

Error handling rethrows exceptions instead of handling them defensively.

The current pattern catches exceptions only to rethrow them, which doesn't add value. Per coding guidelines, consider implementing defensive error handling with logging and graceful degradation.

🛡️ Suggested defensive handling
   try {
     await HeliumStripe.initializeWithStripe(
       apiKey: apiKey,
       stripePublishableKey: stripePublishableKey,
       merchantIdentifier: merchantIdentifier,
       merchantName: merchantName,
       managementURL: managementURL,
       callbacks: LogCallbacks(),
     );
   } on PlatformException catch (e) {
-    rethrow;
+    debugPrint('[HeliumStripe] Platform initialization error: ${e.message}');
+    // App can still run with degraded Stripe functionality
   } catch (e) {
-    rethrow;
+    debugPrint('[HeliumStripe] Unexpected initialization error: $e');
+    // App can still run with degraded Stripe functionality
   }

As per coding guidelines: "Implement defensive error handling with try/catch and backup logic instead of allowing exceptions to propagate, prioritizing stability over crash prevention."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/lib/main.dart` around lines 35 - 39, The current try/catch with "on
PlatformException { rethrow; } catch (e) { rethrow; }" should be replaced with
defensive handling: in the "on PlatformException" handler and the generic "catch
(e)" handler, log the error (include the exception and stacktrace) and implement
graceful fallback logic (e.g., return a sensible default, null, or trigger
retry/alternate code path) instead of rethrowing; update the enclosing function
to support the fallback return type or state and ensure the handlers use a
logging mechanism (print/logger) and/or user-friendly error reporting before
exiting the handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@example/lib/main.dart`:
- Around line 21-24: Validate the required Stripe environment values at startup
by checking each variable (stripePublishableKey, merchantIdentifier,
merchantName, managementURL) for null or empty string and fail fast with a clear
error/exception or process exit; replace the current "?? ''" fallback with a
presence check (e.g., if (stripePublishableKey == null ||
stripePublishableKey.isEmpty) throw/ log a descriptive error) for each variable
in the main.dart startup/init code so the app surfaces a clear message when the
.env is misconfigured.

In `@example/lib/presentation/home_page.dart`:
- Around line 150-157: Wrap the async call inside the onPressed handler in a
try/catch around the HeliumStripe.createStripePortalSession call so exceptions
don't propagate; on success keep the existing log and SnackBar behavior, and in
the catch block log the error (including stack) and show a user-friendly
SnackBar fallback (e.g., "Failed to open billing portal" or null URL handling)
while preserving the context.mounted checks; update the closure where onPressed
calls HeliumStripe.createStripePortalSession to implement this defensive error
handling.
- Around line 137-145: Wrap the async call inside the onPressed callback with a
try/catch around HeliumStripe.hasActiveStripeEntitlement() to prevent unhandled
exceptions; on success keep the existing log and SnackBar, on failure log the
caught error (including stack) and show a user-friendly SnackBar via
ScaffoldMessenger.of(context).showSnackBar with a fallback message and treat
hasEntitlement as false (or other safe default) so the UI has deterministic
behavior.

---

Outside diff comments:
In `@example/lib/main.dart`:
- Around line 35-39: The current try/catch with "on PlatformException { rethrow;
} catch (e) { rethrow; }" should be replaced with defensive handling: in the "on
PlatformException" handler and the generic "catch (e)" handler, log the error
(include the exception and stacktrace) and implement graceful fallback logic
(e.g., return a sensible default, null, or trigger retry/alternate code path)
instead of rethrowing; update the enclosing function to support the fallback
return type or state and ensure the handlers use a logging mechanism
(print/logger) and/or user-friendly error reporting before exiting the handler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 215cc9ca-6fbe-4b2a-8b90-7c4d5f8ed8a1

📥 Commits

Reviewing files that changed from the base of the PR and between ead93a7 and 37596cb.

⛔ Files ignored due to path filters (1)
  • example/ios/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • example/ios/Runner.xcodeproj/project.pbxproj
  • example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/lib/main.dart
  • example/lib/presentation/home_page.dart
  • example/pubspec.yaml
💤 Files with no reviewable changes (2)
  • example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved

Comment thread example/lib/main.dart
Comment on lines +21 to +24
final stripePublishableKey = dotenv.env['STRIPE_PUBLISHABLE_KEY'] ?? '';
final merchantIdentifier = dotenv.env['STRIPE_MERCHANT_IDENTIFIER'] ?? '';
final merchantName = dotenv.env['STRIPE_MERCHANT_NAME'] ?? '';
final managementURL = dotenv.env['STRIPE_MANAGEMENT_URL'] ?? '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider validation for required Stripe configuration values.

Empty string fallbacks for required Stripe parameters (stripePublishableKey, merchantIdentifier, merchantName, managementURL) could cause silent failures or unclear errors at runtime if the .env file is misconfigured.

🛡️ Suggested validation approach
   final apiKey = dotenv.env['API_KEY'] ?? '';
   final stripePublishableKey = dotenv.env['STRIPE_PUBLISHABLE_KEY'] ?? '';
   final merchantIdentifier = dotenv.env['STRIPE_MERCHANT_IDENTIFIER'] ?? '';
   final merchantName = dotenv.env['STRIPE_MERCHANT_NAME'] ?? '';
   final managementURL = dotenv.env['STRIPE_MANAGEMENT_URL'] ?? '';

+  if (apiKey.isEmpty || stripePublishableKey.isEmpty || managementURL.isEmpty) {
+    debugPrint('[HeliumStripe] Warning: Required Stripe configuration values are missing');
+    return;
+  }
+
   try {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/lib/main.dart` around lines 21 - 24, Validate the required Stripe
environment values at startup by checking each variable (stripePublishableKey,
merchantIdentifier, merchantName, managementURL) for null or empty string and
fail fast with a clear error/exception or process exit; replace the current "??
''" fallback with a presence check (e.g., if (stripePublishableKey == null ||
stripePublishableKey.isEmpty) throw/ log a descriptive error) for each variable
in the main.dart startup/init code so the app surfaces a clear message when the
.env is misconfigured.

Comment on lines +137 to +145
onPressed: () async {
final hasEntitlement = await HeliumStripe.hasActiveStripeEntitlement();
log('[HeliumStripe] hasActiveStripeEntitlement: $hasEntitlement');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Active Stripe Entitlement: $hasEntitlement')),
);
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider wrapping async call in try/catch for defensive error handling.

While the underlying hasActiveStripeEntitlement method handles exceptions internally, adding a try/catch here would provide an additional safety layer and allow for user-friendly error messaging in the UI.

🛡️ Suggested defensive handling
             ElevatedButton(
               onPressed: () async {
+                try {
                   final hasEntitlement = await HeliumStripe.hasActiveStripeEntitlement();
                   log('[HeliumStripe] hasActiveStripeEntitlement: $hasEntitlement');
                   if (context.mounted) {
                     ScaffoldMessenger.of(context).showSnackBar(
                       SnackBar(content: Text('Active Stripe Entitlement: $hasEntitlement')),
                     );
                   }
+                } catch (e) {
+                  log('[HeliumStripe] Error checking entitlement: $e');
+                  if (context.mounted) {
+                    ScaffoldMessenger.of(context).showSnackBar(
+                      SnackBar(content: Text('Error checking entitlement')),
+                    );
+                  }
+                }
               },

As per coding guidelines: "Implement defensive error handling with try/catch and backup logic instead of allowing exceptions to propagate."

📝 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
onPressed: () async {
final hasEntitlement = await HeliumStripe.hasActiveStripeEntitlement();
log('[HeliumStripe] hasActiveStripeEntitlement: $hasEntitlement');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Active Stripe Entitlement: $hasEntitlement')),
);
}
},
onPressed: () async {
try {
final hasEntitlement = await HeliumStripe.hasActiveStripeEntitlement();
log('[HeliumStripe] hasActiveStripeEntitlement: $hasEntitlement');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Active Stripe Entitlement: $hasEntitlement')),
);
}
} catch (e) {
log('[HeliumStripe] Error checking entitlement: $e');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error checking entitlement')),
);
}
}
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/lib/presentation/home_page.dart` around lines 137 - 145, Wrap the
async call inside the onPressed callback with a try/catch around
HeliumStripe.hasActiveStripeEntitlement() to prevent unhandled exceptions; on
success keep the existing log and SnackBar, on failure log the caught error
(including stack) and show a user-friendly SnackBar via
ScaffoldMessenger.of(context).showSnackBar with a fallback message and treat
hasEntitlement as false (or other safe default) so the UI has deterministic
behavior.

Comment on lines +150 to +157
onPressed: () async {
final url = await HeliumStripe.createStripePortalSession('heliumexample://stripe-return');
log('[HeliumStripe] Portal session URL: $url');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Portal URL: ${url ?? "null"}')),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider wrapping async call in try/catch for defensive error handling.

Similar to the entitlement check, adding defensive error handling here would prevent unhandled exceptions and provide better UX.

🛡️ Suggested defensive handling
             ElevatedButton(
               onPressed: () async {
+                try {
                   final url = await HeliumStripe.createStripePortalSession('heliumexample://stripe-return');
                   log('[HeliumStripe] Portal session URL: $url');
                   if (context.mounted) {
                     ScaffoldMessenger.of(context).showSnackBar(
                       SnackBar(content: Text('Portal URL: ${url ?? "null"}')),
                     );
                   }
+                } catch (e) {
+                  log('[HeliumStripe] Error creating portal session: $e');
+                  if (context.mounted) {
+                    ScaffoldMessenger.of(context).showSnackBar(
+                      SnackBar(content: Text('Error creating portal session')),
+                    );
+                  }
+                }
               },

As per coding guidelines: "Implement defensive error handling with try/catch and backup logic instead of allowing exceptions to propagate."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/lib/presentation/home_page.dart` around lines 150 - 157, Wrap the
async call inside the onPressed handler in a try/catch around the
HeliumStripe.createStripePortalSession call so exceptions don't propagate; on
success keep the existing log and SnackBar behavior, and in the catch block log
the error (including stack) and show a user-friendly SnackBar fallback (e.g.,
"Failed to open billing portal" or null URL handling) while preserving the
context.mounted checks; update the closure where onPressed calls
HeliumStripe.createStripePortalSession to implement this defensive error
handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant