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
commands/_confirm_buyitem.js
javascript · 375 lines
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}