80 lines
3.1 KiB
PHP
80 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Billing;
|
|
|
|
use App\Support\CompanyProfile;
|
|
|
|
/**
|
|
* The one-off charge for setting a customer up — and the one place it is turned
|
|
* into money.
|
|
*
|
|
* The fee had been advertised for months and charged to nobody. It was on the
|
|
* public price sheet ("Einrichtung kostet einmalig 118,80 €"), on the booking page
|
|
* ("zzgl. Einrichtung einmalig"), and the operator set the figure in the console —
|
|
* and CompanyProfile::setupFeeCents() had no reader outside those two sentences
|
|
* and the form that wrote it. Nothing put it on a checkout, on an invoice item or
|
|
* on any bill at all.
|
|
*
|
|
* It is charged now. The two sentences and the money read the same figure through
|
|
* this class, so a change of rate or of fee cannot move one without moving the
|
|
* others.
|
|
*
|
|
* **The figure is GROSS, like every other price this platform charges.** The
|
|
* console asks the operator for the net amount, because that is the commercial
|
|
* number and the field says so; what a customer pays is that with the domestic
|
|
* rate on it, formed by TaxTreatment::chargedCents(), which is the single place a
|
|
* gross is formed. That is also why the fee is the same for a consumer in Vienna
|
|
* and for a reverse-charge business in Rotterdam: the price on the page is the
|
|
* price at the till, for everybody, and the invoice is where the split differs.
|
|
*
|
|
* Zero means there is no such fee. Every reader has to treat that as "no line at
|
|
* all" rather than "a line of nought" — a checkout that itemises nothing owed is
|
|
* worse than one that says nothing.
|
|
*/
|
|
final class SetupFee
|
|
{
|
|
/** The net figure an operator typed, in cents. Zero when there is no fee. */
|
|
public static function netCents(): int
|
|
{
|
|
return CompanyProfile::setupFeeCents();
|
|
}
|
|
|
|
/** What a customer is actually charged for it, tax included. */
|
|
public static function chargedCents(): int
|
|
{
|
|
$net = self::netCents();
|
|
|
|
return $net === 0 ? 0 : TaxTreatment::chargedCents($net);
|
|
}
|
|
|
|
/**
|
|
* The fee as the extra, non-recurring line of a checkout session — or null
|
|
* when the operator has set none.
|
|
*
|
|
* `$currency` is the recurring Price's own, not the catalogue default: Stripe
|
|
* requires every line of one session to share a currency, and reading two
|
|
* sources for it is how a checkout ends up refused for a mismatch nobody can
|
|
* see from either page.
|
|
*
|
|
* @return array{amount_cents: int, label: string, currency: string}|null
|
|
*/
|
|
public static function checkoutLine(string $currency): ?array
|
|
{
|
|
$charged = self::chargedCents();
|
|
|
|
if ($charged === 0) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'amount_cents' => $charged,
|
|
// What the customer reads on Stripe's page and on Stripe's receipt.
|
|
// The same words the price sheet uses, because meeting a different
|
|
// name for the same charge at the till is exactly the moment somebody
|
|
// abandons a checkout.
|
|
'label' => __('checkout.setup_fee_line'),
|
|
'currency' => strtoupper($currency),
|
|
];
|
|
}
|
|
}
|