接收回调通知

回调通知支付消息通知

介绍了接收 Spell 回调通知的相关信息,以及如何正确处理通知的方法。

Spell 发出的所有回调通知均通过 POST 请求发送到您指定的回调地址。请求的内容为:

  • 请求头
    • Content-Typeapplication/json
    • SPELL-Callback-Signature:请求签名,您可以用于验证请求的真伪。
  • 请求体
    • callback:回调通知 ID。
    • event:事件ID。
    • order:订单ID。
    • timestamp:请求时间戳。
    • user:用户ID。

验证签名有效性

为了确保请求的真实性,防止被篡改或伪造,Spell 会对信息进行签名,以便您进行验证。您需要使用对应的已注册事件的回调密钥(在注册时记录下来的,不是您的账户API Secret)对请求体进行签名,并与请求头中的 SPELL-Callback-Signature 进行对比。以下是完整的验证步骤:

1. 序列化数据

首先,你需要将请求体中的所有字段序列化成一个特定的字符串。这个过程包括:

  1. 按字段名排序:请求体中的所有字段按照字段名进行字母排序。
  2. 格式化:将每个字段转换为 key=value 格式的字符串。
  3. 拼接:使用 & 符号将所有格式化后的字符串连接起来。

您可以使用下面的 serializeData() 函数来完成这一步:

/**
* 序列化数据对象,用于生成签名字符串
* @param data - 需要序列化的数据对象
* @returns 序列化后的字符串,格式为 key1=value1&key2=value2...
*/
function serializeData(data: Record<string, any>): string {
    return Object.keys(data)
        .sort()
        .map(key => {
            const value = data[key];
            const strValue = typeof value === 'object'
                ? JSON.stringify(value)
                : String(value);
            return `${key}=${strValue}`;
        })
        .join('&');
}

2. 计算签名

使用 crypto 模块,创建一个 HMAC 对象,并使用 SHA-256 算法和你的密钥对序列化后的数据进行签名计算。

以下为 Node.js 示例代码:

import * as crypto from 'crypto';

/**
 * 计算签名
 * @param serializedData - 经过序列化处理的字符串
 * @param secretKey - 你的回调通知密钥
 * @returns 十六进制的签名字符串
 */
function calculateSignature(serializedData: string, secretKey: string): string {
    const hmac = crypto.createHmac('sha256', secretKey);
    hmac.update(serializedData);
    return hmac.digest('hex');
}

3. 对比签名

最后,将你计算出的签名与回调请求头中的 SPELL-Callback-Signature 字段对应的值进行对比。如果两者完全一致,则表明请求是真实有效的。

以下是完整的示例代码:

// 从请求头中获取签名
const cbSignature = request.headers['SPELL-Callback-Signature']; 

// 假设这是你收到的请求体
const requestBody = {
    callback: 'callback_id',
    event: 'event_id',
    order: 'order_id',
    timestamp: 1700000000000,
    user: 'user_id',
};

// 将请求体序列化
const serializedData = serializeData(requestBody); 

// 计算签名
// CALLBACK_SECRET_KEY 是在回调注册时生成的
const mySignature = calculateSignature(serializedData, CALLBACK_SECRET_KEY); 

// 对比签名
if (mySignature === cbSignature) {
    console.log("签名验证成功,请求有效!");
    // 处理你的业务逻辑
} else {
    console.log("签名验证失败,请求可能被篡改或伪造,您应该重新创建一个回调接收地址!");
    // 忽略请求
}

处理响应

为了确保回调通知被成功接收,Spell 会在一定时间范围内对于未接收到成功指令的回调进行间隔重试。同样的,为了避免重复收到 Spell 的通知,您的服务器需要在收到通知并确认签名有效后返回一个 HTTP 成功响应:

  • 状态码:200
  • 响应头:Content-Type: text/plain
  • 响应内容:success

    响应内容为文本形式的全小写success,不包含任何其他字符