Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Jan 20, 2026

https://www.loom.com/share/bb7abfde507f40d386ee856f5ffbd506

Summary by CodeRabbit

  • New Features
    • USD-based refund system enabling partial and full refunds with explicit USD amounts
    • Per-entry refund selection with granular quantity controls in refund dialogs
  • Bug Fixes
    • Stronger refund validation and error handling to prevent invalid or out-of-bounds refunds
  • Tests
    • Expanded end-to-end coverage for refund edge cases and scenarios
  • Style
    • Improved refund dialog UI with contextual alerts and better controls

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
stack-backend Ready Ready Preview, Comment Jan 29, 2026 6:40pm
stack-dashboard Ready Ready Preview, Comment Jan 29, 2026 6:40pm
stack-demo Ready Ready Preview, Comment Jan 29, 2026 6:40pm
stack-docs Ready Ready Preview, Comment Jan 29, 2026 6:40pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds USD-aware refund end-to-end support: backend validates per-entry USD refunds, computes Stripe-unit amounts, performs Stripe refunds and subscription quantity updates, and persists metadata; dashboard UI enables per-entry quantity selection and USD amount input; admin/client interfaces and tests updated to pass structured refund entries.

Changes

Cohort / File(s) Summary
Backend Refund Handler
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx
Adds USD-centric refund helpers (getTotalUsdStripeUnits, validateRefundEntries, getRefundAmountStripeUnits, getRefundedQuantity), validates refund_entries and amount_usd, computes Stripe-unit amounts, performs Stripe refunds, updates subscription item quantities, and persists refund timestamps/cancellation flags.
Frontend Refund UI
apps/dashboard/src/components/data-table/transaction-table.tsx
Introduces per-entry refund selection UI, USD amount input, money schema/utility usage (MoneyAmount, moneyAmountToStripeUnits, moneyAmountSchema), validation logic for refund_entries and amount_usd, and updated submission flow sending refundEntries + amountUsd.
UI Dialog Behavior
apps/dashboard/src/components/ui/action-dialog.tsx
Extracts okButton.props into okButtonProps, computes okButtonDisabled from internal state and explicit button disabled, and spreads okButtonProps on the OK button.
Admin / Client Interfaces
packages/stack-shared/src/interface/admin-interface.ts, packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts, packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
Changes refundTransaction signature to accept refundEntries: Array<{ entryIndex, quantity, amountUsd }>; imports MoneyAmount type and maps payload to refund_entries (entry_index, quantity, amount_usd) for the backend.
Client/Server Checkout Callsites
packages/stack-shared/src/interface/client-interface.ts, packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts, packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
Adds requestType param to createCheckoutUrl (client
E2E Test Coverage
apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts
Adds live-mode helper createLiveModeOneTimePurchaseTransaction, centralizes live-mode setup, and expands tests for USD refunds: partial/full refunds, quantity-based refunds, and many SCHEMA_ERROR and edge-case scenarios.
Test Invocation Options
apps/e2e/tests/general/typecheck.test.ts
Replaces numeric timeout with options object { retry: 1, timeout: 240_000 } for the it(...) invocation.
Type Updates / Shared Types
packages/stack-shared/src/interface/client-interface.ts, packages/stack-shared/src/interface/admin-interface.ts
Adds/imports MoneyAmount type usage in public interfaces and request payload typings.

Sequence Diagram

sequenceDiagram
    participant Dashboard as Dashboard UI
    participant AdminAPI as Admin Interface
    participant Backend as Backend Refund Handler
    participant Stripe as Stripe API
    participant DB as Database

    Dashboard->>Dashboard: User selects refund entries\nand enters amount_usd
    Dashboard->>AdminAPI: POST refundTransaction\n{ refundEntries, amountUsd }
    AdminAPI->>Backend: POST /refund\n{ refund_entries, amount_usd }

    Backend->>Backend: validateRefundEntries\n(check bounds, existence)
    Backend->>Backend: getTotalUsdStripeUnits\n(convert USD -> Stripe units)
    Backend->>Stripe: Create refund (amount in Stripe units)
    Stripe-->>Backend: Refund confirmed

    Backend->>Backend: compute per-entry refunded quantities
    Backend->>Stripe: Update subscription item quantities (if needed)
    Stripe-->>Backend: Quantity update confirmed

    Backend->>DB: Persist refund metadata\n(timestamps, cancelled flags)
    DB-->>Backend: Persisted
    Backend-->>AdminAPI: 200 OK
    AdminAPI-->>Dashboard: Success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hopping through entries, cents and cents,
I tally refunds in tiny units dense.
Per-entry hops, a careful count,
Stripe dances, DB stores the amount,
A joyful refund — carrots recompense! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is minimal, containing only a Loom video link and the contribution guidelines template with no substantive implementation details, requirements, or change context. Replace the video link with a detailed written description including: motivation for changes, specific implementation details, testing performed, and any breaking changes or migration notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Partial refunds frontend' is directly related to the changes, which implement USD-based refund handling in the frontend dashboard and backend payment refund endpoint.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 20, 2026

Greptile Summary

This PR adds partial refund functionality to the payments system, allowing admins to specify custom USD refund amounts instead of being limited to full refunds. The implementation includes both backend validation and a polished frontend UI.

Key Changes:

  • Backend API now accepts optional amount_usd parameter with validation ensuring amounts are positive and don't exceed the original charge
  • Frontend refund dialog includes an input field for USD amounts with real-time validation and error messaging
  • Non-USD transactions fall back to full refunds with an informational message
  • Comprehensive test coverage added for partial refund scenarios
  • Action dialog component fixed to properly handle both confirmation requirements and custom button disabled states

Implementation Quality:

  • Validation is consistently applied on both frontend and backend (defense in depth)
  • Error messages are clear and user-friendly
  • The UI gracefully handles edge cases (non-USD transactions, invalid amounts)
  • Code follows existing patterns and conventions in the codebase
  • Test coverage is thorough and well-structured

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-tested, properly validates inputs on both client and server, handles edge cases gracefully, and follows existing code patterns. The changes are focused and don't introduce architectural complexity.
  • No files require special attention

Important Files Changed

Filename Overview
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx Added partial refund support with proper validation for USD amounts, including checks for zero amounts and amounts exceeding charged total
apps/dashboard/src/components/data-table/transaction-table.tsx Implemented refund amount input UI with real-time validation and proper error messaging
apps/dashboard/src/components/ui/action-dialog.tsx Fixed button disabled state handling to properly respect both confirmation requirement and custom disabled props
apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts Added comprehensive test coverage for partial refunds and refactored existing tests to reduce duplication

Sequence Diagram

sequenceDiagram
    participant User as Admin User
    participant UI as Transaction Table UI
    participant Dialog as Action Dialog
    participant App as Admin App
    participant API as Refund API Route
    participant Validation as Amount Validator
    participant Stripe as Stripe API
    participant DB as Database

    User->>UI: Click refund button
    UI->>Dialog: Open refund dialog
    Dialog->>UI: Load charged amount (USD)
    UI->>Dialog: Pre-populate input field
    User->>Dialog: Enter/modify refund amount
    Dialog->>Validation: Validate amount format
    Validation-->>Dialog: Check schema validity
    Validation->>Validation: Check amount > 0
    Validation->>Validation: Check amount <= charged total
    Validation-->>Dialog: Return validation result
    Dialog->>Dialog: Update error state & button disabled
    User->>Dialog: Click "Refund" button
    Dialog->>App: refundTransaction({type, id, amountUsd})
    App->>API: POST /internal/payments/transactions/refund
    API->>DB: Fetch subscription/purchase details
    DB-->>API: Return product, priceId, quantity
    API->>Validation: Calculate total USD stripe units
    Validation-->>API: Return total amount in cents
    API->>Validation: Convert refund amount to stripe units
    Validation-->>API: Return refund amount in cents
    API->>API: Validate refund <= total
    API->>Stripe: Create refund with amount
    Stripe-->>API: Refund created
    API->>DB: Update refundedAt timestamp
    DB-->>API: Updated
    API-->>App: {success: true}
    App-->>Dialog: Success
    Dialog->>Dialog: Close dialog
    Dialog-->>UI: Refresh transaction list
Loading

@BilalG1 BilalG1 requested a review from N2D4 January 20, 2026 19:01
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Jan 20, 2026
@promptless
Copy link
Contributor

promptless bot commented Jan 22, 2026

📝 Documentation updates detected!

New suggestion: Document partial refunds for USD transactions

Copy link
Contributor

@N2D4 N2D4 left a comment

Choose a reason for hiding this comment

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

can you make it so you can't only partially refund money, but also choose which and how many products, etc.?

@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Jan 24, 2026
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In
`@apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx`:
- Around line 143-146: The code calls validateRefundEntries({ entries:
transaction.entries, refundEntries }) but then uses amount_usd to build the
Stripe refund, causing possible mismatch; update the refund flow in route.tsx to
compute the refund amount from refundEntries (using transaction.entries
prices/quantities) and use that computed value for the Stripe refund, or
alternatively compute the total implied by refundEntries and assert it equals
amount_usd before proceeding (throw/return error if mismatch). Locate the logic
around validateRefundEntries, refundEntries, amount_usd and the Stripe refund
creation and either replace amount_usd with the computedTotalFromRefundEntries
or add a validation step that compares computedTotalFromRefundEntries ===
amount_usd and fails on mismatch.

In `@apps/dashboard/src/components/data-table/transaction-table.tsx`:
- Around line 271-300: The validation currently checks refundAmountUsd-derived
refundUnits and selectedEntries-derived selectedUnits separately, which can
conflict; update validateRefund (the logic around refundAmountUsd, refundUnits,
selectedEntries, selectedUnits, maxUnits) to enforce consistency by computing
expectedRefundUnits from selectedEntries (use
moneyAmountToStripeUnits(entry.unitPriceUsd, USD_CURRENCY) *
entry.selectedQuantity) and comparing it to refundUnits, and if they differ
return canSubmit: false with an error like "Entered refund amount does not match
selected product quantities; please adjust amount or quantities." Alternatively
(if desired) auto-set refundAmountUsd to the computed amount before returning
success; ensure you reference refundAmountUsd, refundUnits, selectedEntries,
selectedUnits, and maxUnits when making the change.
🧹 Nitpick comments (6)
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx (3)

141-162: Duplicated refund validation logic between subscription and one-time-purchase branches.

The validation and conversion logic (lines 141-162 for subscriptions and 189-209 for one-time purchases) is nearly identical. Consider extracting this into a shared helper function to reduce duplication and improve maintainability.

♻️ Suggested refactor
+function computeAndValidateRefundAmount(options: {
+  refundAmountUsd: MoneyAmount,
+  product: InferType<typeof productSchema>,
+  priceId: string | null,
+  quantity: number,
+}): number {
+  const totalStripeUnits = getTotalUsdStripeUnits({
+    product: options.product,
+    priceId: options.priceId,
+    quantity: options.quantity,
+  });
+  const refundAmountStripeUnits = moneyAmountToStripeUnits(options.refundAmountUsd, USD_CURRENCY);
+  if (refundAmountStripeUnits <= 0) {
+    throw new KnownErrors.SchemaError("Refund amount must be greater than zero.");
+  }
+  if (refundAmountStripeUnits > totalStripeUnits) {
+    throw new KnownErrors.SchemaError("Refund amount cannot exceed the charged amount.");
+  }
+  return refundAmountStripeUnits;
+}

Then use this helper in both branches.

Also applies to: 189-209


148-151: Unsafe type casting of database JSON field.

The subscription.product is cast directly to InferType<typeof productSchema> without validation. If the stored JSON doesn't match the expected schema (due to schema evolution or data corruption), this could cause runtime errors downstream.

Consider validating the product data before use:

♻️ Suggested validation
-      const totalStripeUnits = getTotalUsdStripeUnits({
-        product: subscription.product as InferType<typeof productSchema>,
-        priceId: subscription.priceId ?? null,
-        quantity: subscription.quantity,
-      });
+      const validatedProduct = productSchema.validateSync(subscription.product);
+      const totalStripeUnits = getTotalUsdStripeUnits({
+        product: validatedProduct,
+        priceId: subscription.priceId ?? null,
+        quantity: subscription.quantity,
+      });

Based on learnings, code defensively with good error messages.


141-141: Unnecessary let declaration with unused null initialization.

refundAmountStripeUnits is declared as let ... | null = null but is immediately assigned without the null ever being read. Consider using const at the point of assignment:

♻️ Suggested fix
-      let refundAmountStripeUnits: number | null = null;
       // ... validation code ...
-      refundAmountStripeUnits = moneyAmountToStripeUnits(refundAmountUsd as MoneyAmount, USD_CURRENCY);
+      const refundAmountStripeUnits = moneyAmountToStripeUnits(refundAmountUsd as MoneyAmount, USD_CURRENCY);

Also applies to: 189-189

apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts (2)

134-135: Avoid any type for transaction filtering.

The any type here bypasses type checking. Per coding guidelines, avoid any and leave a comment if unavoidable.

♻️ Suggested fix
-  const purchaseTransaction = transactionsRes.body.transactions.find((tx: any) => tx.type === "purchase");
+  // Transaction type from API response - typed loosely since this is test code
+  const purchaseTransaction = transactionsRes.body.transactions.find(
+    (tx: { type: string }) => tx.type === "purchase"
+  );

326-390: Consider adding negative test cases for refund validation.

The tests cover successful partial and quantity-based refunds, but there are no tests verifying the validation errors for:

  • Refund amount exceeding the charged amount
  • Refund quantity exceeding the purchased quantity
  • Invalid entry_index values

These would help ensure the backend validation logic is working correctly.

apps/dashboard/src/components/data-table/transaction-table.tsx (1)

43-43: USD_CURRENCY could be undefined without a fallback.

Unlike the backend which uses ?? throwErr(...), the frontend USD_CURRENCY could be undefined if USD is not found in SUPPORTED_CURRENCIES. While this is unlikely, defensive coding would catch configuration errors early.

♻️ Suggested fix
-const USD_CURRENCY = SUPPORTED_CURRENCIES.find((currency) => currency.code === 'USD');
+const USD_CURRENCY = SUPPORTED_CURRENCIES.find((currency) => currency.code === 'USD')
+  ?? throwErr("USD currency configuration missing in SUPPORTED_CURRENCIES");

Based on learnings, prefer ?? throwErr(...) over potentially undefined values.

Copy link
Contributor

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

Caution

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

⚠️ Outside diff range comments (2)
apps/e2e/tests/backend/endpoints/api/v1/email-themes.test.ts (1)

232-257: Fix the display_name inconsistency in the test and backend.

The snapshot change reveals a real API inconsistency: the PATCH response at line 202 returns "display_name": "Default Light", but the subsequent GET response at line 236 returns "display_name": "Unnamed Theme" for the same theme.

This occurs because the backend's onUpdate handler (in apps/backend/src/app/api/latest/internal/email-themes/cud.tsx) only overrides the tsxSource in the config and doesn't preserve or explicitly set the displayName. When the GET request is made, the theme's displayName may fall back to the schema default value of "Unnamed Theme" rather than the original "Default Light".

Either:

  • Update the backend to preserve the theme's displayName when updating tsxSource, or
  • Update the PATCH response snapshot to match the GET response and document this behavior change

Currently, the snapshots don't reflect a consistent API contract, which will cause confusion for API consumers.

apps/dashboard/src/components/data-table/transaction-table.tsx (1)

225-325: Non‑USD refunds can currently throw despite UI implying a full refund.

When chargedAmountUsd is missing, validation returns no amountUsd/refundEntries, but the dialog still allows “Refund” and then throws. Either disable refunds for non‑USD charges or implement a supported full‑refund path, and align the messaging accordingly.

🛠️ One way to guard the UI
-  const canRefund = !!target && !transaction.test_mode && !alreadyRefunded && productEntries.length > 0;
+  const canRefund = !!target && !transaction.test_mode && !alreadyRefunded && productEntries.length > 0 && !!chargedAmountUsd;
-              <Alert>
-                <AlertDescription>
-                  Partial refunds are only available for USD charges. This will issue a full refund.
-                </AlertDescription>
-              </Alert>
+              <Alert>
+                <AlertDescription>
+                  Refunds are currently supported only for USD charges.
+                </AlertDescription>
+              </Alert>

Also applies to: 382-387

🧹 Nitpick comments (2)
apps/dashboard/src/components/data-table/transaction-table.tsx (1)

42-42: Prefer interface for RefundEntrySelection.

This is an object shape and should use interface to match the project’s TypeScript style. As per coding guidelines, please prefer interfaces for object shapes.

♻️ Suggested refactor
-type RefundEntrySelection = { entryIndex: number, quantity: number };
+interface RefundEntrySelection {
+  entryIndex: number;
+  quantity: number;
+}
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx (1)

29-32: Prefer interface for RefundEntrySelection.

This is an object shape and should use interface to align with the TypeScript style rules. As per coding guidelines, please prefer interfaces for object shapes.

♻️ Suggested refactor
-type RefundEntrySelection = {
-  entry_index: number,
-  quantity: number,
-};
+interface RefundEntrySelection {
+  entry_index: number;
+  quantity: number;
+}

Copy link
Contributor

@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)
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx (1)

