FinClip为企业提供小程序生态圈技术产品,开发者可在FinClip小程序开发帮助中心找到相关FinClip小程序指引

# Webhook 使用说明

Webhook 提供了一种标准化的方式,使客户可以订阅系统中的某些关键节点,并进行相应的业务处理。在 FinClip 管理系统中,开发者可以通过 Webhook 功能与自有的第三方系统(如短信、邮件发送系统)进行对接,以便提升用户使用体验。

事件列表如下:

事件名称 事件Code 备注
短信 EVENT_SMS
邮件 EVENT_EMAIL
添加小程序 EVENT_MINIAPP_ADD
删除小程序 EVENT_MINIAPP_DELETE
编辑小程序 EVENT_MINIAPP_EDIT
发布小程序 EVENT_MINIAPP_PUBLISH
取消发布小程序 EVENT_MINIAPP_UNPUBLISH
回滚小程序 EVENT_MINIAPP_ROLLBACK
禁用小程序 EVENT_MINIAPP_DISABLE
启用小程序 EVENT_MINIAPP_ENABLE
启用小程序搜索 EVENT_MINIAPP_ENABLE_SEARCH
禁用小程序搜索 EVENT_MINIAPP_DISABLE_SEARCH

# 事件说明

# 1. 事件-短信

注意:这个地方勾选了短信的事件之后,会禁用系统原本的短信发送渠道,短信的发送功能将完全由Webhook的接收方完成

# 1.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 1.2 请求结构

{
    "event": "EVENT_SMS",
    "params": { // 这一层的params 根据不同的event, 会有不同的值
        "scene": 100, // 短信发送场景
        "areaCode": "+86", // 国家代号
        "phone": "18562310708", // 手机号
        "params": [ //  这一层的params 根据不同的scene, 会有不同的值
          // 这里给的是object array的元数据,不会直接给短信内容。方便根据不同渠道进行不同的处理。
          // 因为类似腾讯云这种短信发送渠道,是在控制台里面维护模板,然后调用的时候使用templateId调用
            {
                "key": "VerifyCode", // 验证码
                "value": "654782"
            },
            {
                "key": "ValidityMinutes", // 验证码有效期的分钟数,默认都是10分钟
                "value": "10"
            }
        ]
    }
}

# 1.3 参数说明

参数 类型 说明
scene Number 目前只会有短信验证码一种场景,即:
100: 短信验证码
areaCode String 国家代号
phone String 手机号
params object array 这里根据不同的 scene ,会有不同的 params

# 2. 事件-邮件

注意:这个地方勾选了邮件的事件之后,会禁用系统原本的邮件发送渠道,邮件的发送功能将完全由Webhook的接收方完成

# 2.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 2.2 请求结构

{
    "event": "EVENT_EMAIL",
    "params": { // 这一层的params 根据不同的event, 会有不同的值
        "scene": 100, // 邮件发送场景
        "to": "test@gmail.com", // 邮件接收者,目前没有群发场景
        "subject": "", // 邮件发送的subject
        "params": [ //  这一层的params 根据不同的scene, 会有不同的值
          // 这里给的是object array的元数据,不会直接给邮件内容。方便根据不同渠道进行不同的处理。
          // 因为部分邮件发送渠道,是在控制台里面维护模板,然后调用的时候使用templateId调用
            {
                "key": "UserName", // FinClip 用户名
                "value": "autolfymcp"
            },
            {
                "key": "VerifyCode", // 邮件验证码
                "value": "177793"
            }
        ]
    }
}

# 2.3 参数说明

参数 类型 说明
scene Number 目前只会有邮件验证码一种场景,即:
100: 邮件验证码
to String 邮件接收者
subject String 手机号
params object array 这里根不同的 scene ,会有不同的 params

# 3. 事件-添加小程序

# 2.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 2.2 请求结构

{
    "event": "EVENT_MINIAPP_ADD",
    "params": {
        "appClassKey": "jinrong1", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1728556977557, // 创建时间
        "desc": "描述", // 小程序描述
        "detailDesc": "详情描述", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "2367587140282053", // 小程序logo id
        "miniAppId": "fc2398954709929221", // 小程序id
        "name": "名称", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 0, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 1, // 状态
        "updateTime": 1728556984213 // 更新时间
    }
}

# 4. 事件-删除小程序

# 4.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 4.2 请求结构

