Request terms acceptance for recurring payments.

This commit is contained in:
John Preston 2022-05-30 17:08:26 +04:00
parent 8e6825771e
commit b7259615a7
11 changed files with 126 additions and 5 deletions

View file

@ -2353,6 +2353,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
"lng_payments_already_paid" = "You have already paid for this item.";
"lng_payments_terms_title" = "Terms of Service";
"lng_payments_terms_text" = "Subscribe and accept terms of service of {bot}?";
"lng_payments_terms_agree" = "I agree to {link}";
"lng_payments_terms_link" = "Terms of Service";
"lng_payments_terms_accept" = "Accept";
"lng_call_status_incoming" = "is calling you...";
"lng_call_status_connecting" = "connecting...";
"lng_call_status_exchanging" = "exchanging encryption keys...";

View file

@ -173,8 +173,8 @@ messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageActi
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#96163f56 flags:# currency:string total_amount:long invoice_slug:flags.0?string = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction;
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
@ -826,7 +826,7 @@ dataJSON#7d748d04 data:string = DataJSON;
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
invoice#cd886e0 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> = Invoice;
invoice#3e85a91b flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> recurring_terms_url:flags.9?string = Invoice;
paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
@ -1345,7 +1345,7 @@ attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;
attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;
@ -1386,6 +1386,8 @@ payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio;
help.premiumPromo#3f7ae6ee status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> = help.PremiumPromo;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1729,6 +1731,7 @@ help.getPromoData#c0977421 = help.PromoData;
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool;
help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
help.getPremiumPromo#b81b93d4 = help.PremiumPromo;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@ -1792,6 +1795,7 @@ payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
payments.assignAppStoreTransaction#6299a12f transaction_id:string = Updates;
payments.assignPlayMarketTransaction#4faa4aed purchase_token:string = Updates;
payments.requestRecurringPayment#146e958d user_id:InputUser recurring_init_charge:string invoice_media:InputMedia = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View file

