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 证书验证。
备注:内容仅供参考。
