mcpField
S
saleor-development

saleor-development

github.com/finsilabs开发6074 次安装6 天前更新
代码仓库

关于 saleor-development

该技能面向使用 Saleor 的开发者,介绍基于 Django 和 GraphQL 的无头电商开发。内容包括查询 API、构建应用、处理 Webhook,以及扩展管理仪表板界面。

主要功能

  1. 01使用 GraphQL 查询商品与结账数据
  2. 02创建接收 Webhook 的 Saleor App
  3. 03验证 Webhook 签名
  4. 04向 Dashboard 注入自定义界面
  5. 05通过 Docker Compose 启动本地环境

适用场景

  1. 01为 Saleor 后端构建自定义店面
  2. 02响应订单或商品生命周期事件
  3. 03在 Dashboard 中添加商品操作面板
  4. 04本地开发和测试 Saleor 集成

安装

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

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill saleor-development

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

包含的文件

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

tile.json
evals/dashboard-extension-with-app-bridge-comm/
criteria.json
task.md
evals/saleor-app-bootstrap-and-webhook-handlin/
criteria.json
task.md
evals/storefront-product-catalog-with-paginati/
criteria.json
task.md

详细介绍

Saleor 开发

概述

Saleor 是一个基于 Django 和 Python 构建的无头、GraphQL 优先的电子商务平台。它提供完全类型化的 GraphQL API,供店面和第三方应用使用;提供基于 React 的仪表板来管理商店;还提供扩展系统,让你能够通过 Webhook 响应事件,或向仪表板注入 UI。本技能涵盖查询 Saleor API、构建 Saleor Apps(托管在 Saleor 之外的插件)以及使用 App Extensions 自定义仪表板。

何时使用本技能

  • 为 Saleor 后端构建自定义店面(Next.js、Remix、移动应用)时
  • 创建能够响应订单或商品生命周期 Webhook 的 Saleor App 时
  • 通过 App Extensions 向 Saleor Dashboard 注入自定义 UI 面板时
  • 探索或扩展 Saleor 商品目录、结账或客户 API 时
  • 使用 Docker 设置本地 Saleor 开发环境时

前置条件与平台说明

本技能面向自定义/无头店面(Node.js、Python 或类似后端)。代码示例使用 TypeScript/Node.js,可适配任何技术栈。

Shopify:Shopify Hydrogen 是 Shopify 的无头框架。使用 Shopify 作为电商后端并搭配自定义前端,或将 Shopify 与其他同类最佳服务结合时,适用 MACH/可组合模式。 WooCommerce:WooCommerce 可通过其 REST API 和 WPGraphQL 作为无头后端。当将前端与 WordPress 解耦时,适用这些模式。 Magento:Magento 的 GraphQL API 和 PWA Studio 支持无头架构。在 MACH 技术栈中将 Magento 用作后端服务时,适用这些可组合模式。

你需要:

  • Node.js 18+(或适配你的后端语言)
  • PostgreSQL(或你偏好的关系型数据库)
  • 用于缓存/队列的 Redis
  • Stripe 账户和 API 密钥
  • 电子邮件发送服务(SendGrid、AWS SES 或 Postmark)
  • 用于容器编排的 Docker 和/或 Kubernetes
  • CDN(Cloudflare、CloudFront 或 Fastly)

