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
ProfileTelegram
146 commands0 envUpdated 2d agoCreated Sep 5, 2026
Back to folder

commands/_confirm_buyitem.js

javascript · 563 lines

Raw
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}