Розробка мобільного додатка крипто-казино
Мобільне крипто-казино — технічно складніше ніж web-версія: AppStore/Google Play політики обмежують азартні додатки, Web3 взаємодія на мобільних працює по-іншому, й UX вимоги на малому екрані значно строжіші.
Стратегія платформи
Проблема AppStore й Google Play
Apple й Google строго обмежують азартні додатки:
- Apple: азартні дозволені тільки в ліцензованих юрисдикціях, вимагається регіональний геоблокування, додаток рецензується вручну
- Google Play: аналогічні обмеження, азартний додаток допускається тільки в дозволених країнах
Для crypto-специфічних казино додаткова складність: Web3 функціональність (крипто-транзакції, гаманці) також обмежена — Apple забороняє in-app покупки за крипту, Google аналогічно.
Реалістичні шляхи:
- PWA (Progressive Web App) — установлюється з браузера без AppStore, працює як native додаток. Немає проблем з модерацією. Обмеження: немає push notifications (iOS), продуктивність трохи нижче.
- Web View wrapper — додаток по суті web, завернутий в native shell. Проходить AppStore тільки якщо явно не азартний або в ліцензованій юрисдикції.
- Direct APK distribution (Android) — без Google Play, користувачі установлюють напрямо. Працює, але знижує довіру.
- Native iOS/Android — з азартною ліцензією в ліцензованих країнах (UK, Malta, і т.д.).
Технічний стек
React Native + Web3
import { WalletConnectModal, useWalletConnectModal } from "@walletconnect/modal-react-native";
import { useProvider, useSigner } from "@web3modal/ethers-react-native";
function CasinoApp() {
const { open, isConnected, address } = useWalletConnectModal();
const provider = useProvider();
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Dice" component={DiceGame} />
<Stack.Screen name="Crash" component={CrashGame} />
<Stack.Screen name="Wallet" component={WalletScreen} />
</Stack.Navigator>
{!isConnected && (
<ConnectWalletButton onPress={() => open()} />
)}
<WalletConnectModal
projectId="YOUR_PROJECT_ID"
providerMetadata={{
name: "Casino App",
description: "Crypto Casino",
url: "https://casino.com",
icons: ["https://casino.com/icon.png"],
}}
/>
</NavigationContainer>
);
}
Embedded Wallet для onboarding
Для користувачів без MetaMask — embedded wallet через Privy або Dynamic:
import { usePrivy, useWallets } from "@privy-io/expo";
function WalletManager() {
const { login, user, isReady } = usePrivy();
const { wallets } = useWallets();
const embeddedWallet = wallets.find(w => w.walletClientType === "privy");
if (!user) {
return (
<View style={styles.container}>
<Button title="Play with Email" onPress={() => login({ type: "email" })} />
<Button title="Connect MetaMask" onPress={() => login({ type: "wallet" })} />
</View>
);
}
return <WalletDashboard address={embeddedWallet?.address} />;
}
Mobile-специфічний UX
Ігрові екрани
Вертикальний layout обов'язковий — більшість грає в portrait режимі.
Touch targets: мінімум 44pt для кнопок. Кнопка «Bet» повинна бути крупною, легкодоступною великим пальцем.
Bottom sheet для настройки ставки — не модальні вікна, що перекривають ігровий екран.
Swipe gestures: свайп вліво/вправо для перемикання між іграми.
// Crash гра на мобільному
function CrashGame() {
const [multiplier, setMultiplier] = useState(1.0);
const [isCashedOut, setIsCashedOut] = useState(false);
// Shake gesture для cashout (fun UX)
useShakeGesture(() => {
if (!isCashedOut && multiplier > 1.5) {
handleCashout();
}
});
return (
<SafeAreaView style={styles.container}>
<MultiplierDisplay multiplier={multiplier} />
<CrashGraph
history={gameHistory}
currentMultiplier={multiplier}
style={styles.graph}
/>
<BetPanel style={styles.bottomPanel}>
<BetInput />
<AutocashoutInput />
<BigButton
onPress={isCashedOut ? undefined : handleCashout}
disabled={isCashedOut}
style={styles.cashoutButton}
>
CASHOUT {(betAmount * multiplier).toFixed(4)} ETH
</BigButton>
</BetPanel>
</SafeAreaView>
);
}
Push уведомлення
import * as Notifications from "expo-notifications";
async function setupNotifications(userId: string) {
const { status } = await Notifications.requestPermissionsAsync();
if (status === "granted") {
const token = await Notifications.getExpoPushTokenAsync();
await api.savePushToken(userId, token.data);
}
}
// Уведомлення: джекпот виграли, cashback зараховано, депозит зачислений
async function sendJackpotNotification(userId: string, amount: number) {
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to: await getPushToken(userId),
title: "Jackpot! 🎰",
body: `Ви виграли $${amount.toFixed(2)}!`,
data: { screen: "Wallet", tab: "History" },
sound: "jackpot.wav",
}),
});
}
Біометрична аутентифікація
import * as LocalAuthentication from "expo-local-authentication";
async function authenticateWithBiometrics(): Promise<boolean> {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (!hasHardware || !isEnrolled) return false;
const result = await LocalAuthentication.authenticateAsync({
promptMessage: "Підтвердіть транзакцію",
cancelLabel: "Скасування",
fallbackLabel: "Використовувати PIN",
});
return result.success;
}
// Обов'язкова біометрія для виводів вище порога
async function requestWithdrawal(amount: number) {
if (amount > BIOMETRIC_THRESHOLD) {
const confirmed = await authenticateWithBiometrics();
if (!confirmed) return;
}
await processWithdrawal(amount);
}
Real-time через WebSocket
Для Crash гри й live елементів — WebSocket з'єднання:
class CasinoWebSocket {
private ws: WebSocket | null = null;
private reconnectInterval = 3000;
connect(userId: string) {
this.ws = new WebSocket(`wss://casino.com/ws?userId=${userId}`);
this.ws.onopen = () => {
this.ws?.send(JSON.stringify({ type: "subscribe", games: ["crash", "jackpot"] }));
};
this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
this.handleMessage(msg);
};
this.ws.onclose = () => {
// Auto reconnect
setTimeout(() => this.connect(userId), this.reconnectInterval);
};
}
private handleMessage(msg: WSMessage) {
switch (msg.type) {
case "crash_multiplier_update":
store.dispatch(updateCrashMultiplier(msg.data.multiplier));
break;
case "jackpot_update":
store.dispatch(updateJackpot(msg.data));
break;
case "balance_update":
store.dispatch(updateBalance(msg.data.balance));
break;
}
}
}
Геймифікація мобільного досвіду
Daily challenges: 5 флипів за день → бонус. Push notification нагадує.
Streak система: 7 днів поспіль → cashback 5%. Відображається на home screen.
Achievements: перший виграш >10x, 100 ігор і т.д. NFT-бейджи як нагороди.
Tournament leaderboard: таблиця сверху екрана, показує місце гравця в поточному турнірі.
Безпека мобільного додатка
Certificate pinning: SSL сертифікат додатка зашит в коді — MITM атака неможлива.
Root/jailbreak detection: гра відмовляється запускатися на рутованих пристроях (потенційне читерство).
Anti-tamper: обфускація кода + checksum перевірка при запуску.
Session management: автоматичний logout після 30 хвилин неактивності. Біометрія для розблокування.
Стек
| Компонент | Технологія |
|---|---|
| Framework | React Native + Expo |
| Навігація | React Navigation v6 |
| State | Zustand або Redux Toolkit |
| Web3 | WalletConnect v2 + Privy |
| Real-time | WebSocket |
| Анімації | React Native Reanimated 3 |
| Charts | Victory Native XL |
| Notifications | Expo Notifications |
| Biometrics | Expo Local Authentication |
Часові рамки
- PWA версія (React + Capacitor): 3-4 тижні з web-версії
- React Native додаток з 5 іграми: 10-14 тижнів
- Embedded wallets + біометрія: +2-3 тижні
- Push notifications + геймифікація: +2-3 тижні
- iOS + Android окремий тюнінг: +2 тижні
- AppStore submission + compliance: +2-4 тижні
- Итого: 4-5 місяців







