fix: unify wheel Stars payment across desktop and mobile

- Use Telegram Stars invoice on all platforms instead of balance
  payment on desktop
- Show star cost in toggle and subtitle everywhere
- Show popup/toast when Stars are not enabled and user tries to spin
- Remove platform-specific payment type auto-select logic
- Add starsNotAvailable translation key (ru, en, zh, fa)
This commit is contained in:
c0mrade
2026-02-04 16:26:07 +03:00
parent 4499c9ad57
commit 02640d1c38
5 changed files with 20 additions and 36 deletions

View File

@@ -713,6 +713,7 @@
"starsPaymentSuccessCheckHistory": "Payment successful! Check history or Telegram for the result.", "starsPaymentSuccessCheckHistory": "Payment successful! Check history or Telegram for the result.",
"starsPaymentFailed": "Payment failed. Please try again.", "starsPaymentFailed": "Payment failed. Please try again.",
"starsPaymentRedirected": "Telegram will open for payment. Refresh the page after payment.", "starsPaymentRedirected": "Telegram will open for payment. Refresh the page after payment.",
"starsNotAvailable": "Star payments are currently unavailable. Contact support.",
"youWon": "You won", "youWon": "You won",
"noPrize": "No luck this time...", "noPrize": "No luck this time...",
"noHistory": "No spin history yet", "noHistory": "No spin history yet",

View File

