注意: 本文為從日文原文機器翻譯而來。如果您發現翻譯錯誤,請告知我們。
為了開設LOPPO畫材涉谷店(無人商店),我將解說在2天內構築的無現金完全自助收銀系統。
開始:LOPPO畫材的無人商店專案
LOPPO畫材是從高橋的興趣開始的賽璐璐畫材品牌,至今主要以網路購物為中心發展。 今年初,我們決定租借一個小店面作為LOPPO畫材的活動空間,本來計劃將其一角作為商品庫存放置場所,但後來想到「既然要放庫存,是否可以直接變成販售點」這個想法。
雖然以前就有實體店鋪的需求,但確保店鋪營運所需的人力資源是困難的。 於是我想到了「畫材無人商店」這種形態。就像蔬菜無人商店一樣,是一個可以24小時365天購買賽璐璐畫材的夢幻般(瘋狂的!)場所。
畫材無人商店(概念圖)
1. 既有自助收銀的課題與解決方案
要實現不需要常駐工作人員的無人商店,需要完全自助的收銀系統。這次以防盜對策為前提,僅支援無現金結帳。
最初我們考慮了市售的自助收銀解決方案,但有兩個重大課題。
- 除了初期成本外,月額固定費很高
- 到入帳的週期很長
為了提高持續性,重要的是將固定費控制在最小限度,同時保持良好的現金流。 因此我們注意到過去活動攤位時導入的Square結帳系統。
Square不僅支援多樣的結帳方式,月額固定費為零(僅收結帳手續費),最快隔日營業日入帳的迅速入帳週期很有魅力。 此外,通過使用Square API可以建立客製化應用程式。 由於已經擁有Square Terminal,透過活用這個設備也可以控制初期成本。
不過,在開發上有很大的限制。由於僅製作店鋪開店用商品的業務就已經忙不過來,自助收銀系統的開發只能分配僅僅2天的時間。
2. 系統設計:安全且易用的自助收銀
整體架構
系統大致分為以下組件構成。
- Linux應用程式伺服器:React前端和Express後端
- 店內系統:Windows 11 Pro Kiosk模式的客戶端終端、周邊設備、Square Terminal
- Square服務:Square API、商品主檔資料
- 監控運營系統:監控攝影機、不斷電電源裝置、網路冗餘化
從使用者看到的只有觸控螢幕、條碼掃描器,以及Square Terminal。客戶端以Windows 11 Pro的Kiosk模式運作,主要的應用程式邏輯在店外的Linux機器上。
安全性與網路
網路位於使用Tailscale的VPN環境下,這樣保護了客戶端終端與Linux伺服器間的通訊。 此外,所有設備的電源都連接到不斷電電源裝置以對抗雷擊和停電,網路也進行了冗餘化以確保穩定運作。
透過導入Tailscale,遠端維護也變得容易。 各終端完全不保存本地資料,全部從Square資料取得的機制。
硬體構成
- 觸控螢幕
- USB條碼掃描器
- Square Terminal(結帳處理・收據列印)
- 監控攝影機(即時監控用)
前端實作
結帳方式選擇畫面
前端使用React實作,由以下主要畫面構成。
- 商品掃描畫面
- 結帳方式選擇畫面
- 結帳處理中畫面
- 結帳完成畫面
雖然使用機器翻譯但也進行了多語言化,支援日語、英語、法語、西班牙語、繁體中文、簡體中文的6種語言。 這樣一來,訪日外國遊客也能安心使用。
// 語言設定的範例
const translations = {
ja: {
title: 'セルフレジシステム',
scanTitle: '商品スキャン',
// ...略
},
en: {
title: 'Self-Checkout System',
scanTitle: 'Product Scan',
// ...略
},
// 其他語言...
};
此外,設計為最優先接受條碼掃描器的輸入,致力於讓使用者不會迷惑的介面。
3. Square API活用的重點
透過Terminal API的結帳處理
Square API中特別重要的是Terminal API。透過使用這個,可以向Square Terminal請求結帳處理。
// 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: "指定了不支援的結帳方式" });
}
// 先建立訂單
const orderId = await createOrder(order);
// 建立Square Terminal Checkout
const checkoutResponse = await squareClient.terminal.checkouts.create({
idempotencyKey: randomUUID(),
checkout: {
amountMoney: {
// 金額必須是 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: "LOPPO自助收銀結帳",
paymentType: paymentType
},
});
res.json(checkoutResponse);
} catch (error) {
handleError("Terminal Checkout 建立錯誤", error, res);
}
});
多樣的結帳方式
Square Terminal支援多樣的結帳方式,因此顧客可以用自己偏好的方法付款。
- 信用卡・金融卡
- 交通IC卡(Suica/PASMO等)
- iD
- QUICPay
- QR碼結帳(PayPay等)
注意不支援銀聯卡。
結帳狀況確認的輪詢
由於結帳處理在Square Terminal上進行,需要透過輪詢確認結帳完成或取消等狀態。
// 透過輪詢確認結帳狀況
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);
// 完成處理
setTimeout(() => {
setStatus('complete');
setCart([]);
}, 2000);
} else if (statusData.status === 'CANCELED' || statusData.status === 'CANCEL_REQUESTED') {
setPaymentStatus(t.paymentCanceled);
setTimeout(() => {
setStatus('ready');
}, 3000);
} else {
// 如果還沒完成則重新確認
setPaymentStatus(t.processing);
setTimeout(checkPaymentStatus, 2000);
}
} catch (error) {
console.error(t.statusCheckFailed, error);
setPaymentStatus(t.statusCheckFailed);
setTimeout(() => {
setStatus('ready');
}, 3000);
}
};
商品主檔資料管理
商品資訊全部在Square的管理畫面註冊,透過API取得。 這樣簡化了商品新增和價格變更等運營作業。
app.get("/api/catalog-items", async (_req, res) => {
try {
const TYPES = "ITEM,ITEM_VARIATION,CATEGORY,IMAGE"; // 列舉所有需要的類型
//------------------------------------------------------------------
// ① 全部讀取
//------------------------------------------------------------------
const objects = [];
for await (const obj of await squareClient.catalog.list({ types: TYPES }))
objects.push(obj);
//------------------------------------------------------------------
// ② 先將CATEGORY / IMAGE / VARIATION 製成map
//------------------------------------------------------------------
const imageMap = {};
const categoryMap = {};
const variationMap = {};
// ...略(map建立處理)
//------------------------------------------------------------------
// ③ 展開ITEM,用先製作的map嵌入資訊
//------------------------------------------------------------------
const filtered = objects
.filter((o) => o.type === "ITEM")
.map((item) => {
// ...略(資料轉換處理)
})
// -- 在此進行需求篩選 --
.filter(
(item) =>
!item.isArchived &&
item.categoryNames.includes("LOPPO畫材")
);
res.json(filtered);
} catch (error) {
handleError("目錄項目取得錯誤", error, res);
}
});
4. 透過LLM活用的快速開發
此專案最大的特徵是在僅僅2天這樣短的期間內完成開發。 使這成為可能的是LLM(大規模語言模型)的活用。
開發時間的分配
- 基本系統開發:約2小時
- 改良・UI調整:約4小時
- 測試・部署:剩餘時間
Claude 3.7 Sonnet的活用方法
開發中主要活用Claude 3.7 Sonnet,提高了實作效率。 不僅是應用程式邏輯,UI設計也能輕鬆處理,連設定文件都準備好了等,真是至極周到。 特別是多語言對應的程式碼和與Square API的連接部分,LLM的提案非常有用。
另外也組合使用了ChatGPT 4o和ChatGPT o3等,但在對WEB應用程式的理解度上完全不及3.7 Sonnet。
LLM活用的具體例子
在開發中活用LLM時的理所當然注意點,生成的程式碼無法直接使用,必須在理解後加上必要的修正這點很重要。 例如,與Square Terminal API的連接部分需要以下修正。
- 結帳方式的追加:LLM生成的程式碼僅支援信用卡結帳,需要追加選擇結帳方式的畫面
- 錯誤處理:Terminal的結帳取消事件的處理方式有誤,根據API文件修正為正確的處理
- 安全性:一部分使用脆弱的協定進行應用程式間通訊,因此構築私人VPN確保通訊路徑的安全性
LLM提供了基本的程式碼結構,但為了能承受正式營運的調整需要手工作業。
5. 國際化與使用便利性
多語言對應的實作
為了讓訪日外國遊客也能使用,系統支援日語、英語、法語、西班牙語、繁體中文、簡體中文的6種語言。 在React組件內管理語言設定,採用從翻譯物件取得畫面上所有文字的方式。
// 語言選擇的狀態管理
const [language, setLanguage] = useState('ja'); // 將預設語言設為日語
// 取得語言設定
const t = translations[language];
// 使用例
<h1 className="text-4xl font-bold">{t.title}</h1>
<p className="text-lg text-gray-700 mb-6">
{t.scanDescription}
</p>
使用便利性的巧思
以超市和便利商店的自助收銀相同的使用便利性為目標,進行了以下巧思。
- 條碼掃描優先:在頁面任意位置接受鍵盤輸入,常時優先條碼掃描器的輸入
- 大按鈕:容易觸控操作的大按鈕尺寸
- 明確的回饋:容易理解操作結果的訊息顯示
透過這些巧思,我認為實現了使用者不會迷惑就能操作的介面。
6. 運營面的巧思
即時監控與故障對應
在店內設置網路攝影機,能即時確認商店的狀況。 發生問題時,顧客可聯絡店頭張貼的電話號碼進行對應。
檢測到營運上的異常時,整備了30分鐘~2小時內趕到現場對應的體制。 此外,萬一結帳終端無法運作時,也準備了事後交付結帳連結讓顧客完成付款的替代手段。
為了穩定運作,也組建了不斷電電源裝置的電源備份、網路的冗餘化、無操作時的定期重啟等。
只有1次發生客戶端電源線插入不牢而脫落的問題,之後系統非常穩定。
7. 實際成果與效果
銷售機會的擴大
透過開設無人商店,獲得了都心車站附近24小時營業的珍貴銷售機會。 最大的成果是在回應實體店鋪開設要求的同時,也克服了人力資源的限制。
結帳方法的傾向
導入的所有結帳方法都被廣泛使用,但特別受歡迎的順序如下。
- 交通IC卡
- QR碼結帳(PayPay等)
- 信用卡(感應式結帳)
8. 今後的展望與擴張計劃
網購的展開
目前的LOPPO畫材EC網站是用BASE構築的,但預計變更為使用Square API的版本。 這樣不僅能節約結帳手續費,還能改善商品購買流程,並且能一元化管理店鋪和網路銷售的庫存・銷售。
此外,還想提供線上購買店頭取貨的手段。
總結:既然能製作顏料,收銀系統當然也能製作
LOPPO手工復刻了各種賽璐璐相關畫材,這次手工製作了收銀系統。
軟體應用程式像這樣透過活用既有API和LLM的支援,有時可以相對簡單地構築。 話雖如此,沒想到收銀系統竟然能如此簡單地製作,真是學到很多。
注力的重點
- 活用既有服務:最大限度活用Square API等既有平台
- 活用LLM等支援工具:積極導入提高開發效率的工具
- 集中於必要最小限的範圍:集中於必要功能簡單實作
希望對有興趣構築自助收銀系統,或考慮活用Square API的人有所參考。
