// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/InputInvoice.h" #include "td/telegram/AuthManager.h" #include "td/telegram/Dimensions.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileType.h" #include "td/telegram/misc.h" #include "td/telegram/PhotoSize.h" #include "td/telegram/ServerMessageId.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/MimeType.h" #include "td/utils/PathView.h" namespace td { bool operator==(const InputInvoice &lhs, const InputInvoice &rhs) { auto are_invoice_equal = [](const InputInvoice::Invoice &lhs, const InputInvoice::Invoice &rhs) { return lhs.is_test_ == rhs.is_test_ && lhs.need_name_ == rhs.need_name_ && lhs.need_phone_number_ == rhs.need_phone_number_ && lhs.need_email_address_ == rhs.need_email_address_ && lhs.need_shipping_address_ == rhs.need_shipping_address_ && lhs.send_phone_number_to_provider_ == rhs.send_phone_number_to_provider_ && lhs.send_email_address_to_provider_ == rhs.send_email_address_to_provider_ && lhs.is_flexible_ == rhs.is_flexible_ && lhs.currency_ == rhs.currency_ && lhs.price_parts_ == rhs.price_parts_ && lhs.max_tip_amount_ == rhs.max_tip_amount_ && lhs.suggested_tip_amounts_ == rhs.suggested_tip_amounts_ && lhs.recurring_payment_terms_of_service_url_ == rhs.recurring_payment_terms_of_service_url_ && lhs.terms_of_service_url_ == rhs.terms_of_service_url_; }; return lhs.title_ == rhs.title_ && lhs.description_ == rhs.description_ && lhs.photo_ == rhs.photo_ && lhs.start_parameter_ == rhs.start_parameter_ && are_invoice_equal(lhs.invoice_, rhs.invoice_) && lhs.payload_ == rhs.payload_ && lhs.provider_token_ == rhs.provider_token_ && lhs.provider_data_ == rhs.provider_data_ && lhs.extended_media_ == rhs.extended_media_ && lhs.extended_media_caption_ == rhs.extended_media_caption_ && lhs.total_amount_ == rhs.total_amount_ && lhs.receipt_message_id_ == rhs.receipt_message_id_; } bool operator!=(const InputInvoice &lhs, const InputInvoice &rhs) { return !(lhs == rhs); } InputInvoice::InputInvoice(tl_object_ptr &&message_invoice, Td *td, DialogId owner_dialog_id, FormattedText &&message) { title_ = std::move(message_invoice->title_); description_ = std::move(message_invoice->description_); photo_ = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id); start_parameter_ = std::move(message_invoice->start_param_); invoice_.currency_ = std::move(message_invoice->currency_); invoice_.is_test_ = message_invoice->test_; invoice_.need_shipping_address_ = message_invoice->shipping_address_requested_; // payload_ = string(); // provider_token_ = string(); // provider_data_ = string(); extended_media_ = MessageExtendedMedia(td, std::move(message_invoice->extended_media_), owner_dialog_id); if (!extended_media_.is_empty()) { extended_media_caption_ = std::move(message); } if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) { LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_; message_invoice->total_amount_ = 0; } total_amount_ = message_invoice->total_amount_; if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) { receipt_message_id_ = MessageId(ServerMessageId(message_invoice->receipt_msg_id_)); if (!receipt_message_id_.is_valid()) { LOG(ERROR) << "Receive as receipt message " << receipt_message_id_ << " in " << owner_dialog_id; receipt_message_id_ = MessageId(); } } } InputInvoice::InputInvoice(tl_object_ptr &&message_invoice, Td *td, DialogId owner_dialog_id) { title_ = std::move(message_invoice->title_); description_ = std::move(message_invoice->description_); photo_ = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id); // start_parameter_ = string(); invoice_.currency_ = std::move(message_invoice->currency_); invoice_.is_test_ = message_invoice->test_; invoice_.need_shipping_address_ = message_invoice->shipping_address_requested_; // payload_ = string(); // provider_token_ = string(); // provider_data_ = string(); // extended_media_ = MessageExtendedMedia(); // extended_media_caption_ = FormattedText(); if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) { LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_; message_invoice->total_amount_ = 0; } total_amount_ = message_invoice->total_amount_; // receipt_message_id_ = MessageId(); } Result InputInvoice::process_input_message_invoice( td_api::object_ptr &&input_message_content, Td *td, DialogId owner_dialog_id) { CHECK(input_message_content != nullptr); CHECK(input_message_content->get_id() == td_api::inputMessageInvoice::ID); auto input_invoice = move_tl_object_as(input_message_content); if (input_invoice->invoice_ == nullptr) { return Status::Error(400, "Invoice must be non-empty"); } if (!clean_input_string(input_invoice->title_)) { return Status::Error(400, "Invoice title must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->description_)) { return Status::Error(400, "Invoice description must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->photo_url_)) { return Status::Error(400, "Invoice photo URL must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->start_parameter_)) { return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->provider_token_)) { return Status::Error(400, "Invoice provider token must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->provider_data_)) { return Status::Error(400, "Invoice provider data must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->invoice_->currency_)) { return Status::Error(400, "Invoice currency must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->invoice_->recurring_payment_terms_of_service_url_)) { return Status::Error(400, "Invoice terms of service URL must be encoded in UTF-8"); } if (!clean_input_string(input_invoice->invoice_->terms_of_service_url_)) { return Status::Error(400, "Invoice terms of service URL must be encoded in UTF-8"); } InputInvoice result; result.title_ = std::move(input_invoice->title_); result.description_ = std::move(input_invoice->description_); auto r_http_url = parse_url(input_invoice->photo_url_); if (r_http_url.is_error()) { if (!input_invoice->photo_url_.empty()) { LOG(INFO) << "Can't register URL " << input_invoice->photo_url_; } } else { auto url = r_http_url.ok().get_url(); auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp); if (r_invoice_file_id.is_error()) { LOG(INFO) << "Can't register URL " << url; } else { auto invoice_file_id = r_invoice_file_id.move_as_ok(); PhotoSize s; s.type = 'n'; s.dimensions = get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_, nullptr); s.size = input_invoice->photo_size_; // TODO use invoice_file_id size s.file_id = invoice_file_id; result.photo_.id = 0; result.photo_.photos.push_back(s); } } result.start_parameter_ = std::move(input_invoice->start_parameter_); result.invoice_.currency_ = std::move(input_invoice->invoice_->currency_); result.invoice_.price_parts_.reserve(input_invoice->invoice_->price_parts_.size()); int64 total_amount = 0; for (auto &price : input_invoice->invoice_->price_parts_) { if (!clean_input_string(price->label_)) { return Status::Error(400, "Invoice price label must be encoded in UTF-8"); } if (!check_currency_amount(price->amount_)) { return Status::Error(400, "Too big amount of the currency specified"); } result.invoice_.price_parts_.emplace_back(std::move(price->label_), price->amount_); total_amount += price->amount_; } if (total_amount <= 0) { return Status::Error(400, "Total price must be positive"); } if (!check_currency_amount(total_amount)) { return Status::Error(400, "Total price is too big"); } result.total_amount_ = total_amount; if (input_invoice->invoice_->max_tip_amount_ < 0 || !check_currency_amount(input_invoice->invoice_->max_tip_amount_)) { return Status::Error(400, "Invalid max_tip_amount of the currency specified"); } for (auto tip_amount : input_invoice->invoice_->suggested_tip_amounts_) { if (tip_amount <= 0) { return Status::Error(400, "Suggested tip amount must be positive"); } if (tip_amount > input_invoice->invoice_->max_tip_amount_) { return Status::Error(400, "Suggested tip amount can't be bigger than max_tip_amount"); } } if (input_invoice->invoice_->suggested_tip_amounts_.size() > 4) { return Status::Error(400, "There can be at most 4 suggested tip amounts"); } result.invoice_.max_tip_amount_ = input_invoice->invoice_->max_tip_amount_; result.invoice_.suggested_tip_amounts_ = std::move(input_invoice->invoice_->suggested_tip_amounts_); result.invoice_.recurring_payment_terms_of_service_url_ = std::move(input_invoice->invoice_->recurring_payment_terms_of_service_url_); result.invoice_.terms_of_service_url_ = std::move(input_invoice->invoice_->terms_of_service_url_); result.invoice_.is_test_ = input_invoice->invoice_->is_test_; result.invoice_.need_name_ = input_invoice->invoice_->need_name_; result.invoice_.need_phone_number_ = input_invoice->invoice_->need_phone_number_; result.invoice_.need_email_address_ = input_invoice->invoice_->need_email_address_; result.invoice_.need_shipping_address_ = input_invoice->invoice_->need_shipping_address_; result.invoice_.send_phone_number_to_provider_ = input_invoice->invoice_->send_phone_number_to_provider_; result.invoice_.send_email_address_to_provider_ = input_invoice->invoice_->send_email_address_to_provider_; result.invoice_.is_flexible_ = input_invoice->invoice_->is_flexible_; if (result.invoice_.send_phone_number_to_provider_) { result.invoice_.need_phone_number_ = true; } if (result.invoice_.send_email_address_to_provider_) { result.invoice_.need_email_address_ = true; } if (result.invoice_.is_flexible_) { result.invoice_.need_shipping_address_ = true; } result.payload_ = std::move(input_invoice->payload_); result.provider_token_ = std::move(input_invoice->provider_token_); result.provider_data_ = std::move(input_invoice->provider_data_); TRY_RESULT(extended_media, MessageExtendedMedia::get_message_extended_media(td, std::move(input_invoice->paid_media_), owner_dialog_id)); result.extended_media_ = std::move(extended_media); if (!result.extended_media_.is_empty()) { bool is_bot = td->auth_manager_->is_bot(); TRY_RESULT(extended_media_caption, get_formatted_text(td, owner_dialog_id, std::move(input_invoice->paid_media_caption_), is_bot, true, false, false)); result.extended_media_caption_ = std::move(extended_media_caption); } return result; } td_api::object_ptr InputInvoice::get_message_invoice_object(Td *td, bool skip_bot_commands, int32 max_media_timestamp) const { auto extended_media_object = extended_media_.get_message_extended_media_object(td); auto extended_media_caption_object = extended_media_object == nullptr ? nullptr : get_formatted_text_object(td->user_manager_.get(), extended_media_caption_, skip_bot_commands, max_media_timestamp); return td_api::make_object( get_product_info_object(td, title_, description_, photo_), invoice_.currency_, total_amount_, start_parameter_, invoice_.is_test_, invoice_.need_shipping_address_, receipt_message_id_.get(), std::move(extended_media_object), std::move(extended_media_caption_object)); } tl_object_ptr InputInvoice::Invoice::get_input_invoice() const { int32 flags = 0; if (is_test_) { flags |= telegram_api::invoice::TEST_MASK; } if (need_name_) { flags |= telegram_api::invoice::NAME_REQUESTED_MASK; } if (need_phone_number_) { flags |= telegram_api::invoice::PHONE_REQUESTED_MASK; } if (need_email_address_) { flags |= telegram_api::invoice::EMAIL_REQUESTED_MASK; } if (need_shipping_address_) { flags |= telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK; } if (send_phone_number_to_provider_) { flags |= telegram_api::invoice::PHONE_TO_PROVIDER_MASK; } if (send_email_address_to_provider_) { flags |= telegram_api::invoice::EMAIL_TO_PROVIDER_MASK; } if (is_flexible_) { flags |= telegram_api::invoice::FLEXIBLE_MASK; } if (max_tip_amount_ != 0) { flags |= telegram_api::invoice::MAX_TIP_AMOUNT_MASK; } string terms_of_service_url; if (!recurring_payment_terms_of_service_url_.empty()) { flags |= telegram_api::invoice::RECURRING_MASK; flags |= telegram_api::invoice::TERMS_URL_MASK; terms_of_service_url = recurring_payment_terms_of_service_url_; } else if (!terms_of_service_url_.empty()) { flags |= telegram_api::invoice::TERMS_URL_MASK; terms_of_service_url = terms_of_service_url_; } auto prices = transform(price_parts_, [](const LabeledPricePart &price) { return telegram_api::make_object(price.label, price.amount); }); return make_tl_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, currency_, std::move(prices), max_tip_amount_, vector(suggested_tip_amounts_), terms_of_service_url); } static tl_object_ptr get_input_web_document(const FileManager *file_manager, const Photo &photo) { if (photo.is_empty()) { return nullptr; } CHECK(photo.photos.size() == 1); const PhotoSize &size = photo.photos[0]; CHECK(size.file_id.is_valid()); vector> attributes; if (size.dimensions.width != 0 && size.dimensions.height != 0) { attributes.push_back( make_tl_object(size.dimensions.width, size.dimensions.height)); } auto file_view = file_manager->get_file_view(size.file_id); CHECK(file_view.has_url()); auto file_name = get_url_file_name(file_view.url()); return make_tl_object( file_view.url(), size.size, MimeType::from_extension(PathView(file_name).extension(), "image/jpeg"), std::move(attributes)); } tl_object_ptr InputInvoice::get_input_media_invoice( Td *td, tl_object_ptr input_file, tl_object_ptr input_thumbnail) const { int32 flags = 0; if (!start_parameter_.empty()) { flags |= telegram_api::inputMediaInvoice::START_PARAM_MASK; } auto input_web_document = get_input_web_document(td->file_manager_.get(), photo_); if (input_web_document != nullptr) { flags |= telegram_api::inputMediaInvoice::PHOTO_MASK; } telegram_api::object_ptr extended_media; if (!extended_media_.is_empty()) { flags |= telegram_api::inputMediaInvoice::EXTENDED_MEDIA_MASK; extended_media = extended_media_.get_input_media(td, std::move(input_file), std::move(input_thumbnail)); if (extended_media == nullptr) { return nullptr; } } if (!provider_token_.empty()) { flags |= telegram_api::inputMediaInvoice::PROVIDER_MASK; } return make_tl_object( flags, title_, description_, std::move(input_web_document), invoice_.get_input_invoice(), BufferSlice(payload_), provider_token_, telegram_api::make_object(provider_data_.empty() ? "null" : provider_data_), start_parameter_, std::move(extended_media)); } tl_object_ptr InputInvoice::get_input_bot_inline_message_media_invoice( tl_object_ptr &&reply_markup, Td *td) const { int32 flags = 0; if (reply_markup != nullptr) { flags |= telegram_api::inputBotInlineMessageMediaInvoice::REPLY_MARKUP_MASK; } auto input_web_document = get_input_web_document(td->file_manager_.get(), photo_); if (input_web_document != nullptr) { flags |= telegram_api::inputBotInlineMessageMediaInvoice::PHOTO_MASK; } return make_tl_object( flags, title_, description_, std::move(input_web_document), invoice_.get_input_invoice(), BufferSlice(payload_), provider_token_, telegram_api::make_object(provider_data_.empty() ? "null" : provider_data_), std::move(reply_markup)); } vector InputInvoice::get_file_ids(const Td *td) const { auto file_ids = photo_get_file_ids(photo_); extended_media_.append_file_ids(td, file_ids); return file_ids; } void InputInvoice::delete_thumbnail(Td *td) { extended_media_.delete_thumbnail(td); } bool InputInvoice::need_reget() const { return extended_media_.need_reget(); } bool InputInvoice::has_media_timestamp() const { return extended_media_.has_media_timestamp(); } bool InputInvoice::is_equal_but_different(const InputInvoice &other) const { return extended_media_.is_equal_but_different(other.extended_media_); } const FormattedText *InputInvoice::get_caption() const { return &extended_media_caption_; } int32 InputInvoice::get_duration(const Td *td) const { return extended_media_.get_duration(td); } FileId InputInvoice::get_upload_file_id() const { return extended_media_.get_upload_file_id(); } FileId InputInvoice::get_any_file_id() const { return extended_media_.get_any_file_id(); } FileId InputInvoice::get_thumbnail_file_id(const Td *td) const { return extended_media_.get_thumbnail_file_id(td); } void InputInvoice::update_from(const InputInvoice &old_input_invoice) { extended_media_.update_from(old_input_invoice.extended_media_); } bool InputInvoice::update_extended_media(telegram_api::object_ptr extended_media, DialogId owner_dialog_id, Td *td) { return extended_media_.update_to(td, std::move(extended_media), owner_dialog_id); } bool InputInvoice::need_poll_extended_media() const { return extended_media_.need_poll(); } td_api::object_ptr get_product_info_object(Td *td, const string &title, const string &description, const Photo &photo) { FormattedText formatted_description; formatted_description.text = description; formatted_description.entities = find_entities(formatted_description.text, true, true); return td_api::make_object( title, get_formatted_text_object(td->user_manager_.get(), formatted_description, true, 0), get_photo_object(td->file_manager_.get(), photo)); } } // namespace td