256-259: Same issue: Setting refundedAt blocks subsequent partial refunds.

Similar to the subscription flow, setting refundedAt immediately after any refund (partial or full) will block future partial refunds due to the check at line 220-221.

For one-time purchases especially, users may want to refund items incrementally. Consider tracking cumulative refunded amounts instead.

🤖 Fix all issues with AI agents
In
`@apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx`:
- Around line 200-212: The current update sets refundedAt for any refund, which
blocks future refunds; change the logic in the refund handler so refundedAt is
only set when the subscription is fully refunded (i.e., newQuantity === 0) —
update the prisma.subscription.update call in the else branch (where newQuantity
!== 0) to omit refundedAt and only set cancelAtPeriodEnd/refundedAt in the
branch that handles full refunds; alternatively, if you prefer cumulative
tracking implement and use a numeric refundedQuantity/amount field on the
subscription and compare cumulative refunded amount to total before setting
refundedAt, but do not mark refundedAt true on partial refunds.
- Around line 162-164: The validation currently uses refundAmountStripeUnits >
totalStripeUnits against the original total; update the logic to compare the
requested refund against the remaining refundable amount by computing cumulative
refunded (e.g., sum previous refunds from the transaction or use a stored
refundedAmount) and enforcing cumulativeRefunded + refundAmountStripeUnits <=
totalStripeUnits; adjust the checks around refundedAt (and any logic in the same
file near the second occurrence at lines 245-247) so partial refunds are allowed
and refundedAt is only set when cumulativeRefunded reaches totalStripeUnits.
🧹 Nitpick comments (3)
apps/backend/src/app/api/latest/internal/payments/transactions/refund/route.tsx (3)