@ -533,6 +533,10 @@ void CheckoutProcess::panelSubmit() {
_form->validateInformation(_form->information());
} else if (!method.newCredentials && !method.savedCredentials) {
editPaymentMethod();
} else if (invoice.isRecurring && !_form->details().termsAccepted) {
_panel->requestTermsAcceptance(
_form->details().termsBotUsername,
invoice.recurringTermsUrl);
} else {
RegisterPaymentStart(this, { _form->invoice().cover.title });
_submitState = SubmitState::Finishing;
@ -545,6 +549,11 @@ void CheckoutProcess::panelTrustAndSubmit() {
panelSubmit();
}
void CheckoutProcess::panelAcceptTermsAndSubmit() {
_form->acceptTerms();
panelSubmit();
}
void CheckoutProcess::panelWebviewMessage(
const QJsonDocument &message,
bool saveInformation) {

View file

@ -123,6 +123,7 @@ private:
void panelCloseSure() override;
void panelSubmit() override;
void panelTrustAndSubmit() override;
void panelAcceptTermsAndSubmit() override;
void panelWebviewMessage(
const QJsonDocument &message,
bool saveInformation) override;

View file

@ -368,9 +368,13 @@ void Form::processInvoice(const MTPDinvoice &data) {
.isPhoneRequested = data.is_phone_requested(),
.isEmailRequested = data.is_email_requested(),
.isShippingAddressRequested = data.is_shipping_address_requested(),
.isRecurring = data.is_recurring(),
.isFlexible = data.is_flexible(),
.isTest = data.is_test(),
.recurringTermsUrl = qs(
data.vrecurring_terms_url().value_or_empty()),
.phoneSentToProvider = data.is_phone_to_provider(),
.emailSentToProvider = data.is_email_to_provider(),
};
@ -403,6 +407,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
if (const auto botId = _details.botId) {
if (const auto bot = _session->data().userLoaded(botId)) {
_invoice.cover.seller = bot->name;
_details.termsBotUsername = bot->username;
}
}
if (const auto providerId = _details.providerId) {
@ -938,6 +943,10 @@ void Form::setTips(int64 value) {
_invoice.tipsSelected = std::min(value, _invoice.tipsMax);
}
void Form::acceptTerms() {
_details.termsAccepted = true;
}
void Form::trustBot() {
_session->local().markBotTrustedPayment(_details.botId);
}

View file

@ -42,11 +42,13 @@ struct FormDetails {
uint64 formId = 0;
QString url;
QString nativeProvider;
QString termsBotUsername;
QByteArray nativeParamsJson;
UserId botId = 0;
UserId providerId = 0;
bool canSaveCredentials = false;
bool passwordMissing = false;
bool termsAccepted = false;
[[nodiscard]] bool valid() const {
return !url.isEmpty();
@ -223,6 +225,7 @@ public:
void setHasPassword(bool has);
void setShippingOption(const QString &id);
void setTips(int64 value);
void acceptTerms();
void trustBot();
void submit();
void submit(const Core::CloudPasswordResult &result);

View file

@ -644,6 +644,91 @@ void Panel::showWarning(const QString &bot, const QString &provider) {
}));
}
void Panel::requestTermsAcceptance(
const QString &username,
const QString &url) {
showBox(Box([=](not_null<GenericBox*> box) {
box->setTitle(tr::lng_payments_terms_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_payments_terms_text(
lt_bot,
rpl::single(Ui::Text::Bold('@' + username)),
Ui::Text::WithEntities),
st::boxLabel));
const auto update = std::make_shared<Fn<void()>>();
auto checkView = std::make_unique<Ui::CheckView>(
st::defaultCheck,
false,
[=] { if (*update) { (*update)(); } });
const auto check = checkView.get();
const auto row = box->addRow(
object_ptr<Ui::Checkbox>(
box.get(),
tr::lng_payments_terms_agree(
lt_link,
rpl::single(Ui::Text::Link(
tr::lng_payments_terms_link(tr::now),
url)),
Ui::Text::WithEntities),
st::defaultBoxCheckbox,
std::move(checkView)),
{
st::boxRowPadding.left(),
st::boxRowPadding.left(),
st::boxRowPadding.right(),
st::defaultBoxCheckbox.margin.bottom(),
});
(*update) = [=] { row->update(); };
struct State {
bool error = false;
Ui::Animations::Simple errorAnimation;
};
const auto state = box->lifetime().make_state<State>();
const auto showError = [=] {
const auto callback = [=] {
const auto error = state->errorAnimation.value(
state->error ? 1. : 0.);
if (error == 0.) {
check->setUntoggledOverride(std::nullopt);
} else {
const auto color = anim::color(
st::defaultCheck.untoggledFg,
st::boxTextFgError,
error);
check->setUntoggledOverride(color);
}
};
state->error = true;
state->errorAnimation.stop();
state->errorAnimation.start(
callback,
0.,
1.,
st::defaultCheck.duration);
};
row->checkedChanges(
) | rpl::filter([=](bool checked) {
return checked;
}) | rpl::start_with_next([=] {
state->error = false;
check->setUntoggledOverride(std::nullopt);
}, row->lifetime());
box->addButton(tr::lng_payments_terms_accept(), [=] {
if (check->checked()) {
_delegate->panelAcceptTermsAndSubmit();
box->closeBox();
} else {
showError();
}
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}));
}
void Panel::showEditCard(
const NativeMethodDetails &native,
CardField field) {

View file

@ -72,6 +72,7 @@ public:
void askSetPassword();
void showCloseConfirm();
void showWarning(const QString &bot, const QString &provider);
void requestTermsAcceptance(const QString &username, const QString &url);
bool showWebview(
const QString &url,

View file

@ -49,10 +49,12 @@ struct Invoice {
bool isPhoneRequested = false;
bool isEmailRequested = false;
bool isShippingAddressRequested = false;
bool isRecurring = false;
bool isFlexible = false;
bool isTest = false;
QString provider;
QString recurringTermsUrl;
bool phoneSentToProvider = false;
bool emailSentToProvider = false;

View file

@ -29,6 +29,7 @@ public:
virtual void panelCloseSure() = 0;
virtual void panelSubmit() = 0;
virtual void panelTrustAndSubmit() = 0;
virtual void panelAcceptTermsAndSubmit() = 0;
virtual void panelWebviewMessage(
const QJsonDocument &message,
bool saveInformation) = 0;

@ -1 +1 @@
Subproject commit 676d8697c6c704c6c5494f03f0bc78d006052768
Subproject commit e1ec6a38beae8f90202b8bfa5a247e8f286b810f