ayushyadav7970824760/goldensellingstore_botPublic · Bot Template
AIThis Telegram store bot lets customers browse digital products and plans, apply coupons, add funds via UPI, and receive purchased keys. It includes a full admin panel for managing products, reordering items, setting reseller prices, viewing and removing key stock, configuring UPI payment details and update links, and checking user balances. Co-admins and resellers are supported, and support tickets are forwarded to the admin.
Commercedigital_storeadmin_panelupi_paymentsresellercouponskey_stock
146 commands0 envUpdated 2d agoCreated Sep 5, 2026
commands/_confirm_buyitem.js
javascript · 563 lines
1/**#command2name: /confirm_buyitem3answer: 4keyboard: 5parse_mode: markdown6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12function resolveApiDuration(plan, cleanPlanDays, planUnit, productNameOverride) {13 // IMPORTANT: Keep the admin/API duration name exactly as entered.14 // The product-specific day ranges below are used only when the stored plan15 // contains a plain numeric Day duration; this lets 1-30 Day plans target16 // the exact website/API names requested by the admin without changing any17 // other product data or purchase logic.18 var raw = String((plan && (plan.api_duration || plan.name_on_website || plan.durationDisplay || plan.name || plan.title)) || "").replace(/\s+/g, " ").trim();19 20 // Already-custom API labels (including casing/suffixes) must pass through unchanged.21 if (raw && !/^[0-9]+(?:\.[0-9]+)?\s*(day|days)$/i.test(raw)) {22 return raw;23 }24 25 var dayMatch = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(day|days)$/i);26 var n = dayMatch ? dayMatch[1] : String(cleanPlanDays || "1");27 var productName = String(productNameOverride || (plan && (plan.product_name || plan.productName || plan.product || plan.category)) || "").trim().toLowerCase();28 29 // If product name is not stored on the plan, the caller's raw API label is30 // still preserved above. Generic numeric Day plans remain "N Days".31 if (/pc\s*aim\s*silent/.test(productName)) return n + " Day Pc Aim Silent";32 if (/pc\s*modmenu\s*x86/.test(productName)) return n + " Day Pc Modmenu x86";33 if (/pc\s*bypass\s*\+\s*silent/.test(productName) || /bypass\s*\+\s*silent/.test(productName)) return n + " Days Pc Bypass + Silent";34 if (/nonroot/.test(productName) && !/root\s*\+\s*nonroot/.test(productName)) return n + " DaYS NONROOT";35 if (/pc\s*aimkill/.test(productName) || /aimkill/.test(productName)) return n + " DaYS PC AIMKILL";36 if (/root\s*\+\s*nonroot/.test(productName)) return n + " DaYs Root + Nonroot";37 if (/fluo?rite\s*ff/.test(productName)) return n + " DAYs FluoRite FF";38 if (/\broot\b/.test(productName)) return n + " DaYs ROOT";39 if (/all\s*colou?rs\s*mix/.test(productName)) return n + " DaYs All Colours Mix";40 if (/\bbasic\b/.test(productName)) return n + " DaYs Basic";41 if (/\bpro\b/.test(productName)) return n + " DaYs PRO";42 if (/\bsafe\b/.test(productName)) return n + " DaYs SAFE";43 if (/\bbrutal\b/.test(productName)) return n + " DaYs BRUTAL";44 if (/\bbrutal\b/.test(productName)) return n + " DaYs";45 46 // Hours/minutes are preserved exactly; no existing API duration is removed.47 var gh = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(hour|hours)$/i);48 if (gh) return gh[1] + " Hours";49 var gm = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(minute|minutes)$/i);50 if (gm) return gm[1] + (Number(gm[1]) === 1 ? " Minute" : " Minutes");51 52 return raw || n + " Days";53}54 55 56// =========================================================57// ✅ ACTUAL PURCHASE EXECUTION — runs after user taps "Confirm" on the58// premium confirmation screen, OR automatically right after a shortfall59// QR payment is verified (see _onCheck.js), so key delivery is fully automatic.60// =========================================================61 62try {63 var productList = Bot.getProperty("stored_products") || [];64 var chatId = chat.chatid;65 var userId = user.telegramid;66 67 var parts = params ? String(params).trim().split(" ") : [];68 var prodIdx = Number(parts[0]);69 var planIdx = Number(parts[1]);70 71 var product = productList[prodIdx];72 if (!product || !product.plans || !product.plans[planIdx]) {73 Api.sendMessage({ chat_id: chatId, text: "❌ Product or Plan configuration missing!" });74 return;75 }76 77 if (product.out_of_stock === true) {78 Api.sendMessage({79 chat_id: chatId,80 text: "<blockquote>🛒 <b>" + String(product.name).toUpperCase() + "</b></blockquote>\n<blockquote>👇 <b>NOTICE: THIS PRODUCT IS CURRENTLY UNDER MAINTENANCE.</b></blockquote>\n\nPlease check back later or contact admin support.",81 parse_mode: "HTML"82 });83 return;84 }85 86 var plan = product.plans[planIdx];87 var planUnit = plan.unit || "day";88 var isReseller = Bot.getProperty("is_reseller_" + userId) === true;89 90 var cleanProdName = product.name.trim().toUpperCase();91 var cleanPlanDays = String(plan.days).replace(/[^0-9.]/g, "").trim();92 var unitLabelWord = (planUnit === "hour") ? (Number(cleanPlanDays) === 1 ? "Hour" : "Hours") :93 (planUnit === "minute") ? (Number(cleanPlanDays) === 1 ? "Minute" : "Minutes") :94 (Number(cleanPlanDays) === 1 ? "Day" : "Days");95 var durationDisplay = plan.durationDisplay || (cleanPlanDays + " " + unitLabelWord);96 97 var priceKeySuffix = (planUnit === "day") ? cleanPlanDays : (cleanPlanDays + "_" + planUnit);98 var normalKey = "price_normal_" + cleanProdName + "_" + priceKeySuffix;99 var resellerKey = "price_reseller_" + cleanProdName + "_" + priceKeySuffix;100 101 var adminNormalPrice = Bot.getProperty(normalKey);102 var adminResellerPrice = Bot.getProperty(resellerKey);103 104 var currentNormal = (adminNormalPrice !== undefined && adminNormalPrice !== null) ? adminNormalPrice : plan.normal_price;105 var currentReseller = (adminResellerPrice !== undefined && adminResellerPrice !== null) ? adminResellerPrice : plan.reseller_price;106 107 var originalPrice = isReseller ? Number(currentReseller) : Number(currentNormal);108 var price = originalPrice;109 110 var planSpecificDiscKey = "auto_discount_" + cleanProdName + "_" + cleanPlanDays + "_" + planUnit.toUpperCase();111 var productWideDiscKey = "auto_discount_" + cleanProdName + "_ALL";112 var autoDiscountPercent = Bot.getProperty(planSpecificDiscKey) || Bot.getProperty(productWideDiscKey) || Bot.getProperty("auto_discount_global") || 0;113 114 // 🎟 Apply coupon discount (if any was applied for this exact prod/plan) — coupon overrides auto-discount115 var appliedCouponCode = User.getProperty("applied_coupon_" + prodIdx + "_" + planIdx);116 var couponDiscountPercent = 0;117 if (appliedCouponCode) {118 var couponRec0 = Bot.getProperty("coupon_" + appliedCouponCode);119 if (couponRec0 && couponRec0.discountPercent) {120 couponDiscountPercent = couponRec0.discountPercent;121 price = Number((originalPrice * (1 - couponDiscountPercent / 100)).toFixed(2));122 }123 User.setProperty("applied_coupon_" + prodIdx + "_" + planIdx, null, "string");124 } else if (autoDiscountPercent > 0) {125 couponDiscountPercent = autoDiscountPercent;126 price = Number((originalPrice * (1 - autoDiscountPercent / 100)).toFixed(2));127 }128 129 // 🛡️ Safety re-check: balance abhi bhi sufficient hai ya nahi (race-condition guard)130 var unifiedBal = (function () {131 var resBal = 0;132 try { resBal = Number(Libs.ResourcesLib.userRes("balance").value()) || 0; } catch (e) {}133 return resBal;134 })();135 136 if (unifiedBal < price) {137 Api.sendMessage({138 chat_id: chatId,139 text: "⚠️ <b>Balance abhi kam hai.</b> Kripya phir se try karein.",140 parse_mode: "HTML"141 });142 return;143 }144 145 // =====================================================146 // 🔑 STEP 0 — MANUAL STOCK FIRST CHECK (✅ NEW)147 // Agar admin ne manually koi key already daal rakhi hai, to wahi seedha148 // deliver karo aur PAID EXTERNAL API ko bilkul call hi mat karo — taaki149 // "1 available key ke liye 2 keys spend" (1 manual + 1 wasted API buy)150 // wala bug kabhi na ho. API sirf tabhi call hoga jab manual stock khaali ho.151 // =====================================================152 var pDaysNumOnly = cleanPlanDays;153 var manualKeysStorageKey = "manual_keys_" + cleanProdName + "_" + pDaysNumOnly;154 var backupStockKey = "stock_" + product.name.trim() + "_" + pDaysNumOnly + "_Day";155 var isBackupUsed = false;156 157 var manualStock = Bot.getProperty(manualKeysStorageKey);158 if (!manualStock || (Array.isArray(manualStock) && manualStock.length === 0)) {159 manualStock = Bot.getProperty(backupStockKey);160 isBackupUsed = true;161 }162 if (typeof manualStock === "string" && manualStock.trim() !== "") {163 try {164 manualStock = JSON.parse(manualStock);165 } catch (err) {166 manualStock = manualStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean);167 }168 }169 170 if (Array.isArray(manualStock) && manualStock.length > 0) {171 var generatedKey = manualStock.shift();172 173 if (isBackupUsed) {174 Bot.setProperty(backupStockKey, manualStock, "json");175 var mainStock = Bot.getProperty(manualKeysStorageKey) || [];176 if (typeof mainStock === "string") { mainStock = mainStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }177 if (Array.isArray(mainStock)) {178 var idx1 = mainStock.indexOf(generatedKey);179 if (idx1 > -1) { mainStock.splice(idx1, 1); }180 Bot.setProperty(manualKeysStorageKey, mainStock, "json");181 }182 } else {183 Bot.setProperty(manualKeysStorageKey, manualStock, "json");184 var backupStock2 = Bot.getProperty(backupStockKey) || [];185 if (typeof backupStock2 === "string") { backupStock2 = backupStock2.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }186 if (Array.isArray(backupStock2)) {187 var idx2 = backupStock2.indexOf(generatedKey);188 if (idx2 > -1) { backupStock2.splice(idx2, 1); }189 Bot.setProperty(backupStockKey, backupStock2, "json");190 }191 }192 193 // 💰 Deduct balance + record purchase (same accounting as API path)194 Libs.ResourcesLib.userRes("balance").add(-price);195 var pastSpent2 = Bot.getProperty("total_spent_by_" + userId) || 0;196 Bot.setProperty("total_spent_by_" + userId, Number(pastSpent2) + price, "number");197 198 // 🎁 REFER & EARN — referrer bonus jab referred friend PEHLI baar purchase kare199 try {200 var referredByBuy2 = User.getProperty("referred_by");201 if (referredByBuy2 && !User.getProperty("ref_buy_bonus_given")) {202 var refBonusBuy2 = Number(Bot.getProperty("refer_earn_buy_amount"));203 if (isNaN(refBonusBuy2)) refBonusBuy2 = 2;204 if (refBonusBuy2 > 0) {205 Libs.ResourcesLib.anotherUserRes("balance", referredByBuy2).add(refBonusBuy2);206 var pastEarnBuy2 = Number(Bot.getProperty("refer_earnings_" + referredByBuy2) || 0);207 Bot.setProperty("refer_earnings_" + referredByBuy2, pastEarnBuy2 + refBonusBuy2, "number");208 try {209 Api.sendMessage({210 chat_id: referredByBuy2,211 text: "<blockquote>💸 <b>Referral Bonus!</b>\n\nAapke referred friend ne pehli purchase ki — aapko ₹" + refBonusBuy2.toFixed(2) + " mile hain!</blockquote>",212 parse_mode: "HTML"213 });214 } catch (e) {}215 }216 User.setProperty("ref_buy_bonus_given", true, "boolean");217 }218 } catch (e) {}219 220 var keysHistory2 = User.getProperty("my_purchased_keys") || [];221 keysHistory2.push({222 product: String(product.name).trim(),223 product_id: product.id || null,224 days: pDaysNumOnly,225 price: price,226 key: generatedKey,227 date: new Date().toLocaleDateString()228 });229 User.setProperty("my_purchased_keys", keysHistory2, "json");230 231 var remainingBal2 = Libs.ResourcesLib.userRes("balance").value().toFixed(2);232 233 // ✅ NEW: Key ke saath automatic "Join Updates" button attach hota hai (agar admin ne link set kiya ho)234 var updateLinkForKey = Bot.getProperty("update_channel_link");235 var keyDeliveryButtons = [[{ text: "📋 Copy Key", copy_text: { text: generatedKey } }]];236 if (updateLinkForKey) {237 keyDeliveryButtons.push([{ text: "📥 Join Updates", url: updateLinkForKey, style: "primary" }]);238 }239 keyDeliveryButtons.push([{ text: "Back to Menu", callback_data: "/back", style: "danger" }]);240 var keyDeliveryMarkup = JSON.stringify({ inline_keyboard: keyDeliveryButtons });241 242 var deliverText2 = "<blockquote>" +243 "<tg-emoji emoji-id='5350447674971660988'>✅</tg-emoji> <b>PURCHASE SUCCESSFUL!</b>\n\n" +244 "<tg-emoji emoji-id='6147767796097884213'>📦</tg-emoji> <b>Product:</b> <code>" + String(product.name).trim() + "</code>\n" +245 "<tg-emoji emoji-id='6284816251143331422'>🗝</tg-emoji> <b>Validity:</b> <code>" + durationDisplay + "</code>\n" +246 "<tg-emoji emoji-id='5352825278672412291'>👆</tg-emoji> <b>Your Key:</b> <code>" + generatedKey + "</code>\n\n" +247 "━━━━━ <tg-emoji emoji-id='6147934084346682063'>#⃣</tg-emoji> <b>BALANCE DETAILS</b> ━━━━━\n" +248 "<tg-emoji emoji-id='6195037488898121775'>✨</tg-emoji> <b>Total Invest:</b> ₹" + price.toFixed(2) + "\n" +249 "<tg-emoji emoji-id='5409048419211682843'>💵</tg-emoji> <b>New Wallet Balance:</b> ₹" + remainingBal2 + "\n\n" +250 "<i>Enjoy your purchase. <tg-emoji emoji-id='6057881002540274780'>🥳</tg-emoji></i>" +251 "</blockquote>";252 253 if (request && request.message) {254 try {255 Api.editMessageText({256 chat_id: String(chatId),257 message_id: Number(request.message.message_id),258 text: deliverText2,259 parse_mode: "HTML",260 reply_markup: keyDeliveryMarkup261 });262 } catch (e) {263 Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });264 }265 } else {266 Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });267 }268 269 var adminId3 = "8477746023";270 Api.sendMessage({271 chat_id: adminId3,272 text: "🔔 <b>NEW PURCHASE DELIVERED (MANUAL STOCK)</b> ✔️\n\n" +273 "👤 <b>Buyer:</b> " + (user.first_name || "User") + " (<code>" + userId + "</code>)\n" +274 "📦 <b>Product:</b> " + String(product.name).trim() + "\n" +275 "⏳ <b>Plan:</b> " + durationDisplay + "\n" +276 "💸 <b>Price Deducted:</b> ₹" + price.toFixed(2) + "\n" +277 "💳 <b>User Remaining Bal:</b> ₹" + remainingBal2 + "\n" +278 "🔑 <b>Key:</b> <code>" + generatedKey + "</code>\n\n" +279 "<i>✅ Manual stock se deliver hui — external paid API call SKIP ki gayi.</i>",280 parse_mode: "HTML"281 });282 283 var qId0 = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;284 if (qId0) {285 try { Api.answerCallbackQuery({ callback_query_id: String(qId0), text: "✅ Key Delivered!", show_alert: false }); } catch (e) {}286 }287 288 return; // 🚫 STOP — external API bilkul call nahi hui289 }290 291 // =====================================================292 // 🆕 STEP 0b — MANUAL-KEY (NO PID) PRODUCT: ADMIN FULFILLS PER ORDER293 // Agar product ka koi Website PID set hi nahi hai (admin ne product add294 // karte waqt PID skip ki thi) aur manual stock bhi khaali hai, to external295 // paid API bilkul call nahi hoti. Balance turant deduct ho jaata hai,296 // admin ko order details ke saath "Give Key" button milta hai, aur wahi297 // click karke jo bhi key type karega, wo seedha buyer ko deliver ho jaayegi.298 // =====================================================299 // ✅ FIXED: sirf khaali PID hi nahi — agar admin ne product add karte waqt300 // galti se placeholder jaisa text (PID, PID_ID, N/A, -, skip, none, 0) type301 // kar diya tha, wo bhi ab manual-key product maana jaata hai. Isse pehle se302 // add ho chuke products (jinki id literally "PID" save ho gayi thi) bhi303 // turant sahi se manual "Give Key" flow me chale jaate hain — dobara add304 // karne ki zaroorat nahi.305 var pidRawCheck = String(product.id || "").trim();306 var isManualKeyProduct = product.manual_key_product === true || !pidRawCheck || /^(pid|pid_id|n\/?a|none|skip|-|0)$/i.test(pidRawCheck);307 if (isManualKeyProduct) {308 // 💰 Balance deduct + accounting (order confirm hote hi payment ho jaata hai)309 Libs.ResourcesLib.userRes("balance").add(-price);310 var pastSpentM = Bot.getProperty("total_spent_by_" + userId) || 0;311 Bot.setProperty("total_spent_by_" + userId, Number(pastSpentM) + price, "number");312 313 // 🎁 REFER & EARN — referrer bonus jab referred friend PEHLI baar purchase kare314 try {315 var referredByBuyM = User.getProperty("referred_by");316 if (referredByBuyM && !User.getProperty("ref_buy_bonus_given")) {317 var refBonusBuyM = Number(Bot.getProperty("refer_earn_buy_amount"));318 if (isNaN(refBonusBuyM)) refBonusBuyM = 2;319 if (refBonusBuyM > 0) {320 Libs.ResourcesLib.anotherUserRes("balance", referredByBuyM).add(refBonusBuyM);321 var pastEarnBuyM = Number(Bot.getProperty("refer_earnings_" + referredByBuyM) || 0);322 Bot.setProperty("refer_earnings_" + referredByBuyM, pastEarnBuyM + refBonusBuyM, "number");323 try {324 Api.sendMessage({325 chat_id: referredByBuyM,326 text: "<blockquote>💸 <b>Referral Bonus!</b>\n\nAapke referred friend ne pehli purchase ki — aapko ₹" + refBonusBuyM.toFixed(2) + " mile hain!</blockquote>",327 parse_mode: "HTML"328 });329 } catch (e) {}330 }331 User.setProperty("ref_buy_bonus_given", true, "boolean");332 }333 } catch (e) {}334 335 var remainingBalM = Libs.ResourcesLib.userRes("balance").value().toFixed(2);336 var buyerFullName = ((user.first_name || "") + " " + (user.last_name || "")).trim() || "No Name";337 var buyerUsername = user.username ? "@" + user.username : "No Username";338 339 var waitText = "<blockquote>" +340 "<tg-emoji emoji-id='6147936236125298267'>⏳</tg-emoji> <b>ORDER PLACED!</b>\n\n" +341 "<tg-emoji emoji-id='6147767796097884213'>📦</tg-emoji> <b>Product:</b> <code>" + String(product.name).trim() + "</code>\n" +342 "<tg-emoji emoji-id='6284816251143331422'>🗝</tg-emoji> <b>Validity:</b> <code>" + durationDisplay + "</code>\n" +343 "<tg-emoji emoji-id='6195037488898121775'>✨</tg-emoji> <b>Paid:</b> ₹" + price.toFixed(2) + "\n" +344 "<tg-emoji emoji-id='5409048419211682843'>💵</tg-emoji> <b>New Wallet Balance:</b> ₹" + remainingBalM + "\n\n" +345 "<i>Payment received! Admin aapki key thodi hi der me manually bhej denge, kripya wait karein.</i>" +346 "</blockquote>";347 348 // ✅ Buyer message ka message_id capture karo — isi ek message ko baad me349 // key delivery ke waqt EDIT kiya jaayega (naya alag message nahi bhejenge).350 var buyerMsgIdM = null;351 if (request && request.message) {352 buyerMsgIdM = Number(request.message.message_id);353 try {354 Api.editMessageText({355 chat_id: String(chatId),356 message_id: buyerMsgIdM,357 text: waitText,358 parse_mode: "HTML",359 reply_markup: JSON.stringify({ inline_keyboard: [[{ text: "Back to Menu", callback_data: "/back", style: "danger" }]] })360 });361 } catch (e) {362 try {363 var bpm = Api.sendMessage({ chat_id: chatId, text: waitText, parse_mode: "HTML" });364 buyerMsgIdM = (bpm && bpm.result && bpm.result.message_id) ? bpm.result.message_id : (bpm && bpm.message_id ? bpm.message_id : null);365 } catch (e2) {}366 }367 } else {368 try {369 var bpm2 = Api.sendMessage({ chat_id: chatId, text: waitText, parse_mode: "HTML" });370 buyerMsgIdM = (bpm2 && bpm2.result && bpm2.result.message_id) ? bpm2.result.message_id : (bpm2 && bpm2.message_id ? bpm2.message_id : null);371 } catch (e3) {}372 }373 374 // 🔔 Admin ko order details + "Give Key" button — message_id yahan bhi375 // capture karo taaki "Give Key" click aur key delivery ke baad usi376 // message ko EDIT kiya ja sake (naya message spam na ho).377 var adminIdM = Bot.getProperty("owner_id") || "8477746023";378 var manualOrderId = String(Date.now()) + "_" + userId;379 var adminMsgIdM = null;380 try {381 var adminAlertRes = Api.sendMessage({382 chat_id: adminIdM,383 text: "<blockquote>🔔 <b>NEW ORDER — MANUAL KEY REQUIRED</b> 🔑</blockquote>\n\n" +384 "👤 <b>Buyer:</b> " + buyerFullName + " (" + buyerUsername + ") — <code>" + userId + "</code>\n" +385 "📦 <b>Product:</b> " + String(product.name).trim() + "\n" +386 "⏳ <b>Plan:</b> " + durationDisplay + "\n" +387 "💸 <b>Price Paid:</b> ₹" + price.toFixed(2) + "\n\n" +388 "<i>Neeche button dabaayein aur agli message me KEY type karke bhejein — buyer ko turant deliver ho jaayegi.</i>",389 parse_mode: "HTML",390 reply_markup: JSON.stringify({391 inline_keyboard: [[{ text: "🔑 Give Key", callback_data: "/give_key_btn " + manualOrderId, style: "success" }]]392 })393 });394 adminMsgIdM = (adminAlertRes && adminAlertRes.result && adminAlertRes.result.message_id) ? adminAlertRes.result.message_id : (adminAlertRes && adminAlertRes.message_id ? adminAlertRes.message_id : null);395 } catch (e) {}396 397 // 🧾 Order ko admin ke fulfillment ke liye save karo — dono taraf ke398 // message_id bhi saath me, taaki delivery ke waqt naya message bhejne ke399 // bajaye yehi messages EDIT ho sakein.400 Bot.setProperty("pending_manual_order_" + manualOrderId, {401 buyerId: String(userId),402 buyerName: buyerFullName,403 buyerUsername: buyerUsername,404 prodName: String(product.name).trim(),405 durationDisplay: durationDisplay,406 price: price,407 buyerChatId: String(chatId),408 buyerMsgId: buyerMsgIdM,409 buyerMsgType: "text",410 adminChatId: String(adminIdM),411 adminMsgId: adminMsgIdM412 }, "json");413 414 var qIdM = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;415 if (qIdM) {416 try { Api.answerCallbackQuery({ callback_query_id: String(qIdM), text: "✅ Order placed! Admin will send your key shortly.", show_alert: false }); } catch (e) {}417 }418 419 return; // 🚫 STOP — external API bilkul call nahi hui420 }421 422 var webProductId = String(product.id || "PID_ID").trim();423 var prodNameLower = String(product.name).toLowerCase();424 425 var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);426 427 // =====================================================428 // 🔌 MULTI-API SYSTEM429 // Admin can add/update/select multiple reseller APIs.430 // The active API is used for external purchases.431 // =====================================================432 var apiRegistry = Bot.getProperty("api_registry") || {};433 var activeApiId = Bot.getProperty("active_reseller_api");434 var selectedApi = activeApiId ? apiRegistry[activeApiId] : null;435 436 // Backward-compatible bootstrap from the existing API configuration.437 if (!selectedApi) {438 selectedApi = {439 id: "default",440 name: "Default Reseller API",441 url: "https://adminpanels.shop/api/reseller_v1.php",442 api_key: "9cd415688a9b01920994099cba20180c",443 master_key: "a7f3e8b2c9d1f4a6b8c2d5e9f1a3b6c8",444 mode: "reseller_v1",445 android_required: false,446 enabled: true447 };448 apiRegistry.default = selectedApi;449 Bot.setProperty("api_registry", apiRegistry, "json");450 Bot.setProperty("active_reseller_api", "default", "string");451 }452 453 if (selectedApi.enabled === false) {454 Api.sendMessage({chat_id: chatId, text: "❌ Selected API is disabled. Admin ko /apiset se another API select karna hoga."});455 return;456 }457 458 var webProductId = String(product.id || "PID_ID").trim();459 var prodNameLower = String(product.name).toLowerCase();460 461 var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);462 463 // Product-level API override is supported: product.api_id.464 var productApiId = product.api_id ? String(product.api_id).trim() : "";465 if (productApiId && apiRegistry[productApiId] && apiRegistry[productApiId].enabled !== false) {466 selectedApi = apiRegistry[productApiId];467 activeApiId = productApiId;468 }469 470 var postFields = {471 api_key: selectedApi.api_key || "",472 action: "buy",473 product_id: webProductId,474 duration: durationParam475 };476 477 // Device-bound APIs can require android_id. The value may be saved by478 // your existing product/user flow as android_id_<USER_ID>.479 var savedAndroidId = User.getProperty("android_id") || User.getProperty("android_id_" + userId) ||480 Bot.getProperty("android_id_" + userId) || "";481 if (selectedApi.android_required) {482 if (!savedAndroidId) {483 Api.sendMessage({484 chat_id: chatId,485 text: "⚠️ <b>Android ID required</b>\nIs API/product ke liye Android ID zaroori hai. Pehle user ka Android ID save karein.",486 parse_mode: "HTML"487 });488 return;489 }490 postFields.android_id = String(savedAndroidId);491 } else if (savedAndroidId) {492 // Optional APIs may also accept it.493 postFields.android_id = String(savedAndroidId);494 }495 496 var apiHeaders = {497 "Content-Type": "application/x-www-form-urlencoded"498 };499 if (selectedApi.master_key) apiHeaders["x-master-key"] = selectedApi.master_key;500 501 var postFieldsString = Object.keys(postFields).map(function(k) {502 return encodeURIComponent(k) + "=" + encodeURIComponent(postFields[k]);503 }).join("&");504 505 User.setProperty("last_pending_price", Number(price), "number");506 User.setProperty("last_pending_prod_id", webProductId, "string");507 User.setProperty("last_pending_prod_name", String(product.name).trim(), "string");508 User.setProperty("last_pending_plan_days", cleanPlanDays, "string");509 User.setProperty("last_pending_plan_unit", planUnit, "string");510 User.setProperty("last_pending_api_id", String(selectedApi.id || activeApiId || ""), "string");511 512 var queryId = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;513 514 var processingText = "<tg-emoji emoji-id='6147936236125298267'>⏳</tg-emoji> <b>Processing your order... please wait!</b>";515 if (request && request.message) {516 try {517 Api.editMessageText({518 chat_id: String(chatId),519 message_id: Number(request.message.message_id),520 text: processingText,521 parse_mode: "HTML"522 });523 Bot.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");524 User.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");525 } catch (e) {526 var pm1 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });527 try {528 var pmid1 = (pm1 && pm1.result && pm1.result.message_id) ? pm1.result.message_id : (pm1 && pm1.message_id ? pm1.message_id : null);529 if (pmid1) { Bot.setProperty("gen_msg_id_" + userId, pmid1, "string"); User.setProperty("gen_msg_id_" + userId, pmid1, "string"); }530 } catch (e2) {}531 }532 } else {533 var pm2 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });534 try {535 var pmid2 = (pm2 && pm2.result && pm2.result.message_id) ? pm2.result.message_id : (pm2 && pm2.message_id ? pm2.message_id : null);536 if (pmid2) { Bot.setProperty("gen_msg_id_" + userId, pmid2, "string"); User.setProperty("gen_msg_id_" + userId, pmid2, "string"); }537 } catch (e2) {}538 }539 540 HTTP.post({541 url: selectedApi.url,542 body: postFieldsString,543 headers: apiHeaders,544 success: "/onWebKeyReceive",545 error: "/onWebKeyError"546 });547 548 if (queryId) {549 try {550 Api.answerCallbackQuery({551 callback_query_id: String(queryId),552 text: "⏳ Processing your order... Please wait!",553 show_alert: false554 });555 } catch (e) {}556 }557 558} catch (e) {559 var errChatId = (typeof chat !== "undefined" && chat && chat.chatid) ? chat.chatid : null;560 if (errChatId) {561 try { Api.sendMessage({ chat_id: errChatId, text: "❌ <b>Error:</b> <code>" + e.message + "</code>", parse_mode: "HTML" }); } catch (fatal) {}562 }563}