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

Skip to content

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Oct 17, 2025

High-level PR Summary

This PR adds support for custom metadata to inline products in the payments system. The change allows developers to attach arbitrary metadata to products created inline (without pre-configuration), which Stack Auth will store and return with the product. This enables applications to associate custom data such as feature flags, reference IDs, or other application-specific attributes with products. The implementation adds a new productSchemaWithMetadata schema, updates the product type handling in the backend, and includes comprehensive e2e tests verifying metadata is persisted and returned correctly through purchase creation, validation, and listing endpoints.

⏱️ Estimated Review Time: 15-30 minutes

💡 Review Order Suggestion
Order File Path
1 packages/stack-shared/src/schema-fields.ts
2 apps/backend/src/lib/payments.tsx
3 apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
4 apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts
5 apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts

Need help? Join our Discord

Analyze latest changes

Summary by CodeRabbit

  • New Features

    • Products now support custom metadata (client, client read-only, and server) and expose these fields in inline product representations.
    • Metadata is preserved and propagated through purchase creation, validation, grants, and owned-product listings so it’s available after purchase.
  • Tests

    • Added end-to-end tests verifying metadata is accepted, persisted, and returned in purchase creation, validation, grant, and listing flows.

Important

Adds support for custom metadata in inline products, updating schemas and functions to handle metadata, with comprehensive tests verifying the changes.

  • Behavior:
    • Adds support for custom metadata in inline products, allowing arbitrary metadata attachment.
    • Updates ensureProductIdOrInlineProduct() and productToInlineProduct() in payments.tsx to handle metadata.
    • Metadata is preserved and returned in purchase creation, validation, and listing endpoints.
  • Schemas:
    • Adds productSchemaWithMetadata in schema-fields.ts to include clientMetadata, clientReadOnlyMetadata, and serverMetadata.
    • Updates inlineProductSchema to support metadata fields.
  • Tests:
    • Adds e2e tests in purchase-session.test.ts, create-purchase-url.test.ts, and products.test.ts to verify metadata handling.

This description was created by Ellipsis for 1b5601c. You can customize this summary. It will automatically update as commits are pushed.

@vercel
Copy link

vercel bot commented Oct 17, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Oct 26, 2025 11:39am
stack-dashboard Ready Ready Preview Comment Oct 26, 2025 11:39am
stack-demo Ready Ready Preview Comment Oct 26, 2025 11:39am
stack-docs Ready Ready Preview Comment Oct 26, 2025 11:39am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Walkthrough

Threads client-, client-read-only-, and server-side product metadata through shared schemas, backend payments types and functions (ProductWithMetadata), inline product generation, owned-product responses, and E2E tests to preserve and validate metadata across purchase flows.

Changes

Cohort / File(s) Summary
Schema extensions
packages/stack-shared/src/schema-fields.ts
Adds productClientMetadataSchema, productClientReadOnlyMetadataSchema, productServerMetadataSchema, productMetadataExample; introduces and exports productSchemaWithMetadata (concat of productSchema + metadata); extends inlineProductSchema with client_metadata, client_read_only_metadata, and server_metadata.
Payments backend updates
apps/backend/src/lib/payments.tsx
Imports productSchemaWithMetadata; defines type ProductWithMetadata = yup.InferType<typeof productSchemaWithMetadata>; updates signatures/returns to use ProductWithMetadata for ensureProductIdOrInlineProduct, productToInlineProduct, grantProductToCustomer; casts owned purchases/subscriptions to ProductWithMetadata; propagates metadata into inline product generation, price/included_items mappings, and owned-product responses.
E2E tests — create-purchase-url / validate-code
apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts, .../validate-code.test.ts, .../outdated--validate-code.test.ts
Adds test asserting inline product server_metadata is returned when validating purchase code; updates snapshots/expectations to include client_metadata, client_read_only_metadata, and server_metadata (often null).
E2E tests — product listings & grants
apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts
Updates fixtures and assertions to include client_metadata, client_read_only_metadata, and server_metadata (null or provided); adds inline-grant payloads that carry server_metadata.
E2E tests — purchase session / test-mode purchase
apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
Adds test that creates an inline product with server_metadata, completes a test-mode purchase, and verifies persisted owned/inline product listing includes the provided metadata.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Backend
    participant PaymentsLib
    participant Schema

    Client->>Backend: POST /create-purchase (inline product + metadata)
    Backend->>PaymentsLib: ensureProductIdOrInlineProduct(inlineProduct)
    PaymentsLib->>Schema: validate with productSchemaWithMetadata
    Schema-->>PaymentsLib: ProductWithMetadata (validated)
    PaymentsLib->>PaymentsLib: productToInlineProduct(ProductWithMetadata)
    Note right of PaymentsLib: inline representation now carries client/server metadata
    PaymentsLib-->>Backend: inline product (with metadata)
    Backend->>PaymentsLib: grantProductToCustomer(ProductWithMetadata)
    PaymentsLib-->>Backend: grant persisted (metadata attached)
    Client->>Backend: GET /owned-products
    Backend->>PaymentsLib: getOwnedProductsForCustomer()
    PaymentsLib-->>Backend: owned products as ProductWithMetadata (include metadata)
    Backend-->>Client: owned products (with metadata)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • improve payment error messages #931 — touches apps/backend/src/lib/payments.tsx and ensureProductIdOrInlineProduct; likely overlaps on signature/validation changes.
  • dashboard grant product #939 — modifies grantProductToCustomer and inline product handling in payments backend; may overlap on metadata propagation.
  • Payments test mode toggle #929 — edits payment product-access/validation logic in the same backend file; may intersect on product resolution and access checks.

