Skip to content

feat: #77 Implement discount codes and promotional pricing - #146

Merged
manoahLinks merged 2 commits into
crowdpass-live:mainfrom
jayteemoney:feature/discount-codes-promotional-pricing
Mar 28, 2026
Merged

feat: #77 Implement discount codes and promotional pricing#146
manoahLinks merged 2 commits into
crowdpass-live:mainfrom
jayteemoney:feature/discount-codes-promotional-pricing

Conversation

@jayteemoney

Copy link
Copy Markdown
Contributor

Summary

  • Adds organizer-controlled discount codes to the event_manager Soroban contract
  • Codes reduce ticket prices by a configurable percentage, support optional usage limits and expiration timestamps, and are validated atomically during purchase
  • A new purchase_ticket_with_discount entry-point applies the code and emits both ticket_purchased and discount_applied events

Requirements fulfilled

Requirement Implementation
Discount code storage per event DataKey::Discount(u32, String)DiscountCode
create_discount(event_id, code, percentage, max_uses, expiration) Added; organizer-only
apply_discount validation during purchase Private apply_discount_code called by purchase_ticket_with_discount
Usage tracking and limits uses_remaining decremented atomically; rejected when exhausted
Expiration dates for codes Checked against env.ledger().timestamp()

New public functions

Function Description
create_discount(event_id, code, percentage, max_uses, expiration) Create a discount code (organizer only)
get_discount(event_id, code) Query a code's current state
purchase_ticket_with_discount(buyer, event_id, tier_index, code) Buy a ticket at a reduced price

Validation

create_discount guards: organizer auth, event not cancelled, percentage 1–100, expiration in the future (if set), no duplicate codes.

purchase_ticket_with_discount guards: all standard purchase checks + code exists, not expired, uses remaining.

New storage / types / errors

DiscountCode struct: code, percentage, max_uses, uses_remaining, expiration

Errors: DiscountNotFound = 24, DiscountExpired = 25, DiscountMaxUsesReached = 26, InvalidDiscountPercentage = 27, DiscountAlreadyExists = 28, InvalidExpiration = 29

Events: discount_created, discount_applied

Test results

running 65 tests
test test::test_create_discount_success ... ok
test test::test_create_discount_invalid_percentage_zero ... ok
test test::test_create_discount_invalid_percentage_over_100 ... ok
test test::test_create_discount_expiration_in_past ... ok
test test::test_create_discount_duplicate_code ... ok
test test::test_create_discount_cancelled_event ... ok
test test::test_get_discount_not_found ... ok
test test::test_purchase_with_discount_price_reduction ... ok
test test::test_purchase_with_discount_100_percent_free ... ok
test test::test_purchase_with_discount_invalid_code ... ok
test test::test_purchase_with_discount_expired_code ... ok
test test::test_purchase_with_discount_max_uses_exhausted ... ok
test test::test_discount_uses_remaining_decrements ... ok
test test::test_discount_unlimited_uses ... ok
test test::test_discount_with_expiration_valid ... ok
... (50 existing tests) ...
test result: ok. 65 passed; 0 failed; 0 ignored

Closes #77

…ass-live#77)

Add organizer-controlled discount codes to the event_manager contract,
allowing percentage-based price reductions during ticket purchase.

New public functions:
- create_discount(event_id, code, percentage, max_uses, expiration):
  organizer-only; creates a named discount code with optional usage
  limit (0 = unlimited) and optional expiration timestamp (0 = none)
- get_discount(event_id, code): query a discount code's details
- purchase_ticket_with_discount(buyer, event_id, tier_index, code):
  applies a valid discount code and purchases a ticket at the reduced
  price; decrements uses_remaining atomically

New storage:
- DataKey::Discount(u32, String): (event_id, code) -> DiscountCode

New type:
- DiscountCode { code, percentage, max_uses, uses_remaining, expiration }

Validation enforced by create_discount:
- Organizer-only (require_auth)
- percentage must be 1–100
- expiration (if set) must be in the future
- duplicate codes per event are rejected
- cancelled events cannot receive new codes

Validation enforced by purchase_ticket_with_discount:
- code must exist (DiscountNotFound)
- code must not be expired (DiscountExpired)
- code must have remaining uses when max_uses > 0 (DiscountMaxUsesReached)

Events emitted:
- discount_created (event_id, code, percentage, max_uses, expiration)
- discount_applied (event_id, buyer, code, base_price, final_price)

New error variants:
- DiscountNotFound = 24
- DiscountExpired = 25
- DiscountMaxUsesReached = 26
- InvalidDiscountPercentage = 27
- DiscountAlreadyExists = 28
- InvalidExpiration = 29

Also includes the cumulative base cleanup from crowdpass-live#75 and crowdpass-live#76 (merge-conflict
artifacts in lib.rs/test.rs) since upstream main has not yet merged those PRs.

Test results:
  65 passed; 0 failed
@drips-wave

drips-wave Bot commented Mar 28, 2026

Copy link
Copy Markdown

@jayteemoney Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@manoahLinks
manoahLinks merged commit 222409e into crowdpass-live:main Mar 28, 2026
1 of 3 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.

Implement discount codes and promotional pricing

2 participants