feat: Revenue ingest API - #523
Conversation
📝 WalkthroughWalkthroughAdds API-based revenue ingestion: new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as RevenueController
participant Service as RevenueService
participant DB as Database
participant Analytics
Client->>Controller: POST /log/revenue (LogRevenueDto)
activate Controller
Controller->>DB: Fetch project & revenue config
DB-->>Controller: Project record
Controller->>Service: Check for Stripe/Paddle connection
Service-->>DB: read keys
DB-->>Service: keys present?
alt Stripe/Paddle connected
Controller-->>Client: 409 Conflict
else allowed (API or first-call)
Controller->>Service: connectApi() if needed
Service->>DB: update project (revenueApiEnabled=true, set currency, clear keys)
DB-->>Service: updated project
Controller->>Service: convert amount & map type/status
Service->>DB: insert transaction
DB-->>Service: transactionId
Service->>DB: update revenueLastSyncAt
DB-->>Service: updated
Service->>Analytics: emit REVENUE_API_INGEST
Analytics-->>Service: recorded
Controller-->>Client: 200 {success: true, transactionId}
end
deactivate Controller
sequenceDiagram
participant User as ProjectOwner
participant Controller as RevenueController
participant Service as RevenueService
participant DB as Database
User->>Controller: POST /revenue/connect (provider='api')
activate Controller
Controller->>Service: connectApi(projectId, currency)
activate Service
Service->>DB: read project
DB-->>Service: project
Service->>DB: update project (revenueApiEnabled=true, clear keys, set currency, reset sync)
DB-->>Service: updated
Service-->>Controller: {success:true}
Controller->>Controller: emit REVENUE_SETUP(provider:'api')
Controller-->>User: 200 OK
deactivate Service
deactivate Controller
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/apps/cloud/src/revenue/dto/log-revenue.dto.ts`:
- Around line 74-80: The currency DTO currently only checks length and
uppercases the value, allowing non-alphabetic strings; update the currency field
validation in log-revenue.dto.ts by adding a pattern match to enforce ISO 4217
(three uppercase letters) — keep the `@Transform`(({ value }) => typeof value ===
'string' ? value.toUpperCase() : value) and add `@Matches`(/^[A-Z]{3}$/, {
message: 'currency must be a 3-letter ISO 4217 code' }) alongside `@IsNotEmpty`()
and `@IsString`() so the currency property only accepts three alphabetic uppercase
characters.
In `@backend/apps/cloud/src/revenue/revenue.controller.ts`:
- Around line 476-478: The targetCurrency is being derived from the stale
pre-update project.revenueCurrency when isFirstCall is true; ensure you use the
updated currency instead by either (A) using the updated project returned from
the DB update operation (e.g., assign savedProject and read
savedProject.revenueCurrency) or (B) when isFirstCall is true, default to
dto.currency explicitly (i.e., compute targetCurrency = isFirstCall ?
dto.currency : project.revenueCurrency || 'USD'), and update any subsequent
logic that uses targetCurrency so it uses the corrected value; reference
variables: project.revenueCurrency, isFirstCall, dto.currency, and
targetCurrency in revenue.controller.ts.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: a705ec0f-ecae-424a-9da8-e25732657958
📒 Files selected for processing (14)
backend/apps/cloud/src/project/entity/project.entity.tsbackend/apps/cloud/src/project/project.controller.tsbackend/apps/cloud/src/revenue/dto/connect-revenue.dto.tsbackend/apps/cloud/src/revenue/dto/log-revenue.dto.tsbackend/apps/cloud/src/revenue/interfaces/revenue.interface.tsbackend/apps/cloud/src/revenue/revenue.controller.tsbackend/apps/cloud/src/revenue/revenue.service.tsbackend/migrations/mysql/2026_04_27_revenue_api_enabled.sqldocs/components/provider-icons.tsxdocs/content/docs/analytics-dashboard/revenue-tracking.mdxdocs/content/docs/api/events.mdxdocs/content/docs/script-reference.mdxdocs/mdx-components.tsxdocs/tsconfig.tsbuildinfo
There was a problem hiding this comment.
🧹 Nitpick comments (1)
backend/apps/cloud/src/revenue/revenue.controller.ts (1)
489-498: Minor: Redundant refund check.
isRefundis computed at line 489 but the type mapping ternary at lines 491-496 re-checksdto.type === 'refund'. Consider usingisRefundin the ternary for consistency:const type = isRefund ? RevenueType.REFUND : dto.type === 'subscription' ? RevenueType.SUBSCRIPTION : RevenueType.SALE🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/apps/cloud/src/revenue/revenue.controller.ts` around lines 489 - 498, The code redundantly checks dto.type === 'refund' twice; update the ternary that sets the local variable type to reuse the previously computed isRefund instead of re-evaluating dto.type === 'refund'. Specifically, change the type assignment that currently uses dto.type === 'refund' to use isRefund (keeping the rest of the nested ternary for 'subscription' vs 'sale' intact), so that isRefund, type, and status (RevenueStatus.REFUNDED/COMPLETED) remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@backend/apps/cloud/src/revenue/revenue.controller.ts`:
- Around line 489-498: The code redundantly checks dto.type === 'refund' twice;
update the ternary that sets the local variable type to reuse the previously
computed isRefund instead of re-evaluating dto.type === 'refund'. Specifically,
change the type assignment that currently uses dto.type === 'refund' to use
isRefund (keeping the rest of the nested ternary for 'subscription' vs 'sale'
intact), so that isRefund, type, and status (RevenueStatus.REFUNDED/COMPLETED)
remain consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a7f3f504-f2cc-4010-b8b7-625f92d8d7bb
📒 Files selected for processing (2)
backend/apps/cloud/src/revenue/dto/log-revenue.dto.tsbackend/apps/cloud/src/revenue/revenue.controller.ts
Changes
If applicable, please describe what changes were made in this pull request.
Community Edition support
Database migrations
Documentation
Summary by CodeRabbit
New Features
Documentation