Suggested reviewers

  • N2D4

Poem

🐇 I hopped through schemas, stitched metadata tight,
Client and server tucked in for the flight.
Inline to owned, each carrot-lined mile,
Tiny fields carried on a rabbit smile.
Hooray — metadata hops along in style!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "inline product metadata" is concise, specific, and directly summarizes the primary change in this PR. It clearly indicates that the changeset adds metadata support to inline products, which aligns with the main functionality additions across all modified files—schema definitions, backend function updates, and comprehensive end-to-end tests verifying metadata handling. A developer scanning the history would immediately understand the purpose and scope of this change.
Description Check ✅ Passed The PR description is comprehensive and well-structured, significantly exceeding the minimal repository template requirements. It provides a clear high-level summary explaining the purpose (adding custom metadata support to inline products), details the implementation across three areas (behavior, schemas, and tests), includes an estimated review time, and offers a suggested review order for the modified files. All key changes are documented, and the description provides sufficient context for reviewers to understand the scope and impact of the changes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch inline-product-metadata

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.

Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

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

Review by RecurseML

🔍 Review performed on 542d444..d42cf54

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (5)

apps/backend/src/lib/payments.tsx
apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts
apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts
apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
packages/stack-shared/src/schema-fields.ts

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Summary

This PR adds support for custom metadata to inline products in the payments system, allowing developers to attach arbitrary JSON data to products created without pre-configuration.

Key Changes:

  • Added productMetadata schema field (optional JSON) and productSchemaWithMetadata type that extends the base product schema
  • Updated inlineProductSchema to include the metadata field
  • Modified backend payment logic to use ProductWithMetadata type throughout, ensuring metadata flows through product creation, validation, and retrieval
  • Added metadata handling in ensureProductIdOrInlineProduct and productToInlineProduct functions
  • Comprehensive e2e tests verify metadata persists correctly through purchase creation, code validation, and product listing endpoints

Testing Coverage:

  • Metadata persistence through test-mode purchase flow
  • Metadata returned in purchase code validation
  • Metadata included in product grant responses

The implementation is clean and follows existing patterns in the codebase.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are well-isolated, properly typed, and thoroughly tested. The metadata field is optional and uses the existing jsonSchema pattern. All type signatures are updated consistently, and comprehensive e2e tests verify the feature works correctly across multiple endpoints.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/stack-shared/src/schema-fields.ts 5/5 Added productMetadata schema and productSchemaWithMetadata to support optional metadata field, added metadata to inlineProductSchema
apps/backend/src/lib/payments.tsx 5/5 Updated to use ProductWithMetadata type throughout, added metadata handling in inline product conversion and type casts

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Backend API
    participant Payments as payments.tsx
    participant Schema as schema-fields
    participant DB as Database

    Note over Client,DB: Creating Purchase with Inline Product + Metadata

    Client->>API: POST /create-purchase-url<br/>{product_inline: {metadata: {...}}}
    API->>Payments: ensureProductIdOrInlineProduct()
    Payments->>Schema: Validate inlineProductSchema<br/>(includes metadata field)
    Schema-->>Payments: Valid inline product
    Payments->>Payments: Convert to ProductWithMetadata<br/>(metadata field preserved)
    Payments-->>API: ProductWithMetadata object
    API->>DB: Store purchase code with<br/>product data (including metadata)
    DB-->>API: Success
    API-->>Client: Purchase URL

    Note over Client,DB: Validating Purchase Code

    Client->>API: POST /validate-code<br/>{full_code: "..."}
    API->>DB: Retrieve purchase code data
    DB-->>API: Product with metadata
    API-->>Client: Product details<br/>(metadata included)

    Note over Client,DB: Completing Purchase & Listing Products

    Client->>API: POST /purchase-session<br/>{full_code: "..."}
    API->>Payments: grantProductToCustomer()
    Payments->>DB: Create subscription/purchase<br/>(product as ProductWithMetadata)
    DB-->>Payments: Success
    Payments-->>API: Grant result
    API-->>Client: Success

    Client->>API: GET /products/user/{userId}
    API->>DB: Fetch owned products
    DB-->>API: Products with metadata
    API->>Payments: Cast to ProductWithMetadata
    API-->>Client: Product list<br/>(metadata included)