{
    "event": "EVENT_MINIAPP_DELETE",
    "params": {
        "appClassKey": "jinrong1", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1728556977557, // 创建时间
        "desc": "www", // 小程序描述
        "detailDesc": "wwww", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "2356288317085829", // 小程序logo id
        "miniAppId": "fc2398954709929221", // 小程序id
        "name": "wwww1", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 0, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 1, // 状态
        "updateTime": 1728557000325 // 更新时间
    }
}

# 5. 事件-编辑小程序

# 5.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 5.2 请求结构

{
    "event": "EVENT_MINIAPP_EDIT",
    "params": {
        "appClassKey": "jinrong1", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1728556977557, // 创建时间
        "desc": "www", // 小程序描述
        "detailDesc": "wwww", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "2356288317085829", // 小程序logo id
        "miniAppId": "fc2398954709929221", // 小程序id
        "name": "wwww1", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 0, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 1, // 状态
        "updateTime": 1728557000325 // 更新时间
    }
}

# 6. 事件-发布小程序

# 6.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 6.2 请求结构

{
    "event": "EVENT_MINIAPP_PUBLISH",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "autoPub": false, // 是否自动发布
        "createTime": 1726642520426, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppCompiledPackageId": "2367587980306374", // 小程序编译包ID
        "miniAppId": "fc2367587200788357", // 小程序id
        "miniAppVersionId": "2367588244187077", // 小程序版本ID
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "sequence": 23, // 序列号
        "status": 6, // 状态
        "updateTime": 1728557033406, // 更新时间
        "version": "1.0.19", // 版本号
        "versionDescription": "版本描述" // 版本描述
    }
}

# 6.3 参数说明

参数 类型 说明
autoPub Boolean 是否自动发布
miniAppCompiledPackageId String 小程序编译包ID
miniAppVersionId String 小程序版本ID
sequence Number 序列号
version String 版本号
versionDescription String 版本描述

# 7. 事件-取消发布小程序

# 7.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 7.2 请求结构

{
    "event": "EVENT_MINIAPP_UNPUBLISH",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "autoPub": true, // 是否自动发布
        "createTime": 1726642519646, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppCompiledPackageId": "2367588230064070", // 小程序编译包ID
        "miniAppId": "fc2367587200788357", // 小程序id
        "miniAppVersionId": "2367588231210949", // 小程序版本ID
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "sequence": 22, // 序列号
        "status": 7, // 状态
        "updateTime": 1728557044812, // 更新时间
        "version": "1.0.22", // 版本号
        "versionDescription": "1.0.22" // 版本描述
    }
}

# 8. 事件-回滚小程序

# 8.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 8.2 请求结构

{
    "event": "EVENT_MINIAPP_ROLLBACK",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "autoPub": true, // 是否自动发布
        "createTime": 1726642519646, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppCompiledPackageId": "2367588230064070", // 小程序编译包ID
        "miniAppId": "fc2367587200788357", // 小程序id
        "miniAppVersionId": "2367588231210949", // 小程序版本ID
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "sequence": 22, // 序列号
        "status": 6, // 状态
        "updateTime": 1728557039793, // 更新时间
        "version": "1.0.22", // 版本号
        "versionDescription": "1.0.22" // 版本描述
    }
}

# 9. 事件-禁用小程序

# 9.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 9.2 请求结构

{
    "event": "EVENT_MINIAPP_DISABLE",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1726642456734, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "isForbidden": true, // 是否禁用
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppId": "fc2367587200788357", // 小程序id
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 1, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 7, // 状态
        "updateTime": 1728557044812 // 更新时间
    }
}

# 10. 事件-启用小程序

# 10.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 10.2 请求结构

{
    "event": "EVENT_MINIAPP_ENABLE",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1726642456734, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppId": "fc2367587200788357", // 小程序id
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 1, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 7, // 状态
        "updateTime": 1728557044812 // 更新时间
    }
}

# 11. 事件-启用小程序搜索

# 11.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 11.2 请求结构

{
    "event": "EVENT_MINIAPP_ENABLE_SEARCH",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1726642456734, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppId": "fc2367587200788357", // 小程序id
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 1, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": true, // 是否可搜索
        "status": 7, // 状态
        "updateTime": 1728557073899 // 更新时间
    }
}

# 12. 事件-禁用小程序搜索

# 12.1 请求头部

Header 描述
X-Fc-Webhook-Sign (可选) 如果在管理后台勾选了Token校验的话会传。具体作用查看下面签名章节

# 12.2 请求结构

{
    "event": "EVENT_MINIAPP_DISABLE_SEARCH",
    "params": {
        "appClassKey": "qita", // 小程序分类key
        "createBy": "2367587140282053", // 创建人
        "createTime": 1726642456734, // 创建时间
        "desc": "testAppletDescUpdate2", // 小程序描述
        "detailDesc": "testAppletDetailDescUpdate2", // 小程序详情描述
        "isForbidden": false, // 是否禁用
        "logo": "https://www-cdn.finclip.com/images/ic-default.png", // 小程序logo
        "miniAppId": "fc2367587200788357", // 小程序id
        "name": "autobplgnf2", // 小程序名称
        "organId": "2367587140839109", // 组织id
        "privacySettingType": 1, // 隐私设置类型
        "projectType": 1, // 项目类型
        "searchable": false, // 是否可搜索
        "status": 7, // 状态
        "updateTime": 1728557070073 // 更新时间
    }
}

# 签名

为了使得您的服务 (webhook事件的接收方) 有途径校验请求来自可信任的调用方,提供了签名的方式。为此您需要:

  1. 在配置Webhook的时候,勾选生成Token
  2. 将生成的Token,保存起来。注意:此Token只会展示一次,妥善保存,遗失的话只能重置
  3. Webhook接收方中校验:Token的值+请求的入参body 计算出来的值需要等于 header中的 X-Fc-Webhook-Sign
    • 配置Webhook的时候,如果没有勾选生成Token, 不会传X-Fc-Webhook-Sign的header

# 签名方式说明

X-Fc-Webhook-Sign 的签名使用了请求body的 HMAC 十六进制摘要,并使用 SHA-256 哈希函数生成,并将 作为webhookTokenHMAC key

验证 webhook 有效负载时需要牢记以下几件重要的事情:

  • FinClip 使用 HMAC 十六进制摘要来计算哈希值。
  • 哈希签名始终以 开头sha256=
  • 哈希签名是使用您的 webhook 的 webhookTokenwebhook请求体内容 生成的。
  • 如果您的语言和服务器实现指定了字符编码,请确保将负载处理为 UTF-8。Webhook 负载可以包含 Unicode 字符。

# 签名示例

# Golang

您可以定义以下VerifySignature函数并在收到 webhook 请求时调用它:

// 参数说明
// payload: webhook请求的request body
// webHookToken: 生成的webhookToken
// signature: webhook请求中header中的 X-Fc-Webhook-Sign
func VerifySignature(payload []byte, webHookToken string, signature string) bool {
	expectedSignature := computeWebHookSign(payload, webHookToken)
	return hmac.Equal([]byte(expectedSignature), []byte(signature))
}

func computeWebHookSign(payload []byte, webhookToken string) string {
	mac := hmac.New(sha256.New, []byte(webhookToken))
	mac.Write(payload)
	return "sha256=" + hex.EncodeToString(mac.Sum(nil))
}

# Python

您可以定义以下verify_signature函数并在收到 webhook 请求时调用它:

import hashlib
import hmac
def verify_signature(payload_body, secret_token, signature_header):
    """Verify that the payload was sent from GitHub by validating SHA256.

    Raise and return 403 if not authorized.

    Args:
        payload_body: original request body to verify (request.body())
        secret_token: GitHub app webhook token (WEBHOOK_SECRET)
        signature_header: header received from GitHub (x-hub-signature-256)
    """
    if not signature_header:
        raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!")
    hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
    expected_signature = "sha256=" + hash_object.hexdigest()
    if not hmac.compare_digest(expected_signature, signature_header):
        raise HTTPException(status_code=403, detail="Request signatures didn't match!")

# JavaScript

let encoder = new TextEncoder();

async function verifySignature(secret, header, payload) {
    let parts = header.split("=");
    let sigHex = parts[1];

    let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } };

    let keyBytes = encoder.encode(secret);
    let extractable = false;
    let key = await crypto.subtle.importKey(
        "raw",
        keyBytes,
        algorithm,
        extractable,
        [ "sign", "verify" ],
    );

    let sigBytes = hexToBytes(sigHex);
    let dataBytes = encoder.encode(payload);
    let equal = await crypto.subtle.verify(
        algorithm.name,
        key,
        sigBytes,
        dataBytes,
    );

    return equal;
}

function hexToBytes(hex) {
    let len = hex.length / 2;
    let bytes = new Uint8Array(len);

    let index = 0;
    for (let i = 0; i < hex.length; i += 2) {
        let c = hex.slice(i, i + 2);
        let b = parseInt(c, 16);
        bytes[index] = b;
        index += 1;
    }

    return bytes;
}
© FinClip with ❤ , Since 2017