f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
234 lines
8.5 KiB
Markdown
234 lines
8.5 KiB
Markdown
---
|
|
id: wiki-2026-0508-point-of-sale
|
|
title: Point of Sale
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [POS, retail-pos, payment-terminal]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.9
|
|
verification_status: applied
|
|
tags: [retail, payments, pos, square, stripe]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: TypeScript
|
|
framework: Stripe Terminal / Square API
|
|
---
|
|
|
|
# Point of Sale (POS)
|
|
|
|
## 매 한 줄
|
|
> **"매 retail transaction 의 capture system — 매 hardware + software + payments + analytics"**. 매 cash register → ECR (1970s) → cloud POS (Square 2009, Toast 2011, Shopify POS) → 매 modern unified commerce (online + offline + AI inventory + loss prevention).
|
|
|
|
## 매 핵심
|
|
|
|
### 매 components
|
|
- **매 hardware**: terminal (iPad/Android), card reader (chip + tap + Apple Pay), receipt printer, cash drawer, barcode scanner, kitchen display.
|
|
- **매 software**: catalog, cart, tax, discounts, tips, split bill, refunds, customer profiles, inventory.
|
|
- **매 payments**: card-present (EMV chip + NFC), digital wallets (Apple Pay, Google Pay), QR (Alipay, WeChat), BNPL (Klarna, Afterpay).
|
|
- **매 analytics**: sales dashboards, item performance, employee performance, demand forecast.
|
|
- **매 integrations**: accounting (QuickBooks), payroll, e-commerce (Shopify), marketing (loyalty, SMS).
|
|
|
|
### 매 modern players (2026)
|
|
- **매 Square** (Block): SMB-focused, Cash App integration, free entry POS.
|
|
- **매 Stripe Terminal**: developer-first, omnichannel API, embedded readers.
|
|
- **매 Toast**: restaurant-vertical, kitchen ops, payroll.
|
|
- **매 Shopify POS**: omni-commerce (online + retail unified inventory).
|
|
- **매 Lightspeed**: retail/restaurant/golf vertical.
|
|
- **매 Clover** (Fiserv): bank-distributed, app marketplace.
|
|
- **매 SumUp / iZettle**: Europe SMB.
|
|
|
|
### 매 AI/ML in POS (2026)
|
|
- **매 loss prevention**: computer vision 매 self-checkout 매 mis-scans (Everseen, Diebold, Mashgin Vision).
|
|
- **매 demand forecasting**: per-SKU per-store sales prediction (XGBoost/transformer).
|
|
- **매 dynamic pricing**: 매 perishables markdown optimization.
|
|
- **매 fraud detection**: 매 chargeback prediction, 매 stolen card flagging.
|
|
- **매 voice ordering / AI drive-thru**: McDonald's, Wendy's deployments.
|
|
- **매 visual search**: scan product 매 photo 의 lookup.
|
|
|
|
### 매 standards / compliance
|
|
- **매 PCI-DSS**: card data handling.
|
|
- **매 EMV**: chip card protocol.
|
|
- **매 P2PE**: point-to-point encryption.
|
|
- **매 tokenization**: store token, not PAN.
|
|
- **매 SOC 2**: SaaS POS providers.
|
|
|
|
### 매 응용
|
|
1. 매 retail (apparel, grocery, c-store).
|
|
2. 매 restaurants (QSR, FSR, food trucks).
|
|
3. 매 services (salons, mobile contractors).
|
|
4. 매 events / pop-ups (mobile readers).
|
|
|
|
## 💻 패턴
|
|
|
|
### Stripe Terminal — collect in-person payment
|
|
```typescript
|
|
import { loadStripeTerminal } from "@stripe/terminal-js";
|
|
|
|
const StripeTerminal = await loadStripeTerminal();
|
|
const terminal = StripeTerminal.create({
|
|
onFetchConnectionToken: async () => {
|
|
const r = await fetch("/connection_token", { method: "POST" });
|
|
return (await r.json()).secret;
|
|
},
|
|
onUnexpectedReaderDisconnect: () => console.warn("disconnected"),
|
|
});
|
|
|
|
const discoverResult = await terminal.discoverReaders();
|
|
await terminal.connectReader(discoverResult.discoveredReaders[0]);
|
|
|
|
// Server creates PaymentIntent, client collects
|
|
const { paymentIntent } = await fetch("/create_pi", {
|
|
method: "POST", body: JSON.stringify({ amount: 1999 }),
|
|
}).then(r => r.json());
|
|
|
|
const result = await terminal.collectPaymentMethod(paymentIntent.client_secret);
|
|
const confirmed = await terminal.processPayment(result.paymentIntent);
|
|
console.log("paid:", confirmed.paymentIntent.id);
|
|
```
|
|
|
|
### Square Terminal API
|
|
```javascript
|
|
const { Client, Environment } = require("square");
|
|
const client = new Client({
|
|
accessToken: process.env.SQUARE_TOKEN,
|
|
environment: Environment.Production,
|
|
});
|
|
|
|
const checkout = await client.terminalApi.createTerminalCheckout({
|
|
idempotencyKey: crypto.randomUUID(),
|
|
checkout: {
|
|
amountMoney: { amount: 1999n, currency: "USD" },
|
|
deviceOptions: { deviceId: "DEVICE_ID" },
|
|
},
|
|
});
|
|
```
|
|
|
|
### Catalog + tax calculation
|
|
```typescript
|
|
type Item = { id: string; name: string; price_cents: number; tax_rate: number; sku: string };
|
|
|
|
function calculateOrder(items: Item[], qty: Record<string, number>, discount_cents = 0) {
|
|
const subtotal = items.reduce((s, it) => s + it.price_cents * (qty[it.id] ?? 0), 0);
|
|
const tax = items.reduce((s, it) =>
|
|
s + it.price_cents * (qty[it.id] ?? 0) * it.tax_rate, 0);
|
|
const total = subtotal + tax - discount_cents;
|
|
return { subtotal, tax, discount: discount_cents, total };
|
|
}
|
|
```
|
|
|
|
### Receipt printer (ESC/POS)
|
|
```javascript
|
|
// node-thermal-printer
|
|
const { ThermalPrinter, PrinterTypes } = require("node-thermal-printer");
|
|
const printer = new ThermalPrinter({
|
|
type: PrinterTypes.EPSON,
|
|
interface: "tcp://192.168.1.100",
|
|
});
|
|
printer.alignCenter(); printer.println("ACME COFFEE");
|
|
printer.drawLine();
|
|
printer.tableCustom([
|
|
{ text: "Latte x2", align: "LEFT" },
|
|
{ text: "$10.00", align: "RIGHT" },
|
|
]);
|
|
printer.drawLine();
|
|
printer.println("TOTAL: $10.80");
|
|
printer.cut(); await printer.execute();
|
|
```
|
|
|
|
### EMV / NFC reader (BBPOS via Stripe)
|
|
```typescript
|
|
// 매 Stripe Terminal SDK 매 BBPOS WisePOS E / Stripe Reader S700
|
|
// 매 PCI-DSS scope 의 reduce — 매 reader 매 PAN 의 handle, 매 server 매 token 의 receive
|
|
const reader = await terminal.connectReader(readers[0], { fail_if_in_use: true });
|
|
```
|
|
|
|
### Inventory sync (Shopify POS)
|
|
```javascript
|
|
// Shopify Admin API — unified inventory across channels
|
|
const res = await fetch(`https://${SHOP}.myshopify.com/admin/api/2025-01/inventory_levels/adjust.json`, {
|
|
method: "POST",
|
|
headers: { "X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
inventory_item_id: 12345,
|
|
location_id: 67890,
|
|
available_adjustment: -1, // sold one
|
|
}),
|
|
});
|
|
```
|
|
|
|
### Loss prevention (CV self-checkout)
|
|
```python
|
|
# 매 simplified pseudocode — 매 actual: YOLOv8 + tracking + match to scanned barcode
|
|
import cv2
|
|
from ultralytics import YOLO
|
|
|
|
model = YOLO("yolov8n.pt")
|
|
cap = cv2.VideoCapture(0)
|
|
scanned_skus = set() # populated by barcode scanner events
|
|
|
|
while True:
|
|
ret, frame = cap.read()
|
|
detections = model(frame)[0]
|
|
visible_items = {classify_to_sku(d) for d in detections.boxes}
|
|
discrepancy = visible_items - scanned_skus
|
|
if discrepancy:
|
|
alert_associate(f"Unscanned: {discrepancy}")
|
|
```
|
|
|
|
### Webhook — payment.succeeded
|
|
```typescript
|
|
// 매 Stripe webhook 의 receive — 매 settle order in DB
|
|
import express from "express";
|
|
const app = express();
|
|
app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
|
|
const sig = req.headers["stripe-signature"]!;
|
|
const event = stripe.webhooks.constructEvent(req.body, sig, WEBHOOK_SECRET);
|
|
if (event.type === "payment_intent.succeeded") {
|
|
const pi = event.data.object;
|
|
await db.orders.update({ stripe_pi: pi.id }, { status: "paid" });
|
|
}
|
|
res.json({ received: true });
|
|
});
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | Solution |
|
|
|---|---|
|
|
| 매 SMB cafe / pop-up | 매 Square |
|
|
| 매 restaurant chain | 매 Toast |
|
|
| 매 omni-commerce retail | 매 Shopify POS |
|
|
| 매 dev-first / custom UX | 매 Stripe Terminal |
|
|
| 매 enterprise multi-store | 매 NCR / Oracle Symphony |
|
|
| 매 EU SMB | 매 SumUp / iZettle |
|
|
| 매 unattended kiosk / self-checkout | 매 Mashgin / Toshiba + Stripe |
|
|
|
|
**기본값**: 매 Stripe Terminal (developer-first, custom flow) 또는 매 Square (turnkey SMB).
|
|
|
|
## 🔗 Graph
|
|
- Adjacent: [[Shopify]]
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: 매 receipt parsing, 매 customer Q&A chatbot, 매 menu generation, 매 anomaly summary 의 사용.
|
|
**언제 X**: 매 payment authorization (deterministic flow), 매 PII handling (compliance risk).
|
|
|
|
## ❌ 안티패턴
|
|
- **매 store full PAN**: 매 PCI-DSS violation. 매 token 만 store.
|
|
- **매 home-grown payment terminal**: 매 EMV cert 매 expensive — 매 SaaS provider 의 사용.
|
|
- **매 ignore offline mode**: 매 retail Wi-Fi 매 unstable — 매 store-and-forward needed.
|
|
- **매 monolithic POS**: 매 vertical lock-in — 매 API-first (Stripe/Shopify) 의 prefer.
|
|
- **매 no idempotency keys**: 매 double-charge risk on retry.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (Stripe Terminal docs, Square API, Shopify Admin API, PCI-DSS v4.0).
|
|
- 신뢰도 A.
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — Stripe/Square/Shopify patterns + AI loss prevention |
|