Skip to content

FE: Refactor checkout to create orders after Stripe PaymentSheet success#413

Open
CherryCIC wants to merge 4 commits into
mainfrom
cherry/FE.checkoutordercreation
Open

FE: Refactor checkout to create orders after Stripe PaymentSheet success#413
CherryCIC wants to merge 4 commits into
mainfrom
cherry/FE.checkoutordercreation

Conversation

@CherryCIC
Copy link
Copy Markdown
Collaborator

@CherryCIC CherryCIC commented May 1, 2026

Summary

I'm hoping this supports the frontend of #397

  • Refactors the checkout flow so the app only calls POST /api/order/create after Stripe PaymentSheet completes successfully.
  • Adds explicit checkout flow states, payment intent tracking, shipment-status handling, and a retry path for order creation without recharging the customer.
  • Extends the payment intent model to support an explicit paymentIntentId, while keeping the client-secret fallback for the current backend response shape.
  • Tightens API logging so Stripe and Firebase secrets are not emitted in debug logs.
  • Updates checkout tests to cover home delivery, pick-up point payloads, and shipmentStatus: pending handling.

Notes

  • Frontend-only change. No backend files were modified.
  • The order payload now includes the explicit checkout metadata required by the backend, including paymentIntentId, deliveryType, shipping option fields, and GB country codes.
  • MVP shipping values are still used as fallbacks until Sendcloud shipping option selection is wired in.
  • shipmentStatus: pending is treated as a partial success, which means checkout completion now depends on shipment status as well as order creation.
  • The diff also includes a small set of unrelated file changes already present in the worktree; reviewers should ignore those unless they touch the same flows.

Testing

  • Added unit coverage for checkout payload construction and shipment status parsing.
  • Ran formatting on the changed Dart files.
  • flutter analyze and the focused Flutter test run were attempted, but both hung in this toolchain and were stopped. No full test run completed; however, I believe this to be a local issue on my device.

Summary by CodeRabbit

  • New Features

    • Order retry during checkout, improved payment-intent tracking, and new checkout messages for delivery/payment/order issues.
  • Bug Fixes

    • Fixed donor-discount display when promotion is inactive.
  • Refactor

    • Streamlined checkout flow and button behavior for clearer success/failure handling.
  • Tests

    • Added/expanded checkout and authentication tests to cover order creation and retry scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@CherryCIC has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 43 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e4dff2cd-3cff-4ed8-9082-ce11fe209ae2

📥 Commits

Reviewing files that changed from the base of the PR and between 132c18f and c53352e.

📒 Files selected for processing (13)
  • android/app/build.gradle.kts
  • lib/core/config/app_strings.dart
  • lib/core/services/network/api_service.dart
  • lib/features/checkout/checkout_page.dart
  • lib/features/checkout/checkout_repository.dart
  • lib/features/checkout/checkout_view_model.dart
  • lib/features/checkout/models/payment_intent.dart
  • lib/features/checkout/models/payment_intent.g.dart
  • lib/features/donation/widgets/donation_dropdown_field.dart
  • lib/features/products/product_page.dart
  • test/auth_ui_test.mocks.dart
  • test/checkout_view_model_test.dart
  • test/checkout_view_model_test.mocks.dart
📝 Walkthrough

Walkthrough

Refactors checkout into a state-driven flow: new CheckoutFlowState, payment intent tracking, submit/retry flows, required paymentIntentId for order creation, enriched order payload builders, and related UI, model, build, string, and test updates.

Changes

Checkout Feature Refactor

