UNPKG

qris-saweria

Version:

saweria.co unofficial api that can create qris and check it automatically

217 lines (190 loc) 7.34 kB
const axios = require('axios'); const cheerio = require('cheerio'); const QRCode = require('qrcode'); const { createCanvas, loadImage } = require('canvas'); const fs = require('fs'); const path = require('path'); const { generate } = require('random-username-generator'); const BACKEND = 'https://backend.saweria.co'; const FRONTEND = 'https://saweria.co'; const headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.9', 'Accept-Encoding': 'gzip, deflate, br', 'Content-Type': 'application/json', 'Origin': 'https://saweria.co', 'Referer': 'https://saweria.co/', 'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', 'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Platform': '"Windows"', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-site' }; function generateRandomMessage() { const messages = [ "Semangat kak!", "Terima kasih atas kontennya", "Lanjutkan karya baiknya", "Support terus", "Keep up the good work!", "Sukses selalu", "Tetap semangat" ]; return messages[Math.floor(Math.random() * messages.length)]; } function insertPlusInEmail(email, insertStr) { const [localPart, domain] = email.split('@'); const cleanLocalPart = localPart.split('+')[0]; return `${cleanLocalPart}+${insertStr}@${domain}`; } async function paidStatus(transactionId) { /** * Check status of a saweria transaction paid or not from transaction_id. * @param {string} transactionId - String from output of createPayment * @returns {Promise<boolean>} - Returns true if paid, false if not paid * @throws {Error} - Throws error if transaction ID is not found */ try { const response = await axios.get(`${BACKEND}/donations/qris/${transactionId}`, { headers }); const data = response.data.data; return data.qr_string === ""; } catch (error) { throw new Error("Transaction ID is not found!"); } } async function createPaymentString(saweriaUsername, amount, sender, email, pesan) { /** * Outputs a details transaction from variables. * @param {string} saweriaUsername - The recipient's Saweria username * @param {number} amount - The donation amount in IDR * @param {string} sender - Name of donor * @param {string} email - Email of sender * @param {string} pesan - Message to be sent to the creator * @returns {Promise<Object>} - Transaction details from input variables * @throws {Error} - Throws error if parameters are missing or invalid */ if (!saweriaUsername || !amount || !sender || !email || !pesan) { throw new Error("Parameter is missing!"); } if (amount < 1000) { throw new Error("Minimum amount is 1000"); } console.log(`Loading ${FRONTEND}/${saweriaUsername}`); const response = await axios.get(`${FRONTEND}/${saweriaUsername}`, { headers: { ...headers, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', 'Content-Type': 'text/html; charset=utf-8' } }); const $ = cheerio.load(response.data); const nextDataScript = $('#__NEXT_DATA__'); if (!nextDataScript.length) { console.log("Saweria account not found"); throw new Error("Saweria account not found"); } const nextData = JSON.parse(nextDataScript.text()); const userId = nextData?.props?.pageProps?.data?.id; if (!userId) { console.log("Saweria account not found"); throw new Error("Saweria account not found"); } const payload = { agree: true, notUnderage: true, message: pesan, amount: parseInt(amount), payment_type: "qris", vote: "", currency: "IDR", customer_info: { first_name: sender, email: insertPlusInEmail(email, sender), phone: "" } }; try { const paymentResponse = await axios.post(`${BACKEND}/donations/${userId}`, payload, { headers: { ...headers, 'Content-Type': 'application/json' } }); return paymentResponse.data.data; } catch (error) { console.error('Payment request failed:', error.response?.data || error.message); throw new Error('Failed to create payment: ' + (error.response?.data?.message || error.message)); } } async function generateQROnly(qrString, outputPath) { await QRCode.toFile(outputPath, qrString, { errorCorrectionLevel: 'H', margin: 1, width: 400, color: { dark: '#000000', light: '#ffffff' } }); return outputPath; } async function generateQRWithTemplate(qrString, outputPath, saweriaUsername) { const TEMPLATE_WIDTH = 710; const TEMPLATE_HEIGHT = 844; const QR_SIZE = 380; const qrCanvas = createCanvas(QR_SIZE, QR_SIZE); await QRCode.toCanvas(qrCanvas, qrString, { errorCorrectionLevel: 'H', margin: 1, width: QR_SIZE, color: { dark: '#000000', light: '#ffffff' } }); const canvas = createCanvas(TEMPLATE_WIDTH, TEMPLATE_HEIGHT); const ctx = canvas.getContext('2d'); try { const template = await loadImage(path.join(__dirname, 'template.png')); ctx.drawImage(template, 0, 0, TEMPLATE_WIDTH, TEMPLATE_HEIGHT); ctx.font = 'bold 32px Arial'; ctx.fillStyle = '#000000'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(saweriaUsername, TEMPLATE_WIDTH / 2, 160); const qrX = (TEMPLATE_WIDTH - QR_SIZE) / 2; const qrY = (TEMPLATE_HEIGHT - QR_SIZE) / 2 + 20; ctx.drawImage(qrCanvas, qrX, qrY); const buffer = canvas.toBuffer('image/png'); fs.writeFileSync(outputPath, buffer); return outputPath; } catch (error) { console.log('Template tidak ditemukan, menggunakan QR code saja'); return generateQROnly(qrString, outputPath); } } async function createPaymentQr(saweriaUsername, amount, email, outputPath = 'qris.png', useTemplate = true) { const randomSender = generate(); const message = generateRandomMessage(); const [qrString, transactionId] = await createPaymentQrRaw(saweriaUsername, amount, randomSender, email, message); if (qrString) { if (useTemplate) { await generateQRWithTemplate(qrString, outputPath, saweriaUsername); } else { await generateQROnly(qrString, outputPath); } } return [qrString, transactionId, outputPath]; } async function createPaymentQrRaw(saweriaUsername, amount, sender, email, pesan) { const paymentDetails = await createPaymentString(saweriaUsername, amount, sender, email, pesan); return [paymentDetails.qr_string, paymentDetails.id]; } module.exports = { paidStatus, createPaymentString, createPaymentQr, createPaymentQrRaw };