Fix "From Telegram" phone button showing for guests without Telegram

index.html loads Telegram's telegram-web-app.js SDK unconditionally, and
that script always defines window.Telegram.WebApp — requestContact
included — as a harmless stub even in a plain desktop browser with no
real Telegram client. canRequestContact only checked "does the method
exist", which was true for everyone, so a guest ordering without Telegram
(see the guest-ordering feature) saw a "From Telegram" button that could
never do anything for them.

Adds isInsideTelegram(), which checks initData is actually a non-empty
signed string (only ever true inside a real Telegram client), and gates
both the manual button and requestContactPhone()'s auto-prompt on it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
byrsapty 2026-08-27 21:18:04 +03:00
parent 5cfcf71154
commit ac3e34b173
2 changed files with 20 additions and 3 deletions

View file

@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
import { api } from "../api";
import { useI18n } from "../i18n";
import { PickupIcon } from "../icons";
import { getTelegram, haptic, requestContactPhone } from "../telegram";
import { getTelegram, haptic, isInsideTelegram, requestContactPhone } from "../telegram";
import type { Cart, CheckoutResult, PromoValidateResult, PublicSettings } from "../types";
interface Props {
@ -100,7 +100,13 @@ export function CheckoutPage({ mode, cart, settings, onBack, onOrderPlaced }: Pr
// a manual button whenever that API exists at all, so a customer whose
// client didn't support it on first launch (or who dismissed the popup)
// always has an explicit way to retry instead of being silently stuck.
const canRequestContact = !!tg?.requestContact;
// isInsideTelegram() matters here too: index.html loads Telegram's own
// SDK script unconditionally, and that script always defines the full
// window.Telegram.WebApp API (requestContact included) as a harmless
// stub even in a plain desktop browser — so `tg?.requestContact` alone is
// truthy for a guest with no Telegram account at all (see the
// guest-ordering feature), which used to show this button to everyone.
const canRequestContact = !!tg?.requestContact && isInsideTelegram();
const [requestingPhone, setRequestingPhone] = useState(false);
const handleRequestPhone = async () => {

View file

@ -52,6 +52,17 @@ export function getTelegram(): TelegramWebApp | null {
return typeof window !== "undefined" && window.Telegram ? window.Telegram.WebApp : null;
}
/** Whether this page is actually running inside a real Telegram client, not
* just a plain browser that happened to load telegram-web-app.js (index.html
* includes that script unconditionally). Telegram's own SDK always exposes
* the full `window.Telegram.WebApp` object methods like requestContact
* included as a harmless stub even outside Telegram, so checking "does
* the method exist" isn't enough. `initData` is only ever a real, signed,
* non-empty string when a genuine Telegram client opened this page. */
export function isInsideTelegram(): boolean {
return !!getTelegram()?.initData;
}
export function getInitData(): string {
return getTelegram()?.initData ?? "";
}
@ -91,7 +102,7 @@ export function addToHomeScreen() {
export function requestContactPhone(): Promise<string | null> {
return new Promise((resolve) => {
const tg = getTelegram();
if (!tg?.requestContact) {
if (!tg?.requestContact || !isInsideTelegram()) {
resolve(null);
return;
}