Layer / File(s) Summary
ViewModel state & lifecycle
lib/features/checkout/checkout_view_model.dart
Adds CheckoutFlowState, FirebaseAuth import, state fields/getters (isCheckoutProcessing, hasCompletedCheckout, canRetryOrderCreation, hasValidDeliveryDetails), and updates reset/process wiring.
Payment flow: submit & retry
lib/features/checkout/checkout_view_model.dart
Adds submitCheckout() and retryOrderCreation(); refactors payWithPaymentSheet() to return Future<String?> and persist _lastPaymentIntentId.
Order creation & payload builders
lib/features/checkout/checkout_view_model.dart
createOrder({required String paymentIntentId}) validates inputs, builds shipping/carrier/address/pickup payloads, and maps repository response to shipment states.
Data Models
lib/features/checkout/models/payment_intent.dart, *.g.dart
Adds nullable paymentIntentId to PaymentIntentData and exposes it via PaymentIntentResponse.paymentIntentId; updates serializers.
Repository
lib/features/checkout/checkout_repository.dart
createOrder() now returns the full result value instead of unwrapping data; minor formatting.
UI & Wiring
lib/features/checkout/checkout_page.dart
Button enablement and on-press behavior now rely on VM flags (isCheckoutProcessing, canRetryOrderCreation, hasCompletedCheckout, hasValidDeliveryDetails); page no longer drives navigation to completion.
Tests
test/checkout_view_model_test.dart, test/checkout_view_model_test.mocks.dart
Adds/updates tests and fakes to validate built order payloads for home vs pickup delivery and shipment-pending state transitions.

Build, Service & Minor UI Updates

Layer / File(s) Summary
Android Build
android/app/build.gradle.kts
Derives ndkVersion, minSdk, and targetSdk from flutter.* Gradle plugin properties; minor blank-line edit.
Strings
lib/core/config/app_strings.dart
Adds checkout-related message constants (checkoutDeliveryAddressRequired, checkoutPaymentFailed, checkoutPaymentCancelled, checkoutPaymentDetailsUnavailable, checkoutOrderCreationFailed, checkoutShipmentFailed, checkoutShipmentPending, checkoutRetryOrder, checkoutDeliveryPending).
Small UI tweaks
lib/features/donation/widgets/donation_dropdown_field.dart, lib/features/products/product_page.dart, lib/main.dart, test/auth_ui_test.mocks.dart
Dropdown uses initialValue; product page uses empty trailing text for inactive donor discount; main loads dotenv from "assets/.env"; generated test mock file added.

Sequence Diagram

sequenceDiagram
    actor User
    participant CheckoutPage
    participant CheckoutVM as CheckoutViewModel
    participant PaymentSheet as Payment Sheet
    participant Repository as CheckoutRepository
    participant Backend as Backend API

    User->>CheckoutPage: Tap Submit or Retry
    CheckoutPage->>CheckoutVM: submitCheckout() or retryOrderCreation()
    
    alt First submission
        CheckoutVM->>PaymentSheet: payWithPaymentSheet(amount)
        PaymentSheet->>User: show payment UI
        User->>PaymentSheet: complete payment
        PaymentSheet->>CheckoutVM: return paymentIntentId
        CheckoutVM->>CheckoutVM: store _lastPaymentIntentId
    else Retry
        CheckoutVM->>CheckoutVM: use stored _lastPaymentIntentId
    end

    CheckoutVM->>CheckoutVM: build order payload (shipping, carrier, address/pickup)
    CheckoutVM->>Repository: createOrder(paymentIntentId, payload)
    Repository->>Backend: POST /api/order/create
    Backend->>Repository: returns order + shipment status
    Repository->>CheckoutVM: response
    CheckoutVM->>CheckoutVM: set checkoutFlowState (shipmentPending/created/failed)
    CheckoutVM->>CheckoutPage: notify state change
    CheckoutPage->>User: show toast/feedback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Cherry-CIC/MVP#419: Related edits to lib/core/config/app_strings.dart that touch the same constants file.

Poem

🐰 A rabbit hops through checkout states,
Payment intents and payload fates,
Retries, shipments, state aligned,
Strings and tests to keep in mind—
A gentle hop, the flow now waits.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective: refactoring the checkout flow to create orders after Stripe PaymentSheet success, which is the core change across checkout-related files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cherry/FE.checkoutordercreation

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.