17-27: Type assertion bypasses MoneyAmount format validation.

The usdPrice is checked to be a string, but the as MoneyAmount cast at line 26 assumes it's in a valid money format (e.g., "10.00"). If the product's price data is malformed, this could produce unexpected results from moneyAmountToStripeUnits.

Consider validating the format or using a parsing function that throws on invalid input.


29-32: Prefer interface over type for object shapes.

Per coding guidelines, use interface for defining object shapes in TypeScript.

Suggested change
-type RefundEntrySelection = {
-  entry_index: number,
-  quantity: number,
-};
+interface RefundEntrySelection {
+  entry_index: number;
+  quantity: number;
+}

146-146: Unnecessary null initialization.

refundAmountStripeUnits is initialized to null but immediately assigned a value on the next few lines. The null initialization is unnecessary.

Suggested change (example for line 146)
-      let refundAmountStripeUnits: number | null = null;
       const transaction = buildSubscriptionTransaction({ subscription });
       validateRefundEntries({
         entries: transaction.entries,
         refundEntries,
       });
       const refundedQuantity = getRefundedQuantity(refundEntries);
       const totalStripeUnits = getTotalUsdStripeUnits({
         product: subscription.product as InferType<typeof productSchema>,
         priceId: subscription.priceId ?? null,
         quantity: subscription.quantity,
       });
