Skip to content

Transactions table#901

Merged
BilalG1 merged 9 commits intostore-price-idfrom
transactions-table
Sep 20, 2025
Merged

Transactions table#901
BilalG1 merged 9 commits intostore-price-idfrom
transactions-table

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Sep 18, 2025

Screenshot 2025-09-17 at 8 19 04 PM <

!--

Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

High-level PR Summary

This PR introduces a transactions feature to the payment system, allowing users to view and manage payment history across different transaction types. The implementation includes a database schema update to add customerType to the ItemQuantityChange table, a new API endpoint for retrieving transaction data, and a UI interface in the dashboard to display transactions in a filterable table format. The PR consolidates subscription purchases, one-time purchases, and item quantity changes into a unified transaction view with appropriate filtering options for customer type and transaction type. The implementation also includes end-to-end tests to verify the functionality of the new transactions API.

⏱️ Estimated Review Time: 15-30 minutes

💡 Review Order Suggestion
Order File Path
1 apps/backend/prisma/schema.prisma
2 apps/backend/prisma/migrations/20250918005821_item_quantity_change_customer_type/migration.sql
3 apps/backend/src/app/api/latest/payments/items/[customer_type]/[customer_id]/[item_id]/update-quantity/route.ts
4 packages/stack-shared/src/interface/crud/transactions.ts
5 apps/backend/src/app/api/latest/internal/payments/transactions/route.tsx
6 packages/stack-shared/src/interface/admin-interface.ts
7 packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
8 packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
9 apps/dashboard/src/components/data-table/transaction-table.tsx
10 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/transactions/page.tsx
11 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/transactions/page-client.tsx
12 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
13 apps/e2e/tests/backend/endpoints/api/v1/internal/transactions.test.ts
14 packages/stack-shared/src/schema-fields.ts
15 packages/stack-ui/src/components/data-table/toolbar.tsx

Review by RecurseML

🔍 Review performed on 4950494..e4e07d2

  Severity     Location     Issue     Delete  
Medium apps/e2e/tests/backend/endpoints/api/v1/internal/transactions.test.ts:207 URL concatenation with string templates
Medium apps/e2e/tests/backend/endpoints/api/v1/internal/transactions.test.ts:236 URL concatenation with string templates
Medium packages/stack-shared/src/interface/crud/transactions.ts:4 Variable name uses camelCase instead of snake_case for REST API schema
Medium packages/stack-shared/src/interface/crud/transactions.ts:20 Type name derived from incorrectly named schema variable
Medium packages/stack-shared/src/interface/admin-interface.ts:607 Parameter value not converted to snake_case for API request
Medium packages/stack-shared/src/interface/admin-interface.ts:609 Improper URL construction using string concatenation
✅ Files analyzed, no issues (2)

apps/backend/src/app/api/latest/internal/payments/transactions/route.tsx
apps/dashboard/src/components/data-table/transaction-table.tsx

⏭️ Files skipped (trigger manually) (10)
  Locations     Trigger Analysis  
apps/backend/prisma/migrations/20250918005821_item_quantity_change_customer_type/migration.sql Analyzed
apps/backend/prisma/schema.prisma Analyzed
apps/backend/src/app/api/latest/payments/items/[customer_type]/[customer_id]/[item_id]/update-quantity/route.ts Analyzed
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/transactions/page-client.tsx Analyze
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/transactions/page.tsx Analyzed
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx Analyze
packages/stack-shared/src/schema-fields.ts Analyzed
packages/stack-ui/src/components/data-table/toolbar.tsx Analyzed
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts Analyzed
packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts Analyzed

Need help? Join our Discord


Important

Add transactions feature with database, API, UI, and test updates for managing payment history.

  • Database:
    • Add customerType column to ItemQuantityChange table in schema.prisma and migration.sql.
  • API:
    • New endpoint in route.tsx for retrieving transactions with filters for type and customer_type.
    • Update update-quantity/route.ts to include customerType in item quantity changes.
  • UI:
    • Add TransactionTable component in transaction-table.tsx for displaying transactions with filtering options.
    • Add transactions page in page.tsx and page-client.tsx.
    • Update sidebar-layout.tsx to include transactions in navigation.
  • Tests:
    • Add end-to-end tests in transactions.test.ts for transaction retrieval and pagination.
  • Models:
    • Define AdminTransaction type in transactions.ts.
  • Misc:
    • Add listTransactions method to admin-interface.ts and admin-app-impl.ts.

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

@vercel
Copy link

vercel bot commented Sep 18, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-backend Error Error Sep 20, 2025 6:17am
stack-dashboard Error Error Sep 20, 2025 6:17am
stack-demo Ready Ready Preview Comment Sep 20, 2025 6:17am
stack-docs Error Error Sep 20, 2025 6:17am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests

✅ Unit Test PR creation complete.

  • Create PR with unit tests
  • Commit unit tests in branch transactions-table
  • Post copyable unit tests in a comment

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.

@BilalG1 BilalG1 marked this pull request as ready for review September 18, 2025 03:17
@claude
Copy link
Contributor

claude bot commented Sep 18, 2025

Claude finished @BilalG1's task —— View job


I'll analyze this and get back to you.

Comment on lines 7 to 8
-- AlterTable
ALTER TABLE "ItemQuantityChange" ADD COLUMN "customerType" "CustomerType" NOT NULL;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

todo: migration here with existing customerId

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 Summary

This PR introduces a comprehensive transactions feature that consolidates payment-related activities from multiple sources into a unified view. The implementation spans database schema changes, API endpoints, frontend components, and comprehensive testing.

