mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Allow saving and using saved credentials.
This commit is contained in:
parent
2e58993181
commit
663db64688
16 changed files with 350 additions and 55 deletions
|
@ -1880,15 +1880,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_payments_info_email" = "Email";
|
||||
"lng_payments_info_phone" = "Phone";
|
||||
"lng_payments_shipping_address_title" = "Shipping Information";
|
||||
"lng_payments_save_shipping_about" = "You can save your shipping information for future use.";
|
||||
"lng_payments_card_title" = "New Card";
|
||||
"lng_payments_card_number" = "Card Number";
|
||||
"lng_payments_card_holder" = "Cardholder name";
|
||||
"lng_payments_billing_address" = "Billing Information";
|
||||
"lng_payments_billing_country" = "Country";
|
||||
"lng_payments_billing_zip_code" = "Zip Code";
|
||||
"lng_payments_save_payment_about" = "You can save your payment information for future use.";
|
||||
"lng_payments_save_information" = "Save Information for future use";
|
||||
"lng_payments_need_password" = "You can save your payment information for future use. Please turn on Two-Step Verification to enable this.";
|
||||
"lng_payments_password_title" = "Payment Confirmation";
|
||||
"lng_payments_password_description" = "Your card {card} is on file. To pay with this card, please enter your 2-Step-Verification password.";
|
||||
"lng_payments_password_submit" = "Pay";
|
||||
"lng_payments_tips_label" = "Tips";
|
||||
"lng_payments_tips_title" = "Tips";
|
||||
"lng_payments_tips_enter" = "Enter tips amount";
|
||||
|
|
|
@ -361,7 +361,11 @@ void PasscodeBox::closeReplacedBy() {
|
|||
}
|
||||
|
||||
void PasscodeBox::setPasswordFail(const MTP::Error &error) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
setPasswordFail(error.type());
|
||||
}
|
||||
|
||||
void PasscodeBox::setPasswordFail(const QString &type) {
|
||||
if (MTP::IsFloodError(type)) {
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
|
||||
|
@ -378,20 +382,19 @@ void PasscodeBox::setPasswordFail(const MTP::Error &error) {
|
|||
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
const auto &err = error.type();
|
||||
if (err == qstr("PASSWORD_HASH_INVALID")
|
||||
|| err == qstr("SRP_PASSWORD_CHANGED")) {
|
||||
if (type == qstr("PASSWORD_HASH_INVALID")
|
||||
|| type == qstr("SRP_PASSWORD_CHANGED")) {
|
||||
if (_oldPasscode->isHidden()) {
|
||||
_passwordReloadNeeded.fire({});
|
||||
closeBox();
|
||||
} else {
|
||||
badOldPasscode();
|
||||
}
|
||||
} else if (err == qstr("SRP_ID_INVALID")) {
|
||||
} else if (type == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid();
|
||||
//} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||
//} else if (err == qstr("NEW_SALT_INVALID")) {
|
||||
} else if (err == qstr("EMAIL_INVALID")) {
|
||||
//} else if (type == qstr("NEW_PASSWORD_BAD")) {
|
||||
//} else if (type == qstr("NEW_SALT_INVALID")) {
|
||||
} else if (type == qstr("EMAIL_INVALID")) {
|
||||
_emailError = tr::lng_cloud_password_bad_email(tr::now);
|
||||
_recoverEmail->setFocus();
|
||||
_recoverEmail->showError();
|
||||
|
@ -682,12 +685,15 @@ void PasscodeBox::serverError() {
|
|||
}
|
||||
|
||||
bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) {
|
||||
const auto &type = error.type();
|
||||
if (MTP::IsFloodError(error)
|
||||
return handleCustomCheckError(error.type());
|
||||
}
|
||||
|
||||
bool PasscodeBox::handleCustomCheckError(const QString &type) {
|
||||
if (MTP::IsFloodError(type)
|
||||
|| type == qstr("PASSWORD_HASH_INVALID")
|
||||
|| type == qstr("SRP_PASSWORD_CHANGED")
|
||||
|| type == qstr("SRP_ID_INVALID")) {
|
||||
setPasswordFail(error);
|
||||
setPasswordFail(type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
rpl::producer<> clearUnconfirmedPassword() const;
|
||||
|
||||
bool handleCustomCheckError(const MTP::Error &error);
|
||||
bool handleCustomCheckError(const QString &type);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
@ -82,6 +83,7 @@ private:
|
|||
|
||||
void setPasswordDone(const QByteArray &newPasswordBytes);
|
||||
void setPasswordFail(const MTP::Error &error);
|
||||
void setPasswordFail(const QString &type);
|
||||
void setPasswordFail(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const QString &email,
|
||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/window_lock_widgets.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
//#include "platform/platform_specific.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "support/support_helper.h"
|
||||
#include "facades.h"
|
||||
|
@ -43,6 +44,7 @@ namespace Main {
|
|||
namespace {
|
||||
|
||||
constexpr auto kLegacyCallsPeerToPeerNobody = 4;
|
||||
constexpr auto kTmpPasswordReserveTime = TimeId(10);
|
||||
|
||||
[[nodiscard]] QString ValidatedInternalLinksDomain(
|
||||
not_null<const Session*> session) {
|
||||
|
@ -155,6 +157,20 @@ Session::Session(
|
|||
_api->requestNotifySettings(MTP_inputNotifyBroadcasts());
|
||||
}
|
||||
|
||||
void Session::setTmpPassword(const QByteArray &password, TimeId validUntil) {
|
||||
if (_tmpPassword.isEmpty() || validUntil > _tmpPasswordValidUntil) {
|
||||
_tmpPassword = password;
|
||||
_tmpPasswordValidUntil = validUntil;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Session::validTmpPassword() const {
|
||||
return (_tmpPasswordValidUntil
|
||||
>= base::unixtime::now() + kTmpPasswordReserveTime)
|
||||
? _tmpPassword
|
||||
: QByteArray();
|
||||
}
|
||||
|
||||
// Can be called only right before ~Session.
|
||||
void Session::finishLogout() {
|
||||
updates().updateOnline();
|
||||
|
|
|
@ -145,6 +145,9 @@ public:
|
|||
[[nodiscard]] QString createInternalLink(const QString &query) const;
|
||||
[[nodiscard]] QString createInternalLinkFull(const QString &query) const;
|
||||
|
||||
void setTmpPassword(const QByteArray &password, TimeId validUntil);
|
||||
[[nodiscard]] QByteArray validTmpPassword() const;
|
||||
|
||||
// Can be called only right before ~Session.
|
||||
void finishLogout();
|
||||
|
||||
|
@ -190,6 +193,9 @@ private:
|
|||
base::flat_set<not_null<Window::SessionController*>> _windows;
|
||||
base::Timer _saveSettingsTimer;
|
||||
|
||||
QByteArray _tmpPassword;
|
||||
TimeId _tmpPasswordValidUntil = 0;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -38,8 +38,12 @@ private:
|
|||
|
||||
};
|
||||
|
||||
inline bool IsFloodError(const QString &type) {
|
||||
return type.startsWith(qstr("FLOOD_WAIT_"));
|
||||
}
|
||||
|
||||
inline bool IsFloodError(const Error &error) {
|
||||
return error.type().startsWith(qstr("FLOOD_WAIT_"));
|
||||
return IsFloodError(error.type());
|
||||
}
|
||||
|
||||
inline bool IsTemporaryError(const Error &error) {
|
||||
|
|
|
@ -16,8 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#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 "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
@ -104,11 +107,19 @@ CheckoutProcess::CheckoutProcess(
|
|||
) | rpl::start_with_next([=](const FormUpdate &update) {
|
||||
handleFormUpdate(update);
|
||||
}, _lifetime);
|
||||
|
||||
_panel->backRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
panelCancelEdit();
|
||||
}, _panel->lifetime());
|
||||
showForm();
|
||||
|
||||
if (mode == Mode::Payment) {
|
||||
_session->api().passwordState(
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
|
||||
_form->setHasPassword(!!state.request);
|
||||
}, _lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
CheckoutProcess::~CheckoutProcess() {
|
||||
|
@ -132,6 +143,9 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
|||
if (!_initialSilentValidation) {
|
||||
showForm();
|
||||
}
|
||||
if (_form->paymentMethod().savedCredentials) {
|
||||
_session->api().reloadPasswordState();
|
||||
}
|
||||
}, [&](const ThumbnailUpdated &data) {
|
||||
_panel->updateFormThumbnail(data.thumbnail);
|
||||
}, [&](const ValidateFinished &) {
|
||||
|
@ -139,12 +153,19 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
|||
_initialSilentValidation = false;
|
||||
}
|
||||
showForm();
|
||||
if (_submitState == SubmitState::Validation) {
|
||||
_submitState = SubmitState::Validated;
|
||||
const auto submitted = (_submitState == SubmitState::Validating);
|
||||
_submitState = SubmitState::Validated;
|
||||
if (submitted) {
|
||||
panelSubmit();
|
||||
}
|
||||
}, [&](const PaymentMethodUpdate &) {
|
||||
}, [&](const PaymentMethodUpdate &data) {
|
||||
showForm();
|
||||
if (data.requestNewPassword) {
|
||||
requestSetPassword();
|
||||
}
|
||||
}, [&](const TmpPasswordRequired &) {
|
||||
_submitState = SubmitState::Validated;
|
||||
requestPassword();
|
||||
}, [&](const VerificationNeeded &data) {
|
||||
if (!_panel->showWebview(data.url, false)) {
|
||||
File::OpenUrl(data.url);
|
||||
|
@ -176,7 +197,7 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
}
|
||||
break;
|
||||
case Error::Type::Validate: {
|
||||
if (_submitState == SubmitState::Validation
|
||||
if (_submitState == SubmitState::Validating
|
||||
|| _submitState == SubmitState::Validated) {
|
||||
_submitState = SubmitState::None;
|
||||
}
|
||||
|
@ -243,9 +264,19 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
showToast({ "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:
|
||||
if (const auto box = _enterPasswordBox.data()) {
|
||||
box->closeBox();
|
||||
}
|
||||
if (_submitState == SubmitState::Finishing) {
|
||||
_submitState = SubmitState::None;
|
||||
_submitState = SubmitState::Validated;
|
||||
}
|
||||
if (id == u"PAYMENT_FAILED"_q) {
|
||||
showToast({ "Error: Payment Failed. Your card has not been billed." }); // #TODO payments errors message
|
||||
|
@ -256,6 +287,8 @@ void CheckoutProcess::handleError(const Error &error) {
|
|||
|| id == u"PAYMENT_CREDENTIALS_INVALID"_q
|
||||
|| id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
|
||||
showToast({ "Error: " + id + ". Your card has not been billed." });
|
||||
} else if (id == u"TMP_PASSWORD_INVALID"_q) {
|
||||
// #TODO payments save
|
||||
} else {
|
||||
showToast({ "Error: " + id });
|
||||
}
|
||||
|
@ -301,7 +334,7 @@ void CheckoutProcess::panelSubmit() {
|
|||
if (_form->invoice().receipt.paid) {
|
||||
closeAndReactivate();
|
||||
return;
|
||||
} else if (_submitState == SubmitState::Validation
|
||||
} else if (_submitState == SubmitState::Validating
|
||||
|| _submitState == SubmitState::Finishing) {
|
||||
return;
|
||||
}
|
||||
|
@ -310,25 +343,25 @@ void CheckoutProcess::panelSubmit() {
|
|||
const auto &options = _form->shippingOptions();
|
||||
if (!options.list.empty() && options.selectedId.isEmpty()) {
|
||||
chooseShippingOption();
|
||||
return;
|
||||
} else if (_submitState != SubmitState::Validated
|
||||
&& options.list.empty()
|
||||
&& (invoice.isShippingAddressRequested
|
||||
|| invoice.isNameRequested
|
||||
|| invoice.isEmailRequested
|
||||
|| invoice.isPhoneRequested)) {
|
||||
_submitState = SubmitState::Validation;
|
||||
_submitState = SubmitState::Validating;
|
||||
_form->validateInformation(_form->savedInformation());
|
||||
return;
|
||||
} else if (!method.newCredentials && !method.savedCredentials) {
|
||||
editPaymentMethod();
|
||||
return;
|
||||
} else {
|
||||
_submitState = SubmitState::Finishing;
|
||||
_form->submit();
|
||||
}
|
||||
_submitState = SubmitState::Finishing;
|
||||
_form->submit();
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
||||
void CheckoutProcess::panelWebviewMessage(
|
||||
const QJsonDocument &message,
|
||||
bool saveInformation) {
|
||||
if (!message.isArray()) {
|
||||
LOG(("Payments Error: "
|
||||
"Not an array received in buy_callback arguments."));
|
||||
|
@ -371,7 +404,7 @@ void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
|||
.data = QJsonDocument(
|
||||
credentials.toObject()
|
||||
).toJson(QJsonDocument::Compact),
|
||||
.saveOnServer = false, // #TODO payments save
|
||||
.saveOnServer = saveInformation,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -399,8 +432,10 @@ void CheckoutProcess::panelEditPaymentMethod() {
|
|||
editPaymentMethod();
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelValidateCard(Ui::UncheckedCardDetails data) {
|
||||
_form->validateCard(data);
|
||||
void CheckoutProcess::panelValidateCard(
|
||||
Ui::UncheckedCardDetails data,
|
||||
bool saveInformation) {
|
||||
_form->validateCard(data, saveInformation);
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelEditShippingInformation() {
|
||||
|
@ -466,6 +501,70 @@ void CheckoutProcess::editPaymentMethod() {
|
|||
_panel->choosePaymentMethod(_form->paymentMethod().ui);
|
||||
}
|
||||
|
||||
void CheckoutProcess::requestSetPassword() {
|
||||
_session->api().reloadPasswordState();
|
||||
_panel->askSetPassword();
|
||||
}
|
||||
|
||||
void CheckoutProcess::requestPassword() {
|
||||
getPasswordState([=](const Core::CloudPasswordState &state) {
|
||||
auto fields = PasscodeBox::CloudFields::From(state);
|
||||
fields.customTitle = tr::lng_payments_password_title();
|
||||
fields.customDescription = tr::lng_payments_password_description(
|
||||
tr::now,
|
||||
lt_card,
|
||||
_form->paymentMethod().savedCredentials.title);
|
||||
fields.customSubmitButton = tr::lng_payments_password_submit();
|
||||
fields.customCheckCallback = [=](
|
||||
const Core::CloudPasswordResult &result) {
|
||||
_form->submit(result);
|
||||
};
|
||||
auto owned = Box<PasscodeBox>(_session, fields);
|
||||
_enterPasswordBox = owned.data();
|
||||
_panel->showBox(std::move(owned));
|
||||
});
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelSetPassword() {
|
||||
getPasswordState([=](const Core::CloudPasswordState &state) {
|
||||
if (state.request) {
|
||||
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().reloadPasswordState();
|
||||
}, box->lifetime());
|
||||
|
||||
box->clearUnconfirmedPassword(
|
||||
) | rpl::start_with_next([=] {
|
||||
_session->api().clearUnconfirmedPassword();
|
||||
}, box->lifetime());
|
||||
|
||||
_panel->showBox(std::move(owned));
|
||||
});
|
||||
}
|
||||
|
||||
void CheckoutProcess::getPasswordState(
|
||||
Fn<void(const Core::CloudPasswordState&)> callback) {
|
||||
Expects(callback != nullptr);
|
||||
|
||||
if (_gettingPasswordState) {
|
||||
return;
|
||||
}
|
||||
_session->api().passwordState(
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
|
||||
_gettingPasswordState.destroy();
|
||||
callback(state);
|
||||
}, _gettingPasswordState);
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelChooseShippingOption() {
|
||||
if (_submitState != SubmitState::None) {
|
||||
return;
|
||||
|
@ -492,6 +591,9 @@ void CheckoutProcess::panelChangeTips(int64 value) {
|
|||
|
||||
void CheckoutProcess::panelValidateInformation(
|
||||
Ui::RequestedInformation data) {
|
||||
if (_submitState == SubmitState::Validated) {
|
||||
_submitState = SubmitState::None;
|
||||
}
|
||||
_form->validateInformation(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/weak_ptr.h"
|
||||
|
||||
class HistoryItem;
|
||||
class PasscodeBox;
|
||||
|
||||
namespace Core {
|
||||
class CloudPasswordState;
|
||||
} // namespace Core
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class GenericBox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
class Panel;
|
||||
enum class InformationField;
|
||||
|
@ -55,7 +64,7 @@ public:
|
|||
private:
|
||||
enum class SubmitState {
|
||||
None,
|
||||
Validation,
|
||||
Validating,
|
||||
Validated,
|
||||
Finishing,
|
||||
};
|
||||
|
@ -77,13 +86,22 @@ private:
|
|||
void chooseTips();
|
||||
void editPaymentMethod();
|
||||
|
||||
void requestSetPassword();
|
||||
void requestSetPasswordSure(QPointer<Ui::GenericBox> old);
|
||||
void requestPassword();
|
||||
void getPasswordState(
|
||||
Fn<void(const Core::CloudPasswordState&)> callback);
|
||||
|
||||
void performInitialSilentValidation();
|
||||
|
||||
void panelRequestClose() override;
|
||||
void panelCloseSure() override;
|
||||
void panelSubmit() override;
|
||||
void panelWebviewMessage(const QJsonDocument &message) override;
|
||||
void panelWebviewMessage(
|
||||
const QJsonDocument &message,
|
||||
bool saveInformation) override;
|
||||
bool panelWebviewNavigationAttempt(const QString &uri) override;
|
||||
void panelSetPassword() override;
|
||||
|
||||
void panelCancelEdit() override;
|
||||
void panelEditPaymentMethod() override;
|
||||
|
@ -97,7 +115,9 @@ private:
|
|||
void panelChangeTips(int64 value) override;
|
||||
|
||||
void panelValidateInformation(Ui::RequestedInformation data) override;
|
||||
void panelValidateCard(Ui::UncheckedCardDetails data) override;
|
||||
void panelValidateCard(
|
||||
Ui::UncheckedCardDetails data,
|
||||
bool saveInformation) override;
|
||||
void panelShowBox(object_ptr<Ui::BoxContent> box) override;
|
||||
|
||||
QString panelWebviewDataPath() override;
|
||||
|
@ -105,10 +125,12 @@ private:
|
|||
const not_null<Main::Session*> _session;
|
||||
const std::unique_ptr<Form> _form;
|
||||
const std::unique_ptr<Ui::Panel> _panel;
|
||||
QPointer<PasscodeBox> _enterPasswordBox;
|
||||
Fn<void()> _reactivate;
|
||||
SubmitState _submitState = SubmitState::None;
|
||||
bool _initialSilentValidation = false;
|
||||
|
||||
rpl::lifetime _gettingPasswordState;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "stripe/stripe_card_validator.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "apiwrap.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "styles/style_payments.h" // paymentsThumbnailSize.
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
@ -32,6 +33,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Payments {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPasswordPeriod = 15 * TimeId(60);
|
||||
|
||||
[[nodiscard]] Ui::Address ParseAddress(const MTPPostAddress &address) {
|
||||
return address.match([](const MTPDpostAddress &data) {
|
||||
return Ui::Address{
|
||||
|
@ -381,11 +384,10 @@ void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
|||
|
||||
void Form::processSavedCredentials(
|
||||
const MTPDpaymentSavedCredentialsCard &data) {
|
||||
// #TODO payments save
|
||||
//_nativePayment.savedCredentials = SavedCredentials{
|
||||
// .id = qs(data.vid()),
|
||||
// .title = qs(data.vtitle()),
|
||||
//};
|
||||
_paymentMethod.savedCredentials = SavedCredentials{
|
||||
.id = qs(data.vid()),
|
||||
.title = qs(data.vtitle()),
|
||||
};
|
||||
refreshPaymentMethodDetails();
|
||||
}
|
||||
|
||||
|
@ -395,6 +397,9 @@ void Form::refreshPaymentMethodDetails() {
|
|||
_paymentMethod.ui.title = entered ? entered.title : saved.title;
|
||||
_paymentMethod.ui.ready = entered || saved;
|
||||
_paymentMethod.ui.native.defaultCountry = defaultCountry();
|
||||
_paymentMethod.ui.canSaveInformation
|
||||
= _paymentMethod.ui.native.canSaveInformation
|
||||
= _details.canSaveCredentials || _details.passwordMissing;
|
||||
}
|
||||
|
||||
QString Form::defaultPhone() const {
|
||||
|
@ -452,7 +457,16 @@ void Form::fillStripeNativeMethod() {
|
|||
}
|
||||
|
||||
void Form::submit() {
|
||||
Expects(!_paymentMethod.newCredentials.data.isEmpty()); // #TODO payments save
|
||||
Expects(_paymentMethod.newCredentials
|
||||
|| _paymentMethod.savedCredentials);
|
||||
|
||||
const auto password = _paymentMethod.newCredentials
|
||||
? QByteArray()
|
||||
: _session->validTmpPassword();
|
||||
if (!_paymentMethod.newCredentials && password.isEmpty()) {
|
||||
_updates.fire(TmpPasswordRequired{});
|
||||
return;
|
||||
}
|
||||
|
||||
using Flag = MTPpayments_SendPaymentForm::Flag;
|
||||
_api.request(MTPpayments_SendPaymentForm(
|
||||
|
@ -468,9 +482,16 @@ void Form::submit() {
|
|||
MTP_int(_msgId),
|
||||
MTP_string(_requestedInformationId),
|
||||
MTP_string(_shippingOptions.selectedId),
|
||||
MTP_inputPaymentCredentials(
|
||||
MTP_flags(0),
|
||||
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data))),
|
||||
(_paymentMethod.newCredentials
|
||||
? MTP_inputPaymentCredentials(
|
||||
MTP_flags((_paymentMethod.newCredentials.saveOnServer
|
||||
&& _details.canSaveCredentials)
|
||||
? MTPDinputPaymentCredentials::Flag::f_save
|
||||
: MTPDinputPaymentCredentials::Flag(0)),
|
||||
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data)))
|
||||
: MTP_inputPaymentCredentialsSaved(
|
||||
MTP_string(_paymentMethod.savedCredentials.id),
|
||||
MTP_bytes(password))),
|
||||
MTP_long(_invoice.tipsSelected)
|
||||
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||
|
@ -483,6 +504,27 @@ void Form::submit() {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void Form::submit(const Core::CloudPasswordResult &result) {
|
||||
if (_passwordRequestId) {
|
||||
return;
|
||||
}
|
||||
_passwordRequestId = _api.request(MTPaccount_GetTmpPassword(
|
||||
result.result,
|
||||
MTP_int(kPasswordPeriod)
|
||||
)).done([=](const MTPaccount_TmpPassword &result) {
|
||||
_passwordRequestId = 0;
|
||||
result.match([&](const MTPDaccount_tmpPassword &data) {
|
||||
_session->setTmpPassword(
|
||||
data.vtmp_password().v,
|
||||
data.vvalid_until().v);
|
||||
submit();
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_passwordRequestId = 0;
|
||||
_updates.fire(Error{ Error::Type::TmpPassword, error.type() });
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Form::validateInformation(const Ui::RequestedInformation &information) {
|
||||
if (_validateRequestId) {
|
||||
if (_validatedInformation == information) {
|
||||
|
@ -573,7 +615,9 @@ Error Form::informationErrorLocal(
|
|||
return Error();
|
||||
}
|
||||
|
||||
void Form::validateCard(const Ui::UncheckedCardDetails &details) {
|
||||
void Form::validateCard(
|
||||
const Ui::UncheckedCardDetails &details,
|
||||
bool saveInformation) {
|
||||
Expects(!v::is_null(_paymentMethod.native.data));
|
||||
|
||||
if (!validateCardLocal(details)) {
|
||||
|
@ -581,7 +625,7 @@ void Form::validateCard(const Ui::UncheckedCardDetails &details) {
|
|||
}
|
||||
const auto &native = _paymentMethod.native.data;
|
||||
if (const auto stripe = std::get_if<StripePaymentMethod>(&native)) {
|
||||
validateCard(*stripe, details);
|
||||
validateCard(*stripe, details, saveInformation);
|
||||
} else {
|
||||
Unexpected("Native payment provider in Form::validateCard.");
|
||||
}
|
||||
|
@ -635,7 +679,8 @@ Error Form::cardErrorLocal(const Ui::UncheckedCardDetails &details) const {
|
|||
|
||||
void Form::validateCard(
|
||||
const StripePaymentMethod &method,
|
||||
const Ui::UncheckedCardDetails &details) {
|
||||
const Ui::UncheckedCardDetails &details,
|
||||
bool saveInformation) {
|
||||
Expects(!method.publishableKey.isEmpty());
|
||||
|
||||
if (_stripe) {
|
||||
|
@ -673,7 +718,7 @@ void Form::validateCard(
|
|||
{ "type", "card" },
|
||||
{ "id", token.tokenId() },
|
||||
}).toJson(QJsonDocument::Compact),
|
||||
.saveOnServer = false, // #TODO payments save
|
||||
.saveOnServer = saveInformation,
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
@ -683,8 +728,17 @@ void Form::setPaymentCredentials(const NewCredentials &credentials) {
|
|||
Expects(!credentials.empty());
|
||||
|
||||
_paymentMethod.newCredentials = credentials;
|
||||
const auto requestNewPassword = credentials.saveOnServer
|
||||
&& !_details.canSaveCredentials
|
||||
&& _details.passwordMissing;
|
||||
refreshPaymentMethodDetails();
|
||||
_updates.fire(PaymentMethodUpdate{});
|
||||
_updates.fire(PaymentMethodUpdate{ requestNewPassword });
|
||||
}
|
||||
|
||||
void Form::setHasPassword(bool has) {
|
||||
if (_details.passwordMissing) {
|
||||
_details.canSaveCredentials = has;
|
||||
}
|
||||
}
|
||||
|
||||
void Form::setShippingOption(const QString &id) {
|
||||
|
|
|
@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class Image;
|
||||
|
||||
namespace Core {
|
||||
struct CloudPasswordResult;
|
||||
} // namespace Core
|
||||
|
||||
namespace Stripe {
|
||||
class APIClient;
|
||||
} // namespace Stripe
|
||||
|
@ -107,10 +111,13 @@ struct ThumbnailUpdated {
|
|||
QImage thumbnail;
|
||||
};
|
||||
struct ValidateFinished {};
|
||||
struct PaymentMethodUpdate {};
|
||||
struct PaymentMethodUpdate {
|
||||
bool requestNewPassword = false;
|
||||
};
|
||||
struct VerificationNeeded {
|
||||
QString url;
|
||||
};
|
||||
struct TmpPasswordRequired {};
|
||||
struct PaymentFinished {
|
||||
MTPUpdates updates;
|
||||
};
|
||||
|
@ -120,6 +127,7 @@ struct Error {
|
|||
Form,
|
||||
Validate,
|
||||
Stripe,
|
||||
TmpPassword,
|
||||
Send,
|
||||
};
|
||||
Type type = Type::None;
|
||||
|
@ -139,6 +147,7 @@ struct FormUpdate : std::variant<
|
|||
ValidateFinished,
|
||||
PaymentMethodUpdate,
|
||||
VerificationNeeded,
|
||||
TmpPasswordRequired,
|
||||
PaymentFinished,
|
||||
Error> {
|
||||
using variant::variant;
|
||||
|
@ -170,11 +179,15 @@ public:
|
|||
}
|
||||
|
||||
void validateInformation(const Ui::RequestedInformation &information);
|
||||
void validateCard(const Ui::UncheckedCardDetails &details);
|
||||
void validateCard(
|
||||
const Ui::UncheckedCardDetails &details,
|
||||
bool saveInformation);
|
||||
void setPaymentCredentials(const NewCredentials &credentials);
|
||||
void setHasPassword(bool has);
|
||||
void setShippingOption(const QString &id);
|
||||
void setTips(int64 value);
|
||||
void submit();
|
||||
void submit(const Core::CloudPasswordResult &result);
|
||||
|
||||
private:
|
||||
void fillInvoiceFromMessage();
|
||||
|
@ -208,7 +221,8 @@ private:
|
|||
|
||||
void validateCard(
|
||||
const StripePaymentMethod &method,
|
||||
const Ui::UncheckedCardDetails &details);
|
||||
const Ui::UncheckedCardDetails &details,
|
||||
bool saveInformation);
|
||||
|
||||
bool validateInformationLocal(
|
||||
const Ui::RequestedInformation &information) const;
|
||||
|
@ -235,6 +249,7 @@ private:
|
|||
|
||||
Ui::RequestedInformation _validatedInformation;
|
||||
mtpRequestId _validateRequestId = 0;
|
||||
mtpRequestId _passwordRequestId = 0;
|
||||
|
||||
std::unique_ptr<Stripe::APIClient> _stripe;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -246,7 +247,7 @@ void EditCard::setupControls() {
|
|||
const auto inner = setupContent();
|
||||
|
||||
_submit->addClickHandler([=] {
|
||||
_delegate->panelValidateCard(collect());
|
||||
_delegate->panelValidateCard(collect(), (_save && _save->checked()));
|
||||
});
|
||||
_cancel->addClickHandler([=] {
|
||||
_delegate->panelCancelEdit();
|
||||
|
@ -357,6 +358,14 @@ not_null<RpWidget*> EditCard::setupContent() {
|
|||
}, lifetime());
|
||||
}
|
||||
}
|
||||
if (_native.canSaveInformation) {
|
||||
_save = inner->add(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
inner,
|
||||
tr::lng_payments_save_information(tr::now),
|
||||
false),
|
||||
st::paymentsSaveCheckboxPadding);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Ui {
|
|||
class ScrollArea;
|
||||
class FadeShadow;
|
||||
class RoundButton;
|
||||
class Checkbox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
@ -62,6 +63,7 @@ private:
|
|||
std::unique_ptr<Field> _name;
|
||||
std::unique_ptr<Field> _country;
|
||||
std::unique_ptr<Field> _zip;
|
||||
Checkbox *_save = nullptr;
|
||||
|
||||
CardField _focusField = CardField::Number;
|
||||
|
||||
|
|
|
@ -229,6 +229,18 @@ void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
|||
showEditCard(method.native, CardField::Number);
|
||||
} else if (!showWebview(method.url, true)) {
|
||||
// #TODO payments errors not supported
|
||||
} else if (method.canSaveInformation) {
|
||||
const auto &padding = st::paymentsPanelPadding;
|
||||
_saveWebviewInformation = CreateChild<Checkbox>(
|
||||
_webviewBottom.get(),
|
||||
tr::lng_payments_save_information(tr::now),
|
||||
false);
|
||||
const auto height = padding.top()
|
||||
+ _saveWebviewInformation->heightNoMargins()
|
||||
+ padding.bottom();
|
||||
_saveWebviewInformation->moveToLeft(padding.right(), padding.top());
|
||||
_saveWebviewInformation->show();
|
||||
_webviewBottom->resize(_webviewBottom->width(), height);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +256,17 @@ bool Panel::showWebview(const QString &url, bool allowBack) {
|
|||
bool Panel::createWebview() {
|
||||
auto container = base::make_unique_q<RpWidget>(_widget.get());
|
||||
|
||||
container->setGeometry(_widget->innerGeometry());
|
||||
_webviewBottom = std::make_unique<RpWidget>(_widget.get());
|
||||
const auto bottom = _webviewBottom.get();
|
||||
bottom->show();
|
||||
|
||||
bottom->heightValue(
|
||||
) | rpl::start_with_next([=, raw = container.get()](int height) {
|
||||
const auto inner = _widget->innerGeometry();
|
||||
bottom->move(inner.x(), inner.y() + inner.height() - height);
|
||||
raw->resize(inner.width(), inner.height() - height);
|
||||
bottom->resizeToWidth(inner.width());
|
||||
}, bottom->lifetime());
|
||||
container->show();
|
||||
|
||||
_webview = std::make_unique<Webview::Window>(
|
||||
|
@ -257,6 +279,9 @@ bool Panel::createWebview() {
|
|||
if (_webview.get() == raw) {
|
||||
_webview = nullptr;
|
||||
}
|
||||
if (_webviewBottom.get() == bottom) {
|
||||
_webviewBottom = nullptr;
|
||||
}
|
||||
});
|
||||
if (!raw->widget()) {
|
||||
return false;
|
||||
|
@ -268,7 +293,9 @@ bool Panel::createWebview() {
|
|||
}, container->lifetime());
|
||||
|
||||
raw->setMessageHandler([=](const QJsonDocument &message) {
|
||||
_delegate->panelWebviewMessage(message);
|
||||
const auto save = _saveWebviewInformation
|
||||
&& _saveWebviewInformation->checked();
|
||||
_delegate->panelWebviewMessage(message, save);
|
||||
});
|
||||
|
||||
raw->setNavigationHandler([=](const QString &uri) {
|
||||
|
@ -308,6 +335,22 @@ void Panel::choosePaymentMethod(const PaymentMethodDetails &method) {
|
|||
}));
|
||||
}
|
||||
|
||||
void Panel::askSetPassword() {
|
||||
showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
tr::lng_payments_need_password(),
|
||||
st::boxLabel),
|
||||
st::boxPadding);
|
||||
box->addButton(tr::lng_continue(), [=] {
|
||||
_delegate->panelSetPassword();
|
||||
box->closeBox();
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}));
|
||||
}
|
||||
|
||||
void Panel::showEditCard(
|
||||
const NativeMethodDetails &native,
|
||||
CardField field) {
|
||||
|
|
|
@ -10,8 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/object_ptr.h"
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
class SeparatePanel;
|
||||
class BoxContent;
|
||||
class Checkbox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Webview {
|
||||
|
@ -65,6 +67,7 @@ public:
|
|||
void chooseShippingOption(const ShippingOptions &options);
|
||||
void chooseTips(const Invoice &invoice);
|
||||
void choosePaymentMethod(const PaymentMethodDetails &method);
|
||||
void askSetPassword();
|
||||
|
||||
bool showWebview(const QString &url, bool allowBack);
|
||||
|
||||
|
@ -81,6 +84,8 @@ private:
|
|||
const not_null<PanelDelegate*> _delegate;
|
||||
std::unique_ptr<SeparatePanel> _widget;
|
||||
std::unique_ptr<Webview::Window> _webview;
|
||||
std::unique_ptr<RpWidget> _webviewBottom;
|
||||
QPointer<Checkbox> _saveWebviewInformation;
|
||||
QPointer<FormSummary> _weakFormSummary;
|
||||
QPointer<EditInformation> _weakEditInformation;
|
||||
QPointer<EditCard> _weakEditCard;
|
||||
|
|
|
@ -154,6 +154,7 @@ struct NativeMethodDetails {
|
|||
bool needCountry = false;
|
||||
bool needZip = false;
|
||||
bool needCardholderName = false;
|
||||
bool canSaveInformation = false;
|
||||
};
|
||||
|
||||
struct PaymentMethodDetails {
|
||||
|
@ -161,6 +162,7 @@ struct PaymentMethodDetails {
|
|||
NativeMethodDetails native;
|
||||
QString url;
|
||||
bool ready = false;
|
||||
bool canSaveInformation = false;
|
||||
};
|
||||
|
||||
enum class CardField {
|
||||
|
|
|
@ -28,8 +28,11 @@ public:
|
|||
virtual void panelRequestClose() = 0;
|
||||
virtual void panelCloseSure() = 0;
|
||||
virtual void panelSubmit() = 0;
|
||||
virtual void panelWebviewMessage(const QJsonDocument &message) = 0;
|
||||
virtual void panelWebviewMessage(
|
||||
const QJsonDocument &message,
|
||||
bool saveInformation) = 0;
|
||||
virtual bool panelWebviewNavigationAttempt(const QString &uri) = 0;
|
||||
virtual void panelSetPassword() = 0;
|
||||
|
||||
virtual void panelCancelEdit() = 0;
|
||||
virtual void panelEditPaymentMethod() = 0;
|
||||
|
@ -43,7 +46,9 @@ public:
|
|||
virtual void panelChangeTips(int64 value) = 0;
|
||||
|
||||
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
||||
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;
|
||||
virtual void panelValidateCard(
|
||||
Ui::UncheckedCardDetails data,
|
||||
bool saveInformation) = 0;
|
||||
virtual void panelShowBox(object_ptr<BoxContent> box) = 0;
|
||||
|
||||
virtual QString panelWebviewDataPath() = 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue