Un sistema di cassa automatica costruito in 2 giorni per un negozio senza personale

Bohfula / ボーフラ
Scritto da:
Un sistema di cassa automatica costruito in 2 giorni per un negozio senza personale

Nota: Questo articolo è una traduzione automatica dal post originale in giapponese. Se notate errori di traduzione, vi preghiamo di farcelo sapere.

Ecco una spiegazione tecnica del sistema di cassa automatica completamente senza contanti che abbiamo costruito in soli due giorni per l'apertura del LOPPO Art Supply Shibuya Store (un punto vendita senza personale).

Introduzione: Il progetto del negozio senza personale LOPPO Art Supply

LOPPO Art Supply è un marchio di materiali artistici per l'animazione cel che è nato come hobby di Takahashi e che è stato commercializzato principalmente attraverso la vendita online. All'inizio di quest'anno, abbiamo deciso di affittare un piccolo locale come spazio eventi per LOPPO Art Supply. Avevamo pianificato di usare un angolo come magazzino, ma è nata l'idea: "Se comunque conserviamo le scorte qui, perché non trasformarlo in un punto vendita?"

Da tempo c'erano richieste per un negozio fisico, ma garantire le risorse umane necessarie per la gestione del negozio era stata una sfida. È stato allora che abbiamo concepito l'idea di un "negozio di materiali artistici senza personale". Pensate a un banco di verdure self-service, ma per materiali da animazione cel — un luogo da sogno (o forse folle!) dove si può comprare 24 ore su 24, 365 giorni all'anno.

Negozio di materiali artistici senza personale (immagine concettuale) Negozio di materiali artistici senza personale (immagine concettuale)

1. Sfide delle soluzioni di cassa automatica esistenti

Per creare un negozio senza personale che non richieda staff in loco, è essenziale un sistema di cassa completamente automatizzato. Per questo progetto, abbiamo deciso di supportare solo pagamenti senza contanti come misura di prevenzione contro i furti.

Inizialmente abbiamo considerato soluzioni di cassa automatica commerciali, ma abbiamo incontrato due problemi principali:

  1. Elevati costi fissi mensili oltre ai costi iniziali di configurazione
  2. Lunghi cicli tra la liquidazione e l'accredito

Per garantire la sostenibilità, è importante mantenere i costi fissi al minimo mantenendo un flusso di cassa sano. È stato allora che abbiamo rivolto la nostra attenzione al sistema di pagamento Square, che avevamo già utilizzato nelle vendite durante eventi.

Square supporta un'ampia gamma di metodi di pagamento, non prevede canoni mensili (solo commissioni per transazione) e offre accrediti rapidi — già dal giorno lavorativo successivo. Inoltre, la Square API consente la creazione di applicazioni personalizzate. Poiché possedevamo già un Square Terminal, abbiamo stabilito che il suo utilizzo avrebbe mantenuto bassi anche i costi iniziali.

Tuttavia, c'era un vincolo importante nello sviluppo. Eravamo già sovraccarichi con la produzione di prodotti per l'apertura del negozio e potevamo dedicare solo appena 2 giorni allo sviluppo del sistema di cassa automatica.

2. Progettazione del sistema: Una cassa automatica sicura e facile da usare

Architettura generale

Diagramma dell'architettura del sistema

Il sistema è composto dai seguenti componenti principali:

  • Server applicativo Linux: Frontend React e backend Express
  • Sistema in negozio: Terminale client in modalità kiosk Windows 11 Pro, periferiche e Square Terminal
  • Servizi Square: Square API, dati anagrafici dei prodotti
  • Monitoraggio e operazioni: Telecamere di sicurezza, gruppo di continuità (UPS), ridondanza di rete

Dal punto di vista del cliente, sono visibili solo il monitor touch, lo scanner di codici a barre e il Square Terminal. Il client funziona in modalità kiosk di Windows 11 Pro, mentre la logica applicativa principale risiede su una macchina Linux situata al di fuori del negozio.

Sicurezza e rete

La rete opera in un ambiente VPN utilizzando Tailscale, che protegge la comunicazione tra il terminale client e il server Linux. Inoltre, tutti i dispositivi sono collegati a un gruppo di continuità per la protezione contro i fulmini e le interruzioni di corrente, e la rete è configurata con ridondanza per garantire un funzionamento stabile.

L'adozione di Tailscale facilita anche la manutenzione remota. Nessun dato locale viene memorizzato su alcun terminale — tutti i dati vengono recuperati dai sistemi di Square.

Configurazione hardware

  • Monitor touch
  • Scanner di codici a barre USB
  • Square Terminal (elaborazione pagamenti e stampa ricevute)
  • Telecamera di sicurezza (per il monitoraggio in tempo reale)

Implementazione del frontend

Schermata di selezione del metodo di pagamento Schermata di selezione del metodo di pagamento

Il frontend è costruito con React e comprende le seguenti schermate principali:

  1. Schermata di scansione prodotti
  2. Schermata di selezione del metodo di pagamento
  3. Schermata di elaborazione del pagamento
  4. Schermata di pagamento completato

Sebbene utilizzando la traduzione automatica, abbiamo anche implementato il supporto multilingue, coprendo 6 lingue: giapponese, inglese, francese, spagnolo, cinese tradizionale e cinese semplificato. Questo permette ai visitatori internazionali di utilizzare il sistema con fiducia.

// Esempio di configurazione delle lingue
const translations = {
  ja: {
    title: 'セルフレジシステム',
    scanTitle: '商品スキャン',
    // ...omesso
  },
  en: {
    title: 'Self-Checkout System',
    scanTitle: 'Product Scan',
    // ...omesso
  },
  // Altre lingue...
};

Abbiamo inoltre progettato il sistema per dare priorità all'input dello scanner di codici a barre, puntando a un'interfaccia che gli utenti possano utilizzare senza confusione.

3. Punti chiave dell'integrazione con Square API

Elaborazione dei pagamenti con Terminal API

Tra le Square API, la Terminal API è particolarmente importante. Ci permette di inviare richieste di elaborazione pagamenti al Square Terminal.

// Creare un checkout Terminal
app.post("/api/create-terminal-checkout", async (req, res) => {
  try {
    const { order, amountMoney, paymentType = "CARD_PRESENT" } = req.body;

    const ALLOWED = new Set([
      "CARD_PRESENT",
      "FELICA_TRANSPORTATION_GROUP",
      "FELICA_ID",
      "FELICA_QUICPAY",
      "QR_CODE"
    ]);

    if (!ALLOWED.has(paymentType)) {
      return res.status(400).json({ error: "Metodo di pagamento non supportato specificato" });
    }

    // Creare prima l'ordine
    const orderId = await createOrder(order);

    // Creare il Square Terminal Checkout
    const checkoutResponse = await squareClient.terminal.checkouts.create({
      idempotencyKey: randomUUID(),
      checkout: {
        amountMoney: {
          // L'importo deve essere BigInt
          amount: BigInt(amountMoney.amount),
          currency: amountMoney.currency,
        },
        deviceOptions: {
          deviceId: SQUARE_DEVICE_ID,
          skip_receipt_screen: true,
          show_itemized_cart: false,
        },
        referenceId: orderId,
        orderId,
        note: "Pagamento alla cassa automatica LOPPO",
        paymentType: paymentType
      },
    });

    res.json(checkoutResponse);
  } catch (error) {
    handleError("Errore nella creazione del Terminal Checkout", error, res);
  }
});

Metodi di pagamento diversificati

Poiché il Square Terminal supporta un'ampia gamma di metodi di pagamento, i clienti possono pagare con il loro metodo preferito:

  • Carte di credito/debito
  • Carte IC per il trasporto (Suica/PASMO, ecc.)
  • iD
  • QUICPay
  • Pagamenti con codice QR (PayPay, ecc.)

Si prega di notare che le carte UnionPay non sono supportate.

Polling dello stato del pagamento

Poiché l'elaborazione del pagamento avviene sul Square Terminal, dobbiamo verificare lo stato tramite polling per rilevare il completamento o l'annullamento.

// Verificare lo stato del pagamento tramite polling
const checkPaymentStatus = async () => {
  try {
    const statusResponse = await fetch(`/api/get-checkout-status?checkoutId=${data.checkout.id}`);
    const statusData = await statusResponse.json();

    if (statusData.status === 'COMPLETED') {
      setPaymentStatus(t.paymentCompleted);
      // Elaborazione del completamento
      setTimeout(() => {
        setStatus('complete');
        setCart([]);
      }, 2000);
    } else if (statusData.status === 'CANCELED' || statusData.status === 'CANCEL_REQUESTED') {
      setPaymentStatus(t.paymentCanceled);
      setTimeout(() => {
        setStatus('ready');
      }, 3000);
    } else {
      // Se non ancora completato, verificare di nuovo
      setPaymentStatus(t.processing);
      setTimeout(checkPaymentStatus, 2000);
    }
  } catch (error) {
    console.error(t.statusCheckFailed, error);
    setPaymentStatus(t.statusCheckFailed);
    setTimeout(() => {
      setStatus('ready');
    }, 3000);
  }
};

Gestione dei dati anagrafici dei prodotti

Tutte le informazioni sui prodotti vengono registrate tramite la dashboard di Square e recuperate via API. Questo semplifica le attività operative come l'aggiunta di prodotti o la modifica dei prezzi.

app.get("/api/catalog-items", async (_req, res) => {
  try {
    const TYPES = "ITEM,ITEM_VARIATION,CATEGORY,IMAGE"; // Elencare tutti i tipi necessari
    //------------------------------------------------------------------
    // 1. Caricare tutto
    //------------------------------------------------------------------
    const objects = [];
    for await (const obj of await squareClient.catalog.list({ types: TYPES }))
      objects.push(obj);

    //------------------------------------------------------------------
    // 2. Creare prima le mappe per CATEGORY / IMAGE / VARIATION
    //------------------------------------------------------------------
    const imageMap     = {};
    const categoryMap  = {};
    const variationMap = {};

    // ...omesso (logica di creazione delle mappe)

    //------------------------------------------------------------------
    // 3. Espandere gli ITEM e incorporare le informazioni con le mappe create sopra
    //------------------------------------------------------------------
    const filtered = objects
      .filter((o) => o.type === "ITEM")
      .map((item) => {
        // ...omesso (logica di trasformazione dei dati)
      })
      // -- Applicare i filtri dei requisiti qui --
      .filter(
        (item) =>
          !item.isArchived &&
          item.categoryNames.includes("六方画材")
      );

    res.json(filtered);
  } catch (error) {
    handleError("Errore nel recupero degli articoli del catalogo", error, res);
  }
});

4. Sviluppo rapido con LLM

La caratteristica distintiva di questo progetto è che è stato completato in soli 2 giorni. Questo è stato reso possibile dall'uso degli LLM (Large Language Model).

Suddivisione del tempo di sviluppo

  • Sviluppo del sistema base: ~2 ore
  • Perfezionamento e regolazioni dell'UI: ~4 ore
  • Test e deployment: tempo rimanente

Come abbiamo utilizzato Claude 3.7 Sonnet

Abbiamo utilizzato principalmente Claude 3.7 Sonnet durante lo sviluppo per ottimizzare l'implementazione. Ha gestito non solo la logica applicativa ma anche il design dell'UI senza sforzo, e ha persino preparato la documentazione di setup — un assistente davvero completo. I suggerimenti dell'LLM sono stati particolarmente preziosi per il codice di supporto multilingue e l'integrazione con Square API.

Abbiamo anche provato a combinare ChatGPT 4o e ChatGPT o3, ma per quanto riguarda la comprensione delle applicazioni web, non erano all'altezza di 3.7 Sonnet.

Esempi pratici di utilizzo degli LLM

Questa è un'avvertenza comune quando si utilizzano gli LLM per lo sviluppo, ma è difficile utilizzare il codice generato così com'è — è essenziale comprenderlo e apportare le modifiche necessarie. Ad esempio, le seguenti correzioni sono state necessarie per l'integrazione con Square Terminal API:

  1. Aggiunta di metodi di pagamento: Il codice generato dall'LLM supportava solo i pagamenti con carta di credito, quindi abbiamo dovuto aggiungere una schermata di selezione del metodo di pagamento
  2. Gestione degli errori: La gestione degli eventi di annullamento del pagamento del Terminal era errata, quindi l'abbiamo corretta basandoci sulla documentazione dell'API
  3. Sicurezza: Parte della comunicazione tra applicazioni utilizzava protocolli non sicuri, quindi abbiamo costruito una VPN privata per proteggere il percorso di comunicazione

L'LLM ha fornito la struttura di base del codice, ma gli aggiustamenti per la produzione hanno dovuto essere effettuati manualmente.

5. Internazionalizzazione e usabilità

Implementazione multilingue

Per accogliere i visitatori internazionali, il sistema supporta 6 lingue: giapponese, inglese, francese, spagnolo, cinese tradizionale e cinese semplificato. Le impostazioni della lingua sono gestite all'interno dei componenti React, e tutti i testi sullo schermo vengono recuperati da oggetti di traduzione.

// Gestione dello stato di selezione della lingua
const [language, setLanguage] = useState('ja'); // Impostare la lingua predefinita su giapponese
// Ottenere le impostazioni della lingua
const t = translations[language];

// Esempio di utilizzo
<h1 className="text-4xl font-bold">{t.title}</h1>
<p className="text-lg text-gray-700 mb-6">
  {t.scanDescription}
</p>

Considerazioni sull'usabilità

Abbiamo mirato a un'esperienza utente simile alle casse automatiche dei supermercati e dei minimarket, incorporando le seguenti scelte di design:

  1. Priorità alla scansione dei codici a barre: L'input da tastiera è accettato ovunque nella pagina, dando sempre priorità all'input dello scanner di codici a barre
  2. Pulsanti grandi: Dimensioni dei pulsanti ottimizzate per un facile utilizzo touch
  3. Feedback chiaro: Messaggi facili da comprendere che mostrano i risultati delle operazioni

Grazie a queste scelte di design, crediamo di aver realizzato un'interfaccia che gli utenti possono utilizzare senza confusione.

6. Considerazioni operative

Monitoraggio in tempo reale e risposta agli incidenti

Nel negozio sono installate telecamere di rete, che ci permettono di verificare lo stato del negozio in tempo reale. In caso di problemi, i clienti possono chiamare il numero di telefono esposto in vetrina per ottenere assistenza.

Quando viene rilevata un'anomalia operativa, disponiamo di un sistema che consente di arrivare sul posto entro 30 minuti - 2 ore. Inoltre, come soluzione di riserva nel caso in cui il terminale di pagamento non funzioni, possiamo fornire un link di pagamento a posteriori per completare la transazione.

Per un funzionamento stabile, abbiamo anche un backup di alimentazione tramite UPS, ridondanza di rete e riavvii periodici durante i periodi di inattività.

C'è stato un incidente in cui il cavo di alimentazione del client si è allentato perché non era completamente inserito, ma da allora il sistema ha funzionato in modo molto stabile.

7. Risultati e impatto

Espansione delle opportunità di vendita

L'apertura del negozio senza personale ci ha permesso di garantire preziose opportunità di vendita 24/7 vicino a una stazione ferroviaria centrale. Il risultato più grande è stato soddisfare la domanda di un negozio fisico superando i vincoli delle risorse umane.

Tendenze dei metodi di pagamento

Tutti i metodi di pagamento implementati vengono utilizzati in modo abbastanza uniforme, ma i più popolari in ordine sono:

  1. Carte IC per il trasporto
  2. Pagamenti con codice QR (PayPay, ecc.)
  3. Carte di credito (pagamenti contactless)

8. Piani futuri ed espansione

Espansione alle vendite online

L'attuale negozio online LOPPO Art Supply è costruito su BASE, ma prevediamo di migrarlo a un sistema basato su Square API. Questo non solo ridurrà le commissioni per transazione, ma migliorerà anche il flusso di acquisto dei prodotti e unificherà la gestione dell'inventario e delle vendite tra il negozio fisico e le vendite online.

Inoltre, vorremmo offrire la possibilità di acquistare online e ritirare in negozio.

Conclusione: Se sai fare la vernice, puoi costruire un sistema di cassa

LOPPO ha riprodotto artigianalmente diversi materiali artistici per l'animazione cel, e questa volta abbiamo costruito artigianalmente un sistema di cassa.

Le applicazioni software possono talvolta essere costruite in modo relativamente semplice sfruttando le API esistenti e l'assistenza degli LLM, come dimostrato qui. Detto questo, non avremmo mai immaginato che un sistema di cassa potesse essere costruito così facilmente, ed è stata un'esperienza estremamente istruttiva.

Principi chiave

  1. Sfruttamento dei servizi esistenti: Massimizzare l'uso delle piattaforme esistenti come Square API
  2. Utilizzo di strumenti di supporto come gli LLM: Adozione proattiva di strumenti che aumentano l'efficienza dello sviluppo
  3. Focus sull'ambito minimo necessario: Mantenere l'implementazione semplice concentrandosi sulle funzionalità essenziali

Speriamo che questo articolo serva come riferimento utile per chiunque sia interessato a costruire un sistema di cassa automatica o stia considerando l'uso di Square API.

Il codice sorgente completo del sistema che abbiamo costruito è disponibile su GitHub. loppo-llc/loppo-register - GitHub

Bohfula / ボーフラ

Bohfula / ボーフラ

Uno sviluppatore di giochi indipendente con una particolare testa a forma di teiera. Spesso chiamato da Takahashi per aiutare con le operazioni e la pubblicità di LOPPO.