Gift Card
Gift Card is a prepaid credit system that allows users to redeem balance into their account. Gift cards have a secret code, a fixed monetary value, an expiration date, and can only be used once. Administrators can generate gift cards in bulk or create special cards with custom secrets.
Core Concept
A Gift Card represents:
- Secret code: Unique 64-character alphanumeric string for redemption
- Amount: Fixed value credited to user’s balance upon redemption
- Expiration: Time limit after which the card becomes invalid
- Single-use: Once redeemed, the card is permanently marked as used
Data Model
pub struct GiftCard {
pub id: i32,
pub secret: String, // 64-char alphanumeric code
pub amount: Decimal, // Value to credit
pub used_by: Option<Uuid>, // User who redeemed (if any)
pub created_at: PrimitiveDateTime, // Creation timestamp
pub redeem_at: Option<PrimitiveDateTime>, // When it was redeemed
pub valid_until: PrimitiveDateTime, // Expiration date
}
Gift Card Lifecycle
1. Generation
Gift cards are created by administrators through bulk generation or special creation:
Bulk Generation (AdminGenerateGiftCard):
- Generates N cards with identical amount and expiration
- Secrets are randomly generated (64-character alphanumeric)
- Returns list of secret codes for distribution
- Uses hash set to ensure uniqueness before database insertion
Special Creation (AdminCreateSpecialGiftCard):
- Creates a single card with custom secret (e.g., promotional codes like “WELCOME2025”)
- Useful for marketing campaigns or personalized gifts
- Secret must be unique (database constraint)
Permissions: Moderator, SuperAdmin, CustomerSupport
2. Distribution
Gift cards exist as secret codes. Administrators must distribute these codes to users through external channels (email, physical cards, promotional materials). The system does not handle distribution automatically.
3. Redemption
Users redeem gift cards through GiftCardService::RedeemGiftCard:
Flow:
- User submits secret code
- System looks up valid gift card (not used, not expired)
- Validates user exists
- Transaction begins:
- Add card amount to user’s available balance
- Log balance change (type: Deposit, reason: “Redeem Gift Card”)
- Mark card as used with
used_byandredeem_at
- Transaction commits
Validation Order:
- Card exists by secret →
CardNotFound - Card not already used →
AlreadyUsed - Card not expired (
valid_until > NOW()) →Expired - User exists →
UserNotFound - Transaction succeeds →
Success
Important: The validation provides specific error reasons. If a card is found but invalid, the system distinguishes between “already used” and “expired” to provide clear feedback.
4. Expiration
Expired cards remain in the database but cannot be redeemed:
- Query filter:
valid_until > NOW() AND used_by IS NULL - Redemption attempt returns
Expiredresult - No automatic cleanup of expired cards (historical record)
User Operations
Redeem Gift Card
Service: GiftCardService::RedeemGiftCard
Users provide a secret code to add balance to their account.
Result Types:
Success: Balance credited successfullyCardNotFound: Secret doesn’t exist in databaseAlreadyUsed: Card was previously redeemed by any userExpired: Card’svalid_untildate has passedUserNotFound: Requesting user account doesn’t exist (edge case)
Transaction Safety: The redemption process is fully transactional. If the balance update fails, the card remains unused. This prevents double-redemption and ensures balance consistency.
Admin Operations
All gift card admin operations are exposed through ManageService and require appropriate admin roles.
Generate Gift Cards
Operation: AdminGenerateGiftCard
Bulk-creates gift cards with identical configuration.
Parameters:
number: How many cards to generate (batch size)amount: Value of each cardvalid_until: Expiration timestamp for all cards
Returns: List of secret codes (strings)
Use Cases:
- Promotional campaigns (e.g., 1000 cards worth $10 each)
- Customer rewards programs
- Event giveaways
Note: The operation returns the secret codes in the response. These should be securely stored or distributed immediately, as they cannot be retrieved later (secrets are logged as [REDACTED] in debug output for security).
Create Special Gift Card
Operation: AdminCreateSpecialGiftCard
Creates a single card with a custom secret.
Parameters:
secret: Custom code (e.g., “NEWYEAR2025”)amount: Card valuevalid_until: Expiration timestamp
Use Cases:
- Marketing promotions with memorable codes
- Influencer partnerships with branded codes
- VIP customer gifts
Database Constraint: The secret must be unique across all gift cards (used or unused). Attempting to create a duplicate secret will fail.
List Gift Cards
Operation: AdminListGiftCards
Retrieves gift cards with optional filtering and pagination.
Filters:
filter_id: Specific card IDfilter_secret: Partial or exact secret matchfilter_is_used: Show only used or unused cardsfilter_used_by: Cards redeemed by specific userlimit,page: Pagination controls
Use Cases:
- Finding cards redeemed by a user (customer support)
- Checking if a secret exists before creation
- Monitoring unused expired cards
- Audit trail of card usage
Delete Gift Cards
Operation: AdminDeleteGiftCards
Permanently removes gift cards from the database.
Parameters: List of card IDs to delete
Permissions: Moderator, SuperAdmin (more restricted than other operations)
Use Cases:
- Removing expired promotional campaigns
- Cleaning up unused cards after campaign ends
Warning: Deleting a redeemed card does not affect user balance. The balance change log remains intact with the reason “Redeem Gift Card”, but the reference to the card is lost.
Integration with Balance System
Gift card redemption is the primary way users add funds to their account (other than admin top-ups).
Balance Credit Flow
When a gift card is redeemed:
- User’s
available_balanceincreases by cardamount - A
UserBalanceChangeLogentry is created:change_type:Depositamount: Card value (positive)reason: “Redeem Gift Card”
- User can immediately use the balance to pay for orders
See Account Balance for complete balance system documentation.
Transaction Integrity
The redemption uses database transactions with row-level locking:
SELECT ... FOR UPDATEon gift card prevents concurrent redemption- Balance update and card marking happen atomically
- If balance update fails (user not found), card remains unused
Security Considerations
Secret Generation
Secrets are 64-character random alphanumeric strings (A-Z, a-z, 0-9):
- Entropy: ~380 bits (62^64 combinations)
- Collision probability: Negligible even for millions of cards
- Not cryptographically signed or verifiable offline
Uniqueness: The system generates secrets in memory using a HashSet before database insertion, ensuring no duplicates in a batch. Database constraint provides final uniqueness guarantee.
Debug Output Redaction
The GiftCard struct implements custom Debug to redact secrets:
.field("secret", &"[REDACTED]")
This prevents accidental secret leakage in logs, error messages, or debug traces.
No Secret Recovery
Once generated, secrets cannot be retrieved by ID. Admins must save the secret codes from the generation response. This is intentional—secrets are meant to be distributed, not stored centrally.
Database Schema
Key schema details (from migrations/20250922063531_gift_card_system.sql):
Table: shop.gift_card
id SERIAL PRIMARY KEY
secret VARCHAR(255) NOT NULL UNIQUE
amount NUMERIC NOT NULL
used_by UUID REFERENCES auth.user_profile(id)
created_at TIMESTAMP NOT NULL DEFAULT NOW()
redeem_at TIMESTAMP
valid_until TIMESTAMP NOT NULL
Indexes:
(secret): Fast lookup by secret (primary redemption path)(secret) WHERE used_by IS NULL: Fast lookup for valid cards(valid_until) WHERE used_by IS NULL: Expiration queries(used_by): Find cards redeemed by a user
Foreign Key: used_by references user profile with ON DELETE RESTRICT, preventing user deletion if they’ve redeemed cards.
Frontend Integration Points
User Redemption Flow
-
Input Field: Provide text input for secret code (64 characters)
-
Validation: Optional client-side format check (alphanumeric, length)
-
Submission: Call
RedeemGiftCardwith the secret -
Result Handling:
Success: Show success message, update balance displayCardNotFound: “Invalid gift card code”AlreadyUsed: “This gift card has already been redeemed”Expired: “This gift card has expired”UserNotFound: Generic error (should never happen)
-
Balance Refresh: Fetch updated balance after successful redemption
Admin Management UI
Generate Cards:
- Form with number, amount, expiration date
- Display generated secrets in a list (with copy buttons)
- Warn user to save secrets before leaving page
List Cards:
- Table with columns: ID, Secret (masked/copyable), Amount, Status (Used/Unused), Used By, Created, Redeemed, Expires
- Filters for used/unused, user, date ranges
- Search by secret or ID
Create Special Card:
- Form with custom secret input, amount, expiration
- Validate secret format before submission
- Handle duplicate secret error clearly
Design Decisions
Why Single-Use Only?
Gift cards are one-time redeemable by design:
- Simplifies balance tracking (single deposit event)
- Prevents confusion about remaining card balance
- Matches traditional physical gift card behavior
- Users can check their balance history for redemptions
For recurring credits or subscriptions, use coupon system or scheduled balance deposits instead.
Why No Secret Retrieval?
Secrets are treated as bearer tokens:
- Admin generates and distributes them
- System validates but doesn’t need to recall them
- Reduces risk of centralized secret exposure
- Aligns with physical gift card model (code is on the card)
Admins can search by secret if a user provides it (customer support), but cannot list all secrets.
Why Soft Delete Not Supported?
Unlike coupons (which have is_active), gift cards are either used or deleted:
- Once distributed, cards shouldn’t be “disabled” (already in user’s hands)
- Unused cards can be deleted if campaign is cancelled
- Used cards should be kept for audit trail (don’t delete redeemed cards)
Why Expiration is Required?
All gift cards must have a valid_until date:
- Prevents indefinite liability on the system
- Aligns with legal/financial regulations for prepaid instruments
- Encourages timely redemption
- Allows cleanup of old campaigns
Set far-future dates (e.g., 10 years) for effectively non-expiring cards if needed.
See Also:
- Account Balance - Balance system and transaction history
- Order System - Using balance to pay for orders
- Shop Module Introduction - Overall shop architecture