// ======================================== // Respon konfirmasi transaksi function generateTransactionPreview(product, customerId, zoneId, price) { let preview = `*KONFIRMASI TRANSAKSI*\n\n`; preview += `*Produk:* ${product.nama_layanan}\n`; preview += `*Jenis:* ${product.kategori}\n`; preview += `*ID:* ${customerId}`; if (zoneId) preview += ` (${zoneId})`; preview += `\n`; preview += `*Harga:* ${rupiah(price)}\n\n`; preview += `> Apakah data di atas sudah benar?\n`; preview += `> Ketik *Y* untuk konfirmasi\n> Ketik *N* untuk membatalkan`; return preview; } function savePendingTransaction( nomor, buyer_sku_code, customer_no, customer_zone, product, adjustedPrice, userSaldo, userData ) { if (!global.pendingTransactions) global.pendingTransactions = {}; global.pendingTransactions[nomor] = { buyer_sku_code, customer_no, customer_zone, product, adjustedPrice, userSaldo, userData, timestamp: Date.now() }; for (const userNomor in global.pendingTransactions) { const trx = global.pendingTransactions[userNomor]; if (Date.now() - trx.timestamp > 5 * 60 * 1000) { delete global.pendingTransactions[userNomor]; } } } function generateUniqueRefID() { const timestamp = new Date().getTime().toString(); const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0'); return `TRX${timestamp}${random}`; } function calculatePrice(product, userRole) { const originalPrice = parseFloat(product.price); let markupPercentage = defaultMarkupPercentage; switch(userRole) { case "GOLD": markupPercentage = markupConfig.gold; break; case "PLATINUM": markupPercentage = markupConfig.platinum; break; case "BRONZE": markupPercentage = markupConfig.bronze; break; case "OWNER": markupPercentage = markupConfig.owner; break; default: markupPercentage = defaultMarkupPercentage; } const increasedPrice = originalPrice * (1 + markupPercentage); return Math.round(increasedPrice); } // ===================================================================== // validasi nickname game async function checkNickname(game, userId, zoneId = null) { try { let nickname = null; let region = null; let apiUrl = ''; if (game.toLowerCase().includes('ml') || game.toLowerCase().includes('mobile legends')) { // Mobile Legends apiUrl = `https://ceknickname.com/api/mlbb/region?userId=${userId}&zoneId=${zoneId || ''}`; } else if (game.toLowerCase().includes('ff') || game.toLowerCase().includes('free fire')) { // Free Fire apiUrl = `https://ceknickname.com/api/free-fire-region?id=${userId}`; } else if (game.toLowerCase().includes('pubg')) { // PUBG Mobile apiUrl = `https://ceknickname.com/api/game/pubg-mobile-global-vc?id=${userId}`; } else { // Jika Game tidak terdeteksi return { success: false, message: "Game tidak didukung untuk cek nickname" }; } const response = await axios.get(apiUrl); if (response.data && response.data.status) { if (game.toLowerCase().includes('ml') || game.toLowerCase().includes('mobile legends')) { nickname = response.data.data.nickname; region = response.data.data.region; } else if (game.toLowerCase().includes('ff') || game.toLowerCase().includes('free fire')) { nickname = response.data.result.username; region = response.data.result.region; } else if (game.toLowerCase().includes('pubg')) { nickname = response.data.data.username; region = null; } return { success: true, data: { nickname: nickname, region: region } }; } else { return { success: false, message: "Nickname tidak ditemukan", statusCode: response.status || "Unknown" }; } } catch (error) { const statusCode = error.response?.status || "Unknown"; return { success: false, message: `Error checking nickname: ${error.message}`, statusCode: statusCode }; } } // ===================================================================== function generateTransactionPreview(product, customer_no, customer_zone, adjustedPrice, nickInfo = null) { let preview = `╭───═[ *DETAIL TRANSAKSI* ]═─────⋆\n`; preview += `│╭───────────────···\n`; preview += `││» *Produk:* ${product.nama_layanan}\n`; preview += `││» *Jenis:* ${product.kategori}\n`; preview += `││» *ID Game:* ${customer_no}`; if (customer_zone) preview += ` (${customer_zone})`; preview += `\n`; if (nickInfo && nickInfo.success) { preview += `││» *Nickname:* ${nickInfo.data.nickname}\n`; if (nickInfo.data.region) { preview += `││» *Region:* ${nickInfo.data.region}\n`; } } preview += `││» *Harga:* ${rupiah(adjustedPrice)}\n`; preview += `│╰───────────────···\n`; preview += `│ Ketik *Y* untuk menkonfirmasi.\n`; preview += `│ Ketik *N* untuk membatalkan.\n`; preview += `╰─────────────────────⋆`; return preview; } function savePendingTransaction(nomor, buyer_sku_code, customer_no, customer_zone, product, adjustedPrice, userSaldo, userData, nickInfo = null) { if (!global.pendingTransactions) { global.pendingTransactions = {}; } global.pendingTransactions[nomor] = { buyer_sku_code, customer_no, customer_zone, product, adjustedPrice, userSaldo, userData, nickInfo }; setTimeout(() => { if (global.pendingTransactions && global.pendingTransactions[nomor]) { delete global.pendingTransactions[nomor]; } }, 6 * 60 * 1000); // 5 menit } case 'trx': { const nomor = sender.split("@")[0]; const Data = JSON.parse(fs.readFileSync(pathUser)); const userProfile = Data.find((user) => user.nomor === nomor); if (!userProfile) return m.reply(`Kamu belum terdaftar, silahkan ketik : *Daftar* untuk bisa mengakses`); if (global.pendingTransactions && global.pendingTransactions[nomor]) { const pendingTrx = global.pendingTransactions[nomor]; let pendingMsg = `*TRANSAKSI MENUNGGU*\n\n`; pendingMsg += `*Produk:* ${pendingTrx.product.nama_layanan}\n`; pendingMsg += `*Jenis:* ${pendingTrx.product.kategori}\n`; pendingMsg += `*ID Game:* ${pendingTrx.customer_no}`; if (pendingTrx.customer_zone) pendingMsg += ` (${pendingTrx.customer_zone})`; pendingMsg += `\n`; if (pendingTrx.nickInfo && pendingTrx.nickInfo.success) { pendingMsg += `*Nickname:* ${pendingTrx.nickInfo.data.nickname}\n`; if (pendingTrx.nickInfo.data.region) { pendingMsg += `*Region:* ${pendingTrx.nickInfo.data.region}\n`; } } pendingMsg += `*Harga:* ${rupiah(pendingTrx.adjustedPrice)}\n\n`; pendingMsg += `Ketik *Y* untuk melanjutkan atau *N* untuk membatalkan`; return m.reply(pendingMsg); } if (args.length < 2) { const example = `Contoh penggunaan:\n` + `• *${command} MLH5 123456789 1234* (Mobile Legends)\n` + `• *${command} FF456 987654321* (Free Fire)\n` + `• *${command} PUBG789 555666777* (PUBG Mobile)\n` + `• *${command} GI999 111222333 os_asia* (Genshin Impact)`; return m.reply(`Format salah!\n\n${example}`); } const buyer_sku_code = args[0]; const productData = JSON.parse(fs.readFileSync('./db/datagz.json', 'utf8')); const product = productData.find(prod => prod.kode && prod.kode.toLowerCase() === buyer_sku_code.toLowerCase() ); if (!product) return m.reply(`❌ Produk dengan kode *${buyer_sku_code}* tidak ditemukan`); let customer_no, customer_zone; const hasServerInfo = args.length >= 3; if (hasServerInfo) { customer_no = args[1]; customer_zone = args.slice(2).join(' '); } else { customer_no = args[1]; customer_zone = ''; } const userData = JSON.parse(fs.readFileSync('./db/users.json')); const userSaldo = userData.find(saldo => saldo.nomor === nomor); if (!userSaldo) return m.reply(`❌ Kamu belum terdaftar, ketik *Daftar* untuk memulai`); try { const userRole = userData.find(role => role.nomor === nomor)?.role || 'BRONZE'; function calculatePrice(product, userRole) { const defaultMarkupPercentage = 0.015; let markupConfig; try { const markupData = fs.readFileSync('./db/markup.json', 'utf8'); markupConfig = JSON.parse(markupData); } catch (error) { console.error('Error loading markup configuration:', error); markupConfig = { bronze: 0.015, // 1.5% markup gold: 0.01, // 1% markup platinum: 0.005, // 0.5% markup owner: 0.001 // 0.1% markup }; } const originalPrice = parseFloat(product.harga); let markupPercentage = defaultMarkupPercentage; if (userRole === "GOLD") { markupPercentage = markupConfig.gold; } else if (userRole === "PLATINUM") { markupPercentage = markupConfig.platinum; } else if (userRole === "BRONZE") { markupPercentage = markupConfig.bronze; } else if (userRole === "OWNER") { markupPercentage = markupConfig.owner; } const increasedPrice = originalPrice * (1 + markupPercentage); return Math.round(increasedPrice); } const adjustedPrice = calculatePrice(product, userRole); let nickInfo = null; if (product.kategori.toLowerCase().includes('ml') || product.kategori.toLowerCase().includes('mobile legends')) { nickInfo = await checkNickname('mobile legends', customer_no, customer_zone); } else if (product.kategori.toLowerCase().includes('ff') || product.kategori.toLowerCase().includes('free fire')) { nickInfo = await checkNickname('free fire', customer_no); } else if (product.kategori.toLowerCase().includes('pubg')) { nickInfo = await checkNickname('pubg', customer_no); } if (nickInfo && !nickInfo.success && nickInfo.message.includes("404")) { return m.reply(`❌ *ID Game tidak ditemukan*\n\nMohon periksa kembali ID Game yang dimasukkan.`); } const trxPreview = generateTransactionPreview( product, customer_no, customer_zone, adjustedPrice, nickInfo ); await xstbot.sendMessage(m.chat, { text: trxPreview }, { quoted: m }); savePendingTransaction( nomor, buyer_sku_code, customer_no, customer_zone, product, adjustedPrice, userSaldo, userData, nickInfo ); } catch (error) { console.error('Transaction Error:', error); if (error.toString().includes("Request failed with status code 404")) { return m.reply(`❌ *ID Game tidak valid*\n\nMohon periksa kembali ID Game yang dimasukkan.`); } else { m.reply('⚠️ Terjadi kesalahan saat memproses transaksi. Silakan coba lagi.'); } } } break; // ========================================================= case 'y': { const nomor = sender.split("@")[0]; if (!global.pendingTransactions || !global.pendingTransactions[nomor]) { return m.reply(`Tidak ada transaksi yang menunggu konfirmasi.`); } const transaction = global.pendingTransactions[nomor]; delete global.pendingTransactions[nomor]; const { product, customer_no, customer_zone, adjustedPrice, userSaldo, userData, nickInfo } = transaction; let modalPrice = product.harga || 0; if (!userSaldo.saldo || userSaldo.saldo < adjustedPrice) { return m.reply(`Total saldo kamu (${rupiah(userSaldo.saldo || 0)}) tidak cukup untuk transaksi senilai ${rupiah(adjustedPrice)}.\n\nSilakan isi saldo terlebih dahulu.`); } else { userSaldo.saldo -= adjustedPrice; fs.writeFileSync('./db/users.json', JSON.stringify(userData, null, 2)); await processGZStoreTransaction(); } async function processGZStoreTransaction() { const ref_id = Invoice(); // Menggunakan fungsi Invoice() untuk generate invoice const apikey = global.GriezSt; try { let invo = `*── 「 TRANSAKSI PROSES 」 ──*\n\n`; invo += `> *Produk :* ${product.nama_layanan}\n`; invo += `> *Kategori :* ${product.kategori}\n`; invo += `> *Tujuan :* ${customer_no}${customer_zone ? ' ' + customer_zone : ''}\n`; if (nickInfo && nickInfo.success) { invo += `> *Nickname :* ${nickInfo.data.nickname}\n`; if (nickInfo.data.region) { invo += `> *Region :* ${nickInfo.data.region}\n`; } } invo += `> *Harga :* ${rupiah(adjustedPrice)}\n`; invo += `> *Invoice :* ${ref_id}\n`; await xstbot.sendText(m.chat, invo, m); const requestBody = { api_key: apikey, user_id: customer_no, zone: customer_zone || '', service: product.kode, quantity: "1", kontak: "6285380779466" }; const orderResponse = await axios.post( 'https://gzstore.id/api/v1/Order', requestBody, { headers: { 'Content-Type': 'application/json' } } ); console.log("GZ Store Order Response:", orderResponse.data); if (!orderResponse.data || !orderResponse.data.status || !orderResponse.data.data || !orderResponse.data.data.id) { userSaldo.saldo += adjustedPrice; fs.writeFileSync('./db/users.json', JSON.stringify(userData, null, 2)); return m.reply(orderResponse.data?.message || "Gagal memproses pesanan"); } const orderId = orderResponse.data.data.id; let transactionCompleted = false; // Real-time status checking - tanpa batasan retry while (!transactionCompleted) { await new Promise(resolve => setTimeout(resolve, 5000)); // Polling setiap 5 detik try { const statusResponse = await axios.post( 'https://gzstore.id/api/v1/status', { api_key: apikey, order_id: orderId }, { headers: { 'Content-Type': 'application/json' } } ); console.log("GZ Store Status Response:", statusResponse.data); if (!statusResponse.data || !statusResponse.data.status || !statusResponse.data.data) { continue; // Lanjutkan polling jika respons tidak valid } const statusData = statusResponse.data.data; if (statusData && statusData.status) { // Transaksi sukses if (statusData.status === 'Success') { let responseMsg = `*── 「 TRANSAKSI SUKSES 」 ──*\n\n`; responseMsg += `*» Produk :* ${statusData.layanan || product.nama_layanan}\n`; responseMsg += `*» Kategori :* ${product.kategori}\n`; responseMsg += `*» Tujuan :* ${statusData.user_id || customer_no}${statusData.zone ? ' ' + statusData.zone : (customer_zone ? ' ' + customer_zone : '')}\n`; const displayUsername = (statusData.username && statusData.username !== '-') ? statusData.username : (nickInfo && nickInfo.success ? nickInfo.data.nickname : ""); if (displayUsername) { responseMsg += `*» Nickname :* ${displayUsername}\n`; if (nickInfo && nickInfo.success && nickInfo.data.region) { responseMsg += `*» Region :* ${nickInfo.data.region}\n`; } } responseMsg += `*» Harga :* ${rupiah(adjustedPrice)}\n`; responseMsg += `*» Invoice :* ${ref_id}\n`; if (statusData.keterangan) { responseMsg += `*» Info :* ${statusData.keterangan}\n`; } await xstbot.sendMessage(m.chat, { text: responseMsg }, { quoted: m }); // Simpan transaksi sukses saveTransaction( nomor, "Berhasil", ref_id, statusData.layanan || product.nama_layanan, product.kategori, "00", statusData.user_id || customer_no, statusData.zone || customer_zone || "", displayUsername, adjustedPrice, product.harga, nickInfo && nickInfo.success ? nickInfo.data.region : null ); transactionCompleted = true; break; } // Status gagal else if ( statusData.status === 'Cancelled' || statusData.status === 'Canceled' || statusData.status === 'Failed' || statusData.status === 'Error' ) { const failedInvoice = Invoice(); let responseMsg = `*── 「 TRANSAKSI GAGAL 」 ──*\n\n`; responseMsg += `Pesananmu *${statusData.layanan || product.nama_layanan}* Gagal\n`; responseMsg += `Kategori: *${product.kategori}*\n`; responseMsg += `Tujuan: *${statusData.user_id || customer_no}${statusData.zone ? ' ' + statusData.zone : (customer_zone ? ' ' + customer_zone : '')}*\n`; // Tambahkan nickname jika ada, dari status atau pengecekan awal const displayUsername = (statusData.username && statusData.username !== '-') ? statusData.username : (nickInfo && nickInfo.success ? nickInfo.data.nickname : ""); if (displayUsername) { responseMsg += `Nickname: *${displayUsername}*\n`; // Tambahkan region jika tersedia dari pengecekan awal if (nickInfo && nickInfo.success && nickInfo.data.region) { responseMsg += `Region: *${nickInfo.data.region}*\n`; } } responseMsg += `Invoice: *${failedInvoice}*\n`; responseMsg += `Pesan: ${statusData.keterangan || "Terjadi kesalahan pada sistem"}\n\n`; responseMsg += `Saldo telah dikembalikan ke akun Anda.`; await xstbot.sendMessage(m.chat, { text: responseMsg }, { quoted: m }); // Kembalikan saldo userSaldo.saldo += adjustedPrice; fs.writeFileSync('./db/users.json', JSON.stringify(userData, null, 2)); // Simpan transaksi gagal saveTransaction( nomor, "Gagal", failedInvoice, statusData.layanan || product.nama_layanan, product.kategori, "99", statusData.user_id || customer_no, statusData.zone || customer_zone || "", displayUsername, adjustedPrice, product.harga, nickInfo && nickInfo.success ? nickInfo.data.region : null ); transactionCompleted = true; break; } // Status masih pending atau processing - lanjutkan polling tanpa memberikan respon else if (statusData.status === 'Pending' || statusData.status === 'Processing') { console.log(`Order ${orderId} masih ${statusData.status}. Menunggu perubahan status...`); continue; } // Status yang tidak dikenali - tetap lanjutkan polling else { console.log(`Status tidak dikenali: ${statusData.status}. Menunggu perubahan status...`); continue; } } } catch (error) { console.log("Error checking order status:", error); // Terus mencoba jika terjadi error pada pengecekan await new Promise(resolve => setTimeout(resolve, 10000)); // Tunggu lebih lama jika terjadi error } } } catch (error) { console.log("Transaction processing error:", error); m.reply("Terjadi kesalahan saat memproses transaksi. Silakan coba lagi nanti."); // Kembalikan saldo jika terjadi error userSaldo.saldo += adjustedPrice; fs.writeFileSync('./db/users.json', JSON.stringify(userData, null, 2)); } } // Fungsi untuk menyimpan transaksi ke file trx.json function saveTransaction(nomor, status, invoice, item, category, rc, tujuan, zone, username, harga, harga_modal, region = null) { try { let transactions = []; if (fs.existsSync("./db/trx.json")) { const rawData = fs.readFileSync("./db/trx.json", "utf8"); transactions = JSON.parse(rawData); } const newTransaction = { nomor: nomor, status: status, invoice: invoice, item: item, category: category, rc: rc, tujuan: `${tujuan}${zone ? ' ' + zone : ''}`, username: username, region: region, harga: harga, harga_modal: harga_modal, waktu: `${time1} | ${hariini}` }; transactions.push(newTransaction); fs.writeFileSync("./db/trx.json", JSON.stringify(transactions, null, 2)); return true; } catch (error) { console.error("Error saving transaction:", error); return false; } } } break case 'n': { const nomor = sender.split("@")[0]; if (!global.pendingTransactions || !global.pendingTransactions[nomor]) { return m.reply(`Tidak ada transaksi yang menunggu konfirmasi.`); } const transaction = global.pendingTransactions[nomor]; delete global.pendingTransactions[nomor]; return m.reply(`Transaksi dibatalkan. Terima kasih.`); } break; // =========================================================