Skip to content
33 linesCodeBlameRaw
1/**
2 * What the billing page says on the way back from Stripe's payment pages.
3 * A card payment usually clears on the way back; a bank transfer, or a
4 * payment the bank holds for a check, has not yet. Pure, so it can be
5 * tested.
6 */
7import type { FeatureState, LedgerEntry } from "@g1t/contracts";
8
9/**
10 * Back from a plan's payment page: done once the plan is on; otherwise
11 * that the payment did not go through, or has not cleared yet.
12 */
13export function planReturnNotice(state: FeatureState | null, done: "subscribed" | "security_on"): string {
14 const security = done === "security_on";
15 if (state?.on)
16 return security ? "Security and quality is on for every private repository in the workspace." : "The g1t plan is on.";
17 const name = security ? "Security and quality" : "The g1t plan";
18 if (state?.subscription?.status === "past_due")
19 return `The payment did not go through. Choose Update payment method on the plan; ${name.charAt(0).toLowerCase()}${name.slice(1)} starts once a payment clears.`;
20 return `Payment pending. ${name} starts once the payment clears, and this page shows it the next time it loads.`;
21}
22
23/**
24 * Back from a prepayment's page: whether it was credited, from the balance
25 * before and after, or a payment in advance entered in the last half hour
26 * (Stripe's webhook may have got there first).
27 */
28export function prepaymentSettled(before: number | null, after: number, ledger: LedgerEntry[] | null, now = Date.now()): boolean {
29 if (before != null && after > before) return true;
30 return (ledger ?? []).some(
31 (entry) => entry.kind === "top_up" && entry.amountMicros > 0 && now - Date.parse(entry.createdAt) < 30 * 60_000,
32 );
33}