Skip to content

Affiliate Flow#8885

Open
jigar-f wants to merge 20 commits into
mainfrom
jigar/affiliate-flow
Open

Affiliate Flow#8885
jigar-f wants to merge 20 commits into
mainfrom
jigar/affiliate-flow

Conversation

@jigar-f

@jigar-f jigar-f commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces support for promo/referral codes during the subscription and payment flows, and adds a new version of the referral code attachment API. It updates both Android and iOS code to pass coupon codes through the payment and purchase acknowledgment processes, updates the core Go interfaces and FFI bindings to support these changes, and adds new localization strings for promo code UI elements. Dependency versions are also updated.

Promo/Referral Code Support in Payments and Subscriptions:

  • Android (MethodHandler.kt): Modified payment and purchase acknowledgment methods to accept and forward a couponCode parameter. [1] [2] [3]
  • iOS (MethodHandler.swift): Updated acknowledgeInAppPurchase to accept and forward a couponCode. [1] [2]
  • Go core and FFI (core.go, ffi.go, mobile.go): Updated payment and subscription methods to accept and forward couponCode parameters throughout the stack, including Stripe and payment redirect flows. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Referral Code Attachment V2:

  • Android: Added a new method AttachReferralCodeV2 to the Methods enum and implemented its handler, which calls the new Mobile.referralAttachmentV2. [1] [2]
  • iOS: Added a handler for "attachReferralCodeV2" and implemented referralAttachV2 to call the new mobile core method. [1] [2]
  • Go core and FFI: Added ReferralAttachmentV2 methods to the core interface and FFI, returning detailed JSON about the referral attachment. [1] [2] [3]

Localization and UI Strings:

  • Added new localization strings for promo/referral code UI elements and discount messaging. [1] [2]
  • Updated the subscription renewal info string for clarity.

Dependency Updates:

  • Updated several Go module dependencies to newer versions, including domainfront, kindling, and lantern-box.

These changes collectively enable promo/referral code entry and attribution throughout the payment flows, improve referral code handling, and prepare the UI for these new features.

Summary by CodeRabbit

  • New Features
    • Added “Referral v2” attachment (including distribution channel) and coupon-code support across checkout, subscription, and payment redirect flows.
    • Updated the plans UI to show original (pre-discount) pricing, promo/referral discount rows, and the localized order total.
    • Added affiliate/referral display support and banner/chip removal behavior for active discount states.
  • Bug Fixes
    • Improved referral error matching so invalid codes show consistent messaging.
    • Enhanced purchase acknowledgement and restore flows to improve reliability, retries, and completion.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds referral attach V2 support, coupon-code propagation, and affiliate discount UI updates across native, Go, Flutter, and purchase flows. Also adjusts dialog sizing, semantic colors, and one plans feature tile.

Changes

Referral V2 and coupon code feature

