UNPKG

wahdx-api

Version:

Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://api.wahdx.co

153 lines (133 loc) 5.27 kB
const QRCode = require("qrcode"); const axios = require("axios"); class QRISGenerator { constructor(config = {}) { this.config = { baseQrString: config.baseQrString || '', tokenKey: config.tokenKey, auth_username: config.auth_username, auth_token: config.auth_token }; } /** * Generate QR code sebagai PNG buffer * @param {string} qrString - String QR code yang akan digenerate * @returns {Promise<Buffer>} - Buffer PNG dari QR code */ async generateQRImage(qrString) { try { if (!qrString) { throw new Error('qrString tidak boleh kosong'); } // Gunakan QRCode.toBuffer langsung tanpa canvas const qrBuffer = await QRCode.toBuffer(qrString, { errorCorrectionLevel: 'H', margin: 2, width: 500, color: { dark: '#000000', light: '#ffffff' } }); return qrBuffer; } catch (error) { throw new Error('Gagal generate QR: ' + error.message); } } /** * Generate QR dengan nominal tertentu * @param {number} amount - Nominal pembayaran * @returns {Promise<{qrString: string, qrBuffer: Buffer}>} */ async generateQR(amount) { const qrString = this.generateQrString(amount); const qrBuffer = await this.generateQRImage(qrString); return { qrString, qrBuffer }; } /** * Generate QR dengan nominal tertentu menggunakan API OrderKuota * @param {number} amount - Nominal pembayaran * @returns {Promise<{qrString: string, qrBuffer: Buffer}>} */ async generateQRFromAPI(amount) { try { if (!amount || amount <= 0) { throw new Error('Nominal harus lebih besar dari 0'); } if (!this.config.tokenKey || !this.config.auth_username || !this.config.auth_token) { throw new Error('tokenKey, auth_username, dan auth_token harus diisi'); } const response = await axios.post( 'https://api.wahdx.co/api/qr-orkut-v2', { username_orkut: this.config.auth_username, token_orkut: this.config.auth_token, nominal: amount.toString() }, { headers: { 'tokenKey': this.config.tokenKey, 'Content-Type': 'application/json' } } ); if (!response.data || !response.data.status || !response.data.qrString) { throw new Error('Response tidak valid dari server'); } const qrString = response.data.qrString; // Generate QR image dari qrString yang didapat dari API const qrBuffer = await this.generateQRImage(qrString); return { qrString, qrBuffer }; } catch (error) { throw new Error('Gagal generate QR dari API: ' + error.message); } } generateQrString(amount) { try { if (!amount || amount <= 0) { throw new Error('Nominal harus lebih besar dari 0'); } if (!this.config.baseQrString) { throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.'); } if (!this.config.baseQrString.includes("5802ID")) { throw new Error("Format QRIS tidak valid"); } const finalAmount = Math.floor(amount); const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212"); const nominalStr = finalAmount.toString(); const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`; const insertPosition = qrisBase.indexOf("5802ID"); const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition); const checksum = this.calculateCRC16(qrisWithNominal); return qrisWithNominal + checksum; } catch (error) { throw new Error('Gagal generate string QRIS: ' + error.message); } } calculateCRC16(str) { try { if (!str) { throw new Error('String tidak boleh kosong'); } let crc = 0xFFFF; for (let i = 0; i < str.length; i++) { crc ^= str.charCodeAt(i) << 8; for (let j = 0; j < 8; j++) { crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1); } crc &= 0xFFFF; } return crc.toString(16).toUpperCase().padStart(4, '0'); } catch (error) { throw new Error('Gagal kalkulasi CRC16: ' + error.message); } } } module.exports = QRISGenerator;