case 'qris2': { const CONFIG = { apiKey: 'PAYKU_1234567890', // Apikey secretKey: '11111aaaaa22222bbbbb33333ccccc44444dddddd55555eeeee', baseURL: 'https://payku.my.id', minAmount: 1000, // minimal deposit maxChecks: 10, checkInterval: 5000 }; if (!global.activeTransactions) { global.activeTransactions = new Map(); } const generateSignature = (data, secretKey) => { const sortedKeys = Object.keys(data).sort(); const stringToSign = sortedKeys.map(k => `${k}=${data[k]}`).join('&'); return crypto.createHmac('sha256', secretKey).update(stringToSign).digest('hex'); }; const createHeaders = (data, timestamp) => { const signature = generateSignature(data, CONFIG.secretKey); return { 'x-api-key': CONFIG.apiKey, 'x-signature': signature, 'x-timestamp': timestamp, 'Content-Type': 'application/json' }; }; const sanitizeUserId = (userId) => userId.replace(/[^0-9]/g, ''); const createTransaction = async (nominal, userId, pushname) => { const timestamp = Date.now().toString(); const sanitizedUserId = sanitizeUserId(userId); const transactionData = { external_id: `BOT-${sanitizedUserId}`, amount: nominal, description: `Top Up QRIS Rp${nominal.toLocaleString()}`, customer_name: pushname || userId.split('@')[0], customer_email: `${sanitizedUserId}@gmail.com`, customer_phone: sanitizedUserId, timestamp }; const headers = createHeaders(transactionData, timestamp); try { const response = await axios.post(`${CONFIG.baseURL}/api/create-transaction`, transactionData, { headers }); if (!response.data.success) { throw new Error(response.data.message || 'Gagal membuat transaksi'); } return response.data.data; } catch (error) { throw new Error(`API Error: ${error.response?.data?.message || error.message}`); } }; const checkTransactionStatus = async (transactionId) => { const timestamp = Date.now().toString(); const data = { transaction_id: transactionId, timestamp }; const headers = createHeaders(data, timestamp); try { const response = await axios.get(`${CONFIG.baseURL}/api/transaction/${transactionId}`, { headers }); if (!response.data.success) { throw new Error(response.data.message || 'Gagal mengecek status transaksi'); } return response.data.data; } catch (error) { throw new Error(`API Error: ${error.response?.data?.message || error.message}`); } }; const cancelTransaction = async (transactionId) => { const timestamp = Date.now().toString(); const data = { transaction_id: transactionId, timestamp }; const headers = createHeaders(data, timestamp); try { const response = await axios.post(`${CONFIG.baseURL}/api/transaction/${transactionId}/cancel`, {}, { headers }); if (!response.data.success) { throw new Error(response.data.message || 'Gagal membatalkan transaksi'); } return response.data.message; } catch (error) { throw new Error(`API Error: ${error.response?.data?.message || error.message}`); } }; const formatQRISMessage = (transaction) => { return `✅ *QRIS Siap Digunakan* • ID: ${transaction.transaction_id} • Nominal: Rp${transaction.amount.toLocaleString()} • Fee: Rp${transaction.fee.toLocaleString()} • Total Bayar: Rp${transaction.total_amount.toLocaleString()} 📥 Klik untuk bayar via QRIS 🔗 ${transaction.qris_url} _Selama belum dibayar, bot akan cek otomatis..._ _Ketik "cancel" untuk membatalkan pembayaran._`; }; const formatSuccessMessage = (statusData) => { return `✅ *Pembayaran Berhasil!* • Nominal yang akan ditambahkan: Rp${statusData.amount.toLocaleString()} • Total Dibayar: Rp${statusData.total_amount.toLocaleString()} • Metode: ${statusData.payment_method} • Dibayar: ${statusData.paid_at} *Catatan:* Fee adalah pajak layanan dan tidak ditambahkan ke saldo Anda.`; }; const pollPaymentStatus = async (transactionId, userId) => { let status = 'pending'; let checkCount = 0; while (status === 'pending' && checkCount < CONFIG.maxChecks) { await new Promise(resolve => setTimeout(resolve, CONFIG.checkInterval)); if (!global.activeTransactions.has(userId)) { console.log(`Transaction ${transactionId} was cancelled by user`); return false; } try { const statusData = await checkTransactionStatus(transactionId); status = statusData.status; checkCount++; if (status === 'paid') { global.activeTransactions.delete(userId); await Ditss.sendMessage(m.chat, { text: formatSuccessMessage(statusData) }, { quoted: m }); return true; } } catch (error) { console.error(`Error checking status (attempt ${checkCount + 1}):`, error); checkCount++; } } if (status !== 'paid' && global.activeTransactions.has(userId)) { global.activeTransactions.delete(userId); await Ditss.sendMessage(m.chat, { text: `⚠️ Pembayaran belum diterima setelah ${CONFIG.maxChecks} kali pengecekan. QRIS telah expired.` }, { quoted: m }); } return false; }; if (!text) return m.reply('❌ Format salah!\n\nContoh:\n• .qris2 15000\n• Ketik "cancel" untuk membatalkan'); if (text.toLowerCase() === 'cancel') { const userId = m.sender; const activeTransaction = global.activeTransactions.get(userId); if (!activeTransaction) { return m.reply('❌ Tidak ada transaksi QRIS yang sedang aktif untuk dibatalkan.'); } try { const message = await cancelTransaction(activeTransaction.transactionId); global.activeTransactions.delete(userId); m.reply(`✅ Transaksi berhasil dibatalkan dan QR QRIS telah dihapus!\n\n${message}`); } catch (error) { console.error('Cancel transaction error:', error); global.activeTransactions.delete(userId); m.reply(`⚠️ QR QRIS telah dihapus. ${error.message}`); } break; } if (text.startsWith('cancel ')) { const transactionId = text.split(' ')[1]; if (!transactionId) { return m.reply('❌ Masukkan ID transaksi yang ingin dibatalkan.\nContoh: .qris2 cancel TRX123456'); } try { const message = await cancelTransaction(transactionId); global.activeTransactions.delete(m.sender); m.reply(`✅ ${message}`); } catch (error) { console.error('Cancel transaction error:', error); m.reply(`❌ Gagal membatalkan transaksi: ${error.message}`); } break; } const nominal = parseInt(text); if (isNaN(nominal) || nominal < CONFIG.minAmount) { return m.reply(`❌ Masukkan nominal minimal Rp${CONFIG.minAmount.toLocaleString()}\nContoh: .qris2 15000`); } if (global.activeTransactions.has(m.sender)) { const activeTransaction = global.activeTransactions.get(m.sender); return m.reply(`❌ Anda masih memiliki transaksi QRIS yang aktif!\n\nID: ${activeTransaction.transactionId}\nNominal: Rp${activeTransaction.amount.toLocaleString()}\n\nKetik "cancel" untuk membatalkan transaksi sebelumnya, atau tunggu hingga selesai.`); } try { const transaction = await createTransaction(nominal, m.sender, pushname); global.activeTransactions.set(m.sender, { transactionId: transaction.transaction_id, amount: transaction.amount, createdAt: Date.now() }); await Ditss.sendMessage(m.chat, { image: { url: transaction.qris_url }, caption: formatQRISMessage(transaction) }, { quoted: m }); pollPaymentStatus(transaction.transaction_id, m.sender).catch(error => { console.error('Polling payment status error:', error); global.activeTransactions.delete(m.sender); }); } catch (error) { console.error('Create transaction error:', error); m.reply(`❌ Gagal membuat transaksi: ${error.message}`); } } break;