mcpField
S
salla-app-lifecycle

salla-app-lifecycle

github.com/sallaapp开发289 次安装5 天前更新
代码仓库

关于 salla-app-lifecycle

面向 Salla 应用开发者的生命周期处理技能,指导配置 webhook 并编写安装、授权、订阅与卸载事件处理器。重点是按商家记录进行幂等更新,并处理签名验证与异步任务可靠性。

主要功能

  1. 01配置生命周期事件 webhook
  2. 02处理安装、更新与卸载
  3. 03处理试用和订阅状态
  4. 04按商家幂等更新记录
  5. 05指导签名校验与异步重试

适用场景

  1. 01接入 Salla 应用安装流程
  2. 02同步商家令牌与订阅状态
  3. 03实现卸载后的数据清理

MCP 相关

通过 Salla Partners MCP 的 `salla_apps` 工具设置 webhook 接收地址并检查应用状态;应用生命周期事件会自动投递到该地址。

安装

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

$npx skills add https://github.com/sallaapp/salla-partners-agent-kit --skill salla-app-lifecycle

安装后在 Claude Code 中输入/salla-app-lifecycle确认已加载。

附带文件references/lifecycle-payloads.md

详细介绍

Salla 应用生命周期流程

让后端与每位商家和应用的关系保持同步——从安装到试用、订阅和卸载。请按顺序完成各步骤;完成每个关卡后再继续。步骤 1 使用 Salla Partners MCP 设置 Webhook 接收端;步骤 2–6 是你需要编写的运行时处理程序。

工具与 MCP