-      refundAmountStripeUnits = moneyAmountToStripeUnits(refundAmountUsd as MoneyAmount, USD_CURRENCY);
+      const refundAmountStripeUnits = moneyAmountToStripeUnits(refundAmountUsd as MoneyAmount, USD_CURRENCY);

Also applies to: 230-230

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@apps/e2e/tests/backend/endpoints/api/v1/internal/config.test.ts`:
- Line 366: Remove the debug console.log that prints OAuth provider details;
specifically delete the statement logging
configWithoutGithub.auth.oauth.providers in the test so secrets like
clientSecret are not emitted to CI logs and test output stays clean.

In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts`:
- Around line 134-135: Replace the inline any usage when iterating transaction
results with the shared Transaction type: import Transaction from
'@stackframe/stack-shared/dist/interface/crud/transactions' and change
occurrences like transactionsRes.body.transactions.find((tx: any) => ...) to
transactionsRes.body.transactions.find((tx: Transaction) => ...), and update
variables such as purchaseTransaction (and the other listed occurrences) to be
typed as Transaction | undefined to preserve nullability; ensure the import is
added at the top and used consistently across all occurrences (e.g., the find
predicates and variable declarations).

@BilalG1 BilalG1 requested a review from N2D4 January 28, 2026 01:59
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Jan 28, 2026
@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Jan 28, 2026
Copy link
Contributor

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

🤖 Fix all issues with AI agents
In `@apps/dashboard/src/components/data-table/transaction-table.tsx`:
- Around line 323-331: The Refund flow can throw when chargedAmountUsd is falsy
or refundValidation.refundEntries is missing; update the click handler and
button props to fully guard against submitting invalid refunds: in the handler
that calls app.refundTransaction (the anonymous async function around
refundValidation/refundEntries), early-return if !refundValidation.canSubmit and
avoid using throwErr to surface missing refundEntries; instead either construct
a valid full-refund payload if backend supports it or surface a user-facing
error before calling app.refundTransaction. Also change the Refund button props
logic (currently using chargedAmountUsd ? { disabled:
!refundValidation.canSubmit } : undefined) so the button is disabled whenever
!refundValidation.canSubmit (not only when chargedAmountUsd exists). Apply the
same fixes for the other refund block referenced by
refundValidation/app.refundTransaction further down.

@BilalG1 BilalG1 merged commit efefa5d into dev Jan 29, 2026
27 of 28 checks passed
@BilalG1 BilalG1 deleted the partial-refunds-frontend branch January 29, 2026 19:20
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.

3 participants