import { type CSSProperties } from "react"; import { type CrediballThemeOverrides } from "./theme"; /** A purchasable credit bundle from the Crediball dashboard (returned by listPackages()). */ export interface PackageOption { id: string; name: string; price: number; credits: number; } /** A recurring subscription plan (returned by listPackages()). */ export interface SubscriptionPlanOption { id: string; name: string; price: number; credits: number; period: "weekly" | "monthly" | "yearly"; } /** The end user's current active subscription (returned by listPackages() with userId). */ export interface ActiveSubscriptionInfo { subscriptionId: string; planName: string; planPeriod: string; currentPeriodEnd: string; } /** The end user's current auto-topup rule (returned by listPackages() with userId). */ export interface AutoTopupInfo { enabled: boolean; packageId: string; thresholdCredits: number; status: "pending" | "active" | "needs_auth" | "failed"; } export interface PaywallModalProps extends CrediballThemeOverrides { open: boolean; /** * Preferred: pass the packages from `meter.listPackages()` so Crediball can * resolve credits and record revenue automatically when the user checks out. * The modal renders one button per package (e.g. "Starter — €5 / 10 cr"). */ packages?: PackageOption[]; /** * Called when the user selects a package. Use this with `packages` — call * `meter.topup({ userId, packageId: pkg.id })` in your backend. No `amount` * calculation needed; Crediball derives it from the package. The second * argument is the promo code the user entered (if the "Have a promo code?" * field is shown and filled in) — pass it through as `code` to `topup()`. */ onSelectPackage?: (pkg: PackageOption, code?: string) => void; /** * Subscription plans from `meter.listPackages()`. When provided (and the user * has no active subscription), the modal shows a "Subscribe" section above * one-time packages. */ subscriptionPlans?: SubscriptionPlanOption[]; /** * Called when the user selects a subscription plan. Call * `meter.subscribe({ userId, planId: plan.id })` in your backend. */ onSelectPlan?: (plan: SubscriptionPlanOption) => void; /** * The end user's active subscription (from `meter.listPackages({ userId })`). * When provided, the subscription section is replaced by a subscription-status * banner and a cancel option. */ activeSubscription?: ActiveSubscriptionInfo | null; /** * Called when the user clicks "Cancel subscription" in the paywall. * Call `meter.cancelSubscription({ userId, subscriptionId })` in your backend. */ onCancelSubscription?: (subscriptionId: string) => void | Promise; /** * The end user's current auto-topup rule (from `meter.listPackages({ userId })` * or `meter.getAutoTopupStatus(userId)`). When absent/null, an opt-in row is * shown below the packages so the user can turn on "keep me topped up * automatically". */ autoTopup?: AutoTopupInfo | null; /** * Called when the user opts into auto top-up. Shown whenever `packages` is * non-empty — same as `onSelectPackage`, this is optional to invoke (a no-op * if you don't wire it, e.g. in a preview with no backend). Call * `meter.setupAutoTopupCheckout({ userId, packageId, thresholdCredits, * successUrl, cancelUrl })` in your backend — it saves a payment method, no * money changes hands in this step — then redirect the browser to the * returned `url`. */ onEnableAutoTopup?: (input: { packageId: string; thresholdCredits: number; }) => void; /** * Legacy: flat list of euro amounts. Prefer `packages` + `onSelectPackage`. * When both are provided, `packages` takes precedence. */ amounts?: number[]; /** Prefix shown on each legacy amount button (default "Add €"). */ amountPrefix?: string; title?: string; description?: string; /** Called when the user clicks a legacy amount button. The second argument is * the promo code the user entered, same as `onSelectPackage`'s. */ onAdd?: (amount: number, code?: string) => void; onClose?: () => void; /** * An error from the last checkout attempt (e.g. "Invalid or expired promo * code.") to show inline, so a failed purchase isn't silently invisible. * Wired automatically by when its built-in checkout * fails; pass it yourself when using the modal standalone. */ checkoutError?: string | null; /** When true, show a free-form amount field in addition to the preset buttons. */ allowCustom?: boolean; /** * When true, show a "Have a promo code?" field above the top-up buttons. The * code is passed as the second argument to `onSelectPackage`/`onAdd`/the * built-in checkout — pass it through as `code` to `topup()` or * `createCheckout()`. Not validated client-side; an invalid code is rejected * when the top-up is actually processed. */ allowPromoCode?: boolean; /** Label above the promo code toggle (default "Have a promo code?"). */ promoCodeLabel?: string; /** Button label for the free-form amount field (default "Add"). */ addButtonText?: string; /** Minimum custom amount (default 1). */ customMin?: number; /** Optional maximum custom amount. */ customMax?: number; /** * Credits granted per 1 currency unit for the custom-amount field (your * dashboard's custom top-up rate). When provided, a live "≈ N credits" * line appears under the field as the user types, computed the same way * the actual charge is (Math.round(amount * rate)) — so it never promises * a different number than what gets granted. */ customTopupRate?: number; /** Currency symbol shown beside the custom field (default "€"). */ currencySymbol?: string; /** * When provided, a compact "Balance" column is shown at the top of the * modal (beside "Usage" if that's provided too) so the user can see why * the modal appeared. */ balance?: number; /** Credits per one currency unit — used to derive the ≈ money value beside the balance. */ balanceRate?: number; /** ISO 4217 currency for the derived balance money value (default "EUR"). */ currency?: string; /** Credits used this period — shown as a second column beside balance, when provided. */ usage?: number; /** Label above the usage column (default "Usage"). */ usageLabel?: string; /** Hide the "Powered by Crediball" footer line (default false). */ hideBranding?: boolean; /** * The user's referral invite link. When provided, a "Refer a friend" row with * a copyable link is shown at the bottom of the modal — a way to earn credits * without paying. Wired automatically by when referrals are * enabled in the dashboard; pass it yourself when using the modal standalone. */ referralLink?: string | null; /** Credits earned per successful referral, shown in the refer-a-friend blurb. */ referralReward?: number; /** * Credits the referred friend earns, shown alongside `referralReward` in the * refer-a-friend blurb (0/undefined omits that clause). Wired automatically * by when referrals are enabled in the dashboard. */ referredReward?: number; /** * Label above the referral link. `{credits}` is replaced with `referralReward`, * `{referredCredits}` with `referredReward`. A `\n` renders as a line break. * Defaults to a two-line "Refer a friend.\nYou earn {credits} credits and * your friend {referredCredits} credits" when `referredReward` is set, or a * single-line "Refer a friend · earn {credits} credits" otherwise. Shown as * written (not uppercased), so lowercase/sentence case is preserved. */ referralLabel?: string; style?: CSSProperties; } /** * Pattern B — Paywall moment. Render when a user has insufficient credits to * continue. The host controls when it appears; shows a small "Powered by * Crediball" line by default (set `hideBranding` to remove it). */ export declare function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts, amountPrefix, title, description, onAdd, onClose, checkoutError, allowCustom, allowPromoCode, promoCodeLabel, addButtonText, customMin, customMax, customTopupRate, currencySymbol, balance, balanceRate, currency, usage, usageLabel, hideBranding, referralLink, referralReward, referredReward, referralLabel, accentColor, style, }: PaywallModalProps): import("react").JSX.Element | null;