Compare commits

...

3 Commits

Author SHA1 Message Date
4489f3d311 Merge pull request '0002566-lettura-tessera-dematerializzata-tramite-QR-code' (#8) from 0002566-lettura-tessera-dematerializzata-tramite-QR-code into dev
Reviewed-on: #8
2025-08-29 14:14:33 +00:00
f8fba1fec3 modifica timeout lettura
ridotto per velocizzare il processo di lettura di qr code.
la classe qrscanner è utilizzata solo a fini di test per la lettura di qr code con webcam.
nel frmcerca aggiunto campo nascosto per gestire la lettura del qr code senza mostrarlo a video, viene inserito il numero di tessera solo se i controlli vanno a buon fine
nel form principale (puntocassa) non è stato necessario, in quanto ci sono thread in ascolto che leggono i caratteri (emulazione tastiera), validi sia per il qr code che per rfid.
nella classe qrcryptoservice.java è inserito il timer del qr code (CLOCK_SKEW) a 60 secondi, nonchè la chiave AES per la decriptazione (AES_KEY): dovrà essere modificata quando si andrà in produzione.
L'algoritmo di decriptazione usato per il qr code è AES-256 con CBC.
2025-08-29 16:12:47 +02:00
92995d5658 creazione classe per gestione qr code 2025-07-21 12:29:32 +02:00
5 changed files with 672 additions and 145 deletions

View File

@ -73,6 +73,8 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="lblCF" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="61" max="-2" attributes="0"/>
<Component id="jtxtNumTesseraHidden" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
@ -98,6 +100,8 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="jLblTitolo" min="-2" pref="25" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
@ -105,7 +109,13 @@
<Component id="lblNome" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblCF" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="jtxtNumTesseraHidden" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jtxtNumTessera" alignment="3" min="-2" pref="40" max="-2" attributes="0"/>
@ -263,6 +273,13 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jbtnGiuActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="jtxtNumTesseraHidden">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -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
*/
@ -102,6 +117,43 @@ private JTextField jtxtTessera;
Disabilita();
}
jtxtNumTesseraHidden.addActionListener(e -> {
System.out.println("QR letto: " + jtxtNumTesseraHidden.getText().trim());
String valueRead = jtxtNumTesseraHidden.getText().trim();
//decripting
List<String> 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()
.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))
.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<String> 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;

View File

