Payment Modal: Keep verification in the same tab (#6889)

This commit is contained in:
zubiden 2026-04-27 14:29:14 +02:00 committed by Alexander Zinchuk
parent 5841fddd0e
commit 04d45b2275
6 changed files with 34 additions and 38 deletions

View File

@ -36,10 +36,15 @@ import {
serializeBytes,
} from '../helpers/misc';
import localDb from '../localDb';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { handleGramJsUpdate, invokeRequest } from './client';
import { getTemporaryPaymentPassword } from './twoFaSettings';
type SendPaymentFormResult = {
completed: true;
} | {
verificationUrl: string;
};
export async function validateRequestedInfo({
inputInvoice,
requestInfo,
@ -90,7 +95,7 @@ export async function sendPaymentForm({
savedCredentialId?: string;
temporaryPassword?: string;
tipAmount?: number;
}) {
}): Promise<SendPaymentFormResult | undefined> {
const inputCredentials = temporaryPassword && savedCredentialId
? new GramJs.InputPaymentCredentialsSaved({
id: savedCredentialId,
@ -109,20 +114,19 @@ export async function sendPaymentForm({
...(tipAmount && { tipAmount: BigInt(tipAmount) }),
}));
if (!result) return false;
if (!result) return undefined;
if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
sendApiUpdate({
'@type': 'updatePaymentVerificationNeeded',
url: result.url,
});
return undefined;
} else {
handleGramJsUpdate(result.updates);
return {
verificationUrl: result.url,
};
}
return Boolean(result);
handleGramJsUpdate(result.updates);
return {
completed: true,
};
}
export async function sendStarPaymentForm({

View File

@ -610,11 +610,6 @@ export type ApiUpdatePeerBlocked = {
isBlockedFromStories?: boolean;
};
export type ApiUpdatePaymentVerificationNeeded = {
'@type': 'updatePaymentVerificationNeeded';
url: string;
};
export type ApiUpdatePaymentStateCompleted = {
'@type': 'updatePaymentStateCompleted';
paymentState: TabState['payment'];
@ -953,7 +948,7 @@ export type ApiUpdate = (
ApiUpdateServerTimeOffset | ApiUpdateMessageReactions | ApiUpdateSavedReactionTags |
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
ApiUpdatePendingJoinRequests | ApiUpdatePaymentStateCompleted |
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifySettings | ApiUpdatePinnedTopic |

View File

@ -85,7 +85,7 @@ const ConfirmPayment = ({
src={url}
title={lang('Checkout.WebConfirmation.Title')}
allow="payment"
sandbox="allow-modals allow-forms allow-scripts allow-top-navigation allow-same-origin"
sandbox="allow-modals allow-forms allow-scripts allow-same-origin"
className="ConfirmPayment__content"
/>
</div>

View File

@ -111,6 +111,7 @@ addActionHandler('openInvoice', async (global, actions, payload): Promise<void>
form,
isPaymentModalOpen: true,
isExtendedMedia: (payload as any).isExtendedMedia,
confirmPaymentUrl: undefined,
status: undefined,
}, tabId);
global = setPaymentStep(global, PaymentStep.Checkout, tabId);
@ -280,11 +281,24 @@ addActionHandler('sendPaymentForm', async (global, actions, payload): Promise<vo
tipAmount,
});
global = getGlobal();
if (selectTabState(global, tabId).payment.form?.formId !== formId) {
return;
}
if (!result) {
return;
}
global = getGlobal();
if ('verificationUrl' in result) {
global = updatePayment(global, {
confirmPaymentUrl: result.verificationUrl,
step: PaymentStep.ConfirmPayment,
}, tabId);
setGlobal(global);
return;
}
global = updatePayment(global, { status: 'paid' }, tabId);
global = closeInvoice(global, tabId);
setGlobal(global);

View File

@ -1,5 +1,4 @@
import type { ActionReturnType } from '../../types';
import { PaymentStep } from '../../../types';
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
import { applyLangPackDifference, getTranslationFn, requestLangPackDifference } from '../../../util/localization';
@ -13,8 +12,6 @@ import {
removeBlockedUser,
removePeerStory,
replaceWebPage,
setConfirmPaymentUrl,
setPaymentStep,
updateFullWebPage,
updateLastReadStoryForPeer,
updatePeerStory,
@ -149,14 +146,6 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
setGlobal(global);
break;
case 'updatePaymentVerificationNeeded':
Object.values(global.byTabId).forEach(({ id: tabId }) => {
global = setConfirmPaymentUrl(global, update.url, tabId);
global = setPaymentStep(global, PaymentStep.ConfirmPayment, tabId);
});
setGlobal(global);
break;
case 'updateWebViewResultSent':
Object.values(global.byTabId).forEach((tabState) => {
Object.entries(tabState.webApps.openedWebApps).forEach(([webAppKey, webApp]) => {

View File

@ -87,13 +87,6 @@ export function setSmartGlocalCardInfo<T extends GlobalState>(
return updatePayment(global, { smartGlocalCredentials: { ...cardInfo } }, tabId);
}
export function setConfirmPaymentUrl<T extends GlobalState>(
global: T, url?: string,
...[tabId = getCurrentTabId()]: TabArgs<T>
): T {
return updatePayment(global, { confirmPaymentUrl: url }, tabId);
}
export function setReceipt<T extends GlobalState>(
global: T,
receipt?: ApiReceiptRegular,
@ -131,6 +124,7 @@ export function closeInvoice<T extends GlobalState>(
...[tabId = getCurrentTabId()]: TabArgs<T>
): T {
global = updatePayment(global, {
confirmPaymentUrl: undefined,
isPaymentModalOpen: undefined,
isExtendedMedia: undefined,
}, tabId);