--- id: wiki-2026-0508-staircase-monetization-model title: Staircase Monetization Model category: 10_Wiki/Topics status: verified canonical_id: self aliases: [staircase-pack, ascending-offer, escalating-iap, value-ladder] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [monetization, mobile-game, f2p, iap, pricing] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: csharp framework: unity-firebase --- # Staircase Monetization Model ## 매 한 줄 > **"매 each pack purchased unlocks the next bigger one"**. Staircase monetization 은 매 mobile F2P game 의 ascending IAP offer sequence — 매 user 가 $0.99 → $4.99 → $19.99 → $99.99 의 step 을 sequentially 만 unlock. 매 commitment-and-consistency 심리, anchoring, sunk cost 의 활용 — 매 Coin Master, Royal Match, Monopoly Go 의 standard. ## 매 핵심 ### 매 메커니즘 - **Locked staircase**: 매 step N 은 step N-1 구매 후 만 visible. - **Increasing value (face)**: 매 step 마다 gem/coin 양 의 % 가 증가 (e.g. +20% better deal). - **Time gate**: 매 24h cooldown 으로 urgency 생성. - **Reset trigger**: 매 step 8~10 도달 시 staircase 의 reset (or branch). - **Personalization**: 매 LiveOps 의 player segment 별 pack value tuning. ### 매 심리 leverage - **Commitment & consistency** (Cialdini): $0.99 결제 후 매 다음 의 escalation 의 "이미 의 spent" mindset. - **Anchoring**: 매 작은 첫 step 이 reference price. - **Loss aversion**: "이 deal 의 24h 후 의 사라진다". - **Variable reward**: 매 step 의 better-than-average value 가 dopamine hit. ### 매 응용 1. **Match-3** — 매 Royal Match, Candy Crush 의 starter pack ladder. 2. **Casual** — 매 Coin Master 의 spin pack staircase. 3. **Mid-core** — 매 Monopoly Go 의 sticker pack escalation. 4. **Card game** — 매 Marvel Snap 의 currency ladder. ## 💻 패턴 ### State machine (Unity C#) ```csharp public class StaircaseOffer : MonoBehaviour { [Serializable] public class Step { public int tierId; public string sku; public int gems; public float priceUsd; public string title; } public List ladder; int currentStep = 0; DateTime? lockedUntil; public Step GetVisibleOffer() { if (lockedUntil.HasValue && DateTime.UtcNow < lockedUntil.Value) return null; return currentStep < ladder.Count ? ladder[currentStep] : null; } public void OnPurchase(string sku) { var step = ladder[currentStep]; if (step.sku != sku) return; Inventory.Add("gems", step.gems); currentStep++; lockedUntil = DateTime.UtcNow.AddHours(24); Analytics.Track("staircase_step_purchased", new {tier = step.tierId, currentStep}); if (currentStep >= ladder.Count) ResetStaircase(); } void ResetStaircase() { currentStep = 0; lockedUntil = DateTime.UtcNow.AddDays(7); } } ``` ### Server-side validation (Firebase Cloud Functions) ```typescript export const purchaseStaircase = onCall(async (req) => { const { uid, sku, receipt } = req.data; const valid = await verifyReceipt(receipt); // App Store / Play if (!valid) throw new HttpsError("permission-denied", "bad receipt"); const ref = db.collection("users").doc(uid); await db.runTransaction(async (tx) => { const u = (await tx.get(ref)).data(); if (u.staircase.expectedSku !== sku) throw new HttpsError("failed-precondition", "wrong step"); const step = LADDER[u.staircase.currentStep]; tx.update(ref, { gems: FieldValue.increment(step.gems), "staircase.currentStep": FieldValue.increment(1), "staircase.lockedUntil": Timestamp.fromMillis(Date.now() + 24*3600*1000), }); }); return { ok: true }; }); ``` ### Value curve (each step 20% better) ```python import numpy as np prices = np.array([0.99, 4.99, 9.99, 19.99, 49.99, 99.99]) base_gems_per_dollar = 100 step_bonus = np.array([1.0, 1.20, 1.44, 1.73, 2.07, 2.49]) # 20% compounding gems = (prices * base_gems_per_dollar * step_bonus).astype(int) # [99, 599, 1438, 3457, 10339, 24875] ``` ### LiveOps personalization (segment-based) ```typescript function pickLadder(player: Player) { if (player.spendTier === "whale") return LADDER_WHALE; // higher caps if (player.spendTier === "minnow") return LADDER_MINNOW; // smaller steps if (player.daysActive < 7) return LADDER_NEWBIE; // gentle on-ramp return LADDER_DEFAULT; } ``` ### A/B test harness ```typescript const variant = await abTest.assign(uid, "staircase_v3", ["control", "20pct", "30pct"]); const ladder = LADDERS[variant]; analytics.track("staircase_assigned", { uid, variant }); ``` ### Anti-fraud (replay / receipt reuse) ```csharp if (await db.usedReceipts.exists(receipt.transactionId)) throw new Exception("receipt replay"); await db.usedReceipts.insert(receipt.transactionId); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 신규 game launch | 6-step ladder, $0.99 → $99.99, 20% compounding bonus | | Mid-core (deeper meta) | 8-step ladder + branch (gems vs energy) | | Whale segment | extended ladder with $499.99 cap | | Conservative market (KR/JP) | smaller steps, less aggressive cooldown | | LiveOps event | event-specific staircase (limited cosmetic) | **기본값**: 6 step / 20% compounding value / 24h cooldown / 7-day reset / server-side validation. 매 personalization 의 LiveOps 적용 권장. ## 🔗 Graph - 부모: [[F2P-Design]] - 변형: [[Gacha]] · [[Loot-Box]] - Adjacent: [[LiveOps]] · [[Player-Segmentation]] ## 🤖 LLM 활용 **언제**: ladder value tuning simulation, copywriting (urgency / FOMO 문구), A/B variant generation, segment definition. **언제 X**: predatory targeting (minor / addiction-prone) — 매 ethical / regulatory (EU Digital Fairness Act) 의 violation 위험. ## ❌ 안티패턴 - **No cooldown**: 매 instant 의 ladder rush → revenue spike 후 churn. - **Client-side validation only**: 매 cheat tool 의 free unlock. 매 server-side mandatory. - **Aggressive whale-targeting**: regulatory backlash (UK gambling commission, EU DFA, KR 게임법). - **No reset / branching**: late-game player 의 의 ladder 소진 후 dead content. - **Same ladder for everyone**: 매 segment-blind 은 LTV leave on table. - **Hidden true price-per-gem**: 매 dark pattern 의 trust 파괴 + Apple/Google 의 reject 위험. ## 🧪 검증 / 중복 - Verified (Deconstructor of Fun analyses, GDC monetization talks 2024-2025, Adjust mobile economy reports, Sensor Tower top-grossing case studies). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — staircase mechanic + Unity/Firebase implementation |