向 hosted payment page 传值时如何将 string 转为 decimal
结论
URL 查询参数在传输时都是字符串,不能通过 URL 传递真正的 decimal 类型。支付平台提示“expects a decimal value”,通常是指 payment_amount 必须采用合法的金额格式,例如 125.50,不能填写字面量 payment_amount。
WordPress 后端应根据订单数据重新计算并格式化金额,再通过支付平台规定的 POST 表单或重定向接口提交。不要直接采用浏览器传来的订单金额。
当前 URL 的问题
以下参数传递的都是固定文本:
payment_amount=payment_amount
customer_surname=Name
customer_email=Email
因此,支付平台实际收到的是:
payment_amount
Name
Email
这些不是表单中的真实值。即使把真实金额写入查询参数,它在网络传输时仍是字符串。支付平台关注的是字符串内容是否符合要求的金额格式,例如:
payment_amount=125.50
平台也可能要求同时提交货币代码、签名和订单号,或者要求使用最小货币单位表示金额。具体字段和格式应以该 HPP 的接口文档为准。
在 WordPress 后端处理金额
如果支付平台要求金额保留两位小数,可以先验证输入,再统一格式化:
<?php
$rawAmount = isset($_POST['order_value'])
? wp_unslash($_POST['order_value'])
: '';
$normalizedAmount = str_replace([',', ' '], ['', ''], trim($rawAmount));
if (!preg_match('/^\d+(?:\.\d{1,2})?$/', $normalizedAmount)) {
wp_die('Invalid order amount.');
}
$paymentAmount = number_format((float) $normalizedAmount, 2, '.', '');
// 例如:125.50
这段代码可以处理 125、125.5 和 125.50 等输入。如果输入可能带有货币符号,或者使用逗号作为小数点,就不能简单地删除或替换字符。应先根据网站支持的地区格式制定明确的解析规则,否则很容易混淆 1,234.56 和 1.234,56。
正式支付时,更不能直接采用用户提交的 order_value。服务器应根据商品编号、数量、折扣和运费重新计算金额:
<?php
$productId = absint($_POST['product_id'] ?? 0);
$quantity = max(1, absint($_POST['quantity'] ?? 1));
// 单价应来自数据库或可信配置,而不是浏览器提交的数据。
$unitPriceMinor = 2599; // 25.99,以最小货币单位表示
$totalMinor = $unitPriceMinor * $quantity;
$paymentAmount = number_format($totalMinor / 100, 2, '.', '');
用整数保存最小货币单位,可以避免浮点数运算造成的金额精度问题。
优先使用 POST 表单提交到 HPP
如果支付平台支持浏览器表单提交,可以生成一个隐藏表单:
<?php
$hppUrl = 'https://payment.example.com/payment';
$fields = [
'payment_amount' => $paymentAmount,
'customer_surname' => sanitize_text_field($_POST['surname'] ?? ''),
'customer_email' => sanitize_email($_POST['email'] ?? ''),
'customer_title' => sanitize_text_field($_POST['title'] ?? ''),
'customer_address_line1' => sanitize_text_field($_POST['address'] ?? ''),
'customer_address_town' => sanitize_text_field($_POST['town'] ?? ''),
'customer_address_postcode' => sanitize_text_field($_POST['postcode'] ?? ''),
'customer_address_country' => sanitize_text_field($_POST['country'] ?? ''),
];
?>
<form id="hpp-form"
method="post"
action="<?php echo esc_url($hppUrl); ?>">
<?php foreach ($fields as $name => $value): ?>
<input
type="hidden"
name="<?php echo esc_attr($name); ?>"
value="<?php echo esc_attr($value); ?>"
>
<?php endforeach; ?>
</form>
<script>
document.getElementById('hpp-form').submit();
</script>
使用 POST 后,客户地址和邮箱不会直接出现在浏览器地址栏、历史记录和常见的访问日志中。但 POST 并不意味着数据一定可信。支付平台需要的签名、令牌或校验字段,仍应由服务器生成。
如果平台只允许 URL 重定向
使用 http_build_query() 构建查询字符串,可以避免手工拼接引起的编码错误:
<?php
$params = [
'payment_amount' => $paymentAmount,
'customer_surname' => sanitize_text_field($_POST['surname'] ?? ''),
'customer_email' => sanitize_email($_POST['email'] ?? ''),
'customer_title' => sanitize_text_field($_POST['title'] ?? ''),
'customer_address_line1' => sanitize_text_field($_POST['address'] ?? ''),
'customer_address_town' => sanitize_text_field($_POST['town'] ?? ''),
'customer_address_postcode' => sanitize_text_field($_POST['postcode'] ?? ''),
'customer_address_country' => sanitize_text_field($_POST['country'] ?? ''),
];
$url = 'https://payment.example.com/payment?'
. http_build_query($params, '', '&', PHP_QUERY_RFC3986);
wp_safe_redirect($url);
exit;
如果 HPP 使用外部域名,WordPress 的 wp_safe_redirect() 可能会因为允许域名配置而拒绝重定向。此时应按照 WordPress 的安全要求配置允许的主机;如果目标 URL 固定且可信,也可以采用支付平台建议的跳转方式。
注意事项
- 服务器必须重新计算金额,不能信任隐藏字段、JavaScript 或 URL 中的金额。
- 使用 HTTPS,并按照 HPP 文档生成签名、摘要或会话令牌。
- 不要将银行卡信息提交到 WordPress。持卡人应直接在支付平台托管的页面填写。
- 不要用
floatval()转换后直接进行多步金额计算。金额计算应优先使用最小货币单位整数,提交时再转换成平台要求的格式。 number_format($amount, 2, '.', '')只能统一输出格式,不能判断金额在业务上是否正确。- 有些平台用
12550表示125.50,有些要求提交 JSON 数字125.50,还有些要求提交字符串"125.50"。最终应采用哪种格式,必须以该支付平台的接口文档为准。
备注:内容仅供参考。
