DDSFulfill开发文档
文档  /  开放应用能力

PROVIDER GUIDE

向 ERP 开放你的应用能力

能力是第三方应用提供、但可直接嵌入 ERP 业务页面调用的服务,例如以图搜索、快速翻译、图片处理或地址校验。开放平台负责路由与授权,应用负责业务执行、套餐和额度。

这不是第二套安装授权

企业仍然只在 ERP 官方安装页确认一次权限。能力调用不会跳到第三方再次授权,也不能由第三方隐藏或修改 Scope。

ERP 调用方的完整请求流程见 ERP 内嵌能力接入指南 →

提供能力的六个步骤

  1. 为能力确定稳定 Key、允许的 ERP Surface、输入输出和所需 Scope。
  2. 在应用服务端实现公网 Provider Endpoint。
  3. 使用对应环境 Client Credential 向开放平台登记能力。
  4. 收到调用后,用一次性 Ticket 换取可信企业和输入。
  5. 按 Installation 检查设置、订阅和额度,并以 Invocation ID 幂等执行。
  6. 返回标准同步、异步或业务拒绝结果;Production 随应用版本提交审核。

1. 登记能力

能力 Key 应使用命名空间形式,例如 image-search.search。Surface 表示能力获准出现的 ERP 业务位置,例如 order.quote.product-picker。

登记图片搜索能力cURL
curl --request PUT \
  --url https://open.dsfulfill.io/v1/capabilities/image-search.search \
  --user "$CLIENT_ID:$CLIENT_SECRET" \
  --header 'Content-Type: application/json' \
  --data '{
    "version": "1.0",
    "title": "以图搜索本地商品",
    "description": "在订单报价中按图片搜索当前企业商品库",
    "mode": "sync",
    "endpoint_url": "https://app.example.com/api/capabilities/image-search",
    "timeout_seconds": 10,
    "required_scopes": ["products:read"],
    "surfaces": ["order.quote.product-picker"],
    "input_schema": {
      "type": "object",
      "required": ["image_url"],
      "properties": {
        "image_url": { "type": "string", "format": "uri" },
        "limit": { "type": "integer", "minimum": 1, "maximum": 50 }
      }
    },
    "output_schema": { "type": "object", "required": ["items"] }
  }'

凭证决定登记环境。Sandbox 能力登记后立即生效并允许 localhost;Production Endpoint 必须是公网 HTTPS,登记或变更后进入待审核状态。

2. 接收调用并兑换 Ticket

Provider Endpoint 收到JSON
POST /api/capabilities/image-search
X-DSFulfill-Environment: production
X-DSFulfill-Capability: image-search.search
X-DSFulfill-Invocation-Id: inv_xxx

{
  "invocation_id": "inv_xxx",
  "ticket": "ds_cap_xxx"
}

Header 可用于追踪日志,但不能代替换票。应用后端应立即用同环境 Client Credential 请求:

兑换可信上下文cURL
curl --request POST \
  --url https://open.dsfulfill.io/v1/capability-invocations/tickets/exchange \
  --user "$CLIENT_ID:$CLIENT_SECRET" \
  --header 'Content-Type: application/json' \
  --data '{"ticket":"ds_cap_xxx"}'
换票响应JSON
{
  "data": {
    "invocation_id": "inv_xxx",
    "application_id": "app_xxx",
    "installation_id": "ins_xxx",
    "company_id": "company_xxx",
    "actor_id": "admin_xxx",
    "environment": "production",
    "capability": "image-search.search",
    "surface": "order.quote.product-picker",
    "input": { "image_url": "https://cdn.example.com/query.jpg", "limit": 12 },
    "expires_at": "2026-09-06T12:00:00.000Z"
  }
}

Ticket 有效期 60 秒,并绑定应用、Installation 和环境。不要从 Cookie、邮箱、请求 Header 或输入字段推断企业;只认换票响应中的上下文。

3. 执行、租户隔离与计费

  1. 校验换票结果中的 capability、surface 和 environment 与 Endpoint 预期一致。
  2. 用 company_id + installation_id 加载应用工作区、设置和套餐。
  3. 以 invocation_id 建立唯一执行记录;存在完成结果时直接重放。
  4. 在同一事务中检查额度、写额度流水并标记消费,避免并发重复扣费。
  5. 所有商品查询必须带可信 company_id,禁止跨企业索引或缓存命中。
防止重复试用

免费额度应归属 Installation 或 ERP Company,而不是第三方登录账号。用户退出后换账号、重新打开应用,都不能重新获得同一企业的首赠额度。

4. 返回标准结果

同步完成JSON
{
  "status": "completed",
  "data": {
    "items": [
      { "product_id": "19550", "title": "Example", "image_url": "https://...", "score": 0.94 }
    ]
  },
  "usage": { "charged": 1, "remaining": 99 }
}
异步接受JSON
{
  "status": "accepted",
  "job_id": "job_xxx"
}

异步任务完成后,使用对应 Installation Access Token 回调 POST /v1/capability-invocations/{invocation_id}/complete。同一 Invocation 的完成回调也必须幂等。

业务拒绝JSON
{
  "status": "denied",
  "reason": "SUBSCRIPTION_REQUIRED",
  "action": {
    "type": "open_app",
    "label": "选择套餐",
    "launch_path": "/billing"
  }
}

允许的拒绝原因只有 SUBSCRIPTION_REQUIRED、INSUFFICIENT_CREDITS 和 SETUP_REQUIRED。launch_path 必须是本站相对路径,不能返回任意外部 URL。

5. Sandbox、Production 与验收

  • 先用 Sandbox Client Credential 登记 localhost 或测试 HTTPS Endpoint。
  • 把应用安装到绑定的 ERP 测试企业,完成正常安装授权。
  • 测试成功、重复请求、未订阅、额度不足、未初始化、Ticket 过期和 Provider 超时。
  • 再用 Production Client Credential 登记公网 HTTPS Endpoint。
  • 提交 Production Release;平台审核 Scope、Surface、Schema、Endpoint 和用途。
  • 发布后用真实 Production Installation 验证,并继续保留 Sandbox 用于下一版本开发。

日志至少记录 invocation_id、环境、耗时和内部结果状态,但必须隐藏 Ticket、Client Secret、Access Token 及用户敏感输入。