mirror of
https://github.com/MarshalX/telegram-crawler.git
synced 2024-11-28 19:23:11 +01:00
Update content of files
This commit is contained in:
parent
f02d25247a
commit
15e070187d
9 changed files with 0 additions and 2186 deletions
|
@ -1,152 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Working with Different Data Centers</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="How to connect to the closest DC access point for faster interaction with the API, and things to watch out for when developing a client.">
|
||||
<meta property="og:title" content="Working with Different Data Centers">
|
||||
<meta property="og:image" content="5bb32ac46255f88a6c">
|
||||
<meta property="og:description" content="How to connect to the closest DC access point for faster interaction with the API, and things to watch out for when developing a client.">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class="active"><a href="/api">API</a></li>
|
||||
<li class=""><a href="/mtproto">Protocol</a></li>
|
||||
<li class=""><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/api/datacenter" >Working with Different Data Centers</a></li></ul></div>
|
||||
<h1 id="dev_page_title">Working with Different Data Centers</h1>
|
||||
|
||||
<div id="dev_page_content"><p>The servers are divided into several data centers (hereinafter “DCs”) in different parts of the world.
|
||||
A complete list of proxy access points for these DCs may be obtained using <a href="/method/help.getConfig">help.getConfig</a>:</p>
|
||||
<pre><code><a href='/constructor/dcOption'>dcOption</a>#18b7a10d flags:<a href='/type/%23'>#</a> ipv6:flags.0?<a href='/constructor/true'>true</a> media_only:flags.1?<a href='/constructor/true'>true</a> tcpo_only:flags.2?<a href='/constructor/true'>true</a> cdn:flags.3?<a href='/constructor/true'>true</a> static:flags.4?<a href='/constructor/true'>true</a> id:<a href='/type/int'>int</a> ip_address:<a href='/type/string'>string</a> port:<a href='/type/int'>int</a> secret:flags.10?<a href='/type/bytes'>bytes</a> = <a href='/type/DcOption'>DcOption</a>;
|
||||
<a href='/constructor/config'>config</a>#330b4067 flags:<a href='/type/%23'>#</a> phonecalls_enabled:flags.1?<a href='/constructor/true'>true</a> default_p2p_contacts:flags.3?<a href='/constructor/true'>true</a> preload_featured_stickers:flags.4?<a href='/constructor/true'>true</a> ignore_phone_entities:flags.5?<a href='/constructor/true'>true</a> revoke_pm_inbox:flags.6?<a href='/constructor/true'>true</a> blocked_mode:flags.8?<a href='/constructor/true'>true</a> pfs_enabled:flags.13?<a href='/constructor/true'>true</a> date:<a href='/type/int'>int</a> expires:<a href='/type/int'>int</a> test_mode:<a href='/type/Bool'>Bool</a> this_dc:<a href='/type/int'>int</a> dc_options:<a href='/type/Vector%20t'>Vector</a><<a href='/type/DcOption'>DcOption</a>> dc_txt_domain_name:<a href='/type/string'>string</a> chat_size_max:<a href='/type/int'>int</a> megagroup_size_max:<a href='/type/int'>int</a> forwarded_count_max:<a href='/type/int'>int</a> online_update_period_ms:<a href='/type/int'>int</a> offline_blur_timeout_ms:<a href='/type/int'>int</a> offline_idle_timeout_ms:<a href='/type/int'>int</a> online_cloud_timeout_ms:<a href='/type/int'>int</a> notify_cloud_delay_ms:<a href='/type/int'>int</a> notify_default_delay_ms:<a href='/type/int'>int</a> push_chat_period_ms:<a href='/type/int'>int</a> push_chat_limit:<a href='/type/int'>int</a> saved_gifs_limit:<a href='/type/int'>int</a> edit_time_limit:<a href='/type/int'>int</a> revoke_time_limit:<a href='/type/int'>int</a> revoke_pm_time_limit:<a href='/type/int'>int</a> rating_e_decay:<a href='/type/int'>int</a> stickers_recent_limit:<a href='/type/int'>int</a> stickers_faved_limit:<a href='/type/int'>int</a> channels_read_media_period:<a href='/type/int'>int</a> tmp_sessions:flags.0?<a href='/type/int'>int</a> pinned_dialogs_count_max:<a href='/type/int'>int</a> pinned_infolder_count_max:<a href='/type/int'>int</a> call_receive_timeout_ms:<a href='/type/int'>int</a> call_ring_timeout_ms:<a href='/type/int'>int</a> call_connect_timeout_ms:<a href='/type/int'>int</a> call_packet_timeout_ms:<a href='/type/int'>int</a> me_url_prefix:<a href='/type/string'>string</a> autoupdate_url_prefix:flags.7?<a href='/type/string'>string</a> gif_search_username:flags.9?<a href='/type/string'>string</a> venue_search_username:flags.10?<a href='/type/string'>string</a> img_search_username:flags.11?<a href='/type/string'>string</a> static_maps_provider:flags.12?<a href='/type/string'>string</a> caption_length_max:<a href='/type/int'>int</a> message_length_max:<a href='/type/int'>int</a> webfile_dc_id:<a href='/type/int'>int</a> suggested_lang_code:flags.2?<a href='/type/string'>string</a> lang_pack_version:flags.2?<a href='/type/int'>int</a> base_lang_pack_version:flags.2?<a href='/type/int'>int</a> = <a href='/type/Config'>Config</a>;
|
||||
---functions---
|
||||
<a href='/method/help.getConfig'>help.getConfig</a>#c4f9186b = <a href='/type/Config'>Config</a>;</code></pre>
|
||||
<p>In this context, <strong>this_dc</strong> is the number of the current DC, <strong>dc_options</strong> is a list of all DCs available at the moment, each of which has an <strong>id</strong>, <strong>ip</strong>, and <strong>port</strong> for establishing a connection. Please note that <strong>ip</strong> and <strong>port</strong> may change frequently, based on proxy server load and the user's current location.<br>
|
||||
Typically, each DC has at least one IPv4 and one IPv6 endpoint available. </p>
|
||||
<p>To optimize client communication with the API, each client must use the connection to the closest access point for its main queries (sending messages, getting contacts, etc.). Therefore, knowing how to select a DC is required before communicating with the API.</p>
|
||||
<h3><a class="anchor" href="#registrationauthorization" id="registrationauthorization" name="registrationauthorization"><i class="anchor-icon"></i></a>Registration/Authorization</h3>
|
||||
<p>The <a href="/method/auth.sendCode">auth.sendCode</a> method is the basic entry point when registering a new user or authorizing an existing user. 95% of all redirection cases to a different DC will occur when invoking this method.</p>
|
||||
<p>The client does not yet know which DC it will be associated with; therefore, it establishes an encrypted connection to a random address and sends its query to that address.
|
||||
Having received a <strong>phone_number</strong> from a client, we can find out whether or not it is registered in the system. If it is, then, if necessary, instead of sending a text message, we request that it establish a connection with a different DC first (PHONE_MIGRATE_X error).
|
||||
If we do not yet have a user with this number, we examine its IP-address. We can use it to identify the closest DC. Again, if necessary, we redirect the user to a different DC (NETWORK_MIGRATE_X error).</p>
|
||||
<h4><a class="anchor" href="#testing-redirects" id="testing-redirects" name="testing-redirects"><i class="anchor-icon"></i></a>Testing Redirects</h4>
|
||||
<p>There are reserved phone number prefixes to test the correctness of the application's handling of redirects between DCs. Read more in <a href="https://core.telegram.org/api/auth#test-phone-numbers">User Authorization</a> article.</p>
|
||||
<h3><a class="anchor" href="#file-access" id="file-access" name="file-access"><i class="anchor-icon"></i></a>File Access</h3>
|
||||
<p>A file saved by a user with <a href="/method/upload.saveFilePart">upload.saveFilePart</a> will be available for direct download only from the DC where the query was executed. That is why each file has a <strong>dc_id</strong> parameter:</p>
|
||||
<pre><code><a href='/constructor/document'>document</a>#1e87342b flags:<a href='/type/%23'>#</a> id:<a href='/type/long'>long</a> access_hash:<a href='/type/long'>long</a> file_reference:<a href='/type/bytes'>bytes</a> date:<a href='/type/int'>int</a> mime_type:<a href='/type/string'>string</a> size:<a href='/type/int'>int</a> thumbs:flags.0?<a href='/type/Vector%20t'>Vector</a><<a href='/type/PhotoSize'>PhotoSize</a>> video_thumbs:flags.1?<a href='/type/Vector%20t'>Vector</a><<a href='/type/VideoSize'>VideoSize</a>> dc_id:<a href='/type/int'>int</a> attributes:<a href='/type/Vector%20t'>Vector</a><<a href='/type/DocumentAttribute'>DocumentAttribute</a>> = <a href='/type/Document'>Document</a>;
|
||||
<a href='/constructor/photo'>photo</a>#fb197a65 flags:<a href='/type/%23'>#</a> has_stickers:flags.0?<a href='/constructor/true'>true</a> id:<a href='/type/long'>long</a> access_hash:<a href='/type/long'>long</a> file_reference:<a href='/type/bytes'>bytes</a> date:<a href='/type/int'>int</a> sizes:<a href='/type/Vector%20t'>Vector</a><<a href='/type/PhotoSize'>PhotoSize</a>> video_sizes:flags.1?<a href='/type/Vector%20t'>Vector</a><<a href='/type/VideoSize'>VideoSize</a>> dc_id:<a href='/type/int'>int</a> = <a href='/type/Photo'>Photo</a>;
|
||||
|
||||
<a href='/constructor/encryptedFile'>encryptedFile</a>#4a70994c id:<a href='/type/long'>long</a> access_hash:<a href='/type/long'>long</a> size:<a href='/type/int'>int</a> dc_id:<a href='/type/int'>int</a> key_fingerprint:<a href='/type/int'>int</a> = <a href='/type/EncryptedFile'>EncryptedFile</a>;
|
||||
|
||||
<a href='/constructor/userProfilePhoto'>userProfilePhoto</a>#82d1f706 flags:<a href='/type/%23'>#</a> has_video:flags.0?<a href='/constructor/true'>true</a> photo_id:<a href='/type/long'>long</a> stripped_thumb:flags.1?<a href='/type/bytes'>bytes</a> dc_id:<a href='/type/int'>int</a> = <a href='/type/UserProfilePhoto'>UserProfilePhoto</a>;
|
||||
<a href='/constructor/chatPhoto'>chatPhoto</a>#1c6e1c11 flags:<a href='/type/%23'>#</a> has_video:flags.0?<a href='/constructor/true'>true</a> photo_id:<a href='/type/long'>long</a> stripped_thumb:flags.1?<a href='/type/bytes'>bytes</a> dc_id:<a href='/type/int'>int</a> = <a href='/type/ChatPhoto'>ChatPhoto</a>;</code></pre>
|
||||
<p>To download the file, an encrypted connection to DC <strong>dc_id</strong> must be established and used to execute the <a href="/method/upload.getFile">upload.getFile</a> query.
|
||||
If an attempt is made to download the file over a wrong connection, the FILE_MIGRATE_X error will be returned.</p>
|
||||
<p>Please note that encryption keys are not copied between DCs; therefore, the process of establishing an encrypted connection is started from the very beginning for each new DC. An issued auth_key can be associated with the current authorized user by using an <a href="#authorization-transfer">authorization transfer</a>.</p>
|
||||
<h3><a class="anchor" href="#user-migration" id="user-migration" name="user-migration"><i class="anchor-icon"></i></a>User Migration</h3>
|
||||
<p>During the process of working with the API, user information is accumulated in the DC with which the user is associated. This is the reason a user cannot be associated with a different DC by means of the client. However, in the future, during prolonged communication from an unusual location, we may decide that the user's data must be moved to a different DC. After some time, the data will be copied and the association will be updated. Once this happens, when executing any query transmitted to the old DC, the API will return the USER_MIGRATE_X error. The client will then have to establish a connection with the new DC and repeat the query.</p>
|
||||
<h3><a class="anchor" href="#authorization-transfer" id="authorization-transfer" name="authorization-transfer"><i class="anchor-icon"></i></a>Authorization Transfer</h3>
|
||||
<p>The following methods can be used to eliminate the need for users to enter the code from a text message every time:</p>
|
||||
<pre><code><a href='/constructor/auth.exportedAuthorization'>auth.exportedAuthorization</a>#b434e2b8 id:<a href='/type/long'>long</a> bytes:<a href='/type/bytes'>bytes</a> = <a href='/type/auth.ExportedAuthorization'>auth.ExportedAuthorization</a>;
|
||||
<a href='/constructor/auth.authorization'>auth.authorization</a>#33fb7bb8 flags:<a href='/type/%23'>#</a> setup_password_required:flags.1?<a href='/constructor/true'>true</a> otherwise_relogin_days:flags.1?<a href='/type/int'>int</a> tmp_sessions:flags.0?<a href='/type/int'>int</a> user:<a href='/type/User'>User</a> = <a href='/type/auth.Authorization'>auth.Authorization</a>;
|
||||
---functions---
|
||||
<a href='/method/auth.importAuthorization'>auth.importAuthorization</a>#a57a7dad id:<a href='/type/long'>long</a> bytes:<a href='/type/bytes'>bytes</a> = <a href='/type/auth.Authorization'>auth.Authorization</a>;
|
||||
<a href='/method/auth.exportAuthorization'>auth.exportAuthorization</a>#e5bfffcd dc_id:<a href='/type/int'>int</a> = <a href='/type/auth.ExportedAuthorization'>auth.ExportedAuthorization</a>;</code></pre>
|
||||
<p><a href="/method/auth.exportAuthorization">auth.exportAuthorization</a> must be executed in the current DC (the DC with which a connection has already been established), passing in <strong>dc_id</strong> as the value for the new DC. The method should return the user identifier and a long string of random data. An import operation can be performed at the new DC by sending it what was received. Queries requiring authorization can then be successfully executed in the new DC.</p></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
|
||||
<script>backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,366 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Payments API</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="You can accept payments from Telegram users via Telegram Bots.
|
||||
Note: This article is intended for MTProto API developers.…">
|
||||
<meta property="og:title" content="Payments API">
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:description" content="You can accept payments from Telegram users via Telegram Bots.
|
||||
Note: This article is intended for MTProto API developers.…">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class="active"><a href="/api">API</a></li>
|
||||
<li class=""><a href="/mtproto">Protocol</a></li>
|
||||
<li class=""><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/api/payments" >Payments API</a></li></ul></div>
|
||||
<h1 id="dev_page_title">Payments API</h1>
|
||||
|
||||
<div id="dev_page_content"><!-- scroll_nav -->
|
||||
|
||||
<p>You can accept payments from Telegram users via <a href="/bots">Telegram Bots</a>.</p>
|
||||
<blockquote>
|
||||
<p>Note: This article is intended for MTProto API developers. If you're looking for a general overview of Telegram Payments, check out the <a href="https://telegram.org/blog/payments">Telegram blog</a> and the <a href="/bots/payments">bot API payment manual</a>.</p>
|
||||
</blockquote>
|
||||
<h3><a class="anchor" href="#introducing-payments" id="introducing-payments" name="introducing-payments"><i class="anchor-icon"></i></a>Introducing Payments</h3>
|
||||
<p>Telegram bots can accept payments for goods and services from users.
|
||||
For more info on how payments work, check out the <a href="https://telegram.org/blog/payments">Telegram Blog</a> and the <a href="/bots/payments">bot API payment manual</a>.</p>
|
||||
<p>This page will elaborate on the actions required to work with payments using the <strong>MTProto API</strong>.</p>
|
||||
<blockquote>
|
||||
<p>A simplified version of the process is available only for bots using the <a href="/bots/payments">bot API</a>.</p>
|
||||
</blockquote>
|
||||
<p>The first step for bots is <a href="/bots/payments#the-payments-api">enable payments as described here »</a>.</p>
|
||||
<p>Then, we work with payments as follows.</p>
|
||||
<h3><a class="anchor" href="#1-create-invoice" id="1-create-invoice" name="1-create-invoice"><i class="anchor-icon"></i></a>1. Create Invoice</h3>
|
||||
<pre><code><a href='/constructor/inputWebDocument'>inputWebDocument</a>#9bed434d url:<a href='/type/string'>string</a> size:<a href='/type/int'>int</a> mime_type:<a href='/type/string'>string</a> attributes:<a href='/type/Vector%20t'>Vector</a><<a href='/type/DocumentAttribute'>DocumentAttribute</a>> = <a href='/type/InputWebDocument'>InputWebDocument</a>;
|
||||
|
||||
<a href='/constructor/labeledPrice'>labeledPrice</a>#cb296bf8 label:<a href='/type/string'>string</a> amount:<a href='/type/long'>long</a> = <a href='/type/LabeledPrice'>LabeledPrice</a>;
|
||||
|
||||
<a href='/constructor/invoice'>invoice</a>#cd886e0 flags:<a href='/type/%23'>#</a> test:flags.0?<a href='/constructor/true'>true</a> name_requested:flags.1?<a href='/constructor/true'>true</a> phone_requested:flags.2?<a href='/constructor/true'>true</a> email_requested:flags.3?<a href='/constructor/true'>true</a> shipping_address_requested:flags.4?<a href='/constructor/true'>true</a> flexible:flags.5?<a href='/constructor/true'>true</a> phone_to_provider:flags.6?<a href='/constructor/true'>true</a> email_to_provider:flags.7?<a href='/constructor/true'>true</a> currency:<a href='/type/string'>string</a> prices:<a href='/type/Vector%20t'>Vector</a><<a href='/type/LabeledPrice'>LabeledPrice</a>> max_tip_amount:flags.8?<a href='/type/long'>long</a> suggested_tip_amounts:flags.8?<a href='/type/Vector%20t'>Vector</a><<a href='/type/long'>long</a>> = <a href='/type/Invoice'>Invoice</a>;
|
||||
|
||||
<a href='/constructor/inputMediaInvoice'>inputMediaInvoice</a>#d9799874 flags:<a href='/type/%23'>#</a> title:<a href='/type/string'>string</a> description:<a href='/type/string'>string</a> photo:flags.0?<a href='/type/InputWebDocument'>InputWebDocument</a> invoice:<a href='/type/Invoice'>Invoice</a> payload:<a href='/type/bytes'>bytes</a> provider:<a href='/type/string'>string</a> provider_data:<a href='/type/DataJSON'>DataJSON</a> start_param:flags.1?<a href='/type/string'>string</a> = <a href='/type/InputMedia'>InputMedia</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/messages.sendMedia'>messages.sendMedia</a>#e25ff8e0 flags:<a href='/type/%23'>#</a> silent:flags.5?<a href='/constructor/true'>true</a> background:flags.6?<a href='/constructor/true'>true</a> clear_draft:flags.7?<a href='/constructor/true'>true</a> noforwards:flags.14?<a href='/constructor/true'>true</a> peer:<a href='/type/InputPeer'>InputPeer</a> reply_to_msg_id:flags.0?<a href='/type/int'>int</a> media:<a href='/type/InputMedia'>InputMedia</a> message:<a href='/type/string'>string</a> random_id:<a href='/type/long'>long</a> reply_markup:flags.2?<a href='/type/ReplyMarkup'>ReplyMarkup</a> entities:flags.3?<a href='/type/Vector%20t'>Vector</a><<a href='/type/MessageEntity'>MessageEntity</a>> schedule_date:flags.10?<a href='/type/int'>int</a> send_as:flags.13?<a href='/type/InputPeer'>InputPeer</a> = <a href='/type/Updates'>Updates</a>;</code></pre>
|
||||
<p>The user contacts the bot and requests to purchase something.
|
||||
The bot forms an <a href="/constructor/inputMediaInvoice">inputMediaInvoice</a> with an <a href="/constructor/invoice">invoice</a> constructor with a description of the goods or service, amount to be paid, as well as requested shipping info.
|
||||
The <code>provider</code> parameter of the <a href="/constructor/inputMediaInvoice">inputMediaInvoice</a> constructor is where you put the token value that <a href="/bots/payments#the-payments-api">you've obtained earlier via Botfather</a>. It is possible for one merchant bot to use several different tokens for different users or different goods and services.</p>
|
||||
<p>Use the <a href="/method/messages.sendMedia">messages.sendMedia</a> method to send the <a href="/constructor/invoice">invoice</a>.
|
||||
You can also attach an inline keyboard to the message using the <code>reply_markup</code> field: if provided, the first button must be a <a href="/constructor/keyboardButtonBuy">keyboardButtonBuy</a> button. Otherwise, an inline keyboard will be generated automatically, with a <code>Pay 'total price'</code> <a href="/constructor/keyboardButtonBuy">keyboardButtonBuy</a> as only button.</p>
|
||||
<p>An invoice message with a pay button can only be sent to a private chat with the user. Groups and channels are not supported.</p>
|
||||
<h3><a class="anchor" href="#2-order-information" id="2-order-information" name="2-order-information"><i class="anchor-icon"></i></a>2. Order information</h3>
|
||||
<h4><a class="anchor" href="#21-invoice-message" id="21-invoice-message" name="21-invoice-message"><i class="anchor-icon"></i></a>2.1 Invoice message</h4>
|
||||
<pre><code><a href='/constructor/keyboardButtonBuy'>keyboardButtonBuy</a>#afd93fbb text:<a href='/type/string'>string</a> = <a href='/type/KeyboardButton'>KeyboardButton</a>;
|
||||
|
||||
<a href='/constructor/keyboardButtonRow'>keyboardButtonRow</a>#77608b83 buttons:<a href='/type/Vector%20t'>Vector</a><<a href='/type/KeyboardButton'>KeyboardButton</a>> = <a href='/type/KeyboardButtonRow'>KeyboardButtonRow</a>;
|
||||
<a href='/constructor/replyInlineMarkup'>replyInlineMarkup</a>#48a30254 rows:<a href='/type/Vector%20t'>Vector</a><<a href='/type/KeyboardButtonRow'>KeyboardButtonRow</a>> = <a href='/type/ReplyMarkup'>ReplyMarkup</a>;
|
||||
|
||||
<a href='/constructor/webDocument'>webDocument</a>#1c570ed1 url:<a href='/type/string'>string</a> access_hash:<a href='/type/long'>long</a> size:<a href='/type/int'>int</a> mime_type:<a href='/type/string'>string</a> attributes:<a href='/type/Vector%20t'>Vector</a><<a href='/type/DocumentAttribute'>DocumentAttribute</a>> = <a href='/type/WebDocument'>WebDocument</a>;
|
||||
<a href='/constructor/webDocumentNoProxy'>webDocumentNoProxy</a>#f9c8bcc6 url:<a href='/type/string'>string</a> size:<a href='/type/int'>int</a> mime_type:<a href='/type/string'>string</a> attributes:<a href='/type/Vector%20t'>Vector</a><<a href='/type/DocumentAttribute'>DocumentAttribute</a>> = <a href='/type/WebDocument'>WebDocument</a>;
|
||||
|
||||
<a href='/constructor/messageMediaInvoice'>messageMediaInvoice</a>#84551347 flags:<a href='/type/%23'>#</a> shipping_address_requested:flags.1?<a href='/constructor/true'>true</a> test:flags.3?<a href='/constructor/true'>true</a> title:<a href='/type/string'>string</a> description:<a href='/type/string'>string</a> photo:flags.0?<a href='/type/WebDocument'>WebDocument</a> receipt_msg_id:flags.2?<a href='/type/int'>int</a> currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> start_param:<a href='/type/string'>string</a> = <a href='/type/MessageMedia'>MessageMedia</a>;
|
||||
|
||||
<a href='/constructor/message'>message</a>#38116ee0 flags:<a href='/type/%23'>#</a> out:flags.1?<a href='/constructor/true'>true</a> mentioned:flags.4?<a href='/constructor/true'>true</a> media_unread:flags.5?<a href='/constructor/true'>true</a> silent:flags.13?<a href='/constructor/true'>true</a> post:flags.14?<a href='/constructor/true'>true</a> from_scheduled:flags.18?<a href='/constructor/true'>true</a> legacy:flags.19?<a href='/constructor/true'>true</a> edit_hide:flags.21?<a href='/constructor/true'>true</a> pinned:flags.24?<a href='/constructor/true'>true</a> noforwards:flags.26?<a href='/constructor/true'>true</a> id:<a href='/type/int'>int</a> from_id:flags.8?<a href='/type/Peer'>Peer</a> peer_id:<a href='/type/Peer'>Peer</a> fwd_from:flags.2?<a href='/type/MessageFwdHeader'>MessageFwdHeader</a> via_bot_id:flags.11?<a href='/type/long'>long</a> reply_to:flags.3?<a href='/type/MessageReplyHeader'>MessageReplyHeader</a> date:<a href='/type/int'>int</a> message:<a href='/type/string'>string</a> media:flags.9?<a href='/type/MessageMedia'>MessageMedia</a> reply_markup:flags.6?<a href='/type/ReplyMarkup'>ReplyMarkup</a> entities:flags.7?<a href='/type/Vector%20t'>Vector</a><<a href='/type/MessageEntity'>MessageEntity</a>> views:flags.10?<a href='/type/int'>int</a> forwards:flags.10?<a href='/type/int'>int</a> replies:flags.23?<a href='/type/MessageReplies'>MessageReplies</a> edit_date:flags.15?<a href='/type/int'>int</a> post_author:flags.16?<a href='/type/string'>string</a> grouped_id:flags.17?<a href='/type/long'>long</a> reactions:flags.20?<a href='/type/MessageReactions'>MessageReactions</a> restriction_reason:flags.22?<a href='/type/Vector%20t'>Vector</a><<a href='/type/RestrictionReason'>RestrictionReason</a>> ttl_period:flags.25?<a href='/type/int'>int</a> = <a href='/type/Message'>Message</a>;
|
||||
|
||||
<a href='/constructor/updateNewMessage'>updateNewMessage</a>#1f2b0afd message:<a href='/type/Message'>Message</a> pts:<a href='/type/int'>int</a> pts_count:<a href='/type/int'>int</a> = <a href='/type/Update'>Update</a>;</code></pre>
|
||||
<p>The user receives an <a href="/constructor/updateNewMessage">updateNewMessage</a> constructor from the bot, containing a <a href="/constructor/messageMediaInvoice">messageMediaInvoice</a> constructor with basic info about the product.</p>
|
||||
<p>The <a href="/constructor/message">message</a> will also have a <a href="/constructor/replyInlineMarkup">replyInlineMarkup</a> keyboard attached to it.
|
||||
The the first button of the keyboard will always be a <a href="/constructor/keyboardButtonBuy">keyboardButtonBuy</a> button.</p>
|
||||
<h4><a class="anchor" href="#22-getting-invoice-info-about-the-product" id="22-getting-invoice-info-about-the-product" name="22-getting-invoice-info-about-the-product"><i class="anchor-icon"></i></a>2.2 Getting invoice info about the product</h4>
|
||||
<pre><code><a href='/constructor/invoice'>invoice</a>#cd886e0 flags:<a href='/type/%23'>#</a> test:flags.0?<a href='/constructor/true'>true</a> name_requested:flags.1?<a href='/constructor/true'>true</a> phone_requested:flags.2?<a href='/constructor/true'>true</a> email_requested:flags.3?<a href='/constructor/true'>true</a> shipping_address_requested:flags.4?<a href='/constructor/true'>true</a> flexible:flags.5?<a href='/constructor/true'>true</a> phone_to_provider:flags.6?<a href='/constructor/true'>true</a> email_to_provider:flags.7?<a href='/constructor/true'>true</a> currency:<a href='/type/string'>string</a> prices:<a href='/type/Vector%20t'>Vector</a><<a href='/type/LabeledPrice'>LabeledPrice</a>> max_tip_amount:flags.8?<a href='/type/long'>long</a> suggested_tip_amounts:flags.8?<a href='/type/Vector%20t'>Vector</a><<a href='/type/long'>long</a>> = <a href='/type/Invoice'>Invoice</a>;
|
||||
|
||||
<a href='/constructor/paymentRequestedInfo'>paymentRequestedInfo</a>#909c3f94 flags:<a href='/type/%23'>#</a> name:flags.0?<a href='/type/string'>string</a> phone:flags.1?<a href='/type/string'>string</a> email:flags.2?<a href='/type/string'>string</a> shipping_address:flags.3?<a href='/type/PostAddress'>PostAddress</a> = <a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a>;
|
||||
|
||||
<a href='/constructor/paymentSavedCredentialsCard'>paymentSavedCredentialsCard</a>#cdc27a1f id:<a href='/type/string'>string</a> title:<a href='/type/string'>string</a> = <a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/payments.getPaymentForm'>payments.getPaymentForm</a>#8a333c8d flags:<a href='/type/%23'>#</a> peer:<a href='/type/InputPeer'>InputPeer</a> msg_id:<a href='/type/int'>int</a> theme_params:flags.0?<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;</code></pre>
|
||||
<p>If the user clicks on the <a href="/constructor/keyboardButtonBuy">keyboardButtonBuy</a> button, the client proceeds to call <a href="/method/payments.getPaymentForm">payments.getPaymentForm</a> with the message ID of the invoice preview message to get the <a href="/constructor/payments.paymentForm">payment form</a>.</p>
|
||||
<p>The returned form will contain fields that should be passed to the payment provider along with the full <a href="/constructor/invoice">invoice</a>.
|
||||
The payment form also contains info about previously saved payment credentials and order information (name, phone number, email, shipping address & so on).</p>
|
||||
<p>The full <a href="/constructor/invoice">invoice</a> contains info about the information required for the order, the price and the currency, and whether this is a <code>test</code> order.</p>
|
||||
<h4><a class="anchor" href="#23-verifying-information" id="23-verifying-information" name="23-verifying-information"><i class="anchor-icon"></i></a>2.3 Verifying information</h4>
|
||||
<pre><code><a href='/constructor/invoice'>invoice</a>#cd886e0 flags:<a href='/type/%23'>#</a> test:flags.0?<a href='/constructor/true'>true</a> name_requested:flags.1?<a href='/constructor/true'>true</a> phone_requested:flags.2?<a href='/constructor/true'>true</a> email_requested:flags.3?<a href='/constructor/true'>true</a> shipping_address_requested:flags.4?<a href='/constructor/true'>true</a> flexible:flags.5?<a href='/constructor/true'>true</a> phone_to_provider:flags.6?<a href='/constructor/true'>true</a> email_to_provider:flags.7?<a href='/constructor/true'>true</a> currency:<a href='/type/string'>string</a> prices:<a href='/type/Vector%20t'>Vector</a><<a href='/type/LabeledPrice'>LabeledPrice</a>> max_tip_amount:flags.8?<a href='/type/long'>long</a> suggested_tip_amounts:flags.8?<a href='/type/Vector%20t'>Vector</a><<a href='/type/long'>long</a>> = <a href='/type/Invoice'>Invoice</a>;
|
||||
|
||||
<a href='/constructor/postAddress'>postAddress</a>#1e8caaeb street_line1:<a href='/type/string'>string</a> street_line2:<a href='/type/string'>string</a> city:<a href='/type/string'>string</a> state:<a href='/type/string'>string</a> country_iso2:<a href='/type/string'>string</a> post_code:<a href='/type/string'>string</a> = <a href='/type/PostAddress'>PostAddress</a>;
|
||||
|
||||
<a href='/constructor/paymentRequestedInfo'>paymentRequestedInfo</a>#909c3f94 flags:<a href='/type/%23'>#</a> name:flags.0?<a href='/type/string'>string</a> phone:flags.1?<a href='/type/string'>string</a> email:flags.2?<a href='/type/string'>string</a> shipping_address:flags.3?<a href='/type/PostAddress'>PostAddress</a> = <a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a>;
|
||||
|
||||
<a href='/constructor/payments.validatedRequestedInfo'>payments.validatedRequestedInfo</a>#d1451883 flags:<a href='/type/%23'>#</a> id:flags.0?<a href='/type/string'>string</a> shipping_options:flags.1?<a href='/type/Vector%20t'>Vector</a><<a href='/type/ShippingOption'>ShippingOption</a>> = <a href='/type/payments.ValidatedRequestedInfo'>payments.ValidatedRequestedInfo</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/payments.validateRequestedInfo'>payments.validateRequestedInfo</a>#db103170 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> peer:<a href='/type/InputPeer'>InputPeer</a> msg_id:<a href='/type/int'>int</a> info:<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> = <a href='/type/payments.ValidatedRequestedInfo'>payments.ValidatedRequestedInfo</a>;</code></pre>
|
||||
<p>If any data at all is requested by the <a href="/constructor/invoice"><strong>invoice</strong></a> (<code>name_requested</code>, <code>phone_requested</code>, <code>email_requested</code>, <code>shipping_address_requested</code>), the user must call <a href="/method/payments.validateRequestedInfo">payments.validateRequestedInfo</a>, providing the required data (as usual, <code>msg_id</code> is the ID of the invoice message).
|
||||
The user can choose to save order information for future use by setting the <code>save</code> flag.
|
||||
Data can be autofilled as described in <a href="#2-3-1-autofill">autofill</a>.</p>
|
||||
<p>If no errors are found in the submitted info, the <a href="/constructor/payments.ValidatedRequestedInfo">response</a> of the method will contain an <code>id</code> flag, to be used later to complete the payment.</p>
|
||||
<p>If the <code>flexible</code> flag of the invoice is set, calling the <a href="/method/payments.validateRequestedInfo">payments.validateRequestedInfo</a> method will send a <a href="/constructor/updateBotShippingQuery">shipping query update</a> to the bot, to which the bot will reply with the available shipping options for the specified address <a href="#2-4-select-delivery-option">as described here »</a>.
|
||||
The return value in this case will also contain a <code>shipping_options</code> field with the available shipping options.</p>
|
||||
<p>If any errors are found in the submitted data, a <a href="/constructor/updateServiceNotification">service notification</a> will be sent to the user, with a description of the error from the bot.</p>
|
||||
<h4><a class="anchor" href="#231-autofill" id="231-autofill" name="231-autofill"><i class="anchor-icon"></i></a>2.3.1 Autofill</h4>
|
||||
<pre><code><a href='/constructor/payments.savedInfo'>payments.savedInfo</a>#fb8fe43c flags:<a href='/type/%23'>#</a> has_saved_credentials:flags.1?<a href='/constructor/true'>true</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> = <a href='/type/payments.SavedInfo'>payments.SavedInfo</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/payments.getSavedInfo'>payments.getSavedInfo</a>#227d824b = <a href='/type/payments.SavedInfo'>payments.SavedInfo</a>;
|
||||
<a href='/method/payments.clearSavedInfo'>payments.clearSavedInfo</a>#d83d70c1 flags:<a href='/type/%23'>#</a> credentials:flags.0?<a href='/constructor/true'>true</a> info:flags.1?<a href='/constructor/true'>true</a> = <a href='/type/Bool'>Bool</a>;</code></pre>
|
||||
<p>The requested fields can be autofilled with the info provided in the <code>saved_info</code> field of the <a href="/constructor/payments.paymentForm">payment form</a>, or with the info fetched manually using <a href="/method/payments.getSavedInfo">payments.getSavedInfo</a>.</p>
|
||||
<p>Saved order information can also be cleared using <a href="/method/payments.clearSavedInfo">payments.clearSavedInfo</a>.</p>
|
||||
<h4><a class="anchor" href="#24-select-delivery-option" id="24-select-delivery-option" name="24-select-delivery-option"><i class="anchor-icon"></i></a>2.4 Select delivery option</h4>
|
||||
<pre><code><a href='/constructor/labeledPrice'>labeledPrice</a>#cb296bf8 label:<a href='/type/string'>string</a> amount:<a href='/type/long'>long</a> = <a href='/type/LabeledPrice'>LabeledPrice</a>;
|
||||
|
||||
<a href='/constructor/shippingOption'>shippingOption</a>#b6213cdf id:<a href='/type/string'>string</a> title:<a href='/type/string'>string</a> prices:<a href='/type/Vector%20t'>Vector</a><<a href='/type/LabeledPrice'>LabeledPrice</a>> = <a href='/type/ShippingOption'>ShippingOption</a>;
|
||||
|
||||
<a href='/constructor/updateBotShippingQuery'>updateBotShippingQuery</a>#b5aefd7d query_id:<a href='/type/long'>long</a> user_id:<a href='/type/long'>long</a> payload:<a href='/type/bytes'>bytes</a> shipping_address:<a href='/type/PostAddress'>PostAddress</a> = <a href='/type/Update'>Update</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/messages.setBotShippingResults'>messages.setBotShippingResults</a>#e5f672fa flags:<a href='/type/%23'>#</a> query_id:<a href='/type/long'>long</a> error:flags.0?<a href='/type/string'>string</a> shipping_options:flags.1?<a href='/type/Vector%20t'>Vector</a><<a href='/type/ShippingOption'>ShippingOption</a>> = <a href='/type/Bool'>Bool</a>;</code></pre>
|
||||
<p>If a shipping address was requested and the bot included the parameter <code>flexible</code>, when the user <a href="#2-3-verifying-information">validates order information</a> the Telegram API will send an <a href="/constructor/updateBotShippingQuery">updateBotShippingQuery</a> to the bot.
|
||||
The bot must respond using <a href="/method/messages.setBotShippingResults">messages.setBotShippingResults</a> either with a list of possible delivery options and the relevant delivery prices, or with an error (for example, if delivery to the specified address is not possible).</p>
|
||||
<p>The returned shipping options or the shipping error will be returned to the user while <a href="#2-3-verifying-information">validating order information</a>.</p>
|
||||
<h3><a class="anchor" href="#3-payment" id="3-payment" name="3-payment"><i class="anchor-icon"></i></a>3. Payment</h3>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentialsSaved'>inputPaymentCredentialsSaved</a>#c10eb2cf id:<a href='/type/string'>string</a> tmp_password:<a href='/type/bytes'>bytes</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentials'>inputPaymentCredentials</a>#3417d728 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentialsApplePay'>inputPaymentCredentialsApplePay</a>#aa1c39f payment_data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentialsGooglePay'>inputPaymentCredentialsGooglePay</a>#8ac32801 payment_token:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;</code></pre>
|
||||
<h4><a class="anchor" href="#31-web-payment" id="31-web-payment" name="31-web-payment"><i class="anchor-icon"></i></a>3.1 Web payment</h4>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentials'>inputPaymentCredentials</a>#3417d728 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;</code></pre>
|
||||
<p>Typically, payment takes place by opening the <code>url</code> in the specified <a href="/constructor/payments.paymentForm">payment form</a>, which leads to a payment form on the website of the payment gateway.
|
||||
Once the user finishes entering their payment credentials, a <a href="/api/web-events"><code>payment_form_submit</code> web event</a> is generated by the payment gateway, containing <code>data</code> and <code>title</code> JSON fields.</p>
|
||||
<p>The <code>title</code> is used by the client app to represent the payment credentials (typically a censored version of credit card information).
|
||||
The <code>data</code> is used to generate an <a href="/constructor/inputPaymentCredentials">inputPaymentCredentials</a> constructor.
|
||||
Eventually, you can set the <code>save</code> flag to save the credit card info for future use, only if <a href="/api/srp">2FA</a> is enabled.</p>
|
||||
<p>Telegram <strong>does not</strong> have access to your card information. Credit card details will be handled only by the payment system.</p>
|
||||
<h4><a class="anchor" href="#32-native-payment" id="32-native-payment" name="32-native-payment"><i class="anchor-icon"></i></a>3.2 Native payment</h4>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentials'>inputPaymentCredentials</a>#3417d728 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;</code></pre>
|
||||
<p>Most telegram apps support working natively with the native APIs of some payment providers, without opening the website of the payment and receiving a JS event.</p>
|
||||
<p>This is done using the JSON <code>native_params</code> parameters field of the <a href="/constructor/payments.paymentForm">payments.paymentForm</a> constructor, which contains an object, which can contain one or more of the following fields:</p>
|
||||
<ul>
|
||||
<li><code>publishable_key</code>: Stripe API publishable key</li>
|
||||
<li><code>apple_pay_merchant_id</code>: Apple Pay merchant ID</li>
|
||||
<li><code>android_pay_public_key</code>: Android Pay public key</li>
|
||||
<li><code>android_pay_bgcolor</code>: Android Pay form background color</li>
|
||||
<li><code>android_pay_inverse</code>: Whether to use the dark theme in the Android Pay form</li>
|
||||
<li><code>need_country</code>: True, if the user country must be provided,</li>
|
||||
<li><code>need_zip</code>: True, if the user ZIP/postal code must be provided,</li>
|
||||
<li><code>need_cardholder_name</code>: True, if the cardholder name must be provided</li>
|
||||
</ul>
|
||||
<p>The payment gateway to use is decided based on the value of the <code>native_provider</code> field.</p>
|
||||
<h5><a class="anchor" href="#321-stripe" id="321-stripe" name="321-stripe"><i class="anchor-icon"></i></a>3.2.1 Stripe</h5>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentials'>inputPaymentCredentials</a>#3417d728 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;</code></pre>
|
||||
<p>If the <code>native_provider</code> field is set and equal to <code>stripe</code>, the client can make use of the <a href="https://stripe.com/docs/api/tokens/object">native Stripe token APIs</a> with the <code>publishable_key</code> from the <code>native_params</code> to add a payment method to Stripe, and then use the token <code>type</code> and <code>id</code> to generate a JSON object:</p>
|
||||
<pre><code>{"type":"token.type", "id":"token.id"}"</code></pre>
|
||||
<p>The generated JSON object can then be passed to the <code>data</code> field of the <a href="/constructor/inputPaymentCredentials">inputPaymentCredentials</a>.
|
||||
Eventually, you can set the <code>save</code> flag to save the credit card info for future use, only if <a href="/api/srp">2FA</a> is enabled.</p>
|
||||
<p>Telegram <strong>does not</strong> have access to your card information. Credit card details will be handled only by the payment system.</p>
|
||||
<p>Example implementation: <a href="https://github.com/DrKLO/Telegram">Telegram for Android</a>.</p>
|
||||
<h4><a class="anchor" href="#33-apple-pay" id="33-apple-pay" name="33-apple-pay"><i class="anchor-icon"></i></a>3.3 Apple pay</h4>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentialsApplePay'>inputPaymentCredentialsApplePay</a>#aa1c39f payment_data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;</code></pre>
|
||||
<p>On iOS devices, Apple Pay can be used to generate payment data, which is then sent using the <a href="/constructor/inputPaymentCredentialsApplePay">inputPaymentCredentialsApplePay</a> constructor.</p>
|
||||
<p>Example implementation: <a href="https://github.com/TelegramMessenger/Telegram-iOS/">Telegram for iOS</a>.</p>
|
||||
<h4><a class="anchor" href="#34-android-pay" id="34-android-pay" name="34-android-pay"><i class="anchor-icon"></i></a>3.4 Android pay</h4>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentialsGooglePay'>inputPaymentCredentialsGooglePay</a>#8ac32801 payment_token:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;</code></pre>
|
||||
<p>On Android devices, Google Pay can be used to generate payment data, which is then sent using the <a href="/constructor/inputPaymentCredentialsApplePay">inputPaymentCredentialsGooglePay</a> constructor.</p>
|
||||
<p>Example implementation: <a href="https://github.com/DrKLO/Telegram/blob/ff5735503e068a6f1cada09b977f633df7caf98d/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java">Telegram for Android</a>.</p>
|
||||
<h4><a class="anchor" href="#35-using-saved-payment-credentials" id="35-using-saved-payment-credentials" name="35-using-saved-payment-credentials"><i class="anchor-icon"></i></a>3.5 Using saved payment credentials</h4>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentialsSaved'>inputPaymentCredentialsSaved</a>#c10eb2cf id:<a href='/type/string'>string</a> tmp_password:<a href='/type/bytes'>bytes</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
<a href='/constructor/paymentSavedCredentialsCard'>paymentSavedCredentialsCard</a>#cdc27a1f id:<a href='/type/string'>string</a> title:<a href='/type/string'>string</a> = <a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentForm'>payments.paymentForm</a>#1694761b flags:<a href='/type/%23'>#</a> can_save_credentials:flags.2?<a href='/constructor/true'>true</a> password_missing:flags.3?<a href='/constructor/true'>true</a> form_id:<a href='/type/long'>long</a> bot_id:<a href='/type/long'>long</a> invoice:<a href='/type/Invoice'>Invoice</a> provider_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> native_provider:flags.4?<a href='/type/string'>string</a> native_params:flags.4?<a href='/type/DataJSON'>DataJSON</a> saved_info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> saved_credentials:flags.1?<a href='/type/PaymentSavedCredentials'>PaymentSavedCredentials</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentForm'>payments.PaymentForm</a>;
|
||||
|
||||
<a href='/constructor/account.tmpPassword'>account.tmpPassword</a>#db64fd34 tmp_password:<a href='/type/bytes'>bytes</a> valid_until:<a href='/type/int'>int</a> = <a href='/type/account.TmpPassword'>account.TmpPassword</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/account.getTmpPassword'>account.getTmpPassword</a>#449e0b51 password:<a href='/type/InputCheckPasswordSRP'>InputCheckPasswordSRP</a> period:<a href='/type/int'>int</a> = <a href='/type/account.TmpPassword'>account.TmpPassword</a>;</code></pre>
|
||||
<p>To reuse saved payment methods, the <code>saved_credentials</code> field of the <a href="/constructor/payments.paymentForm">payment form</a> is used.
|
||||
The <code>title</code> of the <a href="/constructor/paymentSavedCredentialsCard">paymentSavedCredentialsCard</a> can be used to preview a censored version of credit card info.
|
||||
The <code>id</code> field is provided by the payment provider directly to the Telegram servers when saving the payment method, and identifies the payment method.
|
||||
Full credit card info <strong>is not</strong> saved on Telegram Servers, and cannot be fetched by the user.</p>
|
||||
<p>In order to <strong>use</strong> the saved payment method, <a href="/api/srp">2FA</a> must be enabled: the user must verify their identity by entering their <a href="/api/srp">2FA</a> password, which is then used as described in the <a href="/api/srp">SRP docs</a> to generate SRP parameters which must be passed to <a href="/method/account.getTmpPassword">account.getTmpPassword</a>.</p>
|
||||
<p>The generated temporary password can then be used to make payments using the saved credentials using the <a href="/constructor/inputPaymentCredentialsSaved">inputPaymentCredentialsSaved</a> constructor.</p>
|
||||
<ul>
|
||||
<li>The <code>id</code> field is the <a href="/constructor/paymentSavedCredentialsCard">paymentSavedCredentialsCard</a> <code>id</code>.</li>
|
||||
<li>The <code>tmp_password</code> is the temporary payment password generated by the server, if the user provided a correct <a href="/api/srp">2FA password</a>.</li>
|
||||
</ul>
|
||||
<p>Example implementation: <a href="https://github.com/DrKLO/Telegram">Telegram for Android</a>.</p>
|
||||
<h3><a class="anchor" href="#4-pre-checkout" id="4-pre-checkout" name="4-pre-checkout"><i class="anchor-icon"></i></a>4. Pre-Checkout</h3>
|
||||
<pre><code><a href='/constructor/inputPaymentCredentialsSaved'>inputPaymentCredentialsSaved</a>#c10eb2cf id:<a href='/type/string'>string</a> tmp_password:<a href='/type/bytes'>bytes</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentials'>inputPaymentCredentials</a>#3417d728 flags:<a href='/type/%23'>#</a> save:flags.0?<a href='/constructor/true'>true</a> data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentialsApplePay'>inputPaymentCredentialsApplePay</a>#aa1c39f payment_data:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
<a href='/constructor/inputPaymentCredentialsGooglePay'>inputPaymentCredentialsGooglePay</a>#8ac32801 payment_token:<a href='/type/DataJSON'>DataJSON</a> = <a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentResult'>payments.paymentResult</a>#4e5f810d updates:<a href='/type/Updates'>Updates</a> = <a href='/type/payments.PaymentResult'>payments.PaymentResult</a>;
|
||||
<a href='/constructor/payments.paymentVerificationNeeded'>payments.paymentVerificationNeeded</a>#d8411139 url:<a href='/type/string'>string</a> = <a href='/type/payments.PaymentResult'>payments.PaymentResult</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/payments.sendPaymentForm'>payments.sendPaymentForm</a>#30c3bc9d flags:<a href='/type/%23'>#</a> form_id:<a href='/type/long'>long</a> peer:<a href='/type/InputPeer'>InputPeer</a> msg_id:<a href='/type/int'>int</a> requested_info_id:flags.0?<a href='/type/string'>string</a> shipping_option_id:flags.1?<a href='/type/string'>string</a> credentials:<a href='/type/InputPaymentCredentials'>InputPaymentCredentials</a> tip_amount:flags.2?<a href='/type/long'>long</a> = <a href='/type/payments.PaymentResult'>payments.PaymentResult</a>;</code></pre>
|
||||
<p>After <a href="#2-3-verifying-information">verifying order information</a>, the final step for the client is to call <a href="/method/payments.sendPaymentForm">payments.sendPaymentForm</a>, with the following parameters:</p>
|
||||
<ul>
|
||||
<li>The <code>msg_id</code> is set to the ID of the invoice message</li>
|
||||
<li><code>requested_info_id</code> is set to the <code>id</code> of the <a href="#2-3-verifying-information">verified order information</a>, if it was requested</li>
|
||||
<li><code>shipping_option_id</code> is set to the <a href="#2-5-select-delivery-option">selected delivery option</a>, if shipping was requested.</li>
|
||||
<li><code>credentials</code> are the payment credentials generated by the payment provider, required to complete the order.</li>
|
||||
</ul>
|
||||
<p>Payment method info can also be saved to the Telegram Servers and reused, by setting the <code>save</code> flag of <a href="/constructor/inputPaymentCredentials">inputPaymentCredentials</a> when sending the form.
|
||||
This is only possible on accounts with <a href="/api/srp">2FA</a> enabled.</p>
|
||||
<p>The bot then <a href="#4-1-receiving-pre-checkout-query">replies to the received precheckout query</a>, finally the user <a href="#5-checkout">proceeds to checkout</a>.</p>
|
||||
<p>Please note that if the result of the method is a <a href="/constructor/payments.paymentVerificationNeeded">payments.paymentVerificationNeeded</a>, before <a href="#5-checkout">proceeding to checkout</a> the payment provider requires the user to verify his identity by opening the provided <code>url</code> and following instructions.
|
||||
Once the user finishes working with the webpage, the client can <a href="#5-checkout">proceed to checkout</a>.</p>
|
||||
<p>Eventual errors are returned in the form of RPC errors, with the description of the error by the bot contained in <a href="/constructor/updateServiceNotification">service updates</a>.</p>
|
||||
<h4><a class="anchor" href="#41-receiving-pre-checkout-query" id="41-receiving-pre-checkout-query" name="41-receiving-pre-checkout-query"><i class="anchor-icon"></i></a>4.1 Receiving pre-checkout query</h4>
|
||||
<pre><code><a href='/constructor/paymentRequestedInfo'>paymentRequestedInfo</a>#909c3f94 flags:<a href='/type/%23'>#</a> name:flags.0?<a href='/type/string'>string</a> phone:flags.1?<a href='/type/string'>string</a> email:flags.2?<a href='/type/string'>string</a> shipping_address:flags.3?<a href='/type/PostAddress'>PostAddress</a> = <a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a>;
|
||||
|
||||
<a href='/constructor/updateBotPrecheckoutQuery'>updateBotPrecheckoutQuery</a>#8caa9a96 flags:<a href='/type/%23'>#</a> query_id:<a href='/type/long'>long</a> user_id:<a href='/type/long'>long</a> payload:<a href='/type/bytes'>bytes</a> info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> shipping_option_id:flags.1?<a href='/type/string'>string</a> currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> = <a href='/type/Update'>Update</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/messages.setBotPrecheckoutResults'>messages.setBotPrecheckoutResults</a>#9c2dd95 flags:<a href='/type/%23'>#</a> success:flags.1?<a href='/constructor/true'>true</a> query_id:<a href='/type/long'>long</a> error:flags.0?<a href='/type/string'>string</a> = <a href='/type/Bool'>Bool</a>;</code></pre>
|
||||
<p>The user enters their payment information as described above and presses the final pay button.
|
||||
At this moment the Telegram API sends an <a href="/constructor/updateBotPrecheckoutQuery">updateBotPrecheckoutQuery</a> constructor that contains all the available information about the order to the bot.
|
||||
The bot must reply using <a href="/method/messages.setBotPrecheckoutResults">messages.setBotPrecheckoutResults</a> <strong>within 10 seconds</strong> after receiving this update or the transaction is canceled.</p>
|
||||
<p>The bot may return an error if it can't process the order for any reason. We highly recommend specifying a reason for failure to complete the order in human readable form (e.g. "Sorry, we're all out of rubber ducks! Would you be interested in a steel bear instead?"). Telegram will display this reason to the user.</p>
|
||||
<h3><a class="anchor" href="#5-checkout" id="5-checkout" name="5-checkout"><i class="anchor-icon"></i></a>5. Checkout</h3>
|
||||
<pre><code><a href='/constructor/keyboardButtonBuy'>keyboardButtonBuy</a>#afd93fbb text:<a href='/type/string'>string</a> = <a href='/type/KeyboardButton'>KeyboardButton</a>;
|
||||
|
||||
<a href='/constructor/keyboardButtonRow'>keyboardButtonRow</a>#77608b83 buttons:<a href='/type/Vector%20t'>Vector</a><<a href='/type/KeyboardButton'>KeyboardButton</a>> = <a href='/type/KeyboardButtonRow'>KeyboardButtonRow</a>;
|
||||
<a href='/constructor/replyInlineMarkup'>replyInlineMarkup</a>#48a30254 rows:<a href='/type/Vector%20t'>Vector</a><<a href='/type/KeyboardButtonRow'>KeyboardButtonRow</a>> = <a href='/type/ReplyMarkup'>ReplyMarkup</a>;
|
||||
|
||||
<a href='/constructor/messageMediaInvoice'>messageMediaInvoice</a>#84551347 flags:<a href='/type/%23'>#</a> shipping_address_requested:flags.1?<a href='/constructor/true'>true</a> test:flags.3?<a href='/constructor/true'>true</a> title:<a href='/type/string'>string</a> description:<a href='/type/string'>string</a> photo:flags.0?<a href='/type/WebDocument'>WebDocument</a> receipt_msg_id:flags.2?<a href='/type/int'>int</a> currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> start_param:<a href='/type/string'>string</a> = <a href='/type/MessageMedia'>MessageMedia</a>;
|
||||
|
||||
<a href='/constructor/message'>message</a>#38116ee0 flags:<a href='/type/%23'>#</a> out:flags.1?<a href='/constructor/true'>true</a> mentioned:flags.4?<a href='/constructor/true'>true</a> media_unread:flags.5?<a href='/constructor/true'>true</a> silent:flags.13?<a href='/constructor/true'>true</a> post:flags.14?<a href='/constructor/true'>true</a> from_scheduled:flags.18?<a href='/constructor/true'>true</a> legacy:flags.19?<a href='/constructor/true'>true</a> edit_hide:flags.21?<a href='/constructor/true'>true</a> pinned:flags.24?<a href='/constructor/true'>true</a> noforwards:flags.26?<a href='/constructor/true'>true</a> id:<a href='/type/int'>int</a> from_id:flags.8?<a href='/type/Peer'>Peer</a> peer_id:<a href='/type/Peer'>Peer</a> fwd_from:flags.2?<a href='/type/MessageFwdHeader'>MessageFwdHeader</a> via_bot_id:flags.11?<a href='/type/long'>long</a> reply_to:flags.3?<a href='/type/MessageReplyHeader'>MessageReplyHeader</a> date:<a href='/type/int'>int</a> message:<a href='/type/string'>string</a> media:flags.9?<a href='/type/MessageMedia'>MessageMedia</a> reply_markup:flags.6?<a href='/type/ReplyMarkup'>ReplyMarkup</a> entities:flags.7?<a href='/type/Vector%20t'>Vector</a><<a href='/type/MessageEntity'>MessageEntity</a>> views:flags.10?<a href='/type/int'>int</a> forwards:flags.10?<a href='/type/int'>int</a> replies:flags.23?<a href='/type/MessageReplies'>MessageReplies</a> edit_date:flags.15?<a href='/type/int'>int</a> post_author:flags.16?<a href='/type/string'>string</a> grouped_id:flags.17?<a href='/type/long'>long</a> reactions:flags.20?<a href='/type/MessageReactions'>MessageReactions</a> restriction_reason:flags.22?<a href='/type/Vector%20t'>Vector</a><<a href='/type/RestrictionReason'>RestrictionReason</a>> ttl_period:flags.25?<a href='/type/int'>int</a> = <a href='/type/Message'>Message</a>;
|
||||
|
||||
<a href='/constructor/updateNewMessage'>updateNewMessage</a>#1f2b0afd message:<a href='/type/Message'>Message</a> pts:<a href='/type/int'>int</a> pts_count:<a href='/type/int'>int</a> = <a href='/type/Update'>Update</a>;
|
||||
|
||||
<a href='/constructor/payments.paymentReceipt'>payments.paymentReceipt</a>#70c4fe03 flags:<a href='/type/%23'>#</a> date:<a href='/type/int'>int</a> bot_id:<a href='/type/long'>long</a> provider_id:<a href='/type/long'>long</a> title:<a href='/type/string'>string</a> description:<a href='/type/string'>string</a> photo:flags.2?<a href='/type/WebDocument'>WebDocument</a> invoice:<a href='/type/Invoice'>Invoice</a> info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> shipping:flags.1?<a href='/type/ShippingOption'>ShippingOption</a> tip_amount:flags.3?<a href='/type/long'>long</a> currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> credentials_title:<a href='/type/string'>string</a> users:<a href='/type/Vector%20t'>Vector</a><<a href='/type/User'>User</a>> = <a href='/type/payments.PaymentReceipt'>payments.PaymentReceipt</a>;
|
||||
|
||||
---functions---
|
||||
|
||||
<a href='/method/payments.getPaymentReceipt'>payments.getPaymentReceipt</a>#2478d1cc peer:<a href='/type/InputPeer'>InputPeer</a> msg_id:<a href='/type/int'>int</a> = <a href='/type/payments.PaymentReceipt'>payments.PaymentReceipt</a>;</code></pre>
|
||||
<p>In case the bot confirms the order, Telegram requests the payment provider to complete the transaction. If the payment information was entered correctly and the payment goes through, the Telegram API will modify the invoice message and send a service message as described below. Once your bot receives this message, it should proceed with delivering the goods or services purchased by the user.</p>
|
||||
<p>If all is OK, the user receives a <a href="/constructor/payments.paymentResult">payments.paymentResult</a> in reply to the <a href="/method/payments.sendPaymentForm">payments.sendPaymentForm</a> query, containing info about the updated invoice message in the form of an <a href="/constructor/updateEditMessage">updateEditMessage</a>.</p>
|
||||
<p>The invoice message will be updated as follows: the attached <a href="/constructor/messageMediaInvoice">messageMediaInvoice</a> will now have a <code>receipt_msg_id</code> field.
|
||||
Clients should treat invoice messages with a <code>receipt_msg_id</code> field as receipt messages, <strong>locally</strong> modifying the label of the <a href="/constructor/keyboardButtonBuy">keyboardButtonBuy</a> button to a localized version of the word <code>Receipt</code>.
|
||||
From this point, clicking on the <code>Receipt</code> button should trigger a call to <a href="/method/payments.getPaymentReceipt">payments.getPaymentReceipt</a>, providing the <code>receipt_msg_id</code> to the <code>msg_id</code> field, which will return info about the transaction.</p>
|
||||
<p>The payment will also generate one service message of type <a href="/constructor/messageActionPaymentSent">messageActionPaymentSent</a> or <a href="/constructor/messageActionPaymentSentMe">messageActionPaymentSentMe</a>, replying to the invoice.
|
||||
For bots, the service message will be of type <a href="/constructor/messageActionPaymentSentMe">messageActionPaymentSentMe</a>, for users it will be a <a href="/constructor/messageActionPaymentSent">messageActionPaymentSent</a>.</p>
|
||||
<pre><code><a href='/constructor/messageActionPaymentSentMe'>messageActionPaymentSentMe</a>#8f31b327 flags:<a href='/type/%23'>#</a> currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> payload:<a href='/type/bytes'>bytes</a> info:flags.0?<a href='/type/PaymentRequestedInfo'>PaymentRequestedInfo</a> shipping_option_id:flags.1?<a href='/type/string'>string</a> charge:<a href='/type/PaymentCharge'>PaymentCharge</a> = <a href='/type/MessageAction'>MessageAction</a>;
|
||||
<a href='/constructor/messageActionPaymentSent'>messageActionPaymentSent</a>#40699cd0 currency:<a href='/type/string'>string</a> total_amount:<a href='/type/long'>long</a> = <a href='/type/MessageAction'>MessageAction</a>;</code></pre></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
<script src="/js/jquery.min.js?1"></script>
|
||||
<script src="/js/bootstrap.min.js?1"></script>
|
||||
|
||||
<script>window.initDevPageNav&&initDevPageNav();
|
||||
backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>payments.ValidatedRequestedInfo</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="Validated user-provided info">
|
||||
<meta property="og:title" content="payments.ValidatedRequestedInfo">
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:description" content="Validated user-provided info">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class=""><a href="/api">API</a></li>
|
||||
<li class=""><a href="/mtproto">Protocol</a></li>
|
||||
<li class="active"><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/schema" >TL-schema</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/constructor/payments.ValidatedRequestedInfo" >payments.ValidatedRequestedInfo</a></li></ul></div>
|
||||
<h1 id="dev_page_title">payments.ValidatedRequestedInfo</h1>
|
||||
|
||||
<div id="dev_page_content"><p>Validated user-provided info</p>
|
||||
<p>{schema}</p>
|
||||
<h3><a class="anchor" href="#parameters" id="parameters" name="parameters"><i class="anchor-icon"></i></a>Parameters</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th style="text-align: center;">Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>flags</strong></td>
|
||||
<td style="text-align: center;"><a href="/type/%23">#</a></td>
|
||||
<td>Flags, see <a href="/mtproto/TL-combinators#conditional-fields">TL conditional fields</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>id</strong></td>
|
||||
<td style="text-align: center;"><a href="/mtproto/TL-combinators#conditional-fields">flags</a>.0?<a href="/type/string">string</a></td>
|
||||
<td>ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>shipping_options</strong></td>
|
||||
<td style="text-align: center;"><a href="/mtproto/TL-combinators#conditional-fields">flags</a>.1?<a href="/type/Vector%20t">Vector</a><<a href="/type/ShippingOption">ShippingOption</a>></td>
|
||||
<td>Shipping options</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3><a class="anchor" href="#type" id="type" name="type"><i class="anchor-icon"></i></a>Type</h3>
|
||||
<p><a href="/type/payments.ValidatedRequestedInfo">payments.ValidatedRequestedInfo</a></p></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
|
||||
<script>backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,685 +0,0 @@
|
|||
var startTime = +(new Date());
|
||||
function dT() {
|
||||
return '[' + ((+(new Date()) - startTime)/ 1000.0) + '] ';
|
||||
}
|
||||
|
||||
var jsonpCallbacks = [];
|
||||
function twitterCustomShareInit () {
|
||||
var btns = document.querySelectorAll
|
||||
? document.querySelectorAll('.tl_twitter_share_btn')
|
||||
: [document.getElementById('tl_twitter_share_btn')];
|
||||
|
||||
if (!btns.length) {
|
||||
return;
|
||||
}
|
||||
var head = document.getElementsByTagName('head')[0], i, script;
|
||||
for (i = 0; i < btns.length; i++) {
|
||||
(function (btn) {
|
||||
var status = btn.getAttribute('data-text'),
|
||||
url = btn.getAttribute('data-url') || location.toString() || 'https://telegram.org/',
|
||||
via = btn.getAttribute('data-via'),
|
||||
urlEncoded = encodeURIComponent(url),
|
||||
popupUrl = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(status) + '&url=' + urlEncoded + '&via=' + encodeURIComponent(via);
|
||||
|
||||
btn.setAttribute('href', popupUrl);
|
||||
btn.href = popupUrl;
|
||||
|
||||
btn.addEventListener('click', function (e) {
|
||||
var popupW = 550,
|
||||
popupH = 450,
|
||||
params = [
|
||||
'width=' + popupW,
|
||||
'height=' + popupH,
|
||||
'left=' + Math.round(screen.width / 2 - popupW / 2),
|
||||
'top=' + Math.round(screen.height / 2 - popupH / 2),
|
||||
'personalbar=0',
|
||||
'toolbar=0',
|
||||
'scrollbars=1',
|
||||
'resizable=1'
|
||||
].join(','),
|
||||
popup = window.open(popupUrl, '_blank', params);
|
||||
|
||||
if (popup) {
|
||||
try {
|
||||
popup.focus();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return cancelEvent(e);
|
||||
}, false);
|
||||
})(btns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function blogRecentNewsInit () {
|
||||
if (document.querySelectorAll) {
|
||||
var sideImages = document.querySelectorAll('.blog_side_image_wrap');
|
||||
var sideImage, parent, i;
|
||||
var len = len = sideImages.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
sideImage = sideImages[i];
|
||||
parent = sideImage.parentNode.parentNode;
|
||||
if (parent) {
|
||||
parent.insertBefore(sideImage, parent.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var moreBtn = document.getElementById('tlb_blog_head_more_link');
|
||||
if (!moreBtn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var activeClassName = 'tlb_blog_head_recent_active';
|
||||
moreBtn.addEventListener('click', function (event) {
|
||||
var parent = this.parentNode;
|
||||
var className = parent.className;
|
||||
if (className.indexOf(activeClassName) == -1) {
|
||||
className += ' ' + activeClassName;
|
||||
} else {
|
||||
className = className.replace(' ' + activeClassName, '');
|
||||
}
|
||||
parent.className = className;
|
||||
|
||||
return cancelEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
function blogSideImageUpdate(argument) {
|
||||
var isDesktop = document.documentElement.offsetWidth >= 1000
|
||||
document.querySelectorAll('.blog_side_image_wrap').forEach(function (imageWrap) {
|
||||
if (isDesktop) {
|
||||
var titleHeight = imageWrap.parentNode.previousElementSibling.clientHeight;
|
||||
var beforeTitleEl = imageWrap.parentNode.previousElementSibling.previousElementSibling;
|
||||
if (beforeTitleEl) {
|
||||
titleHeight += beforeTitleEl.clientHeight;
|
||||
}
|
||||
imageWrap.firstElementChild.style.marginTop = (-titleHeight - 8) + 'px';
|
||||
} else {
|
||||
imageWrap.firstElementChild.style.marginTop = '';
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function blogSideImageInit() {
|
||||
window.addEventListener('resize', blogSideImageUpdate, false);
|
||||
setTimeout(blogSideImageUpdate, 0);
|
||||
}
|
||||
|
||||
function cancelEvent (event) {
|
||||
event = event || window.event;
|
||||
if (event) event = event.originalEvent || event;
|
||||
|
||||
if (event.stopPropagation) event.stopPropagation();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function trackDlClick (element, event) {
|
||||
var href = element.getAttribute('href'),
|
||||
track = element.getAttribute('data-track') || false;
|
||||
|
||||
if (!track || !window.ga) {
|
||||
return;
|
||||
}
|
||||
|
||||
var trackData = track.toString().split('/');
|
||||
|
||||
ga('send', 'event', trackData[0], trackData[1], href);
|
||||
|
||||
if ((element.getAttribute('target') || '').toLowerCase() != '_blank') {
|
||||
setTimeout(function() { location.href = href; }, 200);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var toTopWrapEl,
|
||||
toTopEl,
|
||||
pageContentWrapEl,
|
||||
curVisible,
|
||||
curShown = false;
|
||||
function backToTopInit (labelHtml) {
|
||||
pageContentWrapEl = document.getElementById('dev_page_content_wrap');
|
||||
if (!pageContentWrapEl) {
|
||||
return false;
|
||||
}
|
||||
var t = document.createElement('div');
|
||||
|
||||
t.innerHTML = '<div class="back_to_top"><i class="icon icon-to-top"></i>' + labelHtml + '</div>';
|
||||
toTopEl = t.firstChild;
|
||||
t.innerHTML = '<a class="back_to_top_wrap' + (pageContentWrapEl.classList.contains('is_rtl') ? ' is_rtl' : '') + '" onclick="backToTopGo()"></a>';
|
||||
toTopWrapEl = t.firstChild;
|
||||
|
||||
toTopWrapEl.appendChild(toTopEl);
|
||||
document.body.appendChild(toTopWrapEl);
|
||||
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('resize', backToTopResize, false);
|
||||
window.addEventListener('scroll', backToTopScroll, false);
|
||||
}
|
||||
backToTopResize();
|
||||
}
|
||||
|
||||
function backToTopGo () {
|
||||
window.scroll(0, 0);
|
||||
backToTopScroll();
|
||||
}
|
||||
|
||||
function backToTopResize () {
|
||||
var left = getXY(pageContentWrapEl)[0],
|
||||
dwidth = Math.max(window.innerWidth, document.documentElement.clientWidth, 0),
|
||||
dheight = Math.max(window.innerHeight, document.documentElement.clientHeight);
|
||||
|
||||
curVisible = pageContentWrapEl && left > 130 && dwidth > 640;
|
||||
toTopWrapEl.style.width = left + 'px';
|
||||
toTopEl.style.height = dheight + 'px';
|
||||
backToTopScroll();
|
||||
}
|
||||
|
||||
function backToTopScroll () {
|
||||
var st = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop || document.documentElement.scrollTop;
|
||||
if ((st > 400 && curVisible) != curShown) {
|
||||
curShown = !curShown;
|
||||
if (curShown) {
|
||||
toTopWrapEl.classList.add('back_to_top_shown');
|
||||
} else {
|
||||
toTopWrapEl.classList.remove('back_to_top_shown');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removePreloadInit() {
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('load', function () {
|
||||
document.body.classList.remove('preload');
|
||||
}, false);
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
document.body.classList.remove('preload');
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
function getXY (obj) {
|
||||
if (!obj) return [0, 0];
|
||||
|
||||
var left = 0, top = 0;
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
left += obj.offsetLeft;
|
||||
top += obj.offsetTop;
|
||||
} while (obj = obj.offsetParent);
|
||||
}
|
||||
return [left, top];
|
||||
}
|
||||
|
||||
|
||||
var onDdBodyClick,
|
||||
currentDd;
|
||||
function dropdownClick (element, event) {
|
||||
var parent = element.parentNode;
|
||||
var isOpen = (parent.className || '').indexOf('open') > 0;
|
||||
if (currentDd && currentDd != parent) {
|
||||
dropdownHide(currentDd);
|
||||
}
|
||||
if (!isOpen) {
|
||||
parent.className = (parent.className || '') + ' open';
|
||||
if (!onDdBodyClick) {
|
||||
window.addEventListener('click', dropdownPageClick, false);
|
||||
}
|
||||
currentDd = parent;
|
||||
} else {
|
||||
dropdownHide(currentDd);
|
||||
currentDd = false;
|
||||
}
|
||||
event.cancelBubble = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function dropdownHide (parent) {
|
||||
parent.className = parent.className.replace(' open', '');
|
||||
}
|
||||
|
||||
function dropdownPageClick (event) {
|
||||
if (currentDd) {
|
||||
dropdownHide(currentDd);
|
||||
currentDd = false;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHTML (html) {
|
||||
html = html || '';
|
||||
return html.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function videoTogglePlay(el) {
|
||||
if (el.paused) {
|
||||
el.play();
|
||||
} else {
|
||||
el.pause();
|
||||
}
|
||||
}
|
||||
|
||||
function getDevPageNav() {
|
||||
// console.time('page nav');
|
||||
var menu = $('<ul class="nav navbar-nav navbar-default"></ul>');
|
||||
var lastLi = false;
|
||||
var items = 0;
|
||||
$('a.anchor').each(function (k, anchor) {
|
||||
var parentTag = anchor.parentNode.tagName;
|
||||
var matches = parentTag.match(/^h([34])$/i);
|
||||
var anchorName = anchor.name;
|
||||
if (!matches || !anchorName) {
|
||||
return;
|
||||
}
|
||||
anchor.id = anchor.name;
|
||||
var level = parseInt(matches[1]);
|
||||
var li = $('<li><a href="#'+ anchorName +'" data-target="#'+ anchorName +'" onmouseenter="showTitleIfOverflows(this)">' + escapeHTML(anchor.nextSibling.textContent) + '</a></li>');
|
||||
if (level == 3) {
|
||||
li.appendTo(menu);
|
||||
lastLi = li;
|
||||
} else {
|
||||
// console.log(lastLi);
|
||||
if (!lastLi) {
|
||||
return;
|
||||
}
|
||||
var subMenu = $('ul', lastLi)[0] || $('<ul class="nav"></ul>').appendTo(lastLi);
|
||||
// console.log(subMenu);
|
||||
li.appendTo(subMenu);
|
||||
}
|
||||
items++;
|
||||
});
|
||||
// console.log(items, menu);
|
||||
// console.timeEnd('page nav');
|
||||
if (items < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
function showTitleIfOverflows(element) {
|
||||
if (element &&
|
||||
element.innerText &&
|
||||
element.scrollWidth &&
|
||||
element.offsetWidth &&
|
||||
element.offsetWidth < element.scrollWidth) {
|
||||
element.setAttribute('title', element.innerText);
|
||||
}
|
||||
else if (element.removeAttribute) {
|
||||
element.removeAttribute('title');
|
||||
}
|
||||
}
|
||||
|
||||
function initDevPageNav() {
|
||||
window.hasDevPageNav = true;
|
||||
var menu = getDevPageNav();
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
var sideNavCont = $('#dev_side_nav_cont');
|
||||
if (!sideNavCont.length) {
|
||||
sideNavCont = $('#dev_page_content_wrap');
|
||||
}
|
||||
var sideNavWrap = $('<div class="dev_side_nav_wrap"></div>').prependTo(sideNavCont);
|
||||
var sideNav = $('<div class="dev_side_nav"></div>').appendTo(sideNavWrap);
|
||||
menu.appendTo(sideNav);
|
||||
$('body').css({position: 'relative'}).scrollspy({ target: '.dev_side_nav' });
|
||||
|
||||
$('body').on('activate.bs.scrollspy', function () {
|
||||
$('.dev_side_nav > ul').affix('checkPosition');
|
||||
var active_el = $('.dev_side_nav li.active').get(-1);
|
||||
if (active_el) {
|
||||
if (active_el.scrollIntoViewIfNeeded) {
|
||||
active_el.scrollIntoViewIfNeeded();
|
||||
} else if (active_el.scrollIntoView) {
|
||||
active_el.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
$('body').trigger('activate.bs.scrollspy');
|
||||
|
||||
updateMenuAffix(menu);
|
||||
}
|
||||
|
||||
function updateDevPageNav() {
|
||||
if (!window.hasDevPageNav) {
|
||||
return;
|
||||
}
|
||||
var menu = getDevPageNav() || $('<ul></ul>');
|
||||
$('.dev_side_nav > ul').replaceWith(menu);
|
||||
$('body').scrollspy('refresh');
|
||||
updateMenuAffix(menu);
|
||||
}
|
||||
|
||||
function updateMenuAffix(menu) {
|
||||
menu.affix({
|
||||
offset: {
|
||||
top: function () {
|
||||
return $('.dev_side_nav_wrap').offset().top;
|
||||
},
|
||||
bottom: function () {
|
||||
return (this.bottom = $('.footer_wrap').outerHeight(true) + 20)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function initScrollVideos(desktop) {
|
||||
var videos = document.querySelectorAll
|
||||
? document.querySelectorAll('video.tl_blog_vid_autoplay')
|
||||
: [];
|
||||
|
||||
window.pageVideos = Array.prototype.slice.apply(videos);
|
||||
if (!pageVideos.length) {
|
||||
return;
|
||||
}
|
||||
window.pageVideosPlaying = {};
|
||||
|
||||
var index = 1;
|
||||
var tgStickersCnt = document.querySelectorAll('.js-tgsticker_image').length;
|
||||
var preloadVideos = tgStickersCnt ? 0 : 2;
|
||||
for (var i = 0; i < pageVideos.length; i++) {
|
||||
var videoEl = pageVideos[i];
|
||||
videoEl.setAttribute('vindex', index++);
|
||||
var preloadValue = i >= preloadVideos ? 'metadata' : 'auto';
|
||||
videoEl.setAttribute('preload', preloadValue);
|
||||
videoEl.preload = preloadValue;
|
||||
if (desktop) {
|
||||
videoEl.removeAttribute('controls');
|
||||
videoEl.autoplay = false;
|
||||
videoEl.removeAttribute('autoplay');
|
||||
} else {
|
||||
videoEl.autoplay = true;
|
||||
videoEl.playsinline = true;
|
||||
videoEl.setAttribute('autoplay', 'autoplay');
|
||||
videoEl.setAttribute('playsinline', 'playsinline');
|
||||
}
|
||||
}
|
||||
if (!desktop) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', checkScrollVideos, false);
|
||||
window.addEventListener('resize', checkScrollVideos, false);
|
||||
setTimeout(checkScrollVideos, 1000);
|
||||
}
|
||||
|
||||
function checkScrollVideos() {
|
||||
var w = window,
|
||||
d = document,
|
||||
e = d.documentElement,
|
||||
g = d.getElementsByTagName('body')[0],
|
||||
winWidth = w.innerWidth || e.clientWidth || g.clientWidth,
|
||||
winHeight = w.innerHeight|| e.clientHeight|| g.clientHeight,
|
||||
scrollTop = e.scrollTop || g.scrollTop || w.pageYOffset;
|
||||
|
||||
for (var i = 0; i < pageVideos.length; i++) {
|
||||
var videoEl = pageVideos[i];
|
||||
var curIndex = videoEl.getAttribute('vindex');
|
||||
var posY = getFullOffsetY(videoEl);
|
||||
var height = videoEl.offsetHeight;
|
||||
// console.log(scrollTop, winHeight, posY, height);
|
||||
|
||||
|
||||
if (isVisibleEnough(posY, height, scrollTop, winHeight, 0.7, 0.9)) {
|
||||
if (!pageVideosPlaying[curIndex]) {
|
||||
pageVideosPlaying[curIndex] = true;
|
||||
console.log('play', videoEl);
|
||||
videoEl.play();
|
||||
}
|
||||
} else {
|
||||
if (pageVideosPlaying[curIndex]) {
|
||||
delete pageVideosPlaying[curIndex];
|
||||
console.log('pause', videoEl);
|
||||
videoEl.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isVisibleEnough(boxOffset, boxSize, viewOffset, viewSize, boxThreshold, viewThreshold) {
|
||||
var boxEnd = boxOffset + boxSize;
|
||||
var viewEnd = viewOffset + viewSize;
|
||||
var viewBox = Math.min(viewEnd, boxEnd) - Math.max(boxOffset, viewOffset);
|
||||
if (viewBox < 0) {
|
||||
return false;
|
||||
}
|
||||
if (viewBox / boxSize > boxThreshold) {
|
||||
return true;
|
||||
}
|
||||
if (viewThreshold && viewBox / viewSize > viewThreshold) {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function getFullOffsetY(el) {
|
||||
var offsetTop = el.offsetTop || 0;
|
||||
if (el.offsetParent) {
|
||||
offsetTop += getFullOffsetY(el.offsetParent);
|
||||
}
|
||||
return offsetTop;
|
||||
}
|
||||
|
||||
function redraw(el) {
|
||||
el.offsetTop + 1;
|
||||
}
|
||||
|
||||
function initRipple() {
|
||||
if (!document.querySelectorAll) return;
|
||||
var rippleTextFields = document.querySelectorAll('.textfield-item input.form-control');
|
||||
for (var i = 0; i < rippleTextFields.length; i++) {
|
||||
(function(rippleTextField) {
|
||||
function onTextRippleStart(e) {
|
||||
if (document.activeElement === rippleTextField) return;
|
||||
var rect = rippleTextField.getBoundingClientRect();
|
||||
if (e.type == 'touchstart') {
|
||||
var clientX = e.targetTouches[0].clientX;
|
||||
} else {
|
||||
var clientX = e.clientX;
|
||||
}
|
||||
var ripple = rippleTextField.parentNode.querySelector('.textfield-item-underline');
|
||||
var rippleX = (clientX - rect.left) / rippleTextField.offsetWidth * 100;
|
||||
ripple.style.transition = 'none';
|
||||
redraw(ripple);
|
||||
ripple.style.left = rippleX + '%';
|
||||
ripple.style.right = (100 - rippleX) + '%';
|
||||
redraw(ripple);
|
||||
ripple.style.left = '';
|
||||
ripple.style.right = '';
|
||||
ripple.style.transition = '';
|
||||
}
|
||||
rippleTextField.removeEventListener('mousedown', onTextRippleStart);
|
||||
rippleTextField.removeEventListener('touchstart', onTextRippleStart);
|
||||
rippleTextField.addEventListener('mousedown', onTextRippleStart);
|
||||
rippleTextField.addEventListener('touchstart', onTextRippleStart);
|
||||
})(rippleTextFields[i]);
|
||||
}
|
||||
var rippleHandlers = document.querySelectorAll('.ripple-handler');
|
||||
for (var i = 0; i < rippleHandlers.length; i++) {
|
||||
(function(rippleHandler) {
|
||||
function onRippleStart(e) {
|
||||
var rippleMask = rippleHandler.querySelector('.ripple-mask');
|
||||
if (!rippleMask) return;
|
||||
var rect = rippleMask.getBoundingClientRect();
|
||||
if (e.type == 'touchstart') {
|
||||
var clientX = e.targetTouches[0].clientX;
|
||||
var clientY = e.targetTouches[0].clientY;
|
||||
} else {
|
||||
var clientX = e.clientX;
|
||||
var clientY = e.clientY;
|
||||
}
|
||||
var rippleX = (clientX - rect.left) - rippleMask.offsetWidth / 2;
|
||||
var rippleY = (clientY - rect.top) - rippleMask.offsetHeight / 2;
|
||||
var ripple = rippleHandler.querySelector('.ripple');
|
||||
ripple.style.transition = 'none';
|
||||
redraw(ripple);
|
||||
ripple.style.transform = 'translate3d(' + rippleX + 'px, ' + rippleY + 'px, 0) scale3d(0.2, 0.2, 1)';
|
||||
ripple.style.opacity = 1;
|
||||
redraw(ripple);
|
||||
ripple.style.transform = 'translate3d(' + rippleX + 'px, ' + rippleY + 'px, 0) scale3d(1, 1, 1)';
|
||||
ripple.style.transition = '';
|
||||
|
||||
function onRippleEnd(e) {
|
||||
ripple.style.transitionDuration = '.2s';
|
||||
ripple.style.opacity = 0;
|
||||
document.removeEventListener('mouseup', onRippleEnd);
|
||||
document.removeEventListener('touchend', onRippleEnd);
|
||||
document.removeEventListener('touchcancel', onRippleEnd);
|
||||
}
|
||||
document.addEventListener('mouseup', onRippleEnd);
|
||||
document.addEventListener('touchend', onRippleEnd);
|
||||
document.addEventListener('touchcancel', onRippleEnd);
|
||||
}
|
||||
rippleHandler.removeEventListener('mousedown', onRippleStart);
|
||||
rippleHandler.removeEventListener('touchstart', onRippleStart);
|
||||
rippleHandler.addEventListener('mousedown', onRippleStart);
|
||||
rippleHandler.addEventListener('touchstart', onRippleStart);
|
||||
})(rippleHandlers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function mainInitRetinaVideos() {
|
||||
var videoEls = document.querySelectorAll('video.video__init_retina');
|
||||
var isRetina = window.devicePixelRatio >= 1.5;
|
||||
var videoEl, i, badChildren, j, badChild, sources, sourceEl;
|
||||
for (i = 0; i < videoEls.length; i++) {
|
||||
videoEl = videoEls[i];
|
||||
sources = (videoEl.getAttribute('data-sources')||'').split(',');
|
||||
sourceEl = document.createElement('source');
|
||||
sourceEl.type = 'video/mp4';
|
||||
sourceEl.src = sources[isRetina ? 1 : 0];
|
||||
videoEl.appendChild(sourceEl);
|
||||
videoEl.classList.remove('video__init_retina');
|
||||
videoEl.setAttribute('preload', 'auto');
|
||||
}
|
||||
}
|
||||
|
||||
function mainInitDemoAutoplay(videoLinkElsSelector) {
|
||||
var videoLinkEls = document.querySelectorAll(videoLinkElsSelector);
|
||||
var videoLinkEl, videoEl, i;
|
||||
for (i = 0; i < videoLinkEls.length; i++) {
|
||||
videoLinkEl = videoLinkEls[i];
|
||||
videoEl = videoLinkEl.querySelector('video');
|
||||
if (!videoEl) {
|
||||
continue;
|
||||
}
|
||||
if (videoEl.readyState > 1) {
|
||||
mainDemoVideoHover(videoLinkEl, 1);
|
||||
} else {
|
||||
videoEl.load();
|
||||
videoEl.addEventListener('loadeddata', (function(el) {
|
||||
return function () {
|
||||
setTimeout(function () {
|
||||
mainDemoVideoHover(el, 1);
|
||||
}, 0)
|
||||
}
|
||||
})(videoLinkEl), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mainDemoVideoHover(videoLinkEl, isHover) {
|
||||
var outTimeout = videoLinkEl.outTimeout;
|
||||
var curIsHover = videoLinkEl.isHover || 0;
|
||||
if (outTimeout) {
|
||||
clearTimeout(outTimeout);
|
||||
}
|
||||
if (curIsHover == isHover) {
|
||||
return false;
|
||||
}
|
||||
if (!isHover) {
|
||||
outTimeout = setTimeout(function () {
|
||||
mainDemoVideoDoHover(videoLinkEl, isHover)
|
||||
}, 100);
|
||||
videoLinkEl.outTimeout = outTimeout;
|
||||
return false;
|
||||
}
|
||||
mainDemoVideoDoHover(videoLinkEl, isHover);
|
||||
}
|
||||
|
||||
function mainDemoVideoDoHover(videoLinkEl, isHover) {
|
||||
delete videoLinkEl.outTimeout;
|
||||
|
||||
var videoEl = videoLinkEl.querySelector('video');
|
||||
if (isHover) {
|
||||
if (videoEl.readyState > 1) {
|
||||
videoLinkEl.classList.add('video_play');
|
||||
videoEl.play();
|
||||
videoLinkEl.isHover = 1;
|
||||
}
|
||||
} else {
|
||||
videoLinkEl.isHover = 0;
|
||||
}
|
||||
if (!videoEl.inited) {
|
||||
videoEl.inited = true;
|
||||
// videoEl.onended =
|
||||
videoEl.addEventListener('ended', function onVideoEnded(e) {
|
||||
if (videoLinkEl.isHover) {
|
||||
videoEl.currentTime = 0;
|
||||
videoEl.play();
|
||||
} else {
|
||||
videoEl.pause();
|
||||
videoEl.currentTime = 0;
|
||||
videoLinkEl.classList.remove('video_play')
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
function mainInitTgStickers(options) {
|
||||
options = options || {};
|
||||
if (!RLottie.isSupported) {
|
||||
if (options.unsupportedURL) {
|
||||
if (!getCookie('stel_notgs')) {
|
||||
setCookie('stel_notgs', 1, 7);
|
||||
}
|
||||
location = options.unsupportedURL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
document.querySelectorAll('.js-tgsticker_image').forEach(function (imgEl) {
|
||||
RLottie.init(imgEl, options);
|
||||
});
|
||||
}
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
var expires = '';
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 86400000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
var nameEQ = name + '=';
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substr(1, c.length);
|
||||
}
|
||||
if (c.indexOf(nameEQ) == 0) {
|
||||
return c.substr(nameEQ.length, c.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function mainScrollTo(element) {
|
||||
if (typeof element === 'string') {
|
||||
element = document.querySelector(element)
|
||||
}
|
||||
if (element) {
|
||||
window.scroll(0, getFullOffsetY(element));
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MTProto Mobile Protocol</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="Please feel free to check out our FAQ for the Technically Inclined.
|
||||
Client developers are required to comply with the Security…">
|
||||
<meta property="og:title" content="MTProto Mobile Protocol">
|
||||
<meta property="og:image" content="58401119934796773c">
|
||||
<meta property="og:description" content="Please feel free to check out our FAQ for the Technically Inclined.
|
||||
Client developers are required to comply with the Security…">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class=""><a href="/api">API</a></li>
|
||||
<li class="active"><a href="/mtproto">Protocol</a></li>
|
||||
<li class=""><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"></div>
|
||||
<h1 id="dev_page_title">MTProto Mobile Protocol</h1>
|
||||
|
||||
<div id="dev_page_content"><!-- scroll_nav -->
|
||||
|
||||
<blockquote>
|
||||
<p>Please feel free to check out our <a href="http://core.telegram.org/techfaq">FAQ for the Technically Inclined</a>.
|
||||
Client developers are required to comply with the <a href="/mtproto/security_guidelines">Security Guidelines</a>.</p>
|
||||
</blockquote>
|
||||
<h3><a class="anchor" href="#related-articles" id="related-articles" name="related-articles"><i class="anchor-icon"></i></a>Related articles</h3>
|
||||
<p><div class="dev_page_nav_wrap"></p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="/mtproto/description">Mobile Protocol: Detailed Description</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/auth_key">Creating an Authorization Key</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/samples-auth_key">Creating an Authorization Key: Example</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/service_messages_about_messages">Mobile Protocol: Service Messages about Messages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/serialize">Binary Data Serialization</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/TL">TL Language</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/schema/mtproto">MTProto TL-schema</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/api/end-to-end">End-to-end encryption, Secret Chats</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/schema/end-to-end">End-to-end TL-schema</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="/mtproto/security_guidelines">Security Guidelines for Client Software Developers</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p></div></p>
|
||||
<hr>
|
||||
<p>This page deals with the basic layer of MTProto encryption used for Cloud chats (server-client encryption). See also:</p>
|
||||
<ul>
|
||||
<li><a href="/api/end-to-end">Secret Chats, end-to-end-encryption</a></li>
|
||||
<li><a href="/api/end-to-end/voice-calls">End-to-end encrypted Voice Calls</a></li>
|
||||
</ul>
|
||||
<h3><a class="anchor" href="#general-description" id="general-description" name="general-description"><i class="anchor-icon"></i></a>General Description</h3>
|
||||
<p>The protocol is designed for access to a server API from applications running on mobile devices. It must be emphasized that a web browser is not such an application.</p>
|
||||
<p>The protocol is subdivided into three virtually independent components:</p>
|
||||
<ul>
|
||||
<li>High-level component (API query language): defines the method whereby API queries and responses are converted to binary <em>messages</em>.</li>
|
||||
<li>Cryptographic (authorization) layer: defines the method by which messages are encrypted prior to being transmitted through the transport protocol.</li>
|
||||
<li>Transport component: defines the method for the client and the server to transmit messages over some other existing network protocol (such as HTTP, HTTPS, WS (plain websockets), WSS (websockets over HTTPS), TCP, UDP).</li>
|
||||
</ul>
|
||||
<div><a href="/file/811140746/2/CzMyJPVnPo8.81605/c2310d6ede1a5e220f">
|
||||
<img src="/file/811140746/2/CzMyJPVnPo8.81605/c2310d6ede1a5e220f" alt="MTProto 2.0, server-client encryption, cloud chats" class="dev_page_image" style="max-width: 600px;">
|
||||
</a></div>
|
||||
<blockquote>
|
||||
<p>As of version 4.6, major Telegram clients are using <strong>MTProto 2.0</strong>, described in this article.
|
||||
MTProto v1.0 (<a href="/mtproto_v1">described here</a> for reference) is deprecated and is currently being phased out. </p>
|
||||
</blockquote>
|
||||
<h3><a class="anchor" href="#brief-component-summary" id="brief-component-summary" name="brief-component-summary"><i class="anchor-icon"></i></a>Brief Component Summary</h3>
|
||||
<h4><a class="anchor" href="#high-level-component-rpc-query-languageapi" id="high-level-component-rpc-query-languageapi" name="high-level-component-rpc-query-languageapi"><i class="anchor-icon"></i></a>High-Level Component (RPC Query Language/API)</h4>
|
||||
<p>From the standpoint of the high-level component, the client and the server exchange <em>messages</em> inside a <em>session</em>. The session is attached to the client device (the application, to be more exact) rather than a specific websocket/http/https/tcp connection. In addition, each session is attached to a <em>user key ID</em> by which authorization is actually accomplished.</p>
|
||||
<p>Several connections to a server may be open; messages may be sent in either direction through any of the connections (a response to a query is not necessarily returned through the same connection that carried the original query, although most often, that is the case; however, in no case can a message be returned through a connection belonging to a different session). When the UDP protocol is used, a response might be returned by a different IP address than the one to which the query had been sent.</p>
|
||||
<p>There are several types of messages:</p>
|
||||
<ul>
|
||||
<li>RPC calls (client to server): calls to API methods</li>
|
||||
<li>RPC responses (server to client): results of RPC calls</li>
|
||||
<li>Message received acknowledgment (or rather, notification of status of a set of messages)</li>
|
||||
<li>Message status query</li>
|
||||
<li><em>Multipart message</em> or <em>container</em> (a container that holds several messages; needed to send several RPC calls at once over an HTTP connection, for example; also, a container may support gzip).</li>
|
||||
</ul>
|
||||
<p>From the standpoint of lower level protocols, a message is a binary data stream aligned along a 4 or 16-byte boundary. The first several fields in the message are fixed and are used by the cryptographic/authorization system.</p>
|
||||
<p>Each message, either individual or inside a container, consists of a <em>message identifier</em> (64 bits, see below), a <em>message sequence number within a session</em> (32 bits), the <em>length</em> (of the message body in bytes; 32 bits), and a <em>body</em> (any size which is a multiple of 4 bytes). In addition, when a container or a single message is sent, an <em>internal header</em> is added at the top (see below), then the entire message is encrypted, and an <em>external header</em> is placed at the top of the message (a 64-bit <em>key identifier</em> and a 128-bit <em>message key</em>).</p>
|
||||
<p>A <em>message body</em> normally consists of a 32-bit <em>message type</em> followed by type-dependent <em>parameters</em>. In particular, each RPC function has a corresponding message type. For more detail, see <a href="/mtproto/serialize">Binary Data Serialization</a>, <a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a>.</p>
|
||||
<p>All numbers are written as little endian. However, very large numbers (2048-bit) used in RSA and DH are written in the big endian format because that is how the OpenSSL library does it.</p>
|
||||
<h4><a class="anchor" href="#authorization-and-encryption" id="authorization-and-encryption" name="authorization-and-encryption"><i class="anchor-icon"></i></a>Authorization and Encryption</h4>
|
||||
<p>Prior to a message (or a multipart message) being transmitted over a network using a transport protocol, it is encrypted in a certain way, and an <em>external header</em> is added at the top of the message which is: a 64-bit <em>key identifier</em> (that uniquely identifies an <em>authorization key</em> for the server as well as the <em>user</em>) and a 128-bit <em>message key</em>. A user key together with the message key defines an actual 256-bit key which is what encrypts the message using AES-256 encryption. Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). The message key is defined as the 128 middle bits of the SHA256 of the message body (including session, message ID, etc.), including the padding bytes, prepended by 32 bytes taken from the authorization key. Multipart messages are encrypted as a single message.</p>
|
||||
<blockquote>
|
||||
<p>For a technical specification, see <a href="/mtproto/description">Mobile Protocol: Detailed Description</a></p>
|
||||
</blockquote>
|
||||
<p>The first thing a client application must do is <a href="/mtproto/auth_key">create an authorization key</a> which is normally generated when it is first run and almost never changes.</p>
|
||||
<p>The protocol's principal drawback is that an intruder passively intercepting messages and then somehow appropriating the authorization key (for example, by stealing a device) will be able to decrypt all the intercepted messages <em>post factum</em>. This probably is not too much of a problem (by stealing a device, one could also gain access to all the information cached on the device without decrypting anything); however, the following steps could be taken to overcome this weakness:</p>
|
||||
<ul>
|
||||
<li><em>Session keys</em> generated using the Diffie-Hellman protocol and used in conjunction with the authorization and the message keys to select AES parameters. To create these, the first thing a client must do after creating a new session is send a special RPC query to the server (“generate session key”) to which the server will respond, whereupon all subsequent messages within the session are encrypted using the session key as well.</li>
|
||||
<li>Protecting the key stored on the client device with a (text) password; this password is never stored in memory and is entered by a user when starting the application or more frequently (depending on application settings).</li>
|
||||
<li>Data stored (cached) on the user device can also be protected by encryption using an authorization key which, in turn, is to be password-protected. Then, a password will be required to gain access even to that data.</li>
|
||||
</ul>
|
||||
<h4><a class="anchor" href="#time-synchronization" id="time-synchronization" name="time-synchronization"><i class="anchor-icon"></i></a>Time Synchronization</h4>
|
||||
<p>If client time diverges widely from server time, a server may start ignoring client messages, or vice versa, because of an invalid message identifier (which is closely related to creation time). Under these circumstances, the server will send the client a special message containing the correct time and a certain 128-bit salt (either explicitly provided by the client in a special RPC synchronization request or equal to the key of the latest message received from the client during the current session). This message could be the first one in a container that includes other messages (if the time discrepancy is significant but does not as yet result in the client's messages being ignored).</p>
|
||||
<p>Having received such a message or a container holding it, the client first performs a time synchronization (in effect, simply storing the difference between the server's time and its own to be able to compute the “correct” time in the future) and then verifies that the message identifiers for correctness.</p>
|
||||
<p>Where a correction has been neglected, the client will have to generate a new session to assure the monotonicity of message identifiers.</p>
|
||||
<h3><a class="anchor" href="#mtproto-transport" id="mtproto-transport" name="mtproto-transport"><i class="anchor-icon"></i></a>MTProto transport</h3>
|
||||
<p>Before being sent using the selected transport protocol, the payload has to be wrapped in a secondary protocol header, defined by the appropriate MTProto transport protocol. </p>
|
||||
<ul>
|
||||
<li><a href="mtproto/mtproto-transports#abridged">Abridged</a></li>
|
||||
<li><a href="mtproto/mtproto-transports#intermediate">Intermediate</a></li>
|
||||
<li><a href="mtproto/mtproto-transports#padded-intermediate">Padded intermediate</a></li>
|
||||
<li><a href="mtproto/mtproto-transports#full">Full</a></li>
|
||||
</ul>
|
||||
<p>The server recognizes these different protocols (and distinguishes them from HTTP, too) by the header.
|
||||
Additionally, the following transport features can be used: </p>
|
||||
<ul>
|
||||
<li><a href="mtproto/mtproto-transports#quick-ack">Quick ack</a></li>
|
||||
<li><a href="mtproto/mtproto-transports#transport-errors">Transport errors</a></li>
|
||||
<li><a href="mtproto/mtproto-transports#transport-obfuscation">Transport obfuscation</a></li>
|
||||
</ul>
|
||||
<p>Example implementations for these protocols can be seen in <a href="https://github.com/tdlib/td/blob/master/td/mtproto/TcpTransport.cpp">tdlib</a> and <a href="https://github.com/danog/MadelineProto/tree/master/src/danog/MadelineProto/Stream/MTProtoTransport">MadelineProto</a>.</p>
|
||||
<h3><a class="anchor" href="#transport" id="transport" name="transport"><i class="anchor-icon"></i></a>Transport</h3>
|
||||
<p>Enables the delivery of encrypted containers together with the external header (hereinafter, <em>Payload</em>) from client to server and back.
|
||||
Multiple transport protocols are defined:</p>
|
||||
<ul>
|
||||
<li><a href="/mtproto/transports#tcp">TCP</a></li>
|
||||
<li><a href="/mtproto/transports#websocket">Websocket</a></li>
|
||||
<li><a href="/mtproto/transports#websocket-over-https">Websocket over HTTPS</a></li>
|
||||
<li><a href="/mtproto/transports#http">HTTP</a></li>
|
||||
<li><a href="/mtproto/transports#https">HTTPS</a></li>
|
||||
<li>UDP</li>
|
||||
</ul>
|
||||
<p>(We shall examine only the first five types.)</p>
|
||||
<h3><a class="anchor" href="#recap" id="recap" name="recap"><i class="anchor-icon"></i></a>Recap</h3>
|
||||
<p>To recap, using the <a href="https://en.wikipedia.org/wiki/OSI_model#Layer_architecture">ISO/OSI stack as comparison</a>: </p>
|
||||
<ul>
|
||||
<li>Layer 7 (Application): <a href="#high-level-component-rpc-query-languageapi">High-level RPC API</a></li>
|
||||
<li>Layer 6 (Presentation): <a href="/mtproto/TL">Type Language</a></li>
|
||||
<li>Layer 5 (Session): <a href="#high-level-component-rpc-query-languageapi">MTProto session</a></li>
|
||||
<li>Layer 4 (Transport):<ul>
|
||||
<li>4.3: <a href="#mtproto-transport">MTProto transport protocol</a></li>
|
||||
<li>4.2: <a href="/mtproto/mtproto-transports#transport-obfuscation">MTProto obfuscation (optional)</a></li>
|
||||
<li>4.1: <a href="#transport">Transport protocol</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Layer 3 (Network): IP</li>
|
||||
<li>Layer 2 (Data link): MAC/LLC</li>
|
||||
<li>Layer 1 (Physical): IEEE 802.3, IEEE 802.11, etc...</li>
|
||||
</ul></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
<script src="/js/jquery.min.js?1"></script>
|
||||
<script src="/js/bootstrap.min.js?1"></script>
|
||||
|
||||
<script>window.initDevPageNav&&initDevPageNav();
|
||||
backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MTProto Mobile Protocol v.1.0 (DEPRECATED)</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="This document describes MTProto v1.0, its status is DEPRECATED.
|
||||
For information on encryption used in up-to-date Telegram…">
|
||||
<meta property="og:title" content="MTProto Mobile Protocol v.1.0 (DEPRECATED)">
|
||||
<meta property="og:image" content="https://corefork.telegram.org/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed">
|
||||
<meta property="og:description" content="This document describes MTProto v1.0, its status is DEPRECATED.
|
||||
For information on encryption used in up-to-date Telegram…">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class=""><a href="/api">API</a></li>
|
||||
<li class=""><a href="/mtproto">Protocol</a></li>
|
||||
<li class=""><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"></div>
|
||||
<h1 id="dev_page_title">MTProto Mobile Protocol v.1.0 (DEPRECATED)</h1>
|
||||
|
||||
<div id="dev_page_content"><blockquote>
|
||||
<p>This document describes MTProto <strong>v1.0</strong>, its status is <strong>DEPRECATED</strong>.<br>For information on encryption used in up-to-date Telegram clients, kindly see <a href="/mtproto">this document</a>.</p>
|
||||
</blockquote>
|
||||
<h3><a class="anchor" name="related-articles" href="#related-articles"><i class="anchor-icon"></i></a>Related articles</h3>
|
||||
<p><div class="dev_page_nav_wrap"></p>
|
||||
<ul>
|
||||
<li><a href="/mtproto">MTProto 2.0</a></li>
|
||||
<li><a href="/mtproto/description">Mobile Protocol: Detailed Description</a></li>
|
||||
<li><a href="/mtproto/auth_key">Creating an Authorization Key</a></li>
|
||||
<li><a href="/mtproto/samples-auth_key">Creating an Authorization Key: Example</a></li>
|
||||
<li><a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a></li>
|
||||
<li><a href="/mtproto/service_messages_about_messages">Mobile Protocol: Service Messages about Messages</a></li>
|
||||
<li><a href="/mtproto/serialize">Binary Data Serialization</a></li>
|
||||
<li><p><a href="/mtproto/TL">TL Language</a></p>
|
||||
</li>
|
||||
<li><p><a href="/schema/mtproto">MTProto TL-schema</a></p>
|
||||
</li>
|
||||
<li><a href="/api/end-to-end">End-to-end encryption, Secret Chats</a></li>
|
||||
<li><a href="/schema/end-to-end">End-to-end TL-schema</a></li>
|
||||
<li><a href="/mtproto/security_guidelines">Security Guidelines for Client Software Developers</a></li>
|
||||
</ul>
|
||||
<p></div></p>
|
||||
<hr>
|
||||
<p>This page deals with the basic layer of MTProto encryption used for Cloud chats (server-client encryption). See also:</p>
|
||||
<ul>
|
||||
<li><a href="/api/end-to-end">Secret Chats, end-to-end-encryption</a></li>
|
||||
<li><a href="/api/end-to-end/voice-calls">End-to-end encrypted Voice Calls</a></li>
|
||||
</ul>
|
||||
<h3><a class="anchor" name="general-description" href="#general-description"><i class="anchor-icon"></i></a>General Description</h3>
|
||||
<p>The protocol is designed for access to a server API from applications running on mobile devices. It must be emphasized that a web browser is not such an application.</p>
|
||||
<p>The protocol is subdivided into three virtually independent components:</p>
|
||||
<ul>
|
||||
<li>High-level component (API query language): defines the method whereby API queries and responses are converted to binary <em>messages</em>.</li>
|
||||
<li>Cryptographic (authorization) layer: defines the method by which messages are encrypted prior to being transmitted through the transport protocol.</li>
|
||||
<li>Transport component: defines the method for the client and the server to transmit messages over some other existing network protocol (such as, http, https, tcp, udp).</li>
|
||||
</ul>
|
||||
<div><a href="/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed">
|
||||
<img src="/file/811140187/1/sfBQV3Trp80/3a3c48bad836b853ed" alt="Server-client encryption in MTProto (Cloud chats)" class="dev_page_image"/>
|
||||
</a></div>
|
||||
|
||||
<blockquote>
|
||||
<p>Got questions about this setup? — Check out the <a href="http://core.telegram.org/techfaq">Advanced FAQ</a>!</p>
|
||||
</blockquote>
|
||||
<h6><a class="anchor" name="note-1" href="#note-1"><i class="anchor-icon"></i></a><strong>Note 1</strong></h6>
|
||||
<p>Each plaintext message to be encrypted in MTProto always contains the following data to be checked upon decryption in order to make the system robust against known problems with the components:</p>
|
||||
<ul>
|
||||
<li>server salt (64-Bit)</li>
|
||||
<li>session id</li>
|
||||
<li>message sequence number</li>
|
||||
<li>message length</li>
|
||||
<li>time</li>
|
||||
</ul>
|
||||
<h6><a class="anchor" name="note-2" href="#note-2"><i class="anchor-icon"></i></a><strong>Note 2</strong></h6>
|
||||
<p>See additional comments on our use of <a href="https://core.telegram.org/techfaq#q-do-you-use-ige-ige-is-broken">IGE</a>, <a href="https://core.telegram.org/techfaq#q-why-do-you-use-sha-1-not-sha-256-or-x-insert-your-favorite-has">SHA-1</a> and <a href="https://core.telegram.org/techfaq#q-how-are-mtproto-messages-authenticated">message authentication</a>.</p>
|
||||
<h6><a class="anchor" name="note-3" href="#note-3"><i class="anchor-icon"></i></a><strong>Note 3</strong></h6>
|
||||
<p>Telegram's <strong>End-to-end</strong> encrypted Secret Chats are using an additional layer of encryption on top of the described above. See <a href="https://core.telegram.org/api/end-to-end">Secret Chats, End-to-End encryption</a> for details.</p>
|
||||
<h3><a class="anchor" name="brief-component-summary" href="#brief-component-summary"><i class="anchor-icon"></i></a>Brief Component Summary</h3>
|
||||
<h4><a class="anchor" name="high-level-component-rpc-query-language-api" href="#high-level-component-rpc-query-language-api"><i class="anchor-icon"></i></a>High-Level Component (RPC Query Language/API)</h4>
|
||||
<p>From the standpoint of the high-level component, the client and the server exchange <em>messages</em> inside a <em>session</em>. The session is attached to the client device (the application, to be more exact) rather than a specific http/https/tcp connection. In addition, each session is attached to a <em>user key ID</em> by which authorization is actually accomplished.</p>
|
||||
<p>Several connections to a server may be open; messages may be sent in either direction through any of the connections (a response to a query is not necessarily returned through the same connection that carried the original query, although most often, that is the case; however, in no case can a message be returned through a connection belonging to a different session). When the UDP protocol is used, a response might be returned by a different IP address than the one to which the query had been sent.</p>
|
||||
<p>There are several types of messages:</p>
|
||||
<ul>
|
||||
<li>RPC calls (client to server): calls to API methods</li>
|
||||
<li>RPC responses (server to client): results of RPC calls</li>
|
||||
<li>Message received acknowledgment (or rather, notification of status of a set of messages)</li>
|
||||
<li>Message status query</li>
|
||||
<li><em>Multipart message</em> or <em>container</em> (a container that holds several messages; needed to send several RPC calls at once over an HTTP connection, for example; also, a container may support gzip).</li>
|
||||
</ul>
|
||||
<p>From the standpoint of lower level protocols, a message is a binary data stream aligned along a 4 or 16-byte boundary. The first several fields in the message are fixed and are used by the cryptographic/authorization system.</p>
|
||||
<p>Each message, either individual or inside a container, consists of a <em>message identifier</em> (64 bits, see below), a <em>message sequence number within a session</em> (32 bits), the <em>length</em> (of the message body in bytes; 32 bits), and a <em>body</em> (any size which is a multiple of 4 bytes). In addition, when a container or a single message is sent, an <em>internal header</em> is added at the top (see below), then the entire message is encrypted, and an <em>external header</em> is placed at the top of the message (a 64-bit <em>key identifier</em> and a 128-bit <em>message key</em>).</p>
|
||||
<p>A <em>message body</em> normally consists of a 32-bit <em>message type</em> followed by type-dependent <em>parameters</em>. In particular, each RPC function has a corresponding message type. For more detail, see <a href="/mtproto/serialize">Binary Data Serialization</a>, <a href="/mtproto/service_messages">Mobile Protocol: Service Messages</a>.</p>
|
||||
<p>All numbers are written as little endian. However, very large numbers (2048-bit) used in RSA and DH are written in the big endian format because that is what the OpenSSL library does.</p>
|
||||
<h4><a class="anchor" name="authorization-and-encryption" href="#authorization-and-encryption"><i class="anchor-icon"></i></a>Authorization and Encryption</h4>
|
||||
<p>Prior to a message (or a multipart message) being transmitted over a network using a transport protocol, it is encrypted in a certain way, and an <em>external header</em> is added at the top of the message which is: a 64-bit <em>key identifier</em> (that uniquely identifies an <em>authorization key</em> for the server as well as the <em>user</em>) and a 128-bit <em>message key</em>. A user key together with the message key defines an actual 256-bit key which is what encrypts the message using AES-256 encryption. Note that the initial part of the message to be encrypted contains variable data (session, message ID, sequence number, server salt) that obviously influences the message key (and thus the AES key and iv). The message key is defined as the 128 lower-order bits of the SHA1 of the message body (including session, message ID, etc.). Multipart messages are encrypted as a single message.</p>
|
||||
<p>For a technical specification, see <a href="/mtproto/description">Mobile Protocol: Detailed Description</a><br>The first thing a client application must do is <a href="/mtproto/auth_key">create an authorization key</a> which is normally generated when it is first run and almost never changes.</p>
|
||||
<p>The protocol’s principal drawback is that an intruder passively intercepting messages and then somehow appropriating the authorization key (for example, by stealing a device) will be able to decrypt all the intercepted messages <em>post factum</em>. This probably is not too much of a problem (by stealing a device, one could also gain access to all the information cached on the device without decrypting anything); however, the following steps could be taken to overcome this weakness:</p>
|
||||
<ul>
|
||||
<li><em>Session keys</em> generated using the Diffie-Hellman protocol and used in conjunction with the authorization and the message keys to select AES parameters. To create these, the first thing a client must do after creating a new session is send a special RPC query to the server (“generate session key”) to which the server will respond, whereupon all subsequent messages within the session are encrypted using the session key as well.</li>
|
||||
<li>Protecting the key stored on the client device with a (text) password; this password is never stored in memory and is entered by a user when starting the application or more frequently (depending on application settings).</li>
|
||||
<li>Data stored (cached) on the user device can also be protected by encryption using an authorization key which, in turn, is to be password-protected. Then, a password will be required to gain access even to those data.</li>
|
||||
</ul>
|
||||
<h4><a class="anchor" name="time-synchronization" href="#time-synchronization"><i class="anchor-icon"></i></a>Time Synchronization</h4>
|
||||
<p>If client time diverges widely from server time, a server may start ignoring client messages, or vice versa, because of an invalid message identifier (which is closely related to creation time). Under these circumstances, the server will send the client a special message containing the correct time and a certain 128-bit salt (either explicitly provided by the client in a special RPC synchronization request or equal to the key of the latest message received from the client during the current session). This message could be the first one in a container that includes other messages (if the time discrepancy is significant but does not as yet result in the client’s messages being ignored).</p>
|
||||
<p>Having received such a message or a container holding it, the client first performs a time synchronization (in effect, simply storing the difference between the server’s time and its own to be able to compute the “correct” time in the future) and then verifies that the message identifiers for correctness.</p>
|
||||
<p>Where a correction has been neglected, the client will have to generate a new session to assure the monotonicity of message identifiers.</p>
|
||||
<h3><a class="anchor" name="transport" href="#transport"><i class="anchor-icon"></i></a>Transport</h3>
|
||||
<p>Enables the delivery of encrypted containers together with the external header (hereinafter, <em>Payload</em>) from client to server and back. There are three types of transport:</p>
|
||||
<ul>
|
||||
<li>HTTP</li>
|
||||
<li>TCP</li>
|
||||
<li>UDP</li>
|
||||
</ul>
|
||||
<p>We shall examine the first two types.</p>
|
||||
<h4><a class="anchor" name="http-transport" href="#http-transport"><i class="anchor-icon"></i></a>HTTP Transport</h4>
|
||||
<p>Implemented over HTTP/1.1 (with keepalive) running over the traditional TCP Port 80. HTTPS is not used; the above encryption method is used instead.</p>
|
||||
<p>An HTTP connection is attached to a session (or rather, to session + key identifier) specified in the most recent user query received; normally, the session is the same in all queries, but crafty HTTP proxies may corrupt that. A server may not return a message into an HTTP connection unless it belongs to the same session, and unless it is the server’s turn (an HTTP request had been received from the client to which a response has not been sent yet).</p>
|
||||
<p>The overall arrangement is as follows. The client opens one or more keepalive HTTP connections to the server. If one or more messages need to be sent, they are made into a <em>payload</em> which is followed by a POST request to the URL/api to which the payload is transmitted as data. In addition, <code>Content-Length</code>, <code>Keepalive</code>, and <code>Host</code> are valid HTTP headers.</p>
|
||||
<p>Having received the query, the server may either wait a little while (if the query requires a response following a short timeout) or immediately return a dummy response (only acknowledging the receipt of the container). In any case, the response may contain any number of messages. The server may at the same time send out any other messages it might be holding for the session.</p>
|
||||
<p>In addition, there exists a special long poll RPC query (valid for HTTP connections only) which transmits maximum timeout T. If the server has messages for the session, they are returned immediately; otherwise, a wait state is entered until such time as the server has a message for the client or T seconds have elapsed. If no events occur in the span of T seconds, a dummy response is returned (special message).</p>
|
||||
<p>If a server needs to send a message to a client, it checks for an HTTP connection that belongs to the required session and is in the “answering an HTTP request” state (including long poll) whereupon the message is added to the response container for the connection and sent to the user. In a typical case, there is some additional wait time (50 milliseconds) against the eventuality that the server will soon have more messages for the session.</p>
|
||||
<p>If no suitable HTTP connection is available, the messages are placed in the current session’s send queue. However, they find their way there anyway until receipt is explicitly or indirectly confirmed by the client. For the HTTP protocol, sending the next query into the same HTTP connection is regarded as an implicit acknowledgment <em>(not any more, the HTTP protocol also requires that explicit acknowledgments be sent)</em>; in other cases, the client must return an explicit acknowledgment within a reasonable time (it can be added to a container for the following request).</p>
|
||||
<p><strong><em>Important</em></strong>: if the acknowledgment fails to arrive on time, the message can be resent (possibly, in a different container). The parties must autonomously be ready for this and must store the identifiers of the most recent messages received (and ignore such duplicates rather than repeat actions). In order not to have the identifiers stored forever, there exist special <em>garbage collection</em> messages that take advantage of message identifier monotonicity.</p>
|
||||
<p>If the send queue overflows or if messages stay in the queue for over 10 minutes, the server forgets them (or sends them to <em>swap</em>, no genius required). This may happen even faster, if the server is running out of buffer space (for example, because of serious network issues resulting in a large number of connections becoming severed).</p>
|
||||
<h4><a class="anchor" name="tcp-transport" href="#tcp-transport"><i class="anchor-icon"></i></a>TCP Transport</h4>
|
||||
<p>This is very similar to the HTTP transport. May also be implemented over Port 80 (to penetrate all firewalls) and even use the same server IP addresses. In this situation, the server understands whether HTTP or TCP protocol must be used for the connection, based on the first four incoming bytes (for HTTP, it is POST).</p>
|
||||
<p>When a TCP connection is created, it is assigned to the session (and the authorization key) transmitted in the first user message, and subsequently used exclusively for this session (multiplexing arrangements are not allowed).</p>
|
||||
<p>If a payload (packet) needs to be transmitted from server to client or from client to server, it is encapsulated as follows: 4 length bytes are added at the front (to include the length, the sequence number, and CRC32; always divisible by 4) and 4 bytes with the packet sequence number within this TCP connection (the first packet sent is numbered 0, the next one 1, etc.), and 4 CRC32 bytes at the end (length, sequence number, and payload together).</p>
|
||||
<p>There is an <em>abridged</em> version of the same protocol: if the client sends <code>0xef</code> as the first byte (<strong>important:</strong> only prior to the very first data packet), then packet length is encoded by a single byte (<code>0x01..0x7e</code> = data length divided by 4; or <code>0x7f</code> followed by 3 length bytes (little endian) divided by 4) followed by the data themselves (sequence number and CRC32 not added). In this case, server responses look the same (the server does not send <code>0xef</code>as the first byte).</p>
|
||||
<p>In case 4-byte data alignment is needed, an <em>intermediate</em> version of the original protocol may be used: if the client sends <code>0xeeeeeeee</code> as the first int (four bytes), then packet length is encoded always by four bytes as in the original version, but the sequence number and CRC32 are omitted, thus decreasing total packet size by 8 bytes.</p>
|
||||
<p>The full, the intermediate and the abridged versions of the protocol have support for quick acknowledgment. In this case, the client sets the highest-order length bit in the query packet, and the server responds with a special 4 bytes as a separate packet. They are the 32 higher-order SHA1 bits of the encrypted portion of the packet with the most significant bit set to make clear that this is not the length of a regular server response packet; if the abridged version is used, bswap is applied to these four bytes.</p>
|
||||
<p>There are no implicit acknowledgments for the TCP transport: all messages must be acknowledged explicitly. Most frequently, acknowledgments are placed in a container with the next query or response if it is transmitted in short order. For example, this is almost always the case for client messages containing RPC queries: the acknowledgment normally arrives with the RPC response.</p>
|
||||
<p>In the event of an error, the server may send a packet whose payload consists of 4 bytes as the error code. For example, Error Code 403 corresponds to situations where the corresponding HTTP error would have been returned by the HTTP protocol.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
|
||||
<script>backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -1,204 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Current MTProto TL-schema</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="description" content="Below you will find the current MTProto TL-schema. More details on TL »
|
||||
See also the detailed schema in JSON »
|
||||
int ? = Int;…">
|
||||
<meta property="og:title" content="Current MTProto TL-schema">
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:description" content="Below you will find the current MTProto TL-schema. More details on TL »
|
||||
See also the detailed schema in JSON »
|
||||
int ? = Int;…">
|
||||
<link rel="shortcut icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div class="dev_page_wrap">
|
||||
<div class="dev_page_head navbar navbar-static-top navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="//telegram.org/">Home</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
|
||||
<li class=""><a href="/api">API</a></li>
|
||||
<li class=""><a href="/mtproto">Protocol</a></li>
|
||||
<li class="active"><a href="/schema">Schema</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix">
|
||||
<div class="dev_page">
|
||||
<div id="dev_page_content_wrap" class=" ">
|
||||
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/mtproto" >Mobile Protocol</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/schema/mtproto" >Current MTProto TL-schema</a></li></ul></div>
|
||||
<h1 id="dev_page_title">Current MTProto TL-schema</h1>
|
||||
|
||||
<div id="dev_page_content"><p>Below you will find the current MTProto TL-schema. <a href="/mtproto/TL">More details on TL »</a> </p>
|
||||
<p>See also the <a href="/schema/mtproto-json">detailed schema in JSON »</a> </p>
|
||||
<p><br></p>
|
||||
<pre><code>int ? = Int;
|
||||
long ? = Long;
|
||||
double ? = Double;
|
||||
string ? = String;
|
||||
|
||||
vector {t:Type} # [ t ] = Vector t;
|
||||
|
||||
int128 4*[ int ] = Int128;
|
||||
int256 8*[ int ] = Int256;
|
||||
|
||||
resPQ#05162463 nonce:int128 server_nonce:int128 pq:bytes server_public_key_fingerprints:Vector<long> = ResPQ;
|
||||
|
||||
p_q_inner_data_dc#a9f55f95 pq:bytes p:bytes q:bytes nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
|
||||
p_q_inner_data_temp_dc#56fddf88 pq:bytes p:bytes q:bytes nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
|
||||
|
||||
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:bytes = Server_DH_Params;
|
||||
|
||||
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:bytes g_a:bytes server_time:int = Server_DH_inner_data;
|
||||
|
||||
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:bytes = Client_DH_Inner_Data;
|
||||
|
||||
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
|
||||
|
||||
bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
|
||||
|
||||
rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;
|
||||
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
|
||||
|
||||
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
|
||||
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
|
||||
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
|
||||
|
||||
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
|
||||
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
|
||||
|
||||
pong#347773c5 msg_id:long ping_id:long = Pong;
|
||||
|
||||
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
|
||||
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
|
||||
|
||||
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
|
||||
|
||||
msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
|
||||
message msg_id:long seqno:int bytes:int body:Object = Message;
|
||||
msg_copy#e06046b2 orig_message:Message = MessageCopy;
|
||||
|
||||
gzip_packed#3072cfa1 packed_data:bytes = Object;
|
||||
|
||||
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
|
||||
|
||||
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
|
||||
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
|
||||
|
||||
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
|
||||
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
|
||||
msgs_state_info#04deb57d req_msg_id:long info:bytes = MsgsStateInfo;
|
||||
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:bytes = MsgsAllInfo;
|
||||
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
|
||||
destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
||||
|
||||
---functions---
|
||||
|
||||
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
|
||||
|
||||
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params;
|
||||
|
||||
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer;
|
||||
|
||||
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
|
||||
get_future_salts#b921bd04 num:int = FutureSalts;
|
||||
ping#7abe77ec ping_id:long = Pong;
|
||||
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
|
||||
destroy_session#e7512126 session_id:long = DestroySessionRes;
|
||||
|
||||
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
|
||||
|
||||
destroy_auth_key#d1435160 = DestroyAuthKeyRes;</code></pre></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/faq">FAQ</a></li>
|
||||
<li><a href="//telegram.org/blog">Blog</a></li>
|
||||
<li><a href="//telegram.org/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="//telegram.org/dl/android">Android</a></li>
|
||||
<li><a href="//telegram.org/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//telegram.org/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
|
||||
<script>backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Telegram Tour</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="Telegram Tour">
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:site_name" content="Telegram">
|
||||
<meta property="og:description" content="">
|
||||
|
||||
<meta property="fb:app_id" content="254098051407226">
|
||||
<meta property="vk:app_id" content="3782569">
|
||||
<meta name="apple-itunes-app" content="app-id=686449807">
|
||||
<meta name="telegram:channel" content="@telegram">
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/img/website_icon.svg?4">
|
||||
<link rel="alternate icon" href="/favicon.ico?4" type="image/x-icon" />
|
||||
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
|
||||
<link href="/css/bootstrap-extra.css?2" rel="stylesheet">
|
||||
<link href="/css/telegram.css?224" rel="stylesheet" media="screen">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body class="preload">
|
||||
<div id="fb-root"></div>
|
||||
<div class="tl_page_wrap">
|
||||
<div class="tl_page_head navbar navbar-static-top navbar navbar-tg">
|
||||
<div class="navbar-inner">
|
||||
<div class="container clearfix">
|
||||
<ul class="nav navbar-nav navbar-right"><li class="navbar-twitter hidden-xs"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i> Twitter</a></li></ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class=""><a href="/">Home</a></li>
|
||||
<li class=""><a href="/faq">FAQ</a></li>
|
||||
<li class=""><a href="/apps">Apps</a></li>
|
||||
<li class="hidden-xs "><a href="//core.telegram.org/api">API</a></li>
|
||||
<li class="hidden-xs "><a href="//core.telegram.org/mtproto">Protocol</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container clearfix tl_page_container ">
|
||||
<div class="tl_page">
|
||||
<div class="tl_blog_list_page_wrap">
|
||||
<h2 class="tl_blog_list_header">Telegram News</h2>
|
||||
<div class="tl_blog_list_cards_wrap">
|
||||
<a class="dev_blog_card_link_wrap" href="/tour/quizbot"><div class="dev_blog_card_wrap">
|
||||
<img class="dev_blog_card_image" src="https://telegram.org/file/464001802/1/fwR7FzJKx2g.142023/3262deaf7701aed468" />
|
||||
<div class="dev_blog_card_alltext_wrap">
|
||||
<h4 class="dev_blog_card_title">Quizzes</h4>
|
||||
<div class="dev_blog_card_lead">With the help of Quiz Bot you can create multi-question quizzes and share them with others. The bot lets you add text or media before questions to help create exam-style prompts with…</div>
|
||||
</div>
|
||||
<div class="dev_blog_card_date">Dec 22, 2020</div>
|
||||
</div></a><a class="dev_blog_card_link_wrap" href="/tour/groups"><div class="dev_blog_card_wrap">
|
||||
<img class="dev_blog_card_image" src="https://telegram.org/img/tl_card_coordinate.gif" />
|
||||
<div class="dev_blog_card_alltext_wrap">
|
||||
<h4 class="dev_blog_card_title">Group Chats on Telegram</h4>
|
||||
<div class="dev_blog_card_lead">Telegram groups are a powerful tool for building communities and each can have up to 200,000 members.</div>
|
||||
</div>
|
||||
<div class="dev_blog_card_date">Jan 29, 2018</div>
|
||||
</div></a><a class="dev_blog_card_link_wrap" href="/tour/channels"><div class="dev_blog_card_wrap">
|
||||
<img class="dev_blog_card_image" src="https://telegram.org/file/464001838/1/GDPE_UE4KFY.77587/f3ebf15475f5ac1f66" />
|
||||
<div class="dev_blog_card_alltext_wrap">
|
||||
<h4 class="dev_blog_card_title">Telegram Channels</h4>
|
||||
<div class="dev_blog_card_lead">Channels are a tool for broadcasting your public messages to large audiences. They offer a unique opportunity to reach people directly, sending a notification to their phones with…</div>
|
||||
</div>
|
||||
<div class="dev_blog_card_date">Jan 29, 2018</div>
|
||||
</div></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_wrap">
|
||||
<div class="footer_columns_wrap footer_desktop">
|
||||
<div class="footer_column footer_column_telegram">
|
||||
<h5>Telegram</h5>
|
||||
<div class="footer_telegram_description"></div>
|
||||
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
|
||||
</div>
|
||||
|
||||
<div class="footer_column">
|
||||
<h5><a href="/faq">About</a></h5>
|
||||
<ul>
|
||||
<li><a href="/faq">FAQ</a></li>
|
||||
<li><a href="/blog">Blog</a></li>
|
||||
<li><a href="/jobs">Jobs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="/apps#mobile-apps">Mobile Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="/dl/ios">iPhone/iPad</a></li>
|
||||
<li><a href="/dl/android">Android</a></li>
|
||||
<li><a href="/dl/wp">Windows Phone</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="/apps#desktop-apps">Desktop Apps</a></h5>
|
||||
<ul>
|
||||
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
|
||||
<li><a href="//macos.telegram.org/">macOS</a></li>
|
||||
<li><a href="/dl/web">Web-browser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer_column footer_column_platform">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
<ul>
|
||||
<li><a href="//core.telegram.org/api">API</a></li>
|
||||
<li><a href="//translations.telegram.org/">Translations</a></li>
|
||||
<li><a href="//instantview.telegram.org/">Instant View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer_columns_wrap footer_mobile">
|
||||
<div class="footer_column">
|
||||
<h5><a href="/faq">About</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="/blog">Blog</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="/apps">Apps</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="//core.telegram.org/">Platform</a></h5>
|
||||
</div>
|
||||
<div class="footer_column">
|
||||
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/main.js?43"></script>
|
||||
|
||||
<script>twitterCustomShareInit();
|
||||
blogSideImageInit();
|
||||
backToTopInit("Go up");
|
||||
removePreloadInit();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in a new issue