Layer / File(s) Summary
Native method handlers and gomobile bridges
android/.../MethodHandler.kt, ios/Runner/Handlers/MethodHandler.swift, macos/Runner/Handlers/MethodHandler.swift
Adds attachReferralCodeV2/referralAttachV2 handlers and forwards couponCode through acknowledgment and redirect calls on Android, iOS, and macOS.
Go core and FFI coupon forwarding
lantern-core/core.go, lantern-core/ffi/ffi.go, lantern-core/mobile/mobile.go
Extends App/Payment interfaces and implementations with ReferralAttachmentV2 and couponCode parameters, and adds exported FFI/gomobile wrappers.
Flutter service wiring and data contracts
lib/lantern/lantern_service.dart, lib/lantern/lantern_ffi_service.dart, lib/lantern/lantern_platform_service.dart, lib/lantern/lantern_core_service.dart, lib/core/models/referral_attach_response.dart, lib/core/models/plan_data.dart, lib/core/extensions/plan.dart, lib/core/extensions/error.dart, lib/features/plans/provider/*, generated .g.dart files
Introduces attachReferralCodeV2, referral response models, sortPlansAndProviders(), original-price/discount getters, referral notifier state, and coupon-code parameters wired through payment and plan providers.
Purchase acknowledgment and pending store refactor
lib/core/services/app_purchase.dart, lib/core/services/purchase/pending_purchase_store.dart, lib/core/services/purchase/purchase_acknowledger.dart, test/core/services/purchase/*
Adds PendingPurchaseStore and PurchaseAcknowledger for persisting purchase metadata and retrying receipt acknowledgment, and refactors AppPurchase into a session-based purchase/restore flow with injectable collaborators and tests.
Plans and checkout UI for affiliate discounts
lib/features/plans/plans.dart, lib/features/plans/plans_list.dart, lib/features/plans/plan_item.dart, lib/features/auth/choose_payment_method.dart, assets/locales/en.po
Adds an affiliate banner, original/discounted price display, promo-code deduction row, referral dialog wording fixes, and localized checkout strings.

Estimated code review effort: 4 (Complex) | ~75 minutes

Dialog and semantic color updates

Layer / File(s) Summary
Dialog width constraints and semantic colors
lib/core/common/app_dialog.dart, lib/core/common/app_theme.dart, lib/core/common/app_semantic_colors.dart, lib/features/plans/feature_list.dart
Adds min/max dialog width constraints, wraps dialog content in a finite-width box, adds three attention semantic color getters, and removes one feature list tile.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: atavism

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and doesn't clearly describe the main changes to promo codes, referral attachment V2, and payment flow updates. Use a specific title like 'Add promo code support and referral attachment V2 to payment flows'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jigar/affiliate-flow

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.

@jigar-f jigar-f changed the title Started working on referral UI Affiliate Flow Jun 24, 2026
@jigar-f jigar-f self-assigned this Jun 24, 2026
@jigar-f jigar-f marked this pull request as ready for review July 9, 2026 07:32
Copilot AI review requested due to automatic review settings July 9, 2026 07:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds end-to-end promo/referral (coupon) code support across the subscription/payment flows (Flutter → platform channels → Go core/FFI), introduces a V2 referral attachment API that returns discounted plan data, and updates the plans UI to reflect affiliate discounts. It also refactors purchase acknowledgment into testable components, updates localizations, and bumps Go dependencies.

Changes:

  • Thread couponCode through IAP start + acknowledgment, Stripe flows, and payment redirects on Android/iOS/macOS/FFI/Go core.
  • Add referral attachment V2 (attachReferralCodeV2) returning structured JSON (plans/providers/discount) and update plans UI to show affiliate pricing + removal flow.
  • Extract and test purchase acknowledgment + pending purchase persistence, and centralize plans sorting.

Reviewed changes

Copilot reviewed 35 out of 41 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/core/services/purchase/purchase_acknowledger_test.dart New unit tests for acknowledgment state machine (retry/dedup/error handling).
test/core/services/purchase/pending_purchase_store_test.dart New unit tests for persisted plan/coupon attribution metadata.
macos/Runner/Handlers/MethodHandler.swift Pass couponCode into Apple purchase acknowledgment; add attachReferralCodeV2 handler.
ios/Runner/Handlers/MethodHandler.swift Pass couponCode into Apple purchase acknowledgment; add attachReferralCodeV2 handler.
android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt Thread couponCode into mobile calls; add AttachReferralCodeV2 method implementation.
lib/lantern/lantern_service.dart Add couponCode parameters to core service methods; expose attachReferralCodeV2.
lib/lantern/lantern_platform_service.dart Forward couponCode through method-channel payment APIs; add platform attachReferralCodeV2.
lib/lantern/lantern_ffi_service.dart Forward couponCode for redirect flows; add FFI-backed attachReferralCodeV2.
lib/lantern/lantern_core_service.dart Extend core service interface with couponCode and V2 referral attachment response type.
lib/features/plans/provider/referral_notifier.dart Switch referral state to hold V2 response and update plans on success.
lib/features/plans/provider/referral_notifier.g.dart Generated Riverpod updates for new referral notifier state type.
lib/features/plans/provider/plans_notifier.dart Add updatePlans and publish state after fetch to unblock callers.
lib/features/plans/provider/plans_notifier.g.dart Generated hash update after notifier changes.
lib/features/plans/provider/payment_notifier.dart Thread couponCode through purchase/redirect/subscription calls; simplify store-build detection.
lib/features/plans/provider/payment_notifier.g.dart Generated hash update after payment notifier changes.
lib/features/plans/plans.dart Add affiliate banner + removal flow; apply referral V2; forward applied code into IAP flow.
lib/features/plans/plans_list.dart Update plan selection logic and pass affiliate discount data to plan items.
lib/features/plans/plan_item.dart Display original (strikethrough) price when affiliate discount applies.
lib/features/plans/feature_list.dart Minor feature list content/formatting changes.
lib/features/auth/choose_payment_method.dart Forward affiliate coupons into Stripe flows; update checkout line items and labels.
lib/core/services/purchase/purchase_acknowledger.dart New acknowledgment state machine (dedupe + retry) with injected collaborators.
lib/core/services/purchase/pending_purchase_store.dart New persistence layer for pending plan/coupon attribution keyed by txn/product.
lib/core/services/app_purchase.dart Integrate pending-store + acknowledger; support offer SKU loading; thread coupon attribution.
lib/core/models/referral_attach_response.dart New V2 referral attachment response model + null-safe state extension.
lib/core/models/plan_data.dart Add shared sortPlansAndProviders; add original-price fields for discounted display.
lib/core/extensions/plan.dart Add formatting helpers for original price + discount amount based on backend fields.
lib/core/extensions/error.dart Map new backend invalid-referral signal to localized invalid referral message.
lib/core/common/app_theme.dart Constrain dialog width globally per Material guidelines.
lib/core/common/app_semantic_colors.dart Add semantic “attention” colors used for affiliate promo UI.
lib/core/common/app_dialog.dart Prevent custom dialog content from collapsing by forcing max width usage.
assets/locales/en.po Add promo/affiliate UI strings; update renewal info and new order total label.
lantern-core/core.go Add couponCode to payment APIs; add ReferralAttachmentV2 returning JSON bytes.
lantern-core/mobile/mobile.go Propagate couponCode into mobile-exposed Go functions; add referral attachment V2.
lantern-core/ffi/ffi.go Extend exported C ABI to accept couponCode and add referralAttachmentV2.
go.mod Update replace directives (incl. radiance) and dependency versions.
go.sum Dependency checksum updates.
lib/features/vpn/provider/available_servers_notifier.g.dart Generated hash update.
lib/features/report_issue/provider/report_issue_draft_notifier.g.dart Generated hash update.
lib/features/home/provider/country_code_notifier.g.dart Generated hash update.
lib/features/home/provider/app_event_notifier.g.dart Generated hash update.
Files not reviewed (4)
  • lib/features/home/provider/app_event_notifier.g.dart: Generated file
  • lib/features/home/provider/country_code_notifier.g.dart: Generated file
  • lib/features/plans/provider/payment_notifier.g.dart: Generated file
  • lib/features/plans/provider/plans_notifier.g.dart: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/core/services/app_purchase.dart
Comment thread lib/features/auth/choose_payment_method.dart
Comment thread lib/core/models/referral_attach_response.dart Outdated
Comment thread lib/features/plans/provider/referral_notifier.dart Outdated
Comment thread go.mod Outdated
Comment thread lib/lantern/lantern_ffi_service.dart

@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: 5

🧹 Nitpick comments (2)
lib/core/models/plan_data.dart (1)

36-45: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused icons parameter from copyWith.

copyWith accepts Map<String, List<String>>? icons but never uses it, and PlansData has no icons field. This is dead code that could mislead callers into thinking they can update icons via copyWith.

♻️ Proposed fix
 PlansData copyWith({
   Providers? providers,
   List<Plan>? plans,
-  Map<String, List<String>>? icons,
 }) {
   return PlansData(
     providers: providers ?? this.providers,
     plans: plans ?? this.plans,
   );
 }
🤖 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/core/models/plan_data.dart` around lines 36 - 45, Remove the unused icons
argument from PlansData.copyWith in plan_data.dart. The method currently accepts
Map<String, List<String>>? icons but never applies it, and PlansData has no
icons field, so update the copyWith signature to only include the actual
supported fields (like providers and plans) and keep the constructor call
aligned with those fields.
lib/features/plans/provider/plans_notifier.dart (1)

91-98: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Background refresh can overwrite referral-discounted plans.

_refreshInBackground calls fetchPlans(fromBackground: true) and then unconditionally sets state = AsyncData(remotePlans). If a referral code is applied (via updatePlans) while a background refresh is in flight, the background completion will overwrite the discounted plans with the original server plans, silently reverting the discount.

Consider guarding against this — e.g., skipping the state assignment if a referral is active, or cancelling the background refresh when updatePlans is called.

🤖 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/features/plans/provider/plans_notifier.dart` around lines 91 - 98, The
background refresh in PlansNotifier can overwrite referral-discounted plans
because _refreshInBackground() always assigns the fetched result to state after
fetchPlans(fromBackground: true). Update the PlansNotifier flow so that when
updatePlans applies a referral code, any in-flight background refresh is ignored
or cancelled, or the state assignment in _refreshInBackground is skipped
whenever a referral is currently active. Use the _refreshInBackground and
updatePlans methods as the key places to add this guard.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@lib/core/models/referral_attach_response.dart`:
- Around line 13-14: `ReferralType.fromString` is mapping every non-`'referral'`
value to `affiliate`, so `null`, empty, `"none"`, and unknown backend values are
misclassified. Update the `fromString` logic in `ReferralType` to return
`ReferralType.none` for `null`/unrecognized values, keep `'referral'` mapping to
`referral`, and only map the explicit affiliate case when the backend actually
provides it.

In `@lib/core/services/app_purchase.dart`:
- Around line 500-515: The `_handlePurchase` catch block in `AppPurchase` leaves
stale session callbacks behind after an unexpected exception, which can cause
re-delivered StoreKit transactions to trigger an abandoned flow. Update the
catch handling to follow the same capture-clear-fire pattern used in the restore
paths: clear `_session.onSuccess` and `_session.onError` before invoking any
callback, and then report the error through the captured callback or logger as
appropriate. Use the `_handlePurchase` method and `_session` callbacks as the
key touchpoints.

In `@lib/core/services/purchase/purchase_acknowledger.dart`:
- Around line 222-259: The retry loop in _scheduleRetry() for PurchaseAck can
run forever because state.attempts is never capped, so add a maximum retry limit
before scheduling the Timer. Update the retry flow in
PurchaseAckRetryState/_scheduleRetry to stop rescheduling once the cap is
reached, log a warning when retries are exhausted, and ensure the final failure
path still goes through _handleFailure() or equivalent for visibility.

In `@lib/features/auth/choose_payment_method.dart`:
- Around line 547-574: The applied promo code text in the discount summary is
using the wrong i18n key, so the referral code is not rendered. Update the
`showDiscountDeduction` UI in `choose_payment_method.dart` to use
`promo_code_with` instead of `promo_code` in the `Text` widget that formats
`referral.code`, so the placeholder is actually filled and the code appears to
the user.

In `@lib/features/plans/provider/referral_notifier.dart`:
- Around line 33-35: The referral plan lookup in `ReferralNotifier` uses
`plans.plans.firstWhere` to find the `bestValue` plan, which can crash with a
runtime `StateError` when no match exists. Update the `defaultPlan` selection to
use a safe fallback such as `firstWhereOrNull` from `package:collection` or an
`orElse` branch, and handle the null/missing-plan case before continuing the
referral flow.

---

Nitpick comments:
In `@lib/core/models/plan_data.dart`:
- Around line 36-45: Remove the unused icons argument from PlansData.copyWith in
plan_data.dart. The method currently accepts Map<String, List<String>>? icons
but never applies it, and PlansData has no icons field, so update the copyWith
signature to only include the actual supported fields (like providers and plans)
and keep the constructor call aligned with those fields.

In `@lib/features/plans/provider/plans_notifier.dart`:
- Around line 91-98: The background refresh in PlansNotifier can overwrite
referral-discounted plans because _refreshInBackground() always assigns the
fetched result to state after fetchPlans(fromBackground: true). Update the
PlansNotifier flow so that when updatePlans applies a referral code, any
in-flight background refresh is ignored or cancelled, or the state assignment in
_refreshInBackground is skipped whenever a referral is currently active. Use the
_refreshInBackground and updatePlans methods as the key places to add this
guard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 751fd42f-e728-44ce-8c04-176ca6c06c5a

📥 Commits

Reviewing files that changed from the base of the PR and between 560357e and 365ea7b.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (40)
  • android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt
  • assets/locales/en.po
  • go.mod
  • ios/Runner/Handlers/MethodHandler.swift
  • lantern-core/core.go
  • lantern-core/ffi/ffi.go
  • lantern-core/mobile/mobile.go
  • lib/core/common/app_dialog.dart
  • lib/core/common/app_semantic_colors.dart
  • lib/core/common/app_theme.dart
  • lib/core/extensions/error.dart
  • lib/core/extensions/plan.dart
  • lib/core/models/plan_data.dart
  • lib/core/models/referral_attach_response.dart
  • lib/core/services/app_purchase.dart
  • lib/core/services/purchase/pending_purchase_store.dart
  • lib/core/services/purchase/purchase_acknowledger.dart
  • lib/features/auth/choose_payment_method.dart
  • lib/features/home/provider/app_event_notifier.g.dart
  • lib/features/home/provider/country_code_notifier.g.dart
  • lib/features/plans/feature_list.dart
  • lib/features/plans/plan_item.dart
  • lib/features/plans/plans.dart
  • lib/features/plans/plans_list.dart
  • lib/features/plans/provider/payment_notifier.dart
  • lib/features/plans/provider/payment_notifier.g.dart
  • lib/features/plans/provider/plans_notifier.dart
  • lib/features/plans/provider/plans_notifier.g.dart
  • lib/features/plans/provider/referral_notifier.dart
  • lib/features/plans/provider/referral_notifier.g.dart
  • lib/features/report_issue/provider/report_issue_draft_notifier.g.dart
  • lib/features/vpn/provider/available_servers_notifier.g.dart
  • lib/lantern/lantern_core_service.dart
  • lib/lantern/lantern_ffi_service.dart
  • lib/lantern/lantern_generated_bindings.dart
  • lib/lantern/lantern_platform_service.dart
  • lib/lantern/lantern_service.dart
  • macos/Runner/Handlers/MethodHandler.swift
  • test/core/services/purchase/pending_purchase_store_test.dart
  • test/core/services/purchase/purchase_acknowledger_test.dart

Comment thread lib/core/models/referral_attach_response.dart Outdated
Comment thread lib/core/services/app_purchase.dart
Comment thread lib/core/services/purchase/purchase_acknowledger.dart
Comment thread lib/features/auth/choose_payment_method.dart
Comment thread lib/features/plans/provider/referral_notifier.dart Outdated
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.

2 participants