/** * Name: Saweria Top-Up Game ( Opsional Game ML ) * Bisa Di Ganti Game ya, streamerId wajib di isi supaya saldo komisi nya masuk ke saweria kita , cara dapatin nya tinggal cek di Saweria nya buat seperti pesanan palsu dan cek log request ya biasanya ada * Credit By Zx * * Sumber: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o * Kalau Mau Di Sher Jangan hapus sumber / Credit nya **/ const axios = require('axios'); const QRCode = require('qrcode'); const path = require('path'); class SaweriaAutomation { constructor(username = 'zixvorx') { this.username = username; this.streamerId = ''; // ( wajib: supaya qris nya masuk ke qris donasi kita ambil dsri request dsri Saweria) this.backend = axios.create({ baseURL: 'https://backend.saweria.co', headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json', 'Origin': 'https://saweria.co', 'Referer': `https://saweria.co/${this.username}/toko-top-up/mobile-legends-bang-bang` }, timeout: 15000 }); } // AMBIL PRODUK async getProducts(gameSlug = 'mobile-legends-bang-bang') { const res = await this.backend.get(`/game-vouchers/${gameSlug}`, { params: { streamer_id: this.streamerId, username: this.username } }); return res.data.data.game_vouchers.products .filter(p => p.status === 'ACTIVE') .map(p => ({ id: p.product_id, sku: p.sku, name: p.product_name, slug: p.slug, sellingPrice: Number(p.pricing.selling_price), donationAmount: Number(p.donation_amount), thumbnail: p.image.thumbnail, vat: p.vat?.rate || 0 })); } // HITUNG FEE async calculateFee(amount, paymentType = 'qris', message = 'pesanan') { const res = await this.backend.post( `/game-vouchers/${this.username}/calculate_pg_amount`, { amount: Number(amount), payment_type: paymentType, message: String(message) } ); return res.data.data; } // BUAT ORDER async createOrder({ product, userId = '1151655541', // id akun game zoneId = '13664', ( bisa berubah kapan aja ๐Ÿ—ฟ๐Ÿ˜น) donatorEmail = 'pribadiarep@gmail.com', donatorName = 'zixvorx', message = 'pesanan 1' }) { const feeData = await this.calculateFee(product.sellingPrice, 'qris', message); const payload = { product_id: product.id, product_sku: String(product.sku), amount: Number(product.sellingPrice), payment_type: 'qris', vat: product.vat, vatAmount: feeData.vat_amount || 0, message: String(message), digital_form: [ { key: "userId", value: String(userId) }, { key: "zoneId", value: String(zoneId) } ], customer_info: { first_name: String(donatorName).trim(), email: String(donatorEmail).trim(), phone: "" } }; try { const res = await this.backend.post(`/game-vouchers/snap/${this.streamerId}`, payload); return { orderId: res.data.data.id, amount: res.data.data.amount, status: res.data.data.status, qrString: res.data.data.etc.qr_string, expiredAt: res.data.data.etc.payment_expired_at, productName: res.data.data.etc.game_voucher.product_name }; } catch (err) { console.error('\n๐Ÿ”ด Detail Error Create Order:', err.response?.data || err.message); throw new Error(`Gagal buat order: ${err.response?.statusText || err.message}`); } } // FORMAT WAKTU formatWaktu(dateString, pakaiPukul = false) { const date = new Date(dateString); const bulanArr = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep', 'Okt', 'Nov', 'Des']; const bulanArrFull = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember']; const tgl = date.getDate(); const bln = pakaiPukul ? bulanArrFull[date.getMonth()] : bulanArr[date.getMonth()]; const thn = date.getFullYear(); const jam = String(date.getHours()).padStart(2, '0'); const mnt = String(date.getMinutes()).padStart(2, '0'); if (pakaiPukul) { return `${tgl} ${bln} ${thn} pukul ${jam}.${mnt} WIB`; } return `${tgl} ${bln} ${thn}, ${jam}.${mnt} WIB`; } hitungSisaWaktu(expiredString) { const expired = new Date(expiredString).getTime(); const sekarang = new Date().getTime(); const selisih = expired - sekarang; if (selisih <= 0) return "Kedaluwarsa"; const menit = Math.floor(selisih / (1000 * 60)); const detik = Math.floor((selisih % (1000 * 60)) / 1000); return `${menit}m ${detik}d`; } // GENERATE GAMBAR QRIS async generateQRImage(qrString, orderId) { const fileName = `QRIS_${orderId.split('-')[0]}.png`; const filePath = path.join(__dirname, fileName); await QRCode.toFile(filePath, qrString, { color: { dark: '#000000', light: '#FFFFFF' }, width: 400, margin: 2 }); return fileName; } // LACAK ORDER & TAMPILKAN HASIL async trackOrder(orderId) { try { const res = await this.backend.get(`/game-vouchers/tracking-order/${orderId}`); const data = res.data.data; const berlakuHingga = this.formatWaktu(data.expired_at, true); const tanggalDibuat = this.formatWaktu(data.created_at, false); let tanggalDiselesaikan = "-"; let statusPengiriman = "Belum Diproses"; if (data.status === 'SUCCESS' || data.payment_status === 'PAID' || data.payment_status === 'SETTLED') { statusPengiriman = "Berhasil"; tanggalDiselesaikan = this.formatWaktu(data.payment_updated_at, false); } let outputText = `Berlaku Hingga: ${berlakuHingga}\n\n`; outputText += `${this.hitungSisaWaktu(data.expired_at)}\n\n`; outputText += `Dukungan untuk ${data.username}: Rp${data.amount_raw.toLocaleString('id-ID')}\n\n`; outputText += `ID: ${data.id}\n\n`; outputText += `Detail Transaksi\n\n`; outputText += `Status pengiriman\n\n${statusPengiriman}\n\n`; outputText += `Tanggal dibuat\n\n${tanggalDibuat}\n\n`; outputText += `Tanggal diselesaikan\n\n${tanggalDiselesaikan}`; console.log(outputText); if (data.etc && data.etc.qr_string) { const savedFile = await this.generateQRImage(data.etc.qr_string, data.id); console.log(`\n=========================================`); console.log(`โœ… String QRIS berhasil diconvert menjadi gambar!`); console.log(`๐Ÿ“ File tersimpan sebagai: ${savedFile}`); console.log(`=========================================\n`); return { success: true, text: outputText, qrPath: savedFile, data }; } else { console.log(`\nโš ๏ธ QR String tidak ditemukan, pesanan mungkin sudah dibayar atau kedaluwarsa.`); return { success: false, text: outputText, message: "QR String tidak ada", data }; } } catch (err) { console.error('โŒ Gagal melacak pesanan:', err.message); return { success: false, message: err.message }; } } } async function main() { const scraper = new SaweriaAutomation('zixvorx'); try { console.log('๐ŸŽฎ [1/5] Mengambil produk...'); const products = await scraper.getProducts(); const product = products[0]; console.log(` โœ… Dipilih: ${product.name} (Rp${product.sellingPrice.toLocaleString('id-ID')})`); console.log('\n๐Ÿ’ฐ [2/5] Menghitung biaya...'); const fee = await scraper.calculateFee(product.sellingPrice); console.log(` โœ… Total Bayar: Rp${fee.amount_to_pay.toLocaleString('id-ID')} (Fee: Rp${fee.pg_fee.toLocaleString('id-ID')})`); console.log('\n๐Ÿ›’ [3/5] Membuat pesanan QRIS...'); const order = await scraper.createOrder({ product: product, userId: '1151655541', // ID game emel kalian zoneId: '13664', // ini zona id emel kalian ( bisa bisa sesuaikan dengan game yang di pilih ) donatorEmail: 'zixvorx@gmail.com', // Gmail Akun Saweria kalian donatorName: 'zixvorx', // username Saweria Pukimak Kalian๐Ÿ˜น message: 'pesanan 1' }); console.log(` โœ… Order ID : ${order.orderId}`); console.log(` โœ… Total : Rp${order.amount.toLocaleString('id-ID')}`); console.log(` โœ… Expired : ${new Date(order.expiredAt).toLocaleString('id-ID')}`); console.log(` โœ… Status : ${order.status}`); console.log('\n๐Ÿ” [4/5] Melacak pesanan & mengambil data QRIS...\n'); const trackResult = await scraper.trackOrder(order.orderId); if (trackResult.success) { console.log('๐Ÿ’ก [5/5] Selesai! Buka file gambar QRIS yang tersimpan di folder project dan scan menggunakan e-wallet kamu.'); } else { console.log('โš ๏ธ Gagal mendapatkan QRIS. Silakan cek log di atas.'); } } catch (err) { console.error('\nโŒ ERROR FATAL:', err.message); } } main();