PAYATHON 2026

PHP 中为 Payment API 动态构建多维 lines 数组

支付小周

结论

不要拼接 PHP 数组语法字符串,也不要使用 eval()。在循环中直接创建关联数组,再将每个商品数组追加到 $lines。调用 API 时传入:

"lines" => $lines

不要写成:

"lines" => [$echoOrders]

第二种写法传入的是一个只包含字符串的数组,并不是 Mollie 要求的商品行二维数组。

正确构建 lines

如果每件商品的数量都是 1,VAT 为 0%,可以这样写:

<?php

$lines = [];

foreach ($_SESSION['cart_products'] as $cartItem) {
    $unitPrice = number_format(
        (float) $cartItem['cat_item_prijs'],
        2,
        '.',
        ''
    );

    $lines[] = [
        "sku" => (string) $cartItem['cat_item_code'],
        "name" => (string) $cartItem['cat_item_titel_' . $lang],
        "quantity" => 1,
        "vatRate" => "0.00",
        "unitPrice" => [
            "currency" => "EUR",
            "value" => $unitPrice,
        ],
        "totalAmount" => [
            "currency" => "EUR",
            "value" => $unitPrice,
        ],
        "vatAmount" => [
            "currency" => "EUR",
            "value" => "0.00",
        ],
    ];
}

生成的数据结构相当于:

[
    [
        "sku" => "ITEM-001",
        "name" => "Product one",
        "quantity" => 1,
        // ...
    ],
    [
        "sku" => "ITEM-002",
        "name" => "Product two",
        "quantity" => 1,
        // ...
    ],
]

然后将 $lines 直接放进请求参数:

$order = $mollie->orders->create([
    "amount" => [
        "value" => $orderTotal,
        "currency" => "EUR",
    ],
    "billingAddress" => [
        "streetAndNumber" => $address,
        "postalCode" => $postalcode,
        "city" => $city,
        "country" => $country,
        "givenName" => $firstname,
        "familyName" => $familyName,
        "email" => $email,
    ],
    "shippingAddress" => [
        "streetAndNumber" => $addressa,
        "postalCode" => $postalcodea,
        "city" => $citya,
        "country" => $countrya,
        "givenName" => $firstnamea,
        "familyName" => $familyNamea,
        "email" => $email,
    ],
    "metadata" => [
        "order_id" => $orderId,
    ],
    "locale" => "nl_BE",
    "orderNumber" => strval($orderId),
    "redirectUrl" => "{$protocol}://{$hostname}{$path}/thankyou.php?order_id={$orderId}",
    "webhookUrl" => "https://www.cluster-park.com/dev/includes/webhook.php",
    "lines" => $lines,
]);

同时计算数量和订单总额

如果购物车中保存了商品数量,就不应把 quantity 和订单金额写死。金额最好按整数分计算,以免遇到浮点数精度问题:

<?php

$lines = [];
$orderTotalCents = 0;

$titleKey = 'cat_item_titel_' . $lang;

foreach ($_SESSION['cart_products'] as $cartItem) {
    $quantity = max(1, (int) ($cartItem['quantity'] ?? 1));

    // 假设 cat_item_prijs 是以欧元表示的十进制价格。
    $unitPriceCents = (int) round(
        (float) $cartItem['cat_item_prijs'] * 100
    );

    $lineTotalCents = $unitPriceCents * $quantity;
    $orderTotalCents += $lineTotalCents;

    $unitPrice = number_format($unitPriceCents / 100, 2, '.', '');
    $lineTotal = number_format($lineTotalCents / 100, 2, '.', '');

    $lines[] = [
        "sku" => (string) $cartItem['cat_item_code'],
        "name" => (string) $cartItem[$titleKey],
        "quantity" => $quantity,
        "vatRate" => "0.00",
        "unitPrice" => [
            "currency" => "EUR",
            "value" => $unitPrice,
        ],
        "totalAmount" => [
            "currency" => "EUR",
            "value" => $lineTotal,
        ],
        "vatAmount" => [
            "currency" => "EUR",
            "value" => "0.00",
        ],
    ];
}

$orderTotal = number_format($orderTotalCents / 100, 2, '.', '');

调用 API 时传入:

$order = $mollie->orders->create([
    "amount" => [
        "value" => $orderTotal,
        "currency" => "EUR",
    ],

    // 其他订单字段……

    "lines" => $lines,
]);

原来的写法为什么会失败

下面这段代码生成的是字符串:

$echoOrders = implode($orders);

即使字符串内容看起来符合 PHP 数组语法,它也仍然只是普通文本。例如:

$echoOrders = '["sku" => "ITEM-001"]';

再把它写成:

"lines" => [$echoOrders]

得到的实际数据结构是:

[
    '["sku" => "ITEM-001"]'
]

API 收到的第一条商品行是字符串,而不是包含 sku、name、quantity 等字段的对象,所以无法将其解析为订单行。

注意事项

  • 不要使用 eval() 执行拼接出来的数组字符串。这样做没有必要,还会带来代码注入风险。
  • amount.value、unitPrice.value、totalAmount.value 等金额通常要使用带两位小数的字符串,例如 "15.50",不能写成 "15,50"。
  • 订单的 amount.value 必须与所有订单行金额、折扣等数据一致,不能一直写死为 "15.50"。
  • 当 quantity 大于 1 时,totalAmount.value 通常不能仍然等于单价。计算时还要考虑折扣、VAT 和当前 Mollie API 的金额规则。
  • vatRate 和 vatAmount 必须与真实税务数据一致。只有确实适用零税率时才能使用 "0.00"。
  • 商品标题字段是动态生成的。使用前要确认 $lang 属于允许的语言值,并检查相应的键是否存在。
  • 不要让 $name 同时表示商品名称和客户姓氏。如果循环覆盖了 $name,billingAddress.familyName 可能会变成最后一件商品的名称。可以分别使用 $productName 和 $familyName。
  • 调用 API 前要处理空购物车,以及缺少价格、SKU 或标题的商品,避免发送不完整的订单数据。

备注:内容仅供参考。