From 0e382d8f72fde9d487f1ef48610b53231d12cf67 Mon Sep 17 00:00:00 2001 From: justuser-31 Date: Mon, 24 Nov 2025 19:06:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D1=80=D0=BE=D0=BA=D0=B0=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BE=D0=BF=D0=BB=D0=B0=D1=82=D1=8B=20=D1=81?= =?UTF-8?q?=D1=87=D1=91=D1=82=D0=B0,=20=D0=BF=D0=BE=20=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D1=8E=20-=20=D1=83=D0=B4=D0=B0?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D1=87=D1=91=D1=82=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Vp_server_integration.kt | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt b/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt index 440384e..847fe24 100644 --- a/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt +++ b/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt @@ -22,6 +22,7 @@ import java.io.FileInputStream import java.util.Properties import kotlin.collections.mutableMapOf import kotlin.math.abs +import java.util.Date class Vp_server_integration() : JavaPlugin(), CommandExecutor { companion object { @@ -38,6 +39,7 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor { var COURSE_STATIC_VALUE: Double = DEFAULT_COURSE_STATIC_VALUE lateinit var COURSE_DYNAMIC_COMMAND: String var COURSE_COMMISSION: Float = DEFAULT_COURSE_COMMISSION + var INVOICE_TIMEOUT_SECONDS: Long = DEFAULT_INVOICE_TIMEOUT_SECONDS // Default configuration values const val DEFAULT_USERNAME: String = "test" @@ -51,6 +53,7 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor { const val DEFAULT_COURSE_STATIC_VALUE: Double = 1000.0 const val DEFAULT_COURSE_DYNAMIC_COMMAND: String = "baltop force" const val DEFAULT_COURSE_COMMISSION: Float = 5.0f + const val DEFAULT_INVOICE_TIMEOUT_SECONDS: Long = 300 // 5 minutes const val PREFIX = "&9[&bVPC&7-&6I&9] &3" @@ -67,6 +70,10 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor { var TO_PAY_INVOICES: MutableMap = mutableMapOf() // Pair: {player: invoice_id} @JvmStatic var INVOICES_AMOUNT: MutableMap = mutableMapOf() // How many should we pay to player after invoice? + + // Track invoice creation times for timeout handling + @JvmStatic + var INVOICE_CREATION_TIMES: MutableMap = mutableMapOf() // Pair: {invoice_id: creation_timestamp} } /** @@ -102,6 +109,7 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor { COURSE_STATIC_VALUE = properties.getProperty("course_static_value", DEFAULT_COURSE_STATIC_VALUE.toString()).replace("'", "").toDouble() COURSE_DYNAMIC_COMMAND = properties.getProperty("course_dynamic_command", DEFAULT_COURSE_DYNAMIC_COMMAND).replace("'", "") COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).replace("'", "").toFloat() + INVOICE_TIMEOUT_SECONDS = properties.getProperty("invoice_timeout_seconds", DEFAULT_INVOICE_TIMEOUT_SECONDS.toString()).replace("'", "").toLong() logger.info("Configuration loaded successfully - Username: $USERNAME, API URL: $USER_API_URL") } catch (e: Exception) { @@ -163,6 +171,11 @@ course_dynamic_command='$DEFAULT_COURSE_DYNAMIC_COMMAND' # For dynamic course recommended 5% and higher (avoid dupe), for static course you can set 0% course_commission=$DEFAULT_COURSE_COMMISSION # -------------------- END ------------------------ + +# --------- Invoice timeout configuration ---------- +# After how many seconds unpaid invoices will be deleted (default: 300 seconds = 5 minutes) +invoice_timeout_seconds=$DEFAULT_INVOICE_TIMEOUT_SECONDS +# -------------------- END ------------------------ """.trimIndent() ) } @@ -381,6 +394,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION val invoiceId = VpcApi.create_invoice(amount).toString() TO_PAY_INVOICES[sender.name] = invoiceId INVOICES_AMOUNT[invoiceId] = amountLC + INVOICE_CREATION_TIMES[invoiceId] = System.currentTimeMillis() sendPaymentPrompt(sender, amount, amountLC, invoiceId) } @@ -437,6 +451,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION synchronized(TO_AUTH_PLAYERS) { TO_AUTH_PLAYERS[sender.name] = vpcUsernameInput TO_AUTH_PLAYERS_INVOICES[sender.name] = invoiceId.toString() + INVOICE_CREATION_TIMES[invoiceId.toString()] = System.currentTimeMillis() } LOGGER.info("Authentication lists updated") @@ -502,6 +517,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION override fun run() { processAuthenticationInvoices() processPaymentInvoices() + cleanupExpiredInvoices() } }.runTaskTimerAsynchronously(this, 0L, 20L) } @@ -531,6 +547,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION // Clean up tracking maps TO_AUTH_PLAYERS.remove(entry.key) + INVOICE_CREATION_TIMES.remove(entry.value) iterator.remove() } } else { @@ -583,6 +600,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION // Clean up tracking maps INVOICES_AMOUNT.remove(entry.key) + INVOICE_CREATION_TIMES.remove(entry.value) iterator.remove() } } else { @@ -596,4 +614,72 @@ course_commission=$DEFAULT_COURSE_COMMISSION } } } -} + + /** + * Cleanup expired invoices based on timeout configuration + */ + private fun cleanupExpiredInvoices() { + val currentTime = System.currentTimeMillis() + val timeoutMillis = INVOICE_TIMEOUT_SECONDS * 1000 + + // Cleanup auth invoices + synchronized(TO_AUTH_PLAYERS_INVOICES) { + val authIterator = TO_AUTH_PLAYERS_INVOICES.iterator() + while (authIterator.hasNext()) { + val entry = authIterator.next() + val invoiceId = entry.value + val creationTime = INVOICE_CREATION_TIMES[invoiceId] ?: continue + + if (currentTime - creationTime > timeoutMillis) { + LOGGER.info("Deleting expired auth invoice: $invoiceId") + try { + VpcApi.delete_invoice(invoiceId) + } catch (e: Exception) { + LOGGER.error("Error deleting expired auth invoice $invoiceId: ${e.message}") + } + + // Clean up tracking maps + TO_AUTH_PLAYERS.remove(entry.key) + INVOICE_CREATION_TIMES.remove(invoiceId) + authIterator.remove() + + // Notify player if online + val player = Bukkit.getPlayer(entry.key) + if (player != null && player.isOnline) { + Utils.send(player, "&cВаша авторизационная оплата истекла. Пожалуйста, попробуйте снова: /vpi auth <ник>") + } + } + } + } + + // Cleanup payment invoices + synchronized(TO_PAY_INVOICES) { + val paymentIterator = TO_PAY_INVOICES.iterator() + while (paymentIterator.hasNext()) { + val entry = paymentIterator.next() + val invoiceId = entry.value + val creationTime = INVOICE_CREATION_TIMES[invoiceId] ?: continue + + if (currentTime - creationTime > timeoutMillis) { + LOGGER.info("Deleting expired payment invoice: $invoiceId") + try { + VpcApi.delete_invoice(invoiceId) + } catch (e: Exception) { + LOGGER.error("Error deleting expired payment invoice $invoiceId: ${e.message}") + } + + // Clean up tracking maps + INVOICES_AMOUNT.remove(invoiceId) + INVOICE_CREATION_TIMES.remove(invoiceId) + paymentIterator.remove() + + // Notify player if online + val player = Bukkit.getPlayer(entry.key) + if (player != null && player.isOnline) { + Utils.send(player, "&cВаша оплата истекла. Пожалуйста, создайте новую: /vpi convert lc <сумма>") + } + } + } + } + } +} \ No newline at end of file