feat(promo-codes): domain-authorized promo codes for early registration access#525
feat(promo-codes): domain-authorized promo codes for early registration access#525caseylocker wants to merge 12 commits intomainfrom
Conversation
…registration access Adds two new promo code subtypes (DomainAuthorizedSummitRegistrationDiscountCode and DomainAuthorizedSummitRegistrationPromoCode) enabling domain-based early registration access. Adds WithPromoCode ticket type audience value for promo-code-only distribution. Includes auto-discovery endpoint, per-account quantity enforcement at checkout, and auto_apply support for existing member/speaker promo code types. SDS: doc/promo-codes-for-early-registration-access.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Task 1: Add ClassName discriminator ENUM widening in migration, add data guard before narrowing Audience ENUM in down() - Task 2: Guard matchesEmailDomain() against emails missing @ to prevent false-positive suffix matches - Task 3: Replace canBeAppliedTo() with direct collection membership check in addTicketTypeRule() (Truth #4), override removeTicketTypeRule() to prevent parent from re-adding to allowed_ticket_types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task 5: accepted NITs for constant naming, interface gap, and pre-existing edge cases. Task 7: MUST-FIX — canBuyRegistrationTicketByType() missing WithPromoCode branch blocks checkout for promo-code-only tickets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace 'sometimes|json' with custom AllowedEmailDomainsArray rule for allowed_email_domains validation — accepts pre-decoded PHP array and validates each entry against @domain.com/.tld/user@email formats - Remove json_decode() from factory populate for both domain-authorized types — value is already a PHP array after request decoding - Fix expand=allowed_ticket_types silently dropping field on DomainAuthorizedSummitRegistrationDiscountCodeSerializer — extend re-add guard to check both $relations and $expand - Rename json_array → json_string_array in both new serializers Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Add WithPromoCode branch to canBuyRegistrationTicketByType() so promo-code-only ticket types are not rejected at checkout for both invited and non-invited users. Replace isSoldOut() with canSell() in the strategy's WithPromoCode loop to align listing visibility with checkout enforcement. Add 5 unit tests for audience-based filtering scenarios required by Task 7 DoD. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task 8: wrap INSTANCE OF chain in parentheses to preserve summit scoping, simplify speaker email matching via getOwnerEmail(), and exclude cancelled tickets from quantity-per-account count. Task 9: add remaining_quantity_per_account (null) to all four member/speaker serializers, re-add allowed_ticket_types to member and speaker discount code serializers, and declare setter/getter on IDomainAuthorizedPromoCode interface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ity_per_account Move ApplyPromoCodeTask after ReserveOrderTask in the saga chain so ticket rows exist when the count query fires. Broaden getTicketCountByMemberAndPromoCode to include 'Reserved' orders, ensuring concurrent checkouts correctly see each other's reservations. Remove the TOCTOU-vulnerable pre-check from PreProcessReservationTask and relocate it inside ApplyPromoCodeTask's locked transaction, where it naturally fires once per unique promo code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sion, canBeAppliedTo, discovery, checkout - Fix base class: extend Tests\TestCase instead of PHPUnit\Framework\TestCase (boots Laravel facades) - Add 3 collision avoidance tests for DomainAuthorizedSummitRegistrationDiscountCode - Add 2 canBeAppliedTo override tests (free ticket guard bypass) - Add 4 auto_apply tests for existing email-linked types (Member/Speaker promo/discount) - Fix vacuous testWithPromoCodeAudienceNoPromoCodeNotReturned (now asserts on real data) - Add 3 serializer tests (auto_apply, remaining_quantity_per_account, email-linked type) - Rename misleading test to testWithPromoCodeAudienceLivePromoCodeReturned - Add 5 discovery endpoint integration tests in OAuth2SummitPromoCodesApiTest - Add 3 checkout enforcement test stubs (2 need order pipeline harness, 1 blocked by D4) - Mark all 9 review follow-ups complete in SDS doc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 |
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-525/ This page is automatically updated on each push to this PR. |
The GET /api/v1/summits/{id}/promo-codes/all/discover route was added in
Task 12 but never seeded into the api_endpoints table. The OAuth2 bearer
token validator middleware rejects any unregistered route with a 400
"API endpoint does not exits" error, causing 5 discover-endpoint integration
tests in OAuth2SummitPromoCodesApiTest to fail.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-525/ This page is automatically updated on each push to this PR. |
The discover endpoint's seeder entry intentionally omits authz_groups per
SDS Task 9 ("any authenticated user with read scope"). The auth.user
middleware requires at least one matching group, so every request fell
through to a 403. Switch to rate.limit:25,1 to match the adjacent
pre-validate-promo-code route, which has the same "any authenticated user"
profile. OAuth2 bearer auth and scope enforcement are still applied via
the parent 'api' middleware group.
All 5 discover integration tests now pass (verified locally).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-525/ This page is automatically updated on each push to this PR. |
ref: https://app.clickup.com/t/86b952pgc
Summary
DomainAuthorizedSummitRegistrationDiscountCode(with discount) andDomainAuthorizedSummitRegistrationPromoCode(access-only)WithPromoCodeaudience value onSummitTicketTypefor promo-code-only ticket typesGET /api/v1/summits/{id}/promo-codes/all/discover) that finds matching codes for the authenticated user's emailauto_applysupport to domain-authorized types and existing email-linked types (Member/Speaker)QuantityPerAccountenforcement at both discovery and checkout timeSDS Implementation (12 tasks)
All 12 tasks implemented. All review follow-ups resolved. Two open deviations remain:
allowed_email_domainsvalidation needs custom rule (currentlysometimes|json)QuantityPerAccountcheckout enforcement —ApplyPromoCodeTaskneeds to move afterReserveOrderTaskin saga chainFiles changed (35 files, +3127/-53)
DomainAuthorizedSummitRegistrationDiscountCode,DomainAuthorizedSummitRegistrationPromoCodeDomainAuthorizedPromoCodeTrait,AutoApplyPromoCodeTraitIDomainAuthorizedPromoCodeVersion20260401150000ApplyPromoCodeTaskDomainAuthorizedPromoCodeTest(30 tests)OAuth2SummitPromoCodesApiTestTest plan
php artisan test --filter=DomainAuthorizedPromoCodeTest— all unit tests passphp artisan test --filter="OAuth2SummitPromoCodesApiTest::testDiscover"— discovery integration tests passupanddownrun cleanly🤖 Generated with Claude Code