@CherryCIC CherryCIC self-assigned this May 1, 2026
@CherryCIC CherryCIC changed the title Refactor checkout to create orders after Stripe PaymentSheet success FE: Refactor checkout to create orders after Stripe PaymentSheet success May 1, 2026
@CherryCIC CherryCIC marked this pull request as ready for review May 1, 2026 18:25
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
lib/core/services/network/api_service.dart (1)

179-194: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid logging raw server error messages in checkout paths.

_handleBadResponse stopped logging payloads at Line 183, but Line 193 still logs serverMessage verbatim. Backend error strings can include PII/token-like values, so this remains a privacy/compliance risk.

🔧 Suggested fix
   switch (statusCode) {
     case 400:
     case 403:
     case 404:
     case 422:
     case 429:
       final serverMessage = _extractErrorMessage(data);
-      if (serverMessage != null) {
-        _log.info('Server validation error: $serverMessage');
-      }
+      if (serverMessage != null) {
+        _log.info('Server validation error received (redacted).');
+      }
       return ErrorStrings.apiError;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/core/services/network/api_service.dart` around lines 179 - 194, In
_handleBadResponse, avoid logging the raw server error string returned by
_extractErrorMessage to prevent leaking PII/tokens; instead sanitize or redact
the value before logging (e.g., replace long token-like substrings with
"<redacted>" or only log a generic severity/type and truncated length), or log
only the existence of a server validation error and its status code; update the
branch that currently does _log.info('Server validation error: $serverMessage')
to use the redacted/sanitizedServerMessage (or a generic message) and add a
short comment pointing to _extractErrorMessage for future maintainers.
🧹 Nitpick comments (4)
lib/features/profile/profile_page.dart (1)

14-14: 💤 Low value

Unrelated change: consider removing from this PR.

While adding const to the constructor is a valid Flutter optimization, this ProfilePage change is unrelated to the checkout flow refactoring described in the PR objectives. Per the PR summary, reviewers should ignore unrelated file changes unless they touch the same flows—this file does not.

Consider reverting this change and including it in a separate housekeeping PR to keep this PR focused on the checkout refactoring.

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

In `@lib/features/profile/profile_page.dart` at line 14, Revert the unrelated
constructor change in the ProfilePage widget by removing the added const
modifier on the ProfilePage constructor (the const ProfilePage({super.key})
change); restore the original constructor signature used before this PR so this
file no longer contains unrelated housekeeping edits and can be moved to a
separate follow-up PR.
lib/features/products/product_page.dart (1)

89-92: ⚡ Quick win

Avoid blank trailing content for inactive donor discount state.

Returning '' for the inactive case removes context in this highlight row; keeping explicit inactive detail reads clearer.

♻️ Suggested adjustment
                   final donorDiscountDetail = isDonorDiscountActive
                       ? AppStrings.productPageBuy2Get1HalfPrice
-                      : '';
+                      : AppStrings.productPageDonorDiscountInactiveDetail;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/products/product_page.dart` around lines 89 - 92, The highlight
row currently sets donorDiscountDetail to an empty string when
isDonorDiscountActive is false, which yields blank trailing content; change the
ternary fallback to an explicit inactive message (e.g., replace '' with a
dedicated constant such as AppStrings.productPageDonorDiscountInactive or a
clear literal like 'Not available') so donorDiscountDetail always conveys state;
update AppStrings to include productPageDonorDiscountInactive if it doesn't
exist and ensure any UI that displays donorDiscountDetail handles that explicit
inactive text.
lib/features/checkout/checkout_page.dart (1)

190-202: 💤 Low value

Redundant validation checks.

These validation checks for hasValidDeliveryDetails and hasSelectedPaymentMethod are unreachable when canRetryOrderCreation is false, because the button is only enabled when canSubmitCheckout is true—which already guarantees both conditions are satisfied.

Consider removing them to reduce cognitive load, or keep them as explicit defensive guards if intentional.

♻️ Proposed simplification
                      if (canRetryOrderCreation) {
                        setState(() => _errorMessage = '');
                        await viewModel.retryOrderCreation();
                        return;
                      }

-                      if (!viewModel.hasValidDeliveryDetails) {
-                        setState(() {
-                          _errorMessage = AppStrings.checkoutDeliveryAddressRequired;
-                        });
-                        return;
-                      }
-
-                      if (!hasSelectedPaymentMethod) {
-                        setState(() {
-                          _errorMessage = AppStrings.checkoutPaymentMethodRequired;
-                        });
-                        return;
-                      }
-
                      setState(() => _errorMessage = '');
                      await viewModel.submitCheckout();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/checkout/checkout_page.dart` around lines 190 - 202, The two
validation blocks checking viewModel.hasValidDeliveryDetails and
hasSelectedPaymentMethod (which set _errorMessage to
AppStrings.checkoutDeliveryAddressRequired and
AppStrings.checkoutPaymentMethodRequired) are redundant because
canSubmitCheckout already guarantees those conditions when the button is
enabled; remove those if statements and their setState calls from the onPressed
flow in checkout_page.dart (leaving canRetryOrderCreation and other retry logic
intact) to simplify the method and reduce cognitive load, and run/update any
tests that asserted those specific error messages if present.
lib/features/checkout/checkout_view_model.dart (1)

659-675: ⚡ Quick win

Consider injecting FirebaseAuth for testability.

Directly accessing FirebaseAuth.instance.currentUser makes this method difficult to unit test without mocking Firebase at the global instance level. The try-catch fallback works but obscures test behavior.

Consider injecting FirebaseAuth (or a user provider interface) into the ViewModel constructor to improve testability and align with the existing dependency injection pattern used for checkoutRepository and navigator.

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

In `@lib/features/checkout/checkout_view_model.dart` around lines 659 - 675, The
_resolveShippingName method uses FirebaseAuth.instance.currentUser which hinders
unit testing; inject a FirebaseAuth (or an abstraction like UserProvider) into
the ViewModel constructor, store it as a private field (e.g., _firebaseAuth or
_userProvider), and update _resolveShippingName to read currentUser from that
injected dependency instead of FirebaseAuth.instance; update the ViewModel
constructor signature (where checkoutRepository and navigator are injected) to
accept the new dependency and adjust tests to pass a mock/fake FirebaseAuth or
provider while preserving the existing try/catch and 'cherry customer' fallback
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/core/config/app_strings.dart`:
- Around line 139-140: Update the two user-facing string constants to correct
grammar: change giveYourBuyerText (currently "Give your buyer the option pick a
cause they care about.") to include the missing word (e.g., "Give your buyer the
option to pick a cause they care about.") and change easyWayText (currently "Its
an easy way to make your listing more impactful.") to use the
contraction/apostrophe or possessive correctly (e.g., "It's an easy way to make
your listing more impactful."). Ensure you edit the values of giveYourBuyerText
and easyWayText in app_strings.dart accordingly.

In `@lib/features/donation/widgets/donation_dropdown_field.dart`:
- Line 76: The dropdown is using initialValue (set in initState) so it never
reflects parent changes; replace initialValue with the controlling value
property on DropdownButtonFormField and ensure the local selectedDropdownItem is
kept in sync by updating it in didUpdateWidget when widget.selectedValue changes
(or simply use widget.selectedValue directly as the value); update
DropdownButtonFormField to use value: selectedDropdownItem (or
widget.selectedValue) and add a didUpdateWidget override to
setState(selectedDropdownItem = widget.selectedValue) when the incoming prop
differs.

---

Outside diff comments:
In `@lib/core/services/network/api_service.dart`:
- Around line 179-194: In _handleBadResponse, avoid logging the raw server error
string returned by _extractErrorMessage to prevent leaking PII/tokens; instead
sanitize or redact the value before logging (e.g., replace long token-like
substrings with "<redacted>" or only log a generic severity/type and truncated
length), or log only the existence of a server validation error and its status
code; update the branch that currently does _log.info('Server validation error:
$serverMessage') to use the redacted/sanitizedServerMessage (or a generic
message) and add a short comment pointing to _extractErrorMessage for future
maintainers.

---

Nitpick comments:
In `@lib/features/checkout/checkout_page.dart`:
- Around line 190-202: The two validation blocks checking
viewModel.hasValidDeliveryDetails and hasSelectedPaymentMethod (which set
_errorMessage to AppStrings.checkoutDeliveryAddressRequired and
AppStrings.checkoutPaymentMethodRequired) are redundant because
canSubmitCheckout already guarantees those conditions when the button is
enabled; remove those if statements and their setState calls from the onPressed
flow in checkout_page.dart (leaving canRetryOrderCreation and other retry logic
intact) to simplify the method and reduce cognitive load, and run/update any
tests that asserted those specific error messages if present.

In `@lib/features/checkout/checkout_view_model.dart`:
- Around line 659-675: The _resolveShippingName method uses
FirebaseAuth.instance.currentUser which hinders unit testing; inject a
FirebaseAuth (or an abstraction like UserProvider) into the ViewModel
constructor, store it as a private field (e.g., _firebaseAuth or _userProvider),
and update _resolveShippingName to read currentUser from that injected
dependency instead of FirebaseAuth.instance; update the ViewModel constructor
signature (where checkoutRepository and navigator are injected) to accept the
new dependency and adjust tests to pass a mock/fake FirebaseAuth or provider
while preserving the existing try/catch and 'cherry customer' fallback behavior.

In `@lib/features/products/product_page.dart`:
- Around line 89-92: The highlight row currently sets donorDiscountDetail to an
empty string when isDonorDiscountActive is false, which yields blank trailing
content; change the ternary fallback to an explicit inactive message (e.g.,
replace '' with a dedicated constant such as
AppStrings.productPageDonorDiscountInactive or a clear literal like 'Not
available') so donorDiscountDetail always conveys state; update AppStrings to
include productPageDonorDiscountInactive if it doesn't exist and ensure any UI
that displays donorDiscountDetail handles that explicit inactive text.

In `@lib/features/profile/profile_page.dart`:
- Line 14: Revert the unrelated constructor change in the ProfilePage widget by
removing the added const modifier on the ProfilePage constructor (the const
ProfilePage({super.key}) change); restore the original constructor signature
used before this PR so this file no longer contains unrelated housekeeping edits
and can be moved to a separate follow-up PR.
🪄 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: 72833e98-9765-4625-8ff1-2c253242d546

📥 Commits

Reviewing files that changed from the base of the PR and between 06fe8f9 and c38d7ab.

📒 Files selected for processing (19)
  • android/app/build.gradle.kts
  • lib/core/config/app_strings.dart
  • lib/core/services/firebase_auth_service.dart
  • lib/core/services/network/api_service.dart
  • lib/features/charity_page/charity_model.dart
  • lib/features/checkout/checkout_page.dart
  • lib/features/checkout/checkout_repository.dart
  • lib/features/checkout/checkout_view_model.dart
  • lib/features/checkout/models/payment_intent.dart
  • lib/features/checkout/models/payment_intent.g.dart
  • lib/features/donation/widgets/donation_dropdown_field.dart
  • lib/features/donation/widgets/photo_tips_bar.dart
  • lib/features/password_flow/widgets/hidden_password_field.dart
  • lib/features/products/product_page.dart
  • lib/features/profile/profile_page.dart
  • lib/main.dart
  • test/auth_ui_test.mocks.dart
  • test/checkout_view_model_test.dart
  • test/checkout_view_model_test.mocks.dart
💤 Files with no reviewable changes (3)
  • lib/features/donation/widgets/photo_tips_bar.dart
  • lib/features/password_flow/widgets/hidden_password_field.dart
  • lib/features/charity_page/charity_model.dart

Comment thread lib/core/config/app_strings.dart Outdated
Comment thread lib/features/donation/widgets/donation_dropdown_field.dart
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c38d7abbf3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/main.dart Outdated
Comment thread lib/features/checkout/checkout_page.dart
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
lib/features/checkout/checkout_view_model.dart (1)

273-278: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

processCheckout() still blocks valid pickup checkouts

Line 274-275 still enforces canCheckout + validateShippingAddress(), which requires a shipping address even for pickup orders. That bypasses the new hasValidDeliveryDetails logic and can reject valid pickup flows.

Suggested fix
Future<bool> processCheckout() async {
-  if (!canCheckout) return false;
-  if (!validateShippingAddress()) return false;
+  if (!hasPaymentMethod) return false;
+  if (!hasValidDeliveryDetails) return false;

  await submitCheckout();
  return hasCompletedCheckout;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/checkout/checkout_view_model.dart` around lines 273 - 278,
processCheckout currently blocks pickup orders by always calling
validateShippingAddress(); update it to allow pickup by replacing the
unconditional validateShippingAddress() call with the new
hasValidDeliveryDetails check (or conditionally call validateShippingAddress()
only when the order requires delivery). In practice, inside Future<bool>
processCheckout() keep the canCheckout guard, then ensure you verify delivery
details via hasValidDeliveryDetails (or if deliverySelected then
validateShippingAddress()), call submitCheckout() as before, and return
hasCompletedCheckout; reference the processCheckout, validateShippingAddress,
hasValidDeliveryDetails, submitCheckout, and hasCompletedCheckout symbols when
making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/checkout/checkout_view_model.dart`:
- Around line 578-600: _buildOrderPayload currently charges the whole basket but
only includes the first product (productId/productName), which can cause
mismatched metadata; update _buildOrderPayload to include a full items array
derived from basketItems (e.g., each item's id, name, quantity,
unitPriceMinorUnits and lineTotalMinorUnits) and remove or deprecate the single
productId/productName fields so the payload reflects the charged amount; ensure
the items array construction uses the same helpers as amount calculation
(_toMinorUnits and any quantity logic) and include any summary fields
(totalAmount, totalQuantity) if needed for backend reconciliation.
- Around line 697-708: The function _resolveShipmentFlowState currently defaults
unknown/absent statuses to CheckoutFlowState.shipmentCreated; change this so
that when _shipmentStatusFromResponse(payload) returns null/empty or an
unrecognized status the function returns CheckoutFlowState.shipmentPending (i.e.
treat unknown as unresolved), by adding an explicit null/empty check on
shipmentStatus before the switch and making the default branch return
shipmentPending; update any callers if they relied on the previous default
behavior.

---

Outside diff comments:
In `@lib/features/checkout/checkout_view_model.dart`:
- Around line 273-278: processCheckout currently blocks pickup orders by always
calling validateShippingAddress(); update it to allow pickup by replacing the
unconditional validateShippingAddress() call with the new
hasValidDeliveryDetails check (or conditionally call validateShippingAddress()
only when the order requires delivery). In practice, inside Future<bool>
processCheckout() keep the canCheckout guard, then ensure you verify delivery
details via hasValidDeliveryDetails (or if deliverySelected then
validateShippingAddress()), call submitCheckout() as before, and return
hasCompletedCheckout; reference the processCheckout, validateShippingAddress,
hasValidDeliveryDetails, submitCheckout, and hasCompletedCheckout symbols when
making the change.
🪄 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: 3d6bd5d3-46b8-4e35-87ab-56814708be9c

📥 Commits

Reviewing files that changed from the base of the PR and between c38d7ab and de6f10e.

📒 Files selected for processing (1)
  • lib/features/checkout/checkout_view_model.dart

Comment thread lib/features/checkout/checkout_view_model.dart
Comment thread lib/features/checkout/checkout_view_model.dart
Copy link
Copy Markdown
Contributor

@JaseElder JaseElder left a comment

Choose a reason for hiding this comment

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

The coderabbit comments need to be considered and implemented if necessary

Comment thread lib/main.dart Outdated
CherryCIC added 2 commits May 16, 2026 23:45
Apply safe review fixes for PR #413: restore root dotenv loading, keep pickup checkout validation aligned with delivery choice, prevent repeat payment attempts after shipment failure, sync the donation dropdown with parent reset state, and correct donation helper copy.

Type: frontend UI/state and configuration cleanup. Backend/API contract is unchanged; the multi-item payload and unknown-shipment-status CodeRabbit suggestions were reviewed but not applied because they conflict with the documented /api/order/create contract.

Testing: dart format was run on touched files before staging; git diff --check passed. flutter analyze and the focused checkout ViewModel test previously hung in this local toolchain, so they were not completed locally.
@CherryCIC
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

✅ Actions performed

Full review triggered.

@CherryCIC CherryCIC requested a review from JaseElder May 17, 2026 13:23
@CherryCIC
Copy link
Copy Markdown
Collaborator Author

@coderabbitai what's the verdict?

@JaseElder I believe I have made the relevant changes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me pull the current review state from the PR.


🏁 Script executed:

#!/bin/bash
# Fetch PR reviews
gh pr view 413 --repo Cherry-CIC/MVP --json reviews,state,reviewDecision

echo "---COMMENTS---"
# Fetch review comments (inline)
gh api repos/Cherry-CIC/MVP/pulls/413/comments --jq '[.[] | {user: .user.login, body: .body, path: .path, line: .line, diff_hunk: .diff_hunk}]' 2>/dev/null | head -300

echo "---ISSUE COMMENTS---"
# Fetch PR issue-level comments
gh api repos/Cherry-CIC/MVP/issues/413/comments --jq '[.[] | {user: .user.login, body: .body}]' 2>/dev/null | head -300

Length of output: 93733


@CherryCIC — here's the current verdict:

Status: 🔴 Changes Requested (blocked by @JaseElder)

Good news — two items from my first review are marked as resolved in commit c53352e (grammar fixes in app_strings.dart and the stale initialValue in the donation dropdown). However, the following issues remain open:


🟠 Major — Must Address

# File Issue
1 lib/features/checkout/checkout_view_model.dart L578–600 Multi-item basket payload mismatch_buildOrderPayload charges the full basket total but only includes the first product's identity. This can cause order metadata to diverge from the charged amount.
2 lib/features/checkout/checkout_view_model.dart L697–708 Unknown shipment status treated as success — the default: branch returns shipmentCreated when the status is absent or unrecognised. It should return shipmentPending to avoid incorrectly marking checkout complete.
3 lib/features/checkout/checkout_view_model.dart L273–278 processCheckout() blocks valid pickup orders — it still calls validateShippingAddress() unconditionally, which rejects pickup flows that don't need a shipping address. Should use hasValidDeliveryDetails instead.
4 lib/core/services/network/api_service.dart L179–194 Raw server error string still logged_handleBadResponse logs serverMessage verbatim, which can leak PII or token-like values. Should be redacted.
5 lib/main.dart @JaseElder specifically flagged this: revert dotenv.load(fileName: "assets/.env") back to await dotenv.load() — the .env file belongs at the root, not assets/.

💤 Lower Priority (Nitpicks — address if feasible)

  • checkout_page.dart L190–202: Redundant hasValidDeliveryDetails / hasSelectedPaymentMethod guards inside onPressedcanSubmitCheckout already guarantees them.
  • checkout_view_model.dart L659–675: FirebaseAuth.instance.currentUser accessed directly — harder to unit-test; consider injecting it.
  • product_page.dart L89–92: Inactive donor discount renders blank trailing content ('') — the original AppStrings.productPageDonorDiscountInactiveDetail was more explicit.
  • profile_page.dart L14: Unrelated const constructor change — consider reverting and including in a separate housekeeping PR.

The dotenv revert and the two checkout_view_model.dart inline issues (items 1, 2, 3 above) are the most critical path to getting @JaseElder's approval. Let me know if you'd like help with any of these! 🐇

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants