PAYATHON 2026

PrestaShop 1.6.1.x payment module 未发送含运费总价

支付阿杰

结论

不要在 foreach ($products as $product) 循环中把运费直接加到每个商品的 $CartItem['Amount']。否则,每件商品都会重复计算一次运费;当 Quantity 大于 1 时,API 还可能再次放大这部分金额。

可以通过以下代码取得含税运费:

$cart->getOrderTotal(true, Cart::ONLY_SHIPPING)

然后将运费作为一条独立明细发送给 API,并将 Quantity 设为 1。如果 API 只接收订单总金额,直接使用 $cart->getOrderTotal(true, Cart::BOTH)。

将运费作为独立明细

商品价格使用 price_wt,也就是含税价格,因此运费也要按含税金额计算。以下代码保留了原有的货币转换方式:

/** CART ITEMS */
$products = $cart->getProducts();
$CartItems = [];

$api_currency_id = (int) Currency::getIdByIsoCode($currency_iso);

foreach ($products as $product) {
    if ($this->context->currency->id != $api_currency_id) {
        $price = Tools::convertPrice(
            (float) $product['price_wt'],
            $this->context->currency,
            false
        );

        $price = Tools::convertPrice(
            (float) $price,
            $api_currency_id
        );
    } else {
        $price = (float) $product['price_wt'];
    }

    $CartItem = [];
    $CartItem['Amount'] = Tools::ps_round($price, 2);
    $CartItem['Currency'] = $currency_iso;
    $CartItem['Name'] = $product['name'];
    $CartItem['Description'] = strip_tags($product['description_short']);
    $CartItem['Quantity'] = (int) $product['cart_quantity'];
    $CartItem['IsTaxFree'] =
        (float) $product['price'] === (float) $product['price_wt'];

    $CartItems[] = $CartItem;
}

/** SHIPPING */
$shipping = (float) $cart->getOrderTotal(
    true,
    Cart::ONLY_SHIPPING
);

if ($this->context->currency->id != $api_currency_id) {
    $shipping = Tools::convertPrice(
        $shipping,
        $this->context->currency,
        false
    );

    $shipping = Tools::convertPrice(
        $shipping,
        $api_currency_id
    );
}

if ($shipping > 0) {
    $ShippingItem = [];
    $ShippingItem['Amount'] = Tools::ps_round($shipping, 2);
    $ShippingItem['Currency'] = $currency_iso;
    $ShippingItem['Name'] = 'Shipping';
    $ShippingItem['Description'] = 'Shipping cost';
    $ShippingItem['Quantity'] = 1;
    $ShippingItem['IsTaxFree'] = false;

    $CartItems[] = $ShippingItem;
}

发送给外部 API 的明细总额为:

商品单价 × 商品数量 + 运费

这样,运费只计算一次。

如果 API 只接收订单总金额

如果外部 API 不需要逐项商品明细,只接收一个支付金额,可以直接获取包含运费的购物车总价:

$total = (float) $cart->getOrderTotal(
    true,
    Cart::BOTH
);

if ($this->context->currency->id != $api_currency_id) {
    $total = Tools::convertPrice(
        $total,
        $this->context->currency,
        false
    );

    $total = Tools::convertPrice(
        $total,
        $api_currency_id
    );
}

$amount = Tools::ps_round($total, 2);

订单中还可能有优惠、购物车规则等价格调整,因此使用 Cart::BOTH 通常比手工累加商品金额和运费更稳妥。

注意事项

  • getOrderTotal(true, ...) 中的 true 表示含税,与 price_wt 的价格口径一致。如果 API 要求未税金额,应统一改用 false 和商品未税价格。
  • 执行这段代码前,购物车必须已经确定配送地址和承运商,否则运费可能为 0,之后也可能发生变化。
  • 不要把完整运费加入每件商品的单价。API 通常会按 Amount × Quantity 计算明细金额,这可能导致重复收费。
  • 原代码中 IsTaxFree 的判断方向可能相反。price 与 price_wt 不相等,通常表示商品金额中含有税额,此时不应标记为免税。
  • 正式提交支付前,应检查所有明细金额之和是否与 getOrderTotal(true, Cart::BOTH) 转换后的金额一致,并特别留意折扣和舍入差异。

备注:内容仅供参考。