devbro468/Dev_x_store_botPublic · Bot Template

AIThis bot operates a digital storefront (DEV X STORE) selling subscription-based products — likely game hacks, accounts, or access keys — categorized by platform (Android Root, Non-Root, iPhone). It features a reseller program with discounted pricing, a dual-currency wallet (INR balance + Telegram Stars/XTR), and a full admin/co-admin panel for managing products, plans, per-plan pricing, stock (keys/accounts), coupons, and API endpoints. Purchases flow through a plan selection screen offering wallet payment, Stars invoices (sendInvoice with XTR currency), and coupon application. Successful Stars payments are handled via successful_payment webhook, crediting wallet or delivering keys directly. Admin tools include user listing with chunked messaging, stock viewing/deletion with inline keyboards, co-admin management, and API registry updates. The bot registers users on first interaction and

Commercecommercedigital-goodsstars-paymentreseller-systemadmin-panelstock-management
ProfileTelegram
119 commands0 envUpdated 15d agoCreated Aug 23, 2026
Back to folder

commands/_confirm_buyitem.js

javascript · 375 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) {13  var raw = String((plan && (plan.api_duration || plan.name_on_website || plan.durationDisplay || plan.name || plan.title)) || "").replace(/\s+/g, " ").trim();14  var canonical = ["1 DaYS NONROOT","House","1 DaYs","15 DaYs All Colours Mix","30 DaYs PRO","1 DaYs Basic","1 Year Ios Esign Gbox Certificate","1 DaYs Root + Nonroot","7 DaYS PC AIMKILL","30 Day Pc Modmenu x86","3 DaYs SAFE","3 DaYs BRUTAL"];15  for (var i=0;i<canonical.length;i++) if (raw.toLowerCase() === canonical[i].toLowerCase()) return canonical[i];16  var gd=raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(day|days)$/i);17  if(gd) return Number(gd[1])===1 ? "1 DaYs" : gd[1]+" Days";18  // HOURS: preserve the exact duration entered by admin/API.19  // Examples: "1 Hours", "2 Hours", "12 Hours" all remain unchanged.20  var gh=raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(hour|hours)$/i);21  if(gh) return raw;22  var gm=raw.match(/^([0-9]+(?:\.[0-9]+)?)\s*(minute|minutes)$/i);23  if(gm) return gm[1]+(Number(gm[1])===1?" Minute":" Minutes");24  return raw || (Number(cleanPlanDays||"1")===1 ? "1 DaYs" : String(cleanPlanDays)+" Days");25}26 27 28// =========================================================29// ✅ ACTUAL PURCHASE EXECUTION — runs after user taps "Confirm" on the30// premium confirmation screen, OR automatically right after a shortfall31// QR payment is verified (see _onCheck.js), so key delivery is fully automatic.32// =========================================================33 34try {35  var productList = Bot.getProperty("stored_products") || [];36  var chatId = chat.chatid;37  var userId = user.telegramid;38 39  var parts = params ? String(params).trim().split(" ") : [];40  var prodIdx = Number(parts[0]);41  var planIdx = Number(parts[1]);42 43  var product = productList[prodIdx];44  if (!product || !product.plans || !product.plans[planIdx]) {45    Api.sendMessage({ chat_id: chatId, text: "❌ Product or Plan configuration missing!" });46    return;47  }48 49  var plan = product.plans[planIdx];50  var planUnit = plan.unit || "day";51  var isReseller = Bot.getProperty("is_reseller_" + userId) === true;52 53  var cleanProdName = product.name.trim().toUpperCase();54  var cleanPlanDays = String(plan.days).replace(/[^0-9.]/g, "").trim();55  var unitLabelWord = (planUnit === "hour") ? (Number(cleanPlanDays) === 1 ? "Hour" : "Hours") :56                       (planUnit === "minute") ? (Number(cleanPlanDays) === 1 ? "Minute" : "Minutes") :57                       (Number(cleanPlanDays) === 1 ? "Day" : "Days");58  var durationDisplay = plan.durationDisplay || (cleanPlanDays + " " + unitLabelWord);59 60  var priceKeySuffix = (planUnit === "day") ? cleanPlanDays : (cleanPlanDays + "_" + planUnit);61  var normalKey = "price_normal_" + cleanProdName + "_" + priceKeySuffix;62  var resellerKey = "price_reseller_" + cleanProdName + "_" + priceKeySuffix;63 64  var adminNormalPrice = Bot.getProperty(normalKey);65  var adminResellerPrice = Bot.getProperty(resellerKey);66 67  var currentNormal = (adminNormalPrice !== undefined && adminNormalPrice !== null) ? adminNormalPrice : plan.normal_price;68  var currentReseller = (adminResellerPrice !== undefined && adminResellerPrice !== null) ? adminResellerPrice : plan.reseller_price;69 70  var originalPrice = isReseller ? Number(currentReseller) : Number(currentNormal);71  var price = originalPrice;72 73  var planSpecificDiscKey = "auto_discount_" + cleanProdName + "_" + cleanPlanDays + "_" + planUnit.toUpperCase();74  var productWideDiscKey = "auto_discount_" + cleanProdName + "_ALL";75  var autoDiscountPercent = Bot.getProperty(planSpecificDiscKey) || Bot.getProperty(productWideDiscKey) || Bot.getProperty("auto_discount_global") || 0;76 77  // 🎟 Apply coupon discount (if any was applied for this exact prod/plan) — coupon overrides auto-discount78  var appliedCouponCode = User.getProperty("applied_coupon_" + prodIdx + "_" + planIdx);79  var couponDiscountPercent = 0;80  if (appliedCouponCode) {81    var couponRec0 = Bot.getProperty("coupon_" + appliedCouponCode);82    if (couponRec0 && couponRec0.discountPercent) {83      couponDiscountPercent = couponRec0.discountPercent;84      price = Number((originalPrice * (1 - couponDiscountPercent / 100)).toFixed(2));85    }86    User.setProperty("applied_coupon_" + prodIdx + "_" + planIdx, null, "string");87  } else if (autoDiscountPercent > 0) {88    couponDiscountPercent = autoDiscountPercent;89    price = Number((originalPrice * (1 - autoDiscountPercent / 100)).toFixed(2));90  }91 92  // 🛡️ Safety re-check: balance abhi bhi sufficient hai ya nahi (race-condition guard)93  var unifiedBal = (function () {94    var resBal = 0;95    try { resBal = Number(Libs.ResourcesLib.userRes("balance").value()) || 0; } catch (e) {}96    var bonusBal = Number(Bot.getProperty("balance" + userId) || 0);97    return resBal + bonusBal;98  })();99 100  if (unifiedBal < price) {101    Api.sendMessage({102      chat_id: chatId,103      text: "⚠️ <b>Balance abhi kam hai.</b> Kripya phir se try karein.",104      parse_mode: "HTML"105    });106    return;107  }108 109  // =====================================================110  // 🔑 STEP 0 — MANUAL STOCK FIRST CHECK (✅ NEW)111  // Agar admin ne manually koi key already daal rakhi hai, to wahi seedha112  // deliver karo aur PAID EXTERNAL API ko bilkul call hi mat karo — taaki113  // "1 available key ke liye 2 keys spend" (1 manual + 1 wasted API buy)114  // wala bug kabhi na ho. API sirf tabhi call hoga jab manual stock khaali ho.115  // =====================================================116  var pDaysNumOnly = cleanPlanDays;117  var manualKeysStorageKey = "manual_keys_" + cleanProdName + "_" + pDaysNumOnly;118  var backupStockKey = "stock_" + product.name.trim() + "_" + pDaysNumOnly + "_Day";119  var isBackupUsed = false;120 121  var manualStock = Bot.getProperty(manualKeysStorageKey);122  if (!manualStock || (Array.isArray(manualStock) && manualStock.length === 0)) {123    manualStock = Bot.getProperty(backupStockKey);124    isBackupUsed = true;125  }126  if (typeof manualStock === "string" && manualStock.trim() !== "") {127    try {128      manualStock = JSON.parse(manualStock);129    } catch (err) {130      manualStock = manualStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean);131    }132  }133 134  if (Array.isArray(manualStock) && manualStock.length > 0) {135    var generatedKey = manualStock.shift();136 137    if (isBackupUsed) {138      Bot.setProperty(backupStockKey, manualStock, "json");139      var mainStock = Bot.getProperty(manualKeysStorageKey) || [];140      if (typeof mainStock === "string") { mainStock = mainStock.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }141      if (Array.isArray(mainStock)) {142        var idx1 = mainStock.indexOf(generatedKey);143        if (idx1 > -1) { mainStock.splice(idx1, 1); }144        Bot.setProperty(manualKeysStorageKey, mainStock, "json");145      }146    } else {147      Bot.setProperty(manualKeysStorageKey, manualStock, "json");148      var backupStock2 = Bot.getProperty(backupStockKey) || [];149      if (typeof backupStock2 === "string") { backupStock2 = backupStock2.split("\n").map(function (k) { return k.trim(); }).filter(Boolean); }150      if (Array.isArray(backupStock2)) {151        var idx2 = backupStock2.indexOf(generatedKey);152        if (idx2 > -1) { backupStock2.splice(idx2, 1); }153        Bot.setProperty(backupStockKey, backupStock2, "json");154      }155    }156 157    // 💰 Deduct balance + record purchase (same accounting as API path)158    Libs.ResourcesLib.userRes("balance").add(-price);159    var pastSpent2 = Bot.getProperty("total_spent_by_" + userId) || 0;160    Bot.setProperty("total_spent_by_" + userId, Number(pastSpent2) + price, "number");161 162 163    var keysHistory2 = User.getProperty("my_purchased_keys") || [];164    keysHistory2.push({165      product: String(product.name).trim(),166      product_id: product.id || null,167      days: pDaysNumOnly,168      price: price,169      key: generatedKey,170      date: new Date().toLocaleDateString()171    });172    User.setProperty("my_purchased_keys", keysHistory2, "json");173 174    var remainingBal2 = Libs.ResourcesLib.userRes("balance").value().toFixed(2);175 176    // 📥 Product-specific download link. If product link is empty, use the global update link as fallback.177    var downloadLinkForKey = product.download_link;178    var keyDeliveryButtons = [[{ text: "📋 Copy Key", copy_text: { text: generatedKey } }]];179    if (downloadLinkForKey) {180      keyDeliveryButtons.push([{ text: "📥 Download Link", url: downloadLinkForKey, style: "primary", icon_custom_emoji_id: "6091571559233755994" }]);181    }182    keyDeliveryButtons.push([{ text: "Back to Menu", callback_data: "/back", style: "primary", icon_custom_emoji_id: "5893163582194978381" }]);183    var keyDeliveryMarkup = JSON.stringify({ inline_keyboard: keyDeliveryButtons });184 185    var deliverText2 = "<blockquote>" +186      "<tg-emoji emoji-id='5350447674971660988'>✅</tg-emoji> <b>PURCHASE SUCCESSFUL!</b>\n\n" +187      "<tg-emoji emoji-id='6147767796097884213'>📦</tg-emoji> <b>Product:</b> <code>" + String(product.name).trim() + "</code>\n" +188      "<tg-emoji emoji-id='6284816251143331422'>🗝</tg-emoji> <b>Validity:</b> <code>" + durationDisplay + "</code>\n" +189      "<tg-emoji emoji-id='5352825278672412291'>👆</tg-emoji> <b>Your Key:</b> <code>" + generatedKey + "</code>\n\n" +190      "━━━━━ <tg-emoji emoji-id='6147934084346682063'>#⃣</tg-emoji> <b>BALANCE DETAILS</b> ━━━━━\n" +191      "<tg-emoji emoji-id='6195037488898121775'>✨</tg-emoji> <b>Total Invest:</b> ₹" + price.toFixed(2) + "\n" +192      "<tg-emoji emoji-id='5409048419211682843'>💵</tg-emoji> <b>New Wallet Balance:</b> ₹" + remainingBal2 + "\n\n" +193      "<i>Enjoy your purchase. <tg-emoji emoji-id='6057881002540274780'>🥳</tg-emoji></i>" +194      "</blockquote>";195 196    if (request && request.message) {197      try {198        Api.editMessageText({199          chat_id: String(chatId),200          message_id: Number(request.message.message_id),201          text: deliverText2,202          parse_mode: "HTML",203          reply_markup: keyDeliveryMarkup204        });205      } catch (e) {206        Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });207      }208    } else {209      Api.sendMessage({ chat_id: chatId, text: deliverText2, parse_mode: "HTML", reply_markup: keyDeliveryMarkup });210    }211 212    var adminId3 = "8875810358";213    Api.sendMessage({214      chat_id: adminId3,215      text: "🔔 <b>NEW PURCHASE DELIVERED (MANUAL STOCK)</b> ✔️\n\n" +216            "👤 <b>Buyer:</b> " + (user.first_name || "User") + " (<code>" + userId + "</code>)\n" +217            "📦 <b>Product:</b> " + String(product.name).trim() + "\n" +218            "⏳ <b>Plan:</b> " + durationDisplay + "\n" +219            "💸 <b>Price Deducted:</b> ₹" + price.toFixed(2) + "\n" +220            "💳 <b>User Remaining Bal:</b> ₹" + remainingBal2 + "\n" +221            "🔑 <b>Key:</b> <code>" + generatedKey + "</code>\n\n" +222            "<i>✅ Manual stock se deliver hui — external paid API call SKIP ki gayi.</i>",223      parse_mode: "HTML"224    });225 226    var qId0 = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;227    if (qId0) {228      try { Api.answerCallbackQuery({ callback_query_id: String(qId0), text: "✅ Key Delivered!", show_alert: false }); } catch (e) {}229    }230 231    return; // 🚫 STOP — external API bilkul call nahi hui232  }233 234  var webProductId = String(product.id || "PID_ID").trim();235  var prodNameLower = String(product.name).toLowerCase();236 237  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit);238 239  // =====================================================240  // 🔌 MULTI-API SYSTEM241  // Admin can add/update/select multiple reseller APIs.242  // The active API is used for external purchases.243  // =====================================================244  var apiRegistry = Bot.getProperty("api_registry") || {};245  var activeApiId = Bot.getProperty("active_reseller_api");246  var selectedApi = activeApiId ? apiRegistry[activeApiId] : null;247 248  // Backward-compatible bootstrap from the existing API configuration.249  if (!selectedApi) {250    selectedApi = {251      id: "default",252      name: "Default Reseller API",253      url: "https://xyzcheats.com/api/reseller_v1.php",254      api_key: "a9b26aa6439ff7cd495d64cb47916cb2",255      master_key: "a7f3e8b2c9d1f4a6b8c2d5e9f1a3b6c8",256      mode: "reseller_v1",257      android_required: false,258      enabled: true259    };260    apiRegistry.default = selectedApi;261    Bot.setProperty("api_registry", apiRegistry, "json");262    Bot.setProperty("active_reseller_api", "default", "string");263  }264 265  if (selectedApi.enabled === false) {266    Api.sendMessage({chat_id: chatId, text: "❌ Selected API is disabled. Admin ko /apiset se another API select karna hoga."});267    return;268  }269 270  var webProductId = String(product.id || "PID_ID").trim();271  var prodNameLower = String(product.name).toLowerCase();272 273  var durationParam = resolveApiDuration(plan, cleanPlanDays, planUnit);274 275  // Product-level API override is supported: product.api_id.276  var productApiId = product.api_id ? String(product.api_id).trim() : "";277  if (productApiId && apiRegistry[productApiId] && apiRegistry[productApiId].enabled !== false) {278    selectedApi = apiRegistry[productApiId];279    activeApiId = productApiId;280  }281 282  var postFields = {283    api_key: selectedApi.api_key || "",284    action: "buy",285    product_id: webProductId,286    duration: durationParam287  };288 289  // Device-bound APIs can require android_id. The value may be saved by290  // your existing product/user flow as android_id_<USER_ID>.291  var savedAndroidId = User.getProperty("android_id") || User.getProperty("android_id_" + userId) ||292                       Bot.getProperty("android_id_" + userId) || "";293  if (selectedApi.android_required) {294    if (!savedAndroidId) {295      Api.sendMessage({296        chat_id: chatId,297        text: "⚠️ <b>Android ID required</b>\nIs API/product ke liye Android ID zaroori hai. Pehle user ka Android ID save karein.",298        parse_mode: "HTML"299      });300      return;301    }302    postFields.android_id = String(savedAndroidId);303  } else if (savedAndroidId) {304    // Optional APIs may also accept it.305    postFields.android_id = String(savedAndroidId);306  }307 308  var apiHeaders = {309    "Content-Type": "application/x-www-form-urlencoded"310  };311  if (selectedApi.master_key) apiHeaders["x-master-key"] = selectedApi.master_key;312 313  var postFieldsString = Object.keys(postFields).map(function(k) {314    return encodeURIComponent(k) + "=" + encodeURIComponent(postFields[k]);315  }).join("&");316 317  User.setProperty("last_pending_price", Number(price), "number");318  User.setProperty("last_pending_prod_id", webProductId, "string");319  User.setProperty("last_pending_prod_name", String(product.name).trim(), "string");320  User.setProperty("last_pending_plan_days", cleanPlanDays, "string");321  User.setProperty("last_pending_plan_unit", planUnit, "string");322  User.setProperty("last_pending_api_id", String(selectedApi.id || activeApiId || ""), "string");323 324  var queryId = request ? (request.id || (request.callback_query && request.callback_query.id)) : null;325 326  var processingText = "<tg-emoji emoji-id='6147936236125298267'>⏳</tg-emoji> <b>Processing your order... please wait!</b>";327  if (request && request.message) {328    try {329      Api.editMessageText({330        chat_id: String(chatId),331        message_id: Number(request.message.message_id),332        text: processingText,333        parse_mode: "HTML"334      });335      Bot.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");336      User.setProperty("gen_msg_id_" + userId, request.message.message_id, "string");337    } catch (e) {338      var pm1 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });339      try {340        var pmid1 = (pm1 && pm1.result && pm1.result.message_id) ? pm1.result.message_id : (pm1 && pm1.message_id ? pm1.message_id : null);341        if (pmid1) { Bot.setProperty("gen_msg_id_" + userId, pmid1, "string"); User.setProperty("gen_msg_id_" + userId, pmid1, "string"); }342      } catch (e2) {}343    }344  } else {345    var pm2 = Api.sendMessage({ chat_id: chatId, text: processingText, parse_mode: "HTML" });346    try {347      var pmid2 = (pm2 && pm2.result && pm2.result.message_id) ? pm2.result.message_id : (pm2 && pm2.message_id ? pm2.message_id : null);348      if (pmid2) { Bot.setProperty("gen_msg_id_" + userId, pmid2, "string"); User.setProperty("gen_msg_id_" + userId, pmid2, "string"); }349    } catch (e2) {}350  }351 352  HTTP.post({353    url: selectedApi.url,354    body: postFieldsString,355    headers: apiHeaders,356    success: "/onWebKeyReceive",357    error: "/onWebKeyError"358  });359 360  if (queryId) {361    try {362      Api.answerCallbackQuery({363        callback_query_id: String(queryId),364        text: "⏳ Processing your order... Please wait!",365        show_alert: false366      });367    } catch (e) {}368  }369 370} catch (e) {371  var errChatId = (typeof chat !== "undefined" && chat && chat.chatid) ? chat.chatid : null;372  if (errChatId) {373    try { Api.sendMessage({ chat_id: errChatId, text: "❌ <b>Error:</b> <code>" + e.message + "</code>", parse_mode: "HTML" }); } catch (fatal) {}374  }375}