@ -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;
//******************************************************************************
@ -226,7 +230,6 @@ public class PuntoCassa extends JFrame implements ActionListener {
settingFontsInterfaccia();
jcmbSmartCard.setVisible(false);
visualizzaMessaggiDisplay = document.getElementsByTagName("Visualizza_Messaggi_Display").item(0).getFirstChild().getNodeValue()
@ -236,7 +239,6 @@ public class PuntoCassa extends JFrame implements ActionListener {
aggiornaSmartCardReaderCombo(myApp);
String ultimiMovimenti = Utils.mySelect("SELECT valore FROM parametri WHERE chiave = 'FlagVisualizzaUltimiPassaggi'",
"valore",
this);
@ -314,7 +316,6 @@ public class PuntoCassa extends JFrame implements ActionListener {
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,6 +332,7 @@ public class PuntoCassa extends JFrame implements ActionListener {
/**
* Lettura del nodo 'display' del file di configurazione
*
* @param document
* @param myApp
* @throws NumberFormatException
@ -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------------------------------------------------------
}
return true;
}
@ -553,7 +556,15 @@ 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 (!isQrCode) {
if (testoSwipCard.toString().contains("ì")) {
String[] txt = testoSwipCard.toString().split("ì");
@ -607,20 +618,180 @@ public class PuntoCassa extends JFrame implements ActionListener {
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<String> 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,6 +872,7 @@ public class PuntoCassa extends JFrame implements ActionListener {
/**
* Aggiorna la lista di lettori smartcard disponibili
*
* @param myApp
*/
private void aggiornaSmartCardReaderCombo(MyApplication myApp) {
@ -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, "
@ -1842,8 +2014,6 @@ public class PuntoCassa extends JFrame implements ActionListener {
+ "where prodotti.id = " + id + " and Tariffe.id_fascia=" + idProfiloTariffario;
try {
//spengo altri togglebutton
for (Integer y = 0; y < this.pnlProdotti.getComponentCount(); y++) {
JToggleButton bt = (JToggleButton) this.pnlProdotti.getComponent(y);
@ -1896,10 +2066,11 @@ 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);
@ -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);
@ -2836,8 +3007,7 @@ public class PuntoCassa extends JFrame implements ActionListener {
private void jbtnEliminaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtnEliminaActionPerformed
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();
@ -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,9 +3344,7 @@ 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) {
Object selectedValue = JOptionPane.showOptionDialog(this, "Desideri annullare tutte le righe?", "Attenzione",
@ -3493,8 +3660,7 @@ public class PuntoCassa extends JFrame implements ActionListener {
}
//---------------------------
lblResiduo.setText("" + df2.format(saldo - t));
}
else {
} else {
lblResiduo.setText("" + df2.format(saldo));
}
@ -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;
@ -3810,7 +3974,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());
@ -3911,9 +4076,10 @@ 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
*/
@ -4269,8 +4435,6 @@ public class PuntoCassa extends JFrame implements ActionListener {
jBtnAsportoActionPerformed(null);
}
if (abilitatoPassaggi != 0L) {
controllaPrenotazioni();
}
@ -4462,7 +4626,9 @@ public class PuntoCassa extends JFrame implements ActionListener {
}
/**
* Calcolo dei prezzi dei prodotti sulla base del profilo tariffario corrente
* Calcolo dei prezzi dei prodotti sulla base del profilo tariffario
* corrente
*
* @param model
*/
private void forzaPrezzi(MyTableModel model) {
@ -4847,8 +5013,9 @@ 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
* 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) {
@ -4862,8 +5029,7 @@ public class PuntoCassa extends JFrame implements ActionListener {
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);
}

View File

@ -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<String> 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();
}
}

View File

@ -0,0 +1,179 @@
/*
* 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 javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
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 {
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());
byte[] iv = SecureRandom.getInstanceStrong().generateSeed(16);
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));
// Concatenazione IV + encrypted
ByteBuffer buffer = ByteBuffer.allocate(iv.length + encrypted.length);
buffer.put(iv);
buffer.put(encrypted);
byte[] combined = buffer.array();
// Codifica tutto in Base64
return Base64.getEncoder().encodeToString(combined);
}
public static List<String> decryptAndValidate(String base64Qr) {
List<String> resultData = new ArrayList<>();
try {
// Step 1: Decodifica base64 del QR
byte[] decoded = Base64.getDecoder().decode(base64Qr);
String jsonString = new String(decoded, StandardCharsets.UTF_8);
// 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;
}
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();
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);
}
}
public static void main(String[] args) throws Exception {
List<String> dataResult = new ArrayList<>();
OffsetDateTime now = OffsetDateTime.now().withNano(0);
OffsetDateTime expiration = now.plusSeconds(CLOCK_SKEW.getSeconds());
//String qr = createEncryptedQr("TSTTST91A48A271O", now, expiration);
String qr = "eyJpdiI6Ik0xTVJvR3dMbm40UGFMalFEVUh5M1E9PSIsImRhdGEiOiJSVERTTkFURi8wK0lrekFGVVRaSkMrb1Y5QWVZcFRnMFFVKzgzY25QUVgrM2ZMVkRMV3kyOFRsMnBIQTlBRWVGTFQvK0pxSWNtblBYRnBjMys5T0tVWHJpc3FQc1VHazUxRzFpSERSOFJ1eEFCSWhPaTZlamlkdHU4d090WmRzY2F2Y0FRbmNESTd3bGxJNWgvOFFmZnc9PSJ9";
System.out.println("QR generato:\n" + qr + "\n");
dataResult = decryptAndValidate(qr);
System.out.println("Verifica:\n" + dataResult);
}
}