wahdx-api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # wahdx-api
2
+
3
+ Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari [https://api.wahdx.co](https://api.wahdx.co).
4
+
5
+ ## Fitur
6
+
7
+ - Membaca dan ekstrak kode QR dari gambar
8
+ - Generate kode QR dengan nominal tertentu
9
+ - Cek status pembayaran secara realtime
10
+ - Generate bukti transaksi (receipt)
11
+
12
+ ## Instalasi
13
+
14
+ ```bash
15
+ npm install wahdx-api
16
+ ```
17
+
18
+ ## Konfigurasi
19
+
20
+ Buat file `.env` di root project Anda:
21
+
22
+ ```
23
+ WAHDX_TOKENKEY=your_wahdx_token_key
24
+ ORKUT_TOKEN_AUTH=your_orkut_token_auth
25
+ ORKUT_USERNAME=your_orkut_username
26
+ ```
27
+
28
+ ## Penggunaan
29
+
30
+ ### Contoh Lengkap
31
+
32
+ ```javascript
33
+ import QRISPayment from 'wahdx-api';
34
+ import '@dotenvx/dotenvx/config';
35
+ import fs from 'fs';
36
+
37
+ // Konfigurasi
38
+ const config = {
39
+ storeName: 'AHDX STORE',
40
+ defaultQrPath: 'QRIS.png', // Path ke QRIS static yang didownload di merchant orderkuota
41
+ tokenKey: process.env.WAHDX_TOKENKEY,
42
+ auth_token: process.env.ORKUT_TOKEN_AUTH,
43
+ auth_username: process.env.ORKUT_USERNAME
44
+ };
45
+
46
+ // Membuat instance QRISPayment
47
+ const qrisPayment = new QRISPayment(config);
48
+
49
+ async function main() {
50
+ try {
51
+ console.log('=== TEST REALTIME QRIS PAYMENT ===\n');
52
+ const randomAmount = Math.floor(Math.random() * 99) + 1; // Random 1-99
53
+ const amount = 100 + randomAmount; // Base 100 + random amount
54
+ const reference = 'REF' + Date.now();
55
+
56
+ // Generate QR code
57
+ const { qrBuffer } = await qrisPayment.generateQRFromImage(amount);
58
+
59
+ // Save QR code image
60
+ fs.writeFileSync(`qr-${amount}.png`, qrBuffer);
61
+
62
+ console.log('=== TRANSACTION DETAILS ===');
63
+ console.log('Reference:', reference);
64
+ console.log('Amount:', amount);
65
+ console.log('QR Image:', `qr-${amount}.png`);
66
+ console.log('\nSilakan scan QR code dan lakukan pembayaran');
67
+ console.log('\nMenunggu pembayaran...\n');
68
+
69
+ // Check payment status with 5 minutes timeout
70
+ const startTime = Date.now();
71
+ const timeout = 5 * 60 * 1000;
72
+
73
+ while (Date.now() - startTime < timeout) {
74
+ const result = await qrisPayment.checkPayment(reference, amount);
75
+
76
+ if (result.success && result.data.status === 'PAID') {
77
+ console.log('✓ Pembayaran berhasil!');
78
+ if (result.receipt) {
79
+ console.log('✓ Bukti transaksi:', result.receipt.filePath);
80
+ }
81
+ return;
82
+ }
83
+
84
+ await new Promise(resolve => setTimeout(resolve, 10000)); // delay 10 detik
85
+ console.log('Menunggu pembayaran...');
86
+ }
87
+
88
+ throw new Error('Timeout: Pembayaran tidak diterima dalam 5 menit');
89
+
90
+ } catch (error) {
91
+ console.error('Error:', error.message);
92
+ }
93
+ }
94
+
95
+ main();
96
+ ```
97
+
98
+ ### API Reference
99
+
100
+ #### Inisialisasi
101
+
102
+ ```javascript
103
+ import QRISPayment from 'wahdx-api';
104
+
105
+ const config = {
106
+ storeName: 'NAMA TOKO',
107
+ defaultQrPath: 'path/to/qris/template.png',
108
+ tokenKey: 'your_wahdx_token_key',
109
+ auth_token: 'your_orkut_token_auth',
110
+ auth_username: 'your_orkut_username'
111
+ };
112
+
113
+ const qrisPayment = new QRISPayment(config);
114
+ ```
115
+
116
+ #### Generate QR dari Template
117
+
118
+ ```javascript
119
+ const amount = 10000; // Rp 10.000
120
+ // Menggunakan default QR yang sudah diatur
121
+ const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount);
122
+ // Atau dengan path QR spesifik
123
+ // const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount, 'path/to/qris/template.png');
124
+
125
+ // Simpan QR ke file
126
+ fs.writeFileSync('qr-output.png', qrBuffer);
127
+ ```
128
+
129
+ #### Cek Status Pembayaran
130
+
131
+ ```javascript
132
+ const reference = 'REF' + Date.now();
133
+ const amount = 10000;
134
+
135
+ const result = await qrisPayment.checkPayment(reference, amount);
136
+ console.log(result);
137
+ /*
138
+ Output jika berhasil:
139
+ {
140
+ success: true,
141
+ data: {
142
+ status: 'PAID',
143
+ amount: 10000,
144
+ reference: 'REF1234567890',
145
+ ...
146
+ },
147
+ receipt: {
148
+ filePath: 'path/to/receipt.pdf',
149
+ ...
150
+ }
151
+ }
152
+ */
153
+ ```
154
+
155
+ ## Persyaratan Sistem
156
+
157
+ - Node.js >= 20.18.3
158
+
159
+ ## Lisensi
160
+
161
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import QRISGenerator from './qr-generator.js';
2
+ import PaymentChecker from './payment-checker.js';
3
+ import ReceiptGenerator from './receipt-generator.js';
4
+
5
+ class QRISPayment {
6
+ constructor(config = {}) {
7
+ this.qrGenerator = new QRISGenerator(config);
8
+ this.paymentChecker = new PaymentChecker(config);
9
+ this.receiptGenerator = new ReceiptGenerator(config);
10
+ }
11
+
12
+ /**
13
+ * Membaca QR code dari file gambar dan mengekstrak baseQrString
14
+ * @param {string} imagePath - Path ke file gambar QR
15
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
16
+ */
17
+ async readQRCode(imagePath) {
18
+ return await this.qrGenerator.readQRFromImage(imagePath);
19
+ }
20
+
21
+ /**
22
+ * Generate QR dengan nominal tertentu
23
+ * @param {number} amount - Nominal pembayaran
24
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
25
+ */
26
+ async generateQR(amount) {
27
+ const qrString = this.qrGenerator.generateQrString(amount);
28
+ const qrBuffer = await this.qrGenerator.generateQRWithLogo(qrString);
29
+ return {
30
+ qrString,
31
+ qrBuffer
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Membaca QR dari file gambar dan langsung menghasilkan QR baru dengan nominal
37
+ * @param {number} amount - Nominal pembayaran
38
+ * @param {string} qrImagePath - Path ke gambar QR (opsional)
39
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
40
+ */
41
+ async generateQRFromImage(amount, qrImagePath = null) {
42
+ return await this.qrGenerator.generateQRFromImage(amount, qrImagePath);
43
+ }
44
+
45
+ async checkPayment(reference, amount) {
46
+ const result = await this.paymentChecker.checkPaymentStatus(reference, amount);
47
+ if (result.success && result.data.status === 'PAID') {
48
+ const receipt = await this.receiptGenerator.generateReceipt(result.data);
49
+ result.receipt = receipt;
50
+ }
51
+
52
+ return result;
53
+ }
54
+
55
+ async generateReceipt(transactionData) {
56
+ return await this.receiptGenerator.generateReceipt(transactionData);
57
+ }
58
+ }
59
+
60
+ // Mengekspor kelas utama
61
+ export default QRISPayment;
@@ -0,0 +1,90 @@
1
+ import axios from 'axios';
2
+
3
+ class PaymentChecker {
4
+ constructor(config) {
5
+ if (!config.tokenKey || !config.auth_username || !config.auth_token) {
6
+ throw new Error('tokenKey, auth_username, dan auth_token harus diisi');
7
+ }
8
+ this.config = {
9
+ tokenKey: config.tokenKey,
10
+ auth_username: config.auth_username,
11
+ auth_token: config.auth_token
12
+ };
13
+ }
14
+
15
+ async checkPaymentStatus(reference, amount) {
16
+ try {
17
+ if (!reference || !amount || amount <= 0) {
18
+ throw new Error('Reference dan amount harus diisi dengan benar');
19
+ }
20
+
21
+ const response = await axios.post(
22
+ 'https://api.wahdx.co/api/mutasi-orkut-v2',
23
+ {
24
+ username_orkut: this.config.auth_username,
25
+ token_orkut: this.config.auth_token
26
+ },
27
+ {
28
+ headers: {
29
+ 'tokenKey': this.config.tokenKey,
30
+ 'Content-Type': 'application/json'
31
+ }
32
+ }
33
+ );
34
+
35
+ if (!response.data || !response.data.status || !response.data.data) {
36
+ throw new Error('Response tidak valid dari server');
37
+ }
38
+
39
+ const transactions = response.data.data;
40
+
41
+ const matchingTransactions = transactions.filter(tx => {
42
+ const txAmount = parseInt(tx.amount);
43
+ const txDate = new Date(tx.date);
44
+ const now = new Date();
45
+ const timeDiff = now - txDate;
46
+
47
+ return txAmount === amount &&
48
+ tx.qris === "static" &&
49
+ tx.type === "CR" &&
50
+ timeDiff <= 5 * 60 * 1000;
51
+ });
52
+
53
+ if (matchingTransactions.length > 0) {
54
+ const latestTransaction = matchingTransactions.reduce((latest, current) => {
55
+ const currentDate = new Date(current.date);
56
+ const latestDate = new Date(latest.date);
57
+ return currentDate > latestDate ? current : latest;
58
+ });
59
+
60
+ return {
61
+ success: true,
62
+ data: {
63
+ status: 'PAID',
64
+ amount: parseInt(latestTransaction.amount),
65
+ reference: latestTransaction.issuer_reff,
66
+ date: latestTransaction.date,
67
+ brand_name: latestTransaction.brand_name,
68
+ buyer_reff: latestTransaction.buyer_reff
69
+ }
70
+ };
71
+ }
72
+
73
+ return {
74
+ success: true,
75
+ data: {
76
+ status: 'UNPAID',
77
+ amount: amount,
78
+ reference: reference
79
+ }
80
+ };
81
+ } catch (error) {
82
+ return {
83
+ success: false,
84
+ error: 'Gagal cek status pembayaran: ' + error.message
85
+ };
86
+ }
87
+ }
88
+ }
89
+
90
+ export default PaymentChecker;
@@ -0,0 +1,202 @@
1
+ import QRCode from 'qrcode';
2
+ import { createCanvas, loadImage } from 'canvas';
3
+ import fs from 'fs';
4
+ import sharp from 'sharp';
5
+ import jsQR from 'jsqr';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ class QRISGenerator {
13
+ constructor(config = {}) {
14
+ this.config = {
15
+ baseQrString: config.baseQrString || '',
16
+ logoPath: config.logoPath || null,
17
+ defaultQrPath: config.defaultQrPath || 'QRIS.png'
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Membaca QR code dari gambar dan mengekstrak baseQrString
23
+ * @param {string} imagePath - Path relatif atau absolut ke file gambar QR
24
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
25
+ */
26
+ async readQRFromImage(imagePath) {
27
+ try {
28
+ // Konversi path relatif ke absolut jika diperlukan
29
+ const absolutePath = path.isAbsolute(imagePath)
30
+ ? imagePath
31
+ : path.resolve(process.cwd(), imagePath);
32
+
33
+ // Periksa apakah file ada
34
+ if (!fs.existsSync(absolutePath)) {
35
+ throw new Error(`File tidak ditemukan: ${absolutePath}`);
36
+ }
37
+
38
+ // Baca gambar menggunakan Sharp
39
+ const image = sharp(absolutePath);
40
+ const metadata = await image.metadata();
41
+ const { width, height } = metadata;
42
+
43
+ // Konversi ke raw pixel data
44
+ const rawData = await image
45
+ .raw()
46
+ .toBuffer();
47
+
48
+ // Format data untuk jsQR
49
+ const imageData = new Uint8ClampedArray(width * height * 4);
50
+
51
+ for (let i = 0; i < rawData.length; i += 3) { // RGB format
52
+ const pixelIndex = (i / 3) * 4; // Convert RGB index to RGBA index
53
+ imageData[pixelIndex] = rawData[i]; // R
54
+ imageData[pixelIndex + 1] = rawData[i + 1]; // G
55
+ imageData[pixelIndex + 2] = rawData[i + 2]; // B
56
+ imageData[pixelIndex + 3] = 255; // A (full opacity)
57
+ }
58
+
59
+ // Dekode QR code menggunakan jsQR
60
+ const code = jsQR(imageData, width, height);
61
+
62
+ if (code) {
63
+ // Simpan baseQrString yang dibaca ke config
64
+ this.config.baseQrString = code.data;
65
+ return code.data;
66
+ } else {
67
+ throw new Error('QR code tidak terdeteksi dalam gambar');
68
+ }
69
+ } catch (error) {
70
+ console.error('Error saat membaca QR code:', error);
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Membaca QR code dari file default (biasanya QRIS.png)
77
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
78
+ */
79
+ async readDefaultQR() {
80
+ try {
81
+ // Path default ke file QRIS.png
82
+ const defaultPath = path.resolve(process.cwd(), this.config.defaultQrPath);
83
+ return await this.readQRFromImage(defaultPath);
84
+ } catch (error) {
85
+ console.error(`Error saat membaca gambar default (${this.config.defaultQrPath}):`, error);
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ async generateQRWithLogo(qrString) {
91
+ try {
92
+ if (!qrString) {
93
+ throw new Error('qrString tidak boleh kosong');
94
+ }
95
+ const canvas = createCanvas(500, 500);
96
+ const ctx = canvas.getContext('2d');
97
+ await QRCode.toCanvas(canvas, qrString, {
98
+ errorCorrectionLevel: 'H',
99
+ margin: 2,
100
+ width: 500,
101
+ color: {
102
+ dark: '#000000',
103
+ light: '#ffffff'
104
+ }
105
+ });
106
+ if (this.config.logoPath && fs.existsSync(this.config.logoPath)) {
107
+ const logo = await loadImage(this.config.logoPath);
108
+ const logoSize = canvas.width * 0.2;
109
+ const logoPosition = (canvas.width - logoSize) / 2;
110
+
111
+ ctx.fillStyle = '#FFFFFF';
112
+ ctx.fillRect(logoPosition - 5, logoPosition - 5, logoSize + 10, logoSize + 10);
113
+ ctx.drawImage(logo, logoPosition, logoPosition, logoSize, logoSize);
114
+ }
115
+ return canvas.toBuffer('image/png');
116
+ } catch (error) {
117
+ throw new Error('Gagal generate QR: ' + error.message);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Membaca QR dari file kemudian generate QR baru dengan nominal
123
+ * @param {number} amount - Nominal pembayaran
124
+ * @param {string} qrImagePath - Path ke gambar QR (opsional)
125
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
126
+ */
127
+ async generateQRFromImage(amount, qrImagePath = null) {
128
+ try {
129
+ // Jika path gambar disediakan, baca dari path tersebut
130
+ if (qrImagePath) {
131
+ await this.readQRFromImage(qrImagePath);
132
+ }
133
+ // Jika tidak ada path dan baseQrString kosong, gunakan default
134
+ else if (!this.config.baseQrString) {
135
+ await this.readDefaultQR();
136
+ }
137
+
138
+ // Generate QR string dengan nominal
139
+ const qrString = this.generateQrString(amount);
140
+
141
+ // Generate QR image
142
+ const qrBuffer = await this.generateQRWithLogo(qrString);
143
+
144
+ return {
145
+ qrString,
146
+ qrBuffer
147
+ };
148
+ } catch (error) {
149
+ throw new Error('Gagal generate QR dari gambar: ' + error.message);
150
+ }
151
+ }
152
+
153
+ generateQrString(amount) {
154
+ try {
155
+ if (!amount || amount <= 0) {
156
+ throw new Error('Nominal harus lebih besar dari 0');
157
+ }
158
+
159
+ if (!this.config.baseQrString) {
160
+ throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
161
+ }
162
+
163
+ if (!this.config.baseQrString.includes("5802ID")) {
164
+ throw new Error("Format QRIS tidak valid");
165
+ }
166
+
167
+ const finalAmount = Math.floor(amount);
168
+ const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
169
+ const nominalStr = finalAmount.toString();
170
+ const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
171
+ const insertPosition = qrisBase.indexOf("5802ID");
172
+ const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
173
+ const checksum = this.calculateCRC16(qrisWithNominal);
174
+
175
+ return qrisWithNominal + checksum;
176
+ } catch (error) {
177
+ throw new Error('Gagal generate string QRIS: ' + error.message);
178
+ }
179
+ }
180
+
181
+ calculateCRC16(str) {
182
+ try {
183
+ if (!str) {
184
+ throw new Error('String tidak boleh kosong');
185
+ }
186
+
187
+ let crc = 0xFFFF;
188
+ for (let i = 0; i < str.length; i++) {
189
+ crc ^= str.charCodeAt(i) << 8;
190
+ for (let j = 0; j < 8; j++) {
191
+ crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
192
+ }
193
+ crc &= 0xFFFF;
194
+ }
195
+ return crc.toString(16).toUpperCase().padStart(4, '0');
196
+ } catch (error) {
197
+ throw new Error('Gagal kalkulasi CRC16: ' + error.message);
198
+ }
199
+ }
200
+ }
201
+
202
+ export default QRISGenerator;
@@ -0,0 +1,130 @@
1
+ import PDFDocument from 'pdfkit';
2
+ import fs from 'fs';
3
+ import moment from 'moment';
4
+ import path from 'path';
5
+
6
+ class ReceiptGenerator {
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+
11
+ async generateReceipt(transactionData) {
12
+ return new Promise((resolve, reject) => {
13
+ try {
14
+ const doc = new PDFDocument({
15
+ size: [300, 450],
16
+ margin: 20,
17
+ layout: 'portrait'
18
+ });
19
+
20
+ const fileName = `receipt_${transactionData.reference}_${Date.now()}.pdf`;
21
+ const filePath = `receipts/${fileName}`;
22
+ if (!fs.existsSync('receipts')) {
23
+ fs.mkdirSync('receipts');
24
+ }
25
+ const writeStream = fs.createWriteStream(filePath);
26
+ doc.pipe(writeStream);
27
+
28
+ if (this.config.logoPath && fs.existsSync(this.config.logoPath)) {
29
+ const logoWidth = 40;
30
+ const logoHeight = 40;
31
+ const logoX = 20 + (260 - logoWidth) / 2;
32
+ const logoY = 10;
33
+ doc.image(this.config.logoPath, logoX, logoY, { width: logoWidth, height: logoHeight });
34
+ doc.y = logoY + logoHeight + 4;
35
+ } else {
36
+ doc.moveDown(0.5);
37
+ }
38
+
39
+ doc.fontSize(14)
40
+ .fillColor('black')
41
+ .font('Helvetica-Bold');
42
+ const headerText = 'QRIS PAYMENT RECEIPT';
43
+ const headerWidth = doc.widthOfString(headerText);
44
+ doc.text(headerText, 20 + (260 - headerWidth) / 2, doc.y, { width: headerWidth });
45
+
46
+ doc.fontSize(9)
47
+ .font('Helvetica');
48
+ const merchantText = this.config.storeName || 'STORE NAME';
49
+ const merchantWidth = doc.widthOfString(merchantText);
50
+ doc.text(merchantText, 20 + (260 - merchantWidth) / 2, doc.y, { width: merchantWidth });
51
+
52
+ doc.moveDown(0.3);
53
+ const lineY = doc.y;
54
+ doc.moveTo(20, lineY)
55
+ .lineTo(280, lineY)
56
+ .strokeColor('#888').stroke();
57
+
58
+ doc.moveDown(0.5);
59
+ doc.fontSize(9)
60
+ .font('Helvetica-Bold');
61
+ const detailsTitle = 'TRANSACTION DETAILS';
62
+ const detailsTitleWidth = doc.widthOfString(detailsTitle);
63
+ doc.text(detailsTitle, 20 + (260 - detailsTitleWidth) / 2, doc.y, { width: detailsTitleWidth });
64
+ doc.moveDown(0.2);
65
+
66
+ const formattedDate = moment(transactionData.date).format('DD/MM/YYYY HH:mm:ss');
67
+ const details = [
68
+ ['Reference', transactionData.reference],
69
+ ['Date', formattedDate],
70
+ ['Amount', `Rp ${transactionData.amount.toLocaleString('id-ID')}`],
71
+ ['Status', transactionData.status],
72
+ ['Payment Method', transactionData.brand_name || '-'],
73
+ ['Buyer Reference', transactionData.buyer_reff || '-']
74
+ ];
75
+
76
+ const tableWidth = 240;
77
+ const startX = (300 - tableWidth) / 2;
78
+ const labelWidth = 100;
79
+ const valueWidth = 130;
80
+ let y = doc.y + 5;
81
+
82
+ details.forEach(([label, value], idx) => {
83
+ doc.font('Helvetica-Bold')
84
+ .fontSize(9)
85
+ .fillColor('black')
86
+ .text(label, startX, y, { width: labelWidth, align: 'left' });
87
+ doc.font('Helvetica')
88
+ .fontSize(9)
89
+ .fillColor('black')
90
+ .text(value, startX + labelWidth, y, { width: valueWidth, align: 'right' });
91
+
92
+ y += 14;
93
+ doc.moveTo(startX, y)
94
+ .lineTo(startX + labelWidth + valueWidth, y)
95
+ .strokeColor('#e0e0e0').stroke();
96
+ y += 2;
97
+ });
98
+
99
+ y += 6;
100
+ doc.moveTo(20, y)
101
+ .lineTo(280, y)
102
+ .strokeColor('#888').stroke();
103
+
104
+ y += 12;
105
+ doc.text('Thank you for your payment!', 0, y, { align: 'right', width: 260 });
106
+ y += 12;
107
+ doc.text(`Receipt No: ${transactionData.reference}`, 0, y, { align: 'right', width: 260 });
108
+
109
+ doc.end();
110
+
111
+ writeStream.on('finish', () => {
112
+ resolve({
113
+ success: true,
114
+ filePath: filePath,
115
+ fileName: fileName
116
+ });
117
+ });
118
+
119
+ writeStream.on('error', (error) => {
120
+ reject(error);
121
+ });
122
+
123
+ } catch (error) {
124
+ reject(error);
125
+ }
126
+ });
127
+ }
128
+ }
129
+
130
+ export default ReceiptGenerator;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "wahdx-api",
3
+ "version": "1.0.0",
4
+ "description": "Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://api.wahdx.co",
5
+ "main": "dist/index.js",
6
+ "author": "wahdalo",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/wahdalo/wahdx-api"
12
+ },
13
+ "keywords": [
14
+ "payment",
15
+ "rest-api",
16
+ "payment-gateway"
17
+ ],
18
+ "scripts": {
19
+ "test": "node tes.js",
20
+ "start": "node dist/index.js",
21
+ "build": "node scripts/build.js",
22
+ "prepublishOnly": "npm run build",
23
+ "pack": "npm pack --dry-run"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "dependencies": {
31
+ "@dotenvx/dotenvx": "^1.47.5",
32
+ "axios": "^1.10.0",
33
+ "canvas": "^3.1.2",
34
+ "jsqr": "^1.4.0",
35
+ "moment": "^2.29.4",
36
+ "pdfkit": "^0.13.0",
37
+ "qrcode": "^1.5.4",
38
+ "sharp": "^0.32.6"
39
+ },
40
+ "engines": {
41
+ "node": ">=20.18.3"
42
+ }
43
+ }