核心说明

  1. 使用 Docker Compose 在本地运行 Saleor

    git clone https://github.com/saleor/saleor-platform.git
    cd saleor-platform
    docker compose up --detach
    # API:       http://localhost:8000/graphql/
    # Dashboard: http://localhost:9000
    

    创建首个超级用户并填充演示数据:

    docker compose run --rm api python manage.py createsuperuser
    docker compose run --rm api python manage.py populatedb --createsuperuser
    
  2. 查询店面 GraphQL API

    使用 Saleor CLI 或任意 GraphQL 客户端(Apollo、urql、graphql-request)。

    安装 CLI 以生成代码:

    npm install -g @saleor/cli
    saleor configure
    

    示例 — 从默认渠道获取前 12 个商品:

    query ProductList($channel: String!) {
      products(first: 12, channel: $channel) {
        edges {
          node {
            id
            name
            slug
            thumbnail { url alt }
            pricing {
              priceRange {
                start { gross { amount currency } }
              }
            }
          }
        }
        pageInfo { hasNextPage endCursor }
      }
    }
    
    import { createClient } from 'urql';
    
    const client = createClient({
      url: process.env.NEXT_PUBLIC_SALEOR_API_URL,
      fetchOptions: () => ({
        headers: { 'Content-Type': 'application/json' },
      }),
    });
    
    const { data } = await client.query(PRODUCT_LIST_QUERY, { channel: 'default-channel' }).toPromise();
    
  3. 验证客户身份并开始结账

    mutation CustomerLogin($email: String!, $password: String!) {
      tokenCreate(email: $email, password: $password) {
        token
        refreshToken
        errors { field message }
        user { id email }
      }
    }
    

    创建结账并添加商品行:

    mutation CheckoutCreate($channel: String!, $lines: [CheckoutLineInput!]!) {
      checkoutCreate(input: { channel: $channel, lines: $lines }) {
        checkout {
          id
          token
          totalPrice { gross { amount currency } }
        }
        errors { field message }
      }
    }
    

    使用支付网关令牌完成结账(例如,来自 Stripe Elements 的令牌):

    mutation CheckoutComplete($checkoutId: ID!, $paymentData: JSONString) {
      checkoutComplete(id: $checkoutId, paymentData: $paymentData) {
        order { id number status }
        errors { field message code }
      }
    }
    
  4. 初始化 Saleor App

    Saleor App 是一种 Node.js 服务,会向 Saleor 注册自身、接收 Webhook,并可选择通过 iframe 在仪表板中呈现 UI。

    npx @saleor/app-sdk@latest create my-saleor-app
    cd my-saleor-app
    npm install
    npm run dev
    # Expose with: npx ngrok http 3000
    

    在仪表板的 Apps → Install custom app 下注册应用,并输入你的 ngrok URL。Saleor 会调用你的 /api/manifest 端点:

    // pages/api/manifest.ts
    import { createManifestHandler } from "@saleor/app-sdk/handlers/next";
    import { AppManifest } from "@saleor/app-sdk/types";
    
    const manifest: AppManifest = {
      id: "my-saleor-app",
      name: "My Saleor App",
      version: "1.0.0",
      about: "Example app",
      permissions: ["MANAGE_ORDERS"],
      appUrl: process.env.APP_URL!,
      tokenTargetUrl: `${process.env.APP_URL}/api/register`,
      webhooks: [
        {
          name: "Order Created",
          asyncEvents: ["ORDER_CREATED"],
          query: `subscription { event { ... on OrderCreated { order { id number } } } }`,
          targetUrl: `${process.env.APP_URL}/api/webhooks/order-created`,
          isActive: true,
        },
      ],
    };
    
    export default createManifestHandler({ manifestFactory: () => manifest });
    
  5. 安全处理 Saleor Webhook

    Saleor 使用应用的密钥令牌,通过 HMAC-SHA256 签名对每个 Webhook 进行签名。

    // pages/api/webhooks/order-created.ts
    import { SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next";
    import { OrderCreatedDocument } from "@/generated/graphql";
    
    const orderCreatedWebhook = new SaleorAsyncWebhook<OrderCreatedPayload>({
      name: "Order Created",
      webhookPath: "api/webhooks/order-created",
      asyncEvent: "ORDER_CREATED",
      apl: saleorApp.apl,
      query: OrderCreatedDocument,
    });
    
    export default orderCreatedWebhook.createHandler((req, res, ctx) => {
      const { order } = ctx.payload;
      console.log(`New order #${order.number} received`);
      // Trigger fulfillment, email, ERP sync, etc.
      return res.status(200).end();
    });
    
    export const config = { api: { bodyParser: false } }; // required for signature check
    
  6. 添加仪表板扩展(自定义 UI 面板)

    扩展会在 Saleor Dashboard 中呈现 iframe。在清单中声明扩展:

    extensions: [
      {
        label: "Sync to ERP",
        mount: "PRODUCT_DETAILS_MORE_ACTIONS",
        target: "POPUP",
        permissions: ["MANAGE_PRODUCTS"],
        url: `${process.env.APP_URL}/extension/product-sync`,
      },
    ],
    

    扩展页面使用 @saleor/app-sdk 与仪表板宿主进行通信:

    import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
    
    export default function ProductSyncExtension() {
      const { appBridge } = useAppBridge();
    
      const handleSync = async () => {
        appBridge?.dispatch(actions.Notification({
          status: "success",
          title: "Sync started",
          text: "Product is being synced to ERP.",
        }));
      };
    
      return <button onClick={handleSync}>Sync to ERP</button>;
    }
    

示例

使用 TypeScript 和 graphql-request 实现分页商品目录
import { GraphQLClient, gql } from 'graphql-request';

const client = new GraphQLClient(process.env.SALEOR_API_URL!, {
  headers: { Authorization: `Bearer ${process.env.SALEOR_APP_TOKEN}` },
});

const PRODUCTS_QUERY = gql`
  query Products($first: Int!, $after: String, $channel: String!) {
    products(first: $first, after: $after, channel: $channel) {
      edges { node { id name slug description } }
      pageInfo { hasNextPage endCursor }
    }
  }
`;

async function fetchAllProducts(channel: string) {
  const products = [];
  let after: string | null = null;

  do {
    const data = await client.request(PRODUCTS_QUERY, { first: 100, after, channel });
    products.push(...data.products.edges.map((e: any) => e.node));
    after = data.products.pageInfo.hasNextPage ? data.products.pageInfo.endCursor : null;
  } while (after);

  return products;
}
通过 Admin API 更新订单状态
mutation FulfillOrder($orderId: ID!, $input: OrderFulfillInput!) {
  orderFulfill(orderId: $orderId, input: $input) {
    fulfillments {
      id
      status
      trackingNumber
    }
    errors { field message code }
  }
}
await client.request(FULFILL_ORDER_MUTATION, {
  orderId: "T3JkZXI6MTIz",
  input: {
    lines: [{ orderLineId: "T3JkZXJMaW5lOjQ1", stocks: [{ warehouse: "V2FyZWhvdXNlOjE=", quantity: 1 }] }],
    notifyCustomer: true,
    allowStockToBeExceeded: false,
  },
});

最佳实践

  • 使用渠道进行多区域或 B2B/B2C 区分 — 每个商品列表、定价和结账流程都限定在特定渠道中;按地区/货币创建单独的渠道,而不是重复创建商品
  • 根据架构生成 TypeScript 类型 — 运行 saleor app generate-types 或使用 graphql-codegen,以确保查询具有完整的类型定义
  • 将应用令牌存储在 Saleor 的 APL(Auth Persistence Layer)中 — 默认的基于文件的 APL 适用于开发;生产环境请使用 Redis 或 Upstash APL
  • 始终验证 Webhook 签名 — 使用 SaleorAsyncWebhook 包装器,它会自动处理 HMAC 验证;绝不处理未经身份验证的负载
  • 使用基于订阅的 Webhook 查询 — Saleor Webhook 使用 GraphQL 订阅来定义负载,让你可以精确控制包含哪些字段
  • 在 CDN 层缓存商品目录响应 — 商品数据很少变化;在商品目录 API 路由上设置 Cache-Control: s-maxage=300
  • 在生产环境中使用 Saleor Cloud — 自托管 Django + Celery + Redis + PostgreSQL 需要成熟的运维能力;Saleor Cloud 会处理这些工作

常见问题

问题解决方案
未授权操作导致 GraphQL 错误确保已在清单中授予应用正确的权限,并在控制面板的应用设置中授予权限
Webhook 负载为空或缺少字段Webhook 负载由清单中的 GraphQL 订阅查询定义 — 将所需字段添加到 query 属性中
店面中的 tokenCreate 返回 null渠道必须启用店面 API 并指定所属国家/地区;请在控制面板中检查渠道配置
应用在本地运行正常,但部署后无法运行APP_URL 环境变量必须与 Saleor 可访问的公开 URL 一致;部署后请在控制面板中更新应用 URL
控制面板扩展 iframe 为空白扩展 URL 必须通过 HTTPS 提供服务,并且必须包含针对控制面板源的 Access-Control-Allow-Origin 标头

相关技能

  • @shopify-hydrogen
  • @composable-commerce
  • @webhook-architecture
  • @jamstack-storefront
  • @commerce-api-gateway

评论

支持 Markdown