$d + [ 'tax_mode' => 'none', 'tax_rate' => '10', 'tax_label' => 'Consumption tax', 'tax_reg_number' => '', ]); add_filter('gatecraft_setting_rules', function (array $r, string $tab) { if ($tab !== 'checkout') return $r; return $r + [ 'tax_mode' => ['enum', ['none', 'inclusive', 'exclusive']], 'tax_rate' => ['rate'], 'tax_label' => ['text', 60], 'tax_reg_number' => ['text', 40], ]; }, 10, 2); /** The rate as people write it: 10, 8, 7.7, 19. */ function tax_rate_text(float $rate): string { return rtrim(rtrim(number_format($rate, 2, '.', ''), '0'), '.') . '%'; } /** Tax for a booking total (after discounts). Returns mode, rate, label, amount (minor units) and the total to charge. */ function tax_compute(int $total): array { $mode = setting('tax_mode'); $rate = (float)setting('tax_rate'); $label = setting('tax_label') ?: 'Tax'; if (!in_array($mode, ['inclusive', 'exclusive'], true) || $rate <= 0 || $total <= 0) { return ['mode' => 'none', 'rate' => null, 'label' => null, 'amount' => 0, 'total' => $total]; } // Integer arithmetic on hundredths of a percent, so 7.7% and 19% round the same way every time (half up). $bp = (int)round($rate * 100); if ($mode === 'exclusive') { $tax = intdiv($total * $bp * 2 + 10000, 20000); return ['mode' => $mode, 'rate' => $rate, 'label' => $label, 'amount' => $tax, 'total' => $total + $tax]; } $tax = intdiv($total * $bp * 2 + (10000 + $bp), 2 * (10000 + $bp)); return ['mode' => $mode, 'rate' => $rate, 'label' => $label, 'amount' => $tax, 'total' => $total]; } /** The line shown under the subtotal: "Consumption tax 10% (included)" or "VAT 20%". */ function tax_line_label(?string $mode, $rate, ?string $label): string { if (!$mode || $mode === 'none' || $rate === null) return ''; $t = ($label ?: 'Tax') . ' ' . tax_rate_text((float)$rate); return $mode === 'inclusive' ? $t . ' (included)' : $t; } function order_tax_label(array $o): string { return tax_line_label($o['tax_mode'] ?? null, $o['tax_rate'] ?? null, $o['tax_label'] ?? null); }