Skip to content

Add monetized subscription support to gateway#1708

Closed
nisan-abeywickrama wants to merge 2 commits intowso2:mainfrom
nisan-abeywickrama:monetization
Closed

Add monetized subscription support to gateway#1708
nisan-abeywickrama wants to merge 2 commits intowso2:mainfrom
nisan-abeywickrama:monetization

Conversation

@nisan-abeywickrama
Copy link
Copy Markdown
Contributor

Purpose

This pull request introduces support for tracking billing information in API subscriptions throughout the gateway controller and runtime. The main changes add billingCustomerId and billingSubscriptionId fields to the subscription data model, API requests/responses, database schema, event payloads, and analytics. This enables enhanced analytics and integration with billing systems.

Approach

Subscription Model and Database Schema Updates:

  • Added billingCustomerId and billingSubscriptionId fields to the Subscription model, API request/response structs, and OpenAPI spec (management-openapi.yaml, subscription.go, generated.go). [1] [2] [3] [4] [5]
  • Updated both PostgreSQL and SQLite schemas to include the new billing fields in the subscriptions table, and incremented schema version. [1] [2] [3] [4] [5] [6]
  • Modified all relevant SQL queries and storage logic to handle the new billing fields when saving, updating, or retrieving subscriptions. [1] [2] [3] [4] [5] [6] [7]

API and Event Handling:

  • Updated subscription creation and response handling to include the new billing fields, ensuring they are properly set and returned in API responses. [1] [2]
  • Extended the subscription event payload and event handling logic to propagate billing information through control plane events. [1] [2]

Analytics and Runtime Integration:

  • Added billing fields to the analytics event structure and ensured they are populated from subscription metadata, enabling billing-aware analytics events. [1] [2] [3]
  • Updated the subscription snapshot logic to include billing fields in the data sent to the runtime.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 2026

Warning

Rate limit exceeded

@nisan-abeywickrama has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 11 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de505650-9479-49f8-bb25-9cd4fe7021e5

📥 Commits

Reviewing files that changed from the base of the PR and between 07ce9fb and 705d9b6.

📒 Files selected for processing (4)
  • gateway/gateway-controller/pkg/api/management/generated.go
  • gateway/gateway-controller/pkg/storage/sqlite_test.go
  • gateway/gateway-controller/tests/integration/schema_test.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/dto/unwrappedEvent.go

Walkthrough

This PR adds two optional billing identifier fields (billingCustomerId and billingSubscriptionId) to subscription APIs, data models, database schema (incrementing version from 1 to 2), and analytics event payloads across the gateway controller, runtime, and SDK components.

Changes

