PAYATHON 2026

向 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"。最终应采用哪种格式,必须以该支付平台的接口文档为准。

备注:内容仅供参考。