Handle native / non-native payment methods (same way).
This commit is contained in:
parent
5e4bc200c2
commit
56031a6402
20 changed files with 360 additions and 355 deletions
|
@ -1864,6 +1864,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_payments_total_label" = "Total";
|
"lng_payments_total_label" = "Total";
|
||||||
"lng_payments_pay_amount" = "Pay {amount}";
|
"lng_payments_pay_amount" = "Pay {amount}";
|
||||||
"lng_payments_payment_method" = "Payment Method";
|
"lng_payments_payment_method" = "Payment Method";
|
||||||
|
"lng_payments_new_card" = "New Card...";
|
||||||
"lng_payments_payment_method_ph" = "Enter your card details";
|
"lng_payments_payment_method_ph" = "Enter your card details";
|
||||||
"lng_payments_shipping_address" = "Shipping Information";
|
"lng_payments_shipping_address" = "Shipping Information";
|
||||||
"lng_payments_shipping_address_ph" = "Enter your shipping information";
|
"lng_payments_shipping_address_ph" = "Enter your shipping information";
|
||||||
|
|
|
@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "payments/payments_form.h"
|
#include "payments/payments_form.h"
|
||||||
#include "payments/ui/payments_panel.h"
|
#include "payments/ui/payments_panel.h"
|
||||||
#include "payments/ui/payments_webview.h"
|
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
#include "main/main_domain.h"
|
#include "main/main_domain.h"
|
||||||
|
@ -17,10 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "core/local_url_handlers.h" // TryConvertUrlToLocal.
|
#include "core/local_url_handlers.h" // TryConvertUrlToLocal.
|
||||||
|
#include "core/file_utilities.h" // File::OpenUrl.
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "stripe/stripe_api_client.h"
|
|
||||||
#include "stripe/stripe_error.h"
|
|
||||||
#include "stripe/stripe_token.h"
|
|
||||||
|
|
||||||
// #TODO payments errors
|
// #TODO payments errors
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -57,13 +54,6 @@ base::flat_map<not_null<Main::Session*>, SessionProcesses> Processes;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString CardTitle(const Stripe::Card &card) {
|
|
||||||
// Like server stores saved_credentials title.
|
|
||||||
return Stripe::CardBrandToString(card.brand()).toLower()
|
|
||||||
+ " *"
|
|
||||||
+ card.last4();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CheckoutProcess::Start(not_null<const HistoryItem*> item) {
|
void CheckoutProcess::Start(not_null<const HistoryItem*> item) {
|
||||||
|
@ -110,7 +100,7 @@ not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||||
v::match(update.data, [&](const FormReady &) {
|
v::match(update, [&](const FormReady &) {
|
||||||
performInitialSilentValidation();
|
performInitialSilentValidation();
|
||||||
if (!_initialSilentValidation) {
|
if (!_initialSilentValidation) {
|
||||||
showForm();
|
showForm();
|
||||||
|
@ -124,17 +114,12 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||||
_submitState = SubmitState::Validated;
|
_submitState = SubmitState::Validated;
|
||||||
panelSubmit();
|
panelSubmit();
|
||||||
}
|
}
|
||||||
|
}, [&](const PaymentMethodUpdate&) {
|
||||||
|
showForm();
|
||||||
}, [&](const VerificationNeeded &info) {
|
}, [&](const VerificationNeeded &info) {
|
||||||
if (_webviewWindow) {
|
if (!_panel->showWebview(info.url, false)) {
|
||||||
_webviewWindow->navigate(info.url);
|
File::OpenUrl(info.url);
|
||||||
} else {
|
panelCloseSure();
|
||||||
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
|
||||||
webviewDataPath(),
|
|
||||||
info.url,
|
|
||||||
panelDelegate());
|
|
||||||
if (!_webviewWindow->shown()) {
|
|
||||||
// #TODO payments errors
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [&](const PaymentFinished &result) {
|
}, [&](const PaymentFinished &result) {
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
|
@ -249,7 +234,7 @@ void CheckoutProcess::panelSubmit() {
|
||||||
|| _submitState == SubmitState::Finishing) {
|
|| _submitState == SubmitState::Finishing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &native = _form->nativePayment();
|
const auto &method = _form->paymentMethod();
|
||||||
const auto &invoice = _form->invoice();
|
const auto &invoice = _form->invoice();
|
||||||
const auto &options = _form->shippingOptions();
|
const auto &options = _form->shippingOptions();
|
||||||
if (!options.list.empty() && options.selectedId.isEmpty()) {
|
if (!options.list.empty() && options.selectedId.isEmpty()) {
|
||||||
|
@ -264,24 +249,12 @@ void CheckoutProcess::panelSubmit() {
|
||||||
_submitState = SubmitState::Validation;
|
_submitState = SubmitState::Validation;
|
||||||
_form->validateInformation(_form->savedInformation());
|
_form->validateInformation(_form->savedInformation());
|
||||||
return;
|
return;
|
||||||
} else if (native
|
} else if (!method.newCredentials && !method.savedCredentials) {
|
||||||
&& !native.newCredentials
|
|
||||||
&& !native.savedCredentials) {
|
|
||||||
editPaymentMethod();
|
editPaymentMethod();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_submitState = SubmitState::Finishing;
|
_submitState = SubmitState::Finishing;
|
||||||
if (!native) {
|
_form->submit();
|
||||||
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
|
||||||
webviewDataPath(),
|
|
||||||
_form->details().url,
|
|
||||||
panelDelegate());
|
|
||||||
if (!_webviewWindow->shown()) {
|
|
||||||
// #TODO payments errors
|
|
||||||
}
|
|
||||||
} else if (native.newCredentials) {
|
|
||||||
_form->send(native.newCredentials.data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
||||||
|
@ -321,19 +294,25 @@ void CheckoutProcess::panelWebviewMessage(const QJsonDocument &message) {
|
||||||
"Not an object received in payment credentials."));
|
"Not an object received in payment credentials."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto serializedCredentials = QJsonDocument(
|
crl::on_main(this, [=] {
|
||||||
credentials.toObject()
|
_form->setPaymentCredentials(NewCredentials{
|
||||||
).toJson(QJsonDocument::Compact);
|
.title = title,
|
||||||
|
.data = QJsonDocument(
|
||||||
_form->send(serializedCredentials);
|
credentials.toObject()
|
||||||
|
).toJson(QJsonDocument::Compact),
|
||||||
|
.saveOnServer = false, // #TODO payments save
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
||||||
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
if (Core::TryConvertUrlToLocal(uri) == uri) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
panelCloseSure();
|
crl::on_main(this, [=] {
|
||||||
App::wnd()->activate();
|
panelCloseSure();
|
||||||
|
App::wnd()->activate();
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,46 +325,7 @@ void CheckoutProcess::panelEditPaymentMethod() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelValidateCard(Ui::UncheckedCardDetails data) {
|
void CheckoutProcess::panelValidateCard(Ui::UncheckedCardDetails data) {
|
||||||
Expects(_form->nativePayment().type == NativePayment::Type::Stripe);
|
_form->validateCard(data);
|
||||||
Expects(!_form->nativePayment().stripePublishableKey.isEmpty());
|
|
||||||
|
|
||||||
if (_stripe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto configuration = Stripe::PaymentConfiguration{
|
|
||||||
.publishableKey = _form->nativePayment().stripePublishableKey,
|
|
||||||
.companyName = "Telegram",
|
|
||||||
};
|
|
||||||
_stripe = std::make_unique<Stripe::APIClient>(std::move(configuration));
|
|
||||||
auto card = Stripe::CardParams{
|
|
||||||
.number = data.number,
|
|
||||||
.expMonth = data.expireMonth,
|
|
||||||
.expYear = data.expireYear,
|
|
||||||
.cvc = data.cvc,
|
|
||||||
.name = data.cardholderName,
|
|
||||||
.addressZip = data.addressZip,
|
|
||||||
.addressCountry = data.addressCountry,
|
|
||||||
};
|
|
||||||
_stripe->createTokenWithCard(std::move(card), crl::guard(this, [=](
|
|
||||||
Stripe::Token token,
|
|
||||||
Stripe::Error error) {
|
|
||||||
_stripe = nullptr;
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
int a = 0;
|
|
||||||
// #TODO payment errors
|
|
||||||
} else {
|
|
||||||
_form->setPaymentCredentials({
|
|
||||||
.title = CardTitle(token.card()),
|
|
||||||
.data = QJsonDocument(QJsonObject{
|
|
||||||
{ "type", "card" },
|
|
||||||
{ "id", token.tokenId() },
|
|
||||||
}).toJson(QJsonDocument::Compact),
|
|
||||||
.saveOnServer = false,
|
|
||||||
});
|
|
||||||
showForm();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelEditShippingInformation() {
|
void CheckoutProcess::panelEditShippingInformation() {
|
||||||
|
@ -408,7 +348,7 @@ void CheckoutProcess::showForm() {
|
||||||
_panel->showForm(
|
_panel->showForm(
|
||||||
_form->invoice(),
|
_form->invoice(),
|
||||||
_form->savedInformation(),
|
_form->savedInformation(),
|
||||||
_form->nativePayment().details,
|
_form->paymentMethod().ui,
|
||||||
_form->shippingOptions());
|
_form->shippingOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +377,7 @@ void CheckoutProcess::chooseShippingOption() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::editPaymentMethod() {
|
void CheckoutProcess::editPaymentMethod() {
|
||||||
_panel->choosePaymentMethod(_form->nativePayment().details);
|
_panel->choosePaymentMethod(_form->paymentMethod().ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelChooseShippingOption() {
|
void CheckoutProcess::panelChooseShippingOption() {
|
||||||
|
@ -474,7 +414,7 @@ void CheckoutProcess::performInitialSilentValidation() {
|
||||||
_form->validateInformation(saved);
|
_form->validateInformation(saved);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CheckoutProcess::webviewDataPath() const {
|
QString CheckoutProcess::panelWebviewDataPath() {
|
||||||
return _session->domain().local().webviewDataPath();
|
return _session->domain().local().webviewDataPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
|
|
||||||
namespace Stripe {
|
|
||||||
class APIClient;
|
|
||||||
} // namespace Stripe
|
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
class Panel;
|
class Panel;
|
||||||
class WebviewWindow;
|
|
||||||
enum class InformationField;
|
enum class InformationField;
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
||||||
|
@ -67,7 +62,6 @@ private:
|
||||||
void editPaymentMethod();
|
void editPaymentMethod();
|
||||||
|
|
||||||
void performInitialSilentValidation();
|
void performInitialSilentValidation();
|
||||||
[[nodiscard]] QString webviewDataPath() const;
|
|
||||||
|
|
||||||
void panelRequestClose() override;
|
void panelRequestClose() override;
|
||||||
void panelCloseSure() override;
|
void panelCloseSure() override;
|
||||||
|
@ -87,11 +81,11 @@ private:
|
||||||
void panelValidateCard(Ui::UncheckedCardDetails data) override;
|
void panelValidateCard(Ui::UncheckedCardDetails data) override;
|
||||||
void panelShowBox(object_ptr<Ui::BoxContent> box) override;
|
void panelShowBox(object_ptr<Ui::BoxContent> box) override;
|
||||||
|
|
||||||
|
QString panelWebviewDataPath() override;
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
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;
|
||||||
std::unique_ptr<Ui::WebviewWindow> _webviewWindow;
|
|
||||||
std::unique_ptr<Stripe::APIClient> _stripe;
|
|
||||||
SubmitState _submitState = SubmitState::None;
|
SubmitState _submitState = SubmitState::None;
|
||||||
bool _initialSilentValidation = false;
|
bool _initialSilentValidation = false;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "stripe/stripe_api_client.h"
|
||||||
|
#include "stripe/stripe_error.h"
|
||||||
|
#include "stripe/stripe_token.h"
|
||||||
|
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
@ -67,6 +70,13 @@ namespace {
|
||||||
MTP_string(information.shippingAddress.postcode)));
|
MTP_string(information.shippingAddress.postcode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString CardTitle(const Stripe::Card &card) {
|
||||||
|
// Like server stores saved_credentials title.
|
||||||
|
return Stripe::CardBrandToString(card.brand()).toLower()
|
||||||
|
+ " *"
|
||||||
|
+ card.last4();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
||||||
|
@ -76,6 +86,8 @@ Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
||||||
requestForm();
|
requestForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Form::~Form() = default;
|
||||||
|
|
||||||
void Form::requestForm() {
|
void Form::requestForm() {
|
||||||
_api.request(MTPpayments_GetPaymentForm(
|
_api.request(MTPpayments_GetPaymentForm(
|
||||||
MTP_int(_msgId)
|
MTP_int(_msgId)
|
||||||
|
@ -84,7 +96,7 @@ void Form::requestForm() {
|
||||||
processForm(data);
|
processForm(data);
|
||||||
});
|
});
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_updates.fire({ Error{ Error::Type::Form, error.type() } });
|
_updates.fire(Error{ Error::Type::Form, error.type() });
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +117,8 @@ void Form::processForm(const MTPDpayments_paymentForm &data) {
|
||||||
processSavedCredentials(data);
|
processSavedCredentials(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fillNativePaymentInformation();
|
fillPaymentMethodInformation();
|
||||||
_updates.fire({ FormReady{} });
|
_updates.fire(FormReady{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::processInvoice(const MTPDinvoice &data) {
|
void Form::processInvoice(const MTPDinvoice &data) {
|
||||||
|
@ -156,30 +168,32 @@ void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
||||||
|
|
||||||
void Form::processSavedCredentials(
|
void Form::processSavedCredentials(
|
||||||
const MTPDpaymentSavedCredentialsCard &data) {
|
const MTPDpaymentSavedCredentialsCard &data) {
|
||||||
// #TODO payments not yet supported
|
// #TODO payments save
|
||||||
//_nativePayment.savedCredentials = SavedCredentials{
|
//_nativePayment.savedCredentials = SavedCredentials{
|
||||||
// .id = qs(data.vid()),
|
// .id = qs(data.vid()),
|
||||||
// .title = qs(data.vtitle()),
|
// .title = qs(data.vtitle()),
|
||||||
//};
|
//};
|
||||||
refreshNativePaymentDetails();
|
refreshPaymentMethodDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::refreshNativePaymentDetails() {
|
void Form::refreshPaymentMethodDetails() {
|
||||||
const auto &saved = _nativePayment.savedCredentials;
|
const auto &saved = _paymentMethod.savedCredentials;
|
||||||
const auto &entered = _nativePayment.newCredentials;
|
const auto &entered = _paymentMethod.newCredentials;
|
||||||
_nativePayment.details.credentialsTitle = entered
|
_paymentMethod.ui.title = entered ? entered.title : saved.title;
|
||||||
? entered.title
|
_paymentMethod.ui.ready = entered || saved;
|
||||||
: saved.title;
|
|
||||||
_nativePayment.details.ready = entered || saved;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::fillNativePaymentInformation() {
|
void Form::fillPaymentMethodInformation() {
|
||||||
auto saved = std::move(_nativePayment.savedCredentials);
|
_paymentMethod.native = NativePaymentMethod();
|
||||||
auto entered = std::move(_nativePayment.newCredentials);
|
_paymentMethod.ui.native = Ui::NativeMethodDetails();
|
||||||
_nativePayment = NativePayment();
|
_paymentMethod.ui.url = _details.url;
|
||||||
if (_details.nativeProvider != "stripe") {
|
if (_details.nativeProvider == "stripe") {
|
||||||
return;
|
fillStripeNativeMethod();
|
||||||
}
|
}
|
||||||
|
refreshPaymentMethodDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Form::fillStripeNativeMethod() {
|
||||||
auto error = QJsonParseError();
|
auto error = QJsonParseError();
|
||||||
auto document = QJsonDocument::fromJson(
|
auto document = QJsonDocument::fromJson(
|
||||||
_details.nativeParamsJson,
|
_details.nativeParamsJson,
|
||||||
|
@ -202,22 +216,22 @@ void Form::fillNativePaymentInformation() {
|
||||||
LOG(("Payment Error: No publishable_key in native_params."));
|
LOG(("Payment Error: No publishable_key in native_params."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_nativePayment = NativePayment{
|
_paymentMethod.native = NativePaymentMethod{
|
||||||
.type = NativePayment::Type::Stripe,
|
.data = StripePaymentMethod{
|
||||||
.stripePublishableKey = key,
|
.publishableKey = key,
|
||||||
.savedCredentials = std::move(saved),
|
|
||||||
.newCredentials = std::move(entered),
|
|
||||||
.details = Ui::NativePaymentDetails{
|
|
||||||
.supported = true,
|
|
||||||
.needCountry = value(u"need_country").toBool(),
|
|
||||||
.needZip = value(u"need_zip").toBool(),
|
|
||||||
.needCardholderName = value(u"need_cardholder_name").toBool(),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
refreshNativePaymentDetails();
|
_paymentMethod.ui.native = Ui::NativeMethodDetails{
|
||||||
|
.supported = true,
|
||||||
|
.needCountry = value(u"need_country").toBool(),
|
||||||
|
.needZip = value(u"need_zip").toBool(),
|
||||||
|
.needCardholderName = value(u"need_cardholder_name").toBool(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::send(const QByteArray &serializedCredentials) {
|
void Form::submit() {
|
||||||
|
Expects(!_paymentMethod.newCredentials.data.isEmpty()); // #TODO payments save
|
||||||
|
|
||||||
using Flag = MTPpayments_SendPaymentForm::Flag;
|
using Flag = MTPpayments_SendPaymentForm::Flag;
|
||||||
_api.request(MTPpayments_SendPaymentForm(
|
_api.request(MTPpayments_SendPaymentForm(
|
||||||
MTP_flags((_requestedInformationId.isEmpty()
|
MTP_flags((_requestedInformationId.isEmpty()
|
||||||
|
@ -231,15 +245,15 @@ void Form::send(const QByteArray &serializedCredentials) {
|
||||||
MTP_string(_shippingOptions.selectedId),
|
MTP_string(_shippingOptions.selectedId),
|
||||||
MTP_inputPaymentCredentials(
|
MTP_inputPaymentCredentials(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data)))
|
||||||
)).done([=](const MTPpayments_PaymentResult &result) {
|
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||||
_updates.fire({ PaymentFinished{ data.vupdates() } });
|
_updates.fire(PaymentFinished{ data.vupdates() });
|
||||||
}, [&](const MTPDpayments_paymentVerificationNeeded &data) {
|
}, [&](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||||
_updates.fire({ VerificationNeeded{ qs(data.vurl()) } });
|
_updates.fire(VerificationNeeded{ qs(data.vurl()) });
|
||||||
});
|
});
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_updates.fire({ Error{ Error::Type::Send, error.type() } });
|
_updates.fire(Error{ Error::Type::Send, error.type() });
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,18 +287,76 @@ void Form::validateInformation(const Ui::RequestedInformation &information) {
|
||||||
_shippingOptions.selectedId = _shippingOptions.list.front().id;
|
_shippingOptions.selectedId = _shippingOptions.list.front().id;
|
||||||
}
|
}
|
||||||
_savedInformation = _validatedInformation;
|
_savedInformation = _validatedInformation;
|
||||||
_updates.fire({ ValidateFinished{} });
|
_updates.fire(ValidateFinished{});
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_validateRequestId = 0;
|
_validateRequestId = 0;
|
||||||
_updates.fire({ Error{ Error::Type::Validate, error.type() } });
|
_updates.fire(Error{ Error::Type::Validate, error.type() });
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Form::validateCard(const Ui::UncheckedCardDetails &details) {
|
||||||
|
Expects(!v::is_null(_paymentMethod.native.data));
|
||||||
|
|
||||||
|
const auto &native = _paymentMethod.native.data;
|
||||||
|
if (const auto stripe = std::get_if<StripePaymentMethod>(&native)) {
|
||||||
|
validateCard(*stripe, details);
|
||||||
|
} else {
|
||||||
|
Unexpected("Native payment provider in Form::validateCard.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Form::validateCard(
|
||||||
|
const StripePaymentMethod &method,
|
||||||
|
const Ui::UncheckedCardDetails &details) {
|
||||||
|
Expects(!method.publishableKey.isEmpty());
|
||||||
|
|
||||||
|
if (_stripe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto configuration = Stripe::PaymentConfiguration{
|
||||||
|
.publishableKey = method.publishableKey,
|
||||||
|
.companyName = "Telegram",
|
||||||
|
};
|
||||||
|
_stripe = std::make_unique<Stripe::APIClient>(std::move(configuration));
|
||||||
|
auto card = Stripe::CardParams{
|
||||||
|
.number = details.number,
|
||||||
|
.expMonth = details.expireMonth,
|
||||||
|
.expYear = details.expireYear,
|
||||||
|
.cvc = details.cvc,
|
||||||
|
.name = details.cardholderName,
|
||||||
|
.addressZip = details.addressZip,
|
||||||
|
.addressCountry = details.addressCountry,
|
||||||
|
};
|
||||||
|
_stripe->createTokenWithCard(std::move(card), crl::guard(this, [=](
|
||||||
|
Stripe::Token token,
|
||||||
|
Stripe::Error error) {
|
||||||
|
_stripe = nullptr;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
LOG(("Stripe Error %1: %2 (%3)"
|
||||||
|
).arg(int(error.code())
|
||||||
|
).arg(error.description()
|
||||||
|
).arg(error.message()));
|
||||||
|
_updates.fire(Error{ Error::Type::Stripe, error.description() });
|
||||||
|
} else {
|
||||||
|
setPaymentCredentials({
|
||||||
|
.title = CardTitle(token.card()),
|
||||||
|
.data = QJsonDocument(QJsonObject{
|
||||||
|
{ "type", "card" },
|
||||||
|
{ "id", token.tokenId() },
|
||||||
|
}).toJson(QJsonDocument::Compact),
|
||||||
|
.saveOnServer = false, // #TODO payments save
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void Form::setPaymentCredentials(const NewCredentials &credentials) {
|
void Form::setPaymentCredentials(const NewCredentials &credentials) {
|
||||||
Expects(!credentials.empty());
|
Expects(!credentials.empty());
|
||||||
|
|
||||||
_nativePayment.newCredentials = credentials;
|
_paymentMethod.newCredentials = credentials;
|
||||||
refreshNativePaymentDetails();
|
refreshPaymentMethodDetails();
|
||||||
|
_updates.fire(PaymentMethodUpdate{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::setShippingOption(const QString &id) {
|
void Form::setShippingOption(const QString &id) {
|
||||||
|
|
|
@ -8,8 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "payments/ui/payments_panel_data.h"
|
#include "payments/ui/payments_panel_data.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
|
namespace Stripe {
|
||||||
|
class APIClient;
|
||||||
|
} // namespace Stripe
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -58,55 +63,64 @@ struct NewCredentials {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NativePayment {
|
struct StripePaymentMethod {
|
||||||
enum class Type {
|
QString publishableKey;
|
||||||
None,
|
};
|
||||||
Stripe,
|
|
||||||
};
|
struct NativePaymentMethod {
|
||||||
Type type = Type::None;
|
std::variant<
|
||||||
QString stripePublishableKey;
|
v::null_t,
|
||||||
SavedCredentials savedCredentials;
|
StripePaymentMethod> data;
|
||||||
NewCredentials newCredentials;
|
|
||||||
Ui::NativePaymentDetails details;
|
|
||||||
|
|
||||||
[[nodiscard]] bool valid() const {
|
[[nodiscard]] bool valid() const {
|
||||||
return (type != Type::None);
|
return !v::is_null(data);
|
||||||
}
|
}
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PaymentMethod {
|
||||||
|
NativePaymentMethod native;
|
||||||
|
SavedCredentials savedCredentials;
|
||||||
|
NewCredentials newCredentials;
|
||||||
|
Ui::PaymentMethodDetails ui;
|
||||||
|
};
|
||||||
|
|
||||||
struct FormReady {};
|
struct FormReady {};
|
||||||
struct ValidateFinished {};
|
struct ValidateFinished {};
|
||||||
struct Error {
|
struct PaymentMethodUpdate {};
|
||||||
enum class Type {
|
|
||||||
Form,
|
|
||||||
Validate,
|
|
||||||
Send,
|
|
||||||
};
|
|
||||||
Type type = Type::Form;
|
|
||||||
QString id;
|
|
||||||
};
|
|
||||||
struct VerificationNeeded {
|
struct VerificationNeeded {
|
||||||
QString url;
|
QString url;
|
||||||
};
|
};
|
||||||
struct PaymentFinished {
|
struct PaymentFinished {
|
||||||
MTPUpdates updates;
|
MTPUpdates updates;
|
||||||
};
|
};
|
||||||
|
struct Error {
|
||||||
struct FormUpdate {
|
enum class Type {
|
||||||
std::variant<
|
Form,
|
||||||
FormReady,
|
Validate,
|
||||||
VerificationNeeded,
|
Stripe,
|
||||||
ValidateFinished,
|
Send,
|
||||||
PaymentFinished,
|
};
|
||||||
Error> data;
|
Type type = Type::Form;
|
||||||
|
QString id;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Form final {
|
struct FormUpdate : std::variant<
|
||||||
|
FormReady,
|
||||||
|
ValidateFinished,
|
||||||
|
PaymentMethodUpdate,
|
||||||
|
VerificationNeeded,
|
||||||
|
PaymentFinished,
|
||||||
|
Error> {
|
||||||
|
using variant::variant;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Form final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
Form(not_null<Main::Session*> session, FullMsgId itemId);
|
Form(not_null<Main::Session*> session, FullMsgId itemId);
|
||||||
|
~Form();
|
||||||
|
|
||||||
[[nodiscard]] const Ui::Invoice &invoice() const {
|
[[nodiscard]] const Ui::Invoice &invoice() const {
|
||||||
return _invoice;
|
return _invoice;
|
||||||
|
@ -117,8 +131,8 @@ public:
|
||||||
[[nodiscard]] const Ui::RequestedInformation &savedInformation() const {
|
[[nodiscard]] const Ui::RequestedInformation &savedInformation() const {
|
||||||
return _savedInformation;
|
return _savedInformation;
|
||||||
}
|
}
|
||||||
[[nodiscard]] const NativePayment &nativePayment() const {
|
[[nodiscard]] const PaymentMethod &paymentMethod() const {
|
||||||
return _nativePayment;
|
return _paymentMethod;
|
||||||
}
|
}
|
||||||
[[nodiscard]] const Ui::ShippingOptions &shippingOptions() const {
|
[[nodiscard]] const Ui::ShippingOptions &shippingOptions() const {
|
||||||
return _shippingOptions;
|
return _shippingOptions;
|
||||||
|
@ -129,9 +143,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void validateInformation(const Ui::RequestedInformation &information);
|
void validateInformation(const Ui::RequestedInformation &information);
|
||||||
|
void validateCard(const Ui::UncheckedCardDetails &details);
|
||||||
void setPaymentCredentials(const NewCredentials &credentials);
|
void setPaymentCredentials(const NewCredentials &credentials);
|
||||||
void setShippingOption(const QString &id);
|
void setShippingOption(const QString &id);
|
||||||
void send(const QByteArray &serializedCredentials);
|
void submit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void requestForm();
|
void requestForm();
|
||||||
|
@ -142,8 +157,13 @@ private:
|
||||||
void processSavedCredentials(
|
void processSavedCredentials(
|
||||||
const MTPDpaymentSavedCredentialsCard &data);
|
const MTPDpaymentSavedCredentialsCard &data);
|
||||||
void processShippingOptions(const QVector<MTPShippingOption> &data);
|
void processShippingOptions(const QVector<MTPShippingOption> &data);
|
||||||
void fillNativePaymentInformation();
|
void fillPaymentMethodInformation();
|
||||||
void refreshNativePaymentDetails();
|
void fillStripeNativeMethod();
|
||||||
|
void refreshPaymentMethodDetails();
|
||||||
|
|
||||||
|
void validateCard(
|
||||||
|
const StripePaymentMethod &method,
|
||||||
|
const Ui::UncheckedCardDetails &details);
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
|
@ -152,11 +172,13 @@ private:
|
||||||
Ui::Invoice _invoice;
|
Ui::Invoice _invoice;
|
||||||
FormDetails _details;
|
FormDetails _details;
|
||||||
Ui::RequestedInformation _savedInformation;
|
Ui::RequestedInformation _savedInformation;
|
||||||
NativePayment _nativePayment;
|
PaymentMethod _paymentMethod;
|
||||||
|
|
||||||
Ui::RequestedInformation _validatedInformation;
|
Ui::RequestedInformation _validatedInformation;
|
||||||
mtpRequestId _validateRequestId = 0;
|
mtpRequestId _validateRequestId = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<Stripe::APIClient> _stripe;
|
||||||
|
|
||||||
Ui::ShippingOptions _shippingOptions;
|
Ui::ShippingOptions _shippingOptions;
|
||||||
QString _requestedInformationId;
|
QString _requestedInformationId;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Stripe {
|
namespace Stripe {
|
||||||
|
|
||||||
|
Error::Code Error::code() const {
|
||||||
|
return _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Error::description() const {
|
||||||
|
return _description;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Error::message() const {
|
||||||
|
return _message;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Error::parameter() const {
|
||||||
|
return _parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error Error::None() {
|
||||||
|
return Error(Code::None, {}, {}, {});
|
||||||
|
}
|
||||||
|
|
||||||
Error Error::DecodedObjectFromResponse(QJsonObject object) {
|
Error Error::DecodedObjectFromResponse(QJsonObject object) {
|
||||||
const auto entry = object.value("error");
|
const auto entry = object.value("error");
|
||||||
if (!entry.isObject()) {
|
if (!entry.isObject()) {
|
||||||
|
@ -80,4 +100,8 @@ Error Error::DecodedObjectFromResponse(QJsonObject object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Error::empty() const {
|
||||||
|
return (_code == Code::None);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Stripe
|
} // namespace Stripe
|
||||||
|
|
|
@ -42,14 +42,15 @@ public:
|
||||||
, _parameter(parameter) {
|
, _parameter(parameter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static Error None() {
|
[[nodiscard]] Code code() const;
|
||||||
return Error(Code::None, QString(), QString());
|
[[nodiscard]] QString description() const;
|
||||||
}
|
[[nodiscard]] QString message() const;
|
||||||
|
[[nodiscard]] QString parameter() const;
|
||||||
|
|
||||||
|
[[nodiscard]] static Error None();
|
||||||
[[nodiscard]] static Error DecodedObjectFromResponse(QJsonObject object);
|
[[nodiscard]] static Error DecodedObjectFromResponse(QJsonObject object);
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
[[nodiscard]] bool empty() const;
|
||||||
return (_code == Code::None);
|
|
||||||
}
|
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return !empty();
|
return !empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ constexpr auto kMaxPostcodeSize = 10;
|
||||||
|
|
||||||
EditCard::EditCard(
|
EditCard::EditCard(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field,
|
CardField field,
|
||||||
not_null<PanelDelegate*> delegate)
|
not_null<PanelDelegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
|
|
|
@ -31,7 +31,7 @@ class EditCard final : public RpWidget {
|
||||||
public:
|
public:
|
||||||
EditCard(
|
EditCard(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field,
|
CardField field,
|
||||||
not_null<PanelDelegate*> delegate);
|
not_null<PanelDelegate*> delegate);
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ private:
|
||||||
[[nodiscard]] UncheckedCardDetails collect() const;
|
[[nodiscard]] UncheckedCardDetails collect() const;
|
||||||
|
|
||||||
const not_null<PanelDelegate*> _delegate;
|
const not_null<PanelDelegate*> _delegate;
|
||||||
NativePaymentDetails _native;
|
NativeMethodDetails _native;
|
||||||
|
|
||||||
object_ptr<ScrollArea> _scroll;
|
object_ptr<ScrollArea> _scroll;
|
||||||
object_ptr<FadeShadow> _topShadow;
|
object_ptr<FadeShadow> _topShadow;
|
||||||
|
|
|
@ -30,12 +30,12 @@ FormSummary::FormSummary(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
const RequestedInformation ¤t,
|
const RequestedInformation ¤t,
|
||||||
const NativePaymentDetails &native,
|
const PaymentMethodDetails &method,
|
||||||
const ShippingOptions &options,
|
const ShippingOptions &options,
|
||||||
not_null<PanelDelegate*> delegate)
|
not_null<PanelDelegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
, _invoice(invoice)
|
, _invoice(invoice)
|
||||||
, _native(native)
|
, _method(method)
|
||||||
, _options(options)
|
, _options(options)
|
||||||
, _information(current)
|
, _information(current)
|
||||||
, _scroll(this, st::passportPanelScroll)
|
, _scroll(this, st::passportPanelScroll)
|
||||||
|
@ -136,20 +136,18 @@ not_null<Ui::RpWidget*> FormSummary::setupContent() {
|
||||||
st::passportFormDividerHeight),
|
st::passportFormDividerHeight),
|
||||||
{ 0, 0, 0, st::passportFormHeaderPadding.top() });
|
{ 0, 0, 0, st::passportFormHeaderPadding.top() });
|
||||||
|
|
||||||
if (_native.supported) {
|
const auto method = inner->add(object_ptr<FormRow>(inner));
|
||||||
const auto method = inner->add(object_ptr<FormRow>(inner));
|
method->addClickHandler([=] {
|
||||||
method->addClickHandler([=] {
|
_delegate->panelEditPaymentMethod();
|
||||||
_delegate->panelEditPaymentMethod();
|
});
|
||||||
});
|
method->updateContent(
|
||||||
method->updateContent(
|
tr::lng_payments_payment_method(tr::now),
|
||||||
tr::lng_payments_payment_method(tr::now),
|
(_method.ready
|
||||||
(_native.ready
|
? _method.title
|
||||||
? _native.credentialsTitle
|
: tr::lng_payments_payment_method_ph(tr::now)),
|
||||||
: tr::lng_payments_payment_method_ph(tr::now)),
|
_method.ready,
|
||||||
_native.ready,
|
false,
|
||||||
false,
|
anim::type::instant);
|
||||||
anim::type::instant);
|
|
||||||
}
|
|
||||||
if (_invoice.isShippingAddressRequested) {
|
if (_invoice.isShippingAddressRequested) {
|
||||||
const auto info = inner->add(object_ptr<FormRow>(inner));
|
const auto info = inner->add(object_ptr<FormRow>(inner));
|
||||||
info->addClickHandler([=] {
|
info->addClickHandler([=] {
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
const RequestedInformation ¤t,
|
const RequestedInformation ¤t,
|
||||||
const NativePaymentDetails &native,
|
const PaymentMethodDetails &method,
|
||||||
const ShippingOptions &options,
|
const ShippingOptions &options,
|
||||||
not_null<PanelDelegate*> delegate);
|
not_null<PanelDelegate*> delegate);
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ private:
|
||||||
|
|
||||||
const not_null<PanelDelegate*> _delegate;
|
const not_null<PanelDelegate*> _delegate;
|
||||||
Invoice _invoice;
|
Invoice _invoice;
|
||||||
NativePaymentDetails _native;
|
PaymentMethodDetails _method;
|
||||||
ShippingOptions _options;
|
ShippingOptions _options;
|
||||||
RequestedInformation _information;
|
RequestedInformation _information;
|
||||||
object_ptr<ScrollArea> _scroll;
|
object_ptr<ScrollArea> _scroll;
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/separate_panel.h"
|
#include "ui/widgets/separate_panel.h"
|
||||||
#include "ui/boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "webview/webview_embed.h"
|
||||||
#include "styles/style_payments.h"
|
#include "styles/style_payments.h"
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
|
||||||
|
@ -37,7 +38,10 @@ Panel::Panel(not_null<PanelDelegate*> delegate)
|
||||||
}, _widget->lifetime());
|
}, _widget->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
Panel::~Panel() = default;
|
Panel::~Panel() {
|
||||||
|
// Destroy _widget before _webview.
|
||||||
|
_widget = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::requestActivate() {
|
void Panel::requestActivate() {
|
||||||
_widget->showAndActivate();
|
_widget->showAndActivate();
|
||||||
|
@ -46,14 +50,14 @@ void Panel::requestActivate() {
|
||||||
void Panel::showForm(
|
void Panel::showForm(
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
const RequestedInformation ¤t,
|
const RequestedInformation ¤t,
|
||||||
const NativePaymentDetails &native,
|
const PaymentMethodDetails &method,
|
||||||
const ShippingOptions &options) {
|
const ShippingOptions &options) {
|
||||||
_widget->showInner(
|
_widget->showInner(
|
||||||
base::make_unique_q<FormSummary>(
|
base::make_unique_q<FormSummary>(
|
||||||
_widget.get(),
|
_widget.get(),
|
||||||
invoice,
|
invoice,
|
||||||
current,
|
current,
|
||||||
native,
|
method,
|
||||||
options,
|
options,
|
||||||
_delegate));
|
_delegate));
|
||||||
_widget->setBackAllowed(false);
|
_widget->setBackAllowed(false);
|
||||||
|
@ -113,23 +117,84 @@ void Panel::chooseShippingOption(const ShippingOptions &options) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::choosePaymentMethod(const NativePaymentDetails &native) {
|
void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
|
||||||
Expects(native.supported);
|
if (method.native.supported) {
|
||||||
|
showEditCard(method.native, CardField::Number);
|
||||||
|
} else if (!showWebview(method.url, true)) {
|
||||||
|
// #TODO payments errors not supported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!native.ready) {
|
bool Panel::showWebview(const QString &url, bool allowBack) {
|
||||||
showEditCard(native, CardField::Number);
|
if (!_webview && !createWebview()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_webview->navigate(url);
|
||||||
|
_widget->setBackAllowed(allowBack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Panel::createWebview() {
|
||||||
|
auto container = base::make_unique_q<RpWidget>(_widget.get());
|
||||||
|
|
||||||
|
container->setGeometry(_widget->innerGeometry());
|
||||||
|
container->show();
|
||||||
|
|
||||||
|
_webview = std::make_unique<Webview::Window>(
|
||||||
|
container.get(),
|
||||||
|
Webview::WindowConfig{
|
||||||
|
.userDataPath = _delegate->panelWebviewDataPath(),
|
||||||
|
});
|
||||||
|
const auto raw = _webview.get();
|
||||||
|
QObject::connect(container.get(), &QObject::destroyed, [=] {
|
||||||
|
if (_webview.get() == raw) {
|
||||||
|
_webview = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!raw->widget()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
container->geometryValue(
|
||||||
|
) | rpl::start_with_next([=](QRect geometry) {
|
||||||
|
raw->widget()->setGeometry(geometry);
|
||||||
|
}, container->lifetime());
|
||||||
|
|
||||||
|
raw->setMessageHandler([=](const QJsonDocument &message) {
|
||||||
|
_delegate->panelWebviewMessage(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
raw->setNavigationHandler([=](const QString &uri) {
|
||||||
|
return _delegate->panelWebviewNavigationAttempt(uri);
|
||||||
|
});
|
||||||
|
|
||||||
|
raw->init(R"(
|
||||||
|
window.TelegramWebviewProxy = {
|
||||||
|
postEvent: function(eventType, eventData) {
|
||||||
|
if (window.external && window.external.invoke) {
|
||||||
|
window.external.invoke(JSON.stringify([eventType, eventData]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};)");
|
||||||
|
|
||||||
|
_widget->showInner(std::move(container));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::choosePaymentMethod(const PaymentMethodDetails &method) {
|
||||||
|
if (!method.ready) {
|
||||||
|
showEditPaymentMethod(method);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto title = native.credentialsTitle;
|
|
||||||
showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
const auto save = [=](int option) {
|
const auto save = [=](int option) {
|
||||||
if (option) {
|
if (option) {
|
||||||
showEditCard(native, CardField::Number);
|
showEditPaymentMethod(method);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
SingleChoiceBox(box, {
|
SingleChoiceBox(box, {
|
||||||
.title = tr::lng_payments_payment_method(),
|
.title = tr::lng_payments_payment_method(),
|
||||||
.options = { native.credentialsTitle, "New Card..." }, // #TODO payments lang
|
.options = { method.title, tr::lng_payments_new_card(tr::now) },
|
||||||
.initialSelection = 0,
|
.initialSelection = 0,
|
||||||
.callback = save,
|
.callback = save,
|
||||||
});
|
});
|
||||||
|
@ -137,8 +202,10 @@ void Panel::choosePaymentMethod(const NativePaymentDetails &native) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::showEditCard(
|
void Panel::showEditCard(
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field) {
|
CardField field) {
|
||||||
|
Expects(native.supported);
|
||||||
|
|
||||||
auto edit = base::make_unique_q<EditCard>(
|
auto edit = base::make_unique_q<EditCard>(
|
||||||
_widget.get(),
|
_widget.get(),
|
||||||
native,
|
native,
|
||||||
|
@ -151,7 +218,7 @@ void Panel::showEditCard(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::showCardError(
|
void Panel::showCardError(
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field) {
|
CardField field) {
|
||||||
if (_weakEditCard) {
|
if (_weakEditCard) {
|
||||||
_weakEditCard->showError(field);
|
_weakEditCard->showError(field);
|
||||||
|
|
|
@ -14,6 +14,10 @@ class SeparatePanel;
|
||||||
class BoxContent;
|
class BoxContent;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Webview {
|
||||||
|
class Window;
|
||||||
|
} // namespace Webview
|
||||||
|
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
|
|
||||||
using namespace ::Ui;
|
using namespace ::Ui;
|
||||||
|
@ -26,7 +30,8 @@ enum class InformationField;
|
||||||
enum class CardField;
|
enum class CardField;
|
||||||
class EditInformation;
|
class EditInformation;
|
||||||
class EditCard;
|
class EditCard;
|
||||||
struct NativePaymentDetails;
|
struct PaymentMethodDetails;
|
||||||
|
struct NativeMethodDetails;
|
||||||
|
|
||||||
class Panel final {
|
class Panel final {
|
||||||
public:
|
public:
|
||||||
|
@ -38,7 +43,7 @@ public:
|
||||||
void showForm(
|
void showForm(
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
const RequestedInformation ¤t,
|
const RequestedInformation ¤t,
|
||||||
const NativePaymentDetails &native,
|
const PaymentMethodDetails &method,
|
||||||
const ShippingOptions &options);
|
const ShippingOptions &options);
|
||||||
void showEditInformation(
|
void showEditInformation(
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
|
@ -48,14 +53,17 @@ public:
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
const RequestedInformation ¤t,
|
const RequestedInformation ¤t,
|
||||||
InformationField field);
|
InformationField field);
|
||||||
|
void showEditPaymentMethod(const PaymentMethodDetails &method);
|
||||||
void showEditCard(
|
void showEditCard(
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field);
|
CardField field);
|
||||||
void showCardError(
|
void showCardError(
|
||||||
const NativePaymentDetails &native,
|
const NativeMethodDetails &native,
|
||||||
CardField field);
|
CardField field);
|
||||||
void chooseShippingOption(const ShippingOptions &options);
|
void chooseShippingOption(const ShippingOptions &options);
|
||||||
void choosePaymentMethod(const NativePaymentDetails &native);
|
void choosePaymentMethod(const PaymentMethodDetails &method);
|
||||||
|
|
||||||
|
bool showWebview(const QString &url, bool allowBack);
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> backRequests() const;
|
[[nodiscard]] rpl::producer<> backRequests() const;
|
||||||
|
|
||||||
|
@ -65,8 +73,11 @@ public:
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool createWebview();
|
||||||
|
|
||||||
const not_null<PanelDelegate*> _delegate;
|
const not_null<PanelDelegate*> _delegate;
|
||||||
std::unique_ptr<SeparatePanel> _widget;
|
std::unique_ptr<SeparatePanel> _widget;
|
||||||
|
std::unique_ptr<Webview::Window> _webview;
|
||||||
QPointer<EditInformation> _weakEditInformation;
|
QPointer<EditInformation> _weakEditInformation;
|
||||||
QPointer<EditCard> _weakEditCard;
|
QPointer<EditCard> _weakEditCard;
|
||||||
|
|
||||||
|
|
|
@ -115,15 +115,20 @@ enum class InformationField {
|
||||||
Phone,
|
Phone,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NativePaymentDetails {
|
struct NativeMethodDetails {
|
||||||
QString credentialsTitle;
|
|
||||||
bool ready = false;
|
|
||||||
bool supported = false;
|
bool supported = false;
|
||||||
bool needCountry = false;
|
bool needCountry = false;
|
||||||
bool needZip = false;
|
bool needZip = false;
|
||||||
bool needCardholderName = false;
|
bool needCardholderName = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PaymentMethodDetails {
|
||||||
|
QString title;
|
||||||
|
NativeMethodDetails native;
|
||||||
|
QString url;
|
||||||
|
bool ready = false;
|
||||||
|
};
|
||||||
|
|
||||||
enum class CardField {
|
enum class CardField {
|
||||||
Number,
|
Number,
|
||||||
CVC,
|
CVC,
|
||||||
|
|
|
@ -42,6 +42,8 @@ public:
|
||||||
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
||||||
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;
|
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;
|
||||||
virtual void panelShowBox(object_ptr<BoxContent> box) = 0;
|
virtual void panelShowBox(object_ptr<BoxContent> box) = 0;
|
||||||
|
|
||||||
|
virtual QString panelWebviewDataPath() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
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/ui/payments_webview.h"
|
|
||||||
|
|
||||||
#include "payments/ui/payments_panel_delegate.h"
|
|
||||||
#include "webview/webview_embed.h"
|
|
||||||
#include "webview/webview_interface.h"
|
|
||||||
#include "ui/widgets/window.h"
|
|
||||||
#include "ui/toast/toast.h"
|
|
||||||
#include "lang/lang_keys.h"
|
|
||||||
|
|
||||||
namespace Payments::Ui {
|
|
||||||
|
|
||||||
using namespace ::Ui;
|
|
||||||
|
|
||||||
class PanelDelegate;
|
|
||||||
|
|
||||||
WebviewWindow::WebviewWindow(
|
|
||||||
const QString &userDataPath,
|
|
||||||
const QString &url,
|
|
||||||
not_null<PanelDelegate*> delegate) {
|
|
||||||
if (!url.startsWith("https://", Qt::CaseInsensitive)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto window = &_window;
|
|
||||||
|
|
||||||
window->setGeometry({
|
|
||||||
style::ConvertScale(100),
|
|
||||||
style::ConvertScale(100),
|
|
||||||
style::ConvertScale(640),
|
|
||||||
style::ConvertScale(480)
|
|
||||||
});
|
|
||||||
window->show();
|
|
||||||
|
|
||||||
window->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
|
||||||
if (e->type() == QEvent::Close) {
|
|
||||||
delegate->panelCloseSure();
|
|
||||||
}
|
|
||||||
}, window->lifetime());
|
|
||||||
|
|
||||||
const auto body = window->body();
|
|
||||||
body->paintRequest(
|
|
||||||
) | rpl::start_with_next([=](QRect clip) {
|
|
||||||
QPainter(body).fillRect(clip, st::windowBg);
|
|
||||||
}, body->lifetime());
|
|
||||||
|
|
||||||
const auto path =
|
|
||||||
_webview = Ui::CreateChild<Webview::Window>(
|
|
||||||
window,
|
|
||||||
window,
|
|
||||||
Webview::WindowConfig{ .userDataPath = userDataPath });
|
|
||||||
if (!_webview->widget()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
body->geometryValue(
|
|
||||||
) | rpl::start_with_next([=](QRect geometry) {
|
|
||||||
_webview->widget()->setGeometry(geometry);
|
|
||||||
}, body->lifetime());
|
|
||||||
|
|
||||||
_webview->setMessageHandler([=](const QJsonDocument &message) {
|
|
||||||
delegate->panelWebviewMessage(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
_webview->setNavigationHandler([=](const QString &uri) {
|
|
||||||
return delegate->panelWebviewNavigationAttempt(uri);
|
|
||||||
});
|
|
||||||
|
|
||||||
_webview->init(R"(
|
|
||||||
window.TelegramWebviewProxy = {
|
|
||||||
postEvent: function(eventType, eventData) {
|
|
||||||
if (window.external && window.external.invoke) {
|
|
||||||
window.external.invoke(JSON.stringify([eventType, eventData]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};)");
|
|
||||||
|
|
||||||
navigate(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool WebviewWindow::shown() const {
|
|
||||||
return _webview && _webview->widget();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebviewWindow::navigate(const QString &url) {
|
|
||||||
if (shown()) {
|
|
||||||
_webview->navigate(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ui/widgets/window.h"
|
|
||||||
|
|
||||||
namespace Webview {
|
|
||||||
class Window;
|
|
||||||
} // namespace Webview
|
|
||||||
|
|
||||||
namespace Payments::Ui {
|
|
||||||
|
|
||||||
using namespace ::Ui;
|
|
||||||
|
|
||||||
class PanelDelegate;
|
|
||||||
|
|
||||||
class WebviewWindow final {
|
|
||||||
public:
|
|
||||||
WebviewWindow(
|
|
||||||
const QString &userDataPath,
|
|
||||||
const QString &url,
|
|
||||||
not_null<PanelDelegate*> delegate);
|
|
||||||
|
|
||||||
[[nodiscard]] bool shown() const;
|
|
||||||
void navigate(const QString &url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::Window _window;
|
|
||||||
Webview::Window *_webview = nullptr;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
|
|
@ -344,6 +344,10 @@ void SeparatePanel::setInnerSize(QSize size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect SeparatePanel::innerGeometry() const {
|
||||||
|
return _body->geometry();
|
||||||
|
}
|
||||||
|
|
||||||
void SeparatePanel::initGeometry(QSize size) {
|
void SeparatePanel::initGeometry(QSize size) {
|
||||||
const auto active = QApplication::activeWindow();
|
const auto active = QApplication::activeWindow();
|
||||||
const auto center = !active
|
const auto center = !active
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
|
|
||||||
void setTitle(rpl::producer<QString> title);
|
void setTitle(rpl::producer<QString> title);
|
||||||
void setInnerSize(QSize size);
|
void setInnerSize(QSize size);
|
||||||
|
[[nodiscard]] QRect innerGeometry() const;
|
||||||
|
|
||||||
void setHideOnDeactivate(bool hideOnDeactivate);
|
void setHideOnDeactivate(bool hideOnDeactivate);
|
||||||
void showAndActivate();
|
void showAndActivate();
|
||||||
|
@ -41,9 +42,9 @@ public:
|
||||||
void showToast(const TextWithEntities &text);
|
void showToast(const TextWithEntities &text);
|
||||||
void destroyLayer();
|
void destroyLayer();
|
||||||
|
|
||||||
rpl::producer<> backRequests() const;
|
[[nodiscard]] rpl::producer<> backRequests() const;
|
||||||
rpl::producer<> closeRequests() const;
|
[[nodiscard]] rpl::producer<> closeRequests() const;
|
||||||
rpl::producer<> closeEvents() const;
|
[[nodiscard]] rpl::producer<> closeEvents() const;
|
||||||
void setBackAllowed(bool allowed);
|
void setBackAllowed(bool allowed);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -79,8 +79,6 @@ PRIVATE
|
||||||
payments/ui/payments_panel.h
|
payments/ui/payments_panel.h
|
||||||
payments/ui/payments_panel_data.h
|
payments/ui/payments_panel_data.h
|
||||||
payments/ui/payments_panel_delegate.h
|
payments/ui/payments_panel_delegate.h
|
||||||
payments/ui/payments_webview.cpp
|
|
||||||
payments/ui/payments_webview.h
|
|
||||||
|
|
||||||
platform/mac/file_bookmark_mac.h
|
platform/mac/file_bookmark_mac.h
|
||||||
platform/mac/file_bookmark_mac.mm
|
platform/mac/file_bookmark_mac.mm
|
||||||
|
|
Loading…
Reference in a new issue