关于 pos-integration
面向线下零售与线上商店整合的 POS 集成技能指南,涵盖 Shopify、WooCommerce、BigCommerce 和 Square 等路径。内容涉及库存同步、跨渠道退货及自定义无头集成。
主要功能
- 01梳理平台与 POS 集成路径
- 02配置 Shopify POS 与门店位置
- 03通过 Square 双向同步库存
- 04用 Webhook 更新线上库存
- 05处理跨渠道退货
适用场景
- 01为线上商店新增实体零售门店
- 02减少线上与店内库存差异
- 03在门店办理线上订单退货
- 04为多门店统一库存和订单管理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill pos-integration安装后在 Claude Code 中输入/pos-integration确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/cross-channel-in-store-returns/criteria.jsontask.mdevals/square-catalog-sync/criteria.jsontask.mdevals/square-webhook-inventory-sync/criteria.jsontask.md详细介绍
POS 集成
概述
销售点(POS)集成可将实体零售业务与在线商店连接起来,实现统一的库存可见性、集中式订单管理,以及跨渠道一致的客户档案。顾客在店内购买商品时,在线商店必须立即反映库存变化;顾客在店内退回在线订单时,退款必须同步至支付网关。本技能介绍如何将 Square 和 Shopify POS 接入商务平台、实时同步库存,以及处理跨渠道退货。
适用场景
- 在已有在线商店的基础上开设实体零售门店时
- 店内与在线库存不一致,导致超卖时
- 顾客希望能在店内退回在线购买的商品时(全渠道退货)
- 加盟店或多门店零售业务需要统一库存和报表时
- 构建自定义自助终端或基于平板电脑的 POS 应用时
核心说明
第 1 步:确定平台和 POS 集成方案
| 平台 | 最简便的 POS 集成方式 | 统一的内容 |
|---|---|---|
| Shopify | Shopify POS(内置,专业版每月 $89)——专为与 Shopify 在线商店原生协作而设计 | POS 与在线商店之间即时同步库存;订单显示在统一的管理后台中;统一客户档案和忠诚度积分 |
| WooCommerce | Square + WooCommerce Square 插件(免费) | 双向同步库存;Square POS 销售会更新 WooCommerce 库存;订单可导入 WooCommerce |
| BigCommerce | Square + BigCommerce Square 集成 | 与 WooCommerce 相同,但需在 BigCommerce 的渠道设置中进行配置;BigCommerce 也支持直接使用 Stripe Terminal |
| 自定义 / 无头架构 | Square API 或 Stripe Terminal | 完全掌控——通过 webhook 同步库存、通过 API 导入 POS 订单、构建统一订单控制面板 |
第 2 步:针对平台设置 POS
Shopify + Shopify POS
对于 Shopify 商店,Shopify POS 是最无缝的选择,因为库存由单一系统管理:
-
订阅 Shopify POS Pro(每月 $89,或包含在 Shopify Plus 中):
- 在 Shopify 管理后台前往 销售点 → 设备
- 在 iPad 或 iPhone 上安装 Shopify POS 应用
- 连接读卡器(Shopify 提供自有的 Tap & Chip 读卡器)
-
为每家门店设置地点:
- 前往 设置 → 地点,然后为每家实体门店点击 添加地点
- 按地点分配库存数量——Shopify 会分别追踪每个地点的库存
-
库存会自动保持同步:
- 在 POS 应用中完成销售后,该地点的库存会立即减少
- 可将在线商店配置为从多个地点销售(前往 设置 → 配送和交付 → 本地自提进行配置)
-
配置线上购买、店内自提:
- 前往 设置 → 配送和交付 → 本地自提,并为每个地点启用此功能
- 顾客在结账时选择自提地点;订单会显示在该门店 POS 应用的 自提订单下
WooCommerce + Square
将 Square 连接到 WooCommerce:
- 从 wordpress.org 安装 WooCommerce Square 插件(免费,由 Square 开发)
- 前往 WooCommerce → Settings → Integrations → Square,然后点击 Connect with Square
- 登录你的 Square 账户并选择 Square 商家地点
- 在 Square 插件设置中,启用 Sync inventory(双向同步)
- 运行初始同步:前往 WooCommerce → System Status → Tools → Sync Products with Square
库存同步的工作方式:
- 在 Square POS 中售出商品后,WooCommerce 库存会自动扣减(通过 webhook 在几分钟内完成)
- WooCommerce 收到线上订单后,Square 库存会扣减
- 前往 WooCommerce → Square → Sync Log 查看同步历史并排查差异
处理线上订单的店内退货:
- 如果原始付款使用 Square,在 WooCommerce 管理后台发起的退货会自动同步到 Square
- 对于使用 Stripe 付款的线上订单,若在店内退货:在 Square 中处理退款,并在 WooCommerce 中手动对账;或者构建自定义退款端点(见下文的“自定义”部分)
BigCommerce + Square
- 在 BigCommerce 管理后台前往 Channel Manager,然后点击 + Create New Channel
- 从渠道列表中选择 Square
- 授权 BigCommerce 连接到你的 Square 账户
- 配置库存同步设置 — BigCommerce 和 Square 会双向同步库存水平
- Square POS 订单可以导入 BigCommerce,以便统一报告
自定义 / Headless + Square API
初始化 Square 客户端:
// lib/pos/square-client.ts
import { Client, Environment } from 'square';
export const squareClient = new Client({
accessToken: process.env.SQUARE_ACCESS_TOKEN!,
environment: process.env.NODE_ENV === 'production' ? Environment.Production : Environment.Sandbox,
});
export const { catalogApi, inventoryApi, ordersApi, paymentsApi, locationsApi } = squareClient;
处理 Square 库存 webhook(店内销售 → 更新线上商店):
// POST /api/webhooks/square
import { createHmac } from 'node:crypto';
export async function POST(req: NextRequest) {
const rawBody = Buffer.from(await req.arrayBuffer());
const signature = req.headers.get('x-square-hmacsha256-signature') ?? '';
// Square HMAC includes the full notification URL in the signature
const notificationUrl = `${process.env.APP_URL}/api/webhooks/square`;
const expected = createHmac('sha256', process.env.SQUARE_WEBHOOK_SECRET!)
.update(notificationUrl + rawBody.toString('utf8'))
.digest('base64');
if (signature !== expected) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody.toString('utf8'));
if (event.type === 'inventory.count.updated') {
const counts = event.data.object.inventory_counts ?? [];
for (const count of counts) {
const variant = await db.variants.findBySquareCatalogId(count.catalog_object_id);
if (!variant) continue;
await db.inventory.updateLocationQuantity(variant.sku, count.location_id, parseInt(count.quantity));
const total = await db.inventory.getTotalAvailable(variant.sku);
await redis.setex(`inventory:${variant.productId}`, 3600, String(total));
}
}
return NextResponse.json({ accepted: true });
}
处理跨渠道退货(线上订单在店内退货):
// lib/pos/returns.ts
export async function processInStoreReturn(params: {
orderId: string;
lineItems: Array<{ lineItemId: string; quantity: number }>;
locationId: string;
}) {
const { orderId, lineItems, locationId } = params;
const order = await db.orders.findById(orderId);
if (!order) throw new Error(`Order ${orderId} not found`);
// Calculate refund amount
const refundAmount = lineItems.reduce((sum, item) => {
const line = order.lineItems.find(l => l.id === item.lineItemId)!;
return sum + (line.unitPriceCents * item.quantity);
}, 0);
// Issue refund via original payment gateway
if (order.paymentGateway === 'stripe') {
await stripe.refunds.create({
payment_intent: order.stripePaymentIntentId,
amount: refundAmount,
metadata: { orderId, locationId, channel: 'in_store_return' },
});
} else if (order.paymentGateway === 'square') {
await paymentsApi.createPaymentRefund({
idempotencyKey: `return_${orderId}_${Date.now()}`,
paymentId: order.squarePaymentId!,
amountMoney: { amount: BigInt(refundAmount), currency: 'USD' },
});
}
// Restock returned items at the return location
for (const item of lineItems) {
const line = order.lineItems.find(l => l.id === item.lineItemId)!;
await db.inventory.incrementLocationQuantity(line.sku, locationId, item.quantity);
}
await db.orders.addReturn({ orderId, lineItems, processedAtLocation: locationId, processedAt: new Date() });
}
将产品目录同步到 Square(Square 跟踪库存前必须完成此操作):
export async function syncProductToSquare(product: Product) {
const { result } = await catalogApi.batchUpsertCatalogObjects({
idempotencyKey: `sync_${product.sku}_${Date.now()}`,
batches: [{
objects: [{
type: 'ITEM',
id: `#item_${product.sku}`,
itemData: {
name: product.name,
variations: product.variants.map(variant => ({
type: 'ITEM_VARIATION' as const,
id: `#var_${variant.sku}`,
itemVariationData: {
itemId: `#item_${product.sku}`,
name: variant.name,
sku: variant.sku,
pricingType: 'FIXED_PRICING',
priceMoney: { amount: BigInt(Math.round(variant.price * 100)), currency: 'USD' },
trackInventory: true,
},
})),
},
}],
}],
});
// Store Square-assigned IDs for future inventory updates
for (const mapping of result.idMappings ?? []) {
if (mapping.clientObjectId?.startsWith('#var_')) {
const sku = mapping.clientObjectId.replace('#var_', '');
await db.variants.update(sku, { squareCatalogVariationId: mapping.objectId });
}
}
}
最佳实践
- Shopify 商家应使用 Shopify POS — 它与线上商店共用同一套库存系统;无需等待同步、不会产生重复记录,并且开箱即用地提供统一报告
- 为线上商店预留安全库存缓冲 — 不要将 100% 的实物库存都展示在线上;预留 10–20% 作为缓冲,以应对库存同步传播前发生的店内销售
- 实施基于地点的库存管理 — 按实体地点(门店)跟踪库存,而不只是记录全局总量;这有助于准确显示到店自提的可用情况
- 监控同步延迟 — 测量 POS 销售后到线上显示库存更新的时间;延迟超过 5 分钟时发出警报
- 构建手动对账工具 — 库存差异时有发生;门店经理需要一个 UI,用于比较 POS 库存数量与系统中的数量,并触发手动同步
常见问题
| 问题 | 解决方案 |
|---|---|
| WooCommerce Square 同步停止工作 | 在 WooCommerce → Settings → Integrations → Square 中重新验证 Square 连接 — OAuth 令牌会过期;插件通常会提示重新验证 |
| 同步延迟导致库存超卖 | 在商城/POS 设置中设置安全库存缓冲;对于高需求商品,在结账时通过 Square 的库存 API 最后检查库存 |
| Square 目录 ID 与 SKU 不一致 | 维护内部 SKU 与 Square 目录变体 ID 之间的映射表;如果映射不同步,请根据 Square 目录重建 |
| 跨渠道退货造成重复库存 | 只有在店内实际收到退货商品时(status: received)才重新入库,而不是在发起退货时 |
| Shopify POS 未显示供自提的线上订单 | 确保客户在结账时选择了 Pick up in store,并且在 Settings → Locations 中为 POS 设备分配了正确的地点 |
相关技能
- @marketplace-connectors
- @webhook-architecture
- @flash-sale-scaling
- @monitoring-alerting-commerce
评论
支持 Markdown