u/Appropriate-Toe-2993

We sell to Saudi merchants and ZATCA Phase 2 mandates UBL 2.1 XML, XAdES-BES digital signing, a PIH/ICV hash chain, and real-time clearance via their API. Building this in WooCommerce was surprisingly complex — especially the hash chain part where two simultaneous orders can corrupt the chain if you're not careful.

We ended up using a WordPress options-based spin-lock (50ms back-off) to prevent race conditions, and scheduling the ZATCA API call via WP-Cron with exponential retry (5min → 25min → 2h → 10h) so checkout stays fast.

Anyone else doing ZATCA compliance in WooCommerce? Curious how others handled the XAdES signing and hash chain — are you doing it sync or async?

reddit.com
u/Appropriate-Toe-2993 — 8 days ago

Since January 1, 2026, German law (BMF regulation) requires that invoices for mixed-rate products show each VAT component separately. A brunch ticket (food at 7% + drinks at 19%) can't be invoiced as a single line item anymore.

WooCommerce only allows one tax class per line item — so there's no native way to comply.

The solution I ended up with: store split rules on the product, inject calculated parts into order item meta at checkout, and hook into invoice plugins to render the breakdown on PDFs.

**The rounding problem nobody talks about:**

Splitting €39 at 80/20 is fine. But €37.50 at 73/27 across thousands of orders creates floating point drift. I used a "remainder on last" approach:

```php

reddit.com
u/Appropriate-Toe-2993 — 8 days ago

Title:

How do you handle products that legally require multiple VAT rates on a single invoice in WooCommerce?

Body:

Running into this with a client who sells brunch tickets in Germany. Since January 2026 the BMF regulation requires invoices to show food (7% VAT) and beverages (19% VAT) as separate line items — even if the customer buys one product.

WooCommerce natively only allows one tax class per line item, so there's no clean way to do this out of the box.

The approach I went with: store split rules on the product meta, then at checkout hook inject the calculated parts into order item meta. Customer still sees one product, but the order/invoice backend shows the split.

The tricky part was rounding — splitting €37 at 80/20 gives floating point drift across multiple orders. Solved it with "remainder on last" algorithm so parts always sum to the exact original total.

```php
function calculate_split_parts( float $total, array $rules ): array {
    $parts     = [];
    $allocated = 0.0;
    $last      = count( $rules ) - 1;

    foreach ( $rules as $i => $rule ) {
        if ( $i === $last ) {
            $amount = round( $total - $allocated, 2 );
        } else {
            $amount     = round( $total * ( $rule['percentage'] / 100 ), 2 );
            $allocated += $amount;
        }
        $parts[] = array_merge( $rule, [ 'amount' => $amount ] );
    }
    return $parts;
}

Also had to make it HPOS compatible — wc_get_orders() only, no direct SQL on order tables.

Anyone else dealing with mixed VAT rate products? Curious how others have approached it, especially for Austrian (UStG) or French (FEC export) requirements.

reddit.com
u/Appropriate-Toe-2993 — 8 days ago