diff --git a/src/puntocassa/FrmCerca.form b/src/puntocassa/FrmCerca.form index 86492e5..2dcead4 100644 --- a/src/puntocassa/FrmCerca.form +++ b/src/puntocassa/FrmCerca.form @@ -73,6 +73,8 @@ + + @@ -98,14 +100,22 @@ - - - - - - + + + + + + + + + + + + + + - + @@ -263,6 +273,13 @@ + + + + + + + diff --git a/src/puntocassa/FrmCerca.java b/src/puntocassa/FrmCerca.java index aaea4cd..63fff9b 100644 --- a/src/puntocassa/FrmCerca.java +++ b/src/puntocassa/FrmCerca.java @@ -6,24 +6,38 @@ package puntocassa; +import com.github.sarxos.webcam.Webcam; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.LuminanceSource; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.NotFoundException; +import com.google.zxing.Result; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.Image; import java.awt.Rectangle; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.table.TableColumn; +import puntocassa.utils.QrCryptoService; import puntocassa.utils.Utils; /** @@ -43,6 +57,7 @@ private JTextField jtxtTessera; private Boolean FlagMostraDataNascita=true; private Boolean DisabilitaPerBuono=false; + /** * Creates new form frm */ @@ -100,7 +115,44 @@ private JTextField jtxtTessera; jtxtCognome.setText(BuoniPasto); Cerca(); Disabilita(); - } + } + + + + jtxtNumTesseraHidden.addActionListener(e -> { + System.out.println("QR letto: " + jtxtNumTesseraHidden.getText().trim()); + String valueRead = jtxtNumTesseraHidden.getText().trim(); + //decripting + List dataResult = QrCryptoService.decryptAndValidate(valueRead); + //System.out.println("QR code decriptato: " + r); + if(dataResult.get(0).equals("VALID")){ + //verifica che il codice fiscale corrisponda ad una tessera + String numTessera = Utils.mySelect("select numero from tessere where id_utente = (select id from utenti where codice_fiscale = '" + + dataResult.get(1) + + "')", "numero", frmPuntoCassa); + if(numTessera == null || "".equals(numTessera)){ + //mostra un popup di errore UTENTE NON COLLEGATO AD UNA TESSERA + System.out.println("nessuna tessera collegata all'utente " + dataResult.get(1)); + JOptionPane.showOptionDialog(this, "Nessuna tessera collegata all'utente " + dataResult.get(1) , "Attenzione", + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); + jtxtNumTesseraHidden.setText(""); + jtxtNumTessera.setText(""); + return; + } + //scrivi il numero della tessera nel campo input + jtxtNumTessera.setText(numTessera); + jtxtNumTesseraHidden.setText(""); + Cerca(); + } + else { + System.out.println("QR non valido: "+ dataResult.get(1)); + JOptionPane.showOptionDialog(this, dataResult.get(1) , "Attenzione", + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); + jtxtNumTesseraHidden.setText(""); + jtxtNumTessera.setText(""); + } + }); + } @@ -128,6 +180,7 @@ private JTextField jtxtTessera; jBtnOK = new javax.swing.JButton(); jbtnSu = new javax.swing.JButton(); jbtnGiu = new javax.swing.JButton(); + jtxtNumTesseraHidden = new javax.swing.JTextField(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Cerca Tessera Attiva"); @@ -237,6 +290,8 @@ private JTextField jtxtTessera; } }); + jtxtNumTesseraHidden.setPreferredSize(new java.awt.Dimension(0, 0)); + javax.swing.GroupLayout pnl1Layout = new javax.swing.GroupLayout(pnl1); pnl1.setLayout(pnl1Layout); pnl1Layout.setHorizontalGroup( @@ -256,6 +311,8 @@ private JTextField jtxtTessera; .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnl1Layout.createSequentialGroup() .addComponent(lblCF) + .addGap(61, 61, 61) + .addComponent(jtxtNumTesseraHidden, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) .addGroup(pnl1Layout.createSequentialGroup() .addComponent(jtxtNumTessera, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -274,12 +331,17 @@ private JTextField jtxtTessera; pnl1Layout.setVerticalGroup( pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnl1Layout.createSequentialGroup() - .addComponent(jLblTitolo, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblCognome) - .addComponent(lblNome) - .addComponent(lblCF)) + .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnl1Layout.createSequentialGroup() + .addComponent(jLblTitolo, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblCognome) + .addComponent(lblNome) + .addComponent(lblCF))) + .addGroup(pnl1Layout.createSequentialGroup() + .addGap(18, 18, 18) + .addComponent(jtxtNumTesseraHidden, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(pnl1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -330,6 +392,9 @@ private JTextField jtxtTessera; jtxtNome.setBackground(Color.white); jtxtCognome.setBackground(Color.white); } + + jtxtNumTesseraHidden.requestFocusInWindow(); + // MyApplication MyA = new MyApplication(); // if (MyA.TastieraVideo) { // if (MyA.UltimoTxt.equalsIgnoreCase(evt.getComponent().toString())) { @@ -478,6 +543,54 @@ private JTextField jtxtTessera; private void jtxtNumTesseraMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jtxtNumTesseraMouseClicked Tastiera(evt); + +// //attivazione webcam per lettura qr +// Webcam webcam = Webcam.getDefault(); +// //webcam.setViewSize(new Dimension(1280, 720)); +// Dimension hd = new Dimension(1280, 720); +// webcam.setCustomViewSizes(new Dimension[] { hd }); +// webcam.setViewSize(hd); +// +// webcam.open(); +// +// while (true) { +// BufferedImage image = webcam.getImage(); +// if (image == null) continue; +// +// LuminanceSource source = new BufferedImageLuminanceSource(image); +// BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); +// +// try { +// Result result = new MultiFormatReader().decode(bitmap); +// System.out.println("QR Code trovato: " + result.getText()); +// //decripting +// List dataResult = QrCryptoService.decryptAndValidate(result.getText()); +// //System.out.println("QR code decriptato: " + r); +// if(dataResult.get(0).equals("VALID")){ +// //verifica che il codice fiscale corrisponda ad una tessera +// String numTessera = Utils.mySelect("select numero from tessere where id_utente = (select id from utenti where codice_fiscale = '" + +// dataResult.get(1) + +// "')", "numero", frmPuntoCassa); +// if(numTessera == null || "".equals(numTessera)){ +// //mostra un popup di errore UTENTE NON COLLEGATO AD UNA TESSERA +// System.out.println("nessuna tessera collegata all'utente " + dataResult.get(1)); +// return; +// } +// //scrivi il numero della tessera nel campo input +// jtxtNumTessera.setText(numTessera); +// Cerca(); +// } +// else { +// System.out.println("QR non valido: "+ dataResult.get(1)); +// } +// break; +// } catch (NotFoundException e) { +// // Nessun QR code trovato nel frame +// } +// } +// +// webcam.close(); + }//GEN-LAST:event_jtxtNumTesseraMouseClicked private void Tastiera(java.awt.event.MouseEvent evt) { MyApplication MyA = new MyApplication(); @@ -753,6 +866,7 @@ private JTextField jtxtTessera; private javax.swing.JTextField jtxtCognome; private javax.swing.JTextField jtxtNome; private javax.swing.JTextField jtxtNumTessera; + private javax.swing.JTextField jtxtNumTesseraHidden; private javax.swing.JLabel lblCF; private javax.swing.JLabel lblCognome; private javax.swing.JLabel lblNome; diff --git a/src/puntocassa/PuntoCassa.java b/src/puntocassa/PuntoCassa.java index 62282aa..1cf1e46 100644 --- a/src/puntocassa/PuntoCassa.java +++ b/src/puntocassa/PuntoCassa.java @@ -27,10 +27,13 @@ import java.util.Date; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; @@ -49,6 +52,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import puntocassa.utils.QrCryptoService; import puntocassa.utils.Utils; //****************************************************************************** @@ -223,20 +227,18 @@ public class PuntoCassa extends JFrame implements ActionListener { //adatto panel login pnlLogin.setSize(calcolaLarghezzaAltezza(pnlLogin.getWidth(), largControlli), calcolaLarghezzaAltezza(pnlLogin.getHeight(), altControlli)); jBtnReadCard.setVisible(false); - + settingFontsInterfaccia(); - jcmbSmartCard.setVisible(false); visualizzaMessaggiDisplay = document.getElementsByTagName("Visualizza_Messaggi_Display").item(0).getFirstChild().getNodeValue() - .equalsIgnoreCase("SI"); + .equalsIgnoreCase("SI"); //visualizzaMessaggiDisplay = VMD.equalsIgnoreCase("SI"); messageDisplay = document.getElementsByTagName("Messaggio_Display").item(0).getFirstChild().getNodeValue(); aggiornaSmartCardReaderCombo(myApp); - String ultimiMovimenti = Utils.mySelect("SELECT valore FROM parametri WHERE chiave = 'FlagVisualizzaUltimiPassaggi'", "valore", this); @@ -313,8 +315,7 @@ public class PuntoCassa extends JFrame implements ActionListener { txtPassword.setFont(f); jTxtTessera.setFont(f); jChLogin.setFont(f); - - + Font newFontBtnAnnulla = new Font(jbtnAnnulla.getFont().getName(), jbtnAnnulla.getFont().getStyle(), hFont); jbtnAnnulla.setFont(newFontBtnAnnulla); Font newFontBtnDuplica = new Font(jbtnDuplica.getFont().getName(), jbtnDuplica.getFont().getStyle(), hFont); @@ -331,10 +332,11 @@ public class PuntoCassa extends JFrame implements ActionListener { /** * Lettura del nodo 'display' del file di configurazione + * * @param document * @param myApp * @throws NumberFormatException - * @throws DOMException + * @throws DOMException */ private void leggiNodoDisplay(Document document, MyApplication myApp) throws NumberFormatException, DOMException { // -------------------------------------------------------------------- @@ -353,7 +355,7 @@ public class PuntoCassa extends JFrame implements ActionListener { largControlli = Integer.parseInt(p.getElementsByTagName("larghezzaControlli").item(0).getFirstChild().getNodeValue()); String tastiera = p.getElementsByTagName("TastieraVideo").item(0).getFirstChild().getNodeValue(); myApp.tastieraVideo = !tastiera.equals("NO"); - + } } } @@ -410,7 +412,7 @@ public class PuntoCassa extends JFrame implements ActionListener { * @throws DOMException */ private void leggiNodoDatabase(Document document) throws DOMException { - NodeList database = document.getElementsByTagName("database"); + NodeList database = document.getElementsByTagName("database"); for (int i = 0; i < database.getLength(); i++) { Node nodo = database.item(i); @@ -508,8 +510,10 @@ public class PuntoCassa extends JFrame implements ActionListener { //29-10-18------------------------------------------------------ if (timerUnaVolta == false) { timerUnaVolta = true; + final Timer timer = new Timer(); final TimerTask task = new TimerTask() { + @Override public void run() { @@ -526,12 +530,11 @@ public class PuntoCassa extends JFrame implements ActionListener { } }; - timer.schedule(task, 5000); + timer.schedule(task, 4000); } - //fine 29-10-18------------------------------------------------------ + //fine 29-10-18------------------------------------------------------ } - return true; } @@ -553,73 +556,241 @@ public class PuntoCassa extends JFrame implements ActionListener { @Override public void run() { String tessera = ""; + boolean isQrCode = false; + //se abbiamo uno \n e la lunghezza è > 200 caratteri, è un qr code + if (testoSwipCard.toString().contains("\n") && testoSwipCard.length() > 200) { + recuperaNumeroTesseraQrCode(); + isQrCode = true; + + } //per tessere tipo 8 e 9 - if (testoSwipCard.toString().contains("ì")) { - String[] txt = testoSwipCard.toString().split("ì"); + if (!isQrCode) { + if (testoSwipCard.toString().contains("ì")) { + String[] txt = testoSwipCard.toString().split("ì"); - // per tessera MASTERCARD CARDHOLDER - if (testoSwipCard.toString().length() > 30) { + // per tessera MASTERCARD CARDHOLDER + if (testoSwipCard.toString().length() > 30) { - tessera = txt[0].trim().replace("_", ""); - } else { - tessera = txt[1].trim().replace("_", ""); - } - } else if (testoSwipCard.toString().contains("&")) { - // per tessera MASTERCARD CARDHOLDER - String[] txt = testoSwipCard.toString().split("&"); - - tessera = txt[0].trim().replace("%B", ""); - - } else { - tessera = testoSwipCard.toString().trim().replace("_", ""); - } - - //per tessera tipo 3 - if (tessera.length() >= 17 && tessera.length() <= 19) { - tessera = tessera.substring(0, 7); - } - - //per tessere tipo 6 e 7 - if (tessera.length() == 21) { - tessera = tessera.substring(0, 10); - int posI = 0; - for (int i = 0; i <= tessera.length(); i++) { - if (tessera.substring(i, i + 1).equalsIgnoreCase("0") == false) { - posI = i; - break; + tessera = txt[0].trim().replace("_", ""); + } else { + tessera = txt[1].trim().replace("_", ""); } + } else if (testoSwipCard.toString().contains("&")) { + // per tessera MASTERCARD CARDHOLDER + String[] txt = testoSwipCard.toString().split("&"); + + tessera = txt[0].trim().replace("%B", ""); + + } else { + tessera = testoSwipCard.toString().trim().replace("_", ""); } - tessera = tessera.substring(posI); - } + //per tessera tipo 3 + if (tessera.length() >= 17 && tessera.length() <= 19) { + tessera = tessera.substring(0, 7); + } - spiaAltreCarte = false; - swip = false; - if (pnlLogin.isVisible()) { - controllaLogInCarta(tessera); - } else { - //05/11/2018 le nuove tessere con CF a volte leggono solo la matricola - jTxtTessera.setText(tessera); - passaggioTessera = true; - logNumeroTessera = tessera; - cercaTessera(); - } + //per tessere tipo 6 e 7 + if (tessera.length() == 21) { + tessera = tessera.substring(0, 10); + int posI = 0; + for (int i = 0; i <= tessera.length(); i++) { + if (tessera.substring(i, i + 1).equalsIgnoreCase("0") == false) { + posI = i; + break; + } + } - testoSwipCard = new StringBuilder(); + tessera = tessera.substring(posI); + } + + spiaAltreCarte = false; + swip = false; + if (pnlLogin.isVisible()) { + controllaLogInCarta(tessera); + } else { + //05/11/2018 le nuove tessere con CF a volte leggono solo la matricola + jTxtTessera.setText(tessera); + passaggioTessera = true; + logNumeroTessera = tessera; + cercaTessera(); + } + + testoSwipCard = new StringBuilder(); + + } timer.cancel(); timer.purge(); statoCardLettore = false; } + }; timer.schedule(task, delaySwipCard); } } - }; + } +// public KeyEventDispatcher creaKeyEventDispatcher() { +// return new KeyEventDispatcher() { +// Boolean timerUnaVolta = false; +// MyApplication myApp = new MyApplication(); +// +// public boolean dispatchKeyEvent(KeyEvent e) { +// int id = e.getID(); +// +// if (jbtnChiudi.isEnabled() && !pnlLogin.isVisible() && jTxtTessera.getText().length() > 0) { +// e.consume(); +// if (!myApp.unaVolta) { +// myApp.unaVolta = true; +// } +// return false; +// } +// myApp.unaVolta = false; +// +// int keyCode = e.getKeyCode(); +// if (id == KeyEvent.KEY_RELEASED) { +// String tasto = "" + e.getKeyChar(); +// System.out.println(keyCode + " - " + tasto); +// +// logTestoTessera.append(e.getKeyChar()); +// +// // RFID input +// if (keyCode != KeyEvent.VK_SHIFT) { +// testoRFIDCard.append(tasto); +// } +// +// // Invio: fine lettura RFID +// if (keyCode == KeyEvent.VK_ENTER) { +// System.out.println(testoRFIDCard.toString()); +// warn(); +// } +// +// // Timer per letture incomplete +// if (!timerUnaVolta) { +// timerUnaVolta = true; +// +// final Timer timer = new Timer(); +// final TimerTask task = new TimerTask() { +// @Override +// public void run() { +// if (testoRFIDCard.toString().length() > 2) { +// System.out.println("Timer: " + testoRFIDCard.toString()); +// warn(); +// testoRFIDCard = new StringBuilder(); +// } +// +// timer.cancel(); +// timer.purge(); +// timerUnaVolta = false; +// } +// }; +// +// timer.schedule(task, 4000); +// } +// } +// +// return true; +// } +// +// public void warn() { +// if (statoCardLettore) { +// return; +// } +// +// //testoRFIDCard = new StringBuilder(); +// +// if (testoRFIDCard.toString().length() > 0) { +// statoCardLettore = true; +// +// final Timer timer = new Timer(); +// final TimerTask task = new TimerTask() { +// @Override +// public void run() { +// String tessera = ""; +// boolean isQrCode = false; +// +// if (testoRFIDCard.toString().contains("\n") && testoRFIDCard.length() > 200) { +// recuperaNumeroTesseraQrCode(); +// isQrCode = true; +// } +// +// if (!isQrCode) { +// tessera = testoRFIDCard.toString().trim().replace("_", ""); +// +// if (tessera.length() >= 17 && tessera.length() <= 19) { +// tessera = tessera.substring(0, 7); +// } +// +// if (tessera.length() == 21) { +// tessera = tessera.substring(0, 10); +// int posI = 0; +// for (int i = 0; i <= tessera.length(); i++) { +// if (!tessera.substring(i, i + 1).equalsIgnoreCase("0")) { +// posI = i; +// break; +// } +// } +// tessera = tessera.substring(posI); +// } +// +// if (pnlLogin.isVisible()) { +// controllaLogInCarta(tessera); +// } else { +// jTxtTessera.setText(tessera); +// passaggioTessera = true; +// logNumeroTessera = tessera; +// cercaTessera(); +// } +// +// testoRFIDCard = new StringBuilder(); +// } +// +// timer.cancel(); +// timer.purge(); +// statoCardLettore = false; +// } +// }; +// +// timer.schedule(task, delaySwipCard); +// } +// } +// }; +// } + + public void recuperaNumeroTesseraQrCode() { + //controllo sul qr code prima di lanciare la ricerca della tessera + String valueRead = testoSwipCard.toString().trim(); + //String valueRead = testoRFIDCard.toString().trim(); + //decripting + java.util.List dataResult = QrCryptoService.decryptAndValidate(valueRead); + if (dataResult.get(0).equals("VALID")) { + //verifica che il codice fiscale corrisponda ad una tessera + String numTessera = Utils.mySelect("select seleziona_tessera ('" + dataResult.get(1) + "') as numero from dual", "numero", this); + System.out.println("Dopo select numero from tessere"); + if (numTessera == null || "".equals(numTessera)) { + //mostra un popup di errore UTENTE NON COLLEGATO AD UNA TESSERA + System.out.println("nessuna tessera collegata all'utente " + dataResult.get(1)); + JOptionPane.showOptionDialog(this, "Nessuna tessera collegata all'utente " + dataResult.get(1), "Attenzione", + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); + jTxtTessera.setText(""); + } + //scrivi il numero della tessera nel campo input + jTxtTessera.setText(numTessera); + cercaTessera(); + } else { + System.out.println("Errore validazione QR code: " + dataResult.get(1)); + JOptionPane.showOptionDialog(this, dataResult.get(1), "Attenzione", + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); + //jtxtNumTesseraHidden.setText(""); + jTxtTessera.setText(""); + //reset del logTestoTessera in quanto non viene richiamata la cercaTessera() che contiene logTessera() che effettua la stessa istruzione + logTestoTessera = new StringBuilder(); + } + } /** @@ -701,7 +872,8 @@ public class PuntoCassa extends JFrame implements ActionListener { /** * Aggiorna la lista di lettori smartcard disponibili - * @param myApp + * + * @param myApp */ private void aggiornaSmartCardReaderCombo(MyApplication myApp) { try { @@ -1796,7 +1968,7 @@ public class PuntoCassa extends JFrame implements ActionListener { // ========================================================================== // *** Metodo per gestire gli eventi sui button dinamici // ========================================================================== - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); String tipo; String id; @@ -1818,7 +1990,7 @@ public class PuntoCassa extends JFrame implements ActionListener { case "PRODOTTO" -> { //boolean prenotazionePagata = false; String idPrenDettaglio = "null"; - + Object src = e.getSource(); if (src instanceof EventParameters evtParams) { flagPrenotazionePagata = evtParams.flagPrenotazionePagata; @@ -1827,8 +1999,8 @@ public class PuntoCassa extends JFrame implements ActionListener { //inserisci i prodotti se la cassa è aperta e //non c'è una prenotazione da gestire oppure //c'è una prenotazione e si stanno inserendo i prodotti in lista (idPrenDettaglio viene valorizzato in inserisciPrenotazioni()) - if (isCassaAperta && - (idPrenotazione == 0 || !idPrenDettaglio.equals("null"))) { + if (isCassaAperta + && (idPrenotazione == 0 || !idPrenDettaglio.equals("null"))) { sql = "SELECT " + "prodotti.id as idProdotto," + "prodotti.nome, " @@ -1841,8 +2013,6 @@ public class PuntoCassa extends JFrame implements ActionListener { + "left join tariffe on prodotti.id=tariffe.id_prodotto " + "where prodotti.id = " + id + " and Tariffe.id_fascia=" + idProfiloTariffario; try { - - //spengo altri togglebutton for (Integer y = 0; y < this.pnlProdotti.getComponentCount(); y++) { @@ -1896,13 +2066,14 @@ public class PuntoCassa extends JFrame implements ActionListener { } tblListaProdotti.setModel(model); - if(idPrenotazione == 0) + if (idPrenotazione == 0) { controllaCompleti(model, "null"); - else + } else { controllaCompleti(model, idPrenotazione.toString()); + } controllaExtra(model); sommaColonne(model, flagPrenotazionePagata); - + //inizio 21-12-18<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if (jbtnChiudi.isEnabled()) { if (saldoAcquisti(0.0) == false @@ -1911,7 +2082,7 @@ public class PuntoCassa extends JFrame implements ActionListener { && (bonusResidui <= 0 || flagBonus == 0) && consentiCredito == false) { JOptionPane.showOptionDialog(this, "Saldo tessera insufficiente!", "Attenzione", - JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null); return; } } else { @@ -1928,8 +2099,7 @@ public class PuntoCassa extends JFrame implements ActionListener { } catch (Exception ex) { System.out.println(ex.getMessage()); } - } - else if(idPrenotazione > 0 && idPrenDettaglio.equals("null")){ + } else if (idPrenotazione > 0 && idPrenDettaglio.equals("null")) { JOptionPane.showMessageDialog(this, "Non è possibile modificare prodotti e composizioni se è presente una prenotazione collegata alla tessera"); } } @@ -1995,10 +2165,10 @@ public class PuntoCassa extends JFrame implements ActionListener { /** * funzione che calcola i prezzi dei prodotti e il totale in base alla - * fascia dello studente oppure utilizza la fascia di default. - * Se flagPrenotazionePagata = true vuol dire che non va modificato il saldo - * residuo della tessera perchè la somma delle colonne viene richiamata - * a seguito della verifica di una prenotazione pagata + * fascia dello studente oppure utilizza la fascia di default. Se + * flagPrenotazionePagata = true vuol dire che non va modificato il saldo + * residuo della tessera perchè la somma delle colonne viene richiamata a + * seguito della verifica di una prenotazione pagata * * @param model * @param flagPrenotazionePagata @@ -2011,8 +2181,9 @@ public class PuntoCassa extends JFrame implements ActionListener { } DecimalFormat df2 = new DecimalFormat("#,###,###,##0.00"); - if(!flagPrenotazionePagata) + if (!flagPrenotazionePagata) { txtTotaleCassa.setText("€ " + df2.format(t)); + } int p = model.sommaInt(2); txtTotalePunti.setText("Punti " + p); @@ -2413,7 +2584,7 @@ public class PuntoCassa extends JFrame implements ActionListener { // ** Quando viene chiusa la form esce dall'applicazione // ========================================================================== private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm - + Object selectedValue = JOptionPane.showOptionDialog(this, "Confermi chiusura programma?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[1]); @@ -2704,7 +2875,7 @@ public class PuntoCassa extends JFrame implements ActionListener { this); progressivoDifferito = Double.valueOf(progDI); aggiornaLabelInfo(); - + if (cercaTestoModoPagamento().equalsIgnoreCase("A scalare") && jTxtTessera.getText().length() > 0) { verificaSaldo(jTxtTessera.getText()); @@ -2834,10 +3005,9 @@ public class PuntoCassa extends JFrame implements ActionListener { } } private void jbtnEliminaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtnEliminaActionPerformed - if(idPrenotazione > 0){ + if (idPrenotazione > 0) { JOptionPane.showMessageDialog(this, "Non è possibile modificare prodotti e composizioni se è presente una prenotazione collegata alla tessera"); - } - else { + } else { cancellaComposizioni(); MyTableModel model = (MyTableModel) tblListaProdotti.getModel(); Integer riga = tblListaProdotti.getSelectedRow(); @@ -2875,7 +3045,7 @@ public class PuntoCassa extends JFrame implements ActionListener { } doLayout(); } - } + } }//GEN-LAST:event_jbtnEliminaActionPerformed private void jbtnChiudiActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtnChiudiActionPerformed @@ -2972,7 +3142,7 @@ public class PuntoCassa extends JFrame implements ActionListener { private void jbtnEsciActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtnEsciActionPerformed - + Object selectedValue = JOptionPane.showOptionDialog(this, "Confermi chiusura sessione?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[0]); if (Integer.parseInt(selectedValue.toString()) == 0) { @@ -3065,7 +3235,7 @@ public class PuntoCassa extends JFrame implements ActionListener { private void aggiornaDB(Boolean messaggio) { try { Boolean aggiorna = false; - if (messaggio) { + if (messaggio) { Object selectedValue = JOptionPane.showOptionDialog(this, "Confermi aggiornamento del database locale?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[0]); if (Integer.parseInt(selectedValue.toString()) != 0) { @@ -3164,8 +3334,7 @@ public class PuntoCassa extends JFrame implements ActionListener { }//GEN-LAST:event_btnImpostaClienteActionPerformed private void jbtnAnnullaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtnAnnullaActionPerformed - if(idPrenotazione > 0) - { + if (idPrenotazione > 0) { Object selectedValue = JOptionPane.showOptionDialog(this, "Desideri bypassare la prenotazione?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[0]); if (Integer.parseInt(selectedValue.toString()) == 0) { @@ -3175,11 +3344,9 @@ public class PuntoCassa extends JFrame implements ActionListener { flagPrenotazionePagata = false; } //JOptionPane.showMessageDialog(this, "Non è possibile modificare prodotti e composizioni se è presente una prenotazione collegata alla tessera"); - } - else - { + } else { MyTableModel model = (MyTableModel) tblListaProdotti.getModel(); - if (model.getRowCount() > 0) { + if (model.getRowCount() > 0) { Object selectedValue = JOptionPane.showOptionDialog(this, "Desideri annullare tutte le righe?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[0]); if (Integer.parseInt(selectedValue.toString()) == 0) { @@ -3193,7 +3360,7 @@ public class PuntoCassa extends JFrame implements ActionListener { try { MyTableModel model = (MyTableModel) tblListaProdotti.getModel(); MyTableModel model1 = (MyTableModel) tblListaComposizioni.getModel(); - + model.clearTable(); model1.clearTable(); jbtnSu.setEnabled(false); @@ -3484,7 +3651,7 @@ public class PuntoCassa extends JFrame implements ActionListener { //l'aggiornamento del residuo va fatto solo se non c'è una prenotazione in corso //oppure se la prenotazione in corso non risulta pagata, altrimenti il residuo //resta pari al saldo della tessera - if(idPrenotazione == 0 || !flagPrenotazionePagata){ + if (idPrenotazione == 0 || !flagPrenotazionePagata) { MyTableModel model = (MyTableModel) tblListaProdotti.getModel(); Double t = model.somma(colImporto); MyTableModel model2 = (MyTableModel) tblListaComposizioni.getModel(); @@ -3493,11 +3660,10 @@ public class PuntoCassa extends JFrame implements ActionListener { } //--------------------------- lblResiduo.setText("€ " + df2.format(saldo - t)); - } - else { + } else { lblResiduo.setText("€ " + df2.format(saldo)); } - + lblPunti.setText(punti); String tipo = cercaTestoModoPagamento(); lblTipoPagamento.setText(tipo); @@ -3623,7 +3789,6 @@ public class PuntoCassa extends JFrame implements ActionListener { } } - private void chiudi() { //04-02-2019--------------------------------------------- @@ -3664,8 +3829,7 @@ public class PuntoCassa extends JFrame implements ActionListener { return; } } - } - else { + } else { //mostra messaggio ma non cancella i piatti JOptionPane.showMessageDialog(this, "Saldo insufficiente!", "Saldo", JOptionPane.OK_OPTION); return; @@ -3794,8 +3958,8 @@ public class PuntoCassa extends JFrame implements ActionListener { + "id_Turno,flag_gratuita,flag_bonus,flag_asporto,flag_passaggio_tessera, id_pren_dettaglio)" + "values(-1," + tessera + "," + model2.getValueAt(i, 2) + "," + idPuntoCassa + "," + //formattaDataOra(adesso) + "," + model2.getValueAt(i, 1) + ",0," + progressivo + "," + - formatLocalDateTime(adesso) + "," - + model2.getValueAt(i, 1) + formatLocalDateTime(adesso) + "," + + model2.getValueAt(i, 1) + ",0," + progressivo + "," + idModoPagamento + "," + idTurno + "," + flagGratuita + "," + flagBonus + "," + flagAsporto + "," + flagPassaggioTessera + "," + model2.getValueAt(i, colIdPrenotazione) + ")", @@ -3805,7 +3969,8 @@ public class PuntoCassa extends JFrame implements ActionListener { spiaProgressivo = true; } - } /*else { + } + /*else { StringBuilder elencoIdProdotti = new StringBuilder(); for (int i = model.getRowCount() - 1; i >= 0; i--) { Long idCateg = Long.valueOf(model.getValueAt(i, colCategoria).toString()); @@ -3906,11 +4071,12 @@ public class PuntoCassa extends JFrame implements ActionListener { } /** - * Verifica se i prodotti selezionati differiscono da quelli presenti - * nella prenotazione collegata alla tessera. L'utente può scegliere di - * modificare i prodotti, oppure di azzerare la lista + * Verifica se i prodotti selezionati differiscono da quelli presenti nella + * prenotazione collegata alla tessera. L'utente può scegliere di modificare + * i prodotti, oppure di azzerare la lista + * * @param elencoIdProdotto - * @return + * @return */ private boolean controllaDiscrepanzePrenotazioni(String elencoIdProdotto) { boolean ok = true; @@ -3938,7 +4104,7 @@ public class PuntoCassa extends JFrame implements ActionListener { return true; } } - + Object selectedValue = JOptionPane.showOptionDialog(this, "I prodotti selezionati sono diversi dai prenotati!\nVuoi proseguire?", "Prenotazione", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, siNoOptions, null); @@ -3946,7 +4112,7 @@ public class PuntoCassa extends JFrame implements ActionListener { azzeraDopoStorno(); return false; } - + } } return ok; @@ -4229,7 +4395,7 @@ public class PuntoCassa extends JFrame implements ActionListener { } lblScadTessera.setText("" + bonusResidui); - + if (flagPrecaricaLista) { forzaPrezzi(model); } @@ -4264,8 +4430,6 @@ public class PuntoCassa extends JFrame implements ActionListener { jBtnAsportoActionPerformed(null); } - - if (abilitatoPassaggi != 0L) { controllaPrenotazioni(); } @@ -4301,7 +4465,7 @@ public class PuntoCassa extends JFrame implements ActionListener { String queryPassaggi = "select count(*) as NRec from st_acquisti a inner join prodotti p on a.id_prodotto=p.id inner join Tipi_Pagamenti tp on a.id_tipo_pagamento=tp.id where TRUNC(data) = TRUNC(SYSDATE) and id_tessera=" + idTessera + " and flag_in_vassoio=0 and id_punto_cassa=" + idPuntoCassa; Long numeroPassaggi = Utils.mySelectInteger(queryPassaggi, "NRec", this); //fine---- - if (flagEsegueStorni && numeroPassaggi > 0) { + if (flagEsegueStorni && numeroPassaggi > 0) { Object selectedValue = JOptionPane.showOptionDialog(this, "N° passaggi giornalieri esauriti. Non è possibile usufruire del pasto!\nSi desidera effettuare uno storno per un pasto erroneamente addebitato?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[1]); if (Integer.parseInt(selectedValue.toString()) == 0) { @@ -4323,7 +4487,7 @@ public class PuntoCassa extends JFrame implements ActionListener { // controllo abilitazione tessera String controlloPunto = "Select controlla_abilitazione('" + numeroTessera + "'," + idPuntoCassa + ") as T from dual"; Long abilitato = Utils.mySelectInteger(controlloPunto, "T", this); - if (abilitato == 0) { + if (abilitato == 0) { Object selectedValue = JOptionPane.showOptionDialog(this, "Tessera non abilitata per questo punto di distribuzione!\nConsenti passaggio?", "Attenzione", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, siNoOptions, siNoOptions[0]); if (Integer.parseInt(selectedValue.toString()) != 0) { @@ -4348,7 +4512,7 @@ public class PuntoCassa extends JFrame implements ActionListener { + "order by id_prenotazione) " + "where rownum = 1"; String idP = Utils.mySelect(query, "nr", this); - if (idP.length() > 0) { + if (idP.length() > 0) { idPrenotazione = Long.valueOf(idP); if (idPrenotazione > 0L) { query = "SELECT Distinct Nome FROM VIEW_PRENOTAZIONI_TURNO v join prodotti p on v.ID_PRODOTTO=p.ID " @@ -4407,14 +4571,14 @@ public class PuntoCassa extends JFrame implements ActionListener { while (rs.next()) { //l'ID nella VIEW_PRENOTAZIONI_TURNO corrisponde all'ID della tabella SIR.PRENOTAZIONI_PASTI_DETTAGLIO //idPrenDettaglio = Long.valueOf(rs.getString("ID")); - - boolean flagPagato = Double.parseDouble(rs.getString("IMPORTO_PAGATO")) > 0.0; - EventParameters evtParams = new EventParameters(rs.getString("ID_PRODOTTO"), - null, - flagPagato, - rs.getString("ID")); - - java.awt.event.ActionEvent evt; + + boolean flagPagato = Double.parseDouble(rs.getString("IMPORTO_PAGATO")) > 0.0; + EventParameters evtParams = new EventParameters(rs.getString("ID_PRODOTTO"), + null, + flagPagato, + rs.getString("ID")); + + java.awt.event.ActionEvent evt; evt = new java.awt.event.ActionEvent(evtParams, 0, "PRODOTTO[" + rs.getString("ID_PRODOTTO") + "]"); actionPerformed(evt); @@ -4457,8 +4621,10 @@ public class PuntoCassa extends JFrame implements ActionListener { } /** - * Calcolo dei prezzi dei prodotti sulla base del profilo tariffario corrente - * @param model + * Calcolo dei prezzi dei prodotti sulla base del profilo tariffario + * corrente + * + * @param model */ private void forzaPrezzi(MyTableModel model) { for (int y = 0; y < model.getRowCount(); y++) { @@ -4840,13 +5006,14 @@ public class PuntoCassa extends JFrame implements ActionListener { } } } - + /** - * Metodo che stampa un messaggio sul POS - * Se il POS è chiuso, stampa a prescindere, altrimenti stampa solo se non è stata letta una tessera - * @param flagAperto + * Metodo che stampa un messaggio sul POS Se il POS è chiuso, stampa a + * prescindere, altrimenti stampa solo se non è stata letta una tessera + * + * @param flagAperto */ - private void stampaSuPos(boolean flagAperto){ + private void stampaSuPos(boolean flagAperto) { String idPuntoCassaString = idPuntoCassa.toString(); if (idPuntoCassaString.length() >= 5) { idPuntoCassaString = idPuntoCassaString.substring(idPuntoCassaString.length() - 5, idPuntoCassaString.length()); @@ -4854,11 +5021,10 @@ public class PuntoCassa extends JFrame implements ActionListener { idPuntoCassaString = Utils.spaziBianchi(idPuntoCassaString, 5, false); } - if(flagAperto && jTxtTessera.getText().trim().length() <= 0){ + if (flagAperto && jTxtTessera.getText().trim().length() <= 0) { String riga = Utils.spaziBianchi(messageDisplay, 20, true) + "POS " + idPuntoCassaString + " APERTO"; display(riga, null); - } - else if (!flagAperto){ + } else if (!flagAperto) { String riga = Utils.spaziBianchi(messageDisplay, 20, true) + "POS " + idPuntoCassaString + " CHIUSO"; display(riga, null); } diff --git a/src/puntocassa/utils/QRScanner.java b/src/puntocassa/utils/QRScanner.java new file mode 100644 index 0000000..30e714b --- /dev/null +++ b/src/puntocassa/utils/QRScanner.java @@ -0,0 +1,51 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package puntocassa.utils; + +/** + * + * @author assis + */ +import com.github.sarxos.webcam.Webcam; +import com.google.zxing.*; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +public class QRScanner { + + public static void main(String[] args) { + Webcam webcam = Webcam.getDefault(); + webcam.setViewSize(new Dimension(1280, 720)); + webcam.open(); + + while (true) { + BufferedImage image = webcam.getImage(); + if (image == null) continue; + + LuminanceSource source = new BufferedImageLuminanceSource(image); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + + try { + List dataResult = new ArrayList<>(); + Result result = new MultiFormatReader().decode(bitmap); + System.out.println("QR Code trovato: " + result.getText()); + //decripting + dataResult = QrCryptoService.decryptAndValidate(result.getText()); + System.out.println("QR code decriptato: " + dataResult); + break; + } catch (NotFoundException e) { + // Nessun QR code trovato nel frame + } + } + + webcam.close(); + } +} + diff --git a/src/puntocassa/utils/QrCryptoService.java b/src/puntocassa/utils/QrCryptoService.java index cd2d6ba..7da95fc 100644 --- a/src/puntocassa/utils/QrCryptoService.java +++ b/src/puntocassa/utils/QrCryptoService.java @@ -9,97 +9,171 @@ package puntocassa.utils; * @author assis */ import javax.crypto.Cipher; -import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; import java.security.SecureRandom; +import java.time.Duration; import java.time.OffsetDateTime; import java.util.Base64; +import javax.crypto.spec.IvParameterSpec; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.time.*; +import java.util.*; +import org.json.JSONException; import org.json.JSONObject; public class QrCryptoService { - // Chiave AES 256-bit (32 byte) - hardcoded per test - private static final byte[] AES_KEY = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.UTF_8); - - // Crea il JSON cifrato da inserire nel QR - public static String createEncryptedQr(String uid, OffsetDateTime issuedAt, OffsetDateTime expiresAt) throws Exception { - // Crea il payload JSON con i dati utente - String payloadJson = new JSONObject() + private static final byte[] AES_KEY = hexToBytes("3031323334353637383961626364656630313233343536373839616263646566"); + private static final Duration CLOCK_SKEW = Duration.ofSeconds(60); + + public static String createEncryptedQr(String uid, OffsetDateTime issuedAt, OffsetDateTime expiresAt) throws GeneralSecurityException, JSONException { + JSONObject payload = new JSONObject() .put("uid", uid) .put("issued_at", issuedAt.toString()) - .put("expires_at", expiresAt.toString()) - .toString(); + .put("expires_at", expiresAt.toString()); - // Genera un Initialization Vector (IV) da 96 bit - byte[] iv = new byte[12]; - new SecureRandom().nextBytes(iv); + byte[] iv = SecureRandom.getInstanceStrong().generateSeed(16); - // Cifra il payload con AES-GCM - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES"); - GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); - byte[] encrypted = cipher.doFinal(payloadJson.getBytes(StandardCharsets.UTF_8)); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(AES_KEY, "AES"), new IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(payload.toString().getBytes(StandardCharsets.UTF_8)); - // Codifica IV e dati cifrati in Base64 - String ivBase64 = Base64.getEncoder().encodeToString(iv); - String encryptedBase64 = Base64.getEncoder().encodeToString(encrypted); + // Concatenazione IV + encrypted + ByteBuffer buffer = ByteBuffer.allocate(iv.length + encrypted.length); + buffer.put(iv); + buffer.put(encrypted); + byte[] combined = buffer.array(); - // Costruisce il JSON finale con iv e data - JSONObject qrJson = new JSONObject() - .put("iv", ivBase64) - .put("data", encryptedBase64); + // Codifica tutto in Base64 + return Base64.getEncoder().encodeToString(combined); + } + + + public static List decryptAndValidate(String base64Qr) { + List resultData = new ArrayList<>(); + try { + // Step 1: Decodifica base64 del QR + byte[] decoded = Base64.getDecoder().decode(base64Qr); + String jsonString = new String(decoded, StandardCharsets.UTF_8); - return qrJson.toString(2); // con indentazione + // Step 2: Parsing JSON + JSONObject qrObject = new JSONObject(jsonString); + String ivBase64 = qrObject.getString("iv"); + String dataBase64 = qrObject.getString("data"); + + // Step 3: Decodifica separata di IV e data + byte[] iv = Base64.getDecoder().decode(ivBase64); + byte[] ciphertext = Base64.getDecoder().decode(dataBase64); + + // Step 4: Decrittazione AES + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(AES_KEY, "AES"), new IvParameterSpec(iv)); + byte[] decrypted = cipher.doFinal(ciphertext); + + // Step 5: Parsing del payload decifrato + JSONObject payload = new JSONObject(new String(decrypted, StandardCharsets.UTF_8)); + ValidationResult result = validatePayload(payload); + + if (result.valid()) { + resultData.add("VALID"); + resultData.add(result.uid()); + } else { + resultData.add("NOT VALID"); + resultData.add("Errore validazione QR code: " + result.reason()); + } + + } catch (Exception e) { + resultData.add("NOT VALID"); + resultData.add("Errore di decifratura o struttura non valida. QR letto: " + base64Qr); + } + + return resultData; } - // Decifra il JSON e verifica se è ancora valido - public static String decryptAndValidate(String qrJson) throws Exception { - JSONObject obj = new JSONObject(qrJson); - byte[] iv = Base64.getDecoder().decode(obj.getString("iv")); - byte[] encryptedData = Base64.getDecoder().decode(obj.getString("data")); - // Decrittazione con AES-GCM - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES"); - GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); - cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec); - byte[] decryptedBytes = cipher.doFinal(encryptedData); - // Parsing del payload decifrato - JSONObject payload = new JSONObject(new String(decryptedBytes, StandardCharsets.UTF_8)); - String uid = payload.getString("uid"); - OffsetDateTime issuedAt = OffsetDateTime.parse(payload.getString("issued_at")); - OffsetDateTime expiresAt = OffsetDateTime.parse(payload.getString("expires_at")); - boolean isExpired = OffsetDateTime.now(expiresAt.getOffset()).isAfter(expiresAt); + private static ValidationResult validatePayload(JSONObject payload) { + try { + String uid = payload.getString("uid"); + + //con timezone esplicito + OffsetDateTime issuedAt = OffsetDateTime.parse(payload.getString("issued_at")); + OffsetDateTime expiresAt = OffsetDateTime.parse(payload.getString("expires_at")); + Instant now = Instant.now(); + Instant iat = issuedAt.toInstant(); + Instant exp = expiresAt.toInstant(); - return String.format( - "Dati decifrati:\n" + - "- UID: %s\n" + - "- Emesso: %s\n" + - "- Scadenza: %s\n" + - "- Stato: %s\n", - uid, - issuedAt, - expiresAt, - isExpired ? "SCADUTO" : "VALIDO" - ); + if (!uid.matches("[A-Z0-9]{16}")) + return ValidationResult.invalid("Formato UID non valido"); + if (exp.isBefore(iat)) + return ValidationResult.invalid("Scadenza antecedente all'emissione"); + if (now.plus(CLOCK_SKEW).isBefore(iat)) + return ValidationResult.invalid("QR non ancora valido"); + if (now.minus(CLOCK_SKEW).isAfter(exp)) + return ValidationResult.invalid("QR scaduto"); + + //senza timezone esplicito + /*LocalDateTime issuedAt = LocalDateTime.parse(payload.getString("issued_at")); + LocalDateTime expiresAt = LocalDateTime.parse(payload.getString("expires_at")); + + ZoneId zone = ZoneId.systemDefault(); // oppure un valore fisso come ZoneId.of("Europe/Rome") + + Instant now = Instant.now(); + Instant iat = issuedAt.atZone(zone).toInstant(); + Instant exp = expiresAt.atZone(zone).toInstant(); + + if (!uid.matches("[A-Z0-9]{16}")) + return ValidationResult.invalid("Formato UID non valido"); + + if (exp.isBefore(iat)) + return ValidationResult.invalid("Scadenza antecedente all'emissione"); + + if (now.plus(CLOCK_SKEW).isBefore(iat)) + return ValidationResult.invalid("QR non ancora valido"); + + if (now.minus(CLOCK_SKEW).isAfter(exp)) + return ValidationResult.invalid("QR scaduto");*/ + + + return ValidationResult.valid(uid, iat, exp); + + } catch (Exception e) { + return ValidationResult.invalid("Errore di validazione QR Code: Payload malformato o incompleto"); + } + } + + public static byte[] hexToBytes(String hex) { + return new BigInteger(hex, 16).toByteArray().length == 33 + ? Arrays.copyOfRange(new BigInteger(hex, 16).toByteArray(), 1, 33) + : new BigInteger(hex, 16).toByteArray(); + } + + public record ValidationResult(boolean valid, String reason, String uid, Instant issuedAt, Instant expiresAt) { + public static ValidationResult valid(String uid, Instant issuedAt, Instant expiresAt) { + return new ValidationResult(true, null, uid, issuedAt, expiresAt); + } + + public static ValidationResult invalid(String reason) { + return new ValidationResult(false, reason, null, null, null); + } } - // Test completo della generazione e verifica public static void main(String[] args) throws Exception { - //data valida - OffsetDateTime issuedAt = OffsetDateTime.now().withNano(0); - //data scaduta - //OffsetDateTime issuedAt = OffsetDateTime.now().minusMinutes(5).withNano(0); - OffsetDateTime expiresAt = issuedAt.plusSeconds(60); - String qrJson = createEncryptedQr("TSTTST91A48A271O", issuedAt, expiresAt); + List dataResult = new ArrayList<>(); + OffsetDateTime now = OffsetDateTime.now().withNano(0); + OffsetDateTime expiration = now.plusSeconds(CLOCK_SKEW.getSeconds()); - System.out.println("QR JSON simulato:"); - System.out.println(qrJson); + //String qr = createEncryptedQr("TSTTST91A48A271O", now, expiration); + String qr = "eyJpdiI6Ik0xTVJvR3dMbm40UGFMalFEVUh5M1E9PSIsImRhdGEiOiJSVERTTkFURi8wK0lrekFGVVRaSkMrb1Y5QWVZcFRnMFFVKzgzY25QUVgrM2ZMVkRMV3kyOFRsMnBIQTlBRWVGTFQvK0pxSWNtblBYRnBjMys5T0tVWHJpc3FQc1VHazUxRzFpSERSOFJ1eEFCSWhPaTZlamlkdHU4d090WmRzY2F2Y0FRbmNESTd3bGxJNWgvOFFmZnc9PSJ9"; + System.out.println("QR generato:\n" + qr + "\n"); - System.out.println("\nVerifica decifratura:"); - System.out.println(decryptAndValidate(qrJson)); + dataResult = decryptAndValidate(qr); + System.out.println("Verifica:\n" + dataResult); } } + + +