| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "payments/payments_checkout_process.h"
- #include "payments/payments_form.h"
- #include "payments/ui/payments_panel.h"
- #include "main/main_session.h"
- #include "main/main_account.h"
- #include "storage/storage_account.h"
- #include "history/history_item.h"
- #include "history/history.h"
- #include "data/data_user.h" // UserData::isBot.
- #include "boxes/passcode_box.h"
- #include "core/local_url_handlers.h" // TryConvertUrlToLocal.
- #include "core/file_utilities.h" // File::OpenUrl.
- #include "core/core_cloud_password.h" // Core::CloudPasswordState
- #include "core/click_handler_types.h"
- #include "lang/lang_keys.h"
- #include "apiwrap.h"
- #include "api/api_cloud_password.h"
- #include "window/themes/window_theme.h"
- #include <QJsonDocument>
- #include <QJsonObject>
- #include <QJsonArray>
- #include <QJsonValue>
- namespace Payments {
- namespace {
- struct SessionProcesses {
- base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem;
- base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug;
- base::flat_map<uint64, std::unique_ptr<CheckoutProcess>> byRandomId;
- base::flat_map<FullMsgId, PaidInvoice> paymentStartedByItem;
- base::flat_map<QString, PaidInvoice> paymentStartedBySlug;
- rpl::lifetime lifetime;
- };
- base::flat_map<not_null<Main::Session*>, SessionProcesses> Processes;
- [[nodiscard]] SessionProcesses &LookupSessionProcesses(
- not_null<Main::Session*> session) {
- const auto i = Processes.find(session);
- if (i != end(Processes)) {
- return i->second;
- }
- const auto j = Processes.emplace(session).first;
- auto &result = j->second;
- session->account().sessionChanges(
- ) | rpl::start_with_next([=] {
- Processes.erase(session);
- }, result.lifetime);
- return result;
- }
- } // namespace
- void CheckoutProcess::Start(
- not_null<const HistoryItem*> item,
- Mode mode,
- Fn<void(CheckoutResult)> reactivate,
- Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
- auto &processes = LookupSessionProcesses(&item->history()->session());
- const auto media = item->media();
- const auto invoice = media ? media->invoice() : nullptr;
- if (mode == Mode::Payment && !invoice) {
- return;
- }
- const auto id = (invoice && invoice->receiptMsgId)
- ? FullMsgId(item->history()->peer->id, invoice->receiptMsgId)
- : item->fullId();
- if (invoice) {
- mode = invoice->receiptMsgId ? Mode::Receipt : Mode::Payment;
- } else if (mode == Mode::Payment) {
- LOG(("API Error: CheckoutProcess Payment start without invoice."));
- return;
- }
- const auto i = processes.byItem.find(id);
- if (i != end(processes.byItem)) {
- i->second->setReactivateCallback(std::move(reactivate));
- i->second->setNonPanelPaymentFormProcess(
- std::move(nonPanelPaymentFormProcess));
- i->second->requestActivate();
- return;
- }
- const auto j = processes.byItem.emplace(
- id,
- std::make_unique<CheckoutProcess>(
- InvoiceId{ InvoiceMessage{ item->history()->peer, id.msg } },
- mode,
- std::move(reactivate),
- std::move(nonPanelPaymentFormProcess),
- PrivateTag{})).first;
- j->second->requestActivate();
- }
- void CheckoutProcess::Start(
- not_null<Main::Session*> session,
- const QString &slug,
- Fn<void(CheckoutResult)> reactivate,
- Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
- auto &processes = LookupSessionProcesses(session);
- const auto i = processes.bySlug.find(slug);
- if (i != end(processes.bySlug)) {
- i->second->setReactivateCallback(std::move(reactivate));
- i->second->setNonPanelPaymentFormProcess(
- std::move(nonPanelPaymentFormProcess));
- i->second->requestActivate();
- return;
- }
- const auto j = processes.bySlug.emplace(
- slug,
- std::make_unique<CheckoutProcess>(
- InvoiceId{ InvoiceSlug{ session, slug } },
- Mode::Payment,
- std::move(reactivate),
- std::move(nonPanelPaymentFormProcess),
- PrivateTag{})).first;
- j->second->requestActivate();
- }
- void CheckoutProcess::Start(
- InvoicePremiumGiftCode giftCodeInvoice,
- Fn<void(CheckoutResult)> reactivate,
- Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
- const auto randomId = giftCodeInvoice.randomId;
- auto id = InvoiceId{ std::move(giftCodeInvoice) };
- auto &processes = LookupSessionProcesses(SessionFromId(id));
- const auto i = processes.byRandomId.find(randomId);
- if (i != end(processes.byRandomId)) {
- i->second->setReactivateCallback(std::move(reactivate));
- i->second->setNonPanelPaymentFormProcess(
- std::move(nonPanelPaymentFormProcess));
- i->second->requestActivate();
- return;
- }
- const auto j = processes.byRandomId.emplace(
- randomId,
- std::make_unique<CheckoutProcess>(
- std::move(id),
- Mode::Payment,
- std::move(reactivate),
- std::move(nonPanelPaymentFormProcess),
- PrivateTag{})).first;
- j->second->requestActivate();
- }
- void CheckoutProcess::Start(
- InvoiceCredits creditsInvoice,
- Fn<void(CheckoutResult)> reactivate) {
- const auto randomId = creditsInvoice.randomId;
- auto id = InvoiceId{ std::move(creditsInvoice) };
- auto &processes = LookupSessionProcesses(SessionFromId(id));
- const auto i = processes.byRandomId.find(randomId);
- if (i != end(processes.byRandomId)) {
- i->second->setReactivateCallback(std::move(reactivate));
- i->second->requestActivate();
- return;
- }
- const auto j = processes.byRandomId.emplace(
- randomId,
- std::make_unique<CheckoutProcess>(
- std::move(id),
- Mode::Payment,
- std::move(reactivate),
- nullptr,
- PrivateTag{})).first;
- j->second->requestActivate();
- }
- void CheckoutProcess::Start(
- InvoiceStarGift giftInvoice,
- Fn<void(CheckoutResult)> reactivate,
- Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
- const auto randomId = giftInvoice.randomId;
- auto id = InvoiceId{ std::move(giftInvoice) };
- auto &processes = LookupSessionProcesses(SessionFromId(id));
- const auto i = processes.byRandomId.find(randomId);
- if (i != end(processes.byRandomId)) {
- i->second->setReactivateCallback(std::move(reactivate));
- i->second->setNonPanelPaymentFormProcess(
- std::move(nonPanelPaymentFormProcess));
- return;
- }
- processes.byRandomId.emplace(
- randomId,
- std::make_unique<CheckoutProcess>(
- std::move(id),
- Mode::Payment,
- std::move(reactivate),
- std::move(nonPanelPaymentFormProcess),
- PrivateTag{}));
- }
- std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
- not_null<const HistoryItem*> item) {
- const auto session = &item->history()->session();
- const auto itemId = item->fullId();
- const auto i = Processes.find(session);
- if (i == end(Processes)) {
- return std::nullopt;
- }
- 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);
- if (j != end(i->second.byItem)) {
- j->second->closeAndReactivate(CheckoutResult::Paid);
- } else if (i->second.paymentStartedByItem.empty()
- && i->second.byItem.empty()
- && i->second.paymentStartedBySlug.empty()
- && i->second.bySlug.empty()
- && i->second.byRandomId.empty()) {
- Processes.erase(i);
- }
- 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()
- && i->second.byRandomId.empty()) {
- Processes.erase(i);
- }
- return result;
- }
- void CheckoutProcess::ClearAll() {
- Processes.clear();
- }
- void CheckoutProcess::RegisterPaymentStart(
- not_null<CheckoutProcess*> process,
- PaidInvoice info) {
- const auto i = Processes.find(process->_session);
- Assert(i != end(Processes));
- for (const auto &[itemId, itemProcess] : i->second.byItem) {
- if (itemProcess.get() == process) {
- i->second.paymentStartedByItem.emplace(itemId, info);
- return;
- }
- }
- for (const auto &[slug, itemProcess] : i->second.bySlug) {
- if (itemProcess.get() == process) {
- i->second.paymentStartedBySlug.emplace(slug, info);
- return;
- }
- }
- for (const auto &[randomId, itemProcess] : i->second.byRandomId) {
- if (itemProcess.get() == process) {
- return;
- }
- }
- }
- void CheckoutProcess::UnregisterPaymentStart(
- not_null<CheckoutProcess*> process) {
- const auto i = Processes.find(process->_session);
- if (i == end(Processes)) {
- return;
- }
- for (const auto &[itemId, itemProcess] : i->second.byItem) {
- if (itemProcess.get() == process) {
- i->second.paymentStartedByItem.remove(itemId);
- break;
- }
- }
- for (const auto &[slug, itemProcess] : i->second.bySlug) {
- if (itemProcess.get() == process) {
- i->second.paymentStartedBySlug.remove(slug);
- break;
- }
- }
- for (const auto &[randomId, itemProcess] : i->second.byRandomId) {
- if (itemProcess.get() == process) {
- break;
- }
- }
- if (i->second.paymentStartedByItem.empty()
- && i->second.byItem.empty()
- && i->second.paymentStartedBySlug.empty()
- && i->second.bySlug.empty()
- && i->second.byRandomId.empty()) {
- Processes.erase(i);
- }
- }
- CheckoutProcess::CheckoutProcess(
- InvoiceId id,
- Mode mode,
- Fn<void(CheckoutResult)> reactivate,
- Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess,
- PrivateTag)
- : _session(SessionFromId(id))
- , _form(std::make_unique<Form>(id, (mode == Mode::Receipt)))
- , _panel(std::make_unique<Ui::Panel>(panelDelegate()))
- , _reactivate(std::move(reactivate))
- , _nonPanelPaymentFormProcess(std::move(nonPanelPaymentFormProcess)) {
- _form->updates(
- ) | rpl::start_with_next([=](const FormUpdate &update) {
- handleFormUpdate(update);
- }, _lifetime);
- _panel->savedMethodChosen(
- ) | rpl::start_with_next([=](QString id) {
- _form->chooseSavedMethod(id);
- }, _panel->lifetime());
- _panel->backRequests(
- ) | rpl::start_with_next([=] {
- panelCancelEdit();
- }, _panel->lifetime());
- if (!_nonPanelPaymentFormProcess) {
- showForm();
- }
- _panel->toggleProgress(true);
- if (mode == Mode::Payment) {
- _session->api().cloudPassword().state(
- ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
- _form->setHasPassword(state.hasPassword);
- }, _lifetime);
- }
- }
- CheckoutProcess::~CheckoutProcess() {
- }
- void CheckoutProcess::setReactivateCallback(
- Fn<void(CheckoutResult)> reactivate) {
- _reactivate = std::move(reactivate);
- }
- void CheckoutProcess::setNonPanelPaymentFormProcess(
- Fn<void(NonPanelPaymentForm)> callback) {
- _nonPanelPaymentFormProcess = std::move(callback);
- }
- void CheckoutProcess::requestActivate() {
- if (!_nonPanelPaymentFormProcess) {
- _panel->requestActivate();
- }
- }
- not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
- return static_cast<PanelDelegate*>(this);
- }
- void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
- v::match(update, [&](const ToggleProgress &data) {
- _panel->toggleProgress(data.shown);
- }, [&](const FormReady &) {
- performInitialSilentValidation();
- if (!_initialSilentValidation) {
- showForm();
- }
- if (!_form->paymentMethod().savedCredentials.empty()) {
- _session->api().cloudPassword().reload();
- }
- }, [&](const ThumbnailUpdated &data) {
- _panel->updateFormThumbnail(data.thumbnail);
- }, [&](const ValidateFinished &) {
- if (_initialSilentValidation) {
- _initialSilentValidation = false;
- }
- showForm();
- const auto submitted = (_submitState == SubmitState::Validating);
- _submitState = SubmitState::Validated;
- if (submitted) {
- panelSubmit();
- }
- }, [&](const PaymentMethodUpdate &data) {
- showForm();
- if (data.requestNewPassword) {
- requestSetPassword();
- }
- }, [&](const TmpPasswordRequired &) {
- UnregisterPaymentStart(this);
- _submitState = SubmitState::Validated;
- requestPassword();
- }, [&](const BotTrustRequired &data) {
- UnregisterPaymentStart(this);
- _submitState = SubmitState::Validated;
- _panel->showWarning(data.bot->name(), data.provider->name());
- if (const auto box = _enterPasswordBox.data()) {
- box->closeBox();
- }
- }, [&](const VerificationNeeded &data) {
- auto bottomText = tr::lng_payments_processed_by(
- lt_provider,
- rpl::single(_form->invoice().provider));
- _sendFormFailed = false;
- _sendFormPending = true;
- if (!_panel->showWebview(data.url, false, std::move(bottomText))) {
- File::OpenUrl(data.url);
- close();
- }
- }, [&](const PaymentFinished &data) {
- const auto weak = base::make_weak(this);
- _session->api().applyUpdates(data.updates);
- if (weak) {
- closeAndReactivate(CheckoutResult::Paid);
- }
- }, [&](const CreditsPaymentStarted &data) {
- if (_nonPanelPaymentFormProcess) {
- _nonPanelPaymentFormProcess(
- std::make_shared<CreditsFormData>(data.data));
- close();
- }
- }, [&](const CreditsReceiptReady &data) {
- if (_nonPanelPaymentFormProcess) {
- _nonPanelPaymentFormProcess(
- std::make_shared<CreditsReceiptData>(data.data));
- close();
- }
- }, [&](const Error &error) {
- handleError(error);
- });
- }
- void CheckoutProcess::handleError(const Error &error) {
- const auto showToast = [&](TextWithEntities &&text) {
- _panel->requestActivate();
- _panel->showToast(std::move(text));
- };
- const auto &id = error.id;
- switch (error.type) {
- case Error::Type::Form:
- 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) {
- _panel->showCriticalError({ "Error: " + id });
- }
- break;
- case Error::Type::Validate: {
- if (_submitState == SubmitState::Validating
- || _submitState == SubmitState::Validated) {
- _submitState = SubmitState::None;
- }
- if (_initialSilentValidation) {
- _initialSilentValidation = false;
- showForm();
- return;
- }
- using InfoField = Ui::InformationField;
- using CardField = Ui::CardField;
- if (id == u"REQ_INFO_NAME_INVALID"_q) {
- showInformationError(InfoField::Name);
- } else if (id == u"REQ_INFO_EMAIL_INVALID"_q) {
- showInformationError(InfoField::Email);
- } else if (id == u"REQ_INFO_PHONE_INVALID"_q) {
- showInformationError(InfoField::Phone);
- } else if (id == u"ADDRESS_STREET_LINE1_INVALID"_q) {
- showInformationError(InfoField::ShippingStreet);
- } else if (id == u"ADDRESS_CITY_INVALID"_q) {
- showInformationError(InfoField::ShippingCity);
- } else if (id == u"ADDRESS_STATE_INVALID"_q) {
- showInformationError(InfoField::ShippingState);
- } else if (id == u"ADDRESS_COUNTRY_INVALID"_q) {
- showInformationError(InfoField::ShippingCountry);
- } else if (id == u"ADDRESS_POSTCODE_INVALID"_q) {
- showInformationError(InfoField::ShippingPostcode);
- } else if (id == u"LOCAL_CARD_NUMBER_INVALID"_q) {
- showCardError(CardField::Number);
- } else if (id == u"LOCAL_CARD_EXPIRE_DATE_INVALID"_q) {
- showCardError(CardField::ExpireDate);
- } else if (id == u"LOCAL_CARD_CVC_INVALID"_q) {
- showCardError(CardField::Cvc);
- } else if (id == u"LOCAL_CARD_HOLDER_NAME_INVALID"_q) {
- showCardError(CardField::Name);
- } else if (id == u"LOCAL_CARD_BILLING_COUNTRY_INVALID"_q) {
- showCardError(CardField::AddressCountry);
- } 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!" });
- } else if (id == u"SHIPPING_NOT_AVAILABLE"_q) {
- showToast({ tr::lng_payments_shipping_not_available(tr::now) });
- } else {
- showToast({ "Error: " + id });
- }
- } break;
- case Error::Type::Stripe: {
- using Field = Ui::CardField;
- if (id == u"InvalidNumber"_q || id == u"IncorrectNumber"_q) {
- showCardError(Field::Number);
- } else if (id == u"InvalidCVC"_q || id == u"IncorrectCVC"_q) {
- showCardError(Field::Cvc);
- } else if (id == u"InvalidExpiryMonth"_q
- || id == u"InvalidExpiryYear"_q
- || id == u"ExpiredCard"_q) {
- showCardError(Field::ExpireDate);
- } else if (id == u"CardDeclined"_q) {
- showToast({ tr::lng_payments_card_declined(tr::now) });
- } else if (id == u"ProcessingError"_q) {
- showToast({ "Sorry, a processing error occurred." });
- } else {
- showToast({ "Stripe Error: " + id });
- }
- } break;
- case Error::Type::SmartGlocal: {
- showToast({ "SmartGlocal Error: " + id });
- } break;
- case Error::Type::TmpPassword:
- if (const auto box = _enterPasswordBox.data()) {
- if (!box->handleCustomCheckError(id)) {
- showToast({ "Error: Could not generate tmp password." });
- }
- }
- break;
- case Error::Type::Send:
- _sendFormFailed = true;
- if (const auto box = _enterPasswordBox.data()) {
- box->closeBox();
- }
- if (_submitState == SubmitState::Finishing) {
- UnregisterPaymentStart(this);
- _submitState = SubmitState::Validated;
- }
- 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({ tr::lng_payments_precheckout_failed(tr::now) });
- } else if (id == u"BOT_PRECHECKOUT_TIMEOUT"_q) {
- showToast({ tr::lng_payments_precheckout_timeout(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) {
- requestPassword();
- } else {
- showToast({ "Error: " + id });
- }
- break;
- default: Unexpected("Error type in CheckoutProcess::handleError.");
- }
- }
- void CheckoutProcess::panelRequestClose() {
- if (_form->hasChanges()) {
- _panel->showCloseConfirm();
- } else {
- panelCloseSure();
- }
- }
- void CheckoutProcess::panelCloseSure() {
- closeAndReactivate(_sendFormFailed
- ? CheckoutResult::Failed
- : _sendFormPending
- ? CheckoutResult::Pending
- : CheckoutResult::Cancelled);
- }
- void CheckoutProcess::closeAndReactivate(CheckoutResult result) {
- const auto reactivate = std::move(_reactivate);
- close();
- if (reactivate) {
- reactivate(result);
- }
- }
- void CheckoutProcess::close() {
- const auto i = Processes.find(_session);
- if (i == end(Processes)) {
- return;
- }
- auto &entry = i->second;
- const auto j = ranges::find(entry.byItem, this, [](const auto &pair) {
- return pair.second.get();
- });
- if (j != end(entry.byItem)) {
- entry.byItem.erase(j);
- }
- const auto k = ranges::find(entry.bySlug, this, [](const auto &pair) {
- return pair.second.get();
- });
- if (k != end(entry.bySlug)) {
- entry.bySlug.erase(k);
- }
- const auto l = ranges::find(
- entry.byRandomId,
- this,
- [](const auto &pair) { return pair.second.get(); });
- if (l != end(entry.byRandomId)) {
- entry.byRandomId.erase(l);
- }
- if (entry.byItem.empty()
- && entry.bySlug.empty()
- && i->second.byRandomId.empty()
- && entry.paymentStartedByItem.empty()
- && entry.paymentStartedBySlug.empty()) {
- Processes.erase(i);
- }
- }
- void CheckoutProcess::panelSubmit() {
- if (_form->invoice().receipt.paid) {
- closeAndReactivate(CheckoutResult::Paid);
- return;
- } else if (_submitState == SubmitState::Validating
- || _submitState == SubmitState::Finishing) {
- return;
- }
- const auto &method = _form->paymentMethod();
- const auto &invoice = _form->invoice();
- const auto &options = _form->shippingOptions();
- if (!options.list.empty() && options.selectedId.isEmpty()) {
- chooseShippingOption();
- } else if (_submitState != SubmitState::Validated
- && options.list.empty()
- && (invoice.isShippingAddressRequested
- || invoice.isNameRequested
- || invoice.isEmailRequested
- || invoice.isPhoneRequested)) {
- _submitState = SubmitState::Validating;
- _form->validateInformation(_form->information());
- } else if (!method.newCredentials
- && method.savedCredentialsIndex >= method.savedCredentials.size()) {
- editPaymentMethod();
- } else if (!invoice.termsUrl.isEmpty()
- && !_form->details().termsAccepted) {
- _panel->requestTermsAcceptance(
- _form->details().termsBotUsername,
- invoice.termsUrl,
- invoice.isRecurring);
- } else {
- RegisterPaymentStart(this, { _form->invoice().cover.title });
- _submitState = SubmitState::Finishing;
- _form->submit();
- }
- }
- void CheckoutProcess::panelTrustAndSubmit() {
- _form->trustBot();
- panelSubmit();
- }
- void CheckoutProcess::panelAcceptTermsAndSubmit() {
- _form->acceptTerms();
- panelSubmit();
- }
- void CheckoutProcess::panelWebviewMessage(
- const QJsonDocument &message,
- bool saveInformation) {
- if (!message.isArray()) {
- LOG(("Payments Error: "
- "Not an array received in buy_callback arguments."));
- return;
- }
- const auto list = message.array();
- if (list.at(0).toString() != "payment_form_submit") {
- return;
- } else if (!list.at(1).isString()) {
- LOG(("Payments Error: "
- "Not a string received in buy_callback result."));
- return;
- }
- auto error = QJsonParseError();
- const auto document = QJsonDocument::fromJson(
- list.at(1).toString().toUtf8(),
- &error);
- if (error.error != QJsonParseError::NoError) {
- LOG(("Payments Error: "
- "Failed to parse buy_callback arguments, error: %1."
- ).arg(error.errorString()));
- return;
- } else if (!document.isObject()) {
- LOG(("Payments Error: "
- "Not an object decoded in buy_callback result."));
- return;
- }
- const auto root = document.object();
- const auto title = root.value("title").toString();
- const auto credentials = root.value("credentials");
- if (!credentials.isObject()) {
- LOG(("Payments Error: "
- "Not an object received in payment credentials."));
- return;
- }
- crl::on_main(this, [=] {
- _form->setPaymentCredentials(NewCredentials{
- .title = title,
- .data = QJsonDocument(
- credentials.toObject()
- ).toJson(QJsonDocument::Compact),
- .saveOnServer = saveInformation,
- });
- });
- }
- std::optional<QDate> CheckoutProcess::panelOverrideExpireDateThreshold() {
- return _form->overrideExpireDateThreshold();
- }
- bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
- if (Core::TryConvertUrlToLocal(uri) == uri) {
- return true;
- }
- // #TODO payments
- crl::on_main(this, [=] { closeAndReactivate(CheckoutResult::Paid); });
- return false;
- }
- void CheckoutProcess::panelCancelEdit() {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- showForm();
- }
- void CheckoutProcess::panelEditPaymentMethod() {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- editPaymentMethod();
- }
- void CheckoutProcess::panelValidateCard(
- Ui::UncheckedCardDetails data,
- bool saveInformation) {
- _form->validateCard(data, saveInformation);
- }
- void CheckoutProcess::panelEditShippingInformation() {
- showEditInformation(Ui::InformationField::ShippingStreet);
- }
- void CheckoutProcess::panelEditName() {
- showEditInformation(Ui::InformationField::Name);
- }
- void CheckoutProcess::panelEditEmail() {
- showEditInformation(Ui::InformationField::Email);
- }
- void CheckoutProcess::panelEditPhone() {
- showEditInformation(Ui::InformationField::Phone);
- }
- void CheckoutProcess::showForm() {
- _panel->showForm(
- _form->invoice(),
- _form->information(),
- _form->paymentMethod().ui,
- _form->shippingOptions());
- if (_nonPanelPaymentFormProcess && !_realFormNotified) {
- _realFormNotified = true;
- const auto weak = base::make_weak(_panel.get());
- _nonPanelPaymentFormProcess(RealFormPresentedNotification());
- if (weak) {
- requestActivate();
- }
- }
- }
- void CheckoutProcess::showEditInformation(Ui::InformationField field) {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- _panel->showEditInformation(
- _form->invoice(),
- _form->information(),
- field);
- }
- void CheckoutProcess::showInformationError(Ui::InformationField field) {
- Expects(_submitState != SubmitState::Validated);
- if (_submitState != SubmitState::None) {
- return;
- }
- _panel->showInformationError(
- _form->invoice(),
- _form->information(),
- field);
- }
- void CheckoutProcess::showCardError(Ui::CardField field) {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- _panel->showCardError(_form->paymentMethod().ui.native, field);
- }
- void CheckoutProcess::chooseShippingOption() {
- _panel->chooseShippingOption(_form->shippingOptions());
- }
- void CheckoutProcess::chooseTips() {
- _panel->chooseTips(_form->invoice());
- }
- void CheckoutProcess::editPaymentMethod() {
- _panel->choosePaymentMethod(_form->paymentMethod().ui);
- }
- void CheckoutProcess::requestSetPassword() {
- _session->api().cloudPassword().reload();
- _panel->askSetPassword();
- }
- void CheckoutProcess::requestPassword() {
- getPasswordState([=](const Core::CloudPasswordState &state) {
- auto fields = PasscodeBox::CloudFields::From(state);
- fields.customTitle = tr::lng_payments_password_title();
- const auto &method = _form->paymentMethod();
- const auto &list = method.savedCredentials;
- const auto index = method.savedCredentialsIndex;
- fields.customDescription = tr::lng_payments_password_description(
- tr::now,
- lt_card,
- (index < list.size()) ? list[index].title : QString());
- fields.customSubmitButton = tr::lng_payments_password_submit();
- fields.customCheckCallback = [=](
- const Core::CloudPasswordResult &result,
- QPointer<PasscodeBox> box) {
- _enterPasswordBox = box;
- _form->submit(result);
- };
- _panel->showBox(Box<PasscodeBox>(_session, fields));
- });
- }
- void CheckoutProcess::panelSetPassword() {
- getPasswordState([=](const Core::CloudPasswordState &state) {
- if (state.hasPassword) {
- return;
- }
- auto owned = Box<PasscodeBox>(
- _session,
- PasscodeBox::CloudFields::From(state));
- const auto box = owned.data();
- rpl::merge(
- box->newPasswordSet() | rpl::to_empty,
- box->passwordReloadNeeded()
- ) | rpl::start_with_next([=] {
- _session->api().cloudPassword().reload();
- }, box->lifetime());
- box->clearUnconfirmedPassword(
- ) | rpl::start_with_next([=] {
- _session->api().cloudPassword().clearUnconfirmedPassword();
- }, box->lifetime());
- _panel->showBox(std::move(owned));
- });
- }
- void CheckoutProcess::panelOpenUrl(const QString &url) {
- File::OpenUrl(url);
- }
- void CheckoutProcess::getPasswordState(
- Fn<void(const Core::CloudPasswordState&)> callback) {
- Expects(callback != nullptr);
- if (_gettingPasswordState) {
- return;
- }
- _session->api().cloudPassword().state(
- ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
- _gettingPasswordState.destroy();
- callback(state);
- }, _gettingPasswordState);
- }
- void CheckoutProcess::panelChooseShippingOption() {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- chooseShippingOption();
- }
- void CheckoutProcess::panelChangeShippingOption(const QString &id) {
- _form->setShippingOption(id);
- showForm();
- }
- void CheckoutProcess::panelChooseTips() {
- if (_submitState != SubmitState::None
- && _submitState != SubmitState::Validated) {
- return;
- }
- chooseTips();
- }
- void CheckoutProcess::panelChangeTips(int64 value) {
- _form->setTips(value);
- showForm();
- }
- void CheckoutProcess::panelValidateInformation(
- Ui::RequestedInformation data) {
- if (_submitState == SubmitState::Validated) {
- _submitState = SubmitState::None;
- }
- _form->validateInformation(data);
- }
- void CheckoutProcess::panelShowBox(object_ptr<Ui::BoxContent> box) {
- _panel->showBox(std::move(box));
- }
- QVariant CheckoutProcess::panelClickHandlerContext() {
- return QVariant::fromValue(ClickHandlerContext{
- .show = _panel->uiShow(),
- });
- }
- void CheckoutProcess::performInitialSilentValidation() {
- const auto &invoice = _form->invoice();
- const auto &saved = _form->information();
- if (invoice.receipt
- || (invoice.isNameRequested && saved.name.isEmpty())
- || (invoice.isEmailRequested && saved.email.isEmpty())
- || (invoice.isPhoneRequested && saved.phone.isEmpty())
- || (invoice.isShippingAddressRequested && !saved.shippingAddress)) {
- return;
- }
- _initialSilentValidation = true;
- _form->validateInformation(saved);
- }
- Webview::StorageId CheckoutProcess::panelWebviewStorageId() {
- return _session->local().resolveStorageIdOther();
- }
- Webview::ThemeParams CheckoutProcess::panelWebviewThemeParams() {
- return Window::Theme::WebViewParams();
- }
- } // namespace Payments
|