wahdx-api 1.0.1 → 1.0.3

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
@@ -8,6 +8,7 @@ Package untuk generate QRIS dan cek payment status secara realtime dengan API Or
8
8
  - Generate kode QR dengan nominal tertentu
9
9
  - Cek status pembayaran secara realtime
10
10
  - Generate bukti transaksi (receipt)
11
+ - Kompatibel di semua platform (Windows, Linux, Mac)
11
12
 
12
13
  ## Instalasi
13
14
 
@@ -162,6 +163,27 @@ A: Anda bisa membeli tokenKey di halaman utama [https://api.wahdx.co](https://ap
162
163
  ### Q: Bagaimana cara mendapatkan kredensial API OrderKuota?
163
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.
164
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
+
165
187
  ## Lisensi
166
188
 
167
189
  MIT
@@ -1,6 +1,6 @@
1
- import QRISGenerator from './qr-generator.js';
2
- import PaymentChecker from './payment-checker.js';
3
- import ReceiptGenerator from './receipt-generator.js';
1
+ const QRISGenerator = require("./qr-generator.cjs");
2
+ const PaymentChecker = require("./payment-checker.cjs");
3
+ const ReceiptGenerator = require("./receipt-generator.cjs");
4
4
 
5
5
  class QRISPayment {
6
6
  constructor(config = {}) {
@@ -24,12 +24,7 @@ class QRISPayment {
24
24
  * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
25
25
  */
26
26
  async generateQR(amount) {
27
- const qrString = this.qrGenerator.generateQrString(amount);
28
- const qrBuffer = await this.qrGenerator.generateQRWithLogo(qrString);
29
- return {
30
- qrString,
31
- qrBuffer
32
- };
27
+ return await this.qrGenerator.generateQR(amount);
33
28
  }
34
29
 
35
30
  /**
@@ -58,4 +53,4 @@ class QRISPayment {
58
53
  }
59
54
 
60
55
  // Mengekspor kelas utama
61
- export default QRISPayment;
56
+ module.exports = QRISPayment;
package/dist/index.mjs ADDED
@@ -0,0 +1,56 @@
1
+ import QRISGenerator from './qr-generator.mjs';
2
+ import PaymentChecker from './payment-checker.mjs';
3
+ import ReceiptGenerator from './receipt-generator.mjs';
4
+
5
+ class QRISPayment {
6
+ constructor(config = {}) {
7
+ this.qrGenerator = new QRISGenerator(config);
8
+ this.paymentChecker = new PaymentChecker(config);
9
+ this.receiptGenerator = new ReceiptGenerator(config);
10
+ }
11
+
12
+ /**
13
+ * Membaca QR code dari file gambar dan mengekstrak baseQrString
14
+ * @param {string} imagePath - Path ke file gambar QR
15
+ * @returns {Promise<string>} - Promise yang menghasilkan baseQrString
16
+ */
17
+ async readQRCode(imagePath) {
18
+ return await this.qrGenerator.readQRFromImage(imagePath);
19
+ }
20
+
21
+ /**
22
+ * Generate QR dengan nominal tertentu
23
+ * @param {number} amount - Nominal pembayaran
24
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
25
+ */
26
+ async generateQR(amount) {
27
+ return await this.qrGenerator.generateQR(amount);
28
+ }
29
+
30
+ /**
31
+ * Membaca QR dari file gambar dan langsung menghasilkan QR baru dengan nominal
32
+ * @param {number} amount - Nominal pembayaran
33
+ * @param {string} qrImagePath - Path ke gambar QR (opsional)
34
+ * @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
35
+ */
36
+ async generateQRFromImage(amount, qrImagePath = null) {
37
+ return await this.qrGenerator.generateQRFromImage(amount, qrImagePath);
38
+ }
39
+
40
+ async checkPayment(reference, amount) {
41
+ const result = await this.paymentChecker.checkPaymentStatus(reference, amount);
42
+ if (result.success && result.data.status === 'PAID') {
43
+ const receipt = await this.receiptGenerator.generateReceipt(result.data);
44
+ result.receipt = receipt;
45
+ }
46
+
47
+ return result;
48
+ }
49
+
50
+ async generateReceipt(transactionData) {
51
+ return await this.receiptGenerator.generateReceipt(transactionData);
52
+ }
53
+ }
54
+
55
+ // Mengekspor kelas utama
56
+ export default QRISPayment;
@@ -0,0 +1,90 @@
1
+ const axios = require("axios");
2
+
3
+ class PaymentChecker {
4
+ constructor(config) {
5
+ if (!config.tokenKey || !config.auth_username || !config.auth_token) {
6
+ throw new Error('tokenKey, auth_username, dan auth_token harus diisi');
7
+ }
8
+ this.config = {
9
+ tokenKey: config.tokenKey,
10
+ auth_username: config.auth_username,
11
+ auth_token: config.auth_token
12
+ };
13
+ }
14
+
15
+ async checkPaymentStatus(reference, amount) {
16
+ try {
17
+ if (!reference || !amount || amount <= 0) {
18
+ throw new Error('Reference dan amount harus diisi dengan benar');
19
+ }
20
+
21
+ const response = await axios.post(
22
+ 'https://api.wahdx.co/api/mutasi-orkut-v2',
23
+ {
24
+ username_orkut: this.config.auth_username,
25
+ token_orkut: this.config.auth_token
26
+ },
27
+ {
28
+ headers: {
29
+ 'tokenKey': this.config.tokenKey,
30
+ 'Content-Type': 'application/json'
31
+ }
32
+ }
33
+ );
34
+
35
+ if (!response.data || !response.data.status || !response.data.data) {
36
+ throw new Error('Response tidak valid dari server');
37
+ }
38
+
39
+ const transactions = response.data.data;
40
+
41
+ const matchingTransactions = transactions.filter(tx => {
42
+ const txAmount = parseInt(tx.amount);
43
+ const txDate = new Date(tx.date);
44
+ const now = new Date();
45
+ const timeDiff = now - txDate;
46
+
47
+ return txAmount === amount &&
48
+ tx.qris === "static" &&
49
+ tx.type === "CR" &&
50
+ timeDiff <= 5 * 60 * 1000;
51
+ });
52
+
53
+ if (matchingTransactions.length > 0) {
54
+ const latestTransaction = matchingTransactions.reduce((latest, current) => {
55
+ const currentDate = new Date(current.date);
56
+ const latestDate = new Date(latest.date);
57
+ return currentDate > latestDate ? current : latest;
58
+ });
59
+
60
+ return {
61
+ success: true,
62
+ data: {
63
+ status: 'PAID',
64
+ amount: parseInt(latestTransaction.amount),
65
+ reference: latestTransaction.issuer_reff,
66
+ date: latestTransaction.date,
67
+ brand_name: latestTransaction.brand_name,
68
+ buyer_reff: latestTransaction.buyer_reff
69
+ }
70
+ };
71
+ }
72
+
73
+ return {
74
+ success: true,
75
+ data: {
76
+ status: 'UNPAID',
77
+ amount: amount,
78
+ reference: reference
79
+ }
80
+ };
81
+ } catch (error) {
82
+ return {
83
+ success: false,
84
+ error: 'Gagal cek status pembayaran: ' + error.message
85
+ };
86
+ }
87
+ }
88
+ }
89
+
90
+ module.exports = PaymentChecker;
@@ -0,0 +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
+
208
+ module.exports = QRISGenerator;
@@ -1,5 +1,4 @@
1
1
  import QRCode from 'qrcode';
2
- import { createCanvas, loadImage } from 'canvas';
3
2
  import fs from 'fs';
4
3
  import sharp from 'sharp';
5
4
  import jsQR from 'jsqr';
@@ -13,7 +12,6 @@ class QRISGenerator {
13
12
  constructor(config = {}) {
14
13
  this.config = {
15
14
  baseQrString: config.baseQrString || '',
16
- logoPath: config.logoPath || null,
17
15
  defaultQrPath: config.defaultQrPath || 'QRIS.png'
18
16
  };
19
17
  }
@@ -87,14 +85,19 @@ class QRISGenerator {
87
85
  }
88
86
  }
89
87
 
90
- async generateQRWithLogo(qrString) {
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) {
91
94
  try {
92
95
  if (!qrString) {
93
96
  throw new Error('qrString tidak boleh kosong');
94
97
  }
95
- const canvas = createCanvas(500, 500);
96
- const ctx = canvas.getContext('2d');
97
- await QRCode.toCanvas(canvas, qrString, {
98
+
99
+ // Gunakan QRCode.toBuffer langsung tanpa canvas
100
+ const qrBuffer = await QRCode.toBuffer(qrString, {
98
101
  errorCorrectionLevel: 'H',
99
102
  margin: 2,
100
103
  width: 500,
@@ -103,16 +106,8 @@ class QRISGenerator {
103
106
  light: '#ffffff'
104
107
  }
105
108
  });
106
- if (this.config.logoPath && fs.existsSync(this.config.logoPath)) {
107
- const logo = await loadImage(this.config.logoPath);
108
- const logoSize = canvas.width * 0.2;
109
- const logoPosition = (canvas.width - logoSize) / 2;
110
-
111
- ctx.fillStyle = '#FFFFFF';
112
- ctx.fillRect(logoPosition - 5, logoPosition - 5, logoSize + 10, logoSize + 10);
113
- ctx.drawImage(logo, logoPosition, logoPosition, logoSize, logoSize);
114
- }
115
- return canvas.toBuffer('image/png');
109
+
110
+ return qrBuffer;
116
111
  } catch (error) {
117
112
  throw new Error('Gagal generate QR: ' + error.message);
118
113
  }
@@ -139,7 +134,7 @@ class QRISGenerator {
139
134
  const qrString = this.generateQrString(amount);
140
135
 
141
136
  // Generate QR image
142
- const qrBuffer = await this.generateQRWithLogo(qrString);
137
+ const qrBuffer = await this.generateQRImage(qrString);
143
138
 
144
139
  return {
145
140
  qrString,
@@ -150,6 +145,20 @@ class QRISGenerator {
150
145
  }
151
146
  }
152
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
+
153
162
  generateQrString(amount) {
154
163
  try {
155
164
  if (!amount || amount <= 0) {
@@ -0,0 +1,130 @@
1
+ const PDFDocument = require("pdfkit");
2
+ const fs = require("fs");
3
+ const moment = require("moment");
4
+ const path = require("path");
5
+
6
+ class ReceiptGenerator {
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+
11
+ async generateReceipt(transactionData) {
12
+ return new Promise((resolve, reject) => {
13
+ try {
14
+ const doc = new PDFDocument({
15
+ size: [300, 450],
16
+ margin: 20,
17
+ layout: 'portrait'
18
+ });
19
+
20
+ const fileName = `receipt_${transactionData.reference}_${Date.now()}.pdf`;
21
+ const filePath = `receipts/${fileName}`;
22
+ if (!fs.existsSync('receipts')) {
23
+ fs.mkdirSync('receipts');
24
+ }
25
+ const writeStream = fs.createWriteStream(filePath);
26
+ doc.pipe(writeStream);
27
+
28
+ if (this.config.logoPath && fs.existsSync(this.config.logoPath)) {
29
+ const logoWidth = 40;
30
+ const logoHeight = 40;
31
+ const logoX = 20 + (260 - logoWidth) / 2;
32
+ const logoY = 10;
33
+ doc.image(this.config.logoPath, logoX, logoY, { width: logoWidth, height: logoHeight });
34
+ doc.y = logoY + logoHeight + 4;
35
+ } else {
36
+ doc.moveDown(0.5);
37
+ }
38
+
39
+ doc.fontSize(14)
40
+ .fillColor('black')
41
+ .font('Helvetica-Bold');
42
+ const headerText = 'QRIS PAYMENT RECEIPT';
43
+ const headerWidth = doc.widthOfString(headerText);
44
+ doc.text(headerText, 20 + (260 - headerWidth) / 2, doc.y, { width: headerWidth });
45
+
46
+ doc.fontSize(9)
47
+ .font('Helvetica');
48
+ const merchantText = this.config.storeName || 'STORE NAME';
49
+ const merchantWidth = doc.widthOfString(merchantText);
50
+ doc.text(merchantText, 20 + (260 - merchantWidth) / 2, doc.y, { width: merchantWidth });
51
+
52
+ doc.moveDown(0.3);
53
+ const lineY = doc.y;
54
+ doc.moveTo(20, lineY)
55
+ .lineTo(280, lineY)
56
+ .strokeColor('#888').stroke();
57
+
58
+ doc.moveDown(0.5);
59
+ doc.fontSize(9)
60
+ .font('Helvetica-Bold');
61
+ const detailsTitle = 'TRANSACTION DETAILS';
62
+ const detailsTitleWidth = doc.widthOfString(detailsTitle);
63
+ doc.text(detailsTitle, 20 + (260 - detailsTitleWidth) / 2, doc.y, { width: detailsTitleWidth });
64
+ doc.moveDown(0.2);
65
+
66
+ const formattedDate = moment(transactionData.date).format('DD/MM/YYYY HH:mm:ss');
67
+ const details = [
68
+ ['Reference', transactionData.reference],
69
+ ['Date', formattedDate],
70
+ ['Amount', `Rp ${transactionData.amount.toLocaleString('id-ID')}`],
71
+ ['Status', transactionData.status],
72
+ ['Payment Method', transactionData.brand_name || '-'],
73
+ ['Buyer Reference', transactionData.buyer_reff || '-']
74
+ ];
75
+
76
+ const tableWidth = 240;
77
+ const startX = (300 - tableWidth) / 2;
78
+ const labelWidth = 100;
79
+ const valueWidth = 130;
80
+ let y = doc.y + 5;
81
+
82
+ details.forEach(([label, value], idx) => {
83
+ doc.font('Helvetica-Bold')
84
+ .fontSize(9)
85
+ .fillColor('black')
86
+ .text(label, startX, y, { width: labelWidth, align: 'left' });
87
+ doc.font('Helvetica')
88
+ .fontSize(9)
89
+ .fillColor('black')
90
+ .text(value, startX + labelWidth, y, { width: valueWidth, align: 'right' });
91
+
92
+ y += 14;
93
+ doc.moveTo(startX, y)
94
+ .lineTo(startX + labelWidth + valueWidth, y)
95
+ .strokeColor('#e0e0e0').stroke();
96
+ y += 2;
97
+ });
98
+
99
+ y += 6;
100
+ doc.moveTo(20, y)
101
+ .lineTo(280, y)
102
+ .strokeColor('#888').stroke();
103
+
104
+ y += 12;
105
+ doc.text('Thank you for your payment!', 0, y, { align: 'right', width: 260 });
106
+ y += 12;
107
+ doc.text(`Receipt No: ${transactionData.reference}`, 0, y, { align: 'right', width: 260 });
108
+
109
+ doc.end();
110
+
111
+ writeStream.on('finish', () => {
112
+ resolve({
113
+ success: true,
114
+ filePath: filePath,
115
+ fileName: fileName
116
+ });
117
+ });
118
+
119
+ writeStream.on('error', (error) => {
120
+ reject(error);
121
+ });
122
+
123
+ } catch (error) {
124
+ reject(error);
125
+ }
126
+ });
127
+ }
128
+ }
129
+
130
+ module.exports = ReceiptGenerator;
package/package.json CHANGED
@@ -1,8 +1,15 @@
1
1
  {
2
2
  "name": "wahdx-api",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Package untuk generate QRIS dan cek payment status secara realtime dengan API OrderKuota dari https://api.wahdx.co",
5
- "main": "dist/index.js",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.cjs",
10
+ "import": "./dist/index.mjs"
11
+ }
12
+ },
6
13
  "author": "wahdalo",
7
14
  "license": "MIT",
8
15
  "type": "module",
@@ -17,7 +24,8 @@
17
24
  ],
18
25
  "scripts": {
19
26
  "test": "node tes.js",
20
- "start": "node dist/index.js",
27
+ "test:cjs": "node tes-cjs.cjs",
28
+ "start": "node dist/index.mjs",
21
29
  "build": "node scripts/build.js",
22
30
  "prepublishOnly": "npm run build",
23
31
  "pack": "npm pack --dry-run"
@@ -30,7 +38,6 @@
30
38
  "dependencies": {
31
39
  "@dotenvx/dotenvx": "^1.47.5",
32
40
  "axios": "^1.10.0",
33
- "canvas": "^3.1.2",
34
41
  "jsqr": "^1.4.0",
35
42
  "moment": "^2.29.4",
36
43
  "pdfkit": "^0.13.0",