wahdx-api 1.0.4 → 2.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 CHANGED
@@ -2,10 +2,18 @@
2
2
 
3
3
  Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari [https://api.wahdx.co](https://api.wahdx.co).
4
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`.
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)
@@ -33,7 +41,7 @@ ORKUT_USERNAME=your_orkut_username
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
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) {
@@ -40,11 +41,12 @@ class PaymentChecker {
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;
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');
46
48
 
47
- return txAmount === amount &&
49
+ return txAmount === amount &&
48
50
  tx.qris === "static" &&
49
51
  tx.type === "CR" &&
50
52
  timeDiff <= 5 * 60 * 1000;
@@ -52,9 +54,9 @@ class PaymentChecker {
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 {
@@ -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) {
@@ -40,11 +41,12 @@ class PaymentChecker {
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;
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');
46
48
 
47
- return txAmount === amount &&
49
+ return txAmount === amount &&
48
50
  tx.qris === "static" &&
49
51
  tx.type === "CR" &&
50
52
  timeDiff <= 5 * 60 * 1000;
@@ -52,9 +54,9 @@ class PaymentChecker {
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 {
@@ -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
@@ -111,26 +40,56 @@ 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');
128
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://api.wahdx.co/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');
88
+ }
89
+
90
+ const qrString = response.data.qrString;
129
91
 
130
- // Generate QR string dengan nominal
131
- const qrString = this.generateQrString(amount);
132
-
133
- // Generate QR image
92
+ // Generate QR image dari qrString yang didapat dari API
134
93
  const qrBuffer = await this.generateQRImage(qrString);
135
94
 
136
95
  return {
@@ -138,24 +97,10 @@ class QRISGenerator {
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) {
@@ -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
@@ -114,26 +40,56 @@ 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');
131
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://api.wahdx.co/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');
88
+ }
89
+
90
+ const qrString = response.data.qrString;
132
91
 
133
- // Generate QR string dengan nominal
134
- const qrString = this.generateQrString(amount);
135
-
136
- // Generate QR image
92
+ // Generate QR image dari qrString yang didapat dari API
137
93
  const qrBuffer = await this.generateQRImage(qrString);
138
94
 
139
95
  return {
@@ -141,24 +97,10 @@ class QRISGenerator {
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) {
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "wahdx-api",
3
- "version": "1.0.4",
3
+ "version": "2.0.0",
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",
@@ -38,11 +38,9 @@
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"