You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用NetSuite SuiteScript 2.0生成Cybersource HTTP签名遇认证失败

SuiteScript 2.0 生成Cybersource HTTP签名示例

以下是适配NetSuite SuiteScript 2.0的Cybersource退款API签名生成代码,解决认证失败的核心是严格遵循Cybersource的签名规范构造待签名字符串:

核心代码实现

/**
 * @NApiVersion 2.x
 * @NScriptType Restlet
 * @NModuleScope SameAccount
 */
define(['N/crypto', 'N/https', 'N/runtime'], function(crypto, https, runtime) {
    function post(context) {
        // 从脚本参数读取Cybersource配置(避免硬编码)
        const apiKeyId = runtime.getCurrentScript().getParameter({name: 'custscript_cybersource_api_key_id'});
        const secretKey = runtime.getCurrentScript().getParameter({name: 'custscript_cybersource_secret_key'});
        const merchantId = runtime.getCurrentScript().getParameter({name: 'custscript_cybersource_merchant_id'});
        const apiUrl = 'https://apitest.cybersource.com/pts/v2/refunds'; // 测试环境地址,生产环境替换

        // 构造退款请求体
        const requestBody = JSON.stringify({
            "clientReferenceInformation": {
                "code": "REFUND_ORDER_001"
            },
            "paymentInformation": {
                "id": "REPLACE_WITH_PAYMENT_ID" // 替换为实际支付ID
            },
            "orderInformation": {
                "amountDetails": {
                    "totalAmount": "10.00",
                    "currency": "USD"
                }
            }
        });

        // 生成签名必备参数
        const httpMethod = 'POST';
        const requestPath = '/pts/v2/refunds';
        const timestamp = Math.floor(Date.now() / 1000).toString(); // 秒级时间戳
        const nonce = crypto.generateRandomString({
            length: 32,
            charset: crypto.Charset.HEXADECIMAL
        });
        // 计算请求体SHA256摘要(Base64编码)
        const digest = crypto.createHash({
            algorithm: crypto.HashAlg.SHA256
        }).update({input: requestBody, inputEncoding: crypto.Encoding.UTF_8}).digest({outputEncoding: crypto.Encoding.BASE_64});

        // 构造待签名字符串(严格按指定顺序拼接,换行分隔)
        const signatureString = [
            httpMethod,
            requestPath,
            timestamp,
            nonce,
            digest
        ].join('\n');

        // 生成HMAC-SHA256签名
        const hmac = crypto.createHmac({
            algorithm: crypto.HashAlg.SHA256,
            key: secretKey
        });
        hmac.update({input: signatureString, inputEncoding: crypto.Encoding.UTF_8});
        const signature = hmac.digest({outputEncoding: crypto.Encoding.BASE_64});

        // 构造符合要求的请求头
        const headers = {
            'Content-Type': 'application/json',
            'v-c-merchant-id': merchantId,
            'Signature': `keyid="${apiKeyId}",algorithm="HmacSHA256",headers="host date request-target digest v-c-merchant-id",signature="${signature}"`,
            'Date': new Date().toUTCString(), // RFC 1123格式UTC时间
            'Host': 'apitest.cybersource.com', // 测试环境主机,生产环境替换
            'Digest': `SHA-256=${digest}`
        };

        // 发送退款请求
        const response = https.post({
            url: apiUrl,
            body: requestBody,
            headers: headers
        });

        return {
            statusCode: response.code,
            responseBody: response.body
        };
    }

    return {
        post: post
    };
});

避免认证失败的关键细节

  • 签名字符串顺序:必须严格按照请求方法、请求路径、时间戳、随机数、请求体摘要的顺序拼接,不能调整顺序
  • 时间同步:签名用的秒级时间戳要和Date头的UTC时间保持误差在5分钟内,否则会被判定为无效请求
  • 路径准确性:请求路径必须和API文档完全一致,不能多/少斜杠或额外参数
  • 摘要匹配:Digest头的内容要和签名字符串里的摘要完全一致,编码格式必须是Base64
  • 密钥安全:API密钥、商户ID必须通过脚本参数或自定义记录存储,禁止硬编码在代码中

内容的提问来源于stack exchange,提问作者Ba.Lal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:40:12