Loading

5 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@BilalG1 BilalG1 requested a review from N2D4 October 17, 2025 17:31
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Oct 17, 2025
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.

we should have clientReadonlyMetadata and serverMetadata (in the future we could also have clientMetadata — maybe we can implement it now, even if in the SDK there's no way to make use it?)

@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Oct 17, 2025
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3152e05 and 20c9691.

📒 Files selected for processing (5)
  • apps/backend/src/lib/payments.tsx (6 hunks)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts (1 hunks)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts (16 hunks)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (1 hunks)
  • packages/stack-shared/src/schema-fields.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

In tests, prefer .toMatchInlineSnapshot where possible; refer to snapshot-serializer.ts for snapshot formatting and handling of non-deterministic values

Files:

  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
  • packages/stack-shared/src/schema-fields.ts
  • apps/backend/src/lib/payments.tsx
🧬 Code graph analysis (2)
apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (1)
apps/e2e/tests/backend/backend-helpers.ts (1)
  • niceBackendFetch (107-171)
apps/backend/src/lib/payments.tsx (2)
packages/stack-shared/src/schema-fields.ts (2)
  • productSchemaWithMetadata (600-604)
  • inlineProductSchema (606-631)
apps/backend/src/lib/tenancies.tsx (1)
  • Tenancy (47-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: Security Check
🔇 Additional comments (7)
packages/stack-shared/src/schema-fields.ts (2)

593-604: LGTM: Metadata schemas properly defined.

The metadata schemas follow the existing pattern for user and team metadata. The use of concat() to extend productSchema is appropriate and type-safe.

Minor observation: All three metadata schemas share the same exampleValue. Consider differentiating them to clarify their distinct purposes (e.g., client-visible flags for clientMetadata, subscription status for clientReadOnlyMetadata, internal correlation IDs for serverMetadata).


628-630: LGTM: Inline product metadata fields properly added.

The metadata fields are correctly added to inlineProductSchema with appropriate snake_case naming, consistent with other fields in the schema. The use of the same metadata schemas ensures consistency between config-based and inline products.

apps/backend/src/lib/payments.tsx (4)

4-4: LGTM: ProductWithMetadata type properly introduced.

The import and type definition follow the existing pattern for Product. Both types now coexist in the codebase, with ProductWithMetadata used for public APIs and storage while Product remains for internal use.

Also applies to: 19-19


27-27: LGTM: Metadata properly threaded through inline product construction.

The return type correctly reflects that products now carry metadata, and the inline product path properly maps all three metadata fields from the snake_case input to camelCase output.

Also applies to: 65-67


427-427: LGTM: Product-to-inline conversion includes metadata.

The function signature correctly accepts ProductWithMetadata, and the metadata fields are properly mapped to the snake_case inline product format with appropriate null fallbacks.

Also applies to: 434-436


562-562: LGTM: Grant function signature updated for metadata support.

The parameter type correctly reflects that products with metadata can now be granted, ensuring metadata persists through purchase creation.

apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (1)

439-510: LGTM: End-to-end test validates inline product metadata persistence.

The test properly verifies that server_metadata flows through the purchase creation, test-mode purchase, and product listing endpoints. The use of toMatchInlineSnapshot aligns with coding guidelines.

Minor observation: Consider adding assertions for client_metadata and client_read_only_metadata in a follow-up test to ensure comprehensive coverage of all metadata types.

As per coding guidelines

@BilalG1 BilalG1 requested a review from N2D4 October 17, 2025 23:14
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Oct 17, 2025
@cursor
Copy link

cursor bot commented Oct 18, 2025

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on November 10.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b5601c and cd6c0c2.

📒 Files selected for processing (2)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts (1 hunks)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/e2e/tests/backend/endpoints/api/v1/payments/create-purchase-url.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use ES6 Maps instead of Records wherever possible in TypeScript code

Files:

  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

When writing tests, prefer .toMatchInlineSnapshot over other selectors where possible

Files:

  • apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
🧬 Code graph analysis (1)
apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (2)
apps/e2e/tests/helpers.ts (1)
  • it (12-12)
apps/e2e/tests/backend/backend-helpers.ts (1)
  • niceBackendFetch (108-172)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: docker
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: setup-tests
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: all-good
  • GitHub Check: Security Check
🔇 Additional comments (1)
apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (1)

440-511: Well-structured test for inline product metadata persistence.

The test effectively verifies that server_metadata on inline products persists through the test-mode purchase flow and is correctly returned in the product listing. The test follows existing patterns in the file and appropriately uses .toMatchInlineSnapshot() as per the coding guidelines.

@github-actions github-actions bot assigned BilalG1 and unassigned N2D4 Oct 24, 2025
@BilalG1 BilalG1 merged commit 6d9c2b1 into dev Oct 27, 2025
20 checks passed
@BilalG1 BilalG1 deleted the inline-product-metadata branch October 27, 2025 17:03
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