mcpField
P
pos-integration

pos-integration

github.com/finsilabs其他60102 次安装6 天前更新
代码仓库

关于 pos-integration

面向线下零售与线上商店整合的 POS 集成技能指南,涵盖 Shopify、WooCommerce、BigCommerce 和 Square 等路径。内容涉及库存同步、跨渠道退货及自定义无头集成。

主要功能

  1. 01梳理平台与 POS 集成路径
  2. 02配置 Shopify POS 与门店位置
  3. 03通过 Square 双向同步库存
  4. 04用 Webhook 更新线上库存
  5. 05处理跨渠道退货

适用场景

  1. 01为线上商店新增实体零售门店
  2. 02减少线上与店内库存差异
  3. 03在门店办理线上订单退货
  4. 04为多门店统一库存和订单管理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill pos-integration

安装后在 Claude Code 中输入/pos-integration确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/cross-channel-in-store-returns/
criteria.json
task.md
evals/square-catalog-sync/
criteria.json
task.md
evals/square-webhook-inventory-sync/
criteria.json
task.md

详细介绍

POS 集成

概述

销售点(POS)集成可将实体零售业务与在线商店连接起来,实现统一的库存可见性、集中式订单管理,以及跨渠道一致的客户档案。顾客在店内购买商品时,在线商店必须立即反映库存变化;顾客在店内退回在线订单时,退款必须同步至支付网关。本技能介绍如何将 Square 和 Shopify POS 接入商务平台、实时同步库存,以及处理跨渠道退货。

适用场景

  • 在已有在线商店的基础上开设实体零售门店时
  • 店内与在线库存不一致,导致超卖时
  • 顾客希望能在店内退回在线购买的商品时(全渠道退货)
  • 加盟店或多门店零售业务需要统一库存和报表时
  • 构建自定义自助终端或基于平板电脑的 POS 应用时

核心说明

第 1 步:确定平台和 POS 集成方案
平台最简便的 POS 集成方式统一的内容
ShopifyShopify POS(内置,专业版每月 $89)——专为与 Shopify 在线商店原生协作而设计POS 与在线商店之间即时同步库存;订单显示在统一的管理后台中;统一客户档案和忠诚度积分
WooCommerceSquare + WooCommerce Square 插件(免费)双向同步库存;Square POS 销售会更新 WooCommerce 库存;订单可导入 WooCommerce
BigCommerceSquare + BigCommerce Square 集成与 WooCommerce 相同,但需在 BigCommerce 的渠道设置中进行配置;BigCommerce 也支持直接使用 Stripe Terminal
自定义 / 无头架构Square API 或 Stripe Terminal完全掌控——通过 webhook 同步库存、通过 API 导入 POS 订单、构建统一订单控制面板
第 2 步:针对平台设置 POS

Shopify + Shopify POS

对于 Shopify 商店,Shopify POS 是最无缝的选择,因为库存由单一系统管理:

  1. 订阅 Shopify POS Pro(每月 $89,或包含在 Shopify Plus 中):

    • 在 Shopify 管理后台前往 销售点 → 设备
    • 在 iPad 或 iPhone 上安装 Shopify POS 应用
    • 连接读卡器(Shopify 提供自有的 Tap & Chip 读卡器)
  2. 为每家门店设置地点:

    • 前往 设置 → 地点,然后为每家实体门店点击 添加地点
    • 按地点分配库存数量——Shopify 会分别追踪每个地点的库存
  3. 库存会自动保持同步:

    • 在 POS 应用中完成销售后,该地点的库存会立即减少
    • 可将在线商店配置为从多个地点销售(前往 设置 → 配送和交付 → 本地自提进行配置)
  4. 配置线上购买、店内自提:

    • 前往 设置 → 配送和交付 → 本地自提,并为每个地点启用此功能
    • 顾客在结账时选择自提地点;订单会显示在该门店 POS 应用的 自提订单下

WooCommerce + Square

将 Square 连接到 WooCommerce:

  1. 从 wordpress.org 安装 WooCommerce Square 插件(免费,由 Square 开发)
  2. 前往 WooCommerce → Settings → Integrations → Square,然后点击 Connect with Square
  3. 登录你的 Square 账户并选择 Square 商家地点
  4. 在 Square 插件设置中,启用 Sync inventory(双向同步)
  5. 运行初始同步:前往 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
  1. 在 BigCommerce 管理后台前往 Channel Manager,然后点击 + Create New Channel
  2. 从渠道列表中选择 Square
  3. 授权 BigCommerce 连接到你的 Square 账户
  4. 配置库存同步设置 — BigCommerce 和 Square 会双向同步库存水平
  5. 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