@@ -599,6 +599,7 @@
"starsPaymentSuccess": "پرداخت موفق! نتیجه به تلگرام ارسال شد.", "starsPaymentSuccess": "پرداخت موفق! نتیجه به تلگرام ارسال شد.",
"starsPaymentFailed": "پرداخت ناموفق. دوباره تلاش کنید.", "starsPaymentFailed": "پرداخت ناموفق. دوباره تلاش کنید.",
"starsPaymentRedirected": "تلگرام برای پرداخت باز می‌شود. پس از پرداخت صفحه را بازخوانی کنید.", "starsPaymentRedirected": "تلگرام برای پرداخت باز می‌شود. پس از پرداخت صفحه را بازخوانی کنید.",
"starsNotAvailable": "پرداخت با ستاره در حال حاضر در دسترس نیست. با پشتیبانی تماس بگیرید.",
"noHistory": "هنوز تاریخچه‌ای نیست", "noHistory": "هنوز تاریخچه‌ای نیست",
"banner": { "banner": {
"title": "شانس خود را امتحان کنید!", "title": "شانس خود را امتحان کنید!",

View File

@@ -741,6 +741,7 @@
"starsPaymentSuccessCheckHistory": "Оплата прошла! Проверьте историю или Telegram для результата.", "starsPaymentSuccessCheckHistory": "Оплата прошла! Проверьте историю или Telegram для результата.",
"starsPaymentFailed": "Ошибка оплаты. Попробуйте еще раз.", "starsPaymentFailed": "Ошибка оплаты. Попробуйте еще раз.",
"starsPaymentRedirected": "Откроется Telegram для оплаты. После оплаты обновите страницу.", "starsPaymentRedirected": "Откроется Telegram для оплаты. После оплаты обновите страницу.",
"starsNotAvailable": "Оплата звёздами сейчас недоступна. Обратитесь в поддержку.",
"youWon": "Вы выиграли", "youWon": "Вы выиграли",
"noPrize": "В этот раз не повезло...", "noPrize": "В этот раз не повезло...",
"noHistory": "История вращений пуста", "noHistory": "История вращений пуста",

View File

@@ -603,6 +603,7 @@
"starsPaymentSuccess": "支付成功抽奖结果已发送到Telegram。", "starsPaymentSuccess": "支付成功抽奖结果已发送到Telegram。",
"starsPaymentFailed": "支付失败。请重试。", "starsPaymentFailed": "支付失败。请重试。",
"starsPaymentRedirected": "将打开Telegram进行支付。支付后请刷新页面。", "starsPaymentRedirected": "将打开Telegram进行支付。支付后请刷新页面。",
"starsNotAvailable": "星星支付暂不可用。请联系客服。",
"noHistory": "暂无抽奖记录", "noHistory": "暂无抽奖记录",
"banner": { "banner": {
"title": "试试运气!", "title": "试试运气!",

View File

@@ -5,8 +5,8 @@ import { wheelApi, type SpinResult, type SpinHistoryItem } from '../api/wheel';
import FortuneWheel from '../components/wheel/FortuneWheel'; import FortuneWheel from '../components/wheel/FortuneWheel';
import WheelLegend from '../components/wheel/WheelLegend'; import WheelLegend from '../components/wheel/WheelLegend';
import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt'; import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt';
import { useCurrency } from '../hooks/useCurrency';
import { usePlatform, useHaptic } from '@/platform'; import { usePlatform, useHaptic } from '@/platform';
import { useNotify } from '@/platform/hooks/useNotify';
import { Card } from '@/components/data-display/Card/Card'; import { Card } from '@/components/data-display/Card/Card';
import { Button } from '@/components/primitives/Button/Button'; import { Button } from '@/components/primitives/Button/Button';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
@@ -60,9 +60,9 @@ const ChevronIcon = ({ expanded }: { expanded: boolean }) => (
export default function Wheel() { export default function Wheel() {
const { t } = useTranslation(); const { t } = useTranslation();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { formatAmount, currencySymbol } = useCurrency(); const { openInvoice, capabilities } = usePlatform();
const { platform, openInvoice, capabilities } = usePlatform();
const haptic = useHaptic(); const haptic = useHaptic();
const notify = useNotify();
const [isSpinning, setIsSpinning] = useState(false); const [isSpinning, setIsSpinning] = useState(false);
const [targetRotation, setTargetRotation] = useState<number | null>(null); const [targetRotation, setTargetRotation] = useState<number | null>(null);
@@ -74,9 +74,6 @@ export default function Wheel() {
const [historyExpanded, setHistoryExpanded] = useState(false); const [historyExpanded, setHistoryExpanded] = useState(false);
const paymentTypeInitialized = useRef(false); const paymentTypeInitialized = useRef(false);
// Check if we're in Telegram Mini App environment
const isTelegramMiniApp = platform === 'telegram';
const { const {
data: config, data: config,
isLoading, isLoading,
@@ -98,25 +95,13 @@ export default function Wheel() {
const starsEnabled = config.spin_cost_stars_enabled && config.spin_cost_stars; const starsEnabled = config.spin_cost_stars_enabled && config.spin_cost_stars;
const daysEnabled = config.spin_cost_days_enabled && config.spin_cost_days; const daysEnabled = config.spin_cost_days_enabled && config.spin_cost_days;
const canPayBalance = starsEnabled && config.can_pay_stars;
const canPayDays = daysEnabled && config.can_pay_days;
if (isTelegramMiniApp) { if (starsEnabled) {
// In Mini App: prefer stars if available setPaymentType('telegram_stars');
if (starsEnabled) { } else if (daysEnabled) {
setPaymentType('telegram_stars'); setPaymentType('subscription_days');
} else if (canPayDays) {
setPaymentType('subscription_days');
}
} else {
// In Web: prefer balance (Stars converted to rubles), fallback to days
if (canPayBalance) {
setPaymentType('telegram_stars');
} else if (canPayDays) {
setPaymentType('subscription_days');
}
} }
}, [config, isTelegramMiniApp]); }, [config]);
// Function to poll for new spin result after Stars payment // Function to poll for new spin result after Stars payment
const pollForSpinResult = useCallback( const pollForSpinResult = useCallback(
@@ -362,7 +347,11 @@ export default function Wheel() {
}; };
const handleUnifiedSpin = () => { const handleUnifiedSpin = () => {
if (isTelegramMiniApp && paymentType === 'telegram_stars') { if (paymentType === 'telegram_stars') {
if (!config?.spin_cost_stars_enabled || !config?.spin_cost_stars) {
notify.warning(t('wheel.starsNotAvailable'));
return;
}
handleDirectStarsPay(); handleDirectStarsPay();
} else { } else {
handleSpin(); handleSpin();
@@ -456,11 +445,7 @@ export default function Wheel() {
const starsEnabled = config.spin_cost_stars_enabled && config.spin_cost_stars; const starsEnabled = config.spin_cost_stars_enabled && config.spin_cost_stars;
const daysEnabled = config.spin_cost_days_enabled && config.spin_cost_days; const daysEnabled = config.spin_cost_days_enabled && config.spin_cost_days;
const canPayBalance = starsEnabled && config.can_pay_stars; // For web: pay with internal balance const bothMethodsAvailable = !!(starsEnabled && daysEnabled);
const canPayDays = daysEnabled && config.can_pay_days;
const bothMethodsAvailable = isTelegramMiniApp
? starsEnabled && daysEnabled && canPayDays
: starsEnabled && daysEnabled && canPayBalance && canPayDays;
const spinDisabled = const spinDisabled =
!config.can_spin || !config.can_spin ||
@@ -472,10 +457,7 @@ export default function Wheel() {
const getCostSubtitle = () => { const getCostSubtitle = () => {
if (bothMethodsAvailable) return null; // toggle handles it if (bothMethodsAvailable) return null; // toggle handles it
if (paymentType === 'telegram_stars' && starsEnabled) { if (paymentType === 'telegram_stars' && starsEnabled) {
if (isTelegramMiniApp) { return `${config.spin_cost_stars}`;
return `${config.spin_cost_stars}`;
}
return `${formatAmount(config.required_balance_kopeks / 100)} ${currencySymbol} (${config.spin_cost_stars} ⭐)`;
} }
if (paymentType === 'subscription_days' && daysEnabled) { if (paymentType === 'subscription_days' && daysEnabled) {
return t('wheel.days', { count: config.spin_cost_days ?? 0 }); return t('wheel.days', { count: config.spin_cost_days ?? 0 });
@@ -529,9 +511,7 @@ export default function Wheel() {
}`} }`}
> >
<StarIcon /> <StarIcon />
{isTelegramMiniApp {`${config.spin_cost_stars}`}
? `${config.spin_cost_stars}`
: `${formatAmount(config.required_balance_kopeks / 100)} ${currencySymbol}`}
</button> </button>
<button <button
onClick={() => setPaymentType('subscription_days')} onClick={() => setPaymentType('subscription_days')}