dra-qris-api 1.0.4 → 1.0.5

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.

Potentially problematic release.


This version of dra-qris-api might be problematic. Click here for more details.

@@ -7,7 +7,6 @@ const globalPending = require("./globalPending");
7
7
 
8
8
  /**
9
9
  * Handle bukti transfer via gambar (QRIS / slip)
10
- * Universal: tidak tahu ini buat panel/topup/dll.
11
10
  *
12
11
  * @param {Object} params
13
12
  * @param {Object} params.m - message object (Baileys)
@@ -47,12 +46,12 @@ async function handlePaymentProof({
47
46
  m,
48
47
  "buffer",
49
48
  {},
50
- { reuploadRequest: conn.waUploadToServer }
49
+ { reuploadRequest: conn.waUploadToServer },
51
50
  );
52
51
 
53
52
  if (!buffer || buffer.length === 0) {
54
53
  await reply(
55
- "❌ Gagal download gambar. Pastikan file terkirim dengan benar."
54
+ "❌ Gagal download gambar. Pastikan file terkirim dengan benar.",
56
55
  );
57
56
  return null;
58
57
  }
@@ -83,12 +82,119 @@ async function handlePaymentProof({
83
82
 
84
83
  const parseOcrAmount = (text) => {
85
84
  const lines = text.split(/\r?\n/);
86
- let amountLine = lines.find((l) => /nominal|jumlah total|total/i.test(l));
87
- if (!amountLine) amountLine = text;
85
+ const amounts = [];
86
+
87
+ const keywords = [
88
+ /nominal/i,
89
+ /jumlah\s*total/i,
90
+ /total\s*bayar/i,
91
+ /total/i,
92
+ /amount/i,
93
+ /transfer/i,
94
+ /dibayar/i,
95
+ /bayar/i,
96
+ /tagihan/i,
97
+ ];
98
+
99
+ for (const line of lines) {
100
+ const hasKeyword = keywords.some((kw) => kw.test(line));
101
+
102
+ if (hasKeyword) {
103
+ const matches = extractAmounts(line);
104
+ amounts.push(...matches);
105
+ }
106
+ }
107
+
108
+ if (amounts.length === 0) {
109
+ for (const line of lines) {
110
+ if (/\b(rp|idr)\s*[\d.,]+/i.test(line)) {
111
+ const matches = extractAmounts(line);
112
+ amounts.push(...matches);
113
+ }
114
+ }
115
+ }
116
+
117
+ if (amounts.length === 0) {
118
+ for (const line of lines) {
119
+ // Cari angka dengan pemisah ribuan (. atau ,) dan minimal 3 digit
120
+ const matches = extractAmounts(line);
121
+ // Filter hanya angka >= 1000 (asumsi minimal nominal transfer)
122
+ const validMatches = matches.filter((amt) => amt >= 1000);
123
+ amounts.push(...validMatches);
124
+ }
125
+ }
126
+
127
+ if (amounts.length > 0) {
128
+ return Math.max(...amounts);
129
+ }
130
+
131
+ const fallbackAmounts = extractAmounts(text);
132
+ return fallbackAmounts.length > 0 ? Math.max(...fallbackAmounts) : 0;
133
+ };
134
+
135
+ /**
136
+ * Ekstrak semua kemungkinan nominal dari string
137
+ * @param {string} str - text input
138
+ * @returns {number[]} - array of amounts
139
+ */
140
+ const extractAmounts = (str) => {
141
+ const amounts = [];
142
+
143
+ // Pattern 1: Rp 50.000 / Rp50.000 / Rp 50000 / Rp50,000
144
+ const rpPattern = /\b(?:rp|idr)\s*([\d.,]+)/gi;
145
+ let match;
146
+ while ((match = rpPattern.exec(str)) !== null) {
147
+ const cleaned = cleanNumber(match[1]);
148
+ if (cleaned > 0) amounts.push(cleaned);
149
+ }
150
+
151
+ // Pattern 2: Angka dengan pemisah ribuan (50.000 atau 50,000)
152
+ // Harus minimal 4 digit untuk menghindari false positive
153
+ const numberPattern =
154
+ /\b(\d{1,3}[.,]\d{3}(?:[.,]\d{3})*(?:[.,]\d{1,2})?)\b/g;
155
+ while ((match = numberPattern.exec(str)) !== null) {
156
+ const cleaned = cleanNumber(match[1]);
157
+ if (cleaned > 0) amounts.push(cleaned);
158
+ }
159
+
160
+ // Pattern 3: Angka polos minimal 4 digit (50000)
161
+ const plainPattern = /\b(\d{4,})\b/g;
162
+ while ((match = plainPattern.exec(str)) !== null) {
163
+ const cleaned = cleanNumber(match[1]);
164
+ if (cleaned > 0) amounts.push(cleaned);
165
+ }
166
+
167
+ return amounts;
168
+ };
169
+
170
+ /**
171
+ * Bersihkan dan konversi string angka ke number
172
+ * Menangani format Indonesia (50.000,00) dan International (50,000.00)
173
+ */
174
+ const cleanNumber = (str) => {
175
+ if (!str) return 0;
176
+
177
+ // Hitung jumlah titik dan koma
178
+ const dotCount = (str.match(/\./g) || []).length;
179
+ const commaCount = (str.match(/,/g) || []).length;
180
+
181
+ let cleaned = str;
182
+
183
+ // Format Indonesia: 50.000,00 (titik sebagai pemisah ribuan, koma desimal)
184
+ if (dotCount > 0 && commaCount <= 1) {
185
+ cleaned = str.replace(/\./g, "").replace(/,/g, ".");
186
+ }
187
+ // Format International: 50,000.00 (koma sebagai pemisah ribuan, titik desimal)
188
+ else if (commaCount > 0 && dotCount <= 1) {
189
+ cleaned = str.replace(/,/g, "");
190
+ }
191
+ // Angka polos tanpa pemisah
192
+ else {
193
+ cleaned = str.replace(/[.,]/g, "");
194
+ }
88
195
 
89
- let cleaned = (amountLine.match(/[\d.,]+/g) || ["0"]).pop();
90
- cleaned = cleaned.replace(/\./g, "").replace(/,/g, "");
91
- return Number(cleaned) || 0;
196
+ const num = parseFloat(cleaned);
197
+ return isNaN(num) ? 0 : Math.floor(num); // Bulatkan ke bawah untuk amount
92
198
  };
