FarelDeveloper commited on
Commit
7619d73
·
verified ·
1 Parent(s): e548378

Create index.js

Browse files
Files changed (1) hide show
  1. index.js +195 -0
index.js ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const path = require('path');
3
+ const app = express();
4
+
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";
12
+
13
+ function hitungCRC16(str) {
14
+ let crc = 0xFFFF;
15
+ for (let c = 0; c < str.length; c++) {
16
+ let code = str.charCodeAt(c);
17
+ crc ^= (code << 8);
18
+ for (let i = 0; i < 8; i++) {
19
+ if ((crc & 0x8000) !== 0) {
20
+ crc = ((crc << 1) ^ 0x1021) & 0xFFFF;
21
+ } else {
22
+ crc = (crc << 1) & 0xFFFF;
23
+ }
24
+ }
25
+ }
26
+ let hex = crc.toString(16).toUpperCase();
27
+ return hex.padStart(4, '0');
28
+ }
29
+
30
+ // 1. ENDPOINT GENERATE QRIS DYNAMIC
31
+ app.get('/create', (req, res) => {
32
+ const nominal = req.query.nominal;
33
+
34
+ if (!nominal || isNaN(nominal) || parseInt(nominal) <= 0) {
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
+
42
+ let qrisTanpaCRC = "";
43
+ if (QRIS_BISNIS_BASE.includes('54011')) {
44
+ qrisTanpaCRC = QRIS_BISNIS_BASE.replace('54011', tagNominalBaru);
45
+ } else {
46
+ const posisiID58 = QRIS_BISNIS_BASE.indexOf('5802ID');
47
+ const bagianAwal = QRIS_BISNIS_BASE.substring(0, posisiID58);
48
+ const bagianAkhir = QRIS_BISNIS_BASE.substring(posisiID58);
49
+ qrisTanpaCRC = bagianAwal + tagNominalBaru + bagianAkhir;
50
+ }
51
+
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()
59
+ };
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
+ // 3. ENDPOINT WEBHOOK NOTIFIKASI DANA (Ditembak oleh MacroDroid HP)
78
+ app.post('/notifdana', (req, res) => {
79
+ const { message } = req.body;
80
+ console.log(`[Notifikasi Masuk]: ${message}`);
81
+
82
+ if (message && message.includes('berhasil diterima DANA Bisnis')) {
83
+ try {
84
+ const regexUang = /Rp([\d.]+)/;
85
+ const matchUang = message.match(regexUang);
86
+
87
+ if (matchUang) {
88
+ const nominalBersih = matchUang[1].replace(/\./g, '');
89
+
90
+ // Jika nominal terdaftar di server, ubah statusnya menjadi SUCCESS
91
+ if (databaseTransaksi[nominalBersih]) {
92
+ databaseTransaksi[nominalBersih].status = "SUCCESS";
93
+ console.log(`[REAL-TIME] Transaksi nominal Rp ${nominalBersih} BERHASIL DIKONFIRMASI!`);
94
+ }
95
+ }
96
+ } catch (error) {
97
+ console.error("Gagal memproses nominal:", error);
98
+ }
99
+ }
100
+ return res.status(200).json({ status: 'success' });
101
+ });
102
+
103
+ // 4. FRONTEND HALAMAN UTAMA (HTML, CSS, JS terintegrasi)
104
+ app.get('/', (req, res) => {
105
+ res.send(`
106
+ <!DOCTYPE html>
107
+ <html lang="id">
108
+ <head>
109
+ <meta charset="UTF-8">
110
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
111
+ <title>FAREL SHOP - Kasir QRIS</title>
112
+ <script src="https://cdn.jsdelivr.net/npm/qrcode@1.4.4/build/qrcode.min.js"></script>
113
+ <style>
114
+ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f4f6f9; margin: 0; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
115
+ .card { background: white; padding: 30px; border-radius: 16px; box-shadow: 0 10px 25px rgba(0,0,0,0.05); width: 100%; max-width: 400px; text-align: center; }
116
+ h2 { color: #111; margin-bottom: 5px; }
117
+ p { color: #666; font-size: 14px; margin-top: 0; }
118
+ 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; }
119
+ 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; }
120
+ button:hover { background: #059669; }
121
+ #qrcode { margin: 20px auto; display: none; background: #fff; padding: 10px; border: 1px solid #eee; display: inline-block; }
122
+ .status-box { margin-top: 15px; padding: 10px; border-radius: 8px; font-weight: bold; display: none; }
123
+ .pending { background: #fef3c7; color: #d97706; }
124
+ .success { background: #d1fae5; color: #059669; animation: pulse 1.5s infinite; }
125
+ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.03); } 100% { transform: scale(1); } }
126
+ </style>
127
+ </head>
128
+ <body>
129
+ <div class="card">
130
+ <h2>FAREL SHOP</h2>
131
+ <p>Generate QRIS & Cek Otomatis</p>
132
+
133
+ <input type="number" id="nominalInput" placeholder="Masukkan Nominal (Contoh: 15000)" />
134
+ <button onclick="buatQRIS()">Buat Kode QRIS</button>
135
+
136
+ <div id="qrisSection" style="display:none; margin-top: 20px;">
137
+ <h3 id="displayNominal"></h3>
138
+ <canvas id="qrcode"></canvas>
139
+ <div id="statusLabel" class="status-box">Menunggu Pembayaran...</div>
140
+ </div>
141
+ </div>
142
+
143
+ <script>
144
+ let intervalCekStatus;
145
+
146
+ async function buatQRIS() {
147
+ clearInterval(intervalCekStatus);
148
+ const nominal = document.getElementById('nominalInput').value;
149
+ if(!nominal) return alert('Masukkan nominal terlebih dahulu!');
150
+
151
+ const res = await fetch('/create?nominal=' + nominal);
152
+ const data = await res.json();
153
+
154
+ if(data.status === 'success') {
155
+ document.getElementById('qrisSection').style.display = 'block';
156
+ document.getElementById('displayNominal').innerText = 'Total: Rp ' + parseInt(nominal).toLocaleString('id-ID');
157
+
158
+ // Render string QRIS menjadi gambar QR Code
159
+ QRCode.toCanvas(document.getElementById('qrcode'), data.qris_string, { width: 250 }, function (error) {
160
+ if (error) console.error(error);
161
+ });
162
+
163
+ // Set label status jadi pending
164
+ const statusLabel = document.getElementById('statusLabel');
165
+ statusLabel.style.display = 'block';
166
+ statusLabel.className = 'status-box pending';
167
+ statusLabel.innerText = 'Menunggu Pembayaran...';
168
+
169
+ // Mulai memantau secara real-time (Long Polling per 2 detik)
170
+ intervalCekStatus = setInterval(() => {
171
+ cekStatusPembayaran(nominal);
172
+ }, 2000);
173
+ }
174
+ }
175
+
176
+ async function cekStatusPembayaran(nominal) {
177
+ const res = await fetch('/check-status?nominal=' + nominal);
178
+ const data = await res.json();
179
+
180
+ if(data.status === 'SUCCESS') {
181
+ clearInterval(intervalCekStatus);
182
+ const statusLabel = document.getElementById('statusLabel');
183
+ statusLabel.className = 'status-box success';
184
+ statusLabel.innerText = '🎉 PEMBAYARAN BERHASIL!';
185
+ alert('Transaksi Rp ' + nominal + ' Sukses Diterima!');
186
+ }
187
+ }
188
+ </script>
189
+ </body>
190
+ </html>
191
+ `);
192
+ });
193
+
194
+ const PORT = process.env.PORT || 7860;
195
+ app.listen(PORT, () => console.log(`Server running on port ${PORT}`));