Phase8: Auto bill credit card
Some checks failed
Build and Push / build (push) Failing after 42s

This commit is contained in:
2026-05-27 22:06:32 +02:00
parent ad4f614130
commit ee6bb89fb6
20 changed files with 1857 additions and 122 deletions

View File

@@ -15,6 +15,14 @@ interface Props {
* is disabled by their billing mode.
*/
isPayByInvoice: boolean;
/**
* Personal-account flag from the session. Personal accounts are
* single-user B2C tenants and don't have the bank-transfer
* affordance — they pay by card or not at all. We hide the
* "Bank transfer is available on request" hint for these accounts
* to keep the messaging unambiguous.
*/
isPersonal: boolean;
}
const BRAND_LABELS: Record<string, string> = {
@@ -41,7 +49,11 @@ const BRAND_LABELS: Record<string, string> = {
* lands here and the new card info needs to load. We also strip
* the query param so a page reload doesn't re-trigger.
*/
export function SavedCardSection({ config, isPayByInvoice }: Props) {
export function SavedCardSection({
config,
isPayByInvoice,
isPersonal,
}: Props) {
const t = useTranslations("settingsBilling");
const router = useRouter();
const searchParams = useSearchParams();
@@ -125,6 +137,18 @@ export function SavedCardSection({ config, isPayByInvoice }: Props) {
<p className="text-sm text-text-secondary mb-4">
{t("savedCardEmptyBody")}
</p>
{/* Phase 9: prominent policy notice. Auto-pay is the
expected default — emphasise that failure to keep a
chargeable card on file may result in tenant suspension.
Sits above the CTA so it's seen before the click. */}
<div className="text-sm rounded-md border border-warning/40 bg-warning/10 text-warning px-4 py-3 mb-4">
<strong className="block mb-1">
{t("savedCardAutoPayRequiredHeading")}
</strong>
<span className="text-text-secondary">
{t("savedCardAutoPayRequiredBody")}
</span>
</div>
{error && (
<div className="text-sm text-error mb-3">{error}</div>
)}
@@ -135,15 +159,20 @@ export function SavedCardSection({ config, isPayByInvoice }: Props) {
>
{busy === "setup" ? t("savedCardRedirecting") : t("savedCardSetupBtn")}
</button>
<p className="text-xs text-text-muted mt-4">
{t("savedCardBankTransferHint")}{" "}
<a
href="/support"
className="text-accent hover:underline"
>
{t("savedCardBankTransferLink")}
</a>
</p>
{/* Bank-transfer hint shown only for company accounts.
Personal (B2C) accounts pay by card only — surfacing
the alternative would only confuse. */}
{!isPersonal && (
<p className="text-xs text-text-muted mt-4">
{t("savedCardBankTransferHint")}{" "}
<a
href="/support"
className="text-accent hover:underline"
>
{t("savedCardBankTransferLink")}
</a>
</p>
)}
</div>
</Card>
);
@@ -211,6 +240,16 @@ export function SavedCardSection({ config, isPayByInvoice }: Props) {
</div>
)}
{/* If the card is on file but the customer has actively
disabled auto-pay, surface the suspension-risk reminder.
Not shown when admin has flipped them to pay-by-invoice —
that's a different deal and the note above explains it. */}
{!isPayByInvoice && !autoChargeOn && (
<div className="text-xs rounded-md border border-warning/40 bg-warning/10 text-warning px-3 py-2 mb-3">
{t("savedCardAutoPayDisabledNote")}
</div>
)}
{error && <div className="text-sm text-error mb-3">{error}</div>}
<div className="flex gap-2 flex-wrap">
@@ -245,15 +284,18 @@ export function SavedCardSection({ config, isPayByInvoice }: Props) {
</button>
</div>
<p className="text-xs text-text-muted mt-4">
{t("savedCardBankTransferHint")}{" "}
<a
href="/support"
className="text-accent hover:underline"
>
{t("savedCardBankTransferLink")}
</a>
</p>
{/* Bank-transfer hint shown only for company accounts. */}
{!isPersonal && (
<p className="text-xs text-text-muted mt-4">
{t("savedCardBankTransferHint")}{" "}
<a
href="/support"
className="text-accent hover:underline"
>
{t("savedCardBankTransferLink")}
</a>
</p>
)}
</div>
</Card>
);