wahdx-api 1.0.4 → 2.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 wahdalo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,11 +1,19 @@
1
1
  # wahdx-api
2
2
 
3
- Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari [https://api.wahdx.co](https://api.wahdx.co).
3
+ Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari [https://eqris.com](https://eqris.com).
4
+
5
+ > **⚠️ PENTING: Perubahan di Versi 2.0.0**
6
+ >
7
+ > Pada versi 2.0.0, kami melakukan perubahan besar pada package ini:
8
+ > - Fitur `generateQRFromImage` telah **DIHAPUS** sepenuhnya
9
+ > - Digantikan dengan `generateQRFromAPI` yang lebih valid dan reliable
10
+ > - Dependency `sharp` dan `jsqr` telah dihapus untuk membuat package lebih ringan
11
+ >
12
+ > Jika Anda mengupgrade dari versi sebelumnya, harap perbarikan kode Anda untuk menggunakan `generateQRFromAPI` alih-alih `generateQRFromImage`.
4
13
 
5
14
  ## Fitur
6
15
 
7
- - Membaca dan ekstrak kode QR dari gambar
8
- - Generate kode QR dengan nominal tertentu
16
+ - Generate kode QR dengan nominal tertentu (lokal atau via API)
9
17
  - Cek status pembayaran secara realtime
10
18
  - Generate bukti transaksi (receipt)
11
19
  - Kompatibel di semua platform (Windows, Linux, Mac)
@@ -26,14 +34,14 @@ ORKUT_TOKEN_AUTH=your_orkut_token_auth
26
34
  ORKUT_USERNAME=your_orkut_username
27
35
  ```
28
36
 
29
- > **Catatan:** Untuk mendapatkan tokenKey, Anda bisa membelinya di halaman [https://api.wahdx.co](https://api.wahdx.co)
37
+ > **Catatan:** Untuk mendapatkan tokenKey, Anda bisa membelinya di halaman [https://eqris.com](https://eqris.com)
30
38
 
31
39
  ### Opsi Konfigurasi
32
40
 
33
41
  | Opsi | Deskripsi | Default |
34
42
  |---------------------|------------------------------------------------------------------|-------------|
35
43
  | storeName | Nama toko yang akan ditampilkan pada receipt | - |
36
- | defaultQrPath | Path ke QRIS static yang didownload di merchant orderkuota | - |
44
+ | baseQrString | Base QR string untuk generate QR secara lokal | - |
37
45
  | tokenKey | Token key dari WAHDX | - |
38
46
  | auth_token | Token autentikasi OrderKuota | - |
39
47
  | auth_username | Username OrderKuota | - |
@@ -51,10 +59,10 @@ import fs from 'fs';
51
59
  // Konfigurasi
52
60
  const config = {
53
61
  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,
62
+ baseQrString: 'base-qr-string-anda', // Diperlukan untuk generateQR secara lokal
63
+ tokenKey: process.env.WAHDX_TOKENKEY, // Diperlukan untuk generateQRFromAPI
64
+ auth_token: process.env.ORKUT_TOKEN_AUTH, // Diperlukan untuk generateQRFromAPI
65
+ auth_username: process.env.ORKUT_USERNAME, // Diperlukan untuk generateQRFromAPI
58
66
  autoGenerateReceipt: true // Atur false jika tidak ingin otomatis membuat receipt
59
67
  };
60
68
 
@@ -68,8 +76,8 @@ async function main() {
68
76
  const amount = 100 + randomAmount; // Base 100 + random amount
69
77
  const reference = 'REF' + Date.now();
70
78
 
71
- // Generate QR code
72
- const { qrBuffer } = await qrisPayment.generateQRFromImage(amount);
79
+ // Generate QR code menggunakan API OrderKuota
80
+ const { qrBuffer } = await qrisPayment.generateQRFromAPI(amount);
73
81
 
74
82
  // Save QR code image
75
83
  fs.writeFileSync(`qr-${amount}.png`, qrBuffer);
@@ -119,29 +127,44 @@ import QRISPayment from 'wahdx-api';
119
127
 
120
128
  const config = {
121
129
  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',
130
+ baseQrString: 'base-qr-string-anda', // Diperlukan untuk generateQR secara lokal
131
+ tokenKey: 'your_wahdx_token_key', // Diperlukan untuk generateQRFromAPI
132
+ auth_token: 'your_orkut_token_auth', // Diperlukan untuk generateQRFromAPI
133
+ auth_username: 'your_orkut_username', // Diperlukan untuk generateQRFromAPI
126
134
  autoGenerateReceipt: true // Atur false jika tidak ingin otomatis membuat receipt
127
135
  };
128
136
 
129
137
  const qrisPayment = new QRISPayment(config);
130
138
  ```
131
139
 
132
- #### Generate QR dari Template
140
+ #### Generate QR
141
+
142
+ Ada dua metode untuk generate QR:
143
+
144
+ ##### 1. Generate QR secara lokal
145
+
146
+ ```javascript
147
+ const amount = 10000; // Rp 10.000
148
+ // Generate QR secara lokal menggunakan baseQrString
149
+ const { qrString, qrBuffer } = await qrisPayment.generateQR(amount);
150
+
151
+ // Simpan QR ke file
152
+ fs.writeFileSync('qr-output.png', qrBuffer);
153
+ ```
154
+
155
+ ##### 2. Generate QR menggunakan API OrderKuota (Lebih direkomendasikan)
133
156
 
134
157
  ```javascript
135
158
  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');
159
+ // Generate QR menggunakan API OrderKuota
160
+ const { qrString, qrBuffer } = await qrisPayment.generateQRFromAPI(amount);
140
161
 
141
162
  // Simpan QR ke file
142
163
  fs.writeFileSync('qr-output.png', qrBuffer);
143
164
  ```
144
165
 
166
+ > **Catatan:** Metode `generateQRFromAPI` lebih direkomendasikan karena QR code dihasilkan langsung dari server OrderKuota, sehingga lebih valid dan reliable.
167
+
145
168
  #### Cek Status Pembayaran
146
169
 
147
170
  ```javascript
@@ -186,10 +209,10 @@ if (result.success && result.data.status === 'PAID') {
186
209
  ## FAQ
187
210
 
188
211
  ### 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)
212
+ A: Anda bisa membeli tokenKey di halaman utama [https://eqris.com](https://eqris.com)
190
213
 
191
214
  ### 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.
215
+ A: Silahkan kunjungi dokumentasi api [https://eqris.com/api-docs](https://eqris.com/api-docs) untuk mendapatkan token pada akun orderkuota anda.
193
216
 
194
217
  ### Q: Apakah module ini bisa digunakan di project CommonJS?
195
218
  A: Ya! Package ini mendukung dual module system (ESM dan CommonJS). Anda bisa menggunakan dengan dua cara:
package/dist/index.cjs CHANGED
@@ -15,15 +15,6 @@ class QRISPayment {
15
15
  this.receiptGenerator = new ReceiptGenerator(config);
16
16
  }
17
17
 
18
- /**
19
- * Membaca QR code dari file gambar dan mengekstrak baseQrString
20
- * @param {string} imagePath - Path ke file gambar QR
21
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
22
- */
23
- async readQRCode(imagePath) {
24
- return await this.qrGenerator.readQRFromImage(imagePath);
25
- }
26
-
27
18
  /**
28
19
  * Generate QR dengan nominal tertentu
29
20
  * @param {number} amount - Nominal pembayaran
@@ -34,13 +25,12 @@ class QRISPayment {
34
25
  }
35
26
 
36
27
  /**
37
- * Membaca QR dari file gambar dan langsung menghasilkan QR baru dengan nominal
28
+ * Generate QR dengan nominal tertentu menggunakan API OrderKuota
38
29
  * @param {number} amount - Nominal pembayaran
39
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
40
30
  * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
41
31
  */
42
- async generateQRFromImage(amount, qrImagePath = null) {
43
- return await this.qrGenerator.generateQRFromImage(amount, qrImagePath);
32
+ async generateQRFromAPI(amount) {
33
+ return await this.qrGenerator.generateQRFromAPI(amount);
44
34
  }
45
35
 
46
36
  async checkPayment(reference, amount) {
package/dist/index.mjs CHANGED
@@ -15,15 +15,6 @@ class QRISPayment {
15
15
  this.receiptGenerator = new ReceiptGenerator(config);
16
16
  }
17
17
 
18
- /**
19
- * Membaca QR code dari file gambar dan mengekstrak baseQrString
20
- * @param {string} imagePath - Path ke file gambar QR
21
- * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
22
- */
23
- async readQRCode(imagePath) {
24
- return await this.qrGenerator.readQRFromImage(imagePath);
25
- }
26
-
27
18
  /**
28
19
  * Generate QR dengan nominal tertentu
29
20
  * @param {number} amount - Nominal pembayaran
@@ -34,13 +25,12 @@ class QRISPayment {
34
25
  }
35
26
 
36
27
  /**
37
- * Membaca QR dari file gambar dan langsung menghasilkan QR baru dengan nominal
28
+ * Generate QR dengan nominal tertentu menggunakan API OrderKuota
38
29
  * @param {number} amount - Nominal pembayaran
39
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
40
30
  * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
41
31
  */
42
- async generateQRFromImage(amount, qrImagePath = null) {
43
- return await this.qrGenerator.generateQRFromImage(amount, qrImagePath);
32
+ async generateQRFromAPI(amount) {
33
+ return await this.qrGenerator.generateQRFromAPI(amount);
44
34
  }
45
35
 
46
36
  async checkPayment(reference, amount) {
@@ -1,4 +1,5 @@
1
1
  const axios = require("axios");
2
+ const moment = require("moment-timezone");
2
3
 
3
4
  class PaymentChecker {
4
5
  constructor(config) {
@@ -11,15 +12,15 @@ class PaymentChecker {
11
12
  auth_token: config.auth_token
12
13
  };
13
14
  }
14
-
15
+
15
16
  async checkPaymentStatus(reference, amount) {
16
17
  try {
17
18
  if (!reference || !amount || amount <= 0) {
18
19
  throw new Error('Reference dan amount harus diisi dengan benar');
19
20
  }
20
-
21
+
21
22
  const response = await axios.post(
22
- 'https://api.wahdx.co/api/mutasi-orkut-v2',
23
+ 'https://eqris.com/api/mutasi-orkut-v2',
23
24
  {
24
25
  username_orkut: this.config.auth_username,
25
26
  token_orkut: this.config.auth_token
@@ -31,32 +32,33 @@ class PaymentChecker {
31
32
  }
32
33
  }
33
34
  );
34
-
35
+
35
36
  if (!response.data || !response.data.status || !response.data.data) {
36
37
  throw new Error('Response tidak valid dari server');
37
38
  }
38
-
39
+
39
40
  const transactions = response.data.data;
40
-
41
+
41
42
  const matchingTransactions = transactions.filter(tx => {
42
43
  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;
44
+ // Parse tanggal dengan timezone Jakarta
45
+ const txDate = moment.tz(tx.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
46
+ const now = moment().tz("Asia/Jakarta");
47
+ const timeDiff = now.diff(txDate, 'milliseconds');
48
+
49
+ return txAmount === amount &&
50
+ tx.qris === "static" &&
51
+ tx.type === "CR" &&
52
+ timeDiff <= 5 * 60 * 1000;
51
53
  });
52
54
 
53
55
  if (matchingTransactions.length > 0) {
54
56
  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;
57
+ const currentDate = moment.tz(current.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
58
+ const latestDate = moment.tz(latest.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
59
+ return currentDate.isAfter(latestDate) ? current : latest;
58
60
  });
59
-
61
+
60
62
  return {
61
63
  success: true,
62
64
  data: {
@@ -69,7 +71,7 @@ class PaymentChecker {
69
71
  }
70
72
  };
71
73
  }
72
-
74
+
73
75
  return {
74
76
  success: true,
75
77
  data: {
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios';
2
+ import moment from 'moment-timezone';
2
3
 
3
4
  class PaymentChecker {
4
5
  constructor(config) {
@@ -11,15 +12,15 @@ class PaymentChecker {
11
12
  auth_token: config.auth_token
12
13
  };
13
14
  }
14
-
15
+
15
16
  async checkPaymentStatus(reference, amount) {
16
17
  try {
17
18
  if (!reference || !amount || amount <= 0) {
18
19
  throw new Error('Reference dan amount harus diisi dengan benar');
19
20
  }
20
-
21
+
21
22
  const response = await axios.post(
22
- 'https://api.wahdx.co/api/mutasi-orkut-v2',
23
+ 'https://eqris.com/api/mutasi-orkut-v2',
23
24
  {
24
25
  username_orkut: this.config.auth_username,
25
26
  token_orkut: this.config.auth_token
@@ -31,32 +32,33 @@ class PaymentChecker {
31
32
  }
32
33
  }
33
34
  );
34
-
35
+
35
36
  if (!response.data || !response.data.status || !response.data.data) {
36
37
  throw new Error('Response tidak valid dari server');
37
38
  }
38
-
39
+
39
40
  const transactions = response.data.data;
40
-
41
+
41
42
  const matchingTransactions = transactions.filter(tx => {
42
43
  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;
44
+ // Parse tanggal dengan timezone Jakarta
45
+ const txDate = moment.tz(tx.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
46
+ const now = moment().tz("Asia/Jakarta");
47
+ const timeDiff = now.diff(txDate, 'milliseconds');
48
+
49
+ return txAmount === amount &&
50
+ tx.qris === "static" &&
51
+ tx.type === "CR" &&
52
+ timeDiff <= 5 * 60 * 1000;
51
53
  });
52
54
 
53
55
  if (matchingTransactions.length > 0) {
54
56
  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;
57
+ const currentDate = moment.tz(current.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
58
+ const latestDate = moment.tz(latest.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta');
59
+ return currentDate.isAfter(latestDate) ? current : latest;
58
60
  });
59
-
61
+
60
62
  return {
61
63
  success: true,
62
64
  data: {
@@ -69,7 +71,7 @@ class PaymentChecker {
69
71
  }
70
72
  };
71
73
  }
72
-
74
+
73
75
  return {
74
76
  success: true,
75
77
  data: {
@@ -1,87 +1,16 @@
1
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
2
+ const axios = require("axios");
7
3
 
8
4
  class QRISGenerator {
9
5
  constructor(config = {}) {
10
6
  this.config = {
11
7
  baseQrString: config.baseQrString || '',
12
- defaultQrPath: config.defaultQrPath || 'QRIS.png'
8
+ tokenKey: config.tokenKey,
9
+ auth_username: config.auth_username,
10
+ auth_token: config.auth_token
13
11
  };
14
12
  }
15
13
 
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
14
  /**
86
15
  * Generate QR code sebagai PNG buffer
87
16
  * @param {string} qrString - String QR code yang akan digenerate
@@ -92,7 +21,7 @@ class QRISGenerator {
92
21
  if (!qrString) {
93
22
  throw new Error('qrString tidak boleh kosong');
94
23
  }
95
-
24
+
96
25
  // Gunakan QRCode.toBuffer langsung tanpa canvas
97
26
  const qrBuffer = await QRCode.toBuffer(qrString, {
98
27
  errorCorrectionLevel: 'H',
@@ -103,7 +32,7 @@ class QRISGenerator {
103
32
  light: '#ffffff'
104
33
  }
105
34
  });
106
-
35
+
107
36
  return qrBuffer;
108
37
  } catch (error) {
109
38
  throw new Error('Gagal generate QR: ' + error.message);
@@ -111,65 +40,81 @@ class QRISGenerator {
111
40
  }
112
41
 
113
42
  /**
114
- * Membaca QR dari file kemudian generate QR baru dengan nominal
43
+ * Generate QR dengan nominal tertentu
44
+ * @param {number} amount - Nominal pembayaran
45
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
46
+ */
47
+ async generateQR(amount) {
48
+ const qrString = this.generateQrString(amount);
49
+ const qrBuffer = await this.generateQRImage(qrString);
50
+ return {
51
+ qrString,
52
+ qrBuffer
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Generate QR dengan nominal tertentu menggunakan API OrderKuota
115
58
  * @param {number} amount - Nominal pembayaran
116
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
117
59
  * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
118
60
  */
119
- async generateQRFromImage(amount, qrImagePath = null) {
61
+ async generateQRFromAPI(amount) {
120
62
  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();
63
+ if (!amount || amount <= 0) {
64
+ throw new Error('Nominal harus lebih besar dari 0');
65
+ }
66
+
67
+ if (!this.config.tokenKey || !this.config.auth_username || !this.config.auth_token) {
68
+ throw new Error('tokenKey, auth_username, dan auth_token harus diisi');
69
+ }
70
+
71
+ const response = await axios.post(
72
+ 'https://eqris.com/api/qr-orkut-v2',
73
+ {
74
+ username_orkut: this.config.auth_username,
75
+ token_orkut: this.config.auth_token,
76
+ nominal: amount.toString()
77
+ },
78
+ {
79
+ headers: {
80
+ 'tokenKey': this.config.tokenKey,
81
+ 'Content-Type': 'application/json'
82
+ }
83
+ }
84
+ );
85
+
86
+ if (!response.data || !response.data.status || !response.data.qrString) {
87
+ throw new Error('Response tidak valid dari server');
128
88
  }
129
-
130
- // Generate QR string dengan nominal
131
- const qrString = this.generateQrString(amount);
132
-
133
- // Generate QR image
89
+
90
+ const qrString = response.data.qrString;
91
+
92
+ // Generate QR image dari qrString yang didapat dari API
134
93
  const qrBuffer = await this.generateQRImage(qrString);
135
-
94
+
136
95
  return {
137
96
  qrString,
138
97
  qrBuffer
139
98
  };
140
99
  } catch (error) {
141
- throw new Error('Gagal generate QR dari gambar: ' + error.message);
100
+ throw new Error('Gagal generate QR dari API: ' + error.message);
142
101
  }
143
102
  }
144
103
 
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
104
  generateQrString(amount) {
160
105
  try {
161
106
  if (!amount || amount <= 0) {
162
107
  throw new Error('Nominal harus lebih besar dari 0');
163
108
  }
164
-
109
+
165
110
  if (!this.config.baseQrString) {
166
111
  throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
167
112
  }
168
-
113
+
169
114
  if (!this.config.baseQrString.includes("5802ID")) {
170
115
  throw new Error("Format QRIS tidak valid");
171
116
  }
172
-
117
+
173
118
  const finalAmount = Math.floor(amount);
174
119
  const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
175
120
  const nominalStr = finalAmount.toString();
@@ -177,7 +122,7 @@ class QRISGenerator {
177
122
  const insertPosition = qrisBase.indexOf("5802ID");
178
123
  const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
179
124
  const checksum = this.calculateCRC16(qrisWithNominal);
180
-
125
+
181
126
  return qrisWithNominal + checksum;
182
127
  } catch (error) {
183
128
  throw new Error('Gagal generate string QRIS: ' + error.message);
@@ -1,90 +1,16 @@
1
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);
2
+ import axios from 'axios';
10
3
 
11
4
  class QRISGenerator {
12
5
  constructor(config = {}) {
13
6
  this.config = {
14
7
  baseQrString: config.baseQrString || '',
15
- defaultQrPath: config.defaultQrPath || 'QRIS.png'
8
+ tokenKey: config.tokenKey,
9
+ auth_username: config.auth_username,
10
+ auth_token: config.auth_token
16
11
  };
17
12
  }
18
13
 
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
14
  /**
89
15
  * Generate QR code sebagai PNG buffer
90
16
  * @param {string} qrString - String QR code yang akan digenerate
@@ -95,7 +21,7 @@ class QRISGenerator {
95
21
  if (!qrString) {
96
22
  throw new Error('qrString tidak boleh kosong');
97
23
  }
98
-
24
+
99
25
  // Gunakan QRCode.toBuffer langsung tanpa canvas
100
26
  const qrBuffer = await QRCode.toBuffer(qrString, {
101
27
  errorCorrectionLevel: 'H',
@@ -106,7 +32,7 @@ class QRISGenerator {
106
32
  light: '#ffffff'
107
33
  }
108
34
  });
109
-
35
+
110
36
  return qrBuffer;
111
37
  } catch (error) {
112
38
  throw new Error('Gagal generate QR: ' + error.message);
@@ -114,65 +40,81 @@ class QRISGenerator {
114
40
  }
115
41
 
116
42
  /**
117
- * Membaca QR dari file kemudian generate QR baru dengan nominal
43
+ * Generate QR dengan nominal tertentu
118
44
  * @param {number} amount - Nominal pembayaran
119
- * @param {string} qrImagePath - Path ke gambar QR (opsional)
120
45
  * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
121
46
  */
122
- async generateQRFromImage(amount, qrImagePath = null) {
47
+ async generateQR(amount) {
48
+ const qrString = this.generateQrString(amount);
49
+ const qrBuffer = await this.generateQRImage(qrString);
50
+ return {
51
+ qrString,
52
+ qrBuffer
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Generate QR dengan nominal tertentu menggunakan API OrderKuota
58
+ * @param {number} amount - Nominal pembayaran
59
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
60
+ */
61
+ async generateQRFromAPI(amount) {
123
62
  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();
63
+ if (!amount || amount <= 0) {
64
+ throw new Error('Nominal harus lebih besar dari 0');
65
+ }
66
+
67
+ if (!this.config.tokenKey || !this.config.auth_username || !this.config.auth_token) {
68
+ throw new Error('tokenKey, auth_username, dan auth_token harus diisi');
69
+ }
70
+
71
+ const response = await axios.post(
72
+ 'https://eqris.com/api/qr-orkut-v2',
73
+ {
74
+ username_orkut: this.config.auth_username,
75
+ token_orkut: this.config.auth_token,
76
+ nominal: amount.toString()
77
+ },
78
+ {
79
+ headers: {
80
+ 'tokenKey': this.config.tokenKey,
81
+ 'Content-Type': 'application/json'
82
+ }
83
+ }
84
+ );
85
+
86
+ if (!response.data || !response.data.status || !response.data.qrString) {
87
+ throw new Error('Response tidak valid dari server');
131
88
  }
132
-
133
- // Generate QR string dengan nominal
134
- const qrString = this.generateQrString(amount);
135
-
136
- // Generate QR image
89
+
90
+ const qrString = response.data.qrString;
91
+
92
+ // Generate QR image dari qrString yang didapat dari API
137
93
  const qrBuffer = await this.generateQRImage(qrString);
138
-
94
+
139
95
  return {
140
96
  qrString,
141
97
  qrBuffer
142
98
  };
143
99
  } catch (error) {
144
- throw new Error('Gagal generate QR dari gambar: ' + error.message);
100
+ throw new Error('Gagal generate QR dari API: ' + error.message);
145
101
  }
146
102
  }
147
103
 
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
104
  generateQrString(amount) {
163
105
  try {
164
106
  if (!amount || amount <= 0) {
165
107
  throw new Error('Nominal harus lebih besar dari 0');
166
108
  }
167
-
109
+
168
110
  if (!this.config.baseQrString) {
169
111
  throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
170
112
  }
171
-
113
+
172
114
  if (!this.config.baseQrString.includes("5802ID")) {
173
115
  throw new Error("Format QRIS tidak valid");
174
116
  }
175
-
117
+
176
118
  const finalAmount = Math.floor(amount);
177
119
  const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
178
120
  const nominalStr = finalAmount.toString();
@@ -180,7 +122,7 @@ class QRISGenerator {
180
122
  const insertPosition = qrisBase.indexOf("5802ID");
181
123
  const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
182
124
  const checksum = this.calculateCRC16(qrisWithNominal);
183
-
125
+
184
126
  return qrisWithNominal + checksum;
185
127
  } catch (error) {
186
128
  throw new Error('Gagal generate string QRIS: ' + error.message);
@@ -1,6 +1,6 @@
1
1
  const PDFDocument = require("pdfkit");
2
2
  const fs = require("fs");
3
- const moment = require("moment");
3
+ const moment = require("moment-timezone");
4
4
  const path = require("path");
5
5
 
6
6
  class ReceiptGenerator {
@@ -63,7 +63,7 @@ class ReceiptGenerator {
63
63
  doc.text(detailsTitle, 20 + (260 - detailsTitleWidth) / 2, doc.y, { width: detailsTitleWidth });
64
64
  doc.moveDown(0.2);
65
65
 
66
- const formattedDate = moment(transactionData.date).format('DD/MM/YYYY HH:mm:ss');
66
+ const formattedDate = moment.tz(transactionData.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta').format('DD/MM/YYYY HH:mm:ss');
67
67
  const details = [
68
68
  ['Reference', transactionData.reference],
69
69
  ['Date', formattedDate],
@@ -1,6 +1,6 @@
1
1
  import PDFDocument from 'pdfkit';
2
2
  import fs from 'fs';
3
- import moment from 'moment';
3
+ import moment from 'moment-timezone';
4
4
  import path from 'path';
5
5
 
6
6
  class ReceiptGenerator {
@@ -63,7 +63,7 @@ class ReceiptGenerator {
63
63
  doc.text(detailsTitle, 20 + (260 - detailsTitleWidth) / 2, doc.y, { width: detailsTitleWidth });
64
64
  doc.moveDown(0.2);
65
65
 
66
- const formattedDate = moment(transactionData.date).format('DD/MM/YYYY HH:mm:ss');
66
+ const formattedDate = moment.tz(transactionData.date, 'YYYY-MM-DD HH:mm', 'Asia/Jakarta').format('DD/MM/YYYY HH:mm:ss');
67
67
  const details = [
68
68
  ['Reference', transactionData.reference],
69
69
  ['Date', formattedDate],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wahdx-api",
3
- "version": "1.0.4",
4
- "description": "Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://api.wahdx.co",
3
+ "version": "2.0.1",
4
+ "description": "Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://eqris.com",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
7
7
  "exports": {
@@ -38,13 +38,11 @@
38
38
  "dependencies": {
39
39
  "@dotenvx/dotenvx": "^1.47.5",
40
40
  "axios": "^1.10.0",
41
- "jsqr": "^1.4.0",
42
- "moment": "^2.29.4",
41
+ "moment-timezone": "^0.6.0",
43
42
  "pdfkit": "^0.13.0",
44
- "qrcode": "^1.5.4",
45
- "sharp": "^0.32.6"
43
+ "qrcode": "^1.5.4"
46
44
  },
47
45
  "engines": {
48
46
  "node": ">=20.18.3"
49
47
  }
50
- }
48
+ }