mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Add better error reporting to payments.
This commit is contained in:
parent
e106bd143e
commit
ee098d00ad
12 changed files with 161 additions and 52 deletions
|
@ -1855,6 +1855,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_theme_editor_menu_show" = "Show palette file";
|
||||
|
||||
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
|
||||
"lng_payments_webview_no_card" = "Unfortunately, you can't add a new card with current system configuration.";
|
||||
"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration.";
|
||||
"lng_payments_webview_install_edge" = "Please install {link}.";
|
||||
"lng_payments_webview_install_webkit" = "Please install WebKitGTK 4 (webkit2gtk-4.0) using your package manager.";
|
||||
"lng_payments_webview_switch_mutter" = "Qt's window embedding doesn't work well with Mutter window manager. Please switch to another window manager or desktop environment.";
|
||||
"lng_payments_webview_switch_wayland" = "There is no way to embed WebView window on Wayland. Please switch to X11.";
|
||||
"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
"lng_payments_receipt_label_test" = "Test receipt";
|
||||
|
@ -1903,6 +1909,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_payments_tips_box_title" = "Add Tip";
|
||||
"lng_payments_tips_max" = "Max possible tip amount: {amount}";
|
||||
|
||||
"lng_payments_shipping_not_available" = "Shipping to the selected country is not available.";
|
||||
"lng_payments_card_declined" = "Your card was declined.";
|
||||
"lng_payments_payment_failed" = "Payment failed. Your card has not been billed.";
|
||||
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
|
||||
"lng_payments_already_paid" = "You have already paid for this item.";
|
||||
|
||||
"lng_call_status_incoming" = "is calling you...";
|
||||
"lng_call_status_connecting" = "connecting...";
|
||||
"lng_call_status_exchanging" = "exchanging encryption keys...";
|
||||
|
|
|
@ -91,7 +91,6 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
|
|||
*data.vphoto(),
|
||||
ImageLocation())
|
||||
: nullptr),
|
||||
.isMultipleAllowed = item->history()->isChannel(), // #TODO payments
|
||||
.isTest = data.is_test(),
|
||||
};
|
||||
}
|
||||
|
@ -189,10 +188,6 @@ PollData *Media::poll() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void Media::setInvoiceReceiptId(MsgId id) {
|
||||
Unexpected("Media::setInvoiceReceiptId.");
|
||||
}
|
||||
|
||||
bool Media::uploading() const {
|
||||
return false;
|
||||
}
|
||||
|
@ -1195,11 +1190,6 @@ const Invoice *MediaInvoice::invoice() const {
|
|||
return &_invoice;
|
||||
}
|
||||
|
||||
void MediaInvoice::setInvoiceReceiptId(MsgId id) {
|
||||
_invoice.receiptMsgId = id;
|
||||
parent()->checkBuyButton();
|
||||
}
|
||||
|
||||
bool MediaInvoice::hasReplyPreview() const {
|
||||
if (const auto photo = _invoice.photo) {
|
||||
return !photo->isNull();
|
||||
|
|
|
@ -62,7 +62,6 @@ struct Invoice {
|
|||
QString title;
|
||||
QString description;
|
||||
PhotoData *photo = nullptr;
|
||||
bool isMultipleAllowed = false;
|
||||
bool isTest = false;
|
||||
};
|
||||
|
||||
|
@ -85,8 +84,6 @@ public:
|
|||
virtual Data::CloudImage *location() const;
|
||||
virtual PollData *poll() const;
|
||||
|
||||
virtual void setInvoiceReceiptId(MsgId id);
|
||||
|
||||
virtual bool uploading() const;
|
||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||
virtual bool canBeGrouped() const;
|
||||
|
@ -384,7 +381,6 @@ public:
|
|||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
const Invoice *invoice() const override;
|
||||
void setInvoiceReceiptId(MsgId id) override;
|
||||
|
||||
bool hasReplyPreview() const override;
|
||||
Image *replyPreview() const override;
|
||||
|
|
|
@ -776,16 +776,9 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
|||
if (payment->msg) {
|
||||
if (const auto media = payment->msg->media()) {
|
||||
if (const auto invoice = media->invoice()) {
|
||||
if (!invoice->isMultipleAllowed
|
||||
&& !invoice->receiptMsgId) {
|
||||
media->setInvoiceReceiptId(id);
|
||||
}
|
||||
return textcmdLink(1, invoice->title);
|
||||
}
|
||||
}
|
||||
return QString();// tr::lng_deleted_message(tr::now);
|
||||
} else if (payment->msgId) {
|
||||
return QString();// tr::lng_contacts_loading(tr::now);
|
||||
}
|
||||
return QString();
|
||||
}();
|
||||
|
|
|
@ -199,10 +199,14 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
const auto &id = error.id;
|
||||
switch (error.type) {
|
||||
case Error::Type::Form:
|
||||
if (true
|
||||
if (id == u"INVOICE_ALREADY_PAID"_q) {
|
||||
_panel->showCriticalError({
|
||||
tr::lng_payments_already_paid(tr::now)
|
||||
});
|
||||
} else if (true
|
||||
|| id == u"PROVIDER_ACCOUNT_INVALID"_q
|
||||
|| id == u"PROVIDER_ACCOUNT_TIMEOUT"_q) {
|
||||
showToast({ "Error: " + id });
|
||||
_panel->showCriticalError({ "Error: " + id });
|
||||
}
|
||||
break;
|
||||
case Error::Type::Validate: {
|
||||
|
@ -246,9 +250,9 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
} else if (id == u"LOCAL_CARD_BILLING_ZIP_INVALID"_q) {
|
||||
showCardError(CardField::AddressZip);
|
||||
} else if (id == u"SHIPPING_BOT_TIMEOUT"_q) {
|
||||
showToast({ "Error: Bot Timeout!" }); // #TODO payments errors message
|
||||
showToast({ "Error: Bot Timeout!" });
|
||||
} else if (id == u"SHIPPING_NOT_AVAILABLE"_q) {
|
||||
showToast({ "Error: Shipping to the selected country is not available!" }); // #TODO payments errors message
|
||||
showToast({ tr::lng_payments_shipping_not_available(tr::now) });
|
||||
} else {
|
||||
showToast({ "Error: " + id });
|
||||
}
|
||||
|
@ -264,11 +268,9 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
|| id == u"ExpiredCard"_q) {
|
||||
showCardError(Field::ExpireDate);
|
||||
} else if (id == u"CardDeclined"_q) {
|
||||
// #TODO payments errors message
|
||||
showToast({ "Error: " + id });
|
||||
showToast({ tr::lng_payments_card_declined(tr::now) });
|
||||
} else if (id == u"ProcessingError"_q) {
|
||||
// #TODO payments errors message
|
||||
showToast({ "Error: " + id });
|
||||
showToast({ "Sorry, a processing error occurred." });
|
||||
} else {
|
||||
showToast({ "Error: " + id });
|
||||
}
|
||||
|
@ -287,17 +289,20 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
if (_submitState == SubmitState::Finishing) {
|
||||
_submitState = SubmitState::Validated;
|
||||
}
|
||||
if (id == u"PAYMENT_FAILED"_q) {
|
||||
showToast({ "Error: Payment Failed. Your card has not been billed." }); // #TODO payments errors message
|
||||
if (id == u"INVOICE_ALREADY_PAID"_q) {
|
||||
showToast({ tr::lng_payments_already_paid(tr::now) });
|
||||
} else if (id == u"PAYMENT_FAILED"_q) {
|
||||
showToast({ tr::lng_payments_payment_failed(tr::now) });
|
||||
} else if (id == u"BOT_PRECHECKOUT_FAILED"_q) {
|
||||
showToast({ "Error: PreCheckout Failed. Your card has not been billed." }); // #TODO payments errors message
|
||||
showToast({ tr::lng_payments_precheckout_failed(tr::now) });
|
||||
} else if (id == u"REQUESTED_INFO_INVALID"_q
|
||||
|| id == u"SHIPPING_OPTION_INVALID"_q
|
||||
|| id == u"PAYMENT_CREDENTIALS_INVALID"_q
|
||||
|| id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
|
||||
showToast({ tr::lng_payments_payment_failed(tr::now) });
|
||||
showToast({ "Error: " + id + ". Your card has not been billed." });
|
||||
} else if (id == u"TMP_PASSWORD_INVALID"_q) {
|
||||
// #TODO payments save
|
||||
requestPassword();
|
||||
} else {
|
||||
showToast({ "Error: " + id });
|
||||
}
|
||||
|
@ -574,6 +579,10 @@ void CheckoutProcess::panelSetPassword() {
|
|||
});
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelOpenUrl(const QString &url) {
|
||||
File::OpenUrl(url);
|
||||
}
|
||||
|
||||
void CheckoutProcess::getPasswordState(
|
||||
Fn<void(const Core::CloudPasswordState&)> callback) {
|
||||
Expects(callback != nullptr);
|
||||
|
|
|
@ -102,6 +102,7 @@ private:
|
|||
bool saveInformation) override;
|
||||
bool panelWebviewNavigationAttempt(const QString &uri) override;
|
||||
void panelSetPassword() override;
|
||||
void panelOpenUrl(const QString &url) override;
|
||||
|
||||
void panelCancelEdit() override;
|
||||
void panelEditPaymentMethod() override;
|
||||
|
|
|
@ -9,6 +9,8 @@ using "ui/basic.style";
|
|||
|
||||
using "info/info.style";
|
||||
|
||||
paymentsPanelSize: size(392px, 600px);
|
||||
|
||||
paymentsPanelButton: defaultBoxButton;
|
||||
paymentsPanelSubmit: RoundButton(defaultActiveButton) {
|
||||
width: -36px;
|
||||
|
@ -116,3 +118,10 @@ paymentTipsErrorPadding: margins(22px, 6px, 22px, 0px);
|
|||
|
||||
paymentsToProviderLabel: paymentsShippingPrice;
|
||||
paymentsToProviderPadding: margins(28px, 6px, 28px, 6px);
|
||||
|
||||
paymentsCriticalError: FlatLabel(boxLabel) {
|
||||
minWidth: 370px;
|
||||
align: align(top);
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
paymentsCriticalErrorPadding: margins(10px, 40px, 10px, 0px);
|
||||
|
|
|
@ -76,6 +76,7 @@ FormSummary::FormSummary(
|
|||
, _options(options)
|
||||
, _information(current)
|
||||
, _scroll(this, st::passportPanelScroll)
|
||||
, _layout(_scroll->setOwnedWidget(object_ptr<VerticalLayout>(this)))
|
||||
, _topShadow(this)
|
||||
, _bottomShadow(this)
|
||||
, _submit(_invoice.receipt.paid
|
||||
|
@ -114,6 +115,20 @@ rpl::producer<int> FormSummary::scrollTopValue() const {
|
|||
return _scroll->scrollTopValue();
|
||||
}
|
||||
|
||||
bool FormSummary::showCriticalError(const TextWithEntities &text) {
|
||||
if (_invoice
|
||||
|| (_scroll->height() - _layout->height()
|
||||
< st::paymentsPanelSize.height() / 2)) {
|
||||
return false;
|
||||
}
|
||||
Settings::AddSkip(_layout.get(), st::paymentsPricesTopSkip);
|
||||
_layout->add(object_ptr<FlatLabel>(
|
||||
_layout.get(),
|
||||
rpl::single(text),
|
||||
st::paymentsCriticalError));
|
||||
return true;
|
||||
}
|
||||
|
||||
void FormSummary::updateThumbnail(const QImage &thumbnail) {
|
||||
_invoice.cover.thumbnail = thumbnail;
|
||||
_thumbnails.fire_copy(thumbnail);
|
||||
|
@ -149,7 +164,7 @@ int64 FormSummary::computeTotalAmount() const {
|
|||
}
|
||||
|
||||
void FormSummary::setupControls() {
|
||||
const auto inner = setupContent();
|
||||
setupContent(_layout.get());
|
||||
|
||||
if (_submit) {
|
||||
_submit->addClickHandler([=] {
|
||||
|
@ -173,7 +188,7 @@ void FormSummary::setupControls() {
|
|||
_bottomShadow->toggleOn(rpl::combine(
|
||||
_scroll->scrollTopValue(),
|
||||
_scroll->heightValue(),
|
||||
inner->heightValue(),
|
||||
_layout->heightValue(),
|
||||
_1 + _2 < _3));
|
||||
}
|
||||
|
||||
|
@ -533,24 +548,19 @@ void FormSummary::setupSections(not_null<VerticalLayout*> layout) {
|
|||
Settings::AddSkip(layout, st::paymentsSectionsTopSkip);
|
||||
}
|
||||
|
||||
not_null<RpWidget*> FormSummary::setupContent() {
|
||||
const auto inner = _scroll->setOwnedWidget(
|
||||
object_ptr<VerticalLayout>(this));
|
||||
|
||||
void FormSummary::setupContent(not_null<VerticalLayout*> layout) {
|
||||
_scroll->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
inner->resizeToWidth(width);
|
||||
}, inner->lifetime());
|
||||
layout->resizeToWidth(width);
|
||||
}, layout->lifetime());
|
||||
|
||||
setupCover(inner);
|
||||
setupCover(layout);
|
||||
if (_invoice) {
|
||||
Settings::AddDivider(inner);
|
||||
setupPrices(inner);
|
||||
Settings::AddDivider(inner);
|
||||
setupSections(inner);
|
||||
Settings::AddDivider(layout);
|
||||
setupPrices(layout);
|
||||
Settings::AddDivider(layout);
|
||||
setupSections(layout);
|
||||
}
|
||||
|
||||
return inner;
|
||||
}
|
||||
|
||||
void FormSummary::resizeEvent(QResizeEvent *e) {
|
||||
|
|
|
@ -39,11 +39,13 @@ public:
|
|||
void updateThumbnail(const QImage &thumbnail);
|
||||
[[nodiscard]] rpl::producer<int> scrollTopValue() const;
|
||||
|
||||
bool showCriticalError(const TextWithEntities &text);
|
||||
|
||||
private:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
void setupControls();
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||
void setupContent(not_null<VerticalLayout*> layout);
|
||||
void setupCover(not_null<VerticalLayout*> layout);
|
||||
void setupPrices(not_null<VerticalLayout*> layout);
|
||||
void setupSuggestedTips(not_null<VerticalLayout*> layout);
|
||||
|
@ -61,6 +63,7 @@ private:
|
|||
ShippingOptions _options;
|
||||
RequestedInformation _information;
|
||||
object_ptr<ScrollArea> _scroll;
|
||||
not_null<VerticalLayout*> _layout;
|
||||
object_ptr<FadeShadow> _topShadow;
|
||||
object_ptr<FadeShadow> _bottomShadow;
|
||||
object_ptr<RoundButton> _submit;
|
||||
|
|
|
@ -17,10 +17,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "webview/webview_embed.h"
|
||||
#include "webview/webview_interface.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_passport.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
@ -28,7 +29,7 @@ namespace Payments::Ui {
|
|||
Panel::Panel(not_null<PanelDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _widget(std::make_unique<SeparatePanel>()) {
|
||||
_widget->setInnerSize(st::passportPanelSize);
|
||||
_widget->setInnerSize(st::paymentsPanelSize);
|
||||
_widget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
|
||||
|
||||
_widget->closeRequests(
|
||||
|
@ -56,6 +57,16 @@ void Panel::showForm(
|
|||
const RequestedInformation ¤t,
|
||||
const PaymentMethodDetails &method,
|
||||
const ShippingOptions &options) {
|
||||
if (invoice && !method.ready && !method.native.supported) {
|
||||
const auto available = Webview::Availability();
|
||||
if (available.error != Webview::Available::Error::None) {
|
||||
showWebviewError(
|
||||
tr::lng_payments_webview_no_use(tr::now),
|
||||
available);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_testMode = invoice.isTest;
|
||||
setTitle(invoice.receipt
|
||||
? tr::lng_payments_receipt_title()
|
||||
|
@ -250,7 +261,15 @@ void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
|||
if (method.native.supported) {
|
||||
showEditCard(method.native, CardField::Number);
|
||||
} else if (!showWebview(method.url, true, std::move(bottomText))) {
|
||||
// #TODO payments errors not supported
|
||||
const auto available = Webview::Availability();
|
||||
if (available.error != Webview::Available::Error::None) {
|
||||
showWebviewError(
|
||||
tr::lng_payments_webview_no_card(tr::now),
|
||||
available);
|
||||
} else {
|
||||
showCriticalError({ "Error: Could not initialize WebView." });
|
||||
}
|
||||
_widget->setBackAllowed(true);
|
||||
} else if (method.canSaveInformation) {
|
||||
const auto &padding = st::paymentsPanelPadding;
|
||||
_saveWebviewInformation = CreateChild<Checkbox>(
|
||||
|
@ -487,6 +506,67 @@ void Panel::showToast(const TextWithEntities &text) {
|
|||
_widget->showToast(text);
|
||||
}
|
||||
|
||||
void Panel::showCriticalError(const TextWithEntities &text) {
|
||||
if (!_weakFormSummary || !_weakFormSummary->showCriticalError(text)) {
|
||||
auto error = base::make_unique_q<PaddingWrap<FlatLabel>>(
|
||||
_widget.get(),
|
||||
object_ptr<FlatLabel>(
|
||||
_widget.get(),
|
||||
rpl::single(text),
|
||||
st::paymentsCriticalError),
|
||||
st::paymentsCriticalErrorPadding);
|
||||
error->entity()->setClickHandlerFilter([=](
|
||||
const ClickHandlerPtr &handler,
|
||||
Qt::MouseButton) {
|
||||
const auto entity = handler->getTextEntity();
|
||||
if (entity.type != EntityType::CustomUrl) {
|
||||
return true;
|
||||
}
|
||||
_delegate->panelOpenUrl(entity.data);
|
||||
return false;
|
||||
});
|
||||
_widget->showInner(std::move(error));
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::showWebviewError(
|
||||
const QString &text,
|
||||
const Webview::Available &information) {
|
||||
using Error = Webview::Available::Error;
|
||||
Expects(information.error != Error::None);
|
||||
|
||||
auto rich = TextWithEntities{ text };
|
||||
rich.append("\n\n");
|
||||
switch (information.error) {
|
||||
case Error::NoWebview2: {
|
||||
const auto command = QString(QChar(TextCommand));
|
||||
const auto text = tr::lng_payments_webview_install_edge(
|
||||
tr::now,
|
||||
lt_link,
|
||||
command);
|
||||
const auto parts = text.split(command);
|
||||
rich.append(parts.value(0))
|
||||
.append(Text::Link(
|
||||
"Microsoft Edge WebView2 Runtime",
|
||||
"https://go.microsoft.com/fwlink/p/?LinkId=2124703"))
|
||||
.append(parts.value(1));
|
||||
} break;
|
||||
case Error::NoGtkOrWebkit2Gtk:
|
||||
rich.append(tr::lng_payments_webview_install_webkit(tr::now));
|
||||
break;
|
||||
case Error::MutterWM:
|
||||
rich.append(tr::lng_payments_webview_switch_mutter(tr::now));
|
||||
break;
|
||||
case Error::Wayland:
|
||||
rich.append(tr::lng_payments_webview_switch_wayland(tr::now));
|
||||
break;
|
||||
default:
|
||||
rich.append(QString::fromStdString(information.details));
|
||||
break;
|
||||
}
|
||||
showCriticalError(rich);
|
||||
}
|
||||
|
||||
rpl::lifetime &Panel::lifetime() {
|
||||
return _widget->lifetime();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ class Checkbox;
|
|||
|
||||
namespace Webview {
|
||||
class Window;
|
||||
struct Available;
|
||||
} // namespace Webview
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
@ -80,11 +81,15 @@ public:
|
|||
|
||||
void showBox(object_ptr<Ui::BoxContent> box);
|
||||
void showToast(const TextWithEntities &text);
|
||||
void showCriticalError(const TextWithEntities &text);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
bool createWebview();
|
||||
void showWebviewError(
|
||||
const QString &text,
|
||||
const Webview::Available &information);
|
||||
void setTitle(rpl::producer<QString> title);
|
||||
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
bool saveInformation) = 0;
|
||||
virtual bool panelWebviewNavigationAttempt(const QString &uri) = 0;
|
||||
virtual void panelSetPassword() = 0;
|
||||
virtual void panelOpenUrl(const QString &url) = 0;
|
||||
|
||||
virtual void panelCancelEdit() = 0;
|
||||
virtual void panelEditPaymentMethod() = 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue