diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6fcaa13d5..bf70590d56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # TelegramBotAPI changelog
 
+## 3.1.1
+
+* `Common`:
+    * Complete Bot API 6.2 implementation
+
 ## 3.1.0
 
 **This update contains including of Bot API 6.2**
diff --git a/gradle.properties b/gradle.properties
index 6ca3ff5cb4..c0a1d7b5fe 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,4 +6,4 @@ kotlin.incremental=true
 kotlin.incremental.js=true
 
 library_group=dev.inmo
-library_version=3.1.0
+library_version=3.1.1
diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt
index 103f327380..0e1cf5f246 100644
--- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt
+++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt
@@ -164,6 +164,7 @@ const val languageCodeField = "language_code"
 const val addedToAttachmentMenuField = "added_to_attachment_menu"
 const val isPremiumField = "is_premium"
 const val hasPrivateForwardsField = "has_private_forwards"
+const val hasRestrictedVoiceAndVideoMessagesField = "has_restricted_voice_and_video_messages"
 const val canJoinGroupsField = "can_join_groups"
 const val canReadAllGroupMessagesField = "can_read_all_group_messages"
 const val supportInlineQueriesField = "supports_inline_queries"
diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Extended.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Extended.kt
index fed027677d..01ead1be28 100644
--- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Extended.kt
+++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Extended.kt
@@ -62,7 +62,9 @@ data class ExtendedPrivateChatImpl(
     @SerialName(bioField)
     override val bio: String = "",
     @SerialName(hasPrivateForwardsField)
-    override val hasPrivateForwards: Boolean = false
+    override val hasPrivateForwards: Boolean = false,
+    @SerialName(hasRestrictedVoiceAndVideoMessagesField)
+    override val hasRestrictedVoiceAndVideoMessages: Boolean = false
 ) : ExtendedPrivateChat
 
 typealias ExtendedUser = ExtendedPrivateChatImpl
diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt
index 26cf8115ae..6705d8203d 100644
--- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt
+++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt
@@ -19,6 +19,7 @@ sealed interface ExtendedGroupChat : GroupChat, ExtendedPublicChat {
 sealed interface ExtendedPrivateChat : PrivateChat, ExtendedChat {
     val bio: String
     val hasPrivateForwards: Boolean
+    val hasRestrictedVoiceAndVideoMessages: Boolean
 
     val allowCreateUserIdLink: Boolean
         get() = hasPrivateForwards
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/AlertCallback.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/AlertCallback.kt
new file mode 100644
index 0000000000..8ed7e55b60
--- /dev/null
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/AlertCallback.kt
@@ -0,0 +1,3 @@
+package dev.inmo.tgbotapi.webapps
+
+typealias AlertCallback = () -> Unit
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/ConfirmCallback.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/ConfirmCallback.kt
new file mode 100644
index 0000000000..4c9f85296b
--- /dev/null
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/ConfirmCallback.kt
@@ -0,0 +1,3 @@
+package dev.inmo.tgbotapi.webapps
+
+typealias ConfirmCallback = (confirmed: Boolean) -> Unit
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventHandler.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventHandler.kt
index e0e5dc7f37..f83d2fd7dc 100644
--- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventHandler.kt
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventHandler.kt
@@ -5,3 +5,4 @@ import dev.inmo.tgbotapi.webapps.invoice.InvoiceClosedInfo
 typealias EventHandler = WebApp.() -> Unit
 typealias ViewportChangedEventHandler = WebApp.(ViewportChangedData) -> Unit
 typealias InvoiceClosedEventHandler = WebApp.(InvoiceClosedInfo) -> Unit
+typealias PopupClosedEventHandler = WebApp.(String?) -> Unit
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventType.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventType.kt
index a75b20218c..ff71329074 100644
--- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventType.kt
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/EventType.kt
@@ -7,4 +7,5 @@ sealed class EventType(val typeName: String) {
     object BackButtonClicked : EventType("backButtonClicked")
     object SettingsButtonClicked : EventType("settingsButtonClicked")
     object InvoiceClosed : EventType("invoiceClosed")
+    object PopupClosed : EventType("popupClosed")
 }
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt
index 365d07af4b..d31d1d6cf9 100644
--- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt
@@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.webapps
 import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
 import dev.inmo.tgbotapi.webapps.haptic.HapticFeedback
 import dev.inmo.tgbotapi.webapps.invoice.InvoiceClosedInfo
+import dev.inmo.tgbotapi.webapps.popup.*
 
 external class WebApp {
     val version: String
@@ -24,6 +25,15 @@ external class WebApp {
     val viewportHeight: Float
     val viewportStableHeight: Float
 
+
+    val isClosingConfirmationEnabled: Boolean
+    fun enableClosingConfirmation()
+    fun disableClosingConfirmation()
+
+    fun showPopup(params: PopupParams, callback: ClosePopupCallback? = definedExternally)
+    fun showAlert(message: String, callback: AlertCallback? = definedExternally)
+    fun showConfirm(message: String, callback: ConfirmCallback? = definedExternally)
+
     @JsName("MainButton")
     val mainButton: MainButton
 
@@ -38,6 +48,8 @@ external class WebApp {
     internal fun onEventWithViewportChangedData(type: String, callback: (ViewportChangedData) -> Unit)
     @JsName("onEvent")
     internal fun onEventWithInvoiceClosedInfo(type: String, callback: (InvoiceClosedInfo) -> Unit)
+    @JsName("onEvent")
+    internal fun onEventWithPopupClosedInfo(type: String, callback: (String?) -> Unit)
 
     fun offEvent(type: String, callback: () -> Unit)
     @JsName("offEvent")
@@ -100,6 +112,18 @@ fun WebApp.onEvent(type: EventType.InvoiceClosed, eventHandler: InvoiceClosedEve
     )
 }
 
+/**
+ * @return The callback which should be used in case you want to turn off events handling
+ */
+fun WebApp.onEvent(type: EventType.PopupClosed, eventHandler: PopupClosedEventHandler) = { it: String? ->
+    eventHandler(js("this").unsafeCast<WebApp>(), it)
+}.also {
+    onEventWithPopupClosedInfo(
+        type.typeName,
+        callback = it
+    )
+}
+
 /**
  * @return The callback which should be used in case you want to turn off events handling
  */
@@ -124,8 +148,55 @@ fun WebApp.onSettingsButtonClicked(eventHandler: EventHandler) = onEvent(EventTy
  * @return The callback which should be used in case you want to turn off events handling
  */
 fun WebApp.onInvoiceClosed(eventHandler: InvoiceClosedEventHandler) = onEvent(EventType.InvoiceClosed, eventHandler)
+/**
+ * @return The callback which should be used in case you want to turn off events handling
+ */
+fun WebApp.onPopupClosed(eventHandler: PopupClosedEventHandler) = onEvent(EventType.PopupClosed, eventHandler)
 
 fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppData(
     initData,
     initDataUnsafe.hash
 )
+
+fun WebApp.showPopup(
+    message: String,
+    title: String?,
+    buttons: Array<PopupButton>,
+    callback: ClosePopupCallback? = null
+) = showPopup(
+    PopupParams(
+        message,
+        title,
+        buttons
+    ),
+    callback
+)
+
+fun WebApp.showPopup(
+    message: String,
+    title: String?,
+    firstButton: PopupButton,
+    vararg otherButtons: PopupButton,
+    callback: ClosePopupCallback? = null
+) = showPopup(
+    PopupParams(
+        message,
+        title,
+        arrayOf(firstButton, *otherButtons)
+    ),
+    callback
+)
+
+var WebApp.requireClosingConfirmation
+    get() = isClosingConfirmationEnabled
+    set(value) {
+        if (value) {
+            enableClosingConfirmation()
+        } else {
+            disableClosingConfirmation()
+        }
+    }
+
+fun WebApp.toggleClosingConfirmation() {
+    requireClosingConfirmation = !requireClosingConfirmation
+}
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebAppUser.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebAppUser.kt
index 92f7b87fd2..facd42061e 100644
--- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebAppUser.kt
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebAppUser.kt
@@ -17,10 +17,14 @@ external interface WebAppUser {
     val username: String?
     @JsName(languageCodeField)
     val languageCode: String?
+    val is_premium: Boolean?
     @JsName(photoUrlField)
     val photoUrl: String?
 }
 
+val WebAppUser.isPremium
+    get() = is_premium == true
+
 fun WebAppUser.asUser() = if (isBot == true) {
     CommonBot(
         UserId(id),
@@ -34,6 +38,7 @@ fun WebAppUser.asUser() = if (isBot == true) {
         firstName,
         lastName ?: "",
         username ?.let(::Username),
-        languageCode ?.let(::IetfLanguageCode)
+        languageCode ?.let(::IetfLanguageCode),
+        isPremium = isPremium
     )
 }
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/ClosePopupCallback.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/ClosePopupCallback.kt
new file mode 100644
index 0000000000..17727bef69
--- /dev/null
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/ClosePopupCallback.kt
@@ -0,0 +1,3 @@
+package dev.inmo.tgbotapi.webapps.popup
+
+typealias ClosePopupCallback = (id: String) -> Unit
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupButton.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupButton.kt
new file mode 100644
index 0000000000..992a70c1cb
--- /dev/null
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupButton.kt
@@ -0,0 +1,55 @@
+package dev.inmo.tgbotapi.webapps.popup
+
+import kotlin.js.json
+
+external interface PopupButton {
+    val id: String
+    val type: PopupButtonType
+    val text: String?
+}
+
+fun PopupButton(
+    id: String,
+    type: PopupButtonType,
+    text: String? = null
+) = json(
+    *listOfNotNull(
+        "id" to id,
+        "type" to type.typeName,
+        ("text" to text).takeIf { text != null }
+    ).toTypedArray()
+).unsafeCast<PopupButton>()
+
+value class PopupButtonType(
+    val typeName: String
+) {
+    companion object {
+        val Default = PopupButtonType("default")
+        val Ok = PopupButtonType("ok")
+        val Close = PopupButtonType("close")
+        val Cancel = PopupButtonType("cancel")
+        val Destructive = PopupButtonType("destructive")
+    }
+}
+
+fun DefaultPopupButton(
+    id: String,
+    text: String
+) = PopupButton(id, PopupButtonType.Default, text)
+
+fun OkPopupButton(
+    id: String
+) = PopupButton(id, PopupButtonType.Ok)
+
+fun ClosePopupButton(
+    id: String
+) = PopupButton(id, PopupButtonType.Close)
+
+fun CancelPopupButton(
+    id: String
+) = PopupButton(id, PopupButtonType.Cancel)
+
+fun DestructivePopupButton(
+    id: String,
+    text: String
+) = PopupButton(id, PopupButtonType.Destructive, text)
diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupParams.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupParams.kt
new file mode 100644
index 0000000000..8fe40507cb
--- /dev/null
+++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/popup/PopupParams.kt
@@ -0,0 +1,48 @@
+package dev.inmo.tgbotapi.webapps.popup
+
+import kotlin.js.json
+
+external interface PopupParams {
+    val message: String
+    val title: String?
+    val buttons: Array<PopupButton>
+}
+
+fun PopupParams(
+    message: String,
+    title: String?,
+    buttons: Array<PopupButton>
+) = json(
+    *listOfNotNull(
+        "message" to message,
+        "buttons" to buttons,
+        ("title" to title).takeIf { title != null }
+    ).toTypedArray()
+).unsafeCast<PopupParams>()
+
+fun PopupParams(
+    message: String,
+    firstButton: PopupButton,
+    vararg otherButtons: PopupButton
+) = PopupParams(
+    message,
+    null,
+    arrayOf(
+        firstButton,
+        *otherButtons
+    )
+)
+
+fun PopupParams(
+    title: String,
+    message: String,
+    firstButton: PopupButton,
+    vararg otherButtons: PopupButton
+) = PopupParams(
+    message,
+    title,
+    arrayOf(
+        firstButton,
+        *otherButtons
+    )
+)