wahdx-api 1.0.3 → 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 +239 -188
- package/dist/index.cjs +12 -14
- package/dist/index.mjs +12 -14
- package/dist/payment-checker.cjs +9 -7
- package/dist/payment-checker.mjs +9 -7
- package/dist/qr-generator.cjs +152 -207
- package/dist/qr-generator.mjs +152 -210
- package/dist/receipt-generator.cjs +2 -2
- package/dist/receipt-generator.mjs +2 -2
- package/package.json +3 -5
package/dist/qr-generator.mjs
CHANGED
|
@@ -1,211 +1,153 @@
|
|
|
1
|
-
import QRCode from 'qrcode';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 axios from 'axios';
|
|
3
|
+
|
|
4
|
+
class QRISGenerator {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.config = {
|
|
7
|
+
baseQrString: config.baseQrString || '',
|
|
8
|
+
tokenKey: config.tokenKey,
|
|
9
|
+
auth_username: config.auth_username,
|
|
10
|
+
auth_token: config.auth_token
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate QR code sebagai PNG buffer
|
|
16
|
+
* @param {string} qrString - String QR code yang akan digenerate
|
|
17
|
+
* @returns {Promise<Buffer>} - Buffer PNG dari QR code
|
|
18
|
+
*/
|
|
19
|
+
async generateQRImage(qrString) {
|
|
20
|
+
try {
|
|
21
|
+
if (!qrString) {
|
|
22
|
+
throw new Error('qrString tidak boleh kosong');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Gunakan QRCode.toBuffer langsung tanpa canvas
|
|
26
|
+
const qrBuffer = await QRCode.toBuffer(qrString, {
|
|
27
|
+
errorCorrectionLevel: 'H',
|
|
28
|
+
margin: 2,
|
|
29
|
+
width: 500,
|
|
30
|
+
color: {
|
|
31
|
+
dark: '#000000',
|
|
32
|
+
light: '#ffffff'
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return qrBuffer;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw new Error('Gagal generate QR: ' + error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
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
|
|
58
|
+
* @param {number} amount - Nominal pembayaran
|
|
59
|
+
* @returns {Promise<{qrString: string, qrBuffer: Buffer}>}
|
|
60
|
+
*/
|
|
61
|
+
async generateQRFromAPI(amount) {
|
|
62
|
+
try {
|
|
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://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;
|
|
91
|
+
|
|
92
|
+
// Generate QR image dari qrString yang didapat dari API
|
|
93
|
+
const qrBuffer = await this.generateQRImage(qrString);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
qrString,
|
|
97
|
+
qrBuffer
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new Error('Gagal generate QR dari API: ' + error.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
generateQrString(amount) {
|
|
105
|
+
try {
|
|
106
|
+
if (!amount || amount <= 0) {
|
|
107
|
+
throw new Error('Nominal harus lebih besar dari 0');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!this.config.baseQrString) {
|
|
111
|
+
throw new Error('BaseQrString tidak tersedia. Gunakan readQRFromImage terlebih dahulu atau berikan baseQrString pada config.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!this.config.baseQrString.includes("5802ID")) {
|
|
115
|
+
throw new Error("Format QRIS tidak valid");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const finalAmount = Math.floor(amount);
|
|
119
|
+
const qrisBase = this.config.baseQrString.slice(0, -4).replace("010211", "010212");
|
|
120
|
+
const nominalStr = finalAmount.toString();
|
|
121
|
+
const nominalTag = `54${nominalStr.length.toString().padStart(2, '0')}${nominalStr}`;
|
|
122
|
+
const insertPosition = qrisBase.indexOf("5802ID");
|
|
123
|
+
const qrisWithNominal = qrisBase.slice(0, insertPosition) + nominalTag + qrisBase.slice(insertPosition);
|
|
124
|
+
const checksum = this.calculateCRC16(qrisWithNominal);
|
|
125
|
+
|
|
126
|
+
return qrisWithNominal + checksum;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new Error('Gagal generate string QRIS: ' + error.message);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
calculateCRC16(str) {
|
|
133
|
+
try {
|
|
134
|
+
if (!str) {
|
|
135
|
+
throw new Error('String tidak boleh kosong');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let crc = 0xFFFF;
|
|
139
|
+
for (let i = 0; i < str.length; i++) {
|
|
140
|
+
crc ^= str.charCodeAt(i) << 8;
|
|
141
|
+
for (let j = 0; j < 8; j++) {
|
|
142
|
+
crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1);
|
|
143
|
+
}
|
|
144
|
+
crc &= 0xFFFF;
|
|
145
|
+
}
|
|
146
|
+
return crc.toString(16).toUpperCase().padStart(4, '0');
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new Error('Gagal kalkulasi CRC16: ' + error.message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
211
153
|
export default QRISGenerator;
|
|
@@ -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": "
|
|
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
|
-
"
|
|
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"
|