Cohort / File(s) Summary
API Schema & Generated Models
gateway/gateway-controller/api/management-openapi.yaml, gateway/gateway-controller/pkg/api/management/generated.go
Added billingCustomerId and billingSubscriptionId as optional string fields to SubscriptionCreateRequest and SubscriptionResponse schemas with omitempty tags.
Domain Models
gateway/gateway-controller/pkg/models/subscription.go, sdk/core/policyengine/subscription_xds.go
Extended Subscription and SubscriptionData structs with BillingCustomerID and BillingSubscriptionID fields; added corresponding JSON/database tags for serialization and persistence.
Database Layer
gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql, gateway/gateway-controller/pkg/storage/gateway-controller-db.sql, gateway/gateway-controller/pkg/storage/sql_store.go, gateway/gateway-controller/pkg/storage/sqlite.go
Incremented database schema version from 1 to 2; added billing_customer_id and billing_subscription_id nullable columns to subscriptions table; updated INSERT/SELECT/UPDATE statements in SQL store to handle new billing columns across all subscription queries.
Gateway Controller - Handlers & Events
gateway/gateway-controller/pkg/api/handlers/subscription_handler.go, gateway/gateway-controller/pkg/controlplane/events.go, gateway/gateway-controller/pkg/controlplane/client.go
Handler now assigns billing fields from request to persisted model; event payload extended with BillingCustomerID and BillingSubscriptionID; event client conditionally populates these fields when present before upserting subscription.
Subscription Snapshot & Store
gateway/gateway-controller/pkg/subscriptionxds/subscription_snapshot.go, sdk/core/policyengine/subscription_store.go
Snapshot builder populates PlanName and billing identifiers in SubscriptionData when available; subscription store adds PlanName, BillingCustomerID, and BillingSubscriptionID fields to SubscriptionEntry during cache population.
Analytics - Constants & DTOs
gateway/gateway-runtime/policy-engine/internal/analytics/constants.go, gateway/gateway-runtime/policy-engine/internal/analytics/dto/subscription.go, gateway/gateway-runtime/policy-engine/internal/analytics/dto/event.go, gateway/gateway-runtime/policy-engine/internal/analytics/dto/unwrappedEvent.go
Added new metadata key constants for billing/subscription fields; introduced dto.Subscription struct with getters/setters for billing customer/subscription IDs, status, and plan name; extended Event and DefaultResponseEvent DTOs to include subscription data.
Analytics - Processing & Publishing
gateway/gateway-runtime/policy-engine/internal/analytics/analytics.go, gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go
prepareAnalyticEvent now creates and populates dto.Subscription from metadata; Moesif publisher conditionally enriches event metadata with non-empty subscription fields when present.
System Policies - Analytics
gateway/system-policies/analytics/analytics.go
Added metadata key constants for billing/subscription fields; OnResponseHeaders now reads and propagates these metadata values into analytics metadata map.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers Purpose, Approach with detailed subsections, but is missing critical sections: Goals, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment from the template. Complete the description by adding the missing template sections: Goals, User stories, Documentation impact, test coverage details, security verification checklist, sample details, related PRs, and test environment specifications.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding monetized subscription support to the gateway, which accurately reflects the core purpose of adding billing identifiers throughout the system.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gateway/gateway-controller/pkg/api/handlers/subscription_handler.go (1)

247-267: ⚠️ Potential issue | 🟠 Major

SubscriptionUpdateRequest is missing billing fields from generated code, contradicting the OpenAPI schema.

The management-openapi.yaml spec defines billingCustomerId and billingSubscriptionId as updatable fields in SubscriptionUpdateRequest (lines 3887–3892), but the generated Go struct (line 1537–1539 in generated.go) contains only the Status field. This schema–code mismatch means clients can send these fields in update requests, but they are silently dropped during JSON unmarshaling, never reaching the handler at all.

To fix this, either:

  1. Remove billingCustomerId and billingSubscriptionId from the SubscriptionUpdateRequest schema in management-openapi.yaml to reflect that they are not updatable, or
  2. Regenerate the Go types to include these fields and then handle them in UpdateSubscription (and emit them in SubscriptionUpdatedEventPayload to maintain consistency with CreateSubscription/subscriptionToResponse).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/subscription_handler.go` around
lines 247 - 267, The generated SubscriptionUpdateRequest struct is missing
billingCustomerId and billingSubscriptionId (schema/code mismatch); either
remove those fields from the OpenAPI schema or regenerate the Go types to
include them and then update the UpdateSubscription handler (where req is bound
with s.bindRequestBody) to copy req.BillingCustomerId and
req.BillingSubscriptionId into the subscription model, and ensure those values
are included in SubscriptionUpdatedEventPayload and in subscriptionToResponse so
updates are persisted and returned consistently with CreateSubscription.
🧹 Nitpick comments (3)
gateway/gateway-runtime/policy-engine/internal/analytics/dto/subscription.go (1)

21-26: Consider omitempty on the subscription JSON tags.

When Event.Subscription is non-nil but some fields are empty (e.g. anonymous or non-billed traffic), this struct will serialize "billingCustomerId":"", "billingSubscriptionId":"", etc. Adding ,omitempty on each tag would keep analytics payloads cleaner and symmetric with the Moesif publisher which already guards on non-empty. Not a correctness issue.

Proposed change
-	BillingCustomerID     string `json:"billingCustomerId"`
-	BillingSubscriptionID string `json:"billingSubscriptionId"`
-	Status                string `json:"status"`
-	PlanName              string `json:"planName"`
+	BillingCustomerID     string `json:"billingCustomerId,omitempty"`
+	BillingSubscriptionID string `json:"billingSubscriptionId,omitempty"`
+	Status                string `json:"status,omitempty"`
+	PlanName              string `json:"planName,omitempty"`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-runtime/policy-engine/internal/analytics/dto/subscription.go`
around lines 21 - 26, The Subscription struct currently always emits empty
strings for missing fields; update the JSON tags on BillingCustomerID,
BillingSubscriptionID, Status, and PlanName in the Subscription struct to
include ",omitempty" so fields with empty values are omitted during JSON
serialization (this affects places that marshal Event.Subscription). Ensure the
change is applied to the Subscription type definition only.
sdk/core/policyengine/subscription_store.go (1)

