FarelDeveloper commited on
Commit
d630ea1
Β·
verified Β·
1 Parent(s): a067d79

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +82 -27
index.js CHANGED
@@ -5,7 +5,6 @@ const app = express();
5
  app.use(express.json());
6
 
7
  // Tempat menyimpan status transaksi sementara di memory server
8
- // Struktur: { "15000": { status: "PENDING" } }
9
  let databaseTransaksi = {};
10
 
11
  const QRIS_BISNIS_BASE = "00020101021226570011ID.DANA.WWW011893600915092050870402099205087040303UMI51440014ID.CO.QRIS.WWW0215ID10254080885610303UMI520448145303360540115802ID5910FAREL SHOP6012Kota Cirebon610545122621960150011ID.DANA.WWW6304";
@@ -35,7 +34,8 @@ app.get('/create', (req, res) => {
35
  return res.status(400).json({ error: 'Nominal harus berupa angka!' });
36
  }
37
 
38
- const valueUang = parseInt(nominal).toString();
 
39
  const lengthUang = valueUang.length.toString().padStart(2, '0');
40
  const tagNominalBaru = `54${lengthUang}${valueUang}`;
41
 
@@ -52,7 +52,7 @@ app.get('/create', (req, res) => {
52
  const hasilCRC = hitungCRC16(qrisTanpaCRC);
53
  const qrisDynamicString = qrisTanpaCRC + hasilCRC;
54
 
55
- // Daftarkan nominal ke database memory dengan status PENDING
56
  databaseTransaksi[valueUang] = {
57
  status: "PENDING",
58
  waktu: new Date()
@@ -60,39 +60,56 @@ app.get('/create', (req, res) => {
60
 
61
  return res.json({
62
  status: 'success',
63
- nominal: parseInt(nominal),
64
  qris_string: qrisDynamicString
65
  });
66
  });
67
 
68
- // 2. ENDPOINT CHECK STATUS (Ditembak oleh Frontend secara Real-time)
69
  app.get('/check-status', (req, res) => {
70
- const nominal = req.query.nominal;
 
 
71
  if (databaseTransaksi[nominal]) {
72
  return res.json({ status: databaseTransaksi[nominal].status });
73
  }
74
  return res.json({ status: "NOT_FOUND" });
75
  });
76
 
77
- // Ganti bagian app.post('/notifdana') di index.js Anda dengan ini:
 
 
 
 
 
 
 
 
 
 
 
 
78
  app.post('/notifdana', (req, res) => {
79
  const { message } = req.body;
80
  console.log(`[Notifikasi Masuk]: ${message}`);
81
 
82
- // Kita buat pendeteksian lebih fleksibel: Cukup pastikan teks mengandung kata "Rp" dan kata "berhasil"
83
  if (message && message.toLowerCase().includes('berhasil') && message.includes('Rp')) {
84
  try {
85
  const regexUang = /Rp([\d.]+)/;
86
  const matchUang = message.match(regexUang);
87
 
88
  if (matchUang) {
89
- const nominalBersih = matchUang[1].replace(/\./g, '');
 
 
 
 
90
 
91
- if (databaseTransaksi[nominalBersih]) {
92
- databaseTransaksi[nominalBersih].status = "SUCCESS";
93
- console.log(`[REAL-TIME] Transaksi nominal Rp ${nominalBersih} BERHASIL DIKONFIRMASI!`);
94
  } else {
95
- console.log(`[PERINGATAN] Nominal Rp ${nominalBersih} diterima tapi tidak dicari oleh frontend.`);
96
  }
97
  }
98
  } catch (error) {
@@ -102,7 +119,7 @@ app.post('/notifdana', (req, res) => {
102
  return res.status(200).json({ status: 'success' });
103
  });
104
 
105
- // 4. FRONTEND HALAMAN UTAMA (HTML, CSS, JS terintegrasi)
106
  app.get('/', (req, res) => {
107
  res.send(`
108
  <!DOCTYPE html>
@@ -118,9 +135,13 @@ app.get('/', (req, res) => {
118
  h2 { color: #111; margin-bottom: 5px; }
119
  p { color: #666; font-size: 14px; margin-top: 0; }
120
  input { width: 100%; padding: 12px; margin: 15px 0; border: 1px solid #ddd; border-radius: 8px; box-sizing: border-box; font-size: 16px; text-align: center; }
121
- button { width: 100%; background: #10b981; color: white; border: none; padding: 12px; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; transition: 0.2s; }
122
  button:hover { background: #059669; }
123
- #qrcode { margin: 20px auto; display: none; background: #fff; padding: 10px; border: 1px solid #eee; display: inline-block; }
 
 
 
 
124
  .status-box { margin-top: 15px; padding: 10px; border-radius: 8px; font-weight: bold; display: none; }
125
  .pending { background: #fef3c7; color: #d97706; }
126
  .success { background: #d1fae5; color: #059669; animation: pulse 1.5s infinite; }
@@ -139,54 +160,88 @@ app.get('/', (req, res) => {
139
  <h3 id="displayNominal"></h3>
140
  <canvas id="qrcode"></canvas>
141
  <div id="statusLabel" class="status-box">Menunggu Pembayaran...</div>
 
 
 
 
 
142
  </div>
143
  </div>
144
 
145
  <script>
146
  let intervalCekStatus;
 
147
 
148
  async function buatQRIS() {
149
  clearInterval(intervalCekStatus);
150
  const nominal = document.getElementById('nominalInput').value;
151
  if(!nominal) return alert('Masukkan nominal terlebih dahulu!');
 
 
152
 
153
- const res = await fetch('/create?nominal=' + nominal);
154
  const data = await res.json();
155
 
156
  if(data.status === 'success') {
157
  document.getElementById('qrisSection').style.display = 'block';
158
- document.getElementById('displayNominal').innerText = 'Total: Rp ' + parseInt(nominal).toLocaleString('id-ID');
159
 
160
- // Render string QRIS menjadi gambar QR Code
161
  QRCode.toCanvas(document.getElementById('qrcode'), data.qris_string, { width: 250 }, function (error) {
162
  if (error) console.error(error);
163
  });
164
 
165
- // Set label status jadi pending
166
  const statusLabel = document.getElementById('statusLabel');
167
  statusLabel.style.display = 'block';
168
  statusLabel.className = 'status-box pending';
169
  statusLabel.innerText = 'Menunggu Pembayaran...';
170
 
171
- // Mulai memantau secara real-time (Long Polling per 2 detik)
172
  intervalCekStatus = setInterval(() => {
173
- cekStatusPembayaran(nominal);
174
  }, 2000);
175
  }
176
  }
177
 
178
- async function cekStatusPembayaran(nominal) {
179
  const res = await fetch('/check-status?nominal=' + nominal);
180
  const data = await res.json();
181
 
182
  if(data.status === 'SUCCESS') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  clearInterval(intervalCekStatus);
184
- const statusLabel = document.getElementById('statusLabel');
185
- statusLabel.className = 'status-box success';
186
- statusLabel.innerText = 'πŸŽ‰ PEMBAYARAN BERHASIL!';
187
- alert('Transaksi Rp ' + nominal + ' Sukses Diterima!');
 
188
  }
189
  }
 
 
 
 
 
 
 
 
190
  </script>
191
  </body>
192
  </html>
 
5
  app.use(express.json());
6
 
7
  // Tempat menyimpan status transaksi sementara di memory server
 
8
  let databaseTransaksi = {};
9
 
10
  const QRIS_BISNIS_BASE = "00020101021226570011ID.DANA.WWW011893600915092050870402099205087040303UMI51440014ID.CO.QRIS.WWW0215ID10254080885610303UMI520448145303360540115802ID5910FAREL SHOP6012Kota Cirebon610545122621960150011ID.DANA.WWW6304";
 
34
  return res.status(400).json({ error: 'Nominal harus berupa angka!' });
35
  }
36
 
37
+ // Pastikan kunci disimpan sebagai String Angka Murni yang Bersih (Tanpa Spasi/Titik)
38
+ const valueUang = parseInt(nominal, 10).toString();
39
  const lengthUang = valueUang.length.toString().padStart(2, '0');
40
  const tagNominalBaru = `54${lengthUang}${valueUang}`;
41
 
 
52
  const hasilCRC = hitungCRC16(qrisTanpaCRC);
53
  const qrisDynamicString = qrisTanpaCRC + hasilCRC;
54
 
55
+ // Daftarkan nominal ke database dengan key string angka bersih
56
  databaseTransaksi[valueUang] = {
57
  status: "PENDING",
58
  waktu: new Date()
 
60
 
61
  return res.json({
62
  status: 'success',
63
+ nominal: parseInt(valueUang, 10),
64
  qris_string: qrisDynamicString
65
  });
66
  });
67
 
68
+ // 2. ENDPOINT CHECK STATUS (Real-time & Manual lewat satu pintu)
69
  app.get('/check-status', (req, res) => {
70
+ if (!req.query.nominal) return res.status(400).json({ status: "ERROR" });
71
+
72
+ const nominal = parseInt(req.query.nominal, 10).toString(); // Normalisasi input key
73
  if (databaseTransaksi[nominal]) {
74
  return res.json({ status: databaseTransaksi[nominal].status });
75
  }
76
  return res.json({ status: "NOT_FOUND" });
77
  });
78
 
79
+ // 3. ENDPOINT BATALKAN TRANSAKSI
80
+ app.get('/cancel', (req, res) => {
81
+ if (!req.query.nominal) return res.status(400).json({ status: "ERROR" });
82
+
83
+ const nominal = parseInt(req.query.nominal, 10).toString();
84
+ if (databaseTransaksi[nominal]) {
85
+ delete databaseTransaksi[nominal]; // Hapus antrean di server
86
+ return res.json({ status: "CANCELLED" });
87
+ }
88
+ return res.json({ status: "NOT_FOUND" });
89
+ });
90
+
91
+ // 4. ENDPOINT WEBHOOK NOTIFIKASI DANA (Ditembak oleh MacroDroid) - FIX MATCHING
92
  app.post('/notifdana', (req, res) => {
93
  const { message } = req.body;
94
  console.log(`[Notifikasi Masuk]: ${message}`);
95
 
 
96
  if (message && message.toLowerCase().includes('berhasil') && message.includes('Rp')) {
97
  try {
98
  const regexUang = /Rp([\d.]+)/;
99
  const matchUang = message.match(regexUang);
100
 
101
  if (matchUang) {
102
+ // Contoh: dari "Rp1" -> "1", dari "Rp50.000" -> "50000"
103
+ const nominalBersih = matchUang[1].replace(/\./g, '').trim();
104
+ const keyTransaksi = parseInt(nominalBersih, 10).toString();
105
+
106
+ console.log(`[Mencari Key Transaksi]: "${keyTransaksi}"`);
107
 
108
+ if (databaseTransaksi[keyTransaksi]) {
109
+ databaseTransaksi[keyTransaksi].status = "SUCCESS";
110
+ console.log(`[FIX-SUCCESS] Transaksi nominal Rp ${keyTransaksi} BERHASIL DIKONFIRMASI!`);
111
  } else {
112
+ console.log(`[PERINGATAN] Nominal Rp ${keyTransaksi} masuk, tapi tidak ada antrean di web.`);
113
  }
114
  }
115
  } catch (error) {
 
119
  return res.status(200).json({ status: 'success' });
120
  });
121
 
122
+ // 5. FRONTEND HALAMAN UTAMA (Dengan tombol Manual Cek dan Cancel)
123
  app.get('/', (req, res) => {
124
  res.send(`
125
  <!DOCTYPE html>
 
135
  h2 { color: #111; margin-bottom: 5px; }
136
  p { color: #666; font-size: 14px; margin-top: 0; }
137
  input { width: 100%; padding: 12px; margin: 15px 0; border: 1px solid #ddd; border-radius: 8px; box-sizing: border-box; font-size: 16px; text-align: center; }
138
+ button { width: 100%; background: #10b981; color: white; border: none; padding: 12px; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; transition: 0.2s; margin-bottom: 10px; }
139
  button:hover { background: #059669; }
140
+ .btn-manual { background: #3b82f6; }
141
+ .btn-manual:hover { background: #2563eb; }
142
+ .btn-cancel { background: #ef4444; }
143
+ .btn-cancel:hover { background: #dc2626; }
144
+ #qrcode { margin: 20px auto; background: #fff; padding: 10px; border: 1px solid #eee; display: inline-block; }
145
  .status-box { margin-top: 15px; padding: 10px; border-radius: 8px; font-weight: bold; display: none; }
146
  .pending { background: #fef3c7; color: #d97706; }
147
  .success { background: #d1fae5; color: #059669; animation: pulse 1.5s infinite; }
 
160
  <h3 id="displayNominal"></h3>
161
  <canvas id="qrcode"></canvas>
162
  <div id="statusLabel" class="status-box">Menunggu Pembayaran...</div>
163
+
164
+ <div style="margin-top: 15px;">
165
+ <button class="btn-manual" onclick="cekStatusManual()">πŸ” Cek Status Manual</button>
166
+ <button class="btn-cancel" onclick="batalkanTransaksi()">❌ Batalkan Transaksi</button>
167
+ </div>
168
  </div>
169
  </div>
170
 
171
  <script>
172
  let intervalCekStatus;
173
+ let currentNominal = "";
174
 
175
  async function buatQRIS() {
176
  clearInterval(intervalCekStatus);
177
  const nominal = document.getElementById('nominalInput').value;
178
  if(!nominal) return alert('Masukkan nominal terlebih dahulu!');
179
+
180
+ currentNominal = parseInt(nominal, 10).toString();
181
 
182
+ const res = await fetch('/create?nominal=' + currentNominal);
183
  const data = await res.json();
184
 
185
  if(data.status === 'success') {
186
  document.getElementById('qrisSection').style.display = 'block';
187
+ document.getElementById('displayNominal').innerText = 'Total: Rp ' + parseInt(currentNominal).toLocaleString('id-ID');
188
 
 
189
  QRCode.toCanvas(document.getElementById('qrcode'), data.qris_string, { width: 250 }, function (error) {
190
  if (error) console.error(error);
191
  });
192
 
 
193
  const statusLabel = document.getElementById('statusLabel');
194
  statusLabel.style.display = 'block';
195
  statusLabel.className = 'status-box pending';
196
  statusLabel.innerText = 'Menunggu Pembayaran...';
197
 
198
+ // Tetap berjalan real-time mengecek otomatis tiap 2 detik
199
  intervalCekStatus = setInterval(() => {
200
+ cekStatusPembayaranOtomatis(currentNominal);
201
  }, 2000);
202
  }
203
  }
204
 
205
+ async function cekStatusPembayaranOtomatis(nominal) {
206
  const res = await fetch('/check-status?nominal=' + nominal);
207
  const data = await res.json();
208
 
209
  if(data.status === 'SUCCESS') {
210
+ ubahKeTampilanSukses(nominal);
211
+ }
212
+ }
213
+
214
+ async function cekStatusManual() {
215
+ if(!currentNominal) return;
216
+ const res = await fetch('/check-status?nominal=' + currentNominal);
217
+ const data = await res.json();
218
+
219
+ if(data.status === 'SUCCESS') {
220
+ ubahKeTampilanSukses(currentNominal);
221
+ } else {
222
+ alert('Status transaksi Rp ' + parseInt(currentNominal).toLocaleString('id-ID') + ' masih BELUM DIBAYAR.');
223
+ }
224
+ }
225
+
226
+ async function batalkanTransaksi() {
227
+ if(!currentNominal) return;
228
+ if(confirm('Apakah Anda yakin ingin membatalkan transaksi ini?')) {
229
  clearInterval(intervalCekStatus);
230
+ await fetch('/cancel?nominal=' + currentNominal);
231
+ document.getElementById('qrisSection').style.display = 'none';
232
+ document.getElementById('nominalInput').value = '';
233
+ currentNominal = "";
234
+ alert('Transaksi berhasil dibatalkan.');
235
  }
236
  }
237
+
238
+ function ubahKeTampilanSukses(nominal) {
239
+ clearInterval(intervalCekStatus);
240
+ const statusLabel = document.getElementById('statusLabel');
241
+ statusLabel.className = 'status-box success';
242
+ statusLabel.innerText = 'πŸŽ‰ PEMBAYARAN BERHASIL!';
243
+ alert('Transaksi Rp ' + parseInt(nominal).toLocaleString('id-ID') + ' Sukses Diterima!');
244
+ }
245
  </script>
246
  </body>
247
  </html>