Saweria Top-Up ( Game Mobile opsional)

JavaScriptPublic
by zx-apiAug 10, 2026, 04:53 AMExpires: Never274 views
Saweria Top-Up ( Game Mobile opsional)
Raw
1/** 
2 * Name: Saweria Top-Up Game ( Opsional Game ML )
3 * 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
4 * Credit By Zx
5 *
6 * Sumber: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o
7 * Kalau Mau Di Sher Jangan hapus sumber / Credit nya
8**/
9
10const axios = require('axios');
11const QRCode = require('qrcode');
12const path = require('path');
13
14class SaweriaAutomation {
15  constructor(username = 'zixvorx') {
16    this.username = username;
17    this.streamerId = ''; // ( wajib: supaya qris nya masuk ke qris donasi kita ambil dsri request dsri Saweria)
18    
19    this.backend = axios.create({
20      baseURL: 'https://backend.saweria.co',
21      headers: {
22        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
23        'Accept': 'application/json, text/plain, */*',
24        'Content-Type': 'application/json',
25        'Origin': 'https://saweria.co',
26        'Referer': `https://saweria.co/${this.username}/toko-top-up/mobile-legends-bang-bang`
27      },
28      timeout: 15000
29    });
30  }
31
32  // AMBIL PRODUK
33  async getProducts(gameSlug = 'mobile-legends-bang-bang') {
34    const res = await this.backend.get(`/game-vouchers/${gameSlug}`, {
35      params: { streamer_id: this.streamerId, username: this.username }
36    });
37    return res.data.data.game_vouchers.products
38      .filter(p => p.status === 'ACTIVE')
39      .map(p => ({
40        id: p.product_id,
41        sku: p.sku,                 
42        name: p.product_name,
43        slug: p.slug,
44        sellingPrice: Number(p.pricing.selling_price),
45        donationAmount: Number(p.donation_amount),
46        thumbnail: p.image.thumbnail,
47        vat: p.vat?.rate || 0       
48      }));
49  }
50
51  // HITUNG FEE
52  async calculateFee(amount, paymentType = 'qris', message = 'pesanan') {
53    const res = await this.backend.post(
54      `/game-vouchers/${this.username}/calculate_pg_amount`,
55      { amount: Number(amount), payment_type: paymentType, message: String(message) }
56    );
57    return res.data.data;
58  }
59
60  // BUAT ORDER
61  async createOrder({
62    product,
63    userId = '1151655541', // id akun game
64    zoneId = '13664', <zone id akun ml > ( bisa berubah kapan aja ๐Ÿ—ฟ๐Ÿ˜น)
65    donatorEmail = 'pribadiarep@gmail.com',
66    donatorName = 'zixvorx',
67    message = 'pesanan 1'
68  }) {
69    const feeData = await this.calculateFee(product.sellingPrice, 'qris', message);
70
71    const payload = {
72      product_id: product.id,
73      product_sku: String(product.sku),
74      amount: Number(product.sellingPrice),
75      payment_type: 'qris',
76      vat: product.vat,
77      vatAmount: feeData.vat_amount || 0,
78      message: String(message),
79      digital_form: [
80        { key: "userId", value: String(userId) },
81        { key: "zoneId", value: String(zoneId) }
82      ],
83      customer_info: {
84        first_name: String(donatorName).trim(),
85        email: String(donatorEmail).trim(),
86        phone: ""
87      }
88    };
89
90    try {
91      const res = await this.backend.post(`/game-vouchers/snap/${this.streamerId}`, payload);
92      
93      return {
94        orderId: res.data.data.id,
95        amount: res.data.data.amount,
96        status: res.data.data.status,
97        qrString: res.data.data.etc.qr_string,
98        expiredAt: res.data.data.etc.payment_expired_at,
99        productName: res.data.data.etc.game_voucher.product_name
100      };
101    } catch (err) {
102      console.error('\n๐Ÿ”ด Detail Error Create Order:', err.response?.data || err.message);
103      throw new Error(`Gagal buat order: ${err.response?.statusText || err.message}`);
104    }
105  }
106
107  // FORMAT WAKTU
108  formatWaktu(dateString, pakaiPukul = false) {
109    const date = new Date(dateString);
110    const bulanArr = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep', 'Okt', 'Nov', 'Des'];
111    const bulanArrFull = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
112    
113    const tgl = date.getDate();
114    const bln = pakaiPukul ? bulanArrFull[date.getMonth()] : bulanArr[date.getMonth()];
115    const thn = date.getFullYear();
116    const jam = String(date.getHours()).padStart(2, '0');
117    const mnt = String(date.getMinutes()).padStart(2, '0');
118
119    if (pakaiPukul) {
120      return `${tgl} ${bln} ${thn} pukul ${jam}.${mnt} WIB`;
121    }
122    return `${tgl} ${bln} ${thn}, ${jam}.${mnt} WIB`;
123  }
124
125  hitungSisaWaktu(expiredString) {
126    const expired = new Date(expiredString).getTime();
127    const sekarang = new Date().getTime();
128    const selisih = expired - sekarang;
129    if (selisih <= 0) return "Kedaluwarsa";
130    const menit = Math.floor(selisih / (1000 * 60));
131    const detik = Math.floor((selisih % (1000 * 60)) / 1000);
132    return `${menit}m ${detik}d`;
133  }
134
135  // GENERATE GAMBAR QRIS
136  async generateQRImage(qrString, orderId) {
137    const fileName = `QRIS_${orderId.split('-')[0]}.png`;
138    const filePath = path.join(__dirname, fileName);
139    await QRCode.toFile(filePath, qrString, {
140      color: { dark: '#000000', light: '#FFFFFF' },
141      width: 400,
142      margin: 2
143    });
144    return fileName;
145  }
146
147  // LACAK ORDER & TAMPILKAN HASIL
148  async trackOrder(orderId) {
149    try {
150      const res = await this.backend.get(`/game-vouchers/tracking-order/${orderId}`);
151      const data = res.data.data;
152
153      const berlakuHingga = this.formatWaktu(data.expired_at, true);
154      const tanggalDibuat = this.formatWaktu(data.created_at, false);
155      
156      let tanggalDiselesaikan = "-";
157      let statusPengiriman = "Belum Diproses";
158
159      if (data.status === 'SUCCESS' || data.payment_status === 'PAID' || data.payment_status === 'SETTLED') {
160          statusPengiriman = "Berhasil";
161          tanggalDiselesaikan = this.formatWaktu(data.payment_updated_at, false);
162      }
163
164      let outputText = `Berlaku Hingga: ${berlakuHingga}\n\n`;
165      outputText += `${this.hitungSisaWaktu(data.expired_at)}\n\n`;
166      outputText += `Dukungan untuk ${data.username}: Rp${data.amount_raw.toLocaleString('id-ID')}\n\n`;
167      outputText += `ID: ${data.id}\n\n`;
168      outputText += `Detail Transaksi\n\n`;
169      outputText += `Status pengiriman\n\n${statusPengiriman}\n\n`;
170      outputText += `Tanggal dibuat\n\n${tanggalDibuat}\n\n`;
171      outputText += `Tanggal diselesaikan\n\n${tanggalDiselesaikan}`;
172
173      console.log(outputText);
174
175      if (data.etc && data.etc.qr_string) {
176        const savedFile = await this.generateQRImage(data.etc.qr_string, data.id);
177        console.log(`\n=========================================`);
178        console.log(`โœ… String QRIS berhasil diconvert menjadi gambar!`);
179        console.log(`๐Ÿ“ File tersimpan sebagai: ${savedFile}`);
180        console.log(`=========================================\n`);
181        return { success: true, text: outputText, qrPath: savedFile, data };
182      } else {
183        console.log(`\nโš ๏ธ QR String tidak ditemukan, pesanan mungkin sudah dibayar atau kedaluwarsa.`);
184        return { success: false, text: outputText, message: "QR String tidak ada", data };
185      }
186    } catch (err) {
187      console.error('โŒ Gagal melacak pesanan:', err.message);
188      return { success: false, message: err.message };
189    }
190  }
191}
192
193async function main() {
194  const scraper = new SaweriaAutomation('zixvorx');
195
196  try {
197    console.log('๐ŸŽฎ [1/5] Mengambil produk...');
198    const products = await scraper.getProducts();
199    const product = products[0]; 
200    console.log(`   โœ… Dipilih: ${product.name} (Rp${product.sellingPrice.toLocaleString('id-ID')})`);
201
202    console.log('\n๐Ÿ’ฐ [2/5] Menghitung biaya...');
203    const fee = await scraper.calculateFee(product.sellingPrice);
204    console.log(`   โœ… Total Bayar: Rp${fee.amount_to_pay.toLocaleString('id-ID')} (Fee: Rp${fee.pg_fee.toLocaleString('id-ID')})`);
205
206    console.log('\n๐Ÿ›’ [3/5] Membuat pesanan QRIS...');
207    const order = await scraper.createOrder({
208      product: product,
209      userId: '1151655541',          // ID game emel kalian
210      zoneId: '13664',               // ini zona id emel kalian ( bisa bisa sesuaikan dengan game yang di pilih )
211      donatorEmail: 'zixvorx@gmail.com', // Gmail Akun Saweria kalian 
212      donatorName: 'zixvorx', // username Saweria Pukimak Kalian๐Ÿ˜น
213      message: 'pesanan 1'
214    });
215
216    console.log(`   โœ… Order ID  : ${order.orderId}`);
217    console.log(`   โœ… Total     : Rp${order.amount.toLocaleString('id-ID')}`);
218    console.log(`   โœ… Expired   : ${new Date(order.expiredAt).toLocaleString('id-ID')}`);
219    console.log(`   โœ… Status    : ${order.status}`);
220
221    console.log('\n๐Ÿ” [4/5] Melacak pesanan & mengambil data QRIS...\n');
222    const trackResult = await scraper.trackOrder(order.orderId);
223
224    if (trackResult.success) {
225      console.log('๐Ÿ’ก [5/5] Selesai! Buka file gambar QRIS yang tersimpan di folder project dan scan menggunakan e-wallet kamu.');
226    } else {
227      console.log('โš ๏ธ Gagal mendapatkan QRIS. Silakan cek log di atas.');
228    }
229
230  } catch (err) {
231    console.error('\nโŒ ERROR FATAL:', err.message);
232  }
233}
234
235main();
236
236 linesยท9,274 charsยท9.1 KB