tweb/src/components/popups/payment.ts
2022-08-21 16:53:49 +02:00

837 lines
28 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import PopupElement from '.';
import Currencies from '../../config/currencies';
import {FontFamily, FontSize} from '../../config/font';
import accumulate from '../../helpers/array/accumulate';
import getTextWidth from '../../helpers/canvas/getTextWidth';
import {detectUnifiedCardBrand} from '../../helpers/cards/cardBrands';
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
import findUpAsChild from '../../helpers/dom/findUpAsChild';
import findUpClassName from '../../helpers/dom/findUpClassName';
import placeCaretAtEnd from '../../helpers/dom/placeCaretAtEnd';
import {renderImageFromUrlPromise} from '../../helpers/dom/renderImageFromUrl';
import replaceContent from '../../helpers/dom/replaceContent';
import setInnerHTML from '../../helpers/dom/setInnerHTML';
import toggleDisability from '../../helpers/dom/toggleDisability';
import {formatPhoneNumber} from '../../helpers/formatPhoneNumber';
import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount';
import ScrollSaver from '../../helpers/scrollSaver';
import tsNow from '../../helpers/tsNow';
import {AccountTmpPassword, InputInvoice, InputPaymentCredentials, LabeledPrice, Message, MessageMedia, PaymentRequestedInfo, PaymentSavedCredentials, PaymentsPaymentForm, PaymentsPaymentReceipt, PaymentsValidatedRequestedInfo, PostAddress, ShippingOption} from '../../layer';
import I18n, {i18n, LangPackKey, _i18n} from '../../lib/langPack';
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import wrapRichText from '../../lib/richTextProcessor/wrapRichText';
import rootScope from '../../lib/rootScope';
import AvatarElement from '../avatar';
import Button from '../button';
import CheckboxField from '../checkboxField';
import PeerTitle from '../peerTitle';
import {putPreloader} from '../putPreloader';
import Row from '../row';
import {toastNew} from '../toast';
import {wrapPhoto} from '../wrappers';
import wrapPeerTitle from '../wrappers/peerTitle';
import PopupPaymentCard, {PaymentCardDetails, PaymentCardDetailsResult} from './paymentCard';
import PopupPaymentCardConfirmation from './paymentCardConfirmation';
import PopupPaymentShipping, {PaymentShippingAddress} from './paymentShipping';
import PopupPaymentShippingMethods from './paymentShippingMethods';
import PopupPaymentVerification from './paymentVerification';
const iconPath = 'assets/img/';
const icons = [
'amex',
'card',
'diners',
'discover',
'jcb',
'mastercard',
'visa',
'unionpay',
'mir',
'logo'
];
export function getPaymentBrandIconPath(brand: string) {
if(!icons.includes(brand)) {
return;
}
return `${iconPath}${brand}.svg`;
}
export function PaymentButton(options: {
onClick: () => Promise<any> | void,
key?: LangPackKey,
textEl?: I18n.IntlElement
}) {
const textEl = options.textEl ?? new I18n.IntlElement({key: options.key ?? 'PaymentInfo.Done'});
const key = textEl.key;
const payButton = Button('btn-primary btn-color-primary payment-item-pay');
payButton.append(textEl.element);
attachClickEvent(payButton, async() => {
const result = options.onClick();
if(!(result instanceof Promise)) {
return;
}
const d = putPreloader(payButton);
const toggle = toggleDisability([payButton], true);
textEl.compareAndUpdate({key: 'PleaseWait'});
try {
await result;
} catch(err) {
if(!(err as any).handled) {
console.error('payment button error', err);
}
toggle();
textEl.compareAndUpdate({key});
d.remove();
}
});
return payButton;
}
export type PaymentsCredentialsToken = {type: 'card', token?: string, id?: string};
export default class PopupPayment extends PopupElement {
private tipButtonsMap: Map<number, HTMLElement>;
constructor(
private message: Message.message,
private inputInvoice: InputInvoice,
private paymentForm?: PaymentsPaymentForm | PaymentsPaymentReceipt
) {
super('popup-payment', {
closable: true,
overlayClosable: true,
body: true,
scrollable: true,
title: true
});
this.tipButtonsMap = new Map();
this.d();
}
private async d() {
this.element.classList.add('is-loading');
this.show();
let confirmed = false;
const onConfirmed = () => {
if(confirmed) {
return;
}
confirmed = true;
if(popupPaymentVerification) {
popupPaymentVerification.hide();
}
this.hide();
};
let {paymentForm, message} = this;
if(message) {
this.listenerSetter.add(rootScope)('payment_sent', ({peerId, mid}) => {
if(message.peerId === peerId && message.mid === mid) {
onConfirmed();
}
});
}
const mediaInvoice = message?.media as MessageMedia.messageMediaInvoice;
const isReceipt = mediaInvoice ? !!mediaInvoice.receipt_msg_id : paymentForm._ === 'payments.paymentReceipt';
const isTest = mediaInvoice ? mediaInvoice.pFlags.test : paymentForm.invoice.pFlags.test;
const photo = mediaInvoice ? mediaInvoice.photo : paymentForm.photo;
const title = mediaInvoice ? mediaInvoice.title : paymentForm.title;
const description = mediaInvoice ? mediaInvoice.description : paymentForm.description;
_i18n(this.title, isReceipt ? 'PaymentReceipt' : 'PaymentCheckout');
if(isTest) {
this.title.append(' (Test)');
}
const className = 'payment-item';
const itemEl = document.createElement('div');
itemEl.classList.add(className);
const detailsClassName = className + '-details';
const details = document.createElement('div');
details.classList.add(detailsClassName);
let photoEl: HTMLElement;
if(photo) {
photoEl = document.createElement('div');
photoEl.classList.add(detailsClassName + '-photo', 'media-container-contain');
wrapPhoto({
photo: photo,
container: photoEl,
boxWidth: 100,
boxHeight: 100,
size: {_: 'photoSizeEmpty', type: ''}
});
details.append(photoEl);
}
const linesClassName = detailsClassName + '-lines';
const linesEl = document.createElement('div');
linesEl.classList.add(linesClassName);
const titleEl = document.createElement('div');
titleEl.classList.add(linesClassName + '-title');
const descriptionEl = document.createElement('div');
descriptionEl.classList.add(linesClassName + '-description');
const botName = document.createElement('div');
botName.classList.add(linesClassName + '-bot-name');
linesEl.append(titleEl, descriptionEl, botName);
setInnerHTML(titleEl, wrapEmojiText(title));
setInnerHTML(descriptionEl, wrapEmojiText(description));
const peerTitle = new PeerTitle();
botName.append(peerTitle.element);
details.append(linesEl);
itemEl.append(details);
this.scrollable.append(itemEl);
const preloaderContainer = document.createElement('div');
preloaderContainer.classList.add(className + '-preloader-container');
const preloader = putPreloader(preloaderContainer, true);
this.scrollable.container.append(preloaderContainer);
const inputInvoice = this.inputInvoice;
if(!paymentForm) {
if(isReceipt) paymentForm = await this.managers.appPaymentsManager.getPaymentReceipt(message.peerId, mediaInvoice.receipt_msg_id);
else paymentForm = await this.managers.appPaymentsManager.getPaymentForm(inputInvoice);
this.paymentForm = paymentForm;
}
let savedInfo = (paymentForm as PaymentsPaymentForm).saved_info || (paymentForm as PaymentsPaymentReceipt).info;
const savedCredentials = (paymentForm as PaymentsPaymentForm).saved_credentials?.[0];
let [lastRequestedInfo, passwordState, providerPeerTitle] = await Promise.all([
!isReceipt && savedInfo && this.managers.appPaymentsManager.validateRequestedInfo(inputInvoice, savedInfo),
savedCredentials && this.managers.passwordManager.getState(),
wrapPeerTitle({peerId: paymentForm.provider_id.toPeerId()})
]);
console.log(paymentForm, lastRequestedInfo);
await peerTitle.update({peerId: paymentForm.bot_id.toPeerId()});
preloaderContainer.remove();
this.element.classList.remove('is-loading');
const wrapAmount = (amount: string | number, skipSymbol?: boolean) => {
return paymentsWrapCurrencyAmount(amount, currency, skipSymbol);
};
const {invoice} = paymentForm;
const currency = invoice.currency;
const isRecurring = invoice.pFlags.recurring && !isReceipt;
await peerTitle.update({peerId: paymentForm.bot_id.toPeerId()});
const peerTitle2 = isRecurring ? await wrapPeerTitle({peerId: paymentForm.bot_id.toPeerId()}) : undefined;
preloaderContainer.remove();
this.element.classList.remove('is-loading');
const makeLabel = () => {
const labelEl = document.createElement('div');
labelEl.classList.add(pricesClassName + '-price');
const left = document.createElement('span');
const right = document.createElement('span');
labelEl.append(left, right);
return {label: labelEl, left, right};
};
const pricesClassName = className + '-prices';
const prices = document.createElement('div');
prices.classList.add(pricesClassName);
const makePricesElements = (prices: LabeledPrice[]) => {
return prices.map((price) => {
const {amount, label} = price;
const _label = makeLabel();
_label.left.textContent = label;
const wrappedAmount = wrapAmount(amount);
_label.right.textContent = wrappedAmount;
return _label.label;
});
};
const pricesElements = makePricesElements(invoice.prices);
let getTipsAmount = (): number => 0;
let shippingAmount = 0;
const getTotalTotal = () => totalAmount + getTipsAmount() + shippingAmount;
const setTotal = () => {
const wrapped = wrapAmount(getTotalTotal());
totalLabel.right.textContent = wrapped;
payI18n.compareAndUpdate({
key: 'PaymentCheckoutPay',
args: [wrapped]
});
};
const payI18n = new I18n.IntlElement();
const totalLabel = makeLabel();
totalLabel.label.classList.add('is-total');
_i18n(totalLabel.left, 'PaymentTransactionTotal');
const totalAmount = accumulate(invoice.prices.map(({amount}) => +amount), 0);
const canTip = (invoice.max_tip_amount !== undefined && !isReceipt) || !!(paymentForm as PaymentsPaymentReceipt).tip_amount;
if(canTip) {
const tipsClassName = className + '-tips';
const currencyData = Currencies[currency];
getTipsAmount = () => +getInputValue().replace(/\D/g, '');
const getInputValue = () => {
// return input.textContent;
return input.value;
};
const setInputWidth = () => {
const width = getTextWidth(getInputValue(), `500 ${FontSize} ${FontFamily}`);
input.style.width = width + 'px';
};
const setInputValue = (amount: string | number) => {
amount = Math.min(+amount, +invoice.max_tip_amount);
const wrapped = wrapAmount(amount, true);
input.value = wrapped;
// input.textContent = wrapped;
if(document.activeElement === input) {
placeCaretAtEnd(input);
}
unsetActiveTip && unsetActiveTip();
const tipEl = this.tipButtonsMap.get(amount);
if(tipEl) {
tipEl.classList.add('active');
}
setInputWidth();
setTotal();
};
const tipsLabel = makeLabel();
_i18n(tipsLabel.left, isReceipt ? 'PaymentTip' : 'PaymentTipOptional');
const input = document.createElement('input');
input.type = 'tel';
// const input: HTMLElement = document.createElement('div');
// input.contentEditable = 'true';
input.classList.add('input-clear', tipsClassName + '-input');
tipsLabel.right.append(input);
if(!isReceipt) {
tipsLabel.label.style.cursor = 'text';
} else {
tipsLabel.label.classList.add('disable-hover');
}
tipsLabel.label.addEventListener('mousedown', (e) => {
if(!findUpAsChild(e.target as HTMLElement, input)) {
placeCaretAtEnd(input);
}
});
const haveToIgnoreEvents = input instanceof HTMLInputElement ? 1 : 2;
const onSelectionChange = () => {
if(ignoreNextSelectionChange) {
--ignoreNextSelectionChange;
return;
}
// setTimeout(() => {
ignoreNextSelectionChange = haveToIgnoreEvents;
placeCaretAtEnd(input);
// }, 0);
};
const onFocus = () => {
// cancelEvent(e);
setTimeout(() => {
ignoreNextSelectionChange = haveToIgnoreEvents;
placeCaretAtEnd(input);
document.addEventListener('selectionchange', onSelectionChange);
}, 0);
};
const onFocusOut = () => {
input.addEventListener('focus', onFocus, {once: true});
document.removeEventListener('selectionchange', onSelectionChange);
};
let ignoreNextSelectionChange: number;
input.addEventListener('focusout', onFocusOut);
onFocusOut();
input.addEventListener('input', () => {
setInputValue(getTipsAmount());
});
const s = [currencyData.symbol, currencyData.space_between ? ' ' : ''];
if(!currencyData.symbol_left) s.reverse();
tipsLabel.right[currencyData.symbol_left ? 'prepend' : 'append'](s.join(''));
pricesElements.push(tipsLabel.label);
//
let unsetActiveTip: () => void;
if(!isReceipt) {
const tipsEl = document.createElement('div');
tipsEl.classList.add(tipsClassName);
const tipClassName = tipsClassName + '-tip';
const tipButtons = invoice.suggested_tip_amounts.map((tipAmount) => {
const button = Button(tipClassName, {noRipple: true});
button.textContent = wrapAmount(tipAmount);
this.tipButtonsMap.set(+tipAmount, button);
return button;
});
unsetActiveTip = () => {
const prevTipEl = tipsEl.querySelector('.active');
if(prevTipEl) {
prevTipEl.classList.remove('active');
}
};
attachClickEvent(tipsEl, (e) => {
const tipEl = findUpClassName(e.target, tipClassName);
if(!tipEl) {
return;
}
let tipAmount = 0;
if(tipEl.classList.contains('active')) {
tipEl.classList.remove('active');
} else {
unsetActiveTip();
tipEl.classList.add('active');
for(const [amount, el] of this.tipButtonsMap) {
if(el === tipEl) {
tipAmount = amount;
break;
}
}
}
setInputValue(tipAmount);
});
setInputValue(0);
tipsEl.append(...tipButtons);
pricesElements.push(tipsEl);
} else {
setInputValue((paymentForm as PaymentsPaymentReceipt).tip_amount);
}
} else {
setTotal();
}
pricesElements.push(totalLabel.label);
prices.append(...pricesElements);
itemEl.append(prices);
// /
const setRowIcon = async(row: Row, icon?: string) => {
const img = document.createElement('img');
img.classList.add('media-photo');
await renderImageFromUrlPromise(img, getPaymentBrandIconPath(icon));
let container = row.media;
if(!container) {
container = row.createMedia('small');
container.classList.add('media-container-cover');
container.append(img);
} else {
replaceContent(container, img);
}
};
const createRow = (options: ConstructorParameters<typeof Row>[0]) => {
if(options.titleLangKey) {
options.subtitleLangKey = options.titleLangKey;
}
options.noWrap = true;
const row = new Row(options);
row.container.classList.add(className + '-row');
if(options.titleLangKey) {
row.subtitle.classList.add('hide');
}
return row;
};
const setRowTitle = (row: Row, textContent: string) => {
row.title.textContent = textContent;
if(!textContent) {
const e = I18n.weakMap.get(row.subtitle.firstElementChild as HTMLElement) as I18n.IntlElement;
row.title.append(i18n(e.key));
}
row.subtitle.classList.toggle('hide', !textContent);
};
const setCardSubtitle = (card: PaymentCardDetailsResult) => {
let brand: string;
let str: string;
let icon: string;
if('title' in card) {
brand = card.title.split(' ').shift();
str = card.title;
icon = card.icon;
} else {
brand = detectUnifiedCardBrand(card.cardNumber);
str = brand + ' *' + card.cardNumber.split(' ').pop();
}
methodRow.title.classList.remove('tgico', 'tgico-card_outline');
setRowIcon(methodRow, icon || brand.toLowerCase());
setRowTitle(methodRow, str);
};
const onMethodClick = () => {
new PopupPaymentCard(paymentForm as PaymentsPaymentForm, previousCardDetails as PaymentCardDetails).addEventListener('finish', ({token, card}) => {
previousToken = token, previousCardDetails = card;
setCardSubtitle(card);
});
};
let previousCardDetails: PaymentCardDetailsResult, previousToken: PaymentsCredentialsToken;
const methodRow = createRow({
titleLangKey: 'PaymentCheckoutMethod',
clickable: isReceipt ? undefined : onMethodClick,
icon: 'card_outline'
});
methodRow.container.classList.add(className + '-method-row');
if(savedCredentials) {
setCardSubtitle(savedCredentials);
} else if((paymentForm as PaymentsPaymentReceipt).credentials_title) {
setCardSubtitle({title: (paymentForm as PaymentsPaymentReceipt).credentials_title});
}
const providerRow = createRow({
title: providerPeerTitle,
subtitleLangKey: 'PaymentCheckoutProvider'
});
const providerAvatar = new AvatarElement();
providerAvatar.classList.add('avatar-32');
providerRow.createMedia('small').append(providerAvatar);
/* await */ providerAvatar.updateWithOptions({peerId: paymentForm.provider_id.toPeerId()});
let shippingAddressRow: Row, shippingNameRow: Row, shippingEmailRow: Row, shippingPhoneRow: Row, shippingMethodRow: Row;
let lastShippingOption: ShippingOption, onShippingAddressClick: (focus?: ConstructorParameters<typeof PopupPaymentShipping>[2]) => void, onShippingMethodClick: () => void;
const setShippingTitle = invoice.pFlags.shipping_address_requested ? (shippingAddress?: PaymentShippingAddress) => {
if(!shippingAddress) {
shippingMethodRow.subtitle.classList.add('hide');
replaceContent(shippingMethodRow.title, i18n('PaymentShippingAddress'));
return;
}
const postAddress = shippingAddress.shipping_address;
setRowTitle(shippingAddressRow, [postAddress.city, postAddress.street_line1, postAddress.street_line2].filter(Boolean).join(', '));
shippingMethodRow.container.classList.toggle('hide', !lastRequestedInfo && !isReceipt);
} : undefined;
const setShippingInfo = (info: PaymentRequestedInfo) => {
setShippingTitle && setShippingTitle(info);
shippingNameRow && setRowTitle(shippingNameRow, info.name);
shippingEmailRow && setRowTitle(shippingEmailRow, info.email);
shippingPhoneRow && setRowTitle(shippingPhoneRow, info.phone && ('+' + formatPhoneNumber(info.phone).formatted));
};
if(!isReceipt) {
onShippingAddressClick = (focus) => {
new PopupPaymentShipping(paymentForm as PaymentsPaymentForm, inputInvoice, focus).addEventListener('finish', ({shippingAddress, requestedInfo}) => {
lastRequestedInfo = requestedInfo;
savedInfo = (paymentForm as PaymentsPaymentForm).saved_info = shippingAddress;
setShippingInfo(shippingAddress);
});
};
}
if(invoice.pFlags.shipping_address_requested) {
const setShippingOption = (shippingOption?: ShippingOption) => {
const scrollSaver = new ScrollSaver(this.scrollable, undefined, true);
scrollSaver.save();
if(lastShippingPricesElements) {
lastShippingPricesElements.forEach((node) => node.remove());
}
if(!shippingOption) {
shippingAmount = 0;
setTotal();
scrollSaver.restore();
this.onContentUpdate();
return;
}
lastShippingOption = shippingOption;
setRowTitle(shippingMethodRow, shippingOption.title);
shippingAmount = accumulate(shippingOption.prices.map(({amount}) => +amount), 0);
lastShippingPricesElements = makePricesElements(shippingOption.prices);
let l = totalLabel.label;
if(canTip) {
l = l.previousElementSibling as any;
if(!isReceipt) {
l = l.previousElementSibling as any;
}
}
lastShippingPricesElements.forEach((element) => l.parentElement.insertBefore(element, l));
setTotal();
scrollSaver.restore();
this.onContentUpdate();
};
shippingAddressRow = createRow({
icon: 'location',
titleLangKey: 'PaymentShippingAddress',
clickable: !isReceipt && onShippingAddressClick.bind(null, undefined)
});
let lastShippingPricesElements: HTMLElement[];
shippingMethodRow = createRow({
icon: 'shipping',
titleLangKey: 'PaymentCheckoutShippingMethod',
clickable: !isReceipt && (onShippingMethodClick = () => {
new PopupPaymentShippingMethods(paymentForm as PaymentsPaymentForm, lastRequestedInfo, lastShippingOption).addEventListener('finish', (shippingOption) => {
setShippingOption(shippingOption);
});
})
});
shippingMethodRow.container.classList.add('hide');
const shippingOption = (paymentForm as PaymentsPaymentReceipt).shipping;
if(shippingOption) {
setShippingOption(shippingOption);
}
}
if(invoice.pFlags.name_requested) {
shippingNameRow = createRow({
icon: 'newprivate',
titleLangKey: 'PaymentCheckoutName',
clickable: !isReceipt && onShippingAddressClick.bind(null, 'name')
});
}
if(invoice.pFlags.email_requested) {
shippingEmailRow = createRow({
icon: 'mention',
titleLangKey: 'PaymentShippingEmailPlaceholder',
clickable: !isReceipt && onShippingAddressClick.bind(null, 'email')
});
}
if(invoice.pFlags.phone_requested) {
shippingPhoneRow = createRow({
icon: 'phone',
titleLangKey: 'PaymentCheckoutPhoneNumber',
clickable: !isReceipt && onShippingAddressClick.bind(null, 'phone')
});
}
if(savedInfo) {
setShippingInfo(savedInfo);
}
const rows = [
methodRow,
providerRow,
shippingAddressRow,
shippingMethodRow,
shippingNameRow,
shippingEmailRow,
shippingPhoneRow
].filter(Boolean);
const acceptTermsCheckboxField = isRecurring && new CheckboxField({
text: 'Payments.Recurrent.Accept',
textArgs: [wrapRichText(invoice.recurring_terms_url), peerTitle2]
});
const acceptTermsRow = isRecurring && createRow({
checkboxField: acceptTermsCheckboxField,
noCheckboxSubtitle: true
});
const recurringElements = isRecurring ? [document.createElement('hr'), acceptTermsRow.container] : [];
this.scrollable.append(...[
document.createElement('hr'),
...rows.map((row) => row.container),
...recurringElements
].filter(Boolean));
// /
let popupPaymentVerification: PopupPaymentVerification, lastTmpPasword: AccountTmpPassword;
const onClick = () => {
const missingInfo = invoice.pFlags.name_requested && !savedInfo?.name ? 'name' : (invoice.pFlags.email_requested && !savedInfo?.email ? 'email' : (invoice.pFlags.phone_requested && !savedInfo?.phone ? 'phone' : undefined));
if(invoice.pFlags.shipping_address_requested) {
if(!lastRequestedInfo) {
onShippingAddressClick();
return;
} else if(!lastShippingOption) {
onShippingMethodClick();
return;
}
} else if(missingInfo) {
onShippingAddressClick(missingInfo);
return;
}
if(!previousCardDetails && !lastTmpPasword) {
if(!savedCredentials) {
onMethodClick();
return;
}
Promise.resolve(passwordState ?? this.managers.passwordManager.getState()).then((_passwordState) => {
new PopupPaymentCardConfirmation(savedCredentials.title, _passwordState).addEventListener('finish', (tmpPassword) => {
passwordState = undefined;
lastTmpPasword = tmpPassword;
simulateClickEvent(payButton);
// * reserve 5 seconds
const diff = tmpPassword.valid_until - tsNow(true) - 5;
setTimeout(() => {
if(lastTmpPasword === tmpPassword) {
lastTmpPasword = undefined;
}
}, diff * 1000);
});
});
return;
}
return Promise.resolve().then(async() => {
const credentials: InputPaymentCredentials = lastTmpPasword ? {
_: 'inputPaymentCredentialsSaved',
id: savedCredentials.id,
tmp_password: lastTmpPasword.tmp_password
} : {
_: 'inputPaymentCredentials',
data: {
_: 'dataJSON',
data: JSON.stringify(previousToken.token ? previousToken : {type: previousToken.type, id: previousToken.id})
},
pFlags: {
save: previousCardDetails.save || undefined
}
};
try {
const paymentResult = await this.managers.appPaymentsManager.sendPaymentForm(
inputInvoice,
(paymentForm as PaymentsPaymentForm).form_id,
lastRequestedInfo?.id,
lastShippingOption?.id,
credentials,
getTipsAmount()
);
if(paymentResult._ === 'payments.paymentResult') {
onConfirmed();
} else {
popupPaymentVerification = new PopupPaymentVerification(paymentResult.url);
popupPaymentVerification.addEventListener('finish', () => {
popupPaymentVerification = undefined;
onConfirmed();
});
await new Promise<void>((resolve, reject) => {
popupPaymentVerification.addEventListener('close', () => {
popupPaymentVerification = undefined;
if(confirmed) {
resolve();
} else {
const err = new Error('payment not finished');
(err as ApiError).handled = true;
reject(err);
}
});
});
}
} catch(err) {
if((err as ApiError).type === 'BOT_PRECHECKOUT_TIMEOUT') {
toastNew({langPackKey: 'Error.AnError'});
(err as ApiError).handled = true;
} else if((err as ApiError).type === 'TMP_PASSWORD_INVALID') {
passwordState = lastTmpPasword = undefined;
simulateClickEvent(payButton);
(err as ApiError).handled = true;
}
throw err;
}
});
};
const onChange = () => {
payButton.disabled = !!(acceptTermsCheckboxField && !acceptTermsCheckboxField.checked);
};
let payButton: HTMLButtonElement;
if(isReceipt) {
payButton = PaymentButton({
onClick: () => this.hide(),
key: 'Done'
});
} else {
payButton = PaymentButton({
onClick: onClick,
textEl: payI18n
});
}
onChange();
if(acceptTermsCheckboxField) {
acceptTermsCheckboxField.input.addEventListener('change', onChange);
}
this.body.append(this.btnConfirmOnEnter = payButton);
this.onContentUpdate();
}
}