17-25: Naming inconsistency between store and xDS DTO (nit).

SubscriptionEntry uses BillingCustomerID/BillingSubscriptionID (Go idiom, uppercase ID) while the source SubscriptionData in sdk/core/policyengine/subscription_xds.go uses BillingCustomerId/BillingSubscriptionId. Both compile, but aligning the two (preferably on the Go-idiomatic ID form, unless you want to match the existing APIId/ApplicationId style in SubscriptionData) avoids subtle developer confusion when searching the codebase.

Also, the billing *string pointers are shared (not cloned) between the incoming SubscriptionData and the stored SubscriptionEntry — fine given callers treat them as immutable, but worth keeping in mind if you ever start mutating fetched entries.

Also applies to: 70-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/core/policyengine/subscription_store.go` around lines 17 - 25, Rename the
billing fields in SubscriptionEntry to use the Go-idiomatic ID capitalization to
match subscription_xds DTO (change BillingCustomerID and BillingSubscriptionID
to the ID form if they are not already), update any mapping code that converts
from SubscriptionData (which has BillingCustomerId/BillingSubscriptionId) to
SubscriptionEntry to use the new names, and ensure the mapping either explicitly
copies the string values (allocating new strings) if you want to avoid sharing
the incoming *string pointers or documents that pointer sharing is intentional;
refer to the SubscriptionEntry struct and the SubscriptionData DTO when making
these changes.
gateway/gateway-controller/pkg/controlplane/events.go (1)

333-340: Nit: struct field alignment is inconsistent.

The added fields use an extra tab between name and type, so the column doesn't line up with neighboring payload structs. gofmt will rewrite this; worth running it to keep the diff clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/controlplane/events.go` around lines 333 -
340, The struct field alignment for fields APIID, SubscriptionID, ApplicationID,
SubscriptionToken, SubscriptionPlanId, Status, BillingCustomerID, and
BillingSubscriptionID is inconsistent (extra tab spacing); fix by normalizing
spacing so each field name uses a single tab (or spaces) before the type to
match neighboring payload structs and then run gofmt (or go fmt) on
gateway-controller/pkg/controlplane/events.go to reformat the file and keep the
diff clean.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gateway/gateway-controller/pkg/controlplane/events.go`:
- Around line 332-341: The SubscriptionUpdatedEventPayload is missing
BillingCustomerID and BillingSubscriptionID so billing changes can't
propagate—add those two fields (same names and json tags as in
SubscriptionCreatedEventPayload) to the SubscriptionUpdatedEventPayload struct
and wire them through the update path: accept and populate them in the
UpdateSubscription handler (subscription_handler.go), ensure the event published
to client.go includes them, have the DB upsert logic consume and persist them,
and make sure the xDS snapshot/updater reads the persisted billing IDs so
downstream consumers receive updated values.

In
`@gateway/gateway-runtime/policy-engine/internal/analytics/dto/unwrappedEvent.go`:
- Around line 73-76: DefaultResponseEvent gained billing/subscription fields
(BillingCustomerID, BillingSubscriptionID, SubscriptionStatus,
SubscriptionPlanName) but DefaultFaultEvent was not changed; add the same four
fields and JSON tags to DefaultFaultEvent so fault events (throttled,
ext_authz_denied, etc.) carry the same billing identifiers for downstream
analytics and billing. Locate the struct named DefaultFaultEvent and mirror the
field names and tags from DefaultResponseEvent, keeping types and JSON keys
identical.

In `@gateway/system-policies/analytics/analytics.go`:
- Around line 29-33: analytics.go defines metadata constants
(BillingCustomerIDMetadataKey, BillingSubscriptionIDMetadataKey,
SubscriptionStatusMetadataKey, SubscriptionPlanNameMetadataKey) that are read
from SharedContext.Metadata but there is no producer in this repo that writes
those keys; locate the subscription-validation or billing producer (or implement
one) and ensure it writes matching keys into SharedContext.Metadata using the
same constant names, or if the producer lives in another module, add a clear
integration note and unit/integration tests to verify the keys are populated
before analytics.go reads them; update the producer to set
SharedContext.Metadata["x-wso2-billing-customer-id"]/... (using the constants)
and add a test that analytics.go sees non-empty values.

---

Outside diff comments:
In `@gateway/gateway-controller/pkg/api/handlers/subscription_handler.go`:
- Around line 247-267: The generated SubscriptionUpdateRequest struct is missing
billingCustomerId and billingSubscriptionId (schema/code mismatch); either
remove those fields from the OpenAPI schema or regenerate the Go types to
include them and then update the UpdateSubscription handler (where req is bound
with s.bindRequestBody) to copy req.BillingCustomerId and
req.BillingSubscriptionId into the subscription model, and ensure those values
are included in SubscriptionUpdatedEventPayload and in subscriptionToResponse so
updates are persisted and returned consistently with CreateSubscription.

---

Nitpick comments:
In `@gateway/gateway-controller/pkg/controlplane/events.go`:
- Around line 333-340: The struct field alignment for fields APIID,
SubscriptionID, ApplicationID, SubscriptionToken, SubscriptionPlanId, Status,
BillingCustomerID, and BillingSubscriptionID is inconsistent (extra tab
spacing); fix by normalizing spacing so each field name uses a single tab (or
spaces) before the type to match neighboring payload structs and then run gofmt
(or go fmt) on gateway-controller/pkg/controlplane/events.go to reformat the
file and keep the diff clean.

In
`@gateway/gateway-runtime/policy-engine/internal/analytics/dto/subscription.go`:
- Around line 21-26: The Subscription struct currently always emits empty
strings for missing fields; update the JSON tags on BillingCustomerID,
BillingSubscriptionID, Status, and PlanName in the Subscription struct to
include ",omitempty" so fields with empty values are omitted during JSON
serialization (this affects places that marshal Event.Subscription). Ensure the
change is applied to the Subscription type definition only.

In `@sdk/core/policyengine/subscription_store.go`:
- Around line 17-25: Rename the billing fields in SubscriptionEntry to use the
Go-idiomatic ID capitalization to match subscription_xds DTO (change
BillingCustomerID and BillingSubscriptionID to the ID form if they are not
already), update any mapping code that converts from SubscriptionData (which has
BillingCustomerId/BillingSubscriptionId) to SubscriptionEntry to use the new
names, and ensure the mapping either explicitly copies the string values
(allocating new strings) if you want to avoid sharing the incoming *string
pointers or documents that pointer sharing is intentional; refer to the
SubscriptionEntry struct and the SubscriptionData DTO when making these changes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c66c6193-acae-48c8-ba73-fa7e2a430831

📥 Commits

Reviewing files that changed from the base of the PR and between f1ca2f6 and 07ce9fb.

📒 Files selected for processing (20)
  • gateway/gateway-controller/api/management-openapi.yaml
  • gateway/gateway-controller/pkg/api/handlers/subscription_handler.go
  • gateway/gateway-controller/pkg/api/management/generated.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • gateway/gateway-controller/pkg/controlplane/events.go
  • gateway/gateway-controller/pkg/models/subscription.go
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sql
  • gateway/gateway-controller/pkg/storage/sql_store.go
  • gateway/gateway-controller/pkg/storage/sqlite.go
  • gateway/gateway-controller/pkg/subscriptionxds/subscription_snapshot.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/analytics.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/constants.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/dto/event.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/dto/subscription.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/dto/unwrappedEvent.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go
  • gateway/system-policies/analytics/analytics.go
  • sdk/core/policyengine/subscription_store.go
  • sdk/core/policyengine/subscription_xds.go

Comment thread gateway/gateway-controller/pkg/controlplane/events.go
Comment thread gateway/system-policies/analytics/analytics.go
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