Database Changes: The core change adds a customerType field to the ItemQuantityChange table, enabling consistent categorization across all payment transaction types (subscriptions, one-time purchases, and item quantity changes). This allows the system to distinguish between USER, TEAM, and CUSTOM customer types uniformly.

API Implementation: A new internal transactions endpoint (/api/latest/internal/payments/transactions) aggregates data from three separate database tables using complex cursor-based pagination. The endpoint supports filtering by transaction type and customer type, with parallel database queries and in-memory sorting to provide a chronological view of all payment activities.

Frontend Integration: The dashboard gains a new transactions page with filtering capabilities, accessible through the payments sidebar. The implementation follows the established patterns in the codebase, using the same data table architecture and admin interface patterns found throughout the application.

Supporting Infrastructure: The changes include schema validation updates, admin interface methods, template implementations, and comprehensive end-to-end tests covering various transaction scenarios and pagination edge cases.

The implementation maintains consistency with existing patterns - database migrations follow Prisma conventions, the API uses the established SmartRouteHandler pattern, and the frontend components follow the same architecture as other admin features like the user table.

Confidence score: 2/5

  • This PR contains a critical database migration issue that will break production deployments with existing data
  • Score lowered due to the dangerous database migration that adds a NOT NULL column without handling existing records
  • Pay close attention to the migration file and consider adding a default value or data backfill strategy

15 files reviewed, 3 comments

Edit Code Review Bot Settings | Greptile

Comment on lines 4 to 8
- Added the required column `customerType` to the `ItemQuantityChange` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "ItemQuantityChange" ADD COLUMN "customerType" "CustomerType" NOT NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: This migration will fail if the ItemQuantityChange table has existing data. Adding a NOT NULL column without a default value to a populated table is not possible. Need to either add a default value, make the column nullable initially, or provide a data migration strategy.

Context Used: Rule - When modifying database schema with NOT NULL constraints or enum changes, ensure migrations handle existing data properly to avoid failures on populated tables. (link)


export function TransactionTable() {
const app = useAdminApp();
const [filters, setFilters] = React.useState<{ cursor?: string, limit?: number, type?: 'subscription' | 'one_time' | 'item_quantity_change', customerType?: 'user' | 'team' | 'custom' }>({
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider extracting the filter type definition to avoid duplication between the state type and the onUpdate function parameters.

cursor: options.cursor,
limit: options.limit,
type: options.columnFilters.find(f => f.id === 'type')?.value as any,
customerType: options.columnFilters.find(f => f.id === 'customer_type')?.value as any,
Copy link
Contributor

Choose a reason for hiding this comment

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

style: The mapping from 'customer_type' column ID to 'customerType' filter property creates inconsistent naming. Consider using consistent naming throughout.

@BilalG1 BilalG1 requested a review from N2D4 September 18, 2025 03:19
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.

for tomorrow this is good to merge once you finish the migration code in the SQL. eventualyl, we'll want the following layout tho:

/**
 * A transaction is an event where one of the following happens:
 * 
 * - An item quantity changes
 * - An offer quantity changes
 * - Money is transferred between the customer and the platform
 * - A combination of the above
 * 
 * The type of the transaction is determined by what triggered it (eg. a checkout flow, automatic renewal, etc.)
 */
type Transaction = {
  id: string,
  createdAtMillis: number,
  effectiveAtMillis: number,  // may differ sometimes, especially for subscription cancellations or renewals
  settledAtMillis: number,  // if a purchase, 

  /**
   * The item quantity changes for this transaction. Note that it does not include any quantity changes that were caused by offer quantity changes, as those are in the separate offerQuantityChanges array.
   */
  itemQuantityChanges: {
    itemId: string,
    quantity: number,
  }[],
  offerQuantityChanges: {
    offerId?: string,
    offer: Offer,
    priceId: string,
    quantity: number,

    // subscriptionId or oneTimePurchaseId can be set, or neither, but never both (the priceId's recurrence must match whichever of the two is set)
    subscriptionId?: string,
    oneTimePurchaseId?: string,
  }[],

  /**
   * Before fees, in the currency of the transaction. May be negative for refunds.
   */
  chargedAmount: null | {
    [currencyCode: Uppercase<CurrencyCode>]: MoneyAmount,
  },
  /**
   * After fees, in the currency of the recipient (currently always USD, although this might change when we expand to other currencies). May be negative for refunds.
   */
  receivedAmount: null | {
    USD: MoneyAmount,
  },
  
  customerType: "user" | "team" | "custom",
  customerId: string,
  
  testMode: boolean,

} & (
  | {
    type: "purchase",  // a purchase, usually happens at the end of a checkout flow
    itemQuantityChanges: [],
  }
  | {
    type: "refund",
    originalTransactionId: string,  // not always 
  }
  | {
    type: "refund_reversal",  // very rare, see stripe docs
    originalRefundTransactionId: string,
  }
  | {
    type: "automatic_subscription_renewal",
    subscriptionId: string,
    itemQuantityChanges: [],
    offerQuantityChanges: [],
  }
  | {
    type: "subscription_cancellation",
    subscriptionId: string,
    itemQuantityChanges: [],
  }
  | {
    type: "manual_item_quantity_change",
    offerQuantityChanges: [],
    chargedAmount: null,
    receivedAmount: null,
    testMode: false,
  }
)

@BilalG1 BilalG1 merged commit c931105 into store-price-id Sep 20, 2025
6 of 17 checks passed
@BilalG1 BilalG1 deleted the transactions-table branch September 20, 2025 06:09
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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

@stack-auth stack-auth deleted a comment from coderabbitai bot Sep 24, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Caution

CodeRabbit plans did not produce any file changes.

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

⚙️ Scanning changes in 4950494..8d7f42d for bugs...

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.

2 participants