93
199
 
94
200
  const ocrAmount = parseOcrAmount(text);
@@ -109,13 +215,16 @@ async function handlePaymentProof({
109
215
  await reply(
110
216
  `✅ Pembayaran untuk *${
111
217
  pending.description || "transaksi kamu"
112
- }* sebesar Rp${pending.nominal} terverifikasi.`
218
+ }* sebesar Rp${pending.nominal.toLocaleString("id-ID")} terverifikasi.\n\n` +
219
+ `💰 Nominal terdeteksi: Rp${ocrAmount.toLocaleString("id-ID")}`,
113
220
  );
114
221
 
115
222
  delete globalPending[m.sender];
116
223
  } else {
117
224
  await reply(
118
- `⚠️ Pembayaran belum valid / belum masuk. Status: *${status}*`
225
+ `⚠️ Pembayaran belum valid / belum masuk. Status: *${status}*\n\n` +
226
+ `💰 Nominal terdeteksi: Rp${ocrAmount.toLocaleString("id-ID")}\n` +
227
+ `📋 Nominal seharusnya: Rp${pending.nominal.toLocaleString("id-ID")}`,
119
228
  );
120
229
  }
121
230
 
package/package.json CHANGED
@@ -1,10 +1,17 @@
1
1
  {
2
2
  "name": "dra-qris-api",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Connector and modules for QRIS API DenayRestAPI ",
5
- "main": "index.js",
6
5
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
6
+ "build": "tsup"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.mjs",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
8
15
  },
9
16
  "keywords": [
10
17
  "draqrisapi"
@@ -14,5 +21,9 @@
14
21
  "axios": "^1.12.2",
15
22
  "crypto": "^1.0.1",
16
23
  "tesseract.js": "^6.0.1"
24
+ },
25
+ "devDependencies": {
26
+ "tsup": "^8.5.1",
27
+ "typescript": "^5.9.3"
17
28
  }
18
29
  }
package/tsup.config.js ADDED
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["index.js", "lib/*.js"],
5
+ format: ["cjs", "esm"],
6
+ outDir: "dist",
7
+ clean: true,
8
+ });