mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support web_app_open_tg_link / web_app_open_invoice.
This commit is contained in:
parent
26c99cea7c
commit
87662de2a6
11 changed files with 230 additions and 62 deletions
|
@ -733,7 +733,7 @@ bool ResolveInvoice(
|
||||||
Payments::CheckoutProcess::Start(
|
Payments::CheckoutProcess::Start(
|
||||||
&controller->session(),
|
&controller->session(),
|
||||||
slug,
|
slug,
|
||||||
crl::guard(window, [=] { window->activate(); }));
|
crl::guard(window, [=](auto) { window->activate(); }));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ void activateBotCommand(
|
||||||
Payments::CheckoutProcess::Start(
|
Payments::CheckoutProcess::Start(
|
||||||
msg,
|
msg,
|
||||||
Payments::Mode::Payment,
|
Payments::Mode::Payment,
|
||||||
crl::guard(App::wnd(), [] { App::wnd()->activate(); }));
|
crl::guard(App::wnd(), [](auto) { App::wnd()->activate(); }));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ButtonType::Url: {
|
case ButtonType::Url: {
|
||||||
|
|
|
@ -1032,24 +1032,31 @@ void History::applyServiceChanges(
|
||||||
}
|
}
|
||||||
}, [&](const MTPDmessageActionPaymentSent &data) {
|
}, [&](const MTPDmessageActionPaymentSent &data) {
|
||||||
if (const auto payment = item->Get<HistoryServicePayment>()) {
|
if (const auto payment = item->Get<HistoryServicePayment>()) {
|
||||||
|
auto paid = std::optional<Payments::PaidInvoice>();
|
||||||
if (const auto message = payment->msg) {
|
if (const auto message = payment->msg) {
|
||||||
if (const auto media = message->media()) {
|
if (const auto media = message->media()) {
|
||||||
if (const auto invoice = media->invoice()) {
|
if (const auto invoice = media->invoice()) {
|
||||||
using Payments::CheckoutProcess;
|
paid = Payments::CheckoutProcess::InvoicePaid(
|
||||||
if (CheckoutProcess::TakePaymentStarted(message)) {
|
message);
|
||||||
// Toast on a current active window.
|
|
||||||
Ui::ShowMultilineToast({
|
|
||||||
.text = tr::lng_payments_success(
|
|
||||||
tr::now,
|
|
||||||
lt_amount,
|
|
||||||
Ui::Text::Bold(payment->amount),
|
|
||||||
lt_title,
|
|
||||||
Ui::Text::Bold(invoice->title),
|
|
||||||
Ui::Text::WithEntities),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (!payment->slug.isEmpty()) {
|
||||||
|
using Payments::CheckoutProcess;
|
||||||
|
paid = Payments::CheckoutProcess::InvoicePaid(
|
||||||
|
&session(),
|
||||||
|
payment->slug);
|
||||||
|
}
|
||||||
|
if (paid) {
|
||||||
|
// Toast on a current active window.
|
||||||
|
Ui::ShowMultilineToast({
|
||||||
|
.text = tr::lng_payments_success(
|
||||||
|
tr::now,
|
||||||
|
lt_amount,
|
||||||
|
Ui::Text::Bold(payment->amount),
|
||||||
|
lt_title,
|
||||||
|
Ui::Text::Bold(paid->title),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [&](const MTPDmessageActionSetChatTheme &data) {
|
}, [&](const MTPDmessageActionSetChatTheme &data) {
|
||||||
|
|
|
@ -1372,7 +1372,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
CheckoutProcess::Start(
|
CheckoutProcess::Start(
|
||||||
item,
|
item,
|
||||||
Mode::Receipt,
|
Mode::Receipt,
|
||||||
crl::guard(weak, [=] { weak->window().activate(); }));
|
crl::guard(weak, [=](auto) { weak->window().activate(); }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (type == mtpc_messageActionGroupCall
|
} else if (type == mtpc_messageActionGroupCall
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct HistoryServiceGameScore
|
||||||
struct HistoryServicePayment
|
struct HistoryServicePayment
|
||||||
: public RuntimeComponent<HistoryServicePayment, HistoryItem>
|
: public RuntimeComponent<HistoryServicePayment, HistoryItem>
|
||||||
, public HistoryServiceDependentData {
|
, public HistoryServiceDependentData {
|
||||||
|
QString slug;
|
||||||
QString amount;
|
QString amount;
|
||||||
ClickHandlerPtr invoiceLink;
|
ClickHandlerPtr invoiceLink;
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/basic_click_handlers.h"
|
#include "ui/basic_click_handlers.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "payments/payments_checkout_process.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
@ -711,6 +712,28 @@ void AttachWebView::show(
|
||||||
close();
|
close();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
const auto panel = std::make_shared<
|
||||||
|
base::weak_ptr<Ui::BotWebView::Panel>>(nullptr);
|
||||||
|
const auto handleInvoice = [=, session = _session](QString slug) {
|
||||||
|
using Result = Payments::CheckoutResult;
|
||||||
|
const auto reactivate = [=](Result result) {
|
||||||
|
if (const auto strong = panel->get()) {
|
||||||
|
strong->invoiceClosed(slug, [&] {
|
||||||
|
switch (result) {
|
||||||
|
case Result::Paid: return "paid";
|
||||||
|
case Result::Failed: return "failed";
|
||||||
|
case Result::Pending: return "pending";
|
||||||
|
case Result::Cancelled: return "cancelled";
|
||||||
|
}
|
||||||
|
Unexpected("Payments::CheckoutResult value.");
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (const auto strong = panel->get()) {
|
||||||
|
strong->hideForPayment();
|
||||||
|
}
|
||||||
|
Payments::CheckoutProcess::Start(session, slug, reactivate);
|
||||||
|
};
|
||||||
auto title = Info::Profile::NameValue(
|
auto title = Info::Profile::NameValue(
|
||||||
_bot
|
_bot
|
||||||
) | rpl::map([](const TextWithEntities &value) {
|
) | rpl::map([](const TextWithEntities &value) {
|
||||||
|
@ -723,10 +746,12 @@ void AttachWebView::show(
|
||||||
.title = std::move(title),
|
.title = std::move(title),
|
||||||
.bottom = rpl::single('@' + _bot->username),
|
.bottom = rpl::single('@' + _bot->username),
|
||||||
.handleLocalUri = handleLocalUri,
|
.handleLocalUri = handleLocalUri,
|
||||||
|
.handleInvoice = handleInvoice,
|
||||||
.sendData = sendData,
|
.sendData = sendData,
|
||||||
.close = close,
|
.close = close,
|
||||||
.themeParams = [] { return Window::Theme::WebViewParams(); },
|
.themeParams = [] { return Window::Theme::WebViewParams(); },
|
||||||
});
|
});
|
||||||
|
*panel = _panel.get();
|
||||||
started(queryId);
|
started(queryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@ namespace {
|
||||||
struct SessionProcesses {
|
struct SessionProcesses {
|
||||||
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem;
|
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem;
|
||||||
base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug;
|
base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug;
|
||||||
base::flat_set<FullMsgId> paymentStartedByItem;
|
base::flat_map<FullMsgId, PaidInvoice> paymentStartedByItem;
|
||||||
base::flat_set<QString> paymentStartedBySlug;
|
base::flat_map<QString, PaidInvoice> paymentStartedBySlug;
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ base::flat_map<not_null<Main::Session*>, SessionProcesses> Processes;
|
||||||
void CheckoutProcess::Start(
|
void CheckoutProcess::Start(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
Mode mode,
|
Mode mode,
|
||||||
Fn<void()> reactivate) {
|
Fn<void(CheckoutResult)> reactivate) {
|
||||||
auto &processes = LookupSessionProcesses(&item->history()->session());
|
auto &processes = LookupSessionProcesses(&item->history()->session());
|
||||||
const auto media = item->media();
|
const auto media = item->media();
|
||||||
const auto invoice = media ? media->invoice() : nullptr;
|
const auto invoice = media ? media->invoice() : nullptr;
|
||||||
|
@ -99,7 +99,7 @@ void CheckoutProcess::Start(
|
||||||
void CheckoutProcess::Start(
|
void CheckoutProcess::Start(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
const QString &slug,
|
const QString &slug,
|
||||||
Fn<void()> reactivate) {
|
Fn<void(CheckoutResult)> reactivate) {
|
||||||
auto &processes = LookupSessionProcesses(session);
|
auto &processes = LookupSessionProcesses(session);
|
||||||
const auto i = processes.bySlug.find(slug);
|
const auto i = processes.bySlug.find(slug);
|
||||||
if (i != end(processes.bySlug)) {
|
if (i != end(processes.bySlug)) {
|
||||||
|
@ -117,24 +117,57 @@ void CheckoutProcess::Start(
|
||||||
j->second->requestActivate();
|
j->second->requestActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckoutProcess::TakePaymentStarted(
|
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
||||||
not_null<const HistoryItem*> item) {
|
not_null<const HistoryItem*> item) {
|
||||||
const auto session = &item->history()->session();
|
const auto session = &item->history()->session();
|
||||||
const auto itemId = item->fullId();
|
const auto itemId = item->fullId();
|
||||||
const auto i = Processes.find(session);
|
const auto i = Processes.find(session);
|
||||||
if (i == end(Processes)
|
if (i == end(Processes)) {
|
||||||
|| !i->second.paymentStartedByItem.contains(itemId)) {
|
return std::nullopt;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
i->second.paymentStartedByItem.erase(itemId);
|
const auto k = i->second.paymentStartedByItem.find(itemId);
|
||||||
|
if (k == end(i->second.paymentStartedByItem)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto result = k->second;
|
||||||
|
i->second.paymentStartedByItem.erase(k);
|
||||||
|
|
||||||
const auto j = i->second.byItem.find(itemId);
|
const auto j = i->second.byItem.find(itemId);
|
||||||
if (j != end(i->second.byItem)) {
|
if (j != end(i->second.byItem)) {
|
||||||
j->second->closeAndReactivate();
|
j->second->closeAndReactivate(CheckoutResult::Paid);
|
||||||
} else if (i->second.paymentStartedByItem.empty()
|
} else if (i->second.paymentStartedByItem.empty()
|
||||||
&& i->second.byItem.empty()) {
|
&& i->second.byItem.empty()
|
||||||
|
&& i->second.paymentStartedBySlug.empty()
|
||||||
|
&& i->second.bySlug.empty()) {
|
||||||
Processes.erase(i);
|
Processes.erase(i);
|
||||||
}
|
}
|
||||||
return true;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const QString &slug) {
|
||||||
|
const auto i = Processes.find(session);
|
||||||
|
if (i == end(Processes)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto k = i->second.paymentStartedBySlug.find(slug);
|
||||||
|
if (k == end(i->second.paymentStartedBySlug)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto result = k->second;
|
||||||
|
i->second.paymentStartedBySlug.erase(k);
|
||||||
|
|
||||||
|
const auto j = i->second.bySlug.find(slug);
|
||||||
|
if (j != end(i->second.bySlug)) {
|
||||||
|
j->second->closeAndReactivate(CheckoutResult::Paid);
|
||||||
|
} else if (i->second.paymentStartedByItem.empty()
|
||||||
|
&& i->second.byItem.empty()
|
||||||
|
&& i->second.paymentStartedBySlug.empty()
|
||||||
|
&& i->second.bySlug.empty()) {
|
||||||
|
Processes.erase(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::ClearAll() {
|
void CheckoutProcess::ClearAll() {
|
||||||
|
@ -142,18 +175,19 @@ void CheckoutProcess::ClearAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::RegisterPaymentStart(
|
void CheckoutProcess::RegisterPaymentStart(
|
||||||
not_null<CheckoutProcess*> process) {
|
not_null<CheckoutProcess*> process,
|
||||||
|
PaidInvoice info) {
|
||||||
const auto i = Processes.find(process->_session);
|
const auto i = Processes.find(process->_session);
|
||||||
Assert(i != end(Processes));
|
Assert(i != end(Processes));
|
||||||
for (const auto &[itemId, itemProcess] : i->second.byItem) {
|
for (const auto &[itemId, itemProcess] : i->second.byItem) {
|
||||||
if (itemProcess.get() == process) {
|
if (itemProcess.get() == process) {
|
||||||
i->second.paymentStartedByItem.emplace(itemId);
|
i->second.paymentStartedByItem.emplace(itemId, info);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto &[slug, itemProcess] : i->second.bySlug) {
|
for (const auto &[slug, itemProcess] : i->second.bySlug) {
|
||||||
if (itemProcess.get() == process) {
|
if (itemProcess.get() == process) {
|
||||||
i->second.paymentStartedBySlug.emplace(slug);
|
i->second.paymentStartedBySlug.emplace(slug, info);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,26 +196,33 @@ void CheckoutProcess::RegisterPaymentStart(
|
||||||
void CheckoutProcess::UnregisterPaymentStart(
|
void CheckoutProcess::UnregisterPaymentStart(
|
||||||
not_null<CheckoutProcess*> process) {
|
not_null<CheckoutProcess*> process) {
|
||||||
const auto i = Processes.find(process->_session);
|
const auto i = Processes.find(process->_session);
|
||||||
if (i != end(Processes)) {
|
if (i == end(Processes)) {
|
||||||
for (const auto &[itemId, itemProcess] : i->second.byItem) {
|
return;
|
||||||
if (itemProcess.get() == process) {
|
}
|
||||||
i->second.paymentStartedByItem.emplace(itemId);
|
for (const auto &[itemId, itemProcess] : i->second.byItem) {
|
||||||
return;
|
if (itemProcess.get() == process) {
|
||||||
}
|
i->second.paymentStartedByItem.remove(itemId);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for (const auto &[slug, itemProcess] : i->second.bySlug) {
|
}
|
||||||
if (itemProcess.get() == process) {
|
for (const auto &[slug, itemProcess] : i->second.bySlug) {
|
||||||
i->second.paymentStartedBySlug.emplace(slug);
|
if (itemProcess.get() == process) {
|
||||||
return;
|
i->second.paymentStartedBySlug.remove(slug);
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (i->second.paymentStartedByItem.empty()
|
||||||
|
&& i->second.byItem.empty()
|
||||||
|
&& i->second.paymentStartedBySlug.empty()
|
||||||
|
&& i->second.bySlug.empty()) {
|
||||||
|
Processes.erase(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckoutProcess::CheckoutProcess(
|
CheckoutProcess::CheckoutProcess(
|
||||||
InvoiceId id,
|
InvoiceId id,
|
||||||
Mode mode,
|
Mode mode,
|
||||||
Fn<void()> reactivate,
|
Fn<void(CheckoutResult)> reactivate,
|
||||||
PrivateTag)
|
PrivateTag)
|
||||||
: _session(SessionFromId(id))
|
: _session(SessionFromId(id))
|
||||||
, _form(std::make_unique<Form>(id, (mode == Mode::Receipt)))
|
, _form(std::make_unique<Form>(id, (mode == Mode::Receipt)))
|
||||||
|
@ -221,7 +262,8 @@ CheckoutProcess::CheckoutProcess(
|
||||||
CheckoutProcess::~CheckoutProcess() {
|
CheckoutProcess::~CheckoutProcess() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::setReactivateCallback(Fn<void()> reactivate) {
|
void CheckoutProcess::setReactivateCallback(
|
||||||
|
Fn<void(CheckoutResult)> reactivate) {
|
||||||
_reactivate = std::move(reactivate);
|
_reactivate = std::move(reactivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +318,8 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||||
auto bottomText = tr::lng_payments_processed_by(
|
auto bottomText = tr::lng_payments_processed_by(
|
||||||
lt_provider,
|
lt_provider,
|
||||||
rpl::single(_form->invoice().provider));
|
rpl::single(_form->invoice().provider));
|
||||||
|
_sendFormFailed = false;
|
||||||
|
_sendFormPending = true;
|
||||||
if (!_panel->showWebview(data.url, false, std::move(bottomText))) {
|
if (!_panel->showWebview(data.url, false, std::move(bottomText))) {
|
||||||
File::OpenUrl(data.url);
|
File::OpenUrl(data.url);
|
||||||
close();
|
close();
|
||||||
|
@ -284,7 +328,7 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
_session->api().applyUpdates(data.updates);
|
_session->api().applyUpdates(data.updates);
|
||||||
if (weak) {
|
if (weak) {
|
||||||
closeAndReactivate();
|
closeAndReactivate(CheckoutResult::Paid);
|
||||||
}
|
}
|
||||||
}, [&](const Error &error) {
|
}, [&](const Error &error) {
|
||||||
handleError(error);
|
handleError(error);
|
||||||
|
@ -386,6 +430,7 @@ void CheckoutProcess::handleError(const Error &error) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Error::Type::Send:
|
case Error::Type::Send:
|
||||||
|
_sendFormFailed = true;
|
||||||
if (const auto box = _enterPasswordBox.data()) {
|
if (const auto box = _enterPasswordBox.data()) {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
}
|
}
|
||||||
|
@ -424,14 +469,18 @@ void CheckoutProcess::panelRequestClose() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelCloseSure() {
|
void CheckoutProcess::panelCloseSure() {
|
||||||
closeAndReactivate();
|
closeAndReactivate(_sendFormFailed
|
||||||
|
? CheckoutResult::Failed
|
||||||
|
: _sendFormPending
|
||||||
|
? CheckoutResult::Pending
|
||||||
|
: CheckoutResult::Cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::closeAndReactivate() {
|
void CheckoutProcess::closeAndReactivate(CheckoutResult result) {
|
||||||
const auto reactivate = std::move(_reactivate);
|
const auto reactivate = std::move(_reactivate);
|
||||||
close();
|
close();
|
||||||
if (reactivate) {
|
if (reactivate) {
|
||||||
reactivate();
|
reactivate(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +512,7 @@ void CheckoutProcess::close() {
|
||||||
|
|
||||||
void CheckoutProcess::panelSubmit() {
|
void CheckoutProcess::panelSubmit() {
|
||||||
if (_form->invoice().receipt.paid) {
|
if (_form->invoice().receipt.paid) {
|
||||||
closeAndReactivate();
|
closeAndReactivate(CheckoutResult::Paid);
|
||||||
return;
|
return;
|
||||||
} else if (_submitState == SubmitState::Validating
|
} else if (_submitState == SubmitState::Validating
|
||||||
|| _submitState == SubmitState::Finishing) {
|
|| _submitState == SubmitState::Finishing) {
|
||||||
|
@ -485,7 +534,7 @@ void CheckoutProcess::panelSubmit() {
|
||||||
} else if (!method.newCredentials && !method.savedCredentials) {
|
} else if (!method.newCredentials && !method.savedCredentials) {
|
||||||
editPaymentMethod();
|
editPaymentMethod();
|
||||||
} else {
|
} else {
|
||||||
RegisterPaymentStart(this);
|
RegisterPaymentStart(this, { _form->invoice().cover.title });
|
||||||
_submitState = SubmitState::Finishing;
|
_submitState = SubmitState::Finishing;
|
||||||
_form->submit();
|
_form->submit();
|
||||||
}
|
}
|
||||||
|
@ -550,7 +599,8 @@ bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
||||||
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
crl::on_main(this, [=] { closeAndReactivate(); });
|
// #TODO payments
|
||||||
|
crl::on_main(this, [=] { closeAndReactivate(CheckoutResult::Paid); });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,17 @@ enum class Mode {
|
||||||
Receipt,
|
Receipt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CheckoutResult {
|
||||||
|
Paid,
|
||||||
|
Pending,
|
||||||
|
Cancelled,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PaidInvoice {
|
||||||
|
QString title;
|
||||||
|
};
|
||||||
|
|
||||||
class CheckoutProcess final
|
class CheckoutProcess final
|
||||||
: public base::has_weak_ptr
|
: public base::has_weak_ptr
|
||||||
, private Ui::PanelDelegate {
|
, private Ui::PanelDelegate {
|
||||||
|
@ -52,19 +63,22 @@ public:
|
||||||
static void Start(
|
static void Start(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
Mode mode,
|
Mode mode,
|
||||||
Fn<void()> reactivate);
|
Fn<void(CheckoutResult)> reactivate);
|
||||||
static void Start(
|
static void Start(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
const QString &slug,
|
const QString &slug,
|
||||||
Fn<void()> reactivate);
|
Fn<void(CheckoutResult)> reactivate);
|
||||||
[[nodiscard]] static bool TakePaymentStarted(
|
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||||
not_null<const HistoryItem*> item);
|
not_null<const HistoryItem*> item);
|
||||||
|
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
const QString &slug);
|
||||||
static void ClearAll();
|
static void ClearAll();
|
||||||
|
|
||||||
CheckoutProcess(
|
CheckoutProcess(
|
||||||
InvoiceId id,
|
InvoiceId id,
|
||||||
Mode mode,
|
Mode mode,
|
||||||
Fn<void()> reactivate,
|
Fn<void(CheckoutResult)> reactivate,
|
||||||
PrivateTag);
|
PrivateTag);
|
||||||
~CheckoutProcess();
|
~CheckoutProcess();
|
||||||
|
|
||||||
|
@ -77,12 +91,14 @@ private:
|
||||||
};
|
};
|
||||||
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
||||||
|
|
||||||
static void RegisterPaymentStart(not_null<CheckoutProcess*> process);
|
static void RegisterPaymentStart(
|
||||||
|
not_null<CheckoutProcess*> process,
|
||||||
|
PaidInvoice info);
|
||||||
static void UnregisterPaymentStart(not_null<CheckoutProcess*> process);
|
static void UnregisterPaymentStart(not_null<CheckoutProcess*> process);
|
||||||
|
|
||||||
void setReactivateCallback(Fn<void()> reactivate);
|
void setReactivateCallback(Fn<void(CheckoutResult)> reactivate);
|
||||||
void requestActivate();
|
void requestActivate();
|
||||||
void closeAndReactivate();
|
void closeAndReactivate(CheckoutResult result);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void handleFormUpdate(const FormUpdate &update);
|
void handleFormUpdate(const FormUpdate &update);
|
||||||
|
@ -137,9 +153,11 @@ private:
|
||||||
const std::unique_ptr<Form> _form;
|
const std::unique_ptr<Form> _form;
|
||||||
const std::unique_ptr<Ui::Panel> _panel;
|
const std::unique_ptr<Ui::Panel> _panel;
|
||||||
QPointer<PasscodeBox> _enterPasswordBox;
|
QPointer<PasscodeBox> _enterPasswordBox;
|
||||||
Fn<void()> _reactivate;
|
Fn<void(CheckoutResult)> _reactivate;
|
||||||
SubmitState _submitState = SubmitState::None;
|
SubmitState _submitState = SubmitState::None;
|
||||||
bool _initialSilentValidation = false;
|
bool _initialSilentValidation = false;
|
||||||
|
bool _sendFormPending = false;
|
||||||
|
bool _sendFormFailed = false;
|
||||||
|
|
||||||
bool _themeUpdateScheduled = false;
|
bool _themeUpdateScheduled = false;
|
||||||
rpl::lifetime _gettingPasswordState;
|
rpl::lifetime _gettingPasswordState;
|
||||||
|
|
|
@ -673,7 +673,7 @@ void DownloadMtprotoTask::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequ
|
||||||
auto ivec = bytes::make_span(state.ivec);
|
auto ivec = bytes::make_span(state.ivec);
|
||||||
std::copy(iv.begin(), iv.end(), ivec.begin());
|
std::copy(iv.begin(), iv.end(), ivec.begin());
|
||||||
|
|
||||||
auto counterOffset = static_cast<uint32>(requestData.offset) >> 4;
|
auto counterOffset = static_cast<uint32>(requestData.offset >> 4);
|
||||||
state.ivec[15] = static_cast<uchar>(counterOffset & 0xFF);
|
state.ivec[15] = static_cast<uchar>(counterOffset & 0xFF);
|
||||||
state.ivec[14] = static_cast<uchar>((counterOffset >> 8) & 0xFF);
|
state.ivec[14] = static_cast<uchar>((counterOffset >> 8) & 0xFF);
|
||||||
state.ivec[13] = static_cast<uchar>((counterOffset >> 16) & 0xFF);
|
state.ivec[13] = static_cast<uchar>((counterOffset >> 16) & 0xFF);
|
||||||
|
|
|
@ -305,11 +305,13 @@ Panel::Panel(
|
||||||
const QString &userDataPath,
|
const QString &userDataPath,
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
Fn<bool(QString)> handleLocalUri,
|
Fn<bool(QString)> handleLocalUri,
|
||||||
|
Fn<void(QString)> handleInvoice,
|
||||||
Fn<void(QByteArray)> sendData,
|
Fn<void(QByteArray)> sendData,
|
||||||
Fn<void()> close,
|
Fn<void()> close,
|
||||||
Fn<Webview::ThemeParams()> themeParams)
|
Fn<Webview::ThemeParams()> themeParams)
|
||||||
: _userDataPath(userDataPath)
|
: _userDataPath(userDataPath)
|
||||||
, _handleLocalUri(std::move(handleLocalUri))
|
, _handleLocalUri(std::move(handleLocalUri))
|
||||||
|
, _handleInvoice(std::move(handleInvoice))
|
||||||
, _sendData(std::move(sendData))
|
, _sendData(std::move(sendData))
|
||||||
, _close(std::move(close))
|
, _close(std::move(close))
|
||||||
, _widget(std::make_unique<SeparatePanel>()) {
|
, _widget(std::make_unique<SeparatePanel>()) {
|
||||||
|
@ -320,7 +322,9 @@ Panel::Panel(
|
||||||
) | rpl::start_with_next(_close, _widget->lifetime());
|
) | rpl::start_with_next(_close, _widget->lifetime());
|
||||||
|
|
||||||
_widget->closeEvents(
|
_widget->closeEvents(
|
||||||
) | rpl::start_with_next(_close, _widget->lifetime());
|
) | rpl::filter([=] {
|
||||||
|
return !_hiddenForPayment;
|
||||||
|
}) | rpl::start_with_next(_close, _widget->lifetime());
|
||||||
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
style::PaletteChanged(),
|
style::PaletteChanged(),
|
||||||
|
@ -568,6 +572,10 @@ bool Panel::createWebview() {
|
||||||
processMainButtonMessage(list.at(1));
|
processMainButtonMessage(list.at(1));
|
||||||
} else if (command == "web_app_request_theme") {
|
} else if (command == "web_app_request_theme") {
|
||||||
_themeUpdateForced.fire({});
|
_themeUpdateForced.fire({});
|
||||||
|
} else if (command == "web_app_open_tg_link") {
|
||||||
|
openTgLink(list.at(1).toString());
|
||||||
|
} else if (command == "web_app_open_invoice") {
|
||||||
|
openInvoice(list.at(1).toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -618,6 +626,38 @@ void Panel::sendDataMessage(const QJsonValue &value) {
|
||||||
_sendData(data.toUtf8());
|
_sendData(data.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::openTgLink(const QJsonValue &value) {
|
||||||
|
const auto json = value.toString();
|
||||||
|
const auto args = ParseMethodArgs(json);
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto path = args["path_full"].toString();
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
LOG(("BotWebView Error: Bad tg link \"%1\".").arg(json));
|
||||||
|
_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_handleLocalUri("https://t.me" + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::openInvoice(const QJsonValue &value) {
|
||||||
|
const auto json = value.toString();
|
||||||
|
const auto args = ParseMethodArgs(json);
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto slug = args["slug"].toString();
|
||||||
|
if (slug.isEmpty()) {
|
||||||
|
LOG(("BotWebView Error: Bad invoice \"%1\".").arg(json));
|
||||||
|
_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_handleInvoice(slug);
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::processMainButtonMessage(const QJsonValue &value) {
|
void Panel::processMainButtonMessage(const QJsonValue &value) {
|
||||||
const auto json = value.toString();
|
const auto json = value.toString();
|
||||||
const auto args = ParseMethodArgs(json);
|
const auto args = ParseMethodArgs(json);
|
||||||
|
@ -760,6 +800,22 @@ void Panel::updateThemeParams(const Webview::ThemeParams ¶ms) {
|
||||||
postEvent("theme_changed", "\"theme_params\": " + params.json);
|
postEvent("theme_changed", "\"theme_params\": " + params.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::invoiceClosed(const QString &slug, const QString &status) {
|
||||||
|
if (!_webview || !_webview->window.widget()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
postEvent(
|
||||||
|
"invoice_closed",
|
||||||
|
"\"slug\": \"" + slug + "\", \"status\": \"" + status + "\"");
|
||||||
|
_widget->showAndActivate();
|
||||||
|
_hiddenForPayment = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::hideForPayment() {
|
||||||
|
_hiddenForPayment = true;
|
||||||
|
_widget->hideGetDuration();
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::postEvent(const QString &event, const QString &data) {
|
void Panel::postEvent(const QString &event, const QString &data) {
|
||||||
_webview->window.eval(R"(
|
_webview->window.eval(R"(
|
||||||
if (window.TelegramGameProxy) {
|
if (window.TelegramGameProxy) {
|
||||||
|
@ -820,6 +876,7 @@ std::unique_ptr<Panel> Show(Args &&args) {
|
||||||
args.userDataPath,
|
args.userDataPath,
|
||||||
std::move(args.title),
|
std::move(args.title),
|
||||||
std::move(args.handleLocalUri),
|
std::move(args.handleLocalUri),
|
||||||
|
std::move(args.handleInvoice),
|
||||||
std::move(args.sendData),
|
std::move(args.sendData),
|
||||||
std::move(args.close),
|
std::move(args.close),
|
||||||
std::move(args.themeParams));
|
std::move(args.themeParams));
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class BoxContent;
|
class BoxContent;
|
||||||
|
@ -29,12 +30,13 @@ struct MainButtonArgs {
|
||||||
QString text;
|
QString text;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Panel final {
|
class Panel final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
Panel(
|
Panel(
|
||||||
const QString &userDataPath,
|
const QString &userDataPath,
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
Fn<bool(QString)> handleLocalUri,
|
Fn<bool(QString)> handleLocalUri,
|
||||||
|
Fn<void(QString)> handleInvoice,
|
||||||
Fn<void(QByteArray)> sendData,
|
Fn<void(QByteArray)> sendData,
|
||||||
Fn<void()> close,
|
Fn<void()> close,
|
||||||
Fn<Webview::ThemeParams()> themeParams);
|
Fn<Webview::ThemeParams()> themeParams);
|
||||||
|
@ -57,6 +59,9 @@ public:
|
||||||
|
|
||||||
void updateThemeParams(const Webview::ThemeParams ¶ms);
|
void updateThemeParams(const Webview::ThemeParams ¶ms);
|
||||||
|
|
||||||
|
void hideForPayment();
|
||||||
|
void invoiceClosed(const QString &slug, const QString &status);
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -70,6 +75,8 @@ private:
|
||||||
void setTitle(rpl::producer<QString> title);
|
void setTitle(rpl::producer<QString> title);
|
||||||
void sendDataMessage(const QJsonValue &value);
|
void sendDataMessage(const QJsonValue &value);
|
||||||
void processMainButtonMessage(const QJsonValue &value);
|
void processMainButtonMessage(const QJsonValue &value);
|
||||||
|
void openTgLink(const QJsonValue &value);
|
||||||
|
void openInvoice(const QJsonValue &value);
|
||||||
void createMainButton();
|
void createMainButton();
|
||||||
|
|
||||||
void postEvent(const QString &event, const QString &data = {});
|
void postEvent(const QString &event, const QString &data = {});
|
||||||
|
@ -80,6 +87,7 @@ private:
|
||||||
|
|
||||||
QString _userDataPath;
|
QString _userDataPath;
|
||||||
Fn<bool(QString)> _handleLocalUri;
|
Fn<bool(QString)> _handleLocalUri;
|
||||||
|
Fn<void(QString)> _handleInvoice;
|
||||||
Fn<void(QByteArray)> _sendData;
|
Fn<void(QByteArray)> _sendData;
|
||||||
Fn<void()> _close;
|
Fn<void()> _close;
|
||||||
std::unique_ptr<SeparatePanel> _widget;
|
std::unique_ptr<SeparatePanel> _widget;
|
||||||
|
@ -93,6 +101,7 @@ private:
|
||||||
rpl::lifetime _bgLifetime;
|
rpl::lifetime _bgLifetime;
|
||||||
bool _webviewProgress = false;
|
bool _webviewProgress = false;
|
||||||
bool _themeUpdateScheduled = false;
|
bool _themeUpdateScheduled = false;
|
||||||
|
bool _hiddenForPayment = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,6 +111,7 @@ struct Args {
|
||||||
rpl::producer<QString> title;
|
rpl::producer<QString> title;
|
||||||
rpl::producer<QString> bottom;
|
rpl::producer<QString> bottom;
|
||||||
Fn<bool(QString)> handleLocalUri;
|
Fn<bool(QString)> handleLocalUri;
|
||||||
|
Fn<void(QString)> handleInvoice;
|
||||||
Fn<void(QByteArray)> sendData;
|
Fn<void(QByteArray)> sendData;
|
||||||
Fn<void()> close;
|
Fn<void()> close;
|
||||||
Fn<Webview::ThemeParams()> themeParams;
|
Fn<Webview::ThemeParams()> themeParams;
|
||||||
|
|
Loading…
Add table
Reference in a new issue