indrajitghosh20006/Testseller_01botPublic · Bot Template

ProfileTelegram

AIThis bot provides an admin‑controlled system for managing user balances, products, and reseller roles. It includes commands to add balances (via manual input, Binance Pay, or UPI), approve and credit payments, and set up product listings with categories and pricing. Admins can also add resellers, view reseller info, and manage stock entries. The bot uses built‑in TeleBotHost APIs such as Bot, Api, HTTP, and Libs for database operations and message handling.

Commerceadminbalanceproductresellerpaymentsbinance
138 commands0 envUpdated 17h agoCreated Sep 27, 2026
Back to folder

commands/_confirm_buyitem.js

javascript · 423 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  var plan = product.plans[planIdx];78  var planUnit = plan.unit || "day";79  var isReseller = Bot.getProperty("is_reseller_" + userId) === true;80 81  var cleanProdName = product.name.trim().toUpperCase();82  var cleanPlanDays = String(plan.days).replace(/[^0-9.]/g, "").trim();83  var unitLabelWord = (planUnit === "hour") ? (Number(cleanPlanDays) === 1 ? "Hour" : "Hours") :84                       (planUnit === "minute") ? (Number(cleanPlanDays) === 1 ? "Minute" : "Minutes") :85                       (Number(cleanPlanDays) === 1 ? "Day" : "Days");86  var durationDisplay = plan.durationDisplay || (cleanPlanDays + " " + unitLabelWord);87 88  var priceKeySuffix = (planUnit === "day") ? cleanPlanDays : (cleanPlanDays + "_" + planUnit);89  var normalKey = "price_normal_" + cleanProdName + "_" + priceKeySuffix;90  var resellerKey = "price_reseller_" + cleanProdName + "_" + priceKeySuffix;91 92  var adminNormalPrice = Bot.getProperty(normalKey);93  var adminResellerPrice = Bot.getProperty(resellerKey);94 95  var currentNormal = (adminNormalPrice !== undefined && adminNormalPrice !== null) ? adminNormalPrice : plan.normal_price;96  var currentReseller = (adminResellerPrice !== undefined && adminResellerPrice !== null) ? adminResellerPrice : plan.reseller_price;97 98  var originalPrice = isReseller ? Number(currentReseller) : Number(currentNormal);99  var price = originalPrice;100 101  var planSpecificDiscKey = "auto_discount_" + cleanProdName + "_" + cleanPlanDays + "_" + planUnit.toUpperCase();102  var productWideDiscKey = "auto_discount_" + cleanProdName + "_ALL";103  var autoDiscountPercent = Bot.getProperty(planSpecificDiscKey) || Bot.getProperty(productWideDiscKey) || Bot.getProperty("auto_discount_global") || 0;104 105  // 🎟 Apply coupon discount (if any was applied for this exact prod/plan) — coupon overrides auto-discount106  var appliedCouponCode = User.getProperty("applied_coupon_" + prodIdx + "_" + planIdx);107  var couponDiscountPercent = 0;108  if (appliedCouponCode) {109    var couponRec0 = Bot.getProperty("coupon_" + appliedCouponCode);110    if (couponRec0 && couponRec0.discountPercent) {111      couponDiscountPercent = couponRec0.discountPercent;112      price = Number((originalPrice * (1 - couponDiscountPercent / 100)).toFixed(2));113    }114    User.setProperty("applied_coupon_" + prodIdx + "_" + planIdx, null, "string");115  } else if (autoDiscountPercent > 0) {116    couponDiscountPercent = autoDiscountPercent;117    price = Number((originalPrice * (1 - autoDiscountPercent / 100)).toFixed(2));118  }119 120  // 🛡️ Safety re-check: balance abhi bhi sufficient hai ya nahi (race-condition guard)121  var unifiedBal = (function () {122    var resBal = 0;123    try { resBal = Number(Libs.ResourcesLib.userRes("balance").value()) || 0; } catch (e) {}124    return resBal;125  })();126 127  if (unifiedBal < price) {128    Api.sendMessage({129      chat_id: chatId,130      text: "⚠️ <b>Balance abhi kam hai.</b> Kripya phir se try karein.",131      parse_mode: "HTML"132    });133    return;134  }135 136  // =====================================================137  // 🔑 STEP 0 — MANUAL STOCK FIRST CHECK (✅ NEW)138  // Agar admin ne manually koi key already daal rakhi hai, to wahi seedha139  // deliver karo aur PAID EXTERNAL API ko bilkul call hi mat karo — taaki140  // "1 available key ke liye 2 keys spend" (1 manual + 1 wasted API buy)141  // wala bug kabhi na ho. API sirf tabhi call hoga jab manual stock khaali ho.142  // =====================================================143  var pDaysNumOnly = cleanPlanDays;144  var manualKeysStorageKey = "manual_keys_" + cleanProdName + "_" + pDaysNumOnly;145  var backupStockKey = "stock_" + product.name.trim() + "_" + pDaysNumOnly + "_Day";146  var isBackupUsed = false;147 148  var manualStock = Bot.getProperty(manualKeysStorageKey);149  if (!manualStock || (Array.isArray(manualStock) && manualStock.length === 0)) {150    manualStock = Bot.getProperty(backupStockKey);151    isBackupUsed = true;152  }153  if (typeof manualStock === "string" && manualStock.trim() !== "") {154    try {155      manualStock = JSON.parse(manualStock);156    } catch (err) {157      manualStock = manualStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean);158    }159  }160 161  if (Array.isArray(manualStock) && manualStock.length > 0) {162    var generatedKey = manualStock.shift();163 164    if (isBackupUsed) {165      Bot.setProperty(backupStockKey, manualStock, "json");166      var mainStock = Bot.getProperty(manualKeysStorageKey) || [];167      if (typeof mainStock === "string") { mainStock = mainStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }168      if (Array.isArray(mainStock)) {169        var idx1 = mainStock.indexOf(generatedKey);170        if (idx1 > -1) { mainStock.splice(idx1, 1); }171        Bot.setProperty(manualKeysStorageKey, mainStock, "json");172      }173    } else {174      Bot.setProperty(manualKeysStorageKey, manualStock, "json");175      var backupStock2 = Bot.getProperty(backupStockKey) || [];176      if (typeof backupStock2 === "string") { backupStock2 = backupStock2.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }177      if (Array.isArray(backupStock2)) {178        var idx2 = backupStock2.indexOf(generatedKey);179        if (idx2 > -1) { backupStock2.splice(idx2, 1); }180        Bot.setProperty(backupStockKey, backupStock2, "json");181      }182    }183 184    // 💰 Deduct balance + record purchase (same accounting as API path)185    Libs.ResourcesLib.userRes("balance").add(-price);186    var pastSpent2 = Bot.getProperty("total_spent_by_" + userId) || 0;187    Bot.setProperty("total_spent_by_" + userId, Number(pastSpent2) + price, "number");188 189    // 🎁 REFER & EARN — referrer bonus jab referred friend PEHLI baar purchase kare190    try {191      var referredByBuy2 = User.getProperty("referred_by");192      if (referredByBuy2 && !User.getProperty("ref_buy_bonus_given")) {193        var refBonusBuy2 = Number(Bot.getProperty("refer_earn_buy_amount"));194        if (isNaN(refBonusBuy2)) refBonusBuy2 = 2;195        if (refBonusBuy2 > 0) {196          Libs.ResourcesLib.anotherUserRes("balance", referredByBuy2).add(refBonusBuy2);197          var pastEarnBuy2 = Number(Bot.getProperty("refer_earnings_" + referredByBuy2) || 0);198          Bot.setProperty("refer_earnings_" + referredByBuy2, pastEarnBuy2 + refBonusBuy2, "number");199          try {200            Api.sendMessage({201              chat_id: referredByBuy2,202              text: "<blockquote>💸 <b>Referral Bonus!</b>\n\nAapke referred friend ne pehli purchase ki — aapko ₹" + refBonusBuy2.toFixed(2) + " mile hain!</blockquote>",203              parse_mode: "HTML"204            });205          } catch (e) {}206        }207        User.setProperty("ref_buy_bonus_given", true, "boolean");208      }209    } catch (e) {}210 211    var keysHistory2 = User.getProperty("my_purchased_keys") || [];212    keysHistory2.push({213      product: String(product.name).trim(),214      product_id: product.id || null,215      days: pDaysNumOnly,216      price: price,217      key: generatedKey,218      date: new Date().toLocaleDateString()219    });220    User.setProperty("my_purchased_keys", keysHistory2, "json");221 222    var remainingBal2 = Libs.ResourcesLib.userRes("balance").value().toFixed(2);223 224    // ✅ NEW: Key ke saath automatic "Join Updates" button attach hota hai (agar admin ne link set kiya ho)225    var updateLinkForKey = Bot.getProperty("update_channel_link");226    var keyDeliveryButtons = [[{ text: "📋 Copy Key", copy_text: { text: generatedKey } }]];227    if (updateLinkForKey) {228      keyDeliveryButtons.push([{ text: "📥 Join Updates", url: updateLinkForKey, style: "primary", icon_custom_emoji_id: "6091571559233755994" }]);229    }230    keyDeliveryButtons.push([{ text: "Back to Menu", callback_data: "/back", style: "primary", icon_custom_emoji_id: "5877536313623711363" }]);231    var keyDeliveryMarkup = JSON.stringify({ inline_keyboard: keyDeliveryButtons });232 233    var deliverText2 = "<blockquote>" +234      "<tg-emoji emoji-id='5350447674971660988'>✅</tg-emoji> <b>PURCHASE SUCCESSFUL!</b>\n\n" +235      "<tg-emoji emoji-id='6147767796097884213'>📦</tg-emoji> <b>Product:</b> <code>" + String(product.name).trim() + "</code>\n" +236      "<tg-emoji emoji-id='6284816251143331422'>🗝</tg-emoji> <b>Validity:</b> <code>" + durationDisplay + "</code>\n" +237      "<tg-emoji emoji-id='5352825278672412291'>👆</tg-emoji> <b>Your Key:</b> <code>" + generatedKey + "</code>\n\n" +238      "━━━━━ <tg-emoji emoji-id='6147934084346682063'>#⃣</tg-emoji> <b>BALANCE DETAILS</b> ━━━━━\n" +239      "<tg-emoji emoji-id='6195037488898121775'>✨</tg-emoji> <b>Total Invest:</b> ₹" + price.toFixed(2) + "\n" +240      "<tg-emoji emoji-id='5409048419211682843'>💵</tg-emoji> <b>New Wallet Balance:</b> ₹" + remainingBal2 + "\n\n" +241      "<i>Enjoy your purchase. <tg-emoji emoji-id='6057881002540274780'>🥳</tg-emoji></i>" +242      "</blockquote>";243 244    if (request && request.message) {245      try {246        Api.editMessageText({247          chat_id: String(chatId),248          message_id: Number(request.message.message_id),249          text: deliverText2,250          parse_mode: "HTML",251          reply_markup: keyDeliveryMarkup252        });253      } catch (e) {254        Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });255      }256    } else {257      Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });258    }259 260    var adminId3 = "8875810358";261    Api.sendMessage({262      chat_id: adminId3,263      text: "🔔 <b>NEW PURCHASE DELIVERED (MANUAL STOCK)</b> ✔️\n\n" +264            "👤 <b>Buyer:</b> " + (user.first_name || "User") + " (<code>" + userId + "</code>)\n" +265            "📦 <b>Product:</b> " + String(product.name).trim() + "\n" +266            "⏳ <b>Plan:</b> " + durationDisplay + "\n" +267            "💸 <b>Price Deducted:</b> ₹" + price.toFixed(2) + "\n" +268            "💳 <b>User Remaining Bal:</b> ₹" + remainingBal2 + "\n" +269            "🔑 <b>Key:</b> <code>" + generatedKey + "</code>\n\n" +270            "<i>✅ Manual stock se deliver hui — external paid API call SKIP ki gayi.</i>",271      parse_mode: "HTML"272    });273 274    var qId0 = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;275    if (qId0) {276      try { Api.answerCallbackQuery({ callback_query_id: String(qId0), text: "✅ Key Delivered!", show_alert: false }); } catch (e) {}277    }278 279    return; // 🚫 STOP — external API bilkul call nahi hui280  }281 282  var webProductId = String(product.id || "PID_ID").trim();283  var prodNameLower = String(product.name).toLowerCase();284 285  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);286 287  // =====================================================288  // 🔌 MULTI-API SYSTEM289  // Admin can add/update/select multiple reseller APIs.290  // The active API is used for external purchases.291  // =====================================================292  var apiRegistry = Bot.getProperty("api_registry") || {};293  var activeApiId = Bot.getProperty("active_reseller_api");294  var selectedApi = activeApiId ? apiRegistry[activeApiId] : null;295 296  // Backward-compatible bootstrap from the existing API configuration.297  if (!selectedApi) {298    selectedApi = {299      id: "default",300      name: "Default Reseller API",301      url: "https://bantibhaiya.to/api/reseller_v1.php",302      api_key: "1a46ed123070dbe5d2e2366f98e4e34a",303      master_key: "a7f3e8b2c9d1f4a6b8c2d5e9f1a3b6c8",304      mode: "reseller_v1",305      android_required: false,306      enabled: true307    };308    apiRegistry.default = selectedApi;309    Bot.setProperty("api_registry", apiRegistry, "json");310    Bot.setProperty("active_reseller_api", "default", "string");311  }312 313  if (selectedApi.enabled === false) {314    Api.sendMessage({chat_id: chatId, text: "❌ Selected API is disabled. Admin ko /apiset se another API select karna hoga."});315    return;316  }317 318  var webProductId = String(product.id || "PID_ID").trim();319  var prodNameLower = String(product.name).toLowerCase();320 321  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit, product && product.name);322 323  // Product-level API override is supported: product.api_id.324  var productApiId = product.api_id ? String(product.api_id).trim() : "";325  if (productApiId && apiRegistry[productApiId] && apiRegistry[productApiId].enabled !== false) {326    selectedApi = apiRegistry[productApiId];327    activeApiId = productApiId;328  }329 330  var postFields = {331    api_key: selectedApi.api_key || "",332    action: "buy",333    product_id: webProductId,334    duration: durationParam335  };336 337  // Device-bound APIs can require android_id. The value may be saved by338  // your existing product/user flow as android_id_<USER_ID>.339  var savedAndroidId = User.getProperty("android_id") || User.getProperty("android_id_" + userId) ||340                       Bot.getProperty("android_id_" + userId) || "";341  if (selectedApi.android_required) {342    if (!savedAndroidId) {343      Api.sendMessage({344        chat_id: chatId,345        text: "⚠️ <b>Android ID required</b>\nIs API/product ke liye Android ID zaroori hai. Pehle user ka Android ID save karein.",346        parse_mode: "HTML"347      });348      return;349    }350    postFields.android_id = String(savedAndroidId);351  } else if (savedAndroidId) {352    // Optional APIs may also accept it.353    postFields.android_id = String(savedAndroidId);354  }355 356  var apiHeaders = {357    "Content-Type": "application/x-www-form-urlencoded"358  };359  if (selectedApi.master_key) apiHeaders["x-master-key"] = selectedApi.master_key;360 361  var postFieldsString = Object.keys(postFields).map(function(k) {362    return encodeURIComponent(k) + "=" + encodeURIComponent(postFields[k]);363  }).join("&");364 365  User.setProperty("last_pending_price", Number(price), "number");366  User.setProperty("last_pending_prod_id", webProductId, "string");367  User.setProperty("last_pending_prod_name", String(product.name).trim(), "string");368  User.setProperty("last_pending_plan_days", cleanPlanDays, "string");369  User.setProperty("last_pending_plan_unit", planUnit, "string");370  User.setProperty("last_pending_api_id", String(selectedApi.id || activeApiId || ""), "string");371 372  var queryId = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;373 374  var processingText = "<tg-emoji emoji-id='6147936236125298267'>⏳</tg-emoji> <b>Processing your order... please wait!</b>";375  if (request && request.message) {376    try {377      Api.editMessageText({378        chat_id: String(chatId),379        message_id: Number(request.message.message_id),380        text: processingText,381        parse_mode: "HTML"382      });383      Bot.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");384      User.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");385    } catch (e) {386      var pm1 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });387      try {388        var pmid1 = (pm1 && pm1.result && pm1.result.message_id) ? pm1.result.message_id : (pm1 && pm1.message_id ? pm1.message_id : null);389        if (pmid1) { Bot.setProperty("gen_msg_id_" + userId, pmid1, "string"); User.setProperty("gen_msg_id_" + userId, pmid1, "string"); }390      } catch (e2) {}391    }392  } else {393    var pm2 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });394    try {395      var pmid2 = (pm2 && pm2.result && pm2.result.message_id) ? pm2.result.message_id : (pm2 && pm2.message_id ? pm2.message_id : null);396      if (pmid2) { Bot.setProperty("gen_msg_id_" + userId, pmid2, "string"); User.setProperty("gen_msg_id_" + userId, pmid2, "string"); }397    } catch (e2) {}398  }399 400  HTTP.post({401    url: selectedApi.url,402    body: postFieldsString,403    headers: apiHeaders,404    success: "/onWebKeyReceive",405    error: "/onWebKeyError"406  });407 408  if (queryId) {409    try {410      Api.answerCallbackQuery({411        callback_query_id: String(queryId),412        text: "⏳ Processing your order... Please wait!",413        show_alert: false414      });415    } catch (e) {}416  }417 418} catch (e) {419  var errChatId = (typeof chat !== "undefined" && chat && chat.chatid) ? chat.chatid : null;420  if (errChatId) {421    try { Api.sendMessage({ chat_id: errChatId, text: "❌ <b>Error:</b> <code>" + e.message + "</code>", parse_mode: "HTML" }); } catch (fatal) {}422  }423}