Skip to content

feat(billing): promote first-purchase bonus with dynamic preview in add-credits form - #3424

Merged
baktun14 merged 8 commits into
mainfrom
feat/billing-first-purchase-bonus-ui
Jul 14, 2026
Merged

feat(billing): promote first-purchase bonus with dynamic preview in add-credits form#3424
baktun14 merged 8 commits into
mainfrom
feat/billing-first-purchase-bonus-ui

Conversation

@baktun14

@baktun14 baktun14 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Why

Part of CON-410

The Add Credits form still promises the stale "dollar-for-dollar match" that was never implemented. CON-410 finalizes the offer as a one-time 10% bonus (min $100 purchase, capped at $100) — the backend grant ships in #3423; this PR updates the purchase UI to promote and preview it. Hidden behind the same first_purchase_bonus Unleash flag, so nothing changes until both sides are deployed and the flag is flipped.

Stacked on #3412 (base = refactor/billing-unify-add-credits-sheet) because the unified tabbed sheet makes AddCreditsForm the single purchase surface (it deletes PaymentPopup); retarget to main when #3412 merges (#3404 already landed).

Screen.Recording.2026-07-13.at.6.06.04.PM.mov

What

  • New FirstPurchaseBonusAlert (blue gift alert, same visual as the old copy) rendered at the top of AddCreditsForm, reacting to the entered amount:
    • no amount → base offer ("Get 10% bonus credits on your first purchase of $100 or more, up to $100.")
    • under $100 → adds "Add $X more to qualify."
    • $100+ → previews the outcome: "You'll receive $TOTAL in credits" with the bonus called out (floored to cents, capped at $100 — mirrors FirstPurchaseBonusService).
  • Eligibility mirrors the backend predicate: hidden when the user has any succeeded Stripe charge (refunded charges keep succeeded status → correctly treated as consumed). Not gated on isTrialing — coupon claims end the trial without producing a charge, so coupon users (non-trialing) still see the offer, matching the backend rule that coupons don't consume eligibility. Replaces the {isTrialing && ...} wrapper from refactor(billing): unify add-credits and coupon on the shared tabbed sheet #3412.
  • usePaymentTransactionsQuery gains an optional queryOptions second param (mirrors usePaymentMethodsQuery) so the alert can gate fetching on the flag.
  • first_purchase_bonus added to the frontend FeatureFlag union.
  • Onboarding picker hero promotes the bonus behind the same flag: “Plus, get 10% in bonus credits on your first purchase — up to $100.” (BONUS_PERCENT/MAX_BONUS are now exported from the alert so the two copies can't drift).

No UI when the flag is off (query disabled, renders null).

Summary by CodeRabbit

Summary

  • New Features
    • Added a feature-flagged first-purchase bonus preview and breakdown in the credits purchase flow.
    • Extended the billing success screen, billing amount table, onboarding header messaging, and transaction CSV export to include bonus amounts.
  • Bug Fixes
    • Fixed bonus calculation/display to consistently reflect the selected credit amount.
    • Updated predefined credit options to 100 / 500 / 1000.
  • Tests
    • Expanded UI/API test coverage for eligibility, thresholds, capping, completion payloads, and CSV output.
  • Chores
    • Changed the default trial credits amount to 1.

@baktun14
baktun14 requested a review from a team as a code owner July 13, 2026 14:40
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

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

The pull request adds first-purchase bonus support across transaction APIs, credit purchasing, onboarding messaging, billing history, and payment-success displays. It introduces feature gating, transaction-based bonus lookup, capped bonus previews, and updated callback and data contracts.

Changes

First-purchase bonus flow

Layer / File(s) Summary
Bonus contracts and transaction data
apps/api/src/billing/..., apps/api/src/types/transactions.ts, packages/http-sdk/src/stripe/stripe.types.ts
Adds bonus metadata to transaction contracts, enriches customer transactions from stored Stripe records, and includes bonus values in CSV exports.
Bonus query and eligibility alert
apps/deploy-web/src/types/feature-flags.ts, apps/deploy-web/src/queries/usePaymentQueries.ts, apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/*
Adds feature-gated eligibility checks, transaction-history suppression, qualifying thresholds, capped bonus calculations, and alert coverage.
Purchase completion and bonus propagation
apps/deploy-web/src/components/billing-usage/AddCreditsForm/*, apps/deploy-web/src/components/billing-usage/AddCreditsTabs/AddCreditsTabs.tsx, apps/deploy-web/src/components/auth/AddCreditsSheet/AddCreditsSheet.tsx, apps/deploy-web/tests/ui/pages/AddCreditsSheetPage.ts
Replaces the inline alert, looks up the latest successful transaction after payment, passes the computed bonus through completion callbacks, and updates predefined amount options and tests.
Bonus display and onboarding messaging
apps/deploy-web/src/components/billing-usage/AccountOverview/*, apps/deploy-web/src/components/billing-usage/PaymentSuccessAnimation/*, apps/deploy-web/src/components/billing-usage/BillingView/*, apps/deploy-web/src/components/onboarding-picker/*
Displays bonus amounts in payment success and billing views, and adds feature-flagged first-purchase copy to onboarding.

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

Possibly related PRs

Suggested reviewers: stalniy, ygrishajev

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/billing-first-purchase-bonus-ui

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

@baktun14
baktun14 force-pushed the refactor/billing-unify-add-credits-sheet branch from b28e26c to 075df0b Compare July 13, 2026 15:06
@baktun14
baktun14 force-pushed the feat/billing-first-purchase-bonus-ui branch from 6a04475 to e889b9f Compare July 13, 2026 15:41
Base automatically changed from refactor/billing-unify-add-credits-sheet to main July 13, 2026 21:10
@baktun14
baktun14 force-pushed the feat/billing-first-purchase-bonus-ui branch from e889b9f to 9e7c5a8 Compare July 13, 2026 21:17
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.36066% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 71.53%. Comparing base (8fa4550) to head (3bed326).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
.../billing-usage/AccountOverview/AccountOverview.tsx 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3424      +/-   ##
==========================================
- Coverage   72.33%   71.53%   -0.80%     
==========================================
  Files        1158     1095      -63     
  Lines       29332    27460    -1872     
  Branches     7254     6883     -371     
==========================================
- Hits        21216    19644    -1572     
+ Misses       7136     6860     -276     
+ Partials      980      956      -24     
Flag Coverage Δ *Carryforward flag
api 85.74% <100.00%> (+0.01%) ⬆️
deploy-web 61.27% <98.14%> (+0.22%) ⬆️
log-collector ?
notifications 91.44% <ø> (ø)
provider-console 81.38% <ø> (ø)
provider-inventory ?
provider-proxy 86.42% <ø> (ø) Carriedforward from d4645f1
tx-signer 85.56% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
apps/api/src/billing/http-schemas/stripe.schema.ts 100.00% <ø> (ø)
...tripe-transaction/stripe-transaction.repository.ts 48.07% <100.00%> (+3.17%) ⬆️
.../api/src/billing/services/stripe/stripe.service.ts 75.00% <100.00%> (+0.29%) ⬆️
...omponents/auth/AddCreditsSheet/AddCreditsSheet.tsx 100.00% <ø> (ø)
.../AddCreditsAmountFields/AddCreditsAmountFields.tsx 100.00% <ø> (ø)
...ts/billing-usage/AddCreditsForm/AddCreditsForm.tsx 94.69% <100.00%> (+0.29%) ⬆️
...ts/billing-usage/AddCreditsTabs/AddCreditsTabs.tsx 87.50% <ø> (ø)
...mponents/billing-usage/BillingView/BillingView.tsx 87.09% <100.00%> (+2.61%) ⬆️
...irstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx 100.00% <100.00%> (ø)
...aymentSuccessAnimation/PaymentSuccessAnimation.tsx 95.65% <100.00%> (+90.65%) ⬆️
... and 4 more

... and 74 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx (1)

58-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Promo copy hardcodes values that already exist as constants.

"$100 or more, up to $100" duplicates MIN_QUALIFYING_AMOUNT/MAX_BONUS, and "10%" duplicates BONUS_PERCENT. If the promo terms change, it's easy to update the constants (which the header comment says must stay in sync with the backend) but forget this string.

♻️ Proposed refactor
-            Get 10% bonus credits on your first purchase of $100 or more, up to $100.
+            Get {BONUS_PERCENT}% bonus credits on your first purchase of ${MIN_QUALIFYING_AMOUNT} or more, up to ${MAX_BONUS}.
🤖 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
`@apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx`
around lines 58 - 66, Update the promo text in FirstPurchaseBonusAlert to render
MIN_QUALIFYING_AMOUNT, MAX_BONUS, and BONUS_PERCENT instead of hardcoded 10%,
$100, and $100 values. Preserve the existing wording and qualification behavior
while formatting the interpolated values appropriately for users.
🤖 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
`@apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx`:
- Around line 34-38: Update the query state check in FirstPurchaseBonusAlert to
use isSuccess instead of isFetched, and gate the existing early return on that
success state. Preserve the hasPaidBefore check and avoid rendering the bonus
alert when the payment transactions request fails.

---

Nitpick comments:
In
`@apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx`:
- Around line 58-66: Update the promo text in FirstPurchaseBonusAlert to render
MIN_QUALIFYING_AMOUNT, MAX_BONUS, and BONUS_PERCENT instead of hardcoded 10%,
$100, and $100 values. Preserve the existing wording and qualification behavior
while formatting the interpolated values appropriately for users.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed94b9bc-7c76-4f26-a443-425679a07000

📥 Commits

Reviewing files that changed from the base of the PR and between f1b322b and 9e7c5a8.

📒 Files selected for processing (6)
  • apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.spec.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.tsx
  • apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.spec.tsx
  • apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx
  • apps/deploy-web/src/queries/usePaymentQueries.ts
  • apps/deploy-web/src/types/feature-flags.ts

@baktun14
baktun14 force-pushed the feat/billing-first-purchase-bonus-ui branch from 9e7c5a8 to 5d9c51f Compare July 13, 2026 21:37
@github-actions github-actions Bot added size: M and removed size: S labels Jul 13, 2026
@baktun14
baktun14 force-pushed the feat/billing-first-purchase-bonus-ui branch from 6250080 to 3e5ff5c Compare July 14, 2026 13:38
@github-actions github-actions Bot added size: L and removed size: M labels Jul 14, 2026

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
apps/api/src/billing/services/stripe/stripe.service.spec.ts (1)

134-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use mock() instead of an as unknown as cast in this spec.

Create a mock<Stripe.Response<Stripe.ApiList<Stripe.Charge>>>() and assign data and has_more after creation. As per coding guidelines, *.spec.ts tests must use mock<T>() instead of as unknown as <Type>.

Suggested replacement
-      const mockCharges = {
-        data: [bonusCharge, plainCharge],
-        has_more: false
-      } as unknown as Stripe.Response<Stripe.ApiList<Stripe.Charge>>;
+      const mockCharges = mock<Stripe.Response<Stripe.ApiList<Stripe.Charge>>>();
+      mockCharges.data = [bonusCharge, plainCharge];
+      mockCharges.has_more = false;
🤖 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 `@apps/api/src/billing/services/stripe/stripe.service.spec.ts` around lines 134
- 137, Replace the as unknown as cast in the mockCharges setup with
mock<Stripe.Response<Stripe.ApiList<Stripe.Charge>>>(). After creating the mock,
assign its data and has_more properties to the existing bonusCharge/plainCharge
values and false.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@apps/api/src/billing/services/stripe/stripe.service.spec.ts`:
- Around line 134-137: Replace the as unknown as cast in the mockCharges setup
with mock<Stripe.Response<Stripe.ApiList<Stripe.Charge>>>(). After creating the
mock, assign its data and has_more properties to the existing
bonusCharge/plainCharge values and false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0d82d35-ff96-4d37-b900-2baefda07cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 3198ecd and 1275f2d.

📒 Files selected for processing (21)
  • apps/api/src/billing/http-schemas/stripe.schema.ts
  • apps/api/src/billing/repositories/stripe-transaction/stripe-transaction.repository.ts
  • apps/api/src/billing/services/stripe/stripe.service.spec.ts
  • apps/api/src/billing/services/stripe/stripe.service.ts
  • apps/api/src/types/transactions.ts
  • apps/api/test/seeders/stripe-transaction.seeder.ts
  • apps/deploy-web/src/components/auth/AddCreditsSheet/AddCreditsSheet.tsx
  • apps/deploy-web/src/components/billing-usage/AccountOverview/AccountOverview.spec.tsx
  • apps/deploy-web/src/components/billing-usage/AccountOverview/AccountOverview.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsAmountFields/AddCreditsAmountFields.spec.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsAmountFields/AddCreditsAmountFields.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.spec.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.tsx
  • apps/deploy-web/src/components/billing-usage/AddCreditsTabs/AddCreditsTabs.tsx
  • apps/deploy-web/src/components/billing-usage/BillingView/BillingView.spec.tsx
  • apps/deploy-web/src/components/billing-usage/BillingView/BillingView.tsx
  • apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx
  • apps/deploy-web/src/components/billing-usage/PaymentSuccessAnimation/PaymentSuccessAnimation.spec.tsx
  • apps/deploy-web/src/components/billing-usage/PaymentSuccessAnimation/PaymentSuccessAnimation.tsx
  • apps/deploy-web/tests/ui/pages/AddCreditsSheetPage.ts
  • packages/http-sdk/src/stripe/stripe.types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/deploy-web/src/components/billing-usage/FirstPurchaseBonusAlert/FirstPurchaseBonusAlert.tsx

Covers the new findByChargeIds method so codecov patch coverage for api passes.
@baktun14
baktun14 added this pull request to the merge queue Jul 14, 2026
Merged via the queue into main with commit 2b2d01c Jul 14, 2026
99 of 100 checks passed
@baktun14
baktun14 deleted the feat/billing-first-purchase-bonus-ui branch July 14, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants