https://www.loom.com/share/59ff826f88324a61bb2fefc51769f840?sid=3fe23444-c56e-46c8-a402-8df38a69403c
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR improves the payment products UI by relocating the save/cancel
buttons from the top to the bottom of the product card and replacing the
generic `EditableInput` component with a custom `ProductEditableInput`
component that better handles the specific styling and interaction
patterns needed for product fields. The changes include better visual
feedback during editing and improved layout alignment for the price
input field.
⏱️ Estimated Review Time: 15-30 minutes
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 |
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx`
|
</details>
[](https://discord.gg/n3SsVDAW6U)
[
<!-- RECURSEML_SUMMARY:END -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* New Features
- Unified editable product input with read-only and inline editing
modes, placeholder support, and explicit Cancel/Save actions.
* Style
- Improved price-editing layout: aligned currency symbol, spacing, and
input visuals for clearer monetary entry.
- Polished input appearance for consistent look and feel across product
views.
* Refactor
- Replaced scattered editable fields with a single reusable input
component across product editing screens for consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Move save/cancel buttons to bottom and replace `EditableInput` with
`ProductEditableInput` for improved UI in
`page-client-catalogs-view.tsx`.
>
> - **UI Changes**:
> - Move save/cancel buttons to the bottom of the product card in
`ProductCard`.
> - Replace `EditableInput` with `ProductEditableInput` for better
styling and interaction in `ProductCard`.
> - **Component Changes**:
> - Add `ProductEditableInput` component to handle product-specific
input styling and behavior.
> - Update `ProductPriceRow` to improve price input layout and visuals.
> - **Misc**:
> - Remove unused import of `EditableInput`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for c09cb54306. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
<!-- ELLIPSIS_HIDDEN -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR fixes a bug in the one-time purchase validation logic by
allowing stackable offers to be purchased multiple times. Previously,
the system blocked duplicate one-time purchases regardless of whether
the offer was marked as stackable. The fix adds a check to only prevent
duplicate purchases when the offer's `stackable` property is not `true`,
enabling customers to purchase stackable offers more than once.
⏱️ Estimated Review Time: 15-30 minutes
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 | `apps/backend/src/lib/payments.tsx` |
| 2 | `apps/backend/src/lib/payments.test.tsx` |
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Fixes purchase validation to allow multiple purchases of stackable
products and updates tests accordingly.
>
> - **Behavior**:
> - Fixes bug in `validatePurchaseSession` in `payments.tsx` to allow
multiple purchases of stackable products.
> - Updates error message for non-stackable products to "Customer
already has purchased this product; this product is not stackable".
> - **Tests**:
> - Adds test cases in `payments.test.tsx` to verify stackable and
non-stackable purchase logic.
> - Ensures stackable products can be purchased multiple times, while
non-stackable products cannot.
> - **Misc**:
> - Minor updates to error messages and test descriptions for clarity.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 113d035b35. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Improved default product handling per catalog with clear errors when
multiple defaults are configured.
- Bug Fixes
- Unified validation for non-stackable products across subscriptions and
one-time purchases, preventing duplicate ownership.
- Standardized error message when attempting to repurchase a
non-stackable product: “Customer already has purchased this product;
this product is not stackable”.
- Tests
- Updated unit and end-to-end tests to reflect non-stackable product
behavior and the new error messaging.
- Adjusted scenarios and expectations around default product selection
and duplicate purchase blocking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR implements a comprehensive renaming of "offer" to "product" and
"offer group" to "product catalog" throughout the codebase. The changes
include database migrations, schema updates, API compatibility layers,
function renames, and updates to client and server implementations.
Backwards compatibility is maintained through migration layers that
handle requests using the old terminology, translating them to the new
terminology before processing. The PR includes documentation of this
approach in CLAUDE-KNOWLEDGE.md. This rename affects multiple parts of
the system including the database schema, API endpoints, error types,
and SDK interfaces.
⏱️ Estimated Review Time: 1-3 hours
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 |
`apps/backend/prisma/migrations/20250923191615_rename_offers_to_products/migration.sql`
|
| 2 |
`apps/backend/src/app/api/migrations/v2beta1/payments/purchases/offers-compat.ts`
|
| 3 |
`apps/backend/src/app/api/migrations/v2beta1/payments/purchases/create-purchase-url/route.ts`
|
| 4 |
`apps/backend/src/app/api/migrations/v2beta1/payments/purchases/validate-code/route.ts`
|
| 5 | `apps/backend/src/lib/payments.tsx` |
| 6 | `.claude/CLAUDE-KNOWLEDGE.md` |
| 7 | `packages/stack-shared/src/schema-fields.ts` |
| 8 | `packages/stack-shared/src/known-errors.tsx` |
| 9 | `packages/stack-shared/src/config/schema.ts` |
| 10 | `packages/template/src/lib/stack-app/customers/index.ts` |
| 11 |
`packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts`
|
| 12 |
`packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts`
|
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Renames 'offer' to 'product' and 'offer group' to 'product catalog'
across the codebase, updating database schema, API endpoints, and
application logic for consistency and backward compatibility.
>
> - **Database**:
> - Rename columns `offer` to `product` and `offerId` to `productId` in
`OneTimePurchase` and `Subscription` tables in `migration.sql`.
> - **API & Migrations**:
> - Update API endpoints to accept `product_id`/`product_inline` instead
of `offer_id`/`offer_inline`.
> - Add `v2beta5` compatibility layer to map legacy `offer` fields to
`product` equivalents.
> - **Shared Schemas**:
> - Rename `offerSchema` to `productSchema` and related schemas in
`schema-fields.ts`.
> - **Server Implementation**:
> - Update `createCheckoutUrl` method in `server-app-impl.ts` to use
`productId`/`InlineProduct`.
> - **Tests**:
> - Update tests to reflect renaming in `backend-helpers.ts` and other
test files.
> - **Miscellaneous**:
> - Remove dummy data related to offers in `dummy-data.tsx`.
> - Update documentation and comments to reflect terminology changes.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for e3227bcbd2. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Backwards-compatibility: legacy offer_id/offer_inline requests are
accepted, normalized, and routed to product-based handlers.
* **Refactor**
* Global rename from Offer/Group → Product/Catalog across UI, APIs,
types, client/server interfaces, and error codes.
* **Bug Fixes**
* Responses, webhooks and UI consistently surface product_display_name
and product-related metadata.
* **Documentation**
* Migration notes and docs updated to explain compatibility and
parameter changes.
* **Tests**
* Unit and E2E suites updated to cover product/catalog flows.
* **Chores**
* Database schema migration, seed and config updates applied.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Renames offers→products and groups→catalogs end-to-end (DB, APIs,
schemas, UI, SDK, docs), adding v2beta5 compatibility to accept legacy
offer fields while updating all internals.
>
> - **Backend/DB**:
> - Prisma migration: rename `offer`/`offerId`→`product`/`productId` in
`OneTimePurchase` and `Subscription`.
> - Update Stripe webhook, purchase-session, and internal test-mode
flows to use `product*` metadata/fields.
> - **API & Migrations**:
> - Latest endpoints now accept `product_id`/`product_inline`.
> - Add `v2beta5` compat layer mapping legacy `offer_id`/`offer_inline`
to product equivalents; responses alias conflicting products.
> - **Shared Schemas/Errors/Config**:
> - `offerSchema`→`productSchema`,
`inlineOfferSchema`→`inlineProductSchema`, prices/types renamed.
> - KnownErrors renamed (e.g., `PRODUCT_DOES_NOT_EXIST`).
> - Config: `groups`→`catalogs`, defaults/migrations updated; improved
override validation messages; ID regex loosened; formatter tweaks; add
schema fuzzer tests.
> - **Payments Lib**:
> - Rename APIs and logic (`offers`→`products`, `groupId`→`catalogId`),
subscription and item-quantity computation updated.
> - **Dashboard/UI**:
> - Routes, dialogs, editors, tables, and code samples switched to
products/catalogs; removed offers dummy data.
> - **SDK/Template**:
> - Client/server `createCheckoutUrl` now uses
`productId`/`InlineProduct`.
> - **Tests/Docs/Utilities**:
> - E2E and unit tests updated; add legacy (pre-rename) tests.
> - Docs and knowledge base revised; minor script tweaks (recent-first,
limits).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e6e20ecd72. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: BilalG1 <bg2002@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR fixes test issues by making two primary changes: 1) In the API
keys test, replacing `Date {}` with actual date strings in test
snapshots to make them deterministic, and 2) Adding "react-dom" to the
node modules list in the email rendering component. These changes likely
address test failures that were occurring due to non-deterministic date
serialization in snapshots and a missing dependency.
⏱️ Estimated Review Time: 5-15 minutes
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 | `apps/e2e/tests/js/api-keys.test.ts` |
| 2 | `apps/backend/src/lib/email-rendering.tsx` |
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Fixes inline snapshot for `expiresAt` in `api-keys.test.ts` and adds
`react-dom` to dependencies in `email-rendering.tsx`.
>
> - **Tests**:
> - Fixes inline snapshot for `expiresAt` in `api-keys.test.ts` by using
a constant `expiresAt` variable.
> - Updates `expiresAt` field in three inline snapshots to use
`expiresAt.toISOString()`.
> - **Dependencies**:
> - Adds `react-dom` version "19.1.1" to `nodeModules` in
`email-rendering.tsx`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 290a6a2cf9. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved reliability of email template rendering, ensuring content
that relies on DOM behavior renders correctly during Freestyle
executions.
* **Tests**
* Unified expiration timestamps in API key tests for consistency and
updated related snapshots.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR fixes a test in the internal metrics endpoint by replacing a
simple wait timer with a more robust retry mechanism. Instead of waiting
for a fixed 3000ms delay to allow for asynchronous event logging, the PR
implements a `Result.retry` approach that attempts to fetch metrics up
to 5 times with exponential backoff, only proceeding when the metrics
response differs from the initial state. This change makes the test more
reliable by ensuring proper test conditions are met before assertions
are made rather than relying on an arbitrary timeout.
⏱️ Estimated Review Time: 5-15 minutes
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 | `apps/e2e/tests/backend/endpoints/api/v1/internal-metrics.test.ts`
|
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Improved backend metrics verification with a retry-based approach to
handle eventual consistency, reducing test flakiness.
* Replaced static waits with structured retries for faster, more
reliable execution.
* Strengthened assertions on metrics fields (e.g., totals and recent
activity) for clearer validation.
* Maintains checks to ensure anonymous users remain excluded from
metrics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Add Convex integration with new auth helpers, update access token
handling, and include documentation, examples, and tests for the new
features.
>
> - **Features**:
> - Add Convex integration with new auth helpers for Convex clients and
HTTP in `client-app-impl.ts` and `server-app-impl.ts`.
> - Support for Convex context in user APIs and partial user retrieval.
> - Access tokens now include `is_anonymous` for better anonymous
handling in `tokens.tsx`.
> - **Documentation**:
> - Add Convex integration guide in `docs/templates/others/convex.mdx`.
> - Update docs navigation in `docs/docs-platform.yml` and
`docs/templates/meta.json`.
> - **Examples**:
> - Add Convex + Next.js example app in `examples/convex` with auth
wiring, functions, schema, and UI.
> - **Tests**:
> - Add E2E tests for Convex auth flows in `convex.test.ts`.
> - Update JWT payload checks in `backend-helpers.ts` and
`anonymous-comprehensive.test.ts`.
> - **Chores**:
> - Add Convex dependencies in `package.json` files.
> - Update CI steps for example environments in `e2e-api-tests.yaml` and
`e2e-source-of-truth-api-tests.yaml`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for aa0983a8b7. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Convex integration: auth helpers for Convex clients/HTTP, Convex-aware
user APIs, and partial-user retrieval (token/convex).
- Access tokens now surface is_anonymous for clearer anonymous handling.
- **Documentation**
- Added Convex integration guide and nav entries.
- **Examples**
- New Convex + Next.js example app with auth wiring, backend functions,
schema, and UI.
- **Tests**
- Added E2E tests covering Convex auth flows and JWT payload checks.
- **Chores**
- Added Convex deps, CI env steps, and workspace/test config updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR adds a new `priceId` field to the `OneTimePurchase` and
`Subscription` models in the database to store Stripe price identifiers.
The change includes database schema updates, corresponding migration
files, and modifications to payment processing logic to properly track
and store price IDs throughout the purchase flow. The implementation
consistently propagates the price ID from Stripe's API responses through
various payment processing endpoints and webhooks handlers, ensuring the
data is properly stored and synced with the database models.
⏱️ Estimated Review Time: 15-30 minutes
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 | `apps/backend/prisma/schema.prisma` |
| 2 |
`apps/backend/prisma/migrations/20250917193043_store_price_id/migration.sql`
|
| 3 | `apps/backend/src/lib/stripe.tsx` |
| 4 |
`apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx`
|
| 5 |
`apps/backend/src/app/api/latest/internal/payments/test-mode-purchase-session/route.tsx`
|
| 6 |
`apps/backend/src/app/api/latest/integrations/stripe/webhooks/route.tsx`
|
| 7 | `apps/e2e/tests/backend/endpoints/api/v1/stripe-webhooks.test.ts`
|
</details>
<!-- RECURSEML_SUMMARY:END -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Add `priceId` field to track Stripe price identifiers in purchase and
subscription models, updating schema, payment logic, and tests.
>
> - **Database Changes**:
> - Add `priceId` field to `OneTimePurchase` and `Subscription` models
in `schema.prisma`.
> - Update database schema with migration
`20250917193043_store_price_id/migration.sql`.
> - **Payment Processing**:
> - Update `processStripeWebhookEvent()` in `webhooks/route.tsx` to
handle `priceId`.
> - Modify `POST` handlers in `purchase-session/route.tsx` and
`test-mode-purchase-session/route.tsx` to include `priceId` in metadata.
> - Update `syncStripeSubscriptions()` in `stripe.tsx` to sync
`priceId`.
> - **Testing**:
> - Add tests in `stripe-webhooks.test.ts` to validate `priceId`
handling in webhook and purchase flows.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 4950494d62. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- RECURSEML_ANALYSIS:START -->
## Review by RecurseML
_🔍 Review performed on
[e48ffa6..4950494](e48ffa67ee...4950494d62)_
| Severity | Location | Issue
| Delete |
|:----------:|----------|-------|:--------:|
|  |
[apps/e2e/tests/backend/endpoints/api/v1/stripe-webhooks.test.ts:153](https://github.com/stack-auth/stack-auth/pull/900#discussion_r2356623574)
| The field `priceId` uses camelCase instead of the required snake_case
for API parameters |
[
|
|  |
[apps/e2e/tests/backend/endpoints/api/v1/stripe-webhooks.test.ts:245](https://github.com/stack-auth/stack-auth/pull/900#discussion_r2356623664)
| The field `priceId` uses camelCase instead of the required snake_case
for API parameters |
[
|
|  |
[apps/e2e/tests/backend/endpoints/api/v1/stripe-webhooks.test.ts:358](https://github.com/stack-auth/stack-auth/pull/900#discussion_r2356623721)
| The field `priceId` uses camelCase instead of the required snake_case
for API parameters |
[
|
<details>
<summary>✅ Files analyzed, no issues (4)</summary>
• `apps/backend/src/lib/stripe.tsx`
•
`apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx`
•
`apps/backend/src/app/api/latest/integrations/stripe/webhooks/route.tsx`
•
`apps/backend/src/app/api/latest/internal/payments/test-mode-purchase-session/route.tsx`
</details>
<details>
<summary>⏭️ Files skipped (trigger manually) (2)</summary>
| Locations | Trigger Analysis |
|-----------|:------------------:|
`apps/backend/prisma/migrations/20250917193043_store_price_id/migration.sql`
|
[
`apps/backend/prisma/schema.prisma` |
[
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_ANALYSIS:END -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Persist the selected price ID with one-time purchases and
subscriptions.
- Carry the price ID through payment flows and Stripe metadata for
better tracking and reporting.
- Test mode purchases now also record the price ID.
- Chores
- Database migration adds a price ID field to purchase and subscription
records.
- Tests
- Updated end-to-end tests to validate price ID handling in webhook and
purchase flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Add support for one-time payments alongside subscriptions, including
database, API, and test updates.
>
> - **Behavior**:
> - Support for one-time purchases added alongside subscriptions.
> - Purchase sessions now accept a quantity parameter for both normal
and test-mode flows.
> - Dashboard checkout auto-selects payment or subscription mode based
on price.
> - **Database**:
> - New `OneTimePurchase` table created in `migration.sql`.
> - `PurchaseCreationSource` enum replaces `SubscriptionCreationSource`
in `schema.prisma`.
> - **API**:
> - `processStripeWebhookEvent()` in `route.tsx` handles one-time
purchase webhooks.
> - `purchase-session/route.tsx` and
`test-mode-purchase-session/route.tsx` updated for one-time purchases.
> - **Tests**:
> - E2E tests added for one-time payments, stackability, and webhook
handling in `purchase-session.test.ts` and `stripe-webhooks.test.ts`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 726ebe2328. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* One-time purchases persisted and supported end-to-end; purchase
sessions accept quantity (default 1). Dashboard checkout auto-selects
payment vs subscription mode.
* **Bug Fixes**
* Centralized Stripe webhook handling with deduplication for retried
payment intents. Improved conflict resolution when switching offers
within groups; add-on and group rules enforced.
* **Tests**
* Expanded E2E and unit tests covering one-time flows, stackability,
test-mode paths, and webhook scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR improves email testing in the end-to-end test suite by adding a
new `waitForMessagesWithSubject` helper method to the `Mailbox` class.
This method replaces the previous pattern of using arbitrary wait times
(e.g., `wait(2000)`) followed by fetching and finding messages, which
could lead to flaky tests. The new approach implements a polling
mechanism that waits until messages with the specified subject appear,
with a reasonable timeout. The PR updates all email-related tests to use
this new helper method, making the tests more reliable and less
dependent on timing assumptions.
⏱️ Estimated Review Time: 0h 15m
<details>
<summary>💡 Review Order Suggestion</summary>
| Order | File Path |
|-------|-----------|
| 1 | `apps/e2e/tests/helpers.ts` |
| 2 | `apps/e2e/tests/backend/endpoints/api/v1/send-email.test.ts` |
| 3 | `apps/e2e/tests/backend/endpoints/api/v1/unsubscribe-link.test.ts`
|
</details>
<!-- RECURSEML_SUMMARY:END -->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Improves email test reliability by adding `waitForMessagesWithSubject`
in `Mailbox` and updating tests to use it.
>
> - **Behavior**:
> - Introduces `waitForMessagesWithSubject` in `Mailbox` class in
`helpers.ts` to replace arbitrary wait times with a polling mechanism.
> - Updates email-related tests in `send-email.test.ts` and
`unsubscribe-link.test.ts` to use `waitForMessagesWithSubject` for more
reliable email verification.
> - **Tests**:
> - Replaces `wait()` calls with `waitForMessagesWithSubject()` in
`send-email.test.ts` and `unsubscribe-link.test.ts`.
> - Ensures emails are verified by subject, reducing flakiness in tests.
> - **Misc**:
> - Minor refactoring in `helpers.ts` to support new functionality.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 90b7bdad01. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- RECURSEML_ANALYSIS:START -->
## Review by RecurseML
_🔍 Review performed on
[d14317c..f42dfc5](d14317c787...f42dfc5351)_
| Severity | Location | Issue | Action |
|----------|----------|-------|--------|
|  |
[apps/e2e/tests/helpers.ts:235](https://github.com/stack-auth/stack-auth/pull/893#discussion_r2342704515)
| Async function call not wrapped with runAsynchronously |
[
|
<details>
<summary>✅ Files analyzed, no issues (2)</summary>
• `apps/e2e/tests/backend/endpoints/api/v1/send-email.test.ts`
• `apps/e2e/tests/backend/endpoints/api/v1/unsubscribe-link.test.ts`
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_ANALYSIS:END -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* More reliable email end-to-end tests by waiting for messages by
subject instead of fixed delays and manual polling.
* Multi-recipient scenarios now independently confirm delivery for each
user.
* Switched to snapshot-style assertions capturing full message metadata
for clearer, more stable verification.
* Unsubscribe email tests now wait deterministically for delivery before
validating links.
* **Chores**
* Mailbox handling improved to reduce flakiness and repeated network
calls.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Implement batch email rendering and sending with enhanced processing
and error handling.
>
> - **Behavior**:
> - Implement batch email rendering and sending in `route.tsx` using
`renderEmailsWithTemplateBatched()` and `sendEmailResendBatched()`.
> - Add per-recipient notification category resolution and unsubscribe
link generation.
> - Support templates from IDs, raw HTML, or drafts with dynamic theme
handling.
> - Enhanced result reporting, including users without primary emails.
> - **Functions**:
> - Add `renderEmailsWithTemplateBatched()` in `email-rendering.tsx` for
batch email rendering.
> - Add `sendEmailResendBatched()` in `emails.tsx` for batch email
sending.
> - Add `getChunks()` in `arrays.tsx` to split arrays into chunks.
> - **Tests**:
> - Add timed waits in `send-email.test.ts` and
`unsubscribe-link.test.ts` to stabilize email delivery checks.
> - **Dependencies**:
> - Add `@react-email/render` and `resend` to `package.json` for email
rendering and sending.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for ff1dea6c31. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- RECURSEML_SUMMARY:START -->
## Review by RecurseML
_🔍 Review performed on
[3c34140..1267879](3c34140aba...1267879cfd)_
| Severity | Location | Issue |
|----------|----------|-------|
|  |
[apps/e2e/tests/backend/endpoints/api/v1/send-email.test.ts:593](https://github.com/stack-auth/stack-auth/pull/875#discussion_r2317293698)
| Asynchronous wait function not wrapped with runAsynchronously |
|  |
[apps/e2e/tests/backend/endpoints/api/v1/send-email.test.ts:743](https://github.com/stack-auth/stack-auth/pull/875#discussion_r2317293796)
| Asynchronous wait function not wrapped with runAsynchronously |
<details>
<summary>✅ Files analyzed, no issues (4)</summary>
• `apps/backend/src/app/api/latest/emails/send-email/route.tsx`
• `apps/backend/src/lib/email-rendering.tsx`
• `apps/backend/src/lib/emails.tsx`
• `packages/stack-shared/src/utils/arrays.tsx`
</details>
<details>
<summary>⏭️ Files skipped (low suspicion) (2)</summary>
• `apps/backend/package.json`
• `pnpm-lock.yaml`
</details>
[](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Batch email rendering and sending to multiple recipients with
background processing.
- Per-recipient notification category resolution and unsubscribe link
generation.
- Support for templates from IDs, raw HTML, or drafts with dynamic theme
handling.
- Enhanced result reporting, including users without primary emails.
- Chores
- Added dependencies for email rendering and bulk sending.
- Tests
- Stabilized email delivery checks with timed waits across relevant e2e
tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
https://www.loom.com/share/cc379c5372244a169f3ae1d2cc91eae5?sid=ec5bc438-56d8-4cca-9bbc-6cf6c6d313ad
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Introduce email draft functionality with AI assistance, including
creation, editing, and sending capabilities, and update APIs and UI
components accordingly.
>
> - **Features**:
> - Add email draft functionality: create, list, edit, preview, and send
drafts.
> - Integrate AI-assisted draft generation with `emailDraftAdapter` in
`email-draft-adapter.ts`.
> - Update `send-email` and `render-email` APIs to support drafts.
> - **Backend**:
> - Add `EmailDraft` model in `schema.prisma`.
> - Implement draft CRUD operations in `email-drafts/route.tsx` and
`email-drafts/[id]/route.tsx`.
> - Update `render-email/route.tsx` and `send-email/route.tsx` to handle
draft inputs.
> - **Frontend**:
> - Add email draft UI in `email-drafts/page-client.tsx` and
`email-drafts/[draftId]/page-client.tsx`.
> - Implement theme selection with `EmailThemeSelector` in
`email-theme-selector.tsx`.
> - Update sidebar navigation in `sidebar-layout.tsx` to include drafts.
> - **Tests**:
> - Add E2E tests for draft lifecycle and sending in
`email-drafts.test.ts` and `send-email.test.ts`.
> - **Misc**:
> - Update `admin-interface.ts` and `server-interface.ts` to support
draft operations.
> - Add `XOR` type utility in `types.tsx` for exclusive option handling.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for c7ebb00ac5. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Email Drafts: create, list, edit, preview, theme-select, AI-assisted
draft generation, send (marks draft as sent) and dashboard UI for
drafting + recipient selection.
* **Improvements**
* Send/render APIs accept html, template, or draft inputs and an
all-users option; per-recipient delivery/reporting, unified theme
selector, expanded chat context for drafts, clearer schema validation
errors, breadcrumb updates.
* **Tests**
* E2E coverage for draft lifecycle, draft-based send, all-users flow,
and updated schema-validation snapshots.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Add OAuth provider client and dashboard with UI components, server and
client interface updates, schema enhancements, and new tests.
>
> - **New Features**:
> - Add UI components in `page-client.tsx` for viewing, adding, editing,
toggling, and removing OAuth providers.
> - Implement `OAuthProviderDialog` and `OAuthProvidersSection` for
managing OAuth providers.
> - **Server and Client Interfaces**:
> - Update `StackServerInterface` and `StackClientInterface` to include
CRUD operations for OAuth providers.
> - Add `createServerOAuthProvider`, `listServerOAuthProviders`,
`updateServerOAuthProvider`, and `deleteServerOAuthProvider` methods.
> - **Schema and Types**:
> - Add `provider_config_id` to OAuth provider schemas in
`oauth-providers.ts` and `schema-fields.ts`.
> - Define `OAuthProvider` and `ServerOAuthProvider` types in
`users/index.ts`.
> - **Tests**:
> - Add `oauth-providers.test.ts` for client-side OAuth provider
functionality.
> - Update existing tests to include `provider_config_id` assertions.
> - **Miscellaneous**:
> - Update `server-app-impl.ts` and `client-app-impl.ts` to handle OAuth
provider operations.
> - Enhance error handling for account-ID conflicts in OAuth provider
operations.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for cd0ceb8ccb. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* User UI to view, add, edit, toggle, and remove OAuth providers; client
& server APIs, hooks, and caching to manage providers.
* **Improvements**
* OAuth provider responses now include a provider_config_id field for
clearer provider identification.
* Better client/server APIs for managing providers and improved
user-facing error handling for account-ID conflicts.
* **Bug Fixes**
* Tests updated to assert presence of provider_config_id.
* **Documentation**
* Added types/interfaces for OAuth provider entities and user methods.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Add secure Neon connection string handling and update connection
routes with new APIs and tests.
>
> - **Features**:
> - Add Neon integration APIs in `route.tsx` for project provisioning
and branch connection string registration.
> - Securely store Neon connection strings in a data vault in
`prisma-client.tsx` and `seed.ts`.
> - Automatically run migrations on provision/update in `route.tsx`.
> - **Refactor**:
> - Change schema resolution to asynchronous in `crud.tsx` and
`metrics/route.tsx`.
> - **Chores**:
> - Add backend environment variables for various services in
`package.json`.
> - Add new backend dependency `@stackframe/stack` in `package.json`.
> - **Tests**:
> - Add end-to-end tests for Neon provisioning and updates in
`provision.test.ts`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 4cd96a74ff. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Added Neon integration APIs to provision projects and register branch
connection strings.
- Securely store Neon connection strings in the data vault and run
migrations automatically on provision/update.
- Refactor
- Switched schema resolution to asynchronous calls across sessions,
users, and internal metrics for improved reliability.
- Chores
- Introduced comprehensive backend environment variables for auth,
email, storage, webhooks, telemetry, and payments.
- Added a new backend dependency for stack integration.
- Tests
- Expanded end-to-end coverage for Neon provisioning, updates,
validation, and vault-based decryption.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Enhances payments system with stackable items, Stripe account
management, and improved purchase flow, including schema updates and new
tests.
>
> - **Behavior**:
> - Adds quantity support for stackable offers in
`apps/backend/src/lib/payments.tsx` and
`apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx`.
> - Introduces Stripe account info viewing and onboarding in
`apps/backend/src/app/api/latest/internal/payments/stripe/account-info/route.ts`.
> - Implements "Include by default" pricing and "Plans" group in
`apps/backend/prisma/seed.ts`.
> - **Schema Changes**:
> - Adds `quantity` and `offerId` columns to `Subscription` table in
`apps/backend/prisma/migrations/20250821212828_subscription_quantity/migration.sql`
and
`apps/backend/prisma/migrations/20250822203223_subscription_offer_id/migration.sql`.
> - Adds `stripeAccountId` column to `Project` table in
`apps/backend/prisma/migrations/20250825221947_stripe_account_id/migration.sql`.
> - **Improvements**:
> - Enhances purchase flow to return Stripe `client_secret` and handle
subscription upgrades/downgrades in
`apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx`.
> - Updates item management with new actions and protections in
`apps/backend/src/app/api/latest/payments/items/[customer_type]/[customer_id]/[item_id]/update-quantity/route.ts`.
> - Tightens validation for customer type and offer conflicts in
`apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts`.
> - **Testing**:
> - Adds extensive tests for new payment features in
`apps/e2e/tests/backend/endpoints/api/v1/internal/payments/setup.test.ts`
and
`apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts`.
> - **Misc**:
> - Removes unused `stripeAccountId` and `stripeAccountSetupComplete`
from `branchPaymentsSchema` in
`packages/stack-shared/src/config/schema.ts`.
> - Refactors currency constants into `currency-constants.tsx` in
`packages/stack-shared/src/utils`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 264563541d. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Quantity support for stackable offers across checkout, test purchases,
and admin flows.
- View Stripe account info and interactive payments onboarding per
project.
- "Include-by-default" pricing and new "Plans" group (Free, Extra
Admins).
- **Improvements**
- Purchase flow returns Stripe client_secret and handles group-based
subscription upgrades/downgrades.
- Item management: Update Customer Quantity action, edit/delete
protections, and read-only form mode.
- Validation surfaces offer conflicts (already_bought_non_stackable,
conflicting_group_offers).
- **Changes**
- Default item quantities now start at 0 unless explicitly granted.
- Stripe account linkage is stored per project.
- **Tests**
- Expanded tests for quantities, stackable behavior, and group
transition scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- ELLIPSIS_HIDDEN -->
----
> [!IMPORTANT]
> Introduces 'custom' customer type support across API, models, and
tests, updating methods and validation schemas accordingly.
>
> - **New Features**:
> - Added 'custom' customer type support in `schema.prisma`,
`payments.tsx`, and `server-app-impl.ts`.
> - **API Changes**:
> - Updated API endpoints in `route.ts` files to include `customer_type`
parameter.
> - Modified `createPurchaseUrl` and `updateItemQuantity` methods to
handle 'custom' type.
> - **Models**:
> - Added 'CUSTOM' to `CustomerType` enum in `schema.prisma`.
> - Changed `customerId` type to `TEXT` in `Subscription` and
`ItemQuantityChange` models.
> - **Validation**:
> - Updated `customerTypeSchema` in `schema-fields.ts` to include
'custom'.
> - **Tests**:
> - Added and updated tests in `payments.test.ts` and `items.test.ts`
for 'custom' type scenarios.
> - **Misc**:
> - Removed `createPurchaseUrl` method from `admin-interface.ts`.
> - Updated `client-app-impl.ts` and `server-app-impl.ts` to support
'custom' type in item-related methods.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 509762d716. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>
----
<!-- ELLIPSIS_HIDDEN -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added universal "custom" customer type; test-mode purchase flow for
admins.
* **API Changes**
* Routes and payloads now include customer_type and accept non‑UUID
customer identifiers; validation and error responses updated.
* **Breaking Changes**
* SDK/Admin interfaces updated (new payload shapes, removed
createPurchaseUrl, added testModePurchase); item quantity routes now
include customer_type.
* **Dashboard/UI**
* New Offers and Items pages, dialogs, and ConnectPayments integration;
customerType options include "custom".
* **Tests**
* E2E and unit tests updated/added for custom and test‑mode flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>