# 店匠(Shoplazza)APP 开发完整指南 ## 参考 https://xp0y6kmku6.feishu.cn/wiki/UsFiwDyGgiDWDwkAe5jcKHbSnHd ## 目录 1. [概述](#概述) 2. [阶段 1:在店匠 Partner 后台创建新 APP](#阶段-1在店匠-partner-后台创建新-app) 3. [阶段 2:创建前端项目](#阶段-2创建前端项目) 4. [阶段 3:开发后端服务](#阶段-3开发后端服务) 5. [阶段 4:前端开发详解](#阶段-4前端开发详解) 6. [阶段 5:测试与部署](#阶段-5测试与部署) 7. [常见问题与最佳实践](#常见问题与最佳实践) --- ## 概述 本指南将帮助您从零开始创建一个完整的店匠(Shoplazza)Public App,包括: - **前端**:使用 Liquid 模板和 Lessjs 组件库开发主题扩展 - **后端**:实现 OAuth 2.0 授权流程、HMAC 验证、API 调用 - **多语言支持**:支持 16 种语言的国际化 - **主题集成**:与店匠主题深度集成 ### 前置要求 - Node.js 14.14.0 或更高版本 - [Shoplazza 合作伙伴账户](https://partners.shoplazza.com/) - [Shoplazza CLI](https://www.shoplazza.dev/docs/overview-3)(用于部署) - 后端服务(Java/Spring Boot 或 Node.js/Express) - 测试店铺(用于开发测试) --- ## 阶段 1:在店匠 Partner 后台创建新 APP ### 步骤 1.1:登录 Partner 后台 1. 访问:https://partners.shoplazza.com/ 2. 使用合作伙伴账号登录 3. 进入管理页面:https://partners.shoplazza.com/{partner_id} ### 步骤 1.2:创建新 APP 1. 左侧导航:**Apps** 2. 点击:**Create App** 或 **创建应用** 3. 填写信息: - **App Name**:智能推荐1.0(或自定义名称) - **App Type**:**Public App**(公共应用) - **Category**:选择合适分类(如 Product Recommendations) 4. 点击:**Create** 或 **创建** ### 步骤 1.3:记录关键信息 系统会生成 **Client credentials**(APP 的公共标识符,OAuth 流程以及与 Shoplazza API 互动都会需要): - **Client ID**:例如 `x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY` - **Client Secret**:例如 `LF71s5gqtPws2i08C9y43uIYXpF_c91ZnqRmS1z8Ekk` **⚠️ 重要**:请妥善保存这些凭证,后续无法再次查看 Client Secret。 ### 步骤 1.4:配置 APP 信息 在 APP 详情页面配置以下信息: #### 1.4.1 App URL(安装入口) 当商家从应用市场点击"安装"时,店匠会跳转到此 URL: ``` https://saas-ai-api.essa.top/recommend/oauth/install ``` **请求参数**: - `shop`:店铺域名,例如 `47167113-1.myshoplaza.com` - `hmac`:HMAC 签名(用于验证请求来源) - `install_from`:安装来源,例如 `app_store` - `store_id`:店铺 ID **完整示例**: ``` https://saas-ai-api.essa.top/recommend/oauth/install? hmac=c4caf9b08bdeff7531bb12712ffea860264ec24f5fd953832505c5024d19edca& install_from=app_store& shop=rwerwre.myshoplaza.com& store_id=1339409 ``` #### 1.4.2 Redirect URL(OAuth 回调地址) 商家授权后,店匠会重定向到此 URL: ``` https://saas-ai-api.essa.top/recommend/oauth/callback ``` **请求参数**: - `code`:授权码(只能使用一次) - `hmac`:HMAC 签名 - `shop`:店铺域名 - `state`:防 CSRF 攻击的随机值(可选) #### 1.4.3 Scopes(权限范围) 根据应用功能需求选择权限: | Scope | 说明 | 是否必需 | |-------|------|---------| | `read_shop` | 读取店铺信息 | ✅ 推荐 | | `read_product` | 读取商品信息 | ✅ 推荐 | | `read_customer` | 读取客户信息 | 可选 | | `read_order` | 读取订单信息 | 可选 | | `read_app_proxy` | 读取应用代理 | 可选 | | `write_cart_transform` | 修改购物车 | 可选 | **推荐配置**(智能推荐 APP): ``` read_shop read_product read_customer ``` ### 步骤 1.5:记录 Extension ID 创建主题扩展后,系统会生成 Extension ID,例如:`580182366958388163` 此 ID 需要在 `shoplazza.extension.toml` 中配置。 --- ## 阶段 2:创建前端项目 ### 步骤 2.1:创建项目目录 ```bash # 进入父目录 cd /home/tw/saas # 创建新项目目录 mkdir shoplazza-recommend-app cd shoplazza-recommend-app ``` ### 步骤 2.2:初始化项目 #### 方式 1:使用 Shoplazza CLI(推荐) ```bash # 安装 Shoplazza CLI(如果未安装) npm i -g shoplazza-cli # 生成项目结构 shoplazza app generate ``` #### 方式 2:手动创建 按照以下结构手动创建文件: ``` shoplazza-recommend-app/ ├── package.json ├── shoplazza.app.toml ├── README.md ├── .gitignore └── extensions/ └── recommend/ ├── shoplazza.extension.toml ├── package.json ├── blocks/ │ └── recommend.liquid ├── assets/ │ ├── recommend.css │ └── assets-manifest.json ├── snippets/ │ └── recommend.liquid └── locales/ ├── zh-CN.json └── en-US.json ``` ### 步骤 2.3:创建根目录配置文件 #### 2.3.1 package.json ```json { "name": "智能推荐1.0", "version": "1.0.0", "scripts": { "shoplazza": "shoplazza", "info": "shoplazza app info", "generate": "shoplazza app generate", "deploy": "shoplazza app deploy" }, "author": "", "license": "ISC", "private": true, "workspaces": [ "extensions/*" ] } ``` #### 2.3.2 shoplazza.app.toml 使用步骤 1.3 中获取的 Client ID: ```toml # 使用步骤 1.3 中获取的 Client ID client_id = "x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY" # 权限范围(与 Partner 后台配置保持一致) scopes = "read_shop read_product read_customer" ``` #### 2.3.3 .gitignore ```gitignore # Dependencies node_modules/ package-lock.json yarn.lock # Build outputs app-deploy/ dist/ build/ # Environment .env .env.local # IDE .vscode/ .idea/ *.swp *.swo # OS .DS_Store Thumbs.db # Logs *.log npm-debug.log* ``` #### 2.3.4 README.md ```markdown # 智能推荐 1.0 店匠(Shoplazza)智能推荐 APP 前端项目。 ## 快速开始 ### 安装依赖 ```bash npm install ``` ### 部署应用 ```bash npm run deploy ``` ### 查看应用信息 ```bash npm run info ``` ## 项目结构 - `extensions/recommend/`:推荐扩展 - `blocks/recommend.liquid`:主组件 - `assets/recommend.css`:样式文件 - `locales/`:多语言文件 ``` ### 步骤 2.4:创建 Extension 配置文件 #### 2.4.1 extensions/recommend/shoplazza.extension.toml ```toml # Extension ID(从 Partner 后台获取,或部署后系统生成) id = "580182366958388163" # Extension 名称(与目录名保持一致) name = "recommend" # Extension 类型(theme 表示主题扩展) type = "theme" ``` #### 2.4.2 extensions/recommend/package.json ```json { "name": "recommend", "version": "1.0.0", "private": true } ``` --- ## 阶段 3:开发后端服务 ### 步骤 3.1:OAuth 2.0 授权流程 店匠使用标准的 OAuth 2.0 授权码(Authorization Code)流程: ``` 1. 商家在应用市场点击"安装" ↓ 2. 店匠跳转到 APP URL(步骤 1.4.1) ↓ 3. 后端验证 HMAC 和 shop 参数 ↓ 4. 后端重定向到店匠授权页面 ↓ 5. 商家在授权页面点击"授权" ↓ 6. 店匠回调 Redirect URL(步骤 1.4.2),携带 code ↓ 7. 后端验证 HMAC,用 code 换取 access_token ↓ 8. 保存 token 到数据库,完成安装 ``` ### 步骤 3.2:实现 APP URL 处理(步骤 3) #### 3.2.1 请求示例 ``` GET https://saas-ai-api.essa.top/recommend/oauth/install? hmac=c4caf9b08bdeff7531bb12712ffea860264ec24f5fd953832505c5024d19edca& install_from=app_store& shop=rwerwre.myshoplaza.com& store_id=1339409 ``` #### 3.2.2 安全检查(必须) 在继续之前,必须执行以下安全检查: 1. **验证 HMAC 签名** - 从查询字符串中移除 `hmac` 参数 - 按字典顺序排序剩余参数 - 使用 `CLIENT_SECRET` 计算 HMAC-SHA256 - 与请求中的 `hmac` 值比较 2. **验证 shop 参数** - 必须是有效的店铺主机名 - 必须以 `.myshoplaza.com` 结尾 #### 3.2.3 Java 实现示例 ```java @RestController @RequestMapping("/recommend/oauth") public class RecommendOAuthController { @Autowired private ShoplazzaOAuthService oAuthService; /** * 处理 APP 安装请求(步骤 3) */ @GetMapping("/install") public ResponseEntity handleInstall( @RequestParam("shop") String shop, @RequestParam("hmac") String hmac, @RequestParam(value = "install_from", required = false) String installFrom, @RequestParam(value = "store_id", required = false) String storeId, HttpServletRequest request) { // 1. 验证 HMAC Map params = new HashMap<>(); params.put("shop", shop); params.put("hmac", hmac); if (installFrom != null) params.put("install_from", installFrom); if (storeId != null) params.put("store_id", storeId); if (!oAuthService.verifyHmac(params, hmac)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } // 2. 验证 shop 参数 if (!shop.endsWith(".myshoplaza.com")) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } // 3. 生成授权 URL 并重定向(步骤 4) String authUrl = oAuthService.buildAuthorizationRedirect(shop); return ResponseEntity.status(HttpStatus.FOUND) .header(HttpHeaders.LOCATION, authUrl) .build(); } } ``` #### 3.2.4 Node.js 实现示例 ```javascript const express = require("express"); const crypto = require("crypto"); const app = express(); const CLIENT_ID = "x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY"; const CLIENT_SECRET = "LF71s5gqtPws2i08C9y43uIYXpF_c91ZnqRmS1z8Ekk"; const REDIRECT_URI = "https://saas-ai-api.essa.top/recommend/oauth/callback"; // HMAC 验证函数 function verifyHmac(queryParams, receivedHmac) { const map = { ...queryParams }; delete map["hmac"]; const sortedKeys = Object.keys(map).sort(); const message = sortedKeys.map(key => `${key}=${map[key]}`).join('&'); const calculatedHmac = crypto .createHmac("sha256", CLIENT_SECRET) .update(message) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(calculatedHmac), Buffer.from(receivedHmac) ); } // 处理安装请求 app.get("/recommend/oauth/install", (req, res) => { const { shop, hmac, install_from, store_id } = req.query; // 1. 验证 HMAC if (!verifyHmac(req.query, hmac)) { return res.status(400).send("HMAC validation failed"); } // 2. 验证 shop 参数 if (!shop || !shop.endsWith(".myshoplaza.com")) { return res.status(400).send("Invalid shop parameter"); } // 3. 生成 state(防 CSRF) const state = crypto.randomBytes(16).toString("hex"); // 4. 构建授权 URL const scopes = "read_shop read_product read_customer"; const authUrl = `https://${shop}/admin/oauth/authorize?` + `client_id=${CLIENT_ID}&` + `scope=${encodeURIComponent(scopes)}&` + `redirect_uri=${encodeURIComponent(REDIRECT_URI)}&` + `response_type=code&` + `state=${state}`; // 5. 重定向到授权页面 res.redirect(authUrl); }); ``` ### 步骤 3.3:实现 OAuth 回调处理(步骤 7-9) #### 3.3.1 请求示例 ``` GET https://saas-ai-api.essa.top/recommend/oauth/callback? code=wBe-NWHzW21e94YqD4bRKBsJsE2GcZlDzP4oW9w2ddk& hmac=4c396fac1912057b65228f5bbd4a65255961d85a60fba1f1105ddbf27f17b58f& shop=rwerwre.myshoplaza.com& state=abc123 ``` #### 3.3.2 Java 实现示例 ```java /** * 处理 OAuth 回调(步骤 7-9) */ @GetMapping("/callback") public ResponseEntity handleCallback( @RequestParam("code") String code, @RequestParam("hmac") String hmac, @RequestParam("shop") String shop, @RequestParam(value = "state", required = false) String state) { // 1. 验证 HMAC Map params = new HashMap<>(); params.put("code", code); params.put("hmac", hmac); params.put("shop", shop); if (state != null) params.put("state", state); if (!oAuthService.verifyHmac(params, hmac)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body("HMAC validation failed"); } // 2. 用 code 换取 access_token ShoplazzaTokenResponse tokenResponse = oAuthService.exchangeForToken(shop, code); // 3. 保存 token 到数据库 ShopConfig shopConfig = shopConfigService.saveOrUpdate(shop, tokenResponse); // 4. 返回安装成功页面 String successPage = buildSuccessPage(shopConfig); return ResponseEntity.ok() .contentType(MediaType.TEXT_HTML) .body(successPage); } /** * 用 code 换取 access_token */ public ShoplazzaTokenResponse exchangeForToken(String shop, String code) { String tokenUrl = "https://" + shop + "/admin/oauth/token"; Map requestBody = new HashMap<>(); requestBody.put("client_id", CLIENT_ID); requestBody.put("client_secret", CLIENT_SECRET); requestBody.put("code", code); requestBody.put("grant_type", "authorization_code"); requestBody.put("redirect_uri", REDIRECT_URI); // 发送 POST 请求 RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity> entity = new HttpEntity<>(requestBody, headers); ResponseEntity response = restTemplate.postForEntity( tokenUrl, entity, ShoplazzaTokenResponse.class); return response.getBody(); } ``` #### 3.3.3 Token 响应格式 ```json { "token_type": "Bearer", "expires_at": 1550546245, "access_token": "eyJ0eXAiOiJKV1QiLCJh...", "refresh_token": "def502003d28ba08a964e...", "store_id": "1339409", "store_name": "rwerwre" } ``` ### 步骤 3.4:实现 API 调用 获取 access_token 后,可以调用店匠 API: ```java /** * 调用店匠 API 示例:获取商品列表 */ public List getProducts(String shop, String accessToken) { String apiUrl = "https://" + shop + "/openapi/2022-01/products"; HttpHeaders headers = new HttpHeaders(); headers.set("Access-Token", accessToken); headers.set("Content-Type", "application/json"); HttpEntity entity = new HttpEntity<>(headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.exchange( apiUrl, HttpMethod.GET, entity, ProductListResponse.class ); return response.getBody().getProducts(); } ``` --- ## 阶段 4:前端开发详解 ### 步骤 4.1:创建 Liquid 模板 #### 4.1.1 extensions/recommend/blocks/recommend.liquid ```liquid {% comment %} 推荐商品组件 使用 {% use %} 引入 CSS 文件 {% endcomment %} {% use "recommend.css" %}
{% comment %} 版本标识(开发测试用){% endcomment %}
智能推荐 APP
{% comment %} 标题(使用多语言){% endcomment %}

{{ 'recommend.title' | t }}

{% comment %} 商品列表容器 {% endcomment %}
{{ 'recommend.loading' | t }}
{% comment %} Schema 配置(用于主题编辑器){% endcomment %} {% schema %} { "name": { "en-US": "recommend(dev)", "zh-CN": "智能推荐(开发)" }, "settings": [ { "type": "text", "id": "title", "label": { "en-US": "Title", "zh-CN": "标题" }, "default": { "en-US": "Recommended Products", "zh-CN": "推荐商品" } }, { "type": "range", "id": "product_count", "label": { "en-US": "Number of products", "zh-CN": "商品数量" }, "min": 4, "max": 20, "step": 1, "default": 8 } ] } {% endschema %} ``` ### 步骤 4.2:创建样式文件 #### 4.2.1 extensions/recommend/assets/recommend.css ```css /* 推荐商品容器 */ .recommend-container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 20px; font-family: var(--font-body-family, sans-serif); color: var(--color-text, #333); background: var(--color-background, #fff); } /* 版本标识(开发测试用)*/ .recommend-badge { position: fixed; top: 10px; right: 10px; background: #ff6b6b; color: white; padding: 5px 10px; border-radius: 4px; font-size: 12px; font-weight: bold; z-index: 9999; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } /* 标题 */ .recommend-container h2 { font-size: 24px; font-weight: 600; margin-bottom: 20px; text-align: center; } /* 商品网格 */ .recommend-products { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; margin-top: 20px; } /* 商品项 */ .product-item { border: 1px solid var(--color-border, #e0e0e0); border-radius: 8px; overflow: hidden; transition: transform 0.2s, box-shadow 0.2s; background: var(--color-background, #fff); } .product-item:hover { transform: translateY(-4px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); } /* 商品链接 */ .product-link { text-decoration: none; color: inherit; display: block; } /* 商品图片 */ .product-image-wrapper { width: 100%; padding-top: 100%; /* 1:1 比例 */ position: relative; overflow: hidden; background: #f5f5f5; } .product-image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; } /* 商品信息 */ .product-info { padding: 12px; } .product-title { font-size: 14px; font-weight: 500; margin: 0 0 8px 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .product-price { font-size: 16px; font-weight: 600; color: var(--color-price, #e74c3c); margin-bottom: 4px; } .product-vendor { font-size: 12px; color: var(--color-text-secondary, #999); } /* 添加到购物车按钮 */ .add-to-cart-btn { width: 100%; padding: 10px; background: var(--color-button, #007bff); color: white; border: none; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background 0.2s; } .add-to-cart-btn:hover { background: var(--color-button-hover, #0056b3); } /* 加载状态 */ .loading { text-align: center; padding: 40px; color: var(--color-text-secondary, #999); } /* 无结果 */ .no-results { text-align: center; padding: 40px; color: var(--color-text-secondary, #999); } /* 错误状态 */ .error { text-align: center; padding: 40px; color: var(--color-error, #e74c3c); } /* 响应式设计 */ @media (max-width: 749px) { .recommend-container { padding: 10px; } .recommend-products { grid-template-columns: repeat(2, 1fr); gap: 10px; } .recommend-container h2 { font-size: 20px; } } @media (min-width: 750px) and (max-width: 989px) { .recommend-products { grid-template-columns: repeat(3, 1fr); } } @media (min-width: 990px) { .recommend-products { grid-template-columns: repeat(4, 1fr); } } ``` ### 步骤 4.3:创建多语言文件 #### 4.3.1 extensions/recommend/locales/zh-CN.json ```json { "recommend": { "title": "为您推荐", "loading": "加载中,请稍候...", "no_results": "暂无推荐商品", "error": "加载失败,请重试", "add_to_cart": "加入购物车", "view_details": "查看详情" } } ``` #### 4.3.2 extensions/recommend/locales/en-US.json ```json { "recommend": { "title": "Recommended for You", "loading": "Loading, please wait...", "no_results": "No recommended products", "error": "Failed to load, please try again", "add_to_cart": "Add to Cart", "view_details": "View Details" } } ``` #### 4.3.3 支持的语言列表 店匠支持 16 种语言,建议至少提供以下语言文件: | 语言代码 | 语言名称 | 文件路径 | |---------|---------|---------| | `zh-CN` | 中文(简体) | `locales/zh-CN.json` | | `zh-TW` | 中文(繁体) | `locales/zh-TW.json` | | `en-US` | 英语 | `locales/en-US.json` | | `ja-JP` | 日语 | `locales/ja-JP.json` | | `ko-KR` | 韩语 | `locales/ko-KR.json` | **完整语言列表**:参考 [多语言支持文档](#多语言支持) ### 步骤 4.4:Lessjs 集成(可选) Lessjs 是店匠官方提供的前端组件库,基于 Web Components。如需使用,参考: - [Lessjs 官方文档](https://lessjs.shoplazza.com/latest/docs/introduction/) - [Lessjs 自定义组件文档](https://lessjs.shoplazza.com/latest/docs/custom-component/) **注意**:对于简单的推荐组件,可以不使用 Lessjs,直接使用原生 JavaScript 和 CSS。 --- ## 阶段 5:测试与部署 ### 步骤 5.1:本地测试 #### 5.1.1 检查项目结构 ```bash cd /home/tw/saas/shoplazza-recommend-app # 检查文件结构 tree -L 3 # 应该看到: # shoplazza-recommend-app/ # ├── package.json # ├── shoplazza.app.toml # └── extensions/ # └── recommend/ # ├── shoplazza.extension.toml # ├── blocks/ # ├── assets/ # └── locales/ ``` #### 5.1.2 验证配置 ```bash # 查看应用信息 npm run info # 应该显示: # - App Name: 智能推荐1.0 # - Client ID: x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY # - Extension ID: 580182366958388163 ``` ### 步骤 5.2:部署到店匠 #### 5.2.1 登录 Shoplazza CLI ```bash # 登录(如果未登录) shoplazza login # 输入 Partner 账号和密码 ``` #### 5.2.2 部署应用 ```bash # 部署应用 npm run deploy # 或者 shoplazza app deploy ``` **部署过程**: 1. CLI 会验证项目配置 2. 上传扩展文件到店匠服务器 3. 返回部署结果 ### 步骤 5.3:在测试店铺中安装 #### 5.3.1 访问应用市场 1. 登录测试店铺后台 2. 进入 **应用市场** 或 **Apps** 3. 搜索您的应用名称(如"智能推荐1.0") 4. 点击 **安装** 或 **Install** #### 5.3.2 OAuth 授权流程 1. 点击安装后,会跳转到您的后端服务(APP URL) 2. 后端验证 HMAC 和 shop 参数 3. 重定向到店匠授权页面 4. 商家点击"授权" 5. 店匠回调您的后端(Redirect URL) 6. 后端用 code 换取 access_token 7. 显示安装成功页面 #### 5.3.3 在主题中添加扩展 1. 进入店铺后台:**在线商店** > **主题** > **自定义** 2. 在页面中添加 **App 扩展** 3. 选择 **智能推荐** 扩展 4. 配置扩展设置(如商品数量) 5. 保存并发布 ### 步骤 5.4:验证功能 #### 5.4.1 前端验证 - [ ] 扩展正确显示在主题编辑器中 - [ ] 商品列表正确加载 - [ ] 多语言切换正常 - [ ] 响应式布局在不同设备上正常 - [ ] 图片加载正常(包括错误处理) #### 5.4.2 后端验证 - [ ] OAuth 授权流程正常 - [ ] HMAC 验证正常 - [ ] Token 正确保存到数据库 - [ ] API 调用正常(如获取商品列表) #### 5.4.3 浏览器控制台检查 打开浏览器开发者工具(F12),检查: - **Console**:无 JavaScript 错误 - **Network**:API 请求正常(状态码 200) - **Elements**:HTML 结构正确 --- ## 常见问题与最佳实践 ### 常见问题 #### Q1: HMAC 验证失败 **原因**: - Client Secret 配置错误 - 参数排序不正确 - HMAC 计算方式错误 **解决方案**: 1. 确认 `CLIENT_SECRET` 与 Partner 后台一致 2. 确保参数按字典顺序排序 3. 使用 `HMAC-SHA256` 算法 #### Q2: 多语言不生效 **原因**: - 语言文件路径错误 - 翻译键名不匹配 - Liquid 模板语法错误 **解决方案**: 1. 确认 `locales/{locale}.json` 文件存在 2. 检查翻译键名是否与模板中的 `{{ 'key' | t }}` 一致 3. 使用 `{{ shop.locale }}` 获取当前语言 #### Q3: 扩展在主题编辑器中不显示 **原因**: - Extension ID 配置错误 - 部署未成功 - Schema 配置错误 **解决方案**: 1. 确认 `shoplazza.extension.toml` 中的 `id` 正确 2. 重新部署应用:`npm run deploy` 3. 检查 `{% schema %}` 配置是否正确 #### Q4: API 调用返回 401 未授权 **原因**: - Access Token 过期 - Token 未正确传递 - API 版本不匹配 **解决方案**: 1. 使用 `refresh_token` 刷新 Access Token 2. 确认请求头包含 `Access-Token: {access_token}` 3. 检查 API 版本(如 `/openapi/2022-01/`) ### 最佳实践 #### 1. 安全性 - ✅ **始终验证 HMAC**:所有来自店匠的请求都必须验证 HMAC - ✅ **验证 shop 参数**:确保 shop 以 `.myshoplaza.com` 结尾 - ✅ **使用 HTTPS**:所有 API 调用必须使用 HTTPS - ✅ **保护 Client Secret**:不要将 Client Secret 提交到代码仓库 #### 2. 性能优化 - ✅ **延迟加载**:非关键资源使用 `defer` 或 `async` - ✅ **图片优化**:使用响应式图片和 CDN - ✅ **缓存策略**:合理使用浏览器缓存和 CDN 缓存 - ✅ **代码分割**:将大型 JavaScript 拆分为多个模块 #### 3. 用户体验 - ✅ **加载状态**:显示加载中、错误、无结果等状态 - ✅ **错误处理**:友好的错误提示和重试机制 - ✅ **响应式设计**:适配移动端、平板、桌面端 - ✅ **多语言支持**:至少支持中文和英语 #### 4. 代码质量 - ✅ **代码注释**:关键逻辑添加注释 - ✅ **错误日志**:记录错误日志便于调试 - ✅ **代码规范**:遵循项目代码规范 - ✅ **版本控制**:使用 Git 进行版本管理 --- ## 参考资源 ### 官方文档 - [Shoplazza 开发者文档](https://www.shoplazza.dev/) - [Shoplazza CLI 文档](https://www.shoplazza.dev/docs/overview-3) - [Lessjs 官方文档](https://lessjs.shoplazza.com/latest/docs/introduction/) - [Liquid 模板语言](https://shopify.github.io/liquid/) ### 社区支持 - [Shoplazza 开发者社区](https://community.shoplazza.com/) - [GitHub Issues](https://github.com/your-org/shoplazza-recommend-app/issues) --- ## 附录 ### A. 完整项目结构 ``` shoplazza-recommend-app/ ├── package.json # 项目配置 ├── shoplazza.app.toml # APP 配置(Client ID) ├── README.md # 项目说明 ├── .gitignore # Git 忽略文件 ├── SHOPLAZZA_APP_DEVELOPMENT_GUIDE.md # 本指南 └── extensions/ └── recommend/ # 推荐扩展 ├── shoplazza.extension.toml # Extension 配置 ├── package.json # Extension 配置 ├── blocks/ # 组件块 │ └── recommend.liquid # 主组件 ├── assets/ # 静态资源 │ ├── recommend.css # 样式文件 │ └── assets-manifest.json # 资源清单 ├── snippets/ # 代码片段 │ └── recommend.liquid # 可复用片段 └── locales/ # 多语言文件 ├── zh-CN.json # 简体中文 ├── zh-TW.json # 繁体中文 ├── en-US.json # 英语 ├── ja-JP.json # 日语 └── ko-KR.json # 韩语 ``` ### B. 配置文件模板 #### B.1 shoplazza.app.toml ```toml client_id = "YOUR_CLIENT_ID" scopes = "read_shop read_product read_customer" ``` #### B.2 shoplazza.extension.toml ```toml id = "YOUR_EXTENSION_ID" name = "recommend" type = "theme" ``` ### C. OAuth 流程时序图 ``` 商家 → 店匠平台 → 后端服务 → 店匠授权页 → 商家授权 → 后端服务 → 数据库 ↓ ↓ ↓ ↓ ↓ ↓ ↓ 安装 跳转APP URL 验证HMAC 重定向授权 点击授权 换取Token 保存配置 ``` ---