tarunpoonia905/OfcDevShopBotPublic · Bot Template

ProfileTelegram

AIDEV SHOP appears to be a Telegram automation bot. Commands include /confirm_delete_stock, /addstock_id, /extkey_menu, /partner_api, /apidelete, /buy_hack_cat, /admin_manage_products, /check_buy. Observed in code: messaging, keyboards, payments, libs, web, http, games.

Utilityutility
228 commands0 envUpdated 12d agoCreated Oct 18, 2026
Back to folder

commands/_confirm_buyitem.js

javascript · 565 lines

Raw
1/**#command2name: /confirm_buyitem3answer: 4keyboard: 5parse_mode: markdown6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12/*#SIZE_V1*/try{(function(){var z=Bot.getProperty("ui_btn_size");var mx=z==="large"?1:(z==="medium"?2:0);if(!mx||Api.__sz)return;var NAV=/^[◀▶⬅➡«»‹›⏪⏩]|\d+\s*\/\s*\d+|prev|next|page/i;var T=function(m){try{var o=typeof m==="string"?JSON.parse(m):m;if(!o||!o.inline_keyboard)return m;var out=[],cnt=0;o.inline_keyboard.forEach(function(row){var nav=row.some(function(b){return NAV.test(String(b.text||"").trim());});if(row.length<=mx||nav){out.push(row);cnt+=row.length;return;}for(var i=0;i<row.length;i+=mx){var c=row.slice(i,i+mx);out.push(c);cnt+=c.length;}});if(cnt>100||out.length>100)return m;o.inline_keyboard=out;return typeof m==="string"?JSON.stringify(o):o;}catch(e){return m;}};["sendMessage","editMessageText","editMessageCaption","editMessageReplyMarkup","sendPhoto","sendVideo","sendAnimation","sendDocument"].forEach(function(n){var f=Api[n];if(typeof f!=="function")return;Api[n]=function(p){try{if(p&&p.reply_markup)p.reply_markup=T(p.reply_markup);}catch(e){}return f.apply(Api,arguments);};});Api.__sz=true;})();}catch(__szE){}13/*#ANIM_V1*/try{(function(){var M={party:"5046509860389126442",fire:"5104841245755180586",heart:"5159385139981059251",like:"5107584321108051014"};var a=Bot.getProperty("ui_anim");if(!a||!M[a]||Api.__an)return;var E=M[a];var OK=/PURCHASE SUCCESSFUL|PAYMENT SUCCESSFUL|Payment verified|CONGRATULATIONS/i;var priv=function(p){return p&&p.chat_id&&String(p.chat_id).charAt(0)!=="-";};["sendMessage","sendPhoto","sendVideo","sendAnimation"].forEach(function(n){var f=Api[n];if(typeof f!=="function")return;Api[n]=function(p){try{if(priv(p)&&!p.message_effect_id&&OK.test(String(p.text||p.caption||"")))p.message_effect_id=E;}catch(e){}return f.apply(Api,arguments);};});var fe=Api.editMessageText;if(typeof fe==="function"){Api.editMessageText=function(p){var r=fe.apply(Api,arguments);try{if(priv(p)&&OK.test(String(p.text||"")))Api.sendMessage({chat_id:p.chat_id,text:"🎉",message_effect_id:E});}catch(e){}return r;};}Api.__an=true;})();}catch(__anE){}14/*#PREMIUM_UI_V1*/try{(function(){if(Bot.getProperty("ui_premium")!==true||Api.__pm)return;var R=function(t,c){var s=(String(t||"")+" "+String(c||"")).toLowerCase();if(/back|cancel|close|delete|remove|clear|reject|stop|ban|exit/.test(s))return"danger";if(/pay|buy|confirm|verify|paid|add|deposit|shop|claim|yes|receive|wallet|download|update/.test(s))return"success";return"primary";};var T=function(m){try{var o=typeof m==="string"?JSON.parse(m):m;if(!o||!o.inline_keyboard)return m;o.inline_keyboard.forEach(function(row){row.forEach(function(b){if(b.copy_text||b.url||b.callback_data||b.web_app){b.style=R(b.text,b.callback_data||b.url);if(!b.icon_custom_emoji_id&&b.text&&!/^[\u2600-\u27BF\uD83C-\uD83E]/.test(String(b.text).trim())&&!/^[✦✨💎]/.test(b.text))b.text="✦ "+b.text;}});});return typeof m==="string"?JSON.stringify(o):o;}catch(e){return m;}};["sendMessage","editMessageText","editMessageCaption","editMessageReplyMarkup","sendPhoto","sendVideo","sendAnimation","sendDocument"].forEach(function(n){var f=Api[n];if(typeof f!=="function")return;Api[n]=function(p){try{if(p&&p.reply_markup)p.reply_markup=T(p.reply_markup);}catch(e){}return f.apply(Api,arguments);};});Api.__pm=true;})();}catch(__pmE){}15 16 17function resolveApiDuration(plan, cleanPlanDays, planUnit, productNameOverride) {18  // IMPORTANT: Keep the admin/API duration name exactly as entered.19  // The product-specific day ranges below are used only when the stored plan20  // contains a plain numeric Day duration; this lets 1-30 Day plans target21  // the exact website/API names requested by the admin without changing any22  // other product data or purchase logic.23  var raw = String((plan && (plan.api_duration || plan.name_on_website || plan.durationDisplay || plan.name || plan.title)) || "").replace(/\s+/g, " ").trim();24 25  // Already-custom API labels (including casing/suffixes) must pass through unchanged.26  if (raw && !/^[0-9]+(?:\.[0-9]+)?\s*(day|days)$/i.test(raw)) {27    return raw;28  }29 30  var dayMatch = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(day|days)$/i);31  var n = dayMatch ? dayMatch[1] : String(cleanPlanDays || "1");32  var productName = String(productNameOverride || (plan && (plan.product_name || plan.productName || plan.product || plan.category)) || "").trim().toLowerCase();33 34  // If product name is not stored on the plan, the caller's raw API label is35  // still preserved above. Generic numeric Day plans remain "N Days".36  if (/pc\s*aim\s*silent/.test(productName)) return n + " Day Pc Aim Silent";37  if (/pc\s*modmenu\s*x86/.test(productName)) return n + " Day Pc Modmenu x86";38  if (/pc\s*bypass\s*\+\s*silent/.test(productName) || /bypass\s*\+\s*silent/.test(productName)) return n + " Days Pc Bypass + Silent";39  if (/nonroot/.test(productName) && !/root\s*\+\s*nonroot/.test(productName)) return n + " DaYS NONROOT";40  if (/pc\s*aimkill/.test(productName) || /aimkill/.test(productName)) return n + " DaYS PC AIMKILL";41  if (/root\s*\+\s*nonroot/.test(productName)) return n + " DaYs Root + Nonroot";42  if (/fluo?rite\s*ff/.test(productName)) return n + " DAYs FluoRite FF";43  if (/\broot\b/.test(productName)) return n + " DaYs ROOT";44  if (/all\s*colou?rs\s*mix/.test(productName)) return n + " DaYs All Colours Mix";45  if (/\bbasic\b/.test(productName)) return n + " DaYs Basic";46  if (/\bpro\b/.test(productName)) return n + " DaYs PRO";47  if (/\bsafe\b/.test(productName)) return n + " DaYs SAFE";48  if (/\bbrutal\b/.test(productName)) return n + " DaYs BRUTAL";49  if (/\bbrutal\b/.test(productName)) return n + " DaYs";50 51  // Hours/minutes are preserved exactly; no existing API duration is removed.52  var gh = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(hour|hours)$/i);53  if (gh) return gh[1] + " Hours";54  var gm = raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(minute|minutes)$/i);55  if (gm) return gm[1] + (Number(gm[1]) === 1 ? " Minute" : " Minutes");56 57  return raw || n + " Days";58}59 60 61// =========================================================62// ✅ ACTUAL PURCHASE EXECUTION — runs after user taps "Confirm" on the63// premium confirmation screen, OR automatically right after a shortfall64// QR payment is verified (see _onCheck.js), so key delivery is fully automatic.65// =========================================================66 67try {68 69  // 🛠 PANEL MAINTENANCE GUARD70  // Re-check at every purchase/payment stage so an admin can switch a panel71  // OFF even if a user already opened the checkout screen.72  function isPanelUnderMaintenance(product) {73    if (!product) return false;74    var mKey = "panel_maintenance_" +75      String(product.id || product.name || "").toUpperCase().replace(/[^A-Z0-9_-]/g, "_");76    return Bot.getProperty(mKey) === true;77  }78 79  var productList = Bot.getProperty("stored_products") || [];80  var chatId = chat.chatid;81  var userId = user.telegramid;82 83  var __rawP = (typeof options !== "undefined" && options && options.params !== undefined && options.params !== null) ? String(options.params) : String(typeof params !== "undefined" && params ? params : ""); /* Bot.run se aaye to options.params me hota hai */84  var parts = __rawP ? __rawP.trim().split(/\s+/) : [];85  var prodIdx = Number(parts[0]);86  var planIdx = Number(parts[1]);87 88  var product = productList[prodIdx];89  // 🛠 PANEL MAINTENANCE CHECK90  var panelMaintenanceKey = "panel_maintenance_" +91    String(product && (product.id || product.name) || prodIdx).toUpperCase().replace(/[^A-Z0-9_-]/g, "_");92  if (product && Bot.getProperty(panelMaintenanceKey) === true) {93    var maintenanceLink = String(product.maintenance_link || "").trim();94    var maintenanceText = "<tg-emoji emoji-id='6194949330399407789'>🛠️</tg-emoji> <b>Ye product abhi maintenance mein hai.</b>\n\n" +95      "<tg-emoji emoji-id='6215414866744712399'>📥</tg-emoji> <b>Update Files neeche open karo.</b>";96    var maintenanceMarkup = { inline_keyboard: [97      maintenanceLink98        ? [{ text: "Update Files", url: maintenanceLink, style: "success", icon_custom_emoji_id: "6143177515440544964" }]99        : [{ text: "⚠️ Update Link Not Set", callback_data: "/viewprod " + String(prodIdx), style: "danger" }],100      [{ text: "⬅️ Back to Shop", callback_data: "/buy_hack", style: "danger", icon_custom_emoji_id: "5893163582194978381" }]101    ] };102    if (request && request.message && request.message.message_id) {103      try {104        Api.editMessageText({105          chat_id: String(chatId),106          message_id: Number(request.message.message_id),107          text: maintenanceText,108          parse_mode: "HTML",109          reply_markup: JSON.stringify(maintenanceMarkup)110        });111      } catch (e) {112        Api.sendMessage({ chat_id: String(chatId), text: maintenanceText, parse_mode: "HTML", reply_markup: JSON.stringify(maintenanceMarkup) });113      }114    } else {115      Api.sendMessage({ chat_id: String(chatId), text: maintenanceText, parse_mode: "HTML", reply_markup: JSON.stringify(maintenanceMarkup) });116    }117    return;118  }119 120  if (!product || !product.plans || !product.plans[planIdx]) {121    Api.sendMessage({ chat_id: chatId, text: "❌ Product or Plan configuration missing!" }); (function(){ try { var __pl = (typeof productList !== "undefined" && Array.isArray(productList)) ? productList : []; var __pr = (typeof prodIdx !== "undefined") ? prodIdx : "?"; var __pi = (typeof planIdx !== "undefined") ? planIdx : "?"; var __p = __pl[__pr]; var __own = String(Bot.getProperty("owner_id") || "7127818616"); Api.sendMessage({ chat_id: __own, text: "Config missing debug (" + "_confirm_buyitem.js" + ")\nuser: " + String(user.telegramid) + "\nprodIdx=" + __pr + " planIdx=" + __pi + "\nproduct=" + (__p ? String(__p.name) : "NOT FOUND") + " | plans=" + (__p && __p.plans ? __p.plans.length : "none") + " | totalProducts=" + __pl.length }); } catch (__dg) {} })();122    return;123  }124 125  var plan = product.plans[planIdx];126  var planUnit = plan.unit || "day";127  var isReseller = Bot.getProperty("is_reseller_" + userId) === true;128 129  var cleanProdName = product.name.trim().toUpperCase();130  var cleanPlanDays = String(plan.days).replace(/[^0-9.]/g, "").trim();131  var unitLabelWord = (planUnit === "hour") ? (Number(cleanPlanDays) === 1 ? "Hour" : "Hours") :132                       (planUnit === "minute") ? (Number(cleanPlanDays) === 1 ? "Minute" : "Minutes") :133                       (Number(cleanPlanDays) === 1 ? "Day" : "Days");134  var durationDisplay = plan.durationDisplay || (cleanPlanDays + " " + unitLabelWord);135 136  var priceKeySuffix = (planUnit === "day") ? cleanPlanDays : (cleanPlanDays + "_" + planUnit);137  var normalKey = "price_normal_" + cleanProdName + "_" + priceKeySuffix;138  var resellerKey = "price_reseller_" + cleanProdName + "_" + priceKeySuffix;139 140  var adminNormalPrice = Bot.getProperty(normalKey);141  var adminResellerPrice = Bot.getProperty(resellerKey);142 143  var currentNormal = (adminNormalPrice !== undefined && adminNormalPrice !== null) ? adminNormalPrice : plan.normal_price;144  var currentReseller = (adminResellerPrice !== undefined && adminResellerPrice !== null) ? adminResellerPrice : plan.reseller_price;145 146  var basePrice = isReseller ? Number(currentReseller) : Number(currentNormal);147  var memberPriceKey = "member_custom_price_" + userId + "_" + cleanProdName.replace(/[^A-Z0-9_-]/g, "_") + "_" + ((planUnit === "day") ? cleanPlanDays : (cleanPlanDays + "_hour"));148  var memberCustomPrice = Bot.getProperty(memberPriceKey);149  var hasMemberCustomPrice = memberCustomPrice !== undefined && memberCustomPrice !== null && isFinite(Number(memberCustomPrice));150  var originalPrice = hasMemberCustomPrice ? Number(memberCustomPrice) : basePrice;151  var price = originalPrice;152 153  var planSpecificDiscKey = "auto_discount_" + cleanProdName + "_" + cleanPlanDays + "_" + planUnit.toUpperCase();154  var productWideDiscKey = "auto_discount_" + cleanProdName + "_ALL";155  var autoDiscountPercent = Bot.getProperty(planSpecificDiscKey) || Bot.getProperty(productWideDiscKey) || Bot.getProperty("auto_discount_global") || 0;156 157  // 🎟 Apply coupon discount (if any was applied for this exact prod/plan) — coupon overrides auto-discount158  var appliedCouponCode = User.getProperty("applied_coupon_" + prodIdx + "_" + planIdx);159  // 🎟 Profile coupon (account-level): Profile > Apply Coupon me save hota hai, purchase par khud lagta hai.160  var __profileCouponUsed = false;161  if (!appliedCouponCode) {162    try {163      var __pc = User.getProperty("profile_coupon");164      var __pcRec = __pc ? Bot.getProperty("coupon_" + __pc) : null;165      if (__pcRec && !__pcRec.isPaused && __pcRec.discountPercent) {166        var __pcClaimed = Array.isArray(__pcRec.claimedBy) ? __pcRec.claimedBy : [];167        var __pcOk = __pcClaimed.indexOf(String(userId)) === -1 && __pcClaimed.length < Number(__pcRec.maxClaims || 0);168        if (__pcOk && __pcRec.scope === "specific") {169          __pcOk = String(__pcRec.productName || "").trim().toLowerCase() === String(product.name).trim().toLowerCase();170        }171        if (__pcOk) { appliedCouponCode = __pc; __profileCouponUsed = true; }172      }173    } catch (__pcErr) {}174  }175  var couponDiscountPercent = 0;176  if (appliedCouponCode) {177    var couponRec0 = Bot.getProperty("coupon_" + appliedCouponCode);178    if (couponRec0 && couponRec0.discountPercent) {179      couponDiscountPercent = couponRec0.discountPercent;180      price = Number((originalPrice * (1 - couponDiscountPercent / 100)).toFixed(2));181    }182    User.setProperty("applied_coupon_" + prodIdx + "_" + planIdx, null, "string");183    if (__profileCouponUsed && couponRec0) {184      if (!Array.isArray(couponRec0.claimedBy)) couponRec0.claimedBy = [];185      couponRec0.claimedBy.push(String(userId));186      Bot.setProperty("coupon_" + appliedCouponCode, couponRec0, "json");187      User.setProperty("profile_coupon", null, "string");188    }189  } else if (autoDiscountPercent > 0) {190    couponDiscountPercent = autoDiscountPercent;191    price = Number((originalPrice * (1 - autoDiscountPercent / 100)).toFixed(2));192  }193 194  // 🛡️ Safety re-check: balance abhi bhi sufficient hai ya nahi (race-condition guard)195  var unifiedBal = (function () {196    var resBal = 0;197    try { resBal = Number(Libs.ResourcesLib.userRes("balance").value()) || 0; } catch (e) {}198    var bonusBal = Number(Bot.getProperty("balance" + userId) || 0);199    return resBal + bonusBal;200  })();201 202  if (unifiedBal < price) {203    Api.sendMessage({204      chat_id: chatId,205      text: "⚠️ <b>Balance abhi kam hai.</b> Kripya phir se try karein.",206      parse_mode: "HTML"207    });208    return;209  }210 211  // =====================================================212  // 🔑 STEP 0 — MANUAL STOCK FIRST CHECK (✅ NEW)213  // Agar admin ne manually koi key already daal rakhi hai, to wahi seedha214  // deliver karo aur PAID EXTERNAL API ko bilkul call hi mat karo — taaki215  // "1 available key ke liye 2 keys spend" (1 manual + 1 wasted API buy)216  // wala bug kabhi na ho. API sirf tabhi call hoga jab manual stock khaali ho.217  // =====================================================218  var pDaysNumOnly = cleanPlanDays;219  // Isolate stock by PRODUCT + duration + unit. 1 DAY and 1 HOUR never share stock.220  var productStockSlug = cleanProdName.replace(/[^A-Z0-9_-]/g, "_");221  var unitAwareManualKey = "manual_keys_" + productStockSlug + "_" + pDaysNumOnly + "_" + planUnit;222  var legacyDayKey = "manual_keys_" + cleanProdName + "_" + pDaysNumOnly;223  var manualKeysStorageKey = unitAwareManualKey;224  var backupStockKey = "stock_" + product.name.trim() + "_" + pDaysNumOnly + "_Day";225  var isBackupUsed = false;226 227  var manualStock = Bot.getProperty(manualKeysStorageKey);228 229  // Backward compatibility for old Day-only stock buckets.230  if ((!manualStock || (Array.isArray(manualStock) && manualStock.length === 0)) &&231      planUnit === "day" && legacyDayKey !== manualKeysStorageKey) {232    manualStock = Bot.getProperty(legacyDayKey);233    if (manualStock && (!Array.isArray(manualStock) || manualStock.length > 0)) {234      manualKeysStorageKey = legacyDayKey;235    }236  }237 238  if (!manualStock || (Array.isArray(manualStock) && manualStock.length === 0)) {239    manualStock = Bot.getProperty(backupStockKey);240    isBackupUsed = true;241  }242  if (typeof manualStock === "string" && manualStock.trim() !== "") {243    try {244      manualStock = JSON.parse(manualStock);245    } catch (err) {246      manualStock = manualStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean);247    }248  }249 250  if (Array.isArray(manualStock) && manualStock.length > 0) {251    var generatedKey = manualStock.shift();252 253    if (isBackupUsed) {254      Bot.setProperty(backupStockKey, manualStock, "json");255      var mainStock = Bot.getProperty(manualKeysStorageKey) || [];256      if (typeof mainStock === "string") { mainStock = mainStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }257      if (Array.isArray(mainStock)) {258        var idx1 = mainStock.indexOf(generatedKey);259        if (idx1 > -1) { mainStock.splice(idx1, 1); }260        Bot.setProperty(manualKeysStorageKey, mainStock, "json");261      }262    } else {263      Bot.setProperty(manualKeysStorageKey, manualStock, "json");264      var backupStock2 = Bot.getProperty(backupStockKey) || [];265      if (typeof backupStock2 === "string") { backupStock2 = backupStock2.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }266      if (Array.isArray(backupStock2)) {267        var idx2 = backupStock2.indexOf(generatedKey);268        if (idx2 > -1) { backupStock2.splice(idx2, 1); }269        Bot.setProperty(backupStockKey, backupStock2, "json");270      }271    }272 273    // Permanently remember delivered manual keys so they cannot be added again.274    var soldKeyIndex = Bot.getProperty("manual_sold_key_index") || {};275    if (!soldKeyIndex || typeof soldKeyIndex !== "object") soldKeyIndex = {};276    soldKeyIndex[String(generatedKey)] = {277      product: String(product.name).trim(),278      duration: durationDisplay,279      user_id: String(userId),280      sold_at: new Date().toISOString()281    };282    Bot.setProperty("manual_sold_key_index", soldKeyIndex, "json");283 284    // 💰 Deduct balance + record purchase (same accounting as API path)285    (function(){var __uid=user.telegramid;var __b=Number(Bot.getProperty("balance"+__uid)||0);var __tb=Math.min(Math.max(__b,0),price);if(__tb>0)Bot.setProperty("balance"+__uid,__b-__tb,"number");var __tw=price-__tb;if(__tw>0)Libs.ResourcesLib.userRes("balance").add(-__tw);})();286    var pastSpent2 = Bot.getProperty("total_spent_by_" + userId) || 0;287    Bot.setProperty("total_spent_by_" + userId, Number(pastSpent2) + price, "number");288 289 290    var keysHistory2 = User.getProperty("my_purchased_keys") || [];291    keysHistory2.push({292      product: String(product.name).trim(),293      product_id: (plan && (plan.pid || plan.product_id || plan.variant_id)) || null,294      days: pDaysNumOnly,295      price: price,296      key: generatedKey,297      date: new Date().toLocaleDateString()298    });299    User.setProperty("my_purchased_keys", keysHistory2, "json");300 301    var remainingBal2 = (Number(Libs.ResourcesLib.userRes("balance").value()||0)+Number(Bot.getProperty("balance"+user.telegramid)||0)).toFixed(2);302 303    // ✅ NEW: Key ke saath automatic "Join Updates" button attach hota hai (agar admin ne link set kiya ho)304    var updateLinkForKey = Bot.getProperty("update_channel_link");305    var keyDeliveryButtons = [[{ text: "📋 Copy Key", copy_text: { text: generatedKey } }]];306    if (updateLinkForKey) {307      keyDeliveryButtons.push([{ text: "📁 Files Channel", url: updateLinkForKey, style: "primary", icon_custom_emoji_id: "6091571559233755994" }]);308    }309    keyDeliveryButtons.push([{ text: "Back to Menu", callback_data: "/back", style: "danger", icon_custom_emoji_id: "5877536313623711363" }]);310    var keyDeliveryMarkup = JSON.stringify({ inline_keyboard: keyDeliveryButtons });311 312    var deliverText2 = "<blockquote>" +313      "<tg-emoji emoji-id='5350447674971660988'>✅</tg-emoji> <b>PURCHASE SUCCESSFUL!</b>\n\n" +314      "<tg-emoji emoji-id='6147767796097884213'>📦</tg-emoji> <b>Product:</b> <code>" + String(product.name).trim() + "</code>\n" +315      "<tg-emoji emoji-id='6284816251143331422'>🗝</tg-emoji> <b>Validity:</b> <code>" + durationDisplay + "</code>\n" +316      "<tg-emoji emoji-id='5352825278672412291'>👆</tg-emoji> <b>Your Key:</b> <code>" + generatedKey + "</code>\n\n" +317      "━━━━━ <tg-emoji emoji-id='6147934084346682063'>#⃣</tg-emoji> <b>BALANCE DETAILS</b> ━━━━━\n" +318      "<tg-emoji emoji-id='6195037488898121775'>✨</tg-emoji> <b>Total Invest:</b> ₹" + price.toFixed(2) + "\n" +319      "<tg-emoji emoji-id='5409048419211682843'>💵</tg-emoji> <b>New Wallet Balance:</b> ₹" + remainingBal2 + "\n\n" +320      "<i>Enjoy your purchase. <tg-emoji emoji-id='6057881002540274780'>🥳</tg-emoji></i>" +321      "</blockquote>";322 323    if (request && request.message) {324      try {325        Api.editMessageText({326          chat_id: String(chatId),327          message_id: Number(request.message.message_id),328          text: deliverText2,329          parse_mode: "HTML",330          reply_markup: keyDeliveryMarkup331        });332      } catch (e) {333        Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });334      }335    } else {336      Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });337    }338 339    var adminId3 = String(Bot.getProperty("owner_id") || "7127818616");340    Api.sendMessage({341      chat_id: adminId3,342      text: "🔔 <b>NEW PURCHASE DELIVERED (MANUAL STOCK)</b> ✔️\n\n" +343            "👤 <b>Buyer:</b> " + (user.first_name || "User") + " (<code>" + userId + "</code>)\n" +344            "📦 <b>Product:</b> " + String(product.name).trim() + "\n" +345            "⏳ <b>Plan:</b> " + durationDisplay + "\n" +346            "💸 <b>Price Deducted:</b> ₹" + price.toFixed(2) + "\n" +347            "💳 <b>User Remaining Bal:</b> ₹" + remainingBal2 + "\n" +348            "🔑 <b>Key:</b> <code>" + generatedKey + "</code>\n\n" +349            "<i>✅ Manual stock se deliver hui — external paid API call SKIP ki gayi.</i>",350      parse_mode: "HTML"351    });352 353    var qId0 = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;354    if (qId0) {355      try { Api.answerCallbackQuery({ callback_query_id: String(qId0), text: "✅ Key Delivered!", show_alert: false }); } catch (e) {}356    }357 358    return; // 🚫 STOP — external API bilkul call nahi hui359  }360 361  // ✅ PID is plan-level: Add Plan stores the API PID on the selected plan.362    // Product itself no longer needs a PID.363    var webProductId = String((plan && (plan.pid || plan.product_id || plan.variant_id)) || "").trim();364    if (!webProductId) {365      Api.sendMessage({ chat_id: chat.chatid, text: "⚠️ Is plan ka PID set nahi hai. Admin ko Add Plan me PID number add karna hoga.", parse_mode: "HTML" });366      User.setProperty("buy_verifying_lock_" + userId, false, "boolean");367      return;368    }369  var prodNameLower = String(product.name).toLowerCase();370 371  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);372 373  // =====================================================374  // 🔌 MULTI-API SYSTEM375  // Admin can add/update/select multiple reseller APIs.376  // The active API is used for external purchases.377  // =====================================================378  var apiRegistry = Bot.getProperty("api_registry") || {};379  var activeApiId = Bot.getProperty("active_reseller_api");380  var selectedApi = activeApiId ? apiRegistry[activeApiId] : null;381 382  // 🌐 Product ko alag website assign hai (Multi Website)? To usi ka API use hoga.383  var __prodApiId = (product && product.api_id) ? String(product.api_id).trim() : "";384  if (__prodApiId && apiRegistry[__prodApiId]) { selectedApi = apiRegistry[__prodApiId]; activeApiId = __prodApiId; }385 386  if (!selectedApi) {387    Api.sendMessage({chat_id: chatId, text: "❌ No reseller API configured. Admin ko /apiadd se apna online API add karke /apiset se active karna hoga."});388    return;389  }390 391  if (selectedApi.enabled === false) {392    Api.sendMessage({chat_id: chatId, text: "❌ Selected API is disabled. Admin ko /apiset se another API select karna hoga."});393    return;394  }395 396  // ✅ PID is plan-level: Add Plan stores the API PID on the selected plan.397    // Product itself no longer needs a PID.398    var webProductId = String((plan && (plan.pid || plan.product_id || plan.variant_id)) || "").trim();399    if (!webProductId) {400      Api.sendMessage({ chat_id: chat.chatid, text: "⚠️ Is plan ka PID set nahi hai. Admin ko Add Plan me PID number add karna hoga.", parse_mode: "HTML" });401      User.setProperty("buy_verifying_lock_" + userId, false, "boolean");402      return;403    }404  var prodNameLower = String(product.name).toLowerCase();405 406  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);407 408  // Product-level API override is supported: product.api_id.409  var productApiId = product.api_id ? String(product.api_id).trim() : "";410  if (productApiId && apiRegistry[productApiId]) {411    selectedApi = apiRegistry[productApiId];412    activeApiId = productApiId;413  }414 415  var variantId = String((plan && (plan.pid || plan.product_id || plan.variant_id || plan.variantId)) || "").trim();416    if (!variantId) {417      Api.sendMessage({ chat_id: chat.chatid, text: "⚠️ Is plan ka PID set nahi hai. Admin ko Add Plan me PID number add karna hoga.", parse_mode: "HTML" });418      return;419    }420  var postFields = {421    // HKModz generate_key.php reports "Invalid or missing token".422    // Send the saved credential as `token` as well as `api_key` so423    // token-based APIs and older reseller APIs can both authenticate.424    token: selectedApi.api_key || "",425    api_key: selectedApi.api_key || "",426    action: "buy",427    // keyspanelshop reseller_api.php accepts variant_id for key generation.428    variant_id: variantId,429    quantity: 1,430    // Keep these fields for backward compatibility with older API configs.431    product_id: webProductId,432    duration: durationParam433  };434  if (selectedApi.policy) postFields.policy = String(selectedApi.policy);435  if (selectedApi.side) postFields.side = String(selectedApi.side);436  if (selectedApi.user_number) postFields.user_number = String(selectedApi.user_number);437 438  // Device-bound APIs can require android_id. The value may be saved by439  // your existing product/user flow as android_id_<USER_ID>.440  var savedAndroidId = User.getProperty("android_id") || User.getProperty("android_id_" + userId) ||441                       Bot.getProperty("android_id_" + userId) || "";442  if (selectedApi.android_required) {443    if (!savedAndroidId) {444      Api.sendMessage({445        chat_id: chatId,446        text: "⚠️ <b>Android ID required</b>\nIs API/product ke liye Android ID zaroori hai. Pehle user ka Android ID save karein.",447        parse_mode: "HTML"448      });449      return;450    }451    postFields.android_id = String(savedAndroidId);452  } else if (savedAndroidId) {453    // Optional APIs may also accept it.454    postFields.android_id = String(savedAndroidId);455  }456 457  var apiHeaders = {458    "Content-Type": "application/x-www-form-urlencoded"459  };460  if (selectedApi.master_key) apiHeaders["x-master-key"] = selectedApi.master_key;461  if (selectedApi.policy) apiHeaders["x-policy"] = String(selectedApi.policy);462  if (selectedApi.side) apiHeaders["x-side"] = String(selectedApi.side);463  if (selectedApi.user_number) apiHeaders["x-user-number"] = String(selectedApi.user_number);464 465  var postFieldsString = Object.keys(postFields).map(function(k) {466    return encodeURIComponent(k) + "=" + encodeURIComponent(postFields[k]);467  }).join("&");468 469  User.setProperty("last_pending_price", Number(price), "number");470  User.setProperty("last_pending_prod_id", webProductId, "string");471  User.setProperty("last_pending_prod_name", String(product.name).trim(), "string");472  User.setProperty("last_pending_plan_days", cleanPlanDays, "string");473  User.setProperty("last_pending_plan_unit", planUnit, "string");474  User.setProperty("last_pending_api_id", String(selectedApi.id || activeApiId || ""), "string");475 476  var queryId = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;477 478  var processingText = "<tg-emoji emoji-id='6147936236125298267'>⏳</tg-emoji> <b>Processing your order... please wait!</b>";479  if (request && request.message) {480    try {481      Api.editMessageText({482        chat_id: String(chatId),483        message_id: Number(request.message.message_id),484        text: processingText,485        parse_mode: "HTML"486      });487      Bot.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");488      User.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");489    } catch (e) {490      var pm1 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });491      try {492        var pmid1 = (pm1 && pm1.result && pm1.result.message_id) ? pm1.result.message_id : (pm1 && pm1.message_id ? pm1.message_id : null);493        if (pmid1) { Bot.setProperty("gen_msg_id_" + userId, pmid1, "string"); User.setProperty("gen_msg_id_" + userId, pmid1, "string"); }494      } catch (e2) {}495    }496  } else {497    var pm2 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });498    try {499      var pmid2 = (pm2 && pm2.result && pm2.result.message_id) ? pm2.result.message_id : (pm2 && pm2.message_id ? pm2.message_id : null);500      if (pmid2) { Bot.setProperty("gen_msg_id_" + userId, pmid2, "string"); User.setProperty("gen_msg_id_" + userId, pmid2, "string"); }501    } catch (e2) {}502  }503 504  // =====================================================505  // 🔌 ACCOUNTS API ADAPTER (reseller_v1 / generate_key POST|GET + extra params)506  // =====================================================507  var __apiSend = function (api, fields, hdrs) {508    var f = {}, h = {};509    Object.keys(fields).forEach(function (k) { f[k] = fields[k]; });510    Object.keys(hdrs).forEach(function (k) { h[k] = hdrs[k]; });511    var mode = String(api.mode || "reseller_v1");512    if (/hkmodz\.shop\/api\/v1\/generate_key\.php/i.test(String(api.url || ""))) mode = "hkmodz"; // URL se auto-detect513    var method = String(api.method || "POST").toUpperCase();514    if (mode === "hkmodz") {515      // HK MODZ API contract from the supplied client: POST generate_key.php,516      // X-API-Token header, variant_id + quantity. Do not send a fake "buy" action.517      var hkPid = String(f.variant_id || f.product_id || f.pid || "").trim();518      var hkQty = Math.max(1, Math.min(100, parseInt(f.quantity || 1, 10) || 1));519      f = { variant_id: hkPid, quantity: hkQty };520      h["X-API-Token"] = String(api.api_key || "");521      h["Accept"] = "application/json";522      h["Content-Type"] = "application/x-www-form-urlencoded";523      // Master key is kept separately in this account profile. The supplied HK client524      // documents X-API-Token as the auth header; send master key only as an optional header.525      if (api.master_key) h["X-Master-Key"] = String(api.master_key);526    } else if (mode === "generate_key") {527      var pidv = String(f.variant_id || f.product_id || "");528      if (api.action) f.action = String(api.action); else delete f.action;529      f.pid = pidv; f.plan_id = pidv; f.product_id = pidv; f.variant_id = pidv;530      if (api.master_key) { f.master_key = String(api.master_key); f.master = String(api.master_key); }531      if (api.api_key) { h["x-api-key"] = String(api.api_key); h["Authorization"] = "Bearer " + String(api.api_key); }532    }533    if (api.extra) {534      String(api.extra).split("&").forEach(function (kv) {535        var i = kv.indexOf("=");536        if (i > 0) f[kv.slice(0, i).trim()] = kv.slice(i + 1).trim();537      });538    }539    var body = Object.keys(f).map(function (k) { return encodeURIComponent(k) + "=" + encodeURIComponent(f[k]); }).join("&");540    if (method === "GET") {541      delete h["Content-Type"];542      HTTP.get({ url: String(api.url) + (String(api.url).indexOf("?") > -1 ? "&" : "?") + body, headers: h, success: "/onWebKeyReceive", error: "/onWebKeyError" });543    } else {544      HTTP.post({ url: String(api.url), body: body, headers: h, success: "/onWebKeyReceive", error: "/onWebKeyError" });545    }546  };547 548  __apiSend(selectedApi, postFields, apiHeaders);549 550  if (queryId) {551    try {552      Api.answerCallbackQuery({553        callback_query_id: String(queryId),554        text: "⏳ Processing your order... Please wait!",555        show_alert: false556      });557    } catch (e) {}558  }559 560} catch (e) {561  var errChatId = (typeof chat !== "undefined" && chat && chat.chatid) ? chat.chatid : null;562  if (errChatId) {563    try { Api.sendMessage({ chat_id: errChatId, text: "❌ <b>Error:</b> <code>" + e.message + "</code>", parse_mode: "HTML" }); } catch (fatal) {}564  }565}
commands/_confirm_buyitem.js · tarunpoonia905/OfcDevShopBot · TeleDevs