wahdx-api 1.0.3 → 1.0.4

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 CHANGED
@@ -1,189 +1,217 @@
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
- - Kompatibel di semua platform (Windows, Linux, Mac)
12
-
13
- ## Instalasi
14
-
15
- ```bash
16
- npm install wahdx-api
17
- ```
18
-
19
- ## Konfigurasi
20
-
21
- Buat file `.env` di root project Anda:
22
-
23
- ```
24
- WAHDX_TOKENKEY=your_wahdx_token_key
25
- ORKUT_TOKEN_AUTH=your_orkut_token_auth
26
- ORKUT_USERNAME=your_orkut_username
27
- ```
28
-
29
- > **Catatan:** Untuk mendapatkan tokenKey, Anda bisa membelinya di halaman [https://api.wahdx.co](https://api.wahdx.co)
30
-
31
- ## Penggunaan
32
-
33
- ### Contoh Lengkap
34
-
35
- ```javascript
36
- import QRISPayment from 'wahdx-api';
37
- import '@dotenvx/dotenvx/config';
38
- import fs from 'fs';
39
-
40
- // Konfigurasi
41
- const config = {
42
- storeName: 'AHDX STORE',
43
- defaultQrPath: 'QRIS.png', // Path ke QRIS static yang didownload di merchant orderkuota
44
- tokenKey: process.env.WAHDX_TOKENKEY,
45
- auth_token: process.env.ORKUT_TOKEN_AUTH,
46
- auth_username: process.env.ORKUT_USERNAME
47
- };
48
-
49
- // Membuat instance QRISPayment
50
- const qrisPayment = new QRISPayment(config);
51
-
52
- async function main() {
53
- try {
54
- console.log('=== TEST REALTIME QRIS PAYMENT ===\n');
55
- const randomAmount = Math.floor(Math.random() * 99) + 1; // Random 1-99
56
- const amount = 100 + randomAmount; // Base 100 + random amount
57
- const reference = 'REF' + Date.now();
58
-
59
- // Generate QR code
60
- const { qrBuffer } = await qrisPayment.generateQRFromImage(amount);
61
-
62
- // Save QR code image
63
- fs.writeFileSync(`qr-${amount}.png`, qrBuffer);
64
-
65
- console.log('=== TRANSACTION DETAILS ===');
66
- console.log('Reference:', reference);
67
- console.log('Amount:', amount);
68
- console.log('QR Image:', `qr-${amount}.png`);
69
- console.log('\nSilakan scan QR code dan lakukan pembayaran');
70
- console.log('\nMenunggu pembayaran...\n');
71
-
72
- // Check payment status with 5 minutes timeout
73
- const startTime = Date.now();
74
- const timeout = 5 * 60 * 1000;
75
-
76
- while (Date.now() - startTime < timeout) {
77
- const result = await qrisPayment.checkPayment(reference, amount);
78
-
79
- if (result.success && result.data.status === 'PAID') {
80
- console.log(' Pembayaran berhasil!');
81
- if (result.receipt) {
82
- console.log(' Bukti transaksi:', result.receipt.filePath);
83
- }
84
- return;
85
- }
86
-
87
- await new Promise(resolve => setTimeout(resolve, 10000)); // delay 10 detik
88
- console.log('Menunggu pembayaran...');
89
- }
90
-
91
- throw new Error('Timeout: Pembayaran tidak diterima dalam 5 menit');
92
-
93
- } catch (error) {
94
- console.error('Error:', error.message);
95
- }
96
- }
97
-
98
- main();
99
- ```
100
-
101
- ### API Reference
102
-
103
- #### Inisialisasi
104
-
105
- ```javascript
106
- import QRISPayment from 'wahdx-api';
107
-
108
- const config = {
109
- storeName: 'NAMA TOKO',
110
- defaultQrPath: 'path/to/qris/template.png',
111
- tokenKey: 'your_wahdx_token_key',
112
- auth_token: 'your_orkut_token_auth',
113
- auth_username: 'your_orkut_username'
114
- };
115
-
116
- const qrisPayment = new QRISPayment(config);
117
- ```
118
-
119
- #### Generate QR dari Template
120
-
121
- ```javascript
122
- const amount = 10000; // Rp 10.000
123
- // Menggunakan default QR yang sudah diatur
124
- const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount);
125
- // Atau dengan path QR spesifik
126
- // const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount, 'path/to/qris/template.png');
127
-
128
- // Simpan QR ke file
129
- fs.writeFileSync('qr-output.png', qrBuffer);
130
- ```
131
-
132
- #### Cek Status Pembayaran
133
-
134
- ```javascript
135
- const reference = 'REF' + Date.now();
136
- const amount = 10000;
137
-
138
- const result = await qrisPayment.checkPayment(reference, amount);
139
- console.log(result);
140
- /*
141
- Output jika berhasil:
142
- {
143
- success: true,
144
- data: {
145
- status: 'PAID',
146
- amount: 10000,
147
- reference: 'REF1234567890',
148
- ...
149
- },
150
- receipt: {
151
- filePath: 'path/to/receipt.pdf',
152
- ...
153
- }
154
- }
155
- */
156
- ```
157
-
158
- ## FAQ
159
-
160
- ### Q: Bagaimana cara mendapatkan tokenKey agar bisa menggunakan module ini?
161
- A: Anda bisa membeli tokenKey di halaman utama [https://api.wahdx.co](https://api.wahdx.co)
162
-
163
- ### Q: Bagaimana cara mendapatkan kredensial API OrderKuota?
164
- A: Silahkan kunjungi dokumentasi api [https://api.wahdx.co/api-docs](https://api.wahdx.co/api-docs) untuk mendapatkan token pada akun orderkuota anda.
165
-
166
- ### Q: Apakah module ini bisa digunakan di project CommonJS?
167
- A: Ya! Package ini mendukung dual module system (ESM dan CommonJS). Anda bisa menggunakan dengan dua cara:
168
-
169
- 1. Dengan ES Modules (dalam file .js dengan `type: "module"` di package.json):
170
- ```javascript
171
- import QRISPayment from 'wahdx-api';
172
-
173
- const qrisPayment = new QRISPayment(config);
174
- // gunakan qrisPayment
175
- ```
176
-
177
- 2. Dengan CommonJS (dalam file .cjs atau project tanpa `type: "module"`):
178
- ```javascript
179
- const QRISPayment = require('wahdx-api');
180
-
181
- const qrisPayment = new QRISPayment(config);
182
- // gunakan qrisPayment
183
- ```
184
-
185
- Package secara otomatis mendeteksi format yang digunakan dan menyediakan versi yang sesuai.
186
-
187
- ## Lisensi
188
-
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
+ - Kompatibel di semua platform (Windows, Linux, Mac)
12
+
13
+ ## Instalasi
14
+
15
+ ```bash
16
+ npm install wahdx-api
17
+ ```
18
+
19
+ ## Konfigurasi
20
+
21
+ Buat file `.env` di root project Anda:
22
+
23
+ ```
24
+ WAHDX_TOKENKEY=your_wahdx_token_key
25
+ ORKUT_TOKEN_AUTH=your_orkut_token_auth
26
+ ORKUT_USERNAME=your_orkut_username
27
+ ```
28
+
29
+ > **Catatan:** Untuk mendapatkan tokenKey, Anda bisa membelinya di halaman [https://api.wahdx.co](https://api.wahdx.co)
30
+
31
+ ### Opsi Konfigurasi
32
+
33
+ | Opsi | Deskripsi | Default |
34
+ |---------------------|------------------------------------------------------------------|-------------|
35
+ | storeName | Nama toko yang akan ditampilkan pada receipt | - |
36
+ | defaultQrPath | Path ke QRIS static yang didownload di merchant orderkuota | - |
37
+ | tokenKey | Token key dari WAHDX | - |
38
+ | auth_token | Token autentikasi OrderKuota | - |
39
+ | auth_username | Username OrderKuota | - |
40
+ | autoGenerateReceipt | Mengaktifkan/menonaktifkan pembuatan receipt otomatis | true |
41
+
42
+ ## Penggunaan
43
+
44
+ ### Contoh Lengkap
45
+
46
+ ```javascript
47
+ import QRISPayment from 'wahdx-api';
48
+ import '@dotenvx/dotenvx/config';
49
+ import fs from 'fs';
50
+
51
+ // Konfigurasi
52
+ const config = {
53
+ storeName: 'AHDX STORE',
54
+ defaultQrPath: 'QRIS.png', // Path ke QRIS static yang didownload di merchant orderkuota
55
+ tokenKey: process.env.WAHDX_TOKENKEY,
56
+ auth_token: process.env.ORKUT_TOKEN_AUTH,
57
+ auth_username: process.env.ORKUT_USERNAME,
58
+ autoGenerateReceipt: true // Atur false jika tidak ingin otomatis membuat receipt
59
+ };
60
+
61
+ // Membuat instance QRISPayment
62
+ const qrisPayment = new QRISPayment(config);
63
+
64
+ async function main() {
65
+ try {
66
+ console.log('=== TEST REALTIME QRIS PAYMENT ===\n');
67
+ const randomAmount = Math.floor(Math.random() * 99) + 1; // Random 1-99
68
+ const amount = 100 + randomAmount; // Base 100 + random amount
69
+ const reference = 'REF' + Date.now();
70
+
71
+ // Generate QR code
72
+ const { qrBuffer } = await qrisPayment.generateQRFromImage(amount);
73
+
74
+ // Save QR code image
75
+ fs.writeFileSync(`qr-${amount}.png`, qrBuffer);
76
+
77
+ console.log('=== TRANSACTION DETAILS ===');
78
+ console.log('Reference:', reference);
79
+ console.log('Amount:', amount);
80
+ console.log('QR Image:', `qr-${amount}.png`);
81
+ console.log('\nSilakan scan QR code dan lakukan pembayaran');
82
+ console.log('\nMenunggu pembayaran...\n');
83
+
84
+ // Check payment status with 5 minutes timeout
85
+ const startTime = Date.now();
86
+ const timeout = 5 * 60 * 1000;
87
+
88
+ while (Date.now() - startTime < timeout) {
89
+ const result = await qrisPayment.checkPayment(reference, amount);
90
+
91
+ if (result.success && result.data.status === 'PAID') {
92
+ console.log('✓ Pembayaran berhasil!');
93
+ if (result.receipt) {
94
+ console.log('✓ Bukti transaksi:', result.receipt.filePath);
95
+ }
96
+ return;
97
+ }
98
+
99
+ await new Promise(resolve => setTimeout(resolve, 10000)); // delay 10 detik
100
+ console.log('Menunggu pembayaran...');
101
+ }
102
+
103
+ throw new Error('Timeout: Pembayaran tidak diterima dalam 5 menit');
104
+
105
+ } catch (error) {
106
+ console.error('Error:', error.message);
107
+ }
108
+ }
109
+
110
+ main();
111
+ ```
112
+
113
+ ### API Reference
114
+
115
+ #### Inisialisasi
116
+
117
+ ```javascript
118
+ import QRISPayment from 'wahdx-api';
119
+
120
+ const config = {
121
+ storeName: 'NAMA TOKO',
122
+ defaultQrPath: 'path/to/qris/template.png',
123
+ tokenKey: 'your_wahdx_token_key',
124
+ auth_token: 'your_orkut_token_auth',
125
+ auth_username: 'your_orkut_username',
126
+ autoGenerateReceipt: true // Atur false jika tidak ingin otomatis membuat receipt
127
+ };
128
+
129
+ const qrisPayment = new QRISPayment(config);
130
+ ```
131
+
132
+ #### Generate QR dari Template
133
+
134
+ ```javascript
135
+ const amount = 10000; // Rp 10.000
136
+ // Menggunakan default QR yang sudah diatur
137
+ const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount);
138
+ // Atau dengan path QR spesifik
139
+ // const { qrString, qrBuffer } = await qrisPayment.generateQRFromImage(amount, 'path/to/qris/template.png');
140
+
141
+ // Simpan QR ke file
142
+ fs.writeFileSync('qr-output.png', qrBuffer);
143
+ ```
144
+
145
+ #### Cek Status Pembayaran
146
+
147
+ ```javascript
148
+ const reference = 'REF' + Date.now();
149
+ const amount = 10000;
150
+
151
+ const result = await qrisPayment.checkPayment(reference, amount);
152
+ console.log(result);
153
+ /*
154
+ Output jika berhasil:
155
+ {
156
+ success: true,
157
+ data: {
158
+ status: 'PAID',
159
+ amount: 10000,
160
+ reference: 'REF1234567890',
161
+ ...
162
+ },
163
+ receipt: {
164
+ filePath: 'path/to/receipt.pdf',
165
+ ...
166
+ }
167
+ }
168
+ */
169
+ ```
170
+
171
+ #### Generate Receipt Secara Manual
172
+
173
+ Jika Anda telah menonaktifkan `autoGenerateReceipt` dalam konfigurasi, Anda dapat menghasilkan receipt secara manual dengan metode berikut:
174
+
175
+ ```javascript
176
+ // Ketika pembayaran sudah diterima
177
+ const result = await qrisPayment.checkPayment(reference, amount);
178
+
179
+ if (result.success && result.data.status === 'PAID') {
180
+ // Generate receipt secara manual
181
+ const receipt = await qrisPayment.generateReceipt(result.data);
182
+ console.log('Receipt berhasil dibuat:', receipt.filePath);
183
+ }
184
+ ```
185
+
186
+ ## FAQ
187
+
188
+ ### Q: Bagaimana cara mendapatkan tokenKey agar bisa menggunakan module ini?
189
+ A: Anda bisa membeli tokenKey di halaman utama [https://api.wahdx.co](https://api.wahdx.co)
190
+
191
+ ### Q: Bagaimana cara mendapatkan kredensial API OrderKuota?
192
+ A: Silahkan kunjungi dokumentasi api [https://api.wahdx.co/api-docs](https://api.wahdx.co/api-docs) untuk mendapatkan token pada akun orderkuota anda.
193
+
194
+ ### Q: Apakah module ini bisa digunakan di project CommonJS?
195
+ A: Ya! Package ini mendukung dual module system (ESM dan CommonJS). Anda bisa menggunakan dengan dua cara:
196
+
197
+ 1. Dengan ES Modules (dalam file .js dengan `type: "module"` di package.json):
198
+ ```javascript
199
+ import QRISPayment from 'wahdx-api';
200
+
201
+ const qrisPayment = new QRISPayment(config);
202
+ // gunakan qrisPayment
203
+ ```
204
+
205
+ 2. Dengan CommonJS (dalam file .cjs atau project tanpa `type: "module"`):
206
+ ```javascript
207
+ const QRISPayment = require('wahdx-api');
208
+
209
+ const qrisPayment = new QRISPayment(config);
210
+ // gunakan qrisPayment
211
+ ```
212
+
213
+ Package secara otomatis mendeteksi format yang digunakan dan menyediakan versi yang sesuai.
214
+
215
+ ## Lisensi
216
+
189
217
  MIT
package/dist/index.cjs CHANGED
@@ -4,6 +4,12 @@ const ReceiptGenerator = require("./receipt-generator.cjs");
4
4
 
5
5
  class QRISPayment {
6
6
  constructor(config = {}) {
7
+ // Default config
8
+ this.config = {
9
+ autoGenerateReceipt: true, // Default-nya adalah true untuk mempertahankan kompatibilitas
10
+ ...config
11
+ };
12
+
7
13
  this.qrGenerator = new QRISGenerator(config);
8
14
  this.paymentChecker = new PaymentChecker(config);
9
15
  this.receiptGenerator = new ReceiptGenerator(config);
@@ -39,7 +45,9 @@ class QRISPayment {
39
45
 
40
46
  async checkPayment(reference, amount) {
41
47
  const result = await this.paymentChecker.checkPaymentStatus(reference, amount);
42
- if (result.success && result.data.status === 'PAID') {
48
+
49
+ // Cek apakah pembayaran sukses dan statusnya PAID, serta autoGenerateReceipt diaktifkan
50
+ if (result.success && result.data.status === 'PAID' && this.config.autoGenerateReceipt) {
43
51
  const receipt = await this.receiptGenerator.generateReceipt(result.data);
44
52
  result.receipt = receipt;
45
53
  }
package/dist/index.mjs CHANGED
@@ -4,6 +4,12 @@ import ReceiptGenerator from './receipt-generator.mjs';
4
4
 
5
5
  class QRISPayment {
6
6
  constructor(config = {}) {
7
+ // Default config
8
+ this.config = {
9
+ autoGenerateReceipt: true, // Default-nya adalah true untuk mempertahankan kompatibilitas
10
+ ...config
11
+ };
12
+
7
13
  this.qrGenerator = new QRISGenerator(config);
8
14
  this.paymentChecker = new PaymentChecker(config);
9
15
  this.receiptGenerator = new ReceiptGenerator(config);
@@ -39,7 +45,9 @@ class QRISPayment {
39
45
 
40
46
  async checkPayment(reference, amount) {
41
47
  const result = await this.paymentChecker.checkPaymentStatus(reference, amount);
42
- if (result.success && result.data.status === 'PAID') {
48
+
49
+ // Cek apakah pembayaran sukses dan statusnya PAID, serta autoGenerateReceipt diaktifkan
50
+ if (result.success && result.data.status === 'PAID' && this.config.autoGenerateReceipt) {
43
51
  const receipt = await this.receiptGenerator.generateReceipt(result.data);
44
52
  result.receipt = receipt;
45
53
  }
@@ -1,208 +1,208 @@
1
- const QRCode = require("qrcode");
2
- const fs = require("fs");
3
- const sharp = require("sharp");
4
- const jsQR = require("jsqr");
5
- const path = require("path");
6
- // CJS sudah menyediakan __filename dan __dirname secara otomatis
7
-
8
- class QRISGenerator {
9
- constructor(config = {}) {
10
- this.config = {
11
- baseQrString: config.baseQrString || '',
12
- defaultQrPath: config.defaultQrPath || 'QRIS.png'
13
- };
14
- }
15
-
16
- /**
17
- * Membaca QR code dari gambar dan mengekstrak baseQrString
18
- * @param {string} imagePath - Path relatif atau absolut ke file gambar QR
19
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
20
- */
21
- async readQRFromImage(imagePath) {
22
- try {
23
- // Konversi path relatif ke absolut jika diperlukan
24
- const absolutePath = path.isAbsolute(imagePath)
25
- ? imagePath
26
- : path.resolve(process.cwd(), imagePath);
27
-
28
- // Periksa apakah file ada
29
- if (!fs.existsSync(absolutePath)) {
30
- throw new Error(`File tidak ditemukan: ${absolutePath}`);
31
- }
32
-
33
- // Baca gambar menggunakan Sharp
34
- const image = sharp(absolutePath);
35
- const metadata = await image.metadata();
36
- const { width, height } = metadata;
37
-
38
- // Konversi ke raw pixel data
39
- const rawData = await image
40
- .raw()
41
- .toBuffer();
42
-
43
- // Format data untuk jsQR
44
- const imageData = new Uint8ClampedArray(width * height * 4);
45
-
46
- for (let i = 0; i < rawData.length; i += 3) { // RGB format
47
- const pixelIndex = (i / 3) * 4; // Convert RGB index to RGBA index
48
- imageData[pixelIndex] = rawData[i]; // R
49
- imageData[pixelIndex + 1] = rawData[i + 1]; // G
50
- imageData[pixelIndex + 2] = rawData[i + 2]; // B
51
- imageData[pixelIndex + 3] = 255; // A (full opacity)
52
- }
53
-
54
- // Dekode QR code menggunakan jsQR
55
- const code = jsQR(imageData, width, height);
56
-
57
- if (code) {
58
- // Simpan baseQrString yang dibaca ke config
59
- this.config.baseQrString = code.data;
60
- return code.data;
61
- } else {
62
- throw new Error('QR code tidak terdeteksi dalam gambar');
63
- }
64
- } catch (error) {
65
- console.error('Error saat membaca QR code:', error);
66
- throw error;
67
- }
68
- }
69
-
70
- /**
71
- * Membaca QR code dari file default (biasanya QRIS.png)
72
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
73
- */
74
- async readDefaultQR() {
75
- try {
76
- // Path default ke file QRIS.png
77
- const defaultPath = path.resolve(process.cwd(), this.config.defaultQrPath);
78
- return await this.readQRFromImage(defaultPath);
79
- } catch (error) {
80
- console.error(`Error saat membaca gambar default (${this.config.defaultQrPath}):`, error);
81
- throw error;
82
- }
83
- }
84
-
85
- /**
86
- * Generate QR code sebagai PNG buffer
87
- * @param {string} qrString - String QR code yang akan digenerate
88
- * @returns {Promise<Buffer>} - Buffer PNG dari QR code
89
- */
90
- async generateQRImage(qrString) {
91
- try {
92
- if (!qrString) {
93
- throw new Error('qrString tidak boleh kosong');
94
- }
95
-
96
- // Gunakan QRCode.toBuffer langsung tanpa canvas
97
- const qrBuffer = await QRCode.toBuffer(qrString, {
98
- errorCorrectionLevel: 'H',
99
- margin: 2,
100
- width: 500,
101
- color: {
102
- dark: '#000000',
103
- light: '#ffffff'
104
- }
105
- });
106
-
107
- return qrBuffer;
108
- } catch (error) {
109
- throw new Error('Gagal generate QR: ' + error.message);
110
- }
111
- }
112
-
113
- /**
114
- * Membaca QR dari file kemudian generate QR baru dengan nominal
115
- * @param {number} amount - Nominal pembayaran
116
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
117
- * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
118
- */
119
- async generateQRFromImage(amount, qrImagePath = null) {
120
- try {
121
- // Jika path gambar disediakan, baca dari path tersebut
122
- if (qrImagePath) {
123
- await this.readQRFromImage(qrImagePath);
124
- }
125
- // Jika tidak ada path dan baseQrString kosong, gunakan default
126
- else if (!this.config.baseQrString) {
127
- await this.readDefaultQR();
128
- }
129
-
130
- // Generate QR string dengan nominal
131
- const qrString = this.generateQrString(amount);
132
-
133
- // Generate QR image
134
- const qrBuffer = await this.generateQRImage(qrString);
135
-
136
- return {
137
- qrString,
138
- qrBuffer
139
- };
140
- } catch (error) {
141
- throw new Error('Gagal generate QR dari gambar: ' + error.message);
142
- }
143
- }
144
-
145
- /**
146
- * Generate QR dengan nominal tertentu
147
- * @param {number} amount - Nominal pembayaran
148
- * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
149
- */
150
- async generateQR(amount) {
151
- const qrString = this.generateQrString(amount);
152
- const qrBuffer = await this.generateQRImage(qrString);
153
- return {
154
- qrString,
155
- qrBuffer
156
- };
157
- }
158
-
159
- generateQrString(amount) {
160
- try {
161
- if (!amount || amount <= 0) {
162
- throw new Error('Nominal harus lebih besar dari 0');
163
- }
164
-
165
- if (!this.config.baseQrString) {
166
- throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
167
- }
168
-
169
- if (!this.config.baseQrString.includes("5802ID")) {
170
- throw new Error("Format QRIS tidak valid");
171
- }
172
-
173
- const finalAmount = Math.floor(amount);
174
- const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
175
- const nominalStr = finalAmount.toString();
176
- const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
177
- const insertPosition = qrisBase.indexOf("5802ID");
178
- const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
179
- const checksum = this.calculateCRC16(qrisWithNominal);
180
-
181
- return qrisWithNominal + checksum;
182
- } catch (error) {
183
- throw new Error('Gagal generate string QRIS: ' + error.message);
184
- }
185
- }
186
-
187
- calculateCRC16(str) {
188
- try {
189
- if (!str) {
190
- throw new Error('String tidak boleh kosong');
191
- }
192
-
193
- let crc = 0xFFFF;
194
- for (let i = 0; i < str.length; i++) {
195
- crc ^= str.charCodeAt(i) << 8;
196
- for (let j = 0; j < 8; j++) {
197
- crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
198
- }
199
- crc &= 0xFFFF;
200
- }
201
- return crc.toString(16).toUpperCase().padStart(4, '0');
202
- } catch (error) {
203
- throw new Error('Gagal kalkulasi CRC16: ' + error.message);
204
- }
205
- }
206
- }
207
-
1
+ const QRCode = require("qrcode");
2
+ const fs = require("fs");
3
+ const sharp = require("sharp");
4
+ const jsQR = require("jsqr");
5
+ const path = require("path");
6
+ // CJS sudah menyediakan __filename dan __dirname secara otomatis
7
+
8
+ class QRISGenerator {
9
+ constructor(config = {}) {
10
+ this.config = {
11
+ baseQrString: config.baseQrString || '',
12
+ defaultQrPath: config.defaultQrPath || 'QRIS.png'
13
+ };
14
+ }
15
+
16
+ /**
17
+ * Membaca QR code dari gambar dan mengekstrak baseQrString
18
+ * @param {string} imagePath - Path relatif atau absolut ke file gambar QR
19
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
20
+ */
21
+ async readQRFromImage(imagePath) {
22
+ try {
23
+ // Konversi path relatif ke absolut jika diperlukan
24
+ const absolutePath = path.isAbsolute(imagePath)
25
+ ? imagePath
26
+ : path.resolve(process.cwd(), imagePath);
27
+
28
+ // Periksa apakah file ada
29
+ if (!fs.existsSync(absolutePath)) {
30
+ throw new Error(`File tidak ditemukan: ${absolutePath}`);
31
+ }
32
+
33
+ // Baca gambar menggunakan Sharp
34
+ const image = sharp(absolutePath);
35
+ const metadata = await image.metadata();
36
+ const { width, height } = metadata;
37
+
38
+ // Konversi ke raw pixel data
39
+ const rawData = await image
40
+ .raw()
41
+ .toBuffer();
42
+
43
+ // Format data untuk jsQR
44
+ const imageData = new Uint8ClampedArray(width * height * 4);
45
+
46
+ for (let i = 0; i < rawData.length; i += 3) { // RGB format
47
+ const pixelIndex = (i / 3) * 4; // Convert RGB index to RGBA index
48
+ imageData[pixelIndex] = rawData[i]; // R
49
+ imageData[pixelIndex + 1] = rawData[i + 1]; // G
50
+ imageData[pixelIndex + 2] = rawData[i + 2]; // B
51
+ imageData[pixelIndex + 3] = 255; // A (full opacity)
52
+ }
53
+
54
+ // Dekode QR code menggunakan jsQR
55
+ const code = jsQR(imageData, width, height);
56
+
57
+ if (code) {
58
+ // Simpan baseQrString yang dibaca ke config
59
+ this.config.baseQrString = code.data;
60
+ return code.data;
61
+ } else {
62
+ throw new Error('QR code tidak terdeteksi dalam gambar');
63
+ }
64
+ } catch (error) {
65
+ console.error('Error saat membaca QR code:', error);
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Membaca QR code dari file default (biasanya QRIS.png)
72
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
73
+ */
74
+ async readDefaultQR() {
75
+ try {
76
+ // Path default ke file QRIS.png
77
+ const defaultPath = path.resolve(process.cwd(), this.config.defaultQrPath);
78
+ return await this.readQRFromImage(defaultPath);
79
+ } catch (error) {
80
+ console.error(`Error saat membaca gambar default (${this.config.defaultQrPath}):`, error);
81
+ throw error;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Generate QR code sebagai PNG buffer
87
+ * @param {string} qrString - String QR code yang akan digenerate
88
+ * @returns {Promise<Buffer>} - Buffer PNG dari QR code
89
+ */
90
+ async generateQRImage(qrString) {
91
+ try {
92
+ if (!qrString) {
93
+ throw new Error('qrString tidak boleh kosong');
94
+ }
95
+
96
+ // Gunakan QRCode.toBuffer langsung tanpa canvas
97
+ const qrBuffer = await QRCode.toBuffer(qrString, {
98
+ errorCorrectionLevel: 'H',
99
+ margin: 2,
100
+ width: 500,
101
+ color: {
102
+ dark: '#000000',
103
+ light: '#ffffff'
104
+ }
105
+ });
106
+
107
+ return qrBuffer;
108
+ } catch (error) {
109
+ throw new Error('Gagal generate QR: ' + error.message);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Membaca QR dari file kemudian generate QR baru dengan nominal
115
+ * @param {number} amount - Nominal pembayaran
116
+ * @param {string} qrImagePath - Path ke gambar QR (opsional)
117
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
118
+ */
119
+ async generateQRFromImage(amount, qrImagePath = null) {
120
+ try {
121
+ // Jika path gambar disediakan, baca dari path tersebut
122
+ if (qrImagePath) {
123
+ await this.readQRFromImage(qrImagePath);
124
+ }
125
+ // Jika tidak ada path dan baseQrString kosong, gunakan default
126
+ else if (!this.config.baseQrString) {
127
+ await this.readDefaultQR();
128
+ }
129
+
130
+ // Generate QR string dengan nominal
131
+ const qrString = this.generateQrString(amount);
132
+
133
+ // Generate QR image
134
+ const qrBuffer = await this.generateQRImage(qrString);
135
+
136
+ return {
137
+ qrString,
138
+ qrBuffer
139
+ };
140
+ } catch (error) {
141
+ throw new Error('Gagal generate QR dari gambar: ' + error.message);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Generate QR dengan nominal tertentu
147
+ * @param {number} amount - Nominal pembayaran
148
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
149
+ */
150
+ async generateQR(amount) {
151
+ const qrString = this.generateQrString(amount);
152
+ const qrBuffer = await this.generateQRImage(qrString);
153
+ return {
154
+ qrString,
155
+ qrBuffer
156
+ };
157
+ }
158
+
159
+ generateQrString(amount) {
160
+ try {
161
+ if (!amount || amount <= 0) {
162
+ throw new Error('Nominal harus lebih besar dari 0');
163
+ }
164
+
165
+ if (!this.config.baseQrString) {
166
+ throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
167
+ }
168
+
169
+ if (!this.config.baseQrString.includes("5802ID")) {
170
+ throw new Error("Format QRIS tidak valid");
171
+ }
172
+
173
+ const finalAmount = Math.floor(amount);
174
+ const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
175
+ const nominalStr = finalAmount.toString();
176
+ const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
177
+ const insertPosition = qrisBase.indexOf("5802ID");
178
+ const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
179
+ const checksum = this.calculateCRC16(qrisWithNominal);
180
+
181
+ return qrisWithNominal + checksum;
182
+ } catch (error) {
183
+ throw new Error('Gagal generate string QRIS: ' + error.message);
184
+ }
185
+ }
186
+
187
+ calculateCRC16(str) {
188
+ try {
189
+ if (!str) {
190
+ throw new Error('String tidak boleh kosong');
191
+ }
192
+
193
+ let crc = 0xFFFF;
194
+ for (let i = 0; i < str.length; i++) {
195
+ crc ^= str.charCodeAt(i) << 8;
196
+ for (let j = 0; j < 8; j++) {
197
+ crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
198
+ }
199
+ crc &= 0xFFFF;
200
+ }
201
+ return crc.toString(16).toUpperCase().padStart(4, '0');
202
+ } catch (error) {
203
+ throw new Error('Gagal kalkulasi CRC16: ' + error.message);
204
+ }
205
+ }
206
+ }
207
+
208
208
  module.exports = QRISGenerator;
@@ -1,211 +1,211 @@
1
- import QRCode from 'qrcode';
2
- import fs from 'fs';
3
- import sharp from 'sharp';
4
- import jsQR from 'jsqr';
5
- import path from 'path';
6
- import { fileURLToPath } from 'url';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
-
11
- class QRISGenerator {
12
- constructor(config = {}) {
13
- this.config = {
14
- baseQrString: config.baseQrString || '',
15
- defaultQrPath: config.defaultQrPath || 'QRIS.png'
16
- };
17
- }
18
-
19
- /**
20
- * Membaca QR code dari gambar dan mengekstrak baseQrString
21
- * @param {string} imagePath - Path relatif atau absolut ke file gambar QR
22
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
23
- */
24
- async readQRFromImage(imagePath) {
25
- try {
26
- // Konversi path relatif ke absolut jika diperlukan
27
- const absolutePath = path.isAbsolute(imagePath)
28
- ? imagePath
29
- : path.resolve(process.cwd(), imagePath);
30
-
31
- // Periksa apakah file ada
32
- if (!fs.existsSync(absolutePath)) {
33
- throw new Error(`File tidak ditemukan: ${absolutePath}`);
34
- }
35
-
36
- // Baca gambar menggunakan Sharp
37
- const image = sharp(absolutePath);
38
- const metadata = await image.metadata();
39
- const { width, height } = metadata;
40
-
41
- // Konversi ke raw pixel data
42
- const rawData = await image
43
- .raw()
44
- .toBuffer();
45
-
46
- // Format data untuk jsQR
47
- const imageData = new Uint8ClampedArray(width * height * 4);
48
-
49
- for (let i = 0; i < rawData.length; i += 3) { // RGB format
50
- const pixelIndex = (i / 3) * 4; // Convert RGB index to RGBA index
51
- imageData[pixelIndex] = rawData[i]; // R
52
- imageData[pixelIndex + 1] = rawData[i + 1]; // G
53
- imageData[pixelIndex + 2] = rawData[i + 2]; // B
54
- imageData[pixelIndex + 3] = 255; // A (full opacity)
55
- }
56
-
57
- // Dekode QR code menggunakan jsQR
58
- const code = jsQR(imageData, width, height);
59
-
60
- if (code) {
61
- // Simpan baseQrString yang dibaca ke config
62
- this.config.baseQrString = code.data;
63
- return code.data;
64
- } else {
65
- throw new Error('QR code tidak terdeteksi dalam gambar');
66
- }
67
- } catch (error) {
68
- console.error('Error saat membaca QR code:', error);
69
- throw error;
70
- }
71
- }
72
-
73
- /**
74
- * Membaca QR code dari file default (biasanya QRIS.png)
75
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
76
- */
77
- async readDefaultQR() {
78
- try {
79
- // Path default ke file QRIS.png
80
- const defaultPath = path.resolve(process.cwd(), this.config.defaultQrPath);
81
- return await this.readQRFromImage(defaultPath);
82
- } catch (error) {
83
- console.error(`Error saat membaca gambar default (${this.config.defaultQrPath}):`, error);
84
- throw error;
85
- }
86
- }
87
-
88
- /**
89
- * Generate QR code sebagai PNG buffer
90
- * @param {string} qrString - String QR code yang akan digenerate
91
- * @returns {Promise<Buffer>} - Buffer PNG dari QR code
92
- */
93
- async generateQRImage(qrString) {
94
- try {
95
- if (!qrString) {
96
- throw new Error('qrString tidak boleh kosong');
97
- }
98
-
99
- // Gunakan QRCode.toBuffer langsung tanpa canvas
100
- const qrBuffer = await QRCode.toBuffer(qrString, {
101
- errorCorrectionLevel: 'H',
102
- margin: 2,
103
- width: 500,
104
- color: {
105
- dark: '#000000',
106
- light: '#ffffff'
107
- }
108
- });
109
-
110
- return qrBuffer;
111
- } catch (error) {
112
- throw new Error('Gagal generate QR: ' + error.message);
113
- }
114
- }
115
-
116
- /**
117
- * Membaca QR dari file kemudian generate QR baru dengan nominal
118
- * @param {number} amount - Nominal pembayaran
119
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
120
- * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
121
- */
122
- async generateQRFromImage(amount, qrImagePath = null) {
123
- try {
124
- // Jika path gambar disediakan, baca dari path tersebut
125
- if (qrImagePath) {
126
- await this.readQRFromImage(qrImagePath);
127
- }
128
- // Jika tidak ada path dan baseQrString kosong, gunakan default
129
- else if (!this.config.baseQrString) {
130
- await this.readDefaultQR();
131
- }
132
-
133
- // Generate QR string dengan nominal
134
- const qrString = this.generateQrString(amount);
135
-
136
- // Generate QR image
137
- const qrBuffer = await this.generateQRImage(qrString);
138
-
139
- return {
140
- qrString,
141
- qrBuffer
142
- };
143
- } catch (error) {
144
- throw new Error('Gagal generate QR dari gambar: ' + error.message);
145
- }
146
- }
147
-
148
- /**
149
- * Generate QR dengan nominal tertentu
150
- * @param {number} amount - Nominal pembayaran
151
- * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
152
- */
153
- async generateQR(amount) {
154
- const qrString = this.generateQrString(amount);
155
- const qrBuffer = await this.generateQRImage(qrString);
156
- return {
157
- qrString,
158
- qrBuffer
159
- };
160
- }
161
-
162
- generateQrString(amount) {
163
- try {
164
- if (!amount || amount <= 0) {
165
- throw new Error('Nominal harus lebih besar dari 0');
166
- }
167
-
168
- if (!this.config.baseQrString) {
169
- throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
170
- }
171
-
172
- if (!this.config.baseQrString.includes("5802ID")) {
173
- throw new Error("Format QRIS tidak valid");
174
- }
175
-
176
- const finalAmount = Math.floor(amount);
177
- const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
178
- const nominalStr = finalAmount.toString();
179
- const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
180
- const insertPosition = qrisBase.indexOf("5802ID");
181
- const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
182
- const checksum = this.calculateCRC16(qrisWithNominal);
183
-
184
- return qrisWithNominal + checksum;
185
- } catch (error) {
186
- throw new Error('Gagal generate string QRIS: ' + error.message);
187
- }
188
- }
189
-
190
- calculateCRC16(str) {
191
- try {
192
- if (!str) {
193
- throw new Error('String tidak boleh kosong');
194
- }
195
-
196
- let crc = 0xFFFF;
197
- for (let i = 0; i < str.length; i++) {
198
- crc ^= str.charCodeAt(i) << 8;
199
- for (let j = 0; j < 8; j++) {
200
- crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
201
- }
202
- crc &= 0xFFFF;
203
- }
204
- return crc.toString(16).toUpperCase().padStart(4, '0');
205
- } catch (error) {
206
- throw new Error('Gagal kalkulasi CRC16: ' + error.message);
207
- }
208
- }
209
- }
210
-
1
+ import QRCode from 'qrcode';
2
+ import fs from 'fs';
3
+ import sharp from 'sharp';
4
+ import jsQR from 'jsqr';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ class QRISGenerator {
12
+ constructor(config = {}) {
13
+ this.config = {
14
+ baseQrString: config.baseQrString || '',
15
+ defaultQrPath: config.defaultQrPath || 'QRIS.png'
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Membaca QR code dari gambar dan mengekstrak baseQrString
21
+ * @param {string} imagePath - Path relatif atau absolut ke file gambar QR
22
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
23
+ */
24
+ async readQRFromImage(imagePath) {
25
+ try {
26
+ // Konversi path relatif ke absolut jika diperlukan
27
+ const absolutePath = path.isAbsolute(imagePath)
28
+ ? imagePath
29
+ : path.resolve(process.cwd(), imagePath);
30
+
31
+ // Periksa apakah file ada
32
+ if (!fs.existsSync(absolutePath)) {
33
+ throw new Error(`File tidak ditemukan: ${absolutePath}`);
34
+ }
35
+
36
+ // Baca gambar menggunakan Sharp
37
+ const image = sharp(absolutePath);
38
+ const metadata = await image.metadata();
39
+ const { width, height } = metadata;
40
+
41
+ // Konversi ke raw pixel data
42
+ const rawData = await image
43
+ .raw()
44
+ .toBuffer();
45
+
46
+ // Format data untuk jsQR
47
+ const imageData = new Uint8ClampedArray(width * height * 4);
48
+
49
+ for (let i = 0; i < rawData.length; i += 3) { // RGB format
50
+ const pixelIndex = (i / 3) * 4; // Convert RGB index to RGBA index
51
+ imageData[pixelIndex] = rawData[i]; // R
52
+ imageData[pixelIndex + 1] = rawData[i + 1]; // G
53
+ imageData[pixelIndex + 2] = rawData[i + 2]; // B
54
+ imageData[pixelIndex + 3] = 255; // A (full opacity)
55
+ }
56
+
57
+ // Dekode QR code menggunakan jsQR
58
+ const code = jsQR(imageData, width, height);
59
+
60
+ if (code) {
61
+ // Simpan baseQrString yang dibaca ke config
62
+ this.config.baseQrString = code.data;
63
+ return code.data;
64
+ } else {
65
+ throw new Error('QR code tidak terdeteksi dalam gambar');
66
+ }
67
+ } catch (error) {
68
+ console.error('Error saat membaca QR code:', error);
69
+ throw error;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Membaca QR code dari file default (biasanya QRIS.png)
75
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
76
+ */
77
+ async readDefaultQR() {
78
+ try {
79
+ // Path default ke file QRIS.png
80
+ const defaultPath = path.resolve(process.cwd(), this.config.defaultQrPath);
81
+ return await this.readQRFromImage(defaultPath);
82
+ } catch (error) {
83
+ console.error(`Error saat membaca gambar default (${this.config.defaultQrPath}):`, error);
84
+ throw error;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Generate QR code sebagai PNG buffer
90
+ * @param {string} qrString - String QR code yang akan digenerate
91
+ * @returns {Promise<Buffer>} - Buffer PNG dari QR code
92
+ */
93
+ async generateQRImage(qrString) {
94
+ try {
95
+ if (!qrString) {
96
+ throw new Error('qrString tidak boleh kosong');
97
+ }
98
+
99
+ // Gunakan QRCode.toBuffer langsung tanpa canvas
100
+ const qrBuffer = await QRCode.toBuffer(qrString, {
101
+ errorCorrectionLevel: 'H',
102
+ margin: 2,
103
+ width: 500,
104
+ color: {
105
+ dark: '#000000',
106
+ light: '#ffffff'
107
+ }
108
+ });
109
+
110
+ return qrBuffer;
111
+ } catch (error) {
112
+ throw new Error('Gagal generate QR: ' + error.message);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Membaca QR dari file kemudian generate QR baru dengan nominal
118
+ * @param {number} amount - Nominal pembayaran
119
+ * @param {string} qrImagePath - Path ke gambar QR (opsional)
120
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
121
+ */
122
+ async generateQRFromImage(amount, qrImagePath = null) {
123
+ try {
124
+ // Jika path gambar disediakan, baca dari path tersebut
125
+ if (qrImagePath) {
126
+ await this.readQRFromImage(qrImagePath);
127
+ }
128
+ // Jika tidak ada path dan baseQrString kosong, gunakan default
129
+ else if (!this.config.baseQrString) {
130
+ await this.readDefaultQR();
131
+ }
132
+
133
+ // Generate QR string dengan nominal
134
+ const qrString = this.generateQrString(amount);
135
+
136
+ // Generate QR image
137
+ const qrBuffer = await this.generateQRImage(qrString);
138
+
139
+ return {
140
+ qrString,
141
+ qrBuffer
142
+ };
143
+ } catch (error) {
144
+ throw new Error('Gagal generate QR dari gambar: ' + error.message);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Generate QR dengan nominal tertentu
150
+ * @param {number} amount - Nominal pembayaran
151
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
152
+ */
153
+ async generateQR(amount) {
154
+ const qrString = this.generateQrString(amount);
155
+ const qrBuffer = await this.generateQRImage(qrString);
156
+ return {
157
+ qrString,
158
+ qrBuffer
159
+ };
160
+ }
161
+
162
+ generateQrString(amount) {
163
+ try {
164
+ if (!amount || amount <= 0) {
165
+ throw new Error('Nominal harus lebih besar dari 0');
166
+ }
167
+
168
+ if (!this.config.baseQrString) {
169
+ throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
170
+ }
171
+
172
+ if (!this.config.baseQrString.includes("5802ID")) {
173
+ throw new Error("Format QRIS tidak valid");
174
+ }
175
+
176
+ const finalAmount = Math.floor(amount);
177
+ const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
178
+ const nominalStr = finalAmount.toString();
179
+ const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
180
+ const insertPosition = qrisBase.indexOf("5802ID");
181
+ const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
182
+ const checksum = this.calculateCRC16(qrisWithNominal);
183
+
184
+ return qrisWithNominal + checksum;
185
+ } catch (error) {
186
+ throw new Error('Gagal generate string QRIS: ' + error.message);
187
+ }
188
+ }
189
+
190
+ calculateCRC16(str) {
191
+ try {
192
+ if (!str) {
193
+ throw new Error('String tidak boleh kosong');
194
+ }
195
+
196
+ let crc = 0xFFFF;
197
+ for (let i = 0; i < str.length; i++) {
198
+ crc ^= str.charCodeAt(i) << 8;
199
+ for (let j = 0; j < 8; j++) {
200
+ crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
201
+ }
202
+ crc &= 0xFFFF;
203
+ }
204
+ return crc.toString(16).toUpperCase().padStart(4, '0');
205
+ } catch (error) {
206
+ throw new Error('Gagal kalkulasi CRC16: ' + error.message);
207
+ }
208
+ }
209
+ }
210
+
211
211
  export default QRISGenerator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wahdx-api",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://api.wahdx.co",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",