Skip to content

feat: Revenue ingest API - #523

Merged
Blaumaus merged 3 commits into
mainfrom
feature/revenue-api
Apr 28, 2026
Merged

feat: Revenue ingest API#523
Blaumaus merged 3 commits into
mainfrom
feature/revenue-api

Conversation

@Blaumaus

@Blaumaus Blaumaus commented Apr 28, 2026

Copy link
Copy Markdown
Member

Changes

If applicable, please describe what changes were made in this pull request.

Community Edition support

  • Your feature is implemented for the Swetrix Community Edition
  • This PR only updates the Cloud (Enterprise) Edition code (e.g. Paddle webhooks, blog, payouts, etc.)

Database migrations

  • Clickhouse / MySQL migrations added for this PR
  • No table schemas changed in this PR

Documentation

  • You have updated the documentation according to your PR
  • This PR did not change any publicly documented endpoints

Summary by CodeRabbit

  • New Features

    • Added API-based revenue source alongside Stripe/Paddle, toggleable per project
    • Introduced POST /log/revenue for direct transaction ingestion with validation and idempotency handling
    • Revenue analytics available when API source is enabled
    • New provider icons (Stripe, Paddle, API) in docs/UI
  • Documentation

    • Expanded revenue tracking guide with API setup, sample requests, field reference, and conflict/permission notes

@Blaumaus Blaumaus self-assigned this Apr 28, 2026
@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds API-based revenue ingestion: new revenueApiEnabled project flag, DTOs and controller endpoint for POST /log/revenue, service methods to connect/disconnect API ingestion, DB migration, UI icons, and documentation updates to support an API provider alongside Stripe and Paddle.

Changes

Cohort / File(s) Summary
Entity & DB
backend/apps/cloud/src/project/entity/project.entity.ts, backend/migrations/mysql/2026_04_27_revenue_api_enabled.sql
Adds revenueApiEnabled boolean field to Project entity and a migration to add the column (default false).
Revenue DTOs
backend/apps/cloud/src/revenue/dto/connect-revenue.dto.ts, backend/apps/cloud/src/revenue/dto/log-revenue.dto.ts
ConnectRevenueDto now accepts provider 'api' and makes apiKey optional; adds new LogRevenueDto with validation for ingestion payloads (pid, amount, currency, type, metadata, etc.).
Revenue Interfaces
backend/apps/cloud/src/revenue/interfaces/revenue.interface.ts
Adds API = 'api' to RevenueProvider enum.
Revenue Service
backend/apps/cloud/src/revenue/revenue.service.ts
Adds connectApi and disconnectApi; updates getRevenueStatus to treat API as a provider; ensure other connect/disconnect flows set revenueApiEnabled=false.
Revenue Controller
backend/apps/cloud/src/revenue/revenue.controller.ts
connectRevenue handles provider='api' via connectApi; adds authenticated POST /log/revenue (logRevenue) to ingest transactions, prevent conflicts with Stripe/Paddle, auto-enable API on first ingest, convert currency, persist transaction, update sync time, and emit analytics; route supports log/revenue and v1/log/revenue.
Project Controller
backend/apps/cloud/src/project/project.controller.ts
Tightens method-level auth: @Auth()@Auth(true) on createProjectView, updateProjectView, deleteProjectView.
Docs & UI
docs/components/provider-icons.tsx, docs/mdx-components.tsx, docs/content/docs/analytics-dashboard/revenue-tracking.mdx, docs/content/docs/api/events.mdx, docs/content/docs/script-reference.mdx
Adds Stripe, Paddle, and Revenue API icons; registers icons for MDX; expands docs to document API ingestion endpoint, request schema, examples, idempotency, conflict behavior, and profile-id guidance.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • feat: Revenue analytics #454: Modifies the same revenue subsystem (entities, DTOs, controllers, services) and overlaps on API-based revenue ingestion and connection flows.

Poem

🐰 I hopped to the server with cheer,

New routes for revenue now appear,
Stripe and Paddle and API too—
Transactions hop in, tidy and true,
My nose twitches: metrics are clear!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description uses the template structure and correctly marks checkboxes, but provides no implementation details about what was actually changed, what the API does, or how it functions despite extensive modifications across multiple files. Add a detailed summary of the changes under the 'Changes' section describing the new revenue API endpoint, its functionality, request/response format, and how it integrates with existing revenue providers.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title "feat: Revenue ingest API" accurately and concisely summarizes the main feature added: an API endpoint for revenue ingestion, which aligns with the widespread changes across entity, DTO, service, controller, database, and documentation files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/revenue-api

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between e214c29 and aae3d72.

📒 Files selected for processing (14)
  • backend/apps/cloud/src/project/entity/project.entity.ts
  • backend/apps/cloud/src/project/project.controller.ts
  • backend/apps/cloud/src/revenue/dto/connect-revenue.dto.ts
  • backend/apps/cloud/src/revenue/dto/log-revenue.dto.ts
  • backend/apps/cloud/src/revenue/interfaces/revenue.interface.ts
  • backend/apps/cloud/src/revenue/revenue.controller.ts
  • backend/apps/cloud/src/revenue/revenue.service.ts
  • backend/migrations/mysql/2026_04_27_revenue_api_enabled.sql
  • docs/components/provider-icons.tsx
  • docs/content/docs/analytics-dashboard/revenue-tracking.mdx
  • docs/content/docs/api/events.mdx
  • docs/content/docs/script-reference.mdx
  • docs/mdx-components.tsx
  • docs/tsconfig.tsbuildinfo

Comment thread backend/apps/cloud/src/revenue/dto/log-revenue.dto.ts
Comment thread backend/apps/cloud/src/revenue/revenue.controller.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/apps/cloud/src/revenue/revenue.controller.ts (1)

489-498: Minor: Redundant refund check.

isRefund is computed at line 489 but the type mapping ternary at lines 491-496 re-checks dto.type === 'refund'. Consider using isRefund in 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

📥 Commits

Reviewing files that changed from the base of the PR and between aae3d72 and d32680a.

📒 Files selected for processing (2)
  • backend/apps/cloud/src/revenue/dto/log-revenue.dto.ts
  • backend/apps/cloud/src/revenue/revenue.controller.ts

@Blaumaus
Blaumaus merged commit b4b040f into main Apr 28, 2026
12 checks passed
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.

1 participant