Add content delivery + owner commission accounting - #506
Conversation
New POST downloads/tests resolves a gama-api legacy test-file download URL through a new IContentDeliveryProvider (ContentSource-keyed, mirroring the payment-gateway provider pattern), charges the downloader via the existing quota-then-points path only when gama-api hasn't already marked the download paid, and accrues a commission to the content's owner (resolved from gama-api's CoreId) only if that charge succeeds - a new ContentOwnerCommission ledger, deliberately kept separate from the points wallet and subscription quota. Commission percent and payout threshold are admin-configurable via ApplicationSettings; the points-to-USD rate is a fixed first-phase constant. Payout itself is out of scope for this phase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tPaper POST downloads (renamed from downloads/tests) now dispatches on ContentType to one of gama-api's three download endpoints: PastPaper/Test -> tests/download (unchanged), Multimedia -> files/download, Exam -> exams/download. Confirmed live that the latter two report neither a price nor an owner, so charging and commission accrual are skipped entirely for those two types - only PastPaper/Test involve SpendPointsAsync/ContentOwnerCommission. Also collapses GameService.SpendPointsAsync's PastPaper/Test branch: both are the same gama-api content (/tests/download), so both now charge FeatureCodes.PastpaperDownload/TransactionType.DownloadPastPaper. The old TestDownload/DownloadTest members stay defined for historical data but are no longer written by any code path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…feature Reverts the earlier collapse of GameService.SpendPointsAsync's PastPaper/Test branch - that endpoint is unrelated to this feature and its separate FeatureCodes.TestDownload/TransactionType.DownloadTest entitlement (already live on a subscription plan) stays untouched. GamaApiContentDeliveryProvider now explicitly rejects any ContentType other than PastPaper/Multimedia/Exam (notably ContentType.Test) with a clear validation error before attempting a gama-api call. ContentType.Test and TransactionType.DownloadTest remain defined in their enums only because migration 20260621193350_TransactionType.cs compiles a reference to both in a historical data-backfill statement - migrations are immutable, so neither member can be removed even though this feature no longer accepts Test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…reality The broader ContentType enum (shared with the unrelated games/spends endpoint) still has a Test member, so reusing it here meant Swagger kept advertising Test as a valid value for POST downloads even after runtime rejection was added - a client reading the schema had no way to know it would always fail. DownloadContentType is a new 3-member enum (PastPaper/Multimedia/Exam) used only by this feature's request/provider DTOs and ViewModel. A request naming Test now fails cleanly at model binding (Required on an unparseable enum) instead of via a bespoke validation message. ContentOwnerCommission and GameService.SpendPointsAsync are untouched - ContentDeliveryService maps the one case that ever reaches them (DownloadContentType.PastPaper, the only type gama-api reports a price for) to ContentType.PastPaper directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A real gama-api download can report price.price: 0 with paid: false (e.g. some extra-file downloads) - the previous logic only skipped the charge for Paid == true or a missing Points field, so a zero-price item still went through SpendPointsAsync and wrote a pointless zero-amount Transaction row. Confirmed live (id 39204/extra/5222): before the fix a Transaction row with Points: 0 was written on every call; after, none is. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🤖 AI Generated Review This review was generated automatically and does not replace human review. Review chunk 1/6SummaryThis pull request introduces a new content delivery system along with accounting for owner commissions. It includes a Potential Issues
Suggestions
Positive Feedback
Review chunk 2/6SummaryThe pull request introduces content delivery and commission accounting features. It specifically adds classes and enumerations to manage content owner commission data and update related entities like Potential Issues
Suggestions
Positive Feedback
Review chunk 3/6SummaryThe pull request aims to enhance the database schema by defining several entity models related to user identity and educational institutions. These models include user accounts, user identities, roles, claims, schools, and associated entities like images, boards, and comments. The schema changes consist of defining new entities with appropriate properties, constraints, indices, and relationships, representing the application's domain for user and content management. Potential Issues
Suggestions
Positive Feedback
Review chunk 4/6SummaryThis pull request introduces several SQL Server entity configurations for various domain entities in the GamaEdtech application. The changes define properties, foreign key relationships, data types, constraints, and indexes for tables like "SubscriptionPlans", "Tickets", "Tags", and more, enhancing the application's database schema to support content delivery and owner commission accounting. Potential IssuesNo significant issues found. Suggestions
Positive Feedback
Review chunk 5/6SummaryThe pull request introduces several key changes, including the addition of content delivery functionality and the implementation of owner commission accounting features. It includes a new migration file to create a Potential Issues
Suggestions
Positive Feedback
Review chunk 6/6SummaryThe pull request introduces a new Potential Issues
Suggestions
Positive Feedback
|
ContentOwnerCommission (#506) was write-only: rows accrued on every paid PastPaper download but there was no way to see them. Adds IContentDeliveryService.GetContentOwnerCommissionsAsync (list + paging, same Specification pattern as GetPaymentsAsync) behind two endpoints: GET downloads/commissions (User, forced to the caller's own OwnerUserId - no ownerUserId field exists on this request model at all, unlike the admin one) and GET admin/contentownercommissions (Admin, any/all owners, optional ownerUserId filter). Both filterable by startDate/endDate. Payout (crossing ContentOwnerCommissionPayoutThresholdUsd) stays out of scope - this is reporting only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
POST api/v1/downloadsresolves a download URL from one of gama-api's three legacy endpoints, selected byContentType:PastPaper/Test→GET /tests/download/{id}/{type}[/{extraId}]— requiresFileType, reportsownerUID/price.paid.Multimedia→GET /files/download/{id}— confirmed live, returns only{url, name}, no owner/price.Exam→GET /exams/download/{id}— confirmed live, same thin shape.All confirmed against real gama-api responses (its
openapi.yamldoesn't document response bodies).IGameService.SpendPointsAsync, quota-then-points) only when the source reports a price and hasn't already marked it paid — soMultimedia/Examare unconditionally free through this endpoint, since gama-api reports no price for either.CoreId) only when the source reports an owner and the charge above succeeded — so commission never applies toMultimedia/Exameither. NewContentOwnerCommissionledger, deliberately separate from the points wallet and subscription quota. Commission percent + payout threshold are admin-configurable viaApplicationSettings; points→USD is a fixed first-phase constant (100 points = $1). Payout itself is out of scope for this phase.IContentDeliveryProvider(ContentSource-keyed, mirroring the payment-gateway provider pattern) dispatches internally onContentTypeto build the right gama-api URL —ContentSource(which external system) andContentType(which kind of content) are kept as separate axes on purpose.CommissionReasonis a separate enum fromContentSource, anticipating a future non-download commission trigger (e.g. a blog-publish bonus) that wouldn't involve an external content source at all.GameService.SpendPointsAsync's PastPaper/Test branch — both route to the same gama-api content (/tests/download), so both now chargeFeatureCodes.PastpaperDownload/TransactionType.DownloadPastPaper. The oldTestDownload/DownloadTestmembers stay defined (historicalTransaction/quota-consumption data references them) but are no longer written by any code path. Confirmed via the dev DB that noSubscriptionPlanFeatureconfig depended on keeping them separate beyond test fixtures.docs/business/content-delivery.md, plus updates todocs/api/endpoints.md,docs/database/schema.md,docs/database/migrations.md,docs/architecture/design-patterns.md,docs/business/payments-and-points.md,docs/business/subscriptions.md,PROJECT_SNAPSHOT.md.staging, no overlap.Test plan
dotnet build— clean, 0 warnings/errors.ContentTypes:PastPaper(paid=true path): no charge, no commission, correct URL.PastPaperwithtype=extra/extraId(paid=false, price=0 path): charge ran (Transactionrow confirmed via DB), commission correctly skipped (owner'sCoreIdhas no linked local account — confirmed via DB).Multimedia: URL resolved, no charge attempted (no price reported).Exam: URL resolved, no charge attempted (no price reported).FileTypeforPastPaper: clean validation error, not an exception.ApplicationSettingsfields are visible/editable viaGET/PUT api/v1/admin/applicationsettingsas an Admin-role user.🤖 Generated with Claude Code