Payments fixes
This commit is contained in:
parent
ff4652530e
commit
2c9d5c30fe
@ -1588,11 +1588,17 @@ export default class ChatBubbles {
|
||||
if(typeof(peerIdStr) === 'string' || savedFrom) {
|
||||
if(savedFrom) {
|
||||
const [peerId, mid] = savedFrom.split('_');
|
||||
|
||||
if(target.classList.contains('is-receipt-link')) {
|
||||
const message = await this.managers.appMessagesManager.getMessageByPeer(peerId.toPeerId(), +mid);
|
||||
if(message) {
|
||||
new PopupPayment(message as Message.message, this.peerId, +bubble.dataset.mid);
|
||||
}
|
||||
} else {
|
||||
this.chat.appImManager.setInnerPeer({
|
||||
peerId: peerId.toPeerId(),
|
||||
lastMsgId: +mid
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const peerId = peerIdStr.toPeerId();
|
||||
if(peerId !== NULL_PEER_ID) {
|
||||
|
@ -52,6 +52,7 @@ const icons = [
|
||||
'mastercard',
|
||||
'visa',
|
||||
'unionpay',
|
||||
'mir',
|
||||
'logo',
|
||||
];
|
||||
|
||||
@ -102,7 +103,11 @@ export default class PopupPayment extends PopupElement {
|
||||
private currency: string;
|
||||
private tipButtonsMap: Map<number, HTMLElement>;
|
||||
|
||||
constructor(private message: Message.message) {
|
||||
constructor(
|
||||
private message: Message.message,
|
||||
private receiptPeerId?: PeerId,
|
||||
private receiptMsgId?: number
|
||||
) {
|
||||
super('popup-payment', {
|
||||
closable: true,
|
||||
overlayClosable: true,
|
||||
@ -153,7 +158,9 @@ export default class PopupPayment extends PopupElement {
|
||||
const {message} = this;
|
||||
const mediaInvoice = message.media as MessageMedia.messageMediaInvoice;
|
||||
|
||||
_i18n(this.title, mediaInvoice.receipt_msg_id ? 'PaymentReceipt' : 'PaymentCheckout');
|
||||
const isReceipt = !!(this.receiptMsgId || mediaInvoice.receipt_msg_id);
|
||||
|
||||
_i18n(this.title, isReceipt ? 'PaymentReceipt' : 'PaymentCheckout');
|
||||
if(mediaInvoice.pFlags.test) {
|
||||
this.title.append(' (Test)');
|
||||
}
|
||||
@ -170,7 +177,7 @@ export default class PopupPayment extends PopupElement {
|
||||
let photoEl: HTMLElement;
|
||||
if(mediaInvoice.photo) {
|
||||
photoEl = document.createElement('div');
|
||||
photoEl.classList.add(detailsClassName + '-photo', 'media-container-cover');
|
||||
photoEl.classList.add(detailsClassName + '-photo', 'media-container-contain');
|
||||
wrapPhoto({
|
||||
photo: mediaInvoice.photo,
|
||||
container: photoEl,
|
||||
@ -212,20 +219,22 @@ export default class PopupPayment extends PopupElement {
|
||||
this.scrollable.container.append(preloaderContainer);
|
||||
|
||||
let paymentForm: PaymentsPaymentForm | PaymentsPaymentReceipt;
|
||||
const isReceipt = !!mediaInvoice.receipt_msg_id;
|
||||
|
||||
if(isReceipt) paymentForm = await this.managers.appPaymentsManager.getPaymentReceipt(message.peerId, mediaInvoice.receipt_msg_id);
|
||||
this.receiptMsgId ??= mediaInvoice.receipt_msg_id;
|
||||
this.receiptPeerId ??= this.receiptMsgId && message.peerId;
|
||||
|
||||
if(isReceipt) paymentForm = await this.managers.appPaymentsManager.getPaymentReceipt(this.receiptPeerId, this.receiptMsgId);
|
||||
else paymentForm = await this.managers.appPaymentsManager.getPaymentForm(message.peerId, message.mid);
|
||||
|
||||
let savedInfo = (paymentForm as PaymentsPaymentForm).saved_info || (paymentForm as PaymentsPaymentReceipt).info;
|
||||
const savedCredentials = (paymentForm as PaymentsPaymentForm).saved_credentials;
|
||||
let [lastRequestedInfo, passwordState, providerPeerTitle] = await Promise.all([
|
||||
!isReceipt && savedInfo && this.managers.appPaymentsManager.validateRequestedInfo(message.peerId, message.mid, savedInfo),
|
||||
!isReceipt && savedInfo && this.managers.appPaymentsManager.validateRequestedInfo(message.peerId, message.mid, savedInfo).catch(() => undefined),
|
||||
savedCredentials && this.managers.passwordManager.getState(),
|
||||
wrapPeerTitle({peerId: paymentForm.provider_id.toPeerId()})
|
||||
]);
|
||||
|
||||
// console.log(paymentForm, lastRequestedInfo);
|
||||
console.log(paymentForm, lastRequestedInfo);
|
||||
|
||||
await peerTitle.update({peerId: paymentForm.bot_id.toPeerId()});
|
||||
preloaderContainer.remove();
|
||||
@ -258,8 +267,8 @@ export default class PopupPayment extends PopupElement {
|
||||
const _label = makeLabel();
|
||||
_label.left.textContent = label;
|
||||
|
||||
const wrappedAmount = wrapAmount(Math.abs(+amount));
|
||||
_label.right.textContent = (amount < 0 ? '-' : '') + wrappedAmount;
|
||||
const wrappedAmount = wrapAmount(amount);
|
||||
_label.right.textContent = wrappedAmount;
|
||||
|
||||
return _label.label;
|
||||
});
|
||||
@ -287,7 +296,7 @@ export default class PopupPayment extends PopupElement {
|
||||
_i18n(totalLabel.left, 'PaymentTransactionTotal');
|
||||
const totalAmount = accumulate(invoice.prices.map(({amount}) => +amount), 0);
|
||||
|
||||
const canTip = invoice.max_tip_amount !== undefined;
|
||||
const canTip = (invoice.max_tip_amount !== undefined && !isReceipt) || !!(paymentForm as PaymentsPaymentReceipt).tip_amount;
|
||||
if(canTip) {
|
||||
const tipsClassName = className + '-tips';
|
||||
|
||||
@ -315,7 +324,7 @@ export default class PopupPayment extends PopupElement {
|
||||
placeCaretAtEnd(input);
|
||||
}
|
||||
|
||||
unsetActiveTip();
|
||||
unsetActiveTip && unsetActiveTip();
|
||||
const tipEl = this.tipButtonsMap.get(amount);
|
||||
if(tipEl) {
|
||||
tipEl.classList.add('active');
|
||||
@ -326,7 +335,7 @@ export default class PopupPayment extends PopupElement {
|
||||
};
|
||||
|
||||
const tipsLabel = makeLabel();
|
||||
_i18n(tipsLabel.left, mediaInvoice.receipt_msg_id ? 'PaymentTip' : 'PaymentTipOptional');
|
||||
_i18n(tipsLabel.left, isReceipt ? 'PaymentTip' : 'PaymentTipOptional');
|
||||
const input = document.createElement('input');
|
||||
input.type = 'tel';
|
||||
// const input: HTMLElement = document.createElement('div');
|
||||
@ -334,7 +343,12 @@ export default class PopupPayment extends PopupElement {
|
||||
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, input)) {
|
||||
placeCaretAtEnd(input);
|
||||
@ -384,6 +398,8 @@ export default class PopupPayment extends PopupElement {
|
||||
pricesElements.push(tipsLabel.label);
|
||||
|
||||
///
|
||||
let unsetActiveTip: () => void;
|
||||
if(!isReceipt) {
|
||||
const tipsEl = document.createElement('div');
|
||||
tipsEl.classList.add(tipsClassName);
|
||||
|
||||
@ -396,7 +412,7 @@ export default class PopupPayment extends PopupElement {
|
||||
return button;
|
||||
});
|
||||
|
||||
const unsetActiveTip = () => {
|
||||
unsetActiveTip = () => {
|
||||
const prevTipEl = tipsEl.querySelector('.active');
|
||||
if(prevTipEl) {
|
||||
prevTipEl.classList.remove('active');
|
||||
@ -431,6 +447,9 @@ export default class PopupPayment extends PopupElement {
|
||||
|
||||
tipsEl.append(...tipButtons);
|
||||
pricesElements.push(tipsEl);
|
||||
} else {
|
||||
setInputValue((paymentForm as PaymentsPaymentReceipt).tip_amount);
|
||||
}
|
||||
} else {
|
||||
setTotal();
|
||||
}
|
||||
@ -474,7 +493,7 @@ export default class PopupPayment extends PopupElement {
|
||||
const setRowTitle = (row: Row, textContent: string) => {
|
||||
row.title.textContent = textContent;
|
||||
if(!textContent) {
|
||||
const e = I18n.weakMap.get(row.subtitle) as I18n.IntlElement;
|
||||
const e = I18n.weakMap.get(row.subtitle.firstElementChild as HTMLElement) as I18n.IntlElement;
|
||||
row.title.append(i18n(e.key));
|
||||
}
|
||||
|
||||
@ -543,7 +562,8 @@ export default class PopupPayment extends PopupElement {
|
||||
|
||||
const postAddress = shippingAddress.shipping_address;
|
||||
setRowTitle(shippingAddressRow, [postAddress.city, postAddress.street_line1, postAddress.street_line2].filter(Boolean).join(', '));
|
||||
shippingMethodRow.container.classList.remove('hide');
|
||||
|
||||
shippingMethodRow.container.classList.toggle('hide', !lastRequestedInfo && !isReceipt);
|
||||
} : undefined;
|
||||
|
||||
const setShippingInfo = (info: PaymentRequestedInfo) => {
|
||||
@ -586,7 +606,13 @@ export default class PopupPayment extends PopupElement {
|
||||
shippingAmount = accumulate(shippingOption.prices.map(({amount}) => +amount), 0);
|
||||
lastShippingPricesElements = makePricesElements(shippingOption.prices);
|
||||
let l = totalLabel.label;
|
||||
if(canTip) l = l.previousElementSibling.previousElementSibling as any;
|
||||
if(canTip) {
|
||||
l = l.previousElementSibling as any;
|
||||
if(!isReceipt) {
|
||||
l = l.previousElementSibling as any;
|
||||
}
|
||||
}
|
||||
|
||||
lastShippingPricesElements.forEach((element) => l.parentElement.insertBefore(element, l));
|
||||
|
||||
setTotal();
|
||||
|
@ -23,6 +23,7 @@ import Row from "../row";
|
||||
import { SettingSection } from "../sidebarLeft";
|
||||
import { getPaymentBrandIconPath, PaymentButton, PaymentsCredentialsToken } from "./payment";
|
||||
import { createVerificationIframe } from "./paymentVerification";
|
||||
// import { putPreloader } from "../putPreloader";
|
||||
|
||||
export type PaymentCardDetails = {
|
||||
cardNumber: string;
|
||||
@ -254,6 +255,7 @@ export default class PopupPaymentCard extends PopupElement<{
|
||||
}
|
||||
});
|
||||
|
||||
// putPreloader(this.body, true);
|
||||
this.body.append(iframe);
|
||||
this.show();
|
||||
}
|
||||
|
@ -264,7 +264,10 @@ export default async function wrapMessageActionTextNewUnsafe(message: MyMessage,
|
||||
managers.appMessagesManager.fetchMessageReplyTo(message);
|
||||
} else {
|
||||
langPackKey = 'PaymentSuccessfullyPaid';
|
||||
args.push(wrapLinkToMessage(invoiceMessage, plain));
|
||||
args.push(wrapLinkToMessage(invoiceMessage, plain).then((el) => {
|
||||
el.classList.add('is-receipt-link');
|
||||
return el;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,15 @@ import replaceNonNumber from "../string/replaceNonNumber";
|
||||
|
||||
const CARD_BRAND_REGEXP: {[brand: string]: RegExp} = {
|
||||
visa: /^4/,
|
||||
mastercard: /^(51|52|53|54|55|22|23|24|25|26|27)/,
|
||||
mastercard: /^(51|52|53|54|55|222|23|24|25|26|27)/,
|
||||
amex: /^(34|37)/,
|
||||
discover: /^(60|64|65)/,
|
||||
diners: /^(30|38|39)/,
|
||||
diners14: /^(36)/,
|
||||
jcb: /^(35)/,
|
||||
unionpay: /^(62[0-6,8-9]|627[0-6,8-9]|6277[0-7,9]|62778[1-9]|81)/,
|
||||
elo: /^(5067|509|636368|627780)/
|
||||
elo: /^(5067|509|636368|627780)/,
|
||||
mir: /^(220[0-4])/
|
||||
};
|
||||
|
||||
// * taken from Stripe
|
||||
@ -74,6 +75,12 @@ export const CARD_BRANDS: {[b: string]: {
|
||||
cvcMaxLength: 3,
|
||||
cvcMinLength: null
|
||||
},
|
||||
mir: {
|
||||
minLength: 16,
|
||||
maxLength: 16,
|
||||
cvcMaxLength: 3,
|
||||
cvcMinLength: null
|
||||
},
|
||||
unknown: {
|
||||
minLength: 16,
|
||||
maxLength: 16,
|
||||
|
@ -16,6 +16,7 @@ function makeValidationError(code?: string) {
|
||||
} : null;
|
||||
}
|
||||
|
||||
// Luhn algorithm
|
||||
function validateCompleteCardNumber(card: string) {
|
||||
const t = '0'.charCodeAt(0);
|
||||
const n = card.length % 2;
|
||||
@ -61,7 +62,11 @@ function getCardInfoByNumber(card: string) {
|
||||
}
|
||||
|
||||
function makeCardNumberError(str: string, length: number, ignoreIncomplete: boolean) {
|
||||
return str.length >= length ? (validateCompleteCardNumber(str) ? null : makeValidationError('invalid')) : (ignoreIncomplete ? null : makeValidationError('incomplete'));
|
||||
if(str.length >= length) {
|
||||
return validateCompleteCardNumber(str) || detectCardBrand(str) === 'mir' ? null : makeValidationError('invalid');
|
||||
}
|
||||
|
||||
return ignoreIncomplete ? null : makeValidationError('incomplete');
|
||||
}
|
||||
|
||||
export function validateCardNumber(str: string, options: PatternValidationOptions = {}) {
|
||||
|
@ -25,49 +25,58 @@ function number_format(number: any, decimals: any, dec_point: any, thousands_sep
|
||||
return s.join(dec);
|
||||
}
|
||||
|
||||
export default function paymentsWrapCurrencyAmount($amount: number | string, $currency: string, $skipSymbol?: boolean) {
|
||||
$amount = +$amount;
|
||||
export default function paymentsWrapCurrencyAmount(amount: number | string, currency: string, skipSymbol?: boolean) {
|
||||
amount = +amount;
|
||||
|
||||
const $currency_data = Currencies[$currency]; // вытащить из json
|
||||
if(!$currency_data) {
|
||||
const isNegative = amount < 0;
|
||||
|
||||
const currencyData = Currencies[currency];
|
||||
if(!currencyData) {
|
||||
throw new Error('CURRENCY_WRAP_INVALID');
|
||||
}
|
||||
|
||||
const $amount_exp = $amount / Math.pow(10, $currency_data['exp']);
|
||||
const amountExp = amount / Math.pow(10, currencyData.exp);
|
||||
|
||||
let $decimals = $currency_data['exp'];
|
||||
if($currency == 'IRR' &&
|
||||
Math.floor($amount_exp) == $amount_exp) {
|
||||
$decimals = 0; // у иранцев копейки почти всегда = 0 и не показываются в UI
|
||||
let decimals = currencyData.exp;
|
||||
if(currency == 'IRR' && Math.floor(amountExp) == amountExp) {
|
||||
decimals = 0; // у иранцев копейки почти всегда = 0 и не показываются в UI
|
||||
}
|
||||
|
||||
const $formatted = number_format($amount_exp, $decimals, $currency_data['decimal_sep'], $currency_data['thousands_sep']);
|
||||
if($skipSymbol) {
|
||||
return $formatted;
|
||||
let formatted = number_format(amountExp, decimals, currencyData.decimal_sep, currencyData.thousands_sep);
|
||||
if(skipSymbol) {
|
||||
return formatted;
|
||||
}
|
||||
|
||||
const $splitter = $currency_data['space_between'] ? " " : '';
|
||||
let $formatted_intern: string;
|
||||
if($currency_data['symbol_left']) {
|
||||
$formatted_intern = $currency_data['symbol'] + $splitter + $formatted;
|
||||
let symbol = currencyData.symbol;
|
||||
if(isNegative && !currencyData.space_between && currencyData.symbol_left) {
|
||||
symbol = '-' + symbol;
|
||||
formatted = formatted.replace('-', '');
|
||||
}
|
||||
|
||||
let out: string;
|
||||
const splitter = currencyData.space_between ? " " : '';
|
||||
if(currencyData.symbol_left) {
|
||||
out = symbol + splitter + formatted;
|
||||
} else {
|
||||
$formatted_intern = $formatted + $splitter + $currency_data['symbol'];
|
||||
out = formatted + splitter + symbol;
|
||||
}
|
||||
return $formatted_intern;
|
||||
return out;
|
||||
}
|
||||
|
||||
function paymentsGetCurrencyExp($currency: string) {
|
||||
if($currency == 'CLF') {
|
||||
return 4;
|
||||
}
|
||||
if(['BHD','IQD','JOD','KWD','LYD','OMR','TND'].includes($currency)) {
|
||||
return 3;
|
||||
}
|
||||
if(['BIF','BYR','CLP','CVE','DJF','GNF','ISK','JPY','KMF','KRW','MGA', 'PYG','RWF','UGX','UYI','VND','VUV','XAF','XOF','XPF'].includes($currency)) {
|
||||
return 0;
|
||||
}
|
||||
if($currency == 'MRO') {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
(window as any).p = paymentsWrapCurrencyAmount;
|
||||
|
||||
// function paymentsGetCurrencyExp($currency: string) {
|
||||
// if($currency == 'CLF') {
|
||||
// return 4;
|
||||
// }
|
||||
// if(['BHD','IQD','JOD','KWD','LYD','OMR','TND'].includes($currency)) {
|
||||
// return 3;
|
||||
// }
|
||||
// if(['BIF','BYR','CLP','CVE','DJF','GNF','ISK','JPY','KMF','KRW','MGA', 'PYG','RWF','UGX','UYI','VND','VUV','XAF','XOF','XPF'].includes($currency)) {
|
||||
// return 0;
|
||||
// }
|
||||
// if($currency == 'MRO') {
|
||||
// return 1;
|
||||
// }
|
||||
// return 2;
|
||||
// }
|
||||
|
@ -2788,6 +2788,7 @@ $bubble-beside-button-width: 38px;
|
||||
overflow: hidden;
|
||||
min-height: 2.5rem;
|
||||
display: flex;
|
||||
border-radius: .375rem;
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: $border-radius-big;
|
||||
@ -2797,7 +2798,7 @@ $bubble-beside-button-width: 38px;
|
||||
|
||||
&-button {
|
||||
padding: .5625rem 0;
|
||||
border-radius: 6px;
|
||||
border-radius: inherit;
|
||||
z-index: 2;
|
||||
font-size: .875rem;
|
||||
user-select: none;
|
||||
|
@ -82,7 +82,8 @@
|
||||
|
||||
.payment-verification {
|
||||
width: 100%;
|
||||
min-height: 30rem;
|
||||
height: 40rem;
|
||||
max-height: 100%;
|
||||
border: none;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
@ -1268,6 +1268,16 @@ middle-ellipsis-element {
|
||||
// }
|
||||
// }
|
||||
|
||||
.media-container-contain {
|
||||
position: relative;
|
||||
|
||||
.media-photo {
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.media-container-cover {
|
||||
position: relative;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user