Skip to content

Modules: Codes

Redeemable promo-code system with DataStore persistence (@broblox/codes). Status: Implemented (46 tests).

Purpose

  • Register redeemable codes with configurable rewards, use limits, and expiry.
  • Server-side redemption with per-player and global use tracking.
  • DataStore persistence for redemption records across sessions.

Core rules

  • All codes are stored and matched in UPPER-CASE (case-insensitive input).
  • 6-step validation chain: registry lookup → status check → expiry → global use limit → per-player limit → persist.
  • DataStore failures return graceful errors without granting rewards.
  • Time-based expiry auto-marks codes as EXPIRED on first attempted use past deadline.

Data model

  • RedeemableCodecode, description, status, rewards[], maxUses, perPlayerLimit, expiresAt, useCount.
  • CodeRewardtype, label, amount?, assetId?.
  • CodeStatus"ACTIVE" | "EXPIRED" | "DISABLED".
  • CodeRedemptionRecordcode, redeemedAt.
  • RedeemResultsuccess, status, message, rewards?.

Public API

Method Description
registerCode(code) Register a redeemable code
registerCodes(codes[]) Bulk register
unregisterCode(code) Remove from registry
setCodeStatus(code, status) Change ACTIVE/EXPIRED/DISABLED
redeemCode(playerId, code) Full redemption flow — returns RedeemResult
hasPlayerRedeemed(playerId, code) Check if player already redeemed
getPlayerRecords(playerId) All redemption records from DataStore
getAllCodes() List all registered codes

Factory

const codes = createCodeRedemptionService({
  codes: [
    {
      code: "LAUNCH2026",
      description: "Launch day reward",
      status: "ACTIVE",
      rewards: [{ type: "currency", label: "Coins", amount: 500 }],
      maxUses: 10000,
      perPlayerLimit: 1,
      expiresAt: 0,
    },
  ],
});

Security

  • UPPER-CASE normalisation — case-insensitive matching.
  • Per-player limits (default 1) prevent multi-redeem.
  • Global use limits (maxUses, 0 = unlimited) prevent exhaustion.
  • DataStore UpdateAsync — atomic updates; failure = no reward granted.
  • pcall wrapping on all DataStore calls — prevents crashes.

Config

Key Default Description
datastoreName "PlayerCodes" DataStore name
enableLogging true Debug logging

Observability

  • codes_redemption_attempts — total attempts
  • codes_redemption_successes — successful redemptions
  • codes_redemption_failures — failed attempts