import { type ReactNode } from "react"; import { type PackageOption, type SubscriptionPlanOption } from "./PaywallModal"; import { type EventCost, type UsagePeriod, type PackagesPayload, type CrediballUiContent, type ColorScheme } from "@crediball/core"; export interface CrediballProviderProps { children: ReactNode; /** * Browser-safe key (cb_pub_...) from the Crediball dashboard. When set * together with `userId`, the provider fetches and live-updates balance, * usage, event costs, and packages — powering useCrediball()'s data fields, * the Tier-2 variable components, and PaywallModal's package list. Omit both * to use the provider in its original, data-less mode (host supplies * everything via props/callbacks). */ publishableKey?: string; /** The end user's id, exactly as passed to track() server-side. Required alongside publishableKey. */ userId?: string; /** Base URL of the Crediball API. Defaults to the hosted instance. */ apiUrl?: string; /** Poll interval while the tab is visible. Default 30s. */ pollIntervalMs?: number; /** Override the default "low credits" threshold (defaults to the highest-cost active event). */ lowCreditThreshold?: number; /** * Top-up amounts to offer in the paywall (host decides the meaning, e.g. euros). * Only used in data-less mode (no publishableKey/userId), where there's no * dashboard catalog to fall back to and the host is fully responsible for * `onTopup`. Ignored whenever publishableKey + userId are set — the paywall * then shows your dashboard's real packages, or no top-up button at all if * you haven't configured any yet, rather than a button that can't actually * charge anything. */ amounts?: number[]; /** * Optional override for the custom-amount top-up. If omitted, the paywall * starts a Crediball-hosted Stripe Checkout itself (using publishableKey + * userId) and redirects — no backend wiring needed, just connect payouts in the * dashboard. Provide this only to run your own top-up/checkout flow instead. */ onTopup?: (amount: number, code?: string) => void | Promise; /** * Optional override for one-time packages. If omitted, the paywall starts a * Crediball-hosted Stripe Checkout itself and redirects (connect payouts in the * dashboard). Provide this only to run your own flow (e.g. `meter.topup({ userId, * packageId })` server-side). The second argument is the promo code the user * entered, if any (see `allowPromoCode`) — pass it through as `code`. */ onSelectPackage?: (pkg: PackageOption, code?: string) => void | Promise; /** * Optional override for subscription plans. If omitted, the paywall starts a * recurring Stripe Checkout itself (connect payouts in the dashboard) and Stripe * grants the plan's credits each period. Provide this only to run your own flow * (e.g. `meter.subscribe({ userId, planId })` server-side). */ onSelectPlan?: (plan: SubscriptionPlanOption) => void | Promise; /** * Called when the user cancels their subscription. Cancellation is server-side * only (it needs your secret key), so the paywall shows a Cancel button on the * active-subscription banner ONLY when this is provided — wire it to * `meter.cancelSubscription({ userId, subscriptionId })` on your backend. */ onCancelSubscription?: (subscriptionId: string) => void | Promise; /** * Optional override for the auto-topup opt-in. If omitted, the paywall starts a * Crediball-hosted Stripe Checkout in setup mode itself (connect payouts in the * dashboard) — no money changes hands there, it only saves a payment method. * Provide this only to run your own flow (e.g. * `meter.setupAutoTopupCheckout({ userId, packageId, thresholdCredits, ... })` * server-side). */ onEnableAutoTopup?: (input: { packageId: string; thresholdCredits: number; }) => void | Promise; /** * Auto-detect insufficient-credit responses from any fetch() call and open the * paywall automatically — no per-call wiring. Defaults to true. Set false to only * open the paywall manually via useCrediball().showPaywall(). * * Handles both HTTP 402 (regular JSON routes) and HTTP 200 text/event-stream / * ndjson (streaming/SSE routes where the error arrives as a stream chunk). */ watchFetch?: boolean; /** * Show a free-form amount field in the paywall. Defaults to your dashboard's * custom-topup setting (with its min/max) when packages are loaded — set this * explicitly only to override (e.g. `false` to force-hide it). */ allowCustom?: boolean; /** Override the custom-amount minimum (defaults to the dashboard's configured minimum). */ customMin?: number; /** Override the custom-amount maximum (defaults to the dashboard's configured maximum). */ customMax?: number; /** * Show a "Have a promo code?" field in the paywall. Auto-derived from your * dashboard's Promos → Promotions config: shown automatically once you have * an active Checkout promotion, same as `allowCustom`/custom top-up. Pass * this only to override (e.g. `false` to force-hide it, or `true` to show it * before you've set one up). The code is passed through to * `onSelectPackage`/`onTopup`, or included automatically in the built-in * checkout when neither is provided. */ allowPromoCode?: boolean; /** Label above the promo code toggle (default "Have a promo code?"). */ promoCodeLabel?: string; /** * Currency symbol shown in the paywall. Auto-derived (via Intl) from the ISO * currency you configured on the dashboard's Packages page once packages load, * so it never drifts out of sync if you switch currencies there. Pass this only * to force a specific symbol instead. Falls back to "€" before the first fetch. */ currencySymbol?: string; title?: string; description?: string; /** Override the accent color (defaults to Action Blue). */ accentColor?: string; /** * Apply the theme (colors, radii, font) the developer published on the * Crediball dashboard's UI-patterns page — served alongside packages — as * `--crediball-*` CSS variables, so preview edits reflect here with no code * change. On by default; the published theme takes precedence over any * `--crediball-*` vars you set in your own CSS. Set false to opt out and * theme entirely from your own stylesheet. */ applyRemoteTheme?: boolean; /** * Which color scheme to render the published theme's dark colors in (if * the developer has configured a dark palette on the dashboard). Defaults * to `"system"` — follows the OS/browser's `prefers-color-scheme` and * updates live if it changes. Pass `"light"`/`"dark"` explicitly to follow * your own app's in-app theme toggle instead (common when it can diverge * from the OS setting). Read back via `useCrediball().colorScheme` for * your own custom UI. */ colorScheme?: "light" | "dark" | "system"; /** * Automatic referral handling. On by default: any visit with `?ref=CODE` in * the URL is captured (stored locally + click counted), and once a `userId` * is present the stored code is attached to that user as a pending referral — * so with referrals enabled in the dashboard there is zero extra wiring. * Rewards are only ever granted server-side when the conversion condition is * met. Set false to manage capture/completion yourself via @crediball/core. */ captureReferrals?: boolean; /** * Whether the provider renders the built-in `` for you. On by * default. Set false when you render your own paywall UI (e.g. a naked/ejected * one built on `useCrediball()`), so `showPaywall()` drives only your version * and not two stacked modals. Everything else — `open`, `hidePaywall`, * `selectPackage`, `selectPlan`, `topUp`, `checkoutError` — keeps working * exactly the same. */ renderPaywall?: boolean; } /** * Internal: the provider's raw connection config, for hooks (useReferral) that * talk to the API directly instead of reading the polled snapshot. */ interface CrediballConfigValue { publishableKey?: string; userId?: string; apiUrl?: string; } /** Internal hook: the provider's publishableKey/userId/apiUrl. Throws outside a provider. */ export declare function useCrediballConfig(): CrediballConfigValue; interface CrediballContextValue { /** * Open the paywall modal. Always safe to call — never guard with "already shown" * state. Calling while already open is a no-op; dismissing via "Not now" does not * prevent the next showPaywall() call from reopening it. */ showPaywall: () => void; /** Close the paywall modal. */ hidePaywall: () => void; /** Whether the paywall is currently open. */ open: boolean; /** Alias of showPaywall — reads better from Tier-2 variable components. */ openPaywall: () => void; /** The user's current credit balance. null until loaded, or always null without publishableKey/userId. */ balance: number | null; /** Credits consumed in the user's current period (subscription cycle, or calendar month). */ usage: UsagePeriod | null; /** Alias of balance — the credits left to spend. */ remaining: number | null; /** True once balance drops below the low-credit threshold. */ isLow: boolean; /** True while the initial fetch is in flight. */ loading: boolean; /** Set when the last fetch failed. */ error: Error | null; /** Credit cost of a tracked event, from the live cost catalog. undefined if unknown. */ costOf: (event: string) => number | undefined; /** * Every action currently billed in the app, with its dashboard label and * price, most-used first — the whole catalog `costOf()` looks into. Empty * until the first fetch resolves, or when no action is active yet. Drives * `` and any hand-rolled "what costs what" screen. */ costList: EventCost[]; /** Trigger a top-up (delegates to the `onTopup` prop) and refresh balance/usage on completion. * `code` is a checkout promo code, if any. */ topUp: (amount: number, code?: string) => Promise; /** * Buy one of `packages.packages` — the same path ``'s package * buttons take. Delegates to the `onSelectPackage` prop when the host supplies * one, otherwise starts the built-in Stripe Checkout. `code` is a checkout * promo code, if any. Exposed so a headless/naked paywall can sell packages, * not just custom amounts via `topUp`. */ selectPackage: (pkg: PackageOption, code?: string) => Promise; /** * Subscribe to one of `packages.subscriptionPlans` — the same path * ``'s plan buttons take. Delegates to the `onSelectPlan` prop, * or starts the built-in subscription Checkout. */ selectPlan: (plan: SubscriptionPlanOption) => Promise; /** * Message from the last failed built-in checkout, for rendering inline in a * custom paywall. Cleared whenever the paywall is (re)opened. */ checkoutError: string | null; /** Refetch balance, usage, costs, and packages immediately. No-op without publishableKey/userId. */ refresh: () => Promise; /** The live packages payload (top-up options, plans, custom-topup, currency, ui). null until loaded. */ packages: PackagesPayload | null; /** Currency symbol derived from the dashboard's configured currency (e.g. "€", "$"). */ currencySymbol: string; /** Developer-published default copy from the dashboard, used as a label fallback. null when unconfigured. */ content: CrediballUiContent | null; /** The resolved color scheme (`colorScheme` prop, or the live OS/browser preference when it's "system"). */ colorScheme: ColorScheme; } /** * Pattern B, automated. Wrap your app once: * * checkout(eur)} * > * * * * With `publishableKey` + `userId` set, every useCrediball() consumer and every * Tier-2 variable component (, , ...) gets * live balance/usage/cost data automatically. Whenever any request returns 402 * `insufficient_credits`, the paywall appears automatically — no extra code per * call. You can also open it yourself with useCrediball().showPaywall(). * * SSE/streaming routes (text/event-stream, ndjson) are also handled automatically — * the watcher scans each chunk for credit errors, so no manual wiring is needed. */ export declare function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts, onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, onEnableAutoTopup, watchFetch, allowCustom, customMin, customMax, allowPromoCode, promoCodeLabel, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme, colorScheme, captureReferrals, renderPaywall, }: CrediballProviderProps): import("react").JSX.Element; /** * Access the paywall + live data from anywhere inside . * * Important: showPaywall() is always safe to call — never add a "paywallAlreadyShown" * guard around it. Dismissing the modal ("Not now") does not prevent future calls * from reopening it. Call showPaywall() unconditionally on every InsufficientCreditsError. * * Building your own paywall UI instead of using ? Everything it * needs is here: read the offers from `packages` (packages / subscriptionPlans / * customTopup) and sell them with `selectPackage`, `selectPlan`, and `topUp` — * the same handlers the modal's own buttons use, so the host's onSelectPackage / * onSelectPlan / onTopup props (or the built-in Stripe Checkout) still apply. * Surface `checkoutError` so a failed checkout isn't silent. */ export declare function useCrediball(): CrediballContextValue; /** * Same as useCrediball(), but returns null instead of throwing outside a * . For components (like CreditsBadge) that work either * standalone via props or auto-wired via a provider. */ export declare function useCrediballOptional(): CrediballContextValue | null; export {};