Разработка мобильного приложения крипто-казино
Мобильное крипто-казино — технически более сложный продукт чем web-версия: AppStore/Google Play политики ограничивают gambling приложения, web3 взаимодействие на мобильных работает иначе, и UX требования на маленьком экране значительно строже.
Platform стратегия
App Store и Google Play проблема
Apple и Google строго ограничивают gambling приложения:
- Apple: gambling разрешён только в юрисдикциях с лицензией, требует регионального геоблокирования, приложение рецензируется вручную
- Google Play: аналогичные ограничения, gambling app допускается только в разрешённых странах
Для crypto-specific казино дополнительная сложность: Web3 функциональность (крипто-транзакции, кошельки) тоже ограничена — Apple запрещает in-app покупки за крипту, Google аналогично.
Реалистичные пути:
- PWA (Progressive Web App) — устанавливается с браузера без AppStore, работает как native app. Нет проблем с модерацией. Ограничения: нет push notifications (iOS), производительность чуть ниже native.
- Web View wrapper — приложение по сути web, завёрнутое в native shell. Проходит AppStore только если явно не gambling или в разрешённой юрисдикции.
- Direct APK distribution (Android) — без Google Play, пользователи устанавливают напрямую. Работает, но снижает доверие.
- Native iOS/Android — с gambling лицензией в разрешённых странах (UK, Malta, etc).
Технический стек
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-specific UX
Игровые экраны
Вертикальный layout обязателен — большинство играет в portrait режиме.
Touch targets: минимум 44pt для кнопок. Кнопка «Bet» должна быть крупной, легкодоступной большим пальцем.
Bottom sheet для настроек ставки — не модальные окна, которые перекрывают игровой экран.
Swipe gestures: свайп влево/вправо для переключения между играми.
// Crash game на мобильном
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);
}
}
// Уведомления: jackpot выиграли, 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 game и 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 леадерборд: табло сверху экрана, показывает место игрока в текущем турнире.
Безопасность мобильного приложения
Certificate pinning: SSL сертификат приложения зашит в коде — MITM атака невозможна.
Root/jailbreak detection: Игра отказывается запускаться на рутованных устройствах (потенциальный cheating).
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 месяцев







