1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/Payments.h"
8
9 #include "td/telegram/AccessRights.h"
10 #include "td/telegram/ContactsManager.h"
11 #include "td/telegram/files/FileManager.h"
12 #include "td/telegram/files/FileType.h"
13 #include "td/telegram/Global.h"
14 #include "td/telegram/MessagesManager.h"
15 #include "td/telegram/misc.h"
16 #include "td/telegram/PasswordManager.h"
17 #include "td/telegram/ServerMessageId.h"
18 #include "td/telegram/Td.h"
19 #include "td/telegram/UpdatesManager.h"
20
21 #include "td/utils/algorithm.h"
22 #include "td/utils/buffer.h"
23 #include "td/utils/common.h"
24 #include "td/utils/format.h"
25 #include "td/utils/HttpUrl.h"
26 #include "td/utils/JsonBuilder.h"
27 #include "td/utils/logging.h"
28 #include "td/utils/MimeType.h"
29 #include "td/utils/PathView.h"
30 #include "td/utils/Status.h"
31
32 namespace td {
33
34 class SetBotShippingAnswerQuery final : public Td::ResultHandler {
35 Promise<Unit> promise_;
36
37 public:
SetBotShippingAnswerQuery(Promise<Unit> && promise)38 explicit SetBotShippingAnswerQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
39 }
40
send(int64 shipping_query_id,const string & error_message,vector<tl_object_ptr<telegram_api::shippingOption>> && shipping_options)41 void send(int64 shipping_query_id, const string &error_message,
42 vector<tl_object_ptr<telegram_api::shippingOption>> &&shipping_options) {
43 int32 flags = 0;
44 if (!error_message.empty()) {
45 flags |= telegram_api::messages_setBotShippingResults::ERROR_MASK;
46 }
47 if (!shipping_options.empty()) {
48 flags |= telegram_api::messages_setBotShippingResults::SHIPPING_OPTIONS_MASK;
49 }
50 send_query(G()->net_query_creator().create(telegram_api::messages_setBotShippingResults(
51 flags, shipping_query_id, error_message, std::move(shipping_options))));
52 }
53
on_result(BufferSlice packet)54 void on_result(BufferSlice packet) final {
55 auto result_ptr = fetch_result<telegram_api::messages_setBotShippingResults>(packet);
56 if (result_ptr.is_error()) {
57 return on_error(result_ptr.move_as_error());
58 }
59
60 bool result = result_ptr.ok();
61 if (!result) {
62 LOG(INFO) << "Sending answer to a shipping query has failed";
63 }
64 promise_.set_value(Unit());
65 }
66
on_error(Status status)67 void on_error(Status status) final {
68 promise_.set_error(std::move(status));
69 }
70 };
71
72 class SetBotPreCheckoutAnswerQuery final : public Td::ResultHandler {
73 Promise<Unit> promise_;
74
75 public:
SetBotPreCheckoutAnswerQuery(Promise<Unit> && promise)76 explicit SetBotPreCheckoutAnswerQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
77 }
78
send(int64 pre_checkout_query_id,const string & error_message)79 void send(int64 pre_checkout_query_id, const string &error_message) {
80 int32 flags = 0;
81 if (!error_message.empty()) {
82 flags |= telegram_api::messages_setBotPrecheckoutResults::ERROR_MASK;
83 } else {
84 flags |= telegram_api::messages_setBotPrecheckoutResults::SUCCESS_MASK;
85 }
86
87 send_query(G()->net_query_creator().create(telegram_api::messages_setBotPrecheckoutResults(
88 flags, false /*ignored*/, pre_checkout_query_id, error_message)));
89 }
90
on_result(BufferSlice packet)91 void on_result(BufferSlice packet) final {
92 auto result_ptr = fetch_result<telegram_api::messages_setBotPrecheckoutResults>(packet);
93 if (result_ptr.is_error()) {
94 return on_error(result_ptr.move_as_error());
95 }
96
97 bool result = result_ptr.ok();
98 if (!result) {
99 LOG(INFO) << "Sending answer to a pre-checkout query has failed";
100 }
101 promise_.set_value(Unit());
102 }
103
on_error(Status status)104 void on_error(Status status) final {
105 promise_.set_error(std::move(status));
106 }
107 };
108
convert_invoice(tl_object_ptr<telegram_api::invoice> invoice)109 static tl_object_ptr<td_api::invoice> convert_invoice(tl_object_ptr<telegram_api::invoice> invoice) {
110 CHECK(invoice != nullptr);
111
112 vector<tl_object_ptr<td_api::labeledPricePart>> labeled_prices;
113 labeled_prices.reserve(invoice->prices_.size());
114 for (auto &labeled_price : invoice->prices_) {
115 labeled_prices.push_back(
116 make_tl_object<td_api::labeledPricePart>(std::move(labeled_price->label_), labeled_price->amount_));
117 }
118
119 bool is_test = (invoice->flags_ & telegram_api::invoice::TEST_MASK) != 0;
120 bool need_name = (invoice->flags_ & telegram_api::invoice::NAME_REQUESTED_MASK) != 0;
121 bool need_phone_number = (invoice->flags_ & telegram_api::invoice::PHONE_REQUESTED_MASK) != 0;
122 bool need_email_address = (invoice->flags_ & telegram_api::invoice::EMAIL_REQUESTED_MASK) != 0;
123 bool need_shipping_address = (invoice->flags_ & telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0;
124 bool send_phone_number_to_provider = (invoice->flags_ & telegram_api::invoice::PHONE_TO_PROVIDER_MASK) != 0;
125 bool send_email_address_to_provider = (invoice->flags_ & telegram_api::invoice::EMAIL_TO_PROVIDER_MASK) != 0;
126 bool is_flexible = (invoice->flags_ & telegram_api::invoice::FLEXIBLE_MASK) != 0;
127 if (send_phone_number_to_provider) {
128 need_phone_number = true;
129 }
130 if (send_email_address_to_provider) {
131 need_email_address = true;
132 }
133 if (is_flexible) {
134 need_shipping_address = true;
135 }
136
137 return make_tl_object<td_api::invoice>(
138 std::move(invoice->currency_), std::move(labeled_prices), invoice->max_tip_amount_,
139 vector<int64>(invoice->suggested_tip_amounts_), is_test, need_name, need_phone_number, need_email_address,
140 need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible);
141 }
142
convert_payment_provider(const string & native_provider_name,tl_object_ptr<telegram_api::dataJSON> native_parameters)143 static tl_object_ptr<td_api::paymentsProviderStripe> convert_payment_provider(
144 const string &native_provider_name, tl_object_ptr<telegram_api::dataJSON> native_parameters) {
145 if (native_parameters == nullptr) {
146 return nullptr;
147 }
148
149 if (native_provider_name == "stripe") {
150 string data = native_parameters->data_;
151 auto r_value = json_decode(data);
152 if (r_value.is_error()) {
153 LOG(ERROR) << "Can't parse JSON object \"" << native_parameters->data_ << "\": " << r_value.error();
154 return nullptr;
155 }
156
157 auto value = r_value.move_as_ok();
158 if (value.type() != JsonValue::Type::Object) {
159 LOG(ERROR) << "Wrong JSON data \"" << native_parameters->data_ << '"';
160 return nullptr;
161 }
162
163 auto r_need_country = get_json_object_bool_field(value.get_object(), "need_country", false);
164 auto r_need_postal_code = get_json_object_bool_field(value.get_object(), "need_zip", false);
165 auto r_need_cardholder_name = get_json_object_bool_field(value.get_object(), "need_cardholder_name", false);
166 auto r_publishable_key = get_json_object_string_field(value.get_object(), "publishable_key", false);
167 // TODO support "gpay_parameters":{"gateway":"stripe","stripe:publishableKey":"...","stripe:version":"..."}
168
169 if (r_need_country.is_error() || r_need_postal_code.is_error() || r_need_cardholder_name.is_error() ||
170 r_publishable_key.is_error()) {
171 LOG(ERROR) << "Unsupported JSON data \"" << native_parameters->data_ << '"';
172 return nullptr;
173 }
174 if (value.get_object().size() != 5) {
175 LOG(ERROR) << "Unsupported JSON data \"" << native_parameters->data_ << '"';
176 }
177
178 return make_tl_object<td_api::paymentsProviderStripe>(r_publishable_key.move_as_ok(), r_need_country.move_as_ok(),
179 r_need_postal_code.move_as_ok(),
180 r_need_cardholder_name.move_as_ok());
181 }
182
183 return nullptr;
184 }
185
convert_address(tl_object_ptr<telegram_api::postAddress> address)186 static tl_object_ptr<td_api::address> convert_address(tl_object_ptr<telegram_api::postAddress> address) {
187 if (address == nullptr) {
188 return nullptr;
189 }
190 return make_tl_object<td_api::address>(std::move(address->country_iso2_), std::move(address->state_),
191 std::move(address->city_), std::move(address->street_line1_),
192 std::move(address->street_line2_), std::move(address->post_code_));
193 }
194
convert_address(tl_object_ptr<td_api::address> address)195 static tl_object_ptr<telegram_api::postAddress> convert_address(tl_object_ptr<td_api::address> address) {
196 if (address == nullptr) {
197 return nullptr;
198 }
199 return make_tl_object<telegram_api::postAddress>(std::move(address->street_line1_), std::move(address->street_line2_),
200 std::move(address->city_), std::move(address->state_),
201 std::move(address->country_code_), std::move(address->postal_code_));
202 }
203
convert_order_info(tl_object_ptr<telegram_api::paymentRequestedInfo> order_info)204 static tl_object_ptr<td_api::orderInfo> convert_order_info(
205 tl_object_ptr<telegram_api::paymentRequestedInfo> order_info) {
206 if (order_info == nullptr) {
207 return nullptr;
208 }
209 return make_tl_object<td_api::orderInfo>(std::move(order_info->name_), std::move(order_info->phone_),
210 std::move(order_info->email_),
211 convert_address(std::move(order_info->shipping_address_)));
212 }
213
convert_labeled_price(tl_object_ptr<telegram_api::labeledPrice> labeled_price)214 static tl_object_ptr<td_api::labeledPricePart> convert_labeled_price(
215 tl_object_ptr<telegram_api::labeledPrice> labeled_price) {
216 CHECK(labeled_price != nullptr);
217 return make_tl_object<td_api::labeledPricePart>(std::move(labeled_price->label_), labeled_price->amount_);
218 }
219
convert_shipping_option(tl_object_ptr<telegram_api::shippingOption> shipping_option)220 static tl_object_ptr<td_api::shippingOption> convert_shipping_option(
221 tl_object_ptr<telegram_api::shippingOption> shipping_option) {
222 if (shipping_option == nullptr) {
223 return nullptr;
224 }
225
226 return make_tl_object<td_api::shippingOption>(std::move(shipping_option->id_), std::move(shipping_option->title_),
227 transform(std::move(shipping_option->prices_), convert_labeled_price));
228 }
229
convert_order_info(tl_object_ptr<td_api::orderInfo> order_info)230 static tl_object_ptr<telegram_api::paymentRequestedInfo> convert_order_info(
231 tl_object_ptr<td_api::orderInfo> order_info) {
232 if (order_info == nullptr) {
233 return nullptr;
234 }
235 int32 flags = 0;
236 if (!order_info->name_.empty()) {
237 flags |= telegram_api::paymentRequestedInfo::NAME_MASK;
238 }
239 if (!order_info->phone_number_.empty()) {
240 flags |= telegram_api::paymentRequestedInfo::PHONE_MASK;
241 }
242 if (!order_info->email_address_.empty()) {
243 flags |= telegram_api::paymentRequestedInfo::EMAIL_MASK;
244 }
245 if (order_info->shipping_address_ != nullptr) {
246 flags |= telegram_api::paymentRequestedInfo::SHIPPING_ADDRESS_MASK;
247 }
248 return make_tl_object<telegram_api::paymentRequestedInfo>(
249 flags, std::move(order_info->name_), std::move(order_info->phone_number_), std::move(order_info->email_address_),
250 convert_address(std::move(order_info->shipping_address_)));
251 }
252
convert_saved_credentials(tl_object_ptr<telegram_api::paymentSavedCredentialsCard> saved_credentials)253 static tl_object_ptr<td_api::savedCredentials> convert_saved_credentials(
254 tl_object_ptr<telegram_api::paymentSavedCredentialsCard> saved_credentials) {
255 if (saved_credentials == nullptr) {
256 return nullptr;
257 }
258 return make_tl_object<td_api::savedCredentials>(std::move(saved_credentials->id_),
259 std::move(saved_credentials->title_));
260 }
261
262 class GetPaymentFormQuery final : public Td::ResultHandler {
263 Promise<tl_object_ptr<td_api::paymentForm>> promise_;
264 DialogId dialog_id_;
265
266 public:
GetPaymentFormQuery(Promise<tl_object_ptr<td_api::paymentForm>> && promise)267 explicit GetPaymentFormQuery(Promise<tl_object_ptr<td_api::paymentForm>> &&promise) : promise_(std::move(promise)) {
268 }
269
send(DialogId dialog_id,ServerMessageId server_message_id,tl_object_ptr<telegram_api::dataJSON> && theme_parameters)270 void send(DialogId dialog_id, ServerMessageId server_message_id,
271 tl_object_ptr<telegram_api::dataJSON> &&theme_parameters) {
272 dialog_id_ = dialog_id;
273 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
274 if (input_peer == nullptr) {
275 return on_error(Status::Error(400, "Can't access the chat"));
276 }
277
278 int32 flags = 0;
279 if (theme_parameters != nullptr) {
280 flags |= telegram_api::payments_getPaymentForm::THEME_PARAMS_MASK;
281 }
282 send_query(G()->net_query_creator().create(telegram_api::payments_getPaymentForm(
283 flags, std::move(input_peer), server_message_id.get(), std::move(theme_parameters))));
284 }
285
on_result(BufferSlice packet)286 void on_result(BufferSlice packet) final {
287 auto result_ptr = fetch_result<telegram_api::payments_getPaymentForm>(packet);
288 if (result_ptr.is_error()) {
289 return on_error(result_ptr.move_as_error());
290 }
291
292 auto payment_form = result_ptr.move_as_ok();
293 LOG(INFO) << "Receive result for GetPaymentFormQuery: " << to_string(payment_form);
294
295 td_->contacts_manager_->on_get_users(std::move(payment_form->users_), "GetPaymentFormQuery");
296
297 UserId payments_provider_user_id(payment_form->provider_id_);
298 if (!payments_provider_user_id.is_valid()) {
299 LOG(ERROR) << "Receive invalid payments provider " << payments_provider_user_id;
300 return on_error(Status::Error(500, "Receive invalid payments provider identifier"));
301 }
302 UserId seller_bot_user_id(payment_form->bot_id_);
303 if (!seller_bot_user_id.is_valid()) {
304 LOG(ERROR) << "Receive invalid seller " << seller_bot_user_id;
305 return on_error(Status::Error(500, "Receive invalid seller identifier"));
306 }
307 bool can_save_credentials = payment_form->can_save_credentials_;
308 bool need_password = payment_form->password_missing_;
309 promise_.set_value(make_tl_object<td_api::paymentForm>(
310 payment_form->form_id_, convert_invoice(std::move(payment_form->invoice_)), std::move(payment_form->url_),
311 td_->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentForm seller"),
312 td_->contacts_manager_->get_user_id_object(payments_provider_user_id, "paymentForm provider"),
313 convert_payment_provider(payment_form->native_provider_, std::move(payment_form->native_params_)),
314 convert_order_info(std::move(payment_form->saved_info_)),
315 convert_saved_credentials(std::move(payment_form->saved_credentials_)), can_save_credentials, need_password));
316 }
317
on_error(Status status)318 void on_error(Status status) final {
319 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetPaymentFormQuery");
320 promise_.set_error(std::move(status));
321 }
322 };
323
324 class ValidateRequestedInfoQuery final : public Td::ResultHandler {
325 Promise<tl_object_ptr<td_api::validatedOrderInfo>> promise_;
326 DialogId dialog_id_;
327
328 public:
ValidateRequestedInfoQuery(Promise<tl_object_ptr<td_api::validatedOrderInfo>> && promise)329 explicit ValidateRequestedInfoQuery(Promise<tl_object_ptr<td_api::validatedOrderInfo>> &&promise)
330 : promise_(std::move(promise)) {
331 }
332
send(DialogId dialog_id,ServerMessageId server_message_id,tl_object_ptr<telegram_api::paymentRequestedInfo> requested_info,bool allow_save)333 void send(DialogId dialog_id, ServerMessageId server_message_id,
334 tl_object_ptr<telegram_api::paymentRequestedInfo> requested_info, bool allow_save) {
335 dialog_id_ = dialog_id;
336 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
337 if (input_peer == nullptr) {
338 return on_error(Status::Error(400, "Can't access the chat"));
339 }
340
341 int32 flags = 0;
342 if (allow_save) {
343 flags |= telegram_api::payments_validateRequestedInfo::SAVE_MASK;
344 }
345 if (requested_info == nullptr) {
346 requested_info = make_tl_object<telegram_api::paymentRequestedInfo>();
347 requested_info->flags_ = 0;
348 }
349 send_query(G()->net_query_creator().create(telegram_api::payments_validateRequestedInfo(
350 flags, false /*ignored*/, std::move(input_peer), server_message_id.get(), std::move(requested_info))));
351 }
352
on_result(BufferSlice packet)353 void on_result(BufferSlice packet) final {
354 auto result_ptr = fetch_result<telegram_api::payments_validateRequestedInfo>(packet);
355 if (result_ptr.is_error()) {
356 return on_error(result_ptr.move_as_error());
357 }
358
359 auto validated_order_info = result_ptr.move_as_ok();
360 LOG(INFO) << "Receive result for ValidateRequestedInfoQuery: " << to_string(validated_order_info);
361
362 promise_.set_value(make_tl_object<td_api::validatedOrderInfo>(
363 std::move(validated_order_info->id_),
364 transform(std::move(validated_order_info->shipping_options_), convert_shipping_option)));
365 }
366
on_error(Status status)367 void on_error(Status status) final {
368 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "ValidateRequestedInfoQuery");
369 promise_.set_error(std::move(status));
370 }
371 };
372
373 class SendPaymentFormQuery final : public Td::ResultHandler {
374 Promise<tl_object_ptr<td_api::paymentResult>> promise_;
375 DialogId dialog_id_;
376
377 public:
SendPaymentFormQuery(Promise<tl_object_ptr<td_api::paymentResult>> && promise)378 explicit SendPaymentFormQuery(Promise<tl_object_ptr<td_api::paymentResult>> &&promise)
379 : promise_(std::move(promise)) {
380 }
381
send(DialogId dialog_id,ServerMessageId server_message_id,int64 payment_form_id,const string & order_info_id,const string & shipping_option_id,tl_object_ptr<telegram_api::InputPaymentCredentials> input_credentials,int64 tip_amount)382 void send(DialogId dialog_id, ServerMessageId server_message_id, int64 payment_form_id, const string &order_info_id,
383 const string &shipping_option_id, tl_object_ptr<telegram_api::InputPaymentCredentials> input_credentials,
384 int64 tip_amount) {
385 CHECK(input_credentials != nullptr);
386
387 dialog_id_ = dialog_id;
388 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
389 if (input_peer == nullptr) {
390 return on_error(Status::Error(400, "Can't access the chat"));
391 }
392
393 int32 flags = 0;
394 if (!order_info_id.empty()) {
395 flags |= telegram_api::payments_sendPaymentForm::REQUESTED_INFO_ID_MASK;
396 }
397 if (!shipping_option_id.empty()) {
398 flags |= telegram_api::payments_sendPaymentForm::SHIPPING_OPTION_ID_MASK;
399 }
400 if (tip_amount != 0) {
401 flags |= telegram_api::payments_sendPaymentForm::TIP_AMOUNT_MASK;
402 }
403 send_query(G()->net_query_creator().create(telegram_api::payments_sendPaymentForm(
404 flags, payment_form_id, std::move(input_peer), server_message_id.get(), order_info_id, shipping_option_id,
405 std::move(input_credentials), tip_amount)));
406 }
407
on_result(BufferSlice packet)408 void on_result(BufferSlice packet) final {
409 auto result_ptr = fetch_result<telegram_api::payments_sendPaymentForm>(packet);
410 if (result_ptr.is_error()) {
411 return on_error(result_ptr.move_as_error());
412 }
413
414 auto payment_result = result_ptr.move_as_ok();
415 LOG(INFO) << "Receive result for SendPaymentFormQuery: " << to_string(payment_result);
416
417 switch (payment_result->get_id()) {
418 case telegram_api::payments_paymentResult::ID: {
419 auto result = move_tl_object_as<telegram_api::payments_paymentResult>(payment_result);
420 td_->updates_manager_->on_get_updates(
421 std::move(result->updates_), PromiseCreator::lambda([promise = std::move(promise_)](Unit) mutable {
422 promise.set_value(make_tl_object<td_api::paymentResult>(true, string()));
423 }));
424 return;
425 }
426 case telegram_api::payments_paymentVerificationNeeded::ID: {
427 auto result = move_tl_object_as<telegram_api::payments_paymentVerificationNeeded>(payment_result);
428 promise_.set_value(make_tl_object<td_api::paymentResult>(false, std::move(result->url_)));
429 return;
430 }
431 default:
432 UNREACHABLE();
433 }
434 }
435
on_error(Status status)436 void on_error(Status status) final {
437 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendPaymentFormQuery");
438 promise_.set_error(std::move(status));
439 }
440 };
441
442 class GetPaymentReceiptQuery final : public Td::ResultHandler {
443 Promise<tl_object_ptr<td_api::paymentReceipt>> promise_;
444 DialogId dialog_id_;
445
446 public:
GetPaymentReceiptQuery(Promise<tl_object_ptr<td_api::paymentReceipt>> && promise)447 explicit GetPaymentReceiptQuery(Promise<tl_object_ptr<td_api::paymentReceipt>> &&promise)
448 : promise_(std::move(promise)) {
449 }
450
send(DialogId dialog_id,ServerMessageId server_message_id)451 void send(DialogId dialog_id, ServerMessageId server_message_id) {
452 dialog_id_ = dialog_id;
453 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
454 if (input_peer == nullptr) {
455 return on_error(Status::Error(400, "Can't access the chat"));
456 }
457
458 send_query(G()->net_query_creator().create(
459 telegram_api::payments_getPaymentReceipt(std::move(input_peer), server_message_id.get())));
460 }
461
on_result(BufferSlice packet)462 void on_result(BufferSlice packet) final {
463 auto result_ptr = fetch_result<telegram_api::payments_getPaymentReceipt>(packet);
464 if (result_ptr.is_error()) {
465 return on_error(result_ptr.move_as_error());
466 }
467
468 auto payment_receipt = result_ptr.move_as_ok();
469 LOG(INFO) << "Receive result for GetPaymentReceiptQuery: " << to_string(payment_receipt);
470
471 td_->contacts_manager_->on_get_users(std::move(payment_receipt->users_), "GetPaymentReceiptQuery");
472
473 UserId payments_provider_user_id(payment_receipt->provider_id_);
474 if (!payments_provider_user_id.is_valid()) {
475 LOG(ERROR) << "Receive invalid payments provider " << payments_provider_user_id;
476 return on_error(Status::Error(500, "Receive invalid payments provider identifier"));
477 }
478 UserId seller_bot_user_id(payment_receipt->bot_id_);
479 if (!seller_bot_user_id.is_valid()) {
480 LOG(ERROR) << "Receive invalid seller " << seller_bot_user_id;
481 return on_error(Status::Error(500, "Receive invalid seller identifier"));
482 }
483 auto photo = get_web_document_photo(td_->file_manager_.get(), std::move(payment_receipt->photo_), dialog_id_);
484
485 promise_.set_value(make_tl_object<td_api::paymentReceipt>(
486 payment_receipt->title_, payment_receipt->description_, get_photo_object(td_->file_manager_.get(), photo),
487 payment_receipt->date_, td_->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentReceipt seller"),
488 td_->contacts_manager_->get_user_id_object(payments_provider_user_id, "paymentReceipt provider"),
489 convert_invoice(std::move(payment_receipt->invoice_)), convert_order_info(std::move(payment_receipt->info_)),
490 convert_shipping_option(std::move(payment_receipt->shipping_)), std::move(payment_receipt->credentials_title_),
491 payment_receipt->tip_amount_));
492 }
493
on_error(Status status)494 void on_error(Status status) final {
495 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetPaymentReceiptQuery");
496 promise_.set_error(std::move(status));
497 }
498 };
499
500 class GetSavedInfoQuery final : public Td::ResultHandler {
501 Promise<tl_object_ptr<td_api::orderInfo>> promise_;
502
503 public:
GetSavedInfoQuery(Promise<tl_object_ptr<td_api::orderInfo>> && promise)504 explicit GetSavedInfoQuery(Promise<tl_object_ptr<td_api::orderInfo>> &&promise) : promise_(std::move(promise)) {
505 }
506
send()507 void send() {
508 send_query(G()->net_query_creator().create(telegram_api::payments_getSavedInfo()));
509 }
510
on_result(BufferSlice packet)511 void on_result(BufferSlice packet) final {
512 auto result_ptr = fetch_result<telegram_api::payments_getSavedInfo>(packet);
513 if (result_ptr.is_error()) {
514 return on_error(result_ptr.move_as_error());
515 }
516
517 auto saved_info = result_ptr.move_as_ok();
518 LOG(INFO) << "Receive result for GetSavedInfoQuery: " << to_string(saved_info);
519 promise_.set_value(convert_order_info(std::move(saved_info->saved_info_)));
520 }
521
on_error(Status status)522 void on_error(Status status) final {
523 promise_.set_error(std::move(status));
524 }
525 };
526
527 class ClearSavedInfoQuery final : public Td::ResultHandler {
528 Promise<Unit> promise_;
529
530 public:
ClearSavedInfoQuery(Promise<Unit> && promise)531 explicit ClearSavedInfoQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
532 }
533
send(bool clear_credentials,bool clear_order_info)534 void send(bool clear_credentials, bool clear_order_info) {
535 CHECK(clear_credentials || clear_order_info);
536 int32 flags = 0;
537 if (clear_credentials) {
538 flags |= telegram_api::payments_clearSavedInfo::CREDENTIALS_MASK;
539 }
540 if (clear_order_info) {
541 flags |= telegram_api::payments_clearSavedInfo::INFO_MASK;
542 }
543 send_query(G()->net_query_creator().create(
544 telegram_api::payments_clearSavedInfo(flags, false /*ignored*/, false /*ignored*/)));
545 }
546
on_result(BufferSlice packet)547 void on_result(BufferSlice packet) final {
548 auto result_ptr = fetch_result<telegram_api::payments_clearSavedInfo>(packet);
549 if (result_ptr.is_error()) {
550 return on_error(result_ptr.move_as_error());
551 }
552
553 promise_.set_value(Unit());
554 }
555
on_error(Status status)556 void on_error(Status status) final {
557 promise_.set_error(std::move(status));
558 }
559 };
560
561 class GetBankCardInfoQuery final : public Td::ResultHandler {
562 Promise<td_api::object_ptr<td_api::bankCardInfo>> promise_;
563
564 public:
GetBankCardInfoQuery(Promise<td_api::object_ptr<td_api::bankCardInfo>> && promise)565 explicit GetBankCardInfoQuery(Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise)
566 : promise_(std::move(promise)) {
567 }
568
send(const string & bank_card_number)569 void send(const string &bank_card_number) {
570 send_query(G()->net_query_creator().create(telegram_api::payments_getBankCardData(bank_card_number),
571 G()->get_webfile_dc_id()));
572 }
573
on_result(BufferSlice packet)574 void on_result(BufferSlice packet) final {
575 auto result_ptr = fetch_result<telegram_api::payments_getBankCardData>(packet);
576 if (result_ptr.is_error()) {
577 return on_error(result_ptr.move_as_error());
578 }
579
580 auto response = result_ptr.move_as_ok();
581 auto actions = transform(response->open_urls_, [](auto &open_url) {
582 return td_api::make_object<td_api::bankCardActionOpenUrl>(open_url->name_, open_url->url_);
583 });
584 promise_.set_value(td_api::make_object<td_api::bankCardInfo>(response->title_, std::move(actions)));
585 }
586
on_error(Status status)587 void on_error(Status status) final {
588 promise_.set_error(std::move(status));
589 }
590 };
591
operator ==(const LabeledPricePart & lhs,const LabeledPricePart & rhs)592 bool operator==(const LabeledPricePart &lhs, const LabeledPricePart &rhs) {
593 return lhs.label == rhs.label && lhs.amount == rhs.amount;
594 }
595
operator !=(const LabeledPricePart & lhs,const LabeledPricePart & rhs)596 bool operator!=(const LabeledPricePart &lhs, const LabeledPricePart &rhs) {
597 return !(lhs == rhs);
598 }
599
operator <<(StringBuilder & string_builder,const LabeledPricePart & labeled_price_part)600 StringBuilder &operator<<(StringBuilder &string_builder, const LabeledPricePart &labeled_price_part) {
601 return string_builder << "[" << labeled_price_part.label << ": " << labeled_price_part.amount << "]";
602 }
603
operator ==(const Invoice & lhs,const Invoice & rhs)604 bool operator==(const Invoice &lhs, const Invoice &rhs) {
605 return lhs.is_test == rhs.is_test && lhs.need_name == rhs.need_name &&
606 lhs.need_phone_number == rhs.need_phone_number && lhs.need_email_address == rhs.need_email_address &&
607 lhs.need_shipping_address == rhs.need_shipping_address &&
608 lhs.send_phone_number_to_provider == rhs.send_phone_number_to_provider &&
609 lhs.send_email_address_to_provider == rhs.send_email_address_to_provider &&
610 lhs.is_flexible == rhs.is_flexible && lhs.currency == rhs.currency && lhs.price_parts == rhs.price_parts &&
611 lhs.max_tip_amount == rhs.max_tip_amount && lhs.suggested_tip_amounts == rhs.suggested_tip_amounts;
612 }
613
operator !=(const Invoice & lhs,const Invoice & rhs)614 bool operator!=(const Invoice &lhs, const Invoice &rhs) {
615 return !(lhs == rhs);
616 }
617
operator <<(StringBuilder & string_builder,const Invoice & invoice)618 StringBuilder &operator<<(StringBuilder &string_builder, const Invoice &invoice) {
619 return string_builder << "[" << (invoice.is_flexible ? "Flexible" : "") << (invoice.is_test ? "Test" : "")
620 << "Invoice" << (invoice.need_name ? ", needs name" : "")
621 << (invoice.need_phone_number ? ", needs phone number" : "")
622 << (invoice.need_email_address ? ", needs email address" : "")
623 << (invoice.need_shipping_address ? ", needs shipping address" : "")
624 << (invoice.send_phone_number_to_provider ? ", sends phone number to provider" : "")
625 << (invoice.send_email_address_to_provider ? ", sends email address to provider" : "") << " in "
626 << invoice.currency << " with price parts " << format::as_array(invoice.price_parts)
627 << " and suggested tip amounts " << invoice.suggested_tip_amounts << " up to "
628 << invoice.max_tip_amount << "]";
629 }
630
operator ==(const InputInvoice & lhs,const InputInvoice & rhs)631 bool operator==(const InputInvoice &lhs, const InputInvoice &rhs) {
632 return lhs.title == rhs.title && lhs.description == rhs.description && lhs.photo == rhs.photo &&
633 lhs.start_parameter == rhs.start_parameter && lhs.invoice == rhs.invoice &&
634 lhs.total_amount == rhs.total_amount && lhs.receipt_message_id == rhs.receipt_message_id &&
635 lhs.payload == rhs.payload && lhs.provider_token == rhs.provider_token &&
636 lhs.provider_data == rhs.provider_data;
637 }
638
operator !=(const InputInvoice & lhs,const InputInvoice & rhs)639 bool operator!=(const InputInvoice &lhs, const InputInvoice &rhs) {
640 return !(lhs == rhs);
641 }
642
get_input_invoice(tl_object_ptr<telegram_api::messageMediaInvoice> && message_invoice,Td * td,DialogId owner_dialog_id)643 InputInvoice get_input_invoice(tl_object_ptr<telegram_api::messageMediaInvoice> &&message_invoice, Td *td,
644 DialogId owner_dialog_id) {
645 InputInvoice result;
646 result.title = std::move(message_invoice->title_);
647 result.description = std::move(message_invoice->description_);
648 result.photo = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id);
649 result.start_parameter = std::move(message_invoice->start_param_);
650 result.invoice.currency = std::move(message_invoice->currency_);
651 result.invoice.is_test = message_invoice->test_;
652 result.invoice.need_shipping_address = message_invoice->shipping_address_requested_;
653 // result.payload = string();
654 // result.provider_token = string();
655 // result.provider_data = string();
656 result.total_amount = message_invoice->total_amount_;
657 if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) {
658 result.receipt_message_id = MessageId(ServerMessageId(message_invoice->receipt_msg_id_));
659 if (!result.receipt_message_id.is_valid()) {
660 LOG(ERROR) << "Receive as receipt message " << result.receipt_message_id << " in " << owner_dialog_id;
661 result.receipt_message_id = MessageId();
662 }
663 }
664 return result;
665 }
666
get_input_invoice(tl_object_ptr<telegram_api::botInlineMessageMediaInvoice> && message_invoice,Td * td,DialogId owner_dialog_id)667 InputInvoice get_input_invoice(tl_object_ptr<telegram_api::botInlineMessageMediaInvoice> &&message_invoice, Td *td,
668 DialogId owner_dialog_id) {
669 InputInvoice result;
670 result.title = std::move(message_invoice->title_);
671 result.description = std::move(message_invoice->description_);
672 result.photo = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id);
673 // result.start_parameter = string();
674 result.invoice.currency = std::move(message_invoice->currency_);
675 result.invoice.is_test = message_invoice->test_;
676 result.invoice.need_shipping_address = message_invoice->shipping_address_requested_;
677 // result.payload = string();
678 // result.provider_token = string();
679 // result.provider_data = string();
680 result.total_amount = message_invoice->total_amount_;
681 // result.receipt_message_id = MessageId();
682 return result;
683 }
684
process_input_message_invoice(td_api::object_ptr<td_api::InputMessageContent> && input_message_content,Td * td)685 Result<InputInvoice> process_input_message_invoice(
686 td_api::object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td) {
687 CHECK(input_message_content != nullptr);
688 CHECK(input_message_content->get_id() == td_api::inputMessageInvoice::ID);
689 auto input_invoice = move_tl_object_as<td_api::inputMessageInvoice>(input_message_content);
690 if (input_invoice->invoice_ == nullptr) {
691 return Status::Error(400, "Invoice must be non-empty");
692 }
693
694 if (!clean_input_string(input_invoice->title_)) {
695 return Status::Error(400, "Invoice title must be encoded in UTF-8");
696 }
697 if (!clean_input_string(input_invoice->description_)) {
698 return Status::Error(400, "Invoice description must be encoded in UTF-8");
699 }
700 if (!clean_input_string(input_invoice->photo_url_)) {
701 return Status::Error(400, "Invoice photo URL must be encoded in UTF-8");
702 }
703 if (!clean_input_string(input_invoice->start_parameter_)) {
704 return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8");
705 }
706 if (!clean_input_string(input_invoice->provider_token_)) {
707 return Status::Error(400, "Invoice provider token must be encoded in UTF-8");
708 }
709 if (!clean_input_string(input_invoice->provider_data_)) {
710 return Status::Error(400, "Invoice provider data must be encoded in UTF-8");
711 }
712 if (!clean_input_string(input_invoice->invoice_->currency_)) {
713 return Status::Error(400, "Invoice currency must be encoded in UTF-8");
714 }
715
716 InputInvoice result;
717 result.title = std::move(input_invoice->title_);
718 result.description = std::move(input_invoice->description_);
719
720 auto r_http_url = parse_url(input_invoice->photo_url_);
721 if (r_http_url.is_error()) {
722 if (!input_invoice->photo_url_.empty()) {
723 LOG(INFO) << "Can't register url " << input_invoice->photo_url_;
724 }
725 } else {
726 auto url = r_http_url.ok().get_url();
727 auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp);
728 if (r_invoice_file_id.is_error()) {
729 LOG(INFO) << "Can't register url " << url;
730 } else {
731 auto invoice_file_id = r_invoice_file_id.move_as_ok();
732
733 PhotoSize s;
734 s.type = 'n';
735 s.dimensions =
736 get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_, "process_input_message_invoice");
737 s.size = input_invoice->photo_size_; // TODO use invoice_file_id size
738 s.file_id = invoice_file_id;
739
740 result.photo.id = 0;
741 result.photo.photos.push_back(s);
742 }
743 }
744 result.start_parameter = std::move(input_invoice->start_parameter_);
745
746 result.invoice.currency = std::move(input_invoice->invoice_->currency_);
747 result.invoice.price_parts.reserve(input_invoice->invoice_->price_parts_.size());
748 int64 total_amount = 0;
749 const int64 MAX_AMOUNT = 9999'9999'9999;
750 for (auto &price : input_invoice->invoice_->price_parts_) {
751 if (!clean_input_string(price->label_)) {
752 return Status::Error(400, "Invoice price label must be encoded in UTF-8");
753 }
754 result.invoice.price_parts.emplace_back(std::move(price->label_), price->amount_);
755 if (price->amount_ < -MAX_AMOUNT || price->amount_ > MAX_AMOUNT) {
756 return Status::Error(400, "Too big amount of the currency specified");
757 }
758 total_amount += price->amount_;
759 }
760 if (total_amount <= 0) {
761 return Status::Error(400, "Total price must be positive");
762 }
763 if (total_amount > MAX_AMOUNT) {
764 return Status::Error(400, "Total price is too big");
765 }
766 result.total_amount = total_amount;
767
768 if (input_invoice->invoice_->max_tip_amount_ < 0 || input_invoice->invoice_->max_tip_amount_ > MAX_AMOUNT) {
769 return Status::Error(400, "Invalid max_tip_amount of the currency specified");
770 }
771 for (auto tip_amount : input_invoice->invoice_->suggested_tip_amounts_) {
772 if (tip_amount <= 0) {
773 return Status::Error(400, "Suggested tip amount must be positive");
774 }
775 if (tip_amount > input_invoice->invoice_->max_tip_amount_) {
776 return Status::Error(400, "Suggested tip amount can't be bigger than max_tip_amount");
777 }
778 }
779 if (input_invoice->invoice_->suggested_tip_amounts_.size() > 4) {
780 return Status::Error(400, "There can be at most 4 suggested tip amounts");
781 }
782
783 result.invoice.max_tip_amount = input_invoice->invoice_->max_tip_amount_;
784 result.invoice.suggested_tip_amounts = std::move(input_invoice->invoice_->suggested_tip_amounts_);
785 result.invoice.is_test = input_invoice->invoice_->is_test_;
786 result.invoice.need_name = input_invoice->invoice_->need_name_;
787 result.invoice.need_phone_number = input_invoice->invoice_->need_phone_number_;
788 result.invoice.need_email_address = input_invoice->invoice_->need_email_address_;
789 result.invoice.need_shipping_address = input_invoice->invoice_->need_shipping_address_;
790 result.invoice.send_phone_number_to_provider = input_invoice->invoice_->send_phone_number_to_provider_;
791 result.invoice.send_email_address_to_provider = input_invoice->invoice_->send_email_address_to_provider_;
792 result.invoice.is_flexible = input_invoice->invoice_->is_flexible_;
793 if (result.invoice.send_phone_number_to_provider) {
794 result.invoice.need_phone_number = true;
795 }
796 if (result.invoice.send_email_address_to_provider) {
797 result.invoice.need_email_address = true;
798 }
799 if (result.invoice.is_flexible) {
800 result.invoice.need_shipping_address = true;
801 }
802
803 result.payload = std::move(input_invoice->payload_);
804 result.provider_token = std::move(input_invoice->provider_token_);
805 result.provider_data = std::move(input_invoice->provider_data_);
806 return result;
807 }
808
get_message_invoice_object(const InputInvoice & input_invoice,Td * td)809 tl_object_ptr<td_api::messageInvoice> get_message_invoice_object(const InputInvoice &input_invoice, Td *td) {
810 return make_tl_object<td_api::messageInvoice>(
811 input_invoice.title, input_invoice.description, get_photo_object(td->file_manager_.get(), input_invoice.photo),
812 input_invoice.invoice.currency, input_invoice.total_amount, input_invoice.start_parameter,
813 input_invoice.invoice.is_test, input_invoice.invoice.need_shipping_address,
814 input_invoice.receipt_message_id.get());
815 }
816
get_input_invoice(const Invoice & invoice)817 static tl_object_ptr<telegram_api::invoice> get_input_invoice(const Invoice &invoice) {
818 int32 flags = 0;
819 if (invoice.is_test) {
820 flags |= telegram_api::invoice::TEST_MASK;
821 }
822 if (invoice.need_name) {
823 flags |= telegram_api::invoice::NAME_REQUESTED_MASK;
824 }
825 if (invoice.need_phone_number) {
826 flags |= telegram_api::invoice::PHONE_REQUESTED_MASK;
827 }
828 if (invoice.need_email_address) {
829 flags |= telegram_api::invoice::EMAIL_REQUESTED_MASK;
830 }
831 if (invoice.need_shipping_address) {
832 flags |= telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK;
833 }
834 if (invoice.send_phone_number_to_provider) {
835 flags |= telegram_api::invoice::PHONE_TO_PROVIDER_MASK;
836 }
837 if (invoice.send_email_address_to_provider) {
838 flags |= telegram_api::invoice::EMAIL_TO_PROVIDER_MASK;
839 }
840 if (invoice.is_flexible) {
841 flags |= telegram_api::invoice::FLEXIBLE_MASK;
842 }
843 if (invoice.max_tip_amount != 0) {
844 flags |= telegram_api::invoice::MAX_TIP_AMOUNT_MASK;
845 }
846
847 auto prices = transform(invoice.price_parts, [](const LabeledPricePart &price) {
848 return telegram_api::make_object<telegram_api::labeledPrice>(price.label, price.amount);
849 });
850 return make_tl_object<telegram_api::invoice>(
851 flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
852 false /*ignored*/, false /*ignored*/, false /*ignored*/, invoice.currency, std::move(prices),
853 invoice.max_tip_amount, vector<int64>(invoice.suggested_tip_amounts));
854 }
855
get_input_web_document(const FileManager * file_manager,const Photo & photo)856 static tl_object_ptr<telegram_api::inputWebDocument> get_input_web_document(const FileManager *file_manager,
857 const Photo &photo) {
858 if (photo.is_empty()) {
859 return nullptr;
860 }
861
862 CHECK(photo.photos.size() == 1);
863 const PhotoSize &size = photo.photos[0];
864 CHECK(size.file_id.is_valid());
865
866 vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
867 if (size.dimensions.width != 0 && size.dimensions.height != 0) {
868 attributes.push_back(
869 make_tl_object<telegram_api::documentAttributeImageSize>(size.dimensions.width, size.dimensions.height));
870 }
871
872 auto file_view = file_manager->get_file_view(size.file_id);
873 CHECK(file_view.has_url());
874
875 auto file_name = get_url_file_name(file_view.url());
876 return make_tl_object<telegram_api::inputWebDocument>(
877 file_view.url(), size.size, MimeType::from_extension(PathView(file_name).extension(), "image/jpeg"),
878 std::move(attributes));
879 }
880
get_input_media_invoice(const InputInvoice & input_invoice,Td * td)881 tl_object_ptr<telegram_api::inputMediaInvoice> get_input_media_invoice(const InputInvoice &input_invoice, Td *td) {
882 int32 flags = 0;
883 if (!input_invoice.start_parameter.empty()) {
884 flags |= telegram_api::inputMediaInvoice::START_PARAM_MASK;
885 }
886 auto input_web_document = get_input_web_document(td->file_manager_.get(), input_invoice.photo);
887 if (input_web_document != nullptr) {
888 flags |= telegram_api::inputMediaInvoice::PHOTO_MASK;
889 }
890
891 return make_tl_object<telegram_api::inputMediaInvoice>(
892 flags, input_invoice.title, input_invoice.description, std::move(input_web_document),
893 get_input_invoice(input_invoice.invoice), BufferSlice(input_invoice.payload), input_invoice.provider_token,
894 telegram_api::make_object<telegram_api::dataJSON>(
895 input_invoice.provider_data.empty() ? "null" : input_invoice.provider_data),
896 input_invoice.start_parameter);
897 }
898
get_input_bot_inline_message_media_invoice(const InputInvoice & input_invoice,tl_object_ptr<telegram_api::ReplyMarkup> && reply_markup,Td * td)899 tl_object_ptr<telegram_api::inputBotInlineMessageMediaInvoice> get_input_bot_inline_message_media_invoice(
900 const InputInvoice &input_invoice, tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup, Td *td) {
901 int32 flags = 0;
902 if (reply_markup != nullptr) {
903 flags |= telegram_api::inputBotInlineMessageMediaInvoice::REPLY_MARKUP_MASK;
904 }
905 auto input_web_document = get_input_web_document(td->file_manager_.get(), input_invoice.photo);
906 if (input_web_document != nullptr) {
907 flags |= telegram_api::inputBotInlineMessageMediaInvoice::PHOTO_MASK;
908 }
909 return make_tl_object<telegram_api::inputBotInlineMessageMediaInvoice>(
910 flags, input_invoice.title, input_invoice.description, std::move(input_web_document),
911 get_input_invoice(input_invoice.invoice), BufferSlice(input_invoice.payload), input_invoice.provider_token,
912 telegram_api::make_object<telegram_api::dataJSON>(
913 input_invoice.provider_data.empty() ? "null" : input_invoice.provider_data),
914 std::move(reply_markup));
915 }
916
get_input_invoice_file_ids(const InputInvoice & input_invoice)917 vector<FileId> get_input_invoice_file_ids(const InputInvoice &input_invoice) {
918 return photo_get_file_ids(input_invoice.photo);
919 }
920
operator ==(const Address & lhs,const Address & rhs)921 bool operator==(const Address &lhs, const Address &rhs) {
922 return lhs.country_code == rhs.country_code && lhs.state == rhs.state && lhs.city == rhs.city &&
923 lhs.street_line1 == rhs.street_line1 && lhs.street_line2 == rhs.street_line2 &&
924 lhs.postal_code == rhs.postal_code;
925 }
926
operator !=(const Address & lhs,const Address & rhs)927 bool operator!=(const Address &lhs, const Address &rhs) {
928 return !(lhs == rhs);
929 }
930
operator <<(StringBuilder & string_builder,const Address & address)931 StringBuilder &operator<<(StringBuilder &string_builder, const Address &address) {
932 return string_builder << "[Address " << tag("country_code", address.country_code) << tag("state", address.state)
933 << tag("city", address.city) << tag("street_line1", address.street_line1)
934 << tag("street_line2", address.street_line2) << tag("postal_code", address.postal_code) << "]";
935 }
936
get_address(tl_object_ptr<telegram_api::postAddress> && address)937 unique_ptr<Address> get_address(tl_object_ptr<telegram_api::postAddress> &&address) {
938 if (address == nullptr) {
939 return nullptr;
940 }
941 return td::make_unique<Address>(std::move(address->country_iso2_), std::move(address->state_),
942 std::move(address->city_), std::move(address->street_line1_),
943 std::move(address->street_line2_), std::move(address->post_code_));
944 }
945
is_capital_alpha(char c)946 static bool is_capital_alpha(char c) {
947 return 'A' <= c && c <= 'Z';
948 }
949
check_country_code(string & country_code)950 Status check_country_code(string &country_code) {
951 if (!clean_input_string(country_code)) {
952 return Status::Error(400, "Country code must be encoded in UTF-8");
953 }
954 if (country_code.size() != 2 || !is_capital_alpha(country_code[0]) || !is_capital_alpha(country_code[1])) {
955 return Status::Error(400, "Wrong country code specified");
956 }
957 return Status::OK();
958 }
959
check_state(string & state)960 static Status check_state(string &state) {
961 if (!clean_input_string(state)) {
962 return Status::Error(400, "State must be encoded in UTF-8");
963 }
964 return Status::OK();
965 }
966
check_city(string & city)967 static Status check_city(string &city) {
968 if (!clean_input_string(city)) {
969 return Status::Error(400, "City must be encoded in UTF-8");
970 }
971 return Status::OK();
972 }
973
check_street_line(string & street_line)974 static Status check_street_line(string &street_line) {
975 if (!clean_input_string(street_line)) {
976 return Status::Error(400, "Street line must be encoded in UTF-8");
977 }
978 return Status::OK();
979 }
980
check_postal_code(string & postal_code)981 static Status check_postal_code(string &postal_code) {
982 if (!clean_input_string(postal_code)) {
983 return Status::Error(400, "Postal code must be encoded in UTF-8");
984 }
985 return Status::OK();
986 }
987
get_address(td_api::object_ptr<td_api::address> && address)988 Result<Address> get_address(td_api::object_ptr<td_api::address> &&address) {
989 if (address == nullptr) {
990 return Status::Error(400, "Address must be non-empty");
991 }
992 TRY_STATUS(check_country_code(address->country_code_));
993 TRY_STATUS(check_state(address->state_));
994 TRY_STATUS(check_city(address->city_));
995 TRY_STATUS(check_street_line(address->street_line1_));
996 TRY_STATUS(check_street_line(address->street_line2_));
997 TRY_STATUS(check_postal_code(address->postal_code_));
998
999 return Address(std::move(address->country_code_), std::move(address->state_), std::move(address->city_),
1000 std::move(address->street_line1_), std::move(address->street_line2_),
1001 std::move(address->postal_code_));
1002 }
1003
get_address_object(const unique_ptr<Address> & address)1004 tl_object_ptr<td_api::address> get_address_object(const unique_ptr<Address> &address) {
1005 if (address == nullptr) {
1006 return nullptr;
1007 }
1008 return get_address_object(*address);
1009 }
1010
get_address_object(const Address & address)1011 tl_object_ptr<td_api::address> get_address_object(const Address &address) {
1012 return make_tl_object<td_api::address>(address.country_code, address.state, address.city, address.street_line1,
1013 address.street_line2, address.postal_code);
1014 }
1015
address_to_json(const Address & address)1016 string address_to_json(const Address &address) {
1017 return json_encode<std::string>(json_object([&](auto &o) {
1018 o("country_code", address.country_code);
1019 o("state", address.state);
1020 o("city", address.city);
1021 o("street_line1", address.street_line1);
1022 o("street_line2", address.street_line2);
1023 o("post_code", address.postal_code);
1024 }));
1025 }
1026
address_from_json(Slice json)1027 Result<Address> address_from_json(Slice json) {
1028 auto json_copy = json.str();
1029 auto r_value = json_decode(json_copy);
1030 if (r_value.is_error()) {
1031 return Status::Error(400, "Can't parse address JSON object");
1032 }
1033
1034 auto value = r_value.move_as_ok();
1035 if (value.type() != JsonValue::Type::Object) {
1036 return Status::Error(400, "Address must be an Object");
1037 }
1038
1039 auto &object = value.get_object();
1040 TRY_RESULT(country_code, get_json_object_string_field(object, "country_code", true));
1041 TRY_RESULT(state, get_json_object_string_field(object, "state", true));
1042 TRY_RESULT(city, get_json_object_string_field(object, "city", true));
1043 TRY_RESULT(street_line1, get_json_object_string_field(object, "street_line1", true));
1044 TRY_RESULT(street_line2, get_json_object_string_field(object, "street_line2", true));
1045 TRY_RESULT(postal_code, get_json_object_string_field(object, "post_code", true));
1046
1047 TRY_STATUS(check_country_code(country_code));
1048 TRY_STATUS(check_state(state));
1049 TRY_STATUS(check_city(city));
1050 TRY_STATUS(check_street_line(street_line1));
1051 TRY_STATUS(check_street_line(street_line2));
1052 TRY_STATUS(check_postal_code(postal_code));
1053
1054 return Address(std::move(country_code), std::move(state), std::move(city), std::move(street_line1),
1055 std::move(street_line2), std::move(postal_code));
1056 }
1057
operator ==(const OrderInfo & lhs,const OrderInfo & rhs)1058 bool operator==(const OrderInfo &lhs, const OrderInfo &rhs) {
1059 return lhs.name == rhs.name && lhs.phone_number == rhs.phone_number && lhs.email_address == rhs.email_address &&
1060 ((lhs.shipping_address == nullptr && rhs.shipping_address == nullptr) ||
1061 (lhs.shipping_address != nullptr && rhs.shipping_address != nullptr &&
1062 *lhs.shipping_address == *rhs.shipping_address));
1063 }
1064
operator !=(const OrderInfo & lhs,const OrderInfo & rhs)1065 bool operator!=(const OrderInfo &lhs, const OrderInfo &rhs) {
1066 return !(lhs == rhs);
1067 }
1068
operator <<(StringBuilder & string_builder,const OrderInfo & order_info)1069 StringBuilder &operator<<(StringBuilder &string_builder, const OrderInfo &order_info) {
1070 string_builder << "[OrderInfo " << tag("name", order_info.name) << tag("phone_number", order_info.phone_number)
1071 << tag("email_address", order_info.email_address);
1072 if (order_info.shipping_address != nullptr) {
1073 string_builder << *order_info.shipping_address;
1074 }
1075 return string_builder << "]";
1076 }
1077
get_order_info(tl_object_ptr<telegram_api::paymentRequestedInfo> order_info)1078 unique_ptr<OrderInfo> get_order_info(tl_object_ptr<telegram_api::paymentRequestedInfo> order_info) {
1079 if (order_info == nullptr || order_info->flags_ == 0) {
1080 return nullptr;
1081 }
1082 return td::make_unique<OrderInfo>(std::move(order_info->name_), std::move(order_info->phone_),
1083 std::move(order_info->email_),
1084 get_address(std::move(order_info->shipping_address_)));
1085 }
1086
get_order_info_object(const unique_ptr<OrderInfo> & order_info)1087 tl_object_ptr<td_api::orderInfo> get_order_info_object(const unique_ptr<OrderInfo> &order_info) {
1088 if (order_info == nullptr) {
1089 return nullptr;
1090 }
1091 return make_tl_object<td_api::orderInfo>(order_info->name, order_info->phone_number, order_info->email_address,
1092 get_address_object(order_info->shipping_address));
1093 }
1094
operator ==(const ShippingOption & lhs,const ShippingOption & rhs)1095 bool operator==(const ShippingOption &lhs, const ShippingOption &rhs) {
1096 return lhs.id == rhs.id && lhs.title == rhs.title && lhs.price_parts == rhs.price_parts;
1097 }
1098
operator !=(const ShippingOption & lhs,const ShippingOption & rhs)1099 bool operator!=(const ShippingOption &lhs, const ShippingOption &rhs) {
1100 return !(lhs == rhs);
1101 }
1102
operator <<(StringBuilder & string_builder,const ShippingOption & shipping_option)1103 StringBuilder &operator<<(StringBuilder &string_builder, const ShippingOption &shipping_option) {
1104 return string_builder << "[ShippingOption " << shipping_option.id << " " << shipping_option.title
1105 << " with price parts " << format::as_array(shipping_option.price_parts) << "]";
1106 }
1107
answer_shipping_query(Td * td,int64 shipping_query_id,vector<tl_object_ptr<td_api::shippingOption>> && shipping_options,const string & error_message,Promise<Unit> && promise)1108 void answer_shipping_query(Td *td, int64 shipping_query_id,
1109 vector<tl_object_ptr<td_api::shippingOption>> &&shipping_options,
1110 const string &error_message, Promise<Unit> &&promise) {
1111 vector<tl_object_ptr<telegram_api::shippingOption>> options;
1112 for (auto &option : shipping_options) {
1113 if (option == nullptr) {
1114 return promise.set_error(Status::Error(400, "Shipping option must be non-empty"));
1115 }
1116 if (!clean_input_string(option->id_)) {
1117 return promise.set_error(Status::Error(400, "Shipping option identifier must be encoded in UTF-8"));
1118 }
1119 if (!clean_input_string(option->title_)) {
1120 return promise.set_error(Status::Error(400, "Shipping option title must be encoded in UTF-8"));
1121 }
1122
1123 vector<tl_object_ptr<telegram_api::labeledPrice>> prices;
1124 for (auto &price_part : option->price_parts_) {
1125 if (price_part == nullptr) {
1126 return promise.set_error(Status::Error(400, "Shipping option price part must be non-empty"));
1127 }
1128 if (!clean_input_string(price_part->label_)) {
1129 return promise.set_error(Status::Error(400, "Shipping option price part label must be encoded in UTF-8"));
1130 }
1131
1132 prices.push_back(make_tl_object<telegram_api::labeledPrice>(std::move(price_part->label_), price_part->amount_));
1133 }
1134
1135 options.push_back(make_tl_object<telegram_api::shippingOption>(std::move(option->id_), std::move(option->title_),
1136 std::move(prices)));
1137 }
1138
1139 td->create_handler<SetBotShippingAnswerQuery>(std::move(promise))
1140 ->send(shipping_query_id, error_message, std::move(options));
1141 }
1142
answer_pre_checkout_query(Td * td,int64 pre_checkout_query_id,const string & error_message,Promise<Unit> && promise)1143 void answer_pre_checkout_query(Td *td, int64 pre_checkout_query_id, const string &error_message,
1144 Promise<Unit> &&promise) {
1145 td->create_handler<SetBotPreCheckoutAnswerQuery>(std::move(promise))->send(pre_checkout_query_id, error_message);
1146 }
1147
get_payment_form(Td * td,FullMessageId full_message_id,const td_api::object_ptr<td_api::paymentFormTheme> & theme,Promise<tl_object_ptr<td_api::paymentForm>> && promise)1148 void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::paymentFormTheme> &theme,
1149 Promise<tl_object_ptr<td_api::paymentForm>> &&promise) {
1150 TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
1151
1152 tl_object_ptr<telegram_api::dataJSON> theme_parameters;
1153 if (theme != nullptr) {
1154 theme_parameters = make_tl_object<telegram_api::dataJSON>(string());
1155 theme_parameters->data_ = json_encode<string>(json_object([&theme](auto &o) {
1156 auto get_color = [](int32 color) {
1157 return static_cast<int64>(static_cast<uint32>(color) | 0x000000FF);
1158 };
1159 o("bg_color", get_color(theme->background_color_));
1160 o("text_color", get_color(theme->text_color_));
1161 o("hint_color", get_color(theme->hint_color_));
1162 o("link_color", get_color(theme->link_color_));
1163 o("button_color", get_color(theme->button_color_));
1164 o("button_text_color", get_color(theme->button_text_color_));
1165 }));
1166 }
1167 td->create_handler<GetPaymentFormQuery>(std::move(promise))
1168 ->send(full_message_id.get_dialog_id(), server_message_id, std::move(theme_parameters));
1169 }
1170
validate_order_info(Td * td,FullMessageId full_message_id,tl_object_ptr<td_api::orderInfo> order_info,bool allow_save,Promise<tl_object_ptr<td_api::validatedOrderInfo>> && promise)1171 void validate_order_info(Td *td, FullMessageId full_message_id, tl_object_ptr<td_api::orderInfo> order_info,
1172 bool allow_save, Promise<tl_object_ptr<td_api::validatedOrderInfo>> &&promise) {
1173 TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
1174
1175 if (order_info != nullptr) {
1176 if (!clean_input_string(order_info->name_)) {
1177 return promise.set_error(Status::Error(400, "Name must be encoded in UTF-8"));
1178 }
1179 if (!clean_input_string(order_info->phone_number_)) {
1180 return promise.set_error(Status::Error(400, "Phone number must be encoded in UTF-8"));
1181 }
1182 if (!clean_input_string(order_info->email_address_)) {
1183 return promise.set_error(Status::Error(400, "Email address must be encoded in UTF-8"));
1184 }
1185 if (order_info->shipping_address_ != nullptr) {
1186 if (!clean_input_string(order_info->shipping_address_->country_code_)) {
1187 return promise.set_error(Status::Error(400, "Country code must be encoded in UTF-8"));
1188 }
1189 if (!clean_input_string(order_info->shipping_address_->state_)) {
1190 return promise.set_error(Status::Error(400, "State must be encoded in UTF-8"));
1191 }
1192 if (!clean_input_string(order_info->shipping_address_->city_)) {
1193 return promise.set_error(Status::Error(400, "City must be encoded in UTF-8"));
1194 }
1195 if (!clean_input_string(order_info->shipping_address_->street_line1_)) {
1196 return promise.set_error(Status::Error(400, "Street address must be encoded in UTF-8"));
1197 }
1198 if (!clean_input_string(order_info->shipping_address_->street_line2_)) {
1199 return promise.set_error(Status::Error(400, "Street address must be encoded in UTF-8"));
1200 }
1201 if (!clean_input_string(order_info->shipping_address_->postal_code_)) {
1202 return promise.set_error(Status::Error(400, "Postal code must be encoded in UTF-8"));
1203 }
1204 }
1205 }
1206
1207 td->create_handler<ValidateRequestedInfoQuery>(std::move(promise))
1208 ->send(full_message_id.get_dialog_id(), server_message_id, convert_order_info(std::move(order_info)), allow_save);
1209 }
1210
send_payment_form(Td * td,FullMessageId full_message_id,int64 payment_form_id,const string & order_info_id,const string & shipping_option_id,const tl_object_ptr<td_api::InputCredentials> & credentials,int64 tip_amount,Promise<tl_object_ptr<td_api::paymentResult>> && promise)1211 void send_payment_form(Td *td, FullMessageId full_message_id, int64 payment_form_id, const string &order_info_id,
1212 const string &shipping_option_id, const tl_object_ptr<td_api::InputCredentials> &credentials,
1213 int64 tip_amount, Promise<tl_object_ptr<td_api::paymentResult>> &&promise) {
1214 TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
1215
1216 if (credentials == nullptr) {
1217 return promise.set_error(Status::Error(400, "Input payment credentials must be non-empty"));
1218 }
1219
1220 tl_object_ptr<telegram_api::InputPaymentCredentials> input_credentials;
1221 switch (credentials->get_id()) {
1222 case td_api::inputCredentialsSaved::ID: {
1223 auto credentials_saved = static_cast<const td_api::inputCredentialsSaved *>(credentials.get());
1224 auto credentials_id = credentials_saved->saved_credentials_id_;
1225 if (!clean_input_string(credentials_id)) {
1226 return promise.set_error(Status::Error(400, "Credentials identifier must be encoded in UTF-8"));
1227 }
1228 auto temp_password_state = PasswordManager::get_temp_password_state_sync();
1229 if (!temp_password_state.has_temp_password) {
1230 return promise.set_error(Status::Error(400, "Temporary password required to use saved credentials"));
1231 }
1232
1233 input_credentials = make_tl_object<telegram_api::inputPaymentCredentialsSaved>(
1234 std::move(credentials_id), BufferSlice(temp_password_state.temp_password));
1235 break;
1236 }
1237 case td_api::inputCredentialsNew::ID: {
1238 auto credentials_new = static_cast<const td_api::inputCredentialsNew *>(credentials.get());
1239 int32 flags = 0;
1240 if (credentials_new->allow_save_) {
1241 flags |= telegram_api::inputPaymentCredentials::SAVE_MASK;
1242 }
1243
1244 input_credentials = make_tl_object<telegram_api::inputPaymentCredentials>(
1245 flags, false /*ignored*/, make_tl_object<telegram_api::dataJSON>(credentials_new->data_));
1246 break;
1247 }
1248 case td_api::inputCredentialsGooglePay::ID: {
1249 auto credentials_google_pay = static_cast<const td_api::inputCredentialsGooglePay *>(credentials.get());
1250 input_credentials = make_tl_object<telegram_api::inputPaymentCredentialsGooglePay>(
1251 make_tl_object<telegram_api::dataJSON>(credentials_google_pay->data_));
1252 break;
1253 }
1254 case td_api::inputCredentialsApplePay::ID: {
1255 auto credentials_apple_pay = static_cast<const td_api::inputCredentialsApplePay *>(credentials.get());
1256 input_credentials = make_tl_object<telegram_api::inputPaymentCredentialsApplePay>(
1257 make_tl_object<telegram_api::dataJSON>(credentials_apple_pay->data_));
1258 break;
1259 }
1260 default:
1261 UNREACHABLE();
1262 }
1263
1264 td->create_handler<SendPaymentFormQuery>(std::move(promise))
1265 ->send(full_message_id.get_dialog_id(), server_message_id, payment_form_id, order_info_id, shipping_option_id,
1266 std::move(input_credentials), tip_amount);
1267 }
1268
get_payment_receipt(Td * td,FullMessageId full_message_id,Promise<tl_object_ptr<td_api::paymentReceipt>> && promise)1269 void get_payment_receipt(Td *td, FullMessageId full_message_id,
1270 Promise<tl_object_ptr<td_api::paymentReceipt>> &&promise) {
1271 TRY_RESULT_PROMISE(promise, server_message_id,
1272 td->messages_manager_->get_payment_successful_message_id(full_message_id));
1273 td->create_handler<GetPaymentReceiptQuery>(std::move(promise))
1274 ->send(full_message_id.get_dialog_id(), server_message_id);
1275 }
1276
get_saved_order_info(Td * td,Promise<tl_object_ptr<td_api::orderInfo>> && promise)1277 void get_saved_order_info(Td *td, Promise<tl_object_ptr<td_api::orderInfo>> &&promise) {
1278 td->create_handler<GetSavedInfoQuery>(std::move(promise))->send();
1279 }
1280
delete_saved_order_info(Td * td,Promise<Unit> && promise)1281 void delete_saved_order_info(Td *td, Promise<Unit> &&promise) {
1282 td->create_handler<ClearSavedInfoQuery>(std::move(promise))->send(false, true);
1283 }
1284
delete_saved_credentials(Td * td,Promise<Unit> && promise)1285 void delete_saved_credentials(Td *td, Promise<Unit> &&promise) {
1286 td->create_handler<ClearSavedInfoQuery>(std::move(promise))->send(true, false);
1287 }
1288
get_bank_card_info(Td * td,const string & bank_card_number,Promise<td_api::object_ptr<td_api::bankCardInfo>> && promise)1289 void get_bank_card_info(Td *td, const string &bank_card_number,
1290 Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise) {
1291 td->create_handler<GetBankCardInfoQuery>(std::move(promise))->send(bank_card_number);
1292 }
1293
1294 } // namespace td
1295