App Events 文档(https://docs.salla.dev/421413m0.md)是所有生命周期负载结构的权威来源。**Salla Partners MCP** 负责执行操作:

工具操作功能
salla_appsconnect / get设置 webhook_url(生命周期应用事件会自动投递到此 URL)/检查应用状态

生命周期事件是 app.* 事件——无需订阅调用,系统会自动投递。 默认情况下,应用已订阅其自身的应用事件,因此 app.installed、app.store.authorize、 app.updated、app.uninstalled、app.trial.*、app.subscription.* 和 app.settings.updated 都会在触发时自动发送到你的 webhook_url。 你无需对其中任何事件调用 salla_events action=subscribe——此操作仅用于非应用(商店)事件(order.*、product.* 等)→ salla-webhooks。

所有事件都会通过标准封装(event、merchant、created_at、data)发送到你的单一 Webhook 端点。验证签名,快速返回 200 确认,然后异步处理。始终以 merchant 为键执行 upsert。 前置条件:签名验证 + 幂等性 + 快速返回 200 → salla-webhooks;从 app.store.authorize 持久化令牌 → salla-app-auth。

事件目录(参考)
事件触发时机操作建议
app.installed首次安装配置商家资源,设置默认值
app.store.authorize安装或刷新令牌按商家保存/更新 access_token + refresh_token + 过期时间
app.updated商家更新应用Salla 随后会立即触发 app.store.authorize — 等待该事件以获取新令牌
app.settings.updated商家更改应用设置应用 data.settings — 架构/验证由 salla-app-settings 负责
app.trial.started试用开始启用试用功能
app.trial.expired试用结束且未升级限制访问
app.trial.canceled试用已取消限制访问
app.subscription.started付费计划或附加项已激活解锁功能;根据 data.item_type 分支处理
app.subscription.renewed计划/附加项已续订确认处于有效状态;保存新的 data.end_date / renew_date
app.subscription.expired计划/附加项已到期限制访问,通知商家
app.subscription.canceled计划/附加项已取消限制访问
app.feedback.created商家提交评价记录评分/评论

app.subscription.* 会针对计划和附加项同时触发 — data.item_type ("plan" | "addon")用于区分两者。app.trial.* 的载荷比订阅事件更精简(不包含 subscription_id、item_type、定价等)— 请参阅参考资料。完整的订阅载荷和计划状态处理 → salla-app-billing。各事件的完整 JSON(以 https://docs.salla.dev/421413m0.md 中的权威结构为准)→ references/lifecycle-payloads.md。


步骤 0 — 探索

开始前请先询问:

  1. 你的应用关注哪些生命周期状态?(仅安装,还是也包括试用和订阅?)
  2. 安装时要配置哪些内容,卸载时必须清理哪些内容(数据保留/GDPR 政策)?
  3. 是否要根据计划/试用状态限制功能?(如果是,请配合 salla-app-billing)

步骤 1 — 设置 Webhook URL(生命周期事件会自动投递)

生命周期事件是应用事件——应用默认已订阅这些事件,因此设置 webhook_url 后,事件会立即送达。无需调用订阅接口;只需通过 Partners MCP 配置接收端:

  1. 配置接收端:salla_apps action=connect、app_id、webhook_url、webhook_security_strategy: "signature"。connect 不会生成签名密钥——请在 Partner Portal(https://portal.salla.partners/apps/{app_id})中创建或轮换密钥,并在部署前通过 salla_apps action=get(webhook_secret 字段)读取当前值。将其存入密钥管理器/KMS(绝不要存入源代码或日志)——安全存储和 HMAC 验证由 salla-webhooks 负责。
  2. 投递配置到此为止:app.installed、app.store.authorize、app.updated、app.uninstalled、app.trial.*、app.subscription.* 和 app.settings.updated 现在都会自动发送到该 URL。(salla_events action=subscribe 用于店铺事件——order.*、product.* 等——生命周期处理无需订阅这些事件 → salla-webhooks。)

随时使用 salla_apps action=get、app_id 查看应用的当前配置。

检查点:“已通过 salla_apps action=connect 设置 webhook_url(应用事件会自动发送到该地址,无需调用订阅接口),且你的端点会返回 200?”


步骤 2 — 处理安装流程

首次安装时,Salla 会发送 app.installed 和 app.store.authorize。请将每个处理程序视为独立且幂等的;事件到达顺序无法保证。

在每个处理程序中对每个 merchant 记录执行插入或更新(创建或更新)。 任何生命周期事件——例如 app.trial.started——都可能先于 app.store.authorize 到达,因此记录可能尚不存在。每个处理程序都应在商户记录不存在时创建,存在时更新,并以 merchant 为键。这样无论事件以何种顺序到达,或应用重新安装,每个处理程序都能安全运行。

if (payload.event === "app.installed") {
  // Provision: create the merchant row, seed defaults, queue a welcome step.
  await db.merchants.upsert({
    where: { id: payload.merchant },
    create: {
      id: payload.merchant,
      status: "installed",
      installedAt: new Date(),
    },
    update: { status: "installed" },
  });
}

if (payload.event === "app.store.authorize") {
  // Persist tokens — see salla-app-auth for the full upsert + refresh rules.
  // Encrypt access_token/refresh_token at rest and keep them out of logs.
  const { access_token, refresh_token, expires, scope } = payload.data;
  await db.merchants.upsert({
    where: { id: payload.merchant },
    create: {
      id: payload.merchant,
      accessToken: access_token,
      refreshToken: refresh_token,
      tokenExpiresAt: new Date(expires * 1000),
      scope,
    },
    update: {
      accessToken: access_token,
      refreshToken: refresh_token,
      tokenExpiresAt: new Date(expires * 1000),
      scope,
    },
  });
}

使用带版本控制的迁移工具管理商户/令牌表(使用带版本号的 ALTER TABLE)。随着添加或更改列,这样可确保架构与代码保持同步;而启动时创建表的步骤会跳过已存在的表。

检查点:“演示店铺安装后,无论事件以何种顺序到达,都会创建商户记录并持久化令牌?”


步骤 3 — 处理更新流程

商户更新应用时,Salla 会先发送 app.updated,然后立即发送包含新令牌的 app.store.authorize。

  • 将 app.updated 仅视为一个信号——不要指望其负载中包含令牌。
  • 让现有的 app.store.authorize 处理程序持久化新令牌。

检查点:“应用更新会通过授权处理程序刷新已存储的令牌,而不是通过更新处理程序刷新?”


步骤 4 — 处理卸载清理

app.uninstalled 表示商户已移除你的应用。清理是 App Store 的要求——请按照你的数据保留/GDPR 政策进行处理。

if (payload.event === "app.uninstalled") {
  await jobs.enqueue("cleanupMerchant", { merchantId: payload.merchant });
  // In the job: revoke cached tokens, delete or anonymize PII, cancel scheduled work,
  // mark merchant status = "uninstalled". Keep only what your policy/law requires.
}

请说明存储了哪些数据,以及卸载时如何删除这些数据——提交审核时会检查此项。

检查点:“卸载会将清理任务加入队列,且任务会撤销令牌并移除/匿名化 PII?”


步骤 5 — 处理试用与订阅事件

事件效果
app.trial.started启用试用版功能;记录试用结束时间
app.trial.expired降级/限制功能,直到商户订阅
app.trial.canceled立即限制功能

商户通常会经历 trial.started →(转为付费时为 subscription.started;未转为付费时为 trial.expired)。此处仅提供最低限度的订阅处理——完整逻辑见 salla-app-billing:

if (payload.event === "app.subscription.started") {
  const { item_type, item_slug, end_date } = payload.data; // item_type: "plan" | "addon"
  if (item_type === "addon") {
    await entitlements.activateAddon(payload.merchant, item_slug, end_date);
  } else {
    await merchants.activatePlan(payload.merchant, payload.data);
  }
}

留意 data.store_type(development | demo | live),以免将测试店铺的订阅视为真实收入。

检查点:“试用和订阅事件会根据 item_type 分支,并更新商户已存储的状态?”


步骤 6 — 加固:可靠性与状态机

以上每个处理程序都必须满足以下要求:

  • 首先验证签名(HMAC-SHA256,使用时序安全比较),否则拒绝请求。
  • 快速确认,返回 200,然后异步处理 — Salla 会重试失败的投递; 超时与重试策略由 salla-webhooks 管理。
  • 确保幂等 — 使用载荷中的稳定标识符(例如 subscription_id)或原始请求体的哈希作为键。(created_at 精确到秒,因此 单独使用 ${merchant}:${event}:${created_at} 可能发生冲突。)
  • 使用 upsert,以便同一商家可以重新安装并重新授权。
  • 处理异步任务失败 — 200 已经发出,因此排队的工作进程中抛出的异常对 Salla 不可见。将失败的任务路由到死信队列,采用退避策略重试(确保幂等),并在重试耗尽时发出告警,以免安装或卸载事件丢失而无人察觉。

建议的商家状态机 — 按商家持久化 status,并从以上处理程序触发状态转换:

        app.installed
              │
              ▼
  ┌──────► installed ──────┐
  │           │            │ app.trial.started
  │           │            ▼
  │           │          trial ──── app.trial.expired/canceled ──► restricted
  │           │            │ app.subscription.started
  │           ▼            ▼
  │        active ◄── app.subscription.renewed
  │           │
  │           │ app.subscription.expired/canceled
  │           ▼
  │       restricted
  │           │ app.subscription.started (merchant re-purchases)
  └───────────┘
              │ app.uninstalled (from any state)
              ▼
        uninstalled  (run cleanup)

根据 status(以及附加组件的权益 — 参见 salla-app-billing)控制功能。

检查:“每个处理程序都会验证签名、快速返回 200、确保幂等,并按照状态机更新 status 吗?”


风险信号

在快速测试中看似成立、但面对真实商家时会出问题的生命周期投递假设。如果你的方案包含以下任何一点,请重新阅读所标注的步骤。

容易产生的想法错误原因
"app.installed 会先于 app.store.authorize 到达。"事件顺序不保证 — 任何事件都可能先到。让每个处理程序彼此独立并确保幂等,按 merchant 执行 upsert(步骤 2)。
"app.updated 会携带新令牌。"它只是一条信号 — 令牌会在之后的 app.store.authorize 中提供。不要从更新事件的载荷中读取令牌(步骤 3)。
"insert 没问题 — 应用只会安装一次。"商家可能会重新安装并重新授权。仅插入会报错或产生重复数据;始终按 merchant 执行 upsert(步骤 2、6)。
"异步任务已经入队,因此工作是安全的。"200 已经发出 — 工作进程中抛出的异常对 Salla 不可见。使用死信队列,进行幂等重试,并发出告警(步骤 6)。
"${merchant}:${event}:${created_at} 是唯一键。"created_at 精确到秒;同一秒内两个同类型事件会发生冲突。添加 subscription_id 或原始请求体的哈希(步骤 6)。
"我会把 test-store 的订阅事件计为收入。"检查 data.store_type — 只有 live 才是真实数据;开发或演示安装会污染收入和权益(步骤 5)。
"卸载只需切换状态标记。"清理工作(撤销令牌、删除或匿名化 PII)是 App Store 的要求,提交时会进行审核 — 将完整任务加入队列(步骤 4)。

关键资源

资源URL
应用事件文档https://docs.salla.dev/421413m0.md
Webhooks(安全)salla-webhooks 技能
令牌处理salla-app-auth 技能
套餐/试用状态salla-app-billing 技能
Partners Portalhttps://salla.partners
Telegram 社区https://t.me/salladev

评论

支持 Markdown