Skip to main content

Outcome Architecture

luxy separates reward business logic from reward presentation. The component should animate the result; it should not decide whether a user deserves a valuable prize unless the app intentionally opts into local demo odds.

Backend-first flow

  1. The app creates an interaction id and starts the promotion UI.
  2. The app calls a backend endpoint to reserve or retrieve the reward.
  3. The backend validates eligibility, inventory, rate limits, fraud signals, campaign rules, and audit logging.
  4. The backend returns a predetermined luxy outcome.
  5. luxy animates the scratch card or slot machine to that predetermined result.
  6. The app records reveal/spin completion and redemption status without putting secrets in callbacks.

Supported outcome modes

Use exactly one mode per component instance.

ModeUse whenNotes
outcomeThe app already knows the reward.Best for controlled demos, pre-reserved rewards, and tests.
resolveOutcome(context)The app needs an async backend result.Recommended for valuable or fraud-sensitive rewards.
oddsYou need cosmetic local demo behavior.Not secure. Do not use for valuable or regulated rewards.

Resolver example

<ScratchTicket
resolveOutcome={(context) => api.reserveReward(context.interactionId)}
game={{ type: "number-match", winningNumbers, yourNumbers }}
layout={{ title: "luxy cash" }}
scratcher
/>

Slot stop payloads

Backend-resolved SlotMachine outcomes should return a payload.stops array whose symbol ids map to the visible reel stops.

const outcome = {
id: "golden-ticket",
label: "Golden ticket",
isWin: true,
payload: {
stops: ["gold", "gold", "gold"],
payout: 100,
},
};

Data minimization

Callbacks should pass reward ids, interaction ids, and coarse state transitions. Avoid raw tokens, coupon secrets, full customer profiles, sensitive eligibility data, and fraud details in component props, callbacks, logs, and analytics payloads.