PAYATHON 2026

CodeIgniter PHP 如何生成随机 merchantTransactionId

支付小周

结论

问题出在函数的调用方式。$generateRandomString(24) 调用的是变量 $generateRandomString 中保存的函数,而不是名为 generateRandomString() 的普通函数。

先生成交易 ID,再赋值给 merchantTransactionId:

$merchantTransactionId = 'amarechat_' . generateRandomString(24);

之后使用 http_build_query() 构造请求参数,避免手工拼接带来的编码问题。

完整实现

以下代码生成的交易 ID 由 amarechat_ 前缀和 24 个随机字母或数字组成:

function generateRandomString(int $length = 24): string
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $result = '';
    $maxIndex = strlen($characters) - 1;

    for ($i = 0; $i < $length; $i++) {
        $result .= $characters[random_int(0, $maxIndex)];
    }

    return $result;
}

function paymentGateway($amount)
{
    $url = 'https://oppwa.com/v1/checkouts';

    // 例如:amarechat_f8Ea21mQ0ZpL6xKcR4sW9nBt
    $merchantTransactionId = 'amarechat_' . generateRandomString(24);

    $data = http_build_query([
        'entityId'              => '8ac7a4c96afd0574016afd649e36013f',
        'amount'                => $amount,
        'currency'              => 'ZAR',
        'paymentType'           => 'DB',
        'merchantTransactionId' => $merchantTransactionId,
    ]);

    $ch = curl_init();

    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ******',
            'Content-Type: application/x-www-form-urlencoded',
        ],
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $data,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_RETURNTRANSFER => true,
    ]);

    $responseData = curl_exec($ch);

    if ($responseData === false) {
        $error = curl_error($ch);
        curl_close($ch);

        return $error;
    }

    curl_close($ch);

    return $responseData;
}

random_int() 可以生成不可预测的随机内容,比 rand() 或 mt_rand() 更适合用于支付交易标识。

如果总长度必须是 24 个字符

上述代码生成的交易 ID 总长度为 34 个字符:

10 个前缀字符 + 24 个随机字符 = 34 个字符

amarechat_ 本身有 10 个字符。如果支付接口要求整个 merchantTransactionId 恰好为 24 个字符,随机部分就只能有 14 个字符:

$prefix = 'amarechat_';
$totalLength = 24;
$randomLength = $totalLength - strlen($prefix);

$merchantTransactionId = $prefix . generateRandomString($randomLength);

也可以把这段逻辑封装成函数:

function generateMerchantTransactionId(int $totalLength = 24): string
{
    $prefix = 'amarechat_';
    $randomLength = $totalLength - strlen($prefix);

    if ($randomLength < 1) {
        throw new InvalidArgumentException(
            'Transaction ID length must be greater than the prefix length.'
        );
    }

    return $prefix . generateRandomString($randomLength);
}

调用方式如下:

$merchantTransactionId = generateMerchantTransactionId(24);

原代码为什么没有生效

下面两种写法都有问题:

$generateRandomString(24);
$generateRandomString;

第一种写法要求 $generateRandomString 是一个可调用变量,例如闭包。第二种写法只是读取变量的值。如果这个变量没有定义,PHP 会产生未定义变量警告,接口最终可能收到空值或不完整的参数。

调用普通函数时不需要 $:

generateRandomString(24);

只有使用匿名函数时,才需要把函数保存在变量中:

$generateRandomString = function (int $length): string {
    // 生成字符串
};

$id = $generateRandomString(24);

当前场景直接定义并调用普通函数更清楚。

在 CodeIgniter 中使用

随机 ID 应在服务端创建支付请求时生成,不要依赖前端表单。前端只需提交金额等业务数据,后端负责生成并保存 merchantTransactionId,然后调用支付接口。

发送请求前,应将同一个 ID 保存下来:

$merchantTransactionId = generateMerchantTransactionId(24);

// 将 $merchantTransactionId 与当前订单写入数据库

$data = http_build_query([
    'entityId'              => '******',
    'amount'                => $amount,
    'currency'              => 'ZAR',
    'paymentType'           => 'DB',
    'merchantTransactionId' => $merchantTransactionId,
]);

数据库交易记录中的 ID 必须与发送给支付处理商的值完全一致,否则后续处理回调、查询或对账时,无法可靠地关联订单。

注意事项

  • 确认支付接口对 merchantTransactionId 的最大长度和允许字符范围。如果接口不接受下划线或大小写字母,需要调整前缀或字符集。
  • 不要用支付金额充当交易 ID,因为不同订单可能具有相同金额。
  • 最好为数据库中的 merchantTransactionId 添加唯一索引,防止极低概率的重复。
  • 重试同一次支付请求时,通常应复用原交易 ID。只有创建全新交易时才生成新 ID,具体做法仍需符合支付处理商当前的重试和幂等规则。
  • Authorization 请求头中的空格应保留,写成 Authorization: Bearer ******。
  • 不要关闭生产环境中的 SSL 证书验证。

备注:内容仅供参考。