From ce5737fcbc44030595c0dc7a3cfda92e19aa2dbf Mon Sep 17 00:00:00 2001 From: c0mrade Date: Mon, 8 Jun 2026 14:21:45 +0300 Subject: [PATCH] fix(payments): open_url_direct payment URL opens externally in Telegram (#654272) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reported #654272 case ('функция немедленного открытия ссылки', reproduced on RollyPay AND YooKassa, fine on desktop) is the open_url_direct flow — not the connection deep-link opener fixed in 325e221. When a payment method has open_url_direct, the cabinet did window.location.href = payment_url INSIDE the Telegram in-app WebView. SBP/RollyPay/YooKassa pages then hand off to a bank app via a custom scheme, which the WebView can't open: Android shows net::ERR_UNKNOWN_URL_SCHEME, iOS opens nothing ('приложение не определяется'); link generation logs fine. Desktop works because it's a real browser. Add openPaymentUrl(): in Telegram open via openLink (external browser — the OS hands off to the bank app, return_url brings the user back); on web keep same-tab navigation (no popup blocker). Applied to TopUpAmount (top-up) and GiftSubscription (gift purchase). QuickPurchase is a web landing page (no platform abstraction) and is unaffected. --- src/pages/GiftSubscription.tsx | 7 +++++-- src/pages/TopUpAmount.tsx | 7 ++++++- src/utils/openPaymentUrl.ts | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/utils/openPaymentUrl.ts diff --git a/src/pages/GiftSubscription.tsx b/src/pages/GiftSubscription.tsx index 9dc2002..92c6da5 100644 --- a/src/pages/GiftSubscription.tsx +++ b/src/pages/GiftSubscription.tsx @@ -22,6 +22,7 @@ import { getApiErrorMessage } from '../utils/api-error'; import { formatPrice } from '../utils/format'; import { useCurrency } from '../hooks/useCurrency'; import { usePlatform, useHaptic } from '@/platform'; +import { openPaymentUrl } from '../utils/openPaymentUrl'; import { SparklesIcon, GiftIcon, @@ -388,7 +389,7 @@ function BuyTabContent({ }) { const { t } = useTranslation(); const queryClient = useQueryClient(); - const { openInvoice, capabilities } = usePlatform(); + const { openInvoice, capabilities, openLink, platform } = usePlatform(); const haptic = useHaptic(); // Selection state @@ -481,7 +482,9 @@ function BuyTabContent({ } return; } - window.location.href = result.payment_url; + // Non-Stars provider (RollyPay/YooKassa/SBP …): open externally inside Telegram so + // a bank-app hand-off via custom scheme doesn't dead-end in the WebView (#654272). + openPaymentUrl(result.payment_url, platform, openLink); } else { // Balance purchase: switch to MyGifts tab so the new code is visible queryClient.invalidateQueries({ queryKey: ['balance'] }); diff --git a/src/pages/TopUpAmount.tsx b/src/pages/TopUpAmount.tsx index a4cc3d7..94528e8 100644 --- a/src/pages/TopUpAmount.tsx +++ b/src/pages/TopUpAmount.tsx @@ -14,6 +14,7 @@ import type { PaymentMethod, PaymentMethodOption } from '../types'; import BentoCard from '../components/ui/BentoCard'; import { saveTopUpPendingInfo } from '../utils/topUpStorage'; import { getSafeRedirectPath } from '../utils/safeRedirect'; +import { openPaymentUrl } from '../utils/openPaymentUrl'; import { copyToClipboard } from '@/utils/clipboard'; import { CardIcon, @@ -245,7 +246,11 @@ export default function TopUpAmount() { lowerUrl.startsWith('http://t.me/') || lowerUrl.startsWith('tg://'); if (method?.open_url_direct && !isTelegramDeepLink) { - window.location.href = redirectUrl; + // In the Telegram WebView, same-container navigation to the provider page breaks + // when it hands off to a bank app via a custom scheme (SBP) — Android shows + // ERR_UNKNOWN_URL_SCHEME, iOS opens nothing (bug #654272). Open externally there; + // on web keep same-tab navigation. + openPaymentUrl(redirectUrl, platform, openLink); return; } diff --git a/src/utils/openPaymentUrl.ts b/src/utils/openPaymentUrl.ts new file mode 100644 index 0000000..b0a9dba --- /dev/null +++ b/src/utils/openPaymentUrl.ts @@ -0,0 +1,24 @@ +/** + * Open a payment-provider URL the right way for the current platform. + * + * In the Telegram in-app WebView, navigating the SAME container to the provider page + * (window.location.href) breaks when that page hands off to a bank app via a custom scheme + * — SBP/RollyPay/YooKassa do this. Android then shows a full-page net::ERR_UNKNOWN_URL_SCHEME + * and iOS opens nothing ("приложение не определяется"), even though link generation succeeded + * (Telegram bug #654272). Opening in the EXTERNAL browser (openLink) lets the OS hand off to + * the bank app, and the provider's return_url brings the user back. + * + * On the web platform a real browser handles the hand-off inline, so same-tab navigation is + * correct — and it isn't popup-blocked the way window.open() from an async callback would be. + */ +export function openPaymentUrl( + url: string, + platform: string, + openLink: (url: string) => void, +): void { + if (platform === 'telegram') { + openLink(url); + } else { + window.location.href = url; + } +}