适用unicloud的抖音通用交易支付签名和验签nodejs代码

2024-2-1 Frank NodeJS

  1. 签名:
// https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/api/industry/general_trade/create_order/requestOrder#87daf5bf
const createConfig = require('uni-config-center')
const shareConfig = createConfig({ // 获取配置实例
    pluginId: 'mymp-pay' // common/uni-config-center下的插件配置目录名
})
const payConfig = shareConfig.requireFile('config.js');
const getNotifyUrl = function (cloudInfo, payid, notify) {
    let notifyUrl = '';
    if (cloudInfo.provider == 'tencent') {
        notifyUrl = `https://${cloudInfo.spaceId}.service.tcloudbase.com/tt-pay/${notify}/${payid}`;
    } else if (cloudInfo.provider == 'aliyun') {
        notifyUrl = `https://fc-${cloudInfo.spaceId}.next.bspapp.com/tt-pay/${notify}/${payid}`;
    }
    return notifyUrl;
};
function getSignature(privateKeyStr, method, uri, timestamp, nonce, data) {
    const sign = crypto.createSign('SHA256');
    const rawStr = method + "\n" +
        uri + "\n" +
        timestamp + "\n" +
        nonce + "\n" +
        data + "\n";
    sign.update(rawStr);
    sign.end();
    const signature = sign.sign(privateKeyStr, 'base64');
    return signature;
}

function _getByteAuthorization(privateKeyStr, data, appId, nonceStr, timestamp, keyVersion) {
    try {
        const signature = getSignature(privateKeyStr, "POST", "/requestOrder", timestamp, nonceStr, data);
        const byteAuthorization = `SHA256-RSA2048 appid=${appId},nonce_str=${nonceStr},timestamp=${timestamp},key_version=${keyVersion},signature=${signature}`;
        return byteAuthorization;
    } catch (ex) {
        console.error(ex);
        return "";
    }
}
const getOrderData = function (out_order_no, skuId, title, price, image) {
    const cloudInfo = uniCloud.getCloudInfos()[0];
    const data = {
        skuList: [{
            skuId: skuId,
            title: title,
            price: price,
            quantity: 1,
            imageList: [image],
            type: 401, // 内容消费类商品
            tagGroupId: "tag_group_7272625659888041996", // 内容消费: 虚拟商品不支持退款
            entrySchema: {
                path: 'pages/index/index'
            }
        }],
        outOrderNo: out_order_no,
        totalAmount: price,
        payExpireSeconds: 0, // 支付超时时间,单位秒,例如 300 表示 300 秒后过期;不传或传 0 会使用默认值 300,不能超过48小时。
        payNotifyUrl: getNotifyUrl(cloudInfo, '', 'tt_callback'),
        orderEntrySchema: {
            path: 'pages/index/index'
        }
    }

    return JSON.stringify(data);
}    

/**
 * 签名
 * @param {string} data 
 * @returns 
 */
const getByteAuthorization = function (data) {    
    const privateKeyStr = payConfig.toutiao['mp-toutiao'].privateKey; // 从文件中读取私钥
    const timestamp = Math.floor(Date.now() / 1000);
    const appId = ttConfig.app_id;
    const nonceStr = uuidv4();
    const keyVersion = "1";

    const byteAuthorization = _getByteAuthorization(privateKeyStr, data, appId, nonceStr, timestamp, keyVersion);

    return byteAuthorization;
}
  1. 验签:
// https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/signature-algorithm#%E7%AD%BE%E5%90%8D%E9%AA%8C%E8%AF%81
function verify(httpBody, publicKeyPem, signStr, timestamp, nonce) {
    const message = `${timestamp}\n${nonce}\n${httpBody}\n`;    

    // 使用RSA和SHA256进行验签
    const verifier = crypto.createVerify('RSA-SHA256');
    verifier.update(message);

    // 验证签名
    return verifier.verify(publicKeyPem, Buffer.from(signStr, 'base64'));
}

const httpInfo = this.getHttpInfo();
// uniCloud.logger.log(`httpInfo:`, httpInfo);
const bodyStr = httpInfo.body;
const nonce = httpInfo.headers['byte-nonce-str'];
const timestamp = httpInfo.headers['byte-timestamp'];
const signature = httpInfo.headers['byte-signature'];

const publicKey = payConfig.toutiao['mp-toutiao'].platformPublicKey;
if (!verify(bodyStr, publicKey, signature, timestamp, nonce)) {
    uniCloud.logger.log(`verify error`);
    return {
        mpserverlessComposedResponse: true,
        statusCode: 200,
        headers: {
            'content-type': 'application/json'
        },
        body: {
            "err_no": 1, //非0
            "err_tips": "verify error"  //非success
        }
    };
}

标签: 抖音支付

发表评论 登录

Top