@crediball/react 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,6 +65,11 @@ function symbolForCurrency(iso) {
65
65
  */
66
66
  export function CrediballProvider({ children, publishableKey, userId, apiUrl, pollIntervalMs, lowCreditThreshold, amounts = [5, 10, 20], onTopup, onSelectPackage, onSelectPlan, onCancelSubscription, onEnableAutoTopup, watchFetch = true, allowCustom, customMin, customMax, allowPromoCode, promoCodeLabel, currencySymbol: currencySymbolProp, title, description, accentColor, applyRemoteTheme = true, captureReferrals = true, }) {
67
67
  const [open, setOpen] = useState(false);
68
+ // Surfaced in the paywall itself (not just the console) when the built-in
69
+ // checkout fails — e.g. an invalid/expired promo code — so a user isn't
70
+ // left clicking a "dead" button with no visible feedback. Cleared at the
71
+ // start of every attempt so it always reflects the latest one.
72
+ const [checkoutError, setCheckoutError] = useState(null);
68
73
  // Referral auto-capture: pick up a `?ref=CODE` visit into localStorage (and
69
74
  // count the click) as soon as the provider mounts — the visitor usually has
70
75
  // no account yet at this point.
@@ -104,7 +109,10 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
104
109
  cancelled = true;
105
110
  };
106
111
  }, [publishableKey, userId, apiUrl]);
107
- const showPaywall = useCallback(() => setOpen(true), []);
112
+ const showPaywall = useCallback(() => {
113
+ setCheckoutError(null);
114
+ setOpen(true);
115
+ }, []);
108
116
  const hidePaywall = useCallback(() => setOpen(false), []);
109
117
  // Keep a ref so the fetch-watcher closure always calls the latest version.
110
118
  const showPaywallRef = useRef(showPaywall);
@@ -209,11 +217,13 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
209
217
  return;
210
218
  }
211
219
  if (client) {
220
+ setCheckoutError(null);
212
221
  try {
213
222
  await builtIn();
214
223
  }
215
224
  catch (err) {
216
225
  console.error(`[crediball] ${errorLabel} failed:`, err);
226
+ setCheckoutError(err instanceof Error ? err.message : `Could not start ${errorLabel}. Please try again.`);
217
227
  }
218
228
  return;
219
229
  }
@@ -269,7 +279,7 @@ export function CrediballProvider({ children, publishableKey, userId, apiUrl, po
269
279
  // layout box. Host props/CSS are overridden by design (dashboard wins).
270
280
  const themeVars = applyRemoteTheme ? themeToCssVars(uiTheme) : {};
271
281
  const hasThemeVars = Object.keys(themeVars).length > 0;
272
- const inner = (_jsxs(_Fragment, { children: [children, _jsx(PaywallModal, { open: open, packages: pkgs?.packages, onSelectPackage: handleSelectPackage, subscriptionPlans: pkgs?.subscriptionPlans, onSelectPlan: handleSelectPlan, activeSubscription: pkgs?.activeSubscription, onCancelSubscription: onCancelSubscription ? handleCancelSubscription : undefined, autoTopup: pkgs?.autoTopup, onEnableAutoTopup: handleEnableAutoTopup, amounts: amounts, amountPrefix: `Add ${currencySymbol}`, onAdd: handleAdd, onClose: hidePaywall, allowCustom: effectiveAllowCustom, customMin: effectiveCustomMin, customMax: effectiveCustomMax, allowPromoCode: effectiveAllowPromoCode, promoCodeLabel: promoCodeLabel, customTopupRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currencySymbol: currencySymbol, balance: snapshot.balance ?? undefined, balanceRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currency: pkgs?.currency, usage: snapshot.usage?.usedCredits ?? undefined, title: title ?? uiContent?.paywallTitle, description: description ?? uiContent?.paywallDescription, addButtonText: uiContent?.paywallButtonText, referralLink: referral?.link ?? undefined, referralReward: referral?.rewardCredits, referredReward: referral?.referredRewardCredits, referralLabel: uiContent?.referralLabel, accentColor: accentColor })] }));
282
+ const inner = (_jsxs(_Fragment, { children: [children, _jsx(PaywallModal, { open: open, packages: pkgs?.packages, onSelectPackage: handleSelectPackage, subscriptionPlans: pkgs?.subscriptionPlans, onSelectPlan: handleSelectPlan, activeSubscription: pkgs?.activeSubscription, onCancelSubscription: onCancelSubscription ? handleCancelSubscription : undefined, autoTopup: pkgs?.autoTopup, onEnableAutoTopup: handleEnableAutoTopup, amounts: amounts, amountPrefix: `Add ${currencySymbol}`, onAdd: handleAdd, onClose: hidePaywall, allowCustom: effectiveAllowCustom, customMin: effectiveCustomMin, customMax: effectiveCustomMax, allowPromoCode: effectiveAllowPromoCode, promoCodeLabel: promoCodeLabel, customTopupRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currencySymbol: currencySymbol, balance: snapshot.balance ?? undefined, balanceRate: pkgs?.customTopup.enabled ? pkgs.customTopup.rate : undefined, currency: pkgs?.currency, usage: snapshot.usage?.usedCredits ?? undefined, title: title ?? uiContent?.paywallTitle, description: description ?? uiContent?.paywallDescription, addButtonText: uiContent?.paywallButtonText, referralLink: referral?.link ?? undefined, referralReward: referral?.rewardCredits, referredReward: referral?.referredRewardCredits, referralLabel: uiContent?.referralLabel, checkoutError: checkoutError, accentColor: accentColor })] }));
273
283
  const configCtx = useMemo(() => ({ publishableKey, userId, apiUrl }), [publishableKey, userId, apiUrl]);
274
284
  return (_jsx(CrediballContext.Provider, { value: ctx, children: _jsx(CrediballConfigContext.Provider, { value: configCtx, children: hasThemeVars ? (_jsx("div", { style: { display: "contents", ...themeVars }, children: inner })) : (inner) }) }));
275
285
  }
@@ -100,6 +100,13 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
100
100
  * the promo code the user entered, same as `onSelectPackage`'s. */
101
101
  onAdd?: (amount: number, code?: string) => void;
102
102
  onClose?: () => void;
103
+ /**
104
+ * An error from the last checkout attempt (e.g. "Invalid or expired promo
105
+ * code.") to show inline, so a failed purchase isn't silently invisible.
106
+ * Wired automatically by <CrediballProvider> when its built-in checkout
107
+ * fails; pass it yourself when using the modal standalone.
108
+ */
109
+ checkoutError?: string | null;
103
110
  /** When true, show a free-form amount field in addition to the preset buttons. */
104
111
  allowCustom?: boolean;
105
112
  /**
@@ -175,4 +182,4 @@ export interface PaywallModalProps extends CrediballThemeOverrides {
175
182
  * continue. The host controls when it appears; shows a small "Powered by
176
183
  * Crediball" line by default (set `hideBranding` to remove it).
177
184
  */
178
- export declare function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts, amountPrefix, title, description, onAdd, onClose, 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;
185
+ 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;
@@ -24,7 +24,7 @@ function sectionLabelStyle(marginBottom) {
24
24
  * continue. The host controls when it appears; shows a small "Powered by
25
25
  * Crediball" line by default (set `hideBranding` to remove it).
26
26
  */
27
- export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts = [5, 10], amountPrefix = "Add €", title = "You need more credits to continue.", description = "Top up to keep using this feature.", onAdd, onClose, allowCustom = false, allowPromoCode = false, promoCodeLabel = "Have a promo code?", addButtonText = "Add", customMin = 1, customMax, customTopupRate, currencySymbol = "€", balance, balanceRate, currency = "EUR", usage, usageLabel = "Usage", hideBranding = false, referralLink, referralReward, referredReward, referralLabel, accentColor, style, }) {
27
+ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlans, onSelectPlan, activeSubscription, onCancelSubscription, autoTopup, onEnableAutoTopup, amounts = [5, 10], amountPrefix = "Add €", title = "You need more credits to continue.", description = "Top up to keep using this feature.", onAdd, onClose, checkoutError, allowCustom = false, allowPromoCode = false, promoCodeLabel = "Have a promo code?", addButtonText = "Add", customMin = 1, customMax, customTopupRate, currencySymbol = "€", balance, balanceRate, currency = "EUR", usage, usageLabel = "Usage", hideBranding = false, referralLink, referralReward, referredReward, referralLabel, accentColor, style, }) {
28
28
  // Stack the top-up buttons one-per-line when the dialog itself is narrow.
29
29
  // Measured off the dialog's own box (via ResizeObserver) rather than the
30
30
  // window — in normal use the two are the same width, but this also makes
@@ -217,7 +217,7 @@ export function PaywallModal({ open, packages, onSelectPackage, subscriptionPlan
217
217
  ? "You've used all your credits for this period."
218
218
  : title }), _jsx("p", { style: { marginTop: 8, marginBottom: 0, fontSize: 13, color: theme.inkMuted }, children: activeSubscription
219
219
  ? `Your ${activeSubscription.planName} credits renew on ${new Date(activeSubscription.currentPeriodEnd).toLocaleDateString()}.`
220
- : description }), activeSubscription && (_jsxs("div", { style: {
220
+ : description }), checkoutError ? (_jsx("p", { style: { marginTop: 12, marginBottom: 0, fontSize: 13, color: theme.accent }, children: checkoutError })) : null, activeSubscription && (_jsxs("div", { style: {
221
221
  marginTop: 16,
222
222
  padding: "12px 16px",
223
223
  borderRadius: theme.radiusCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediball/react",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Drop-in React components for showing Crediball credits inside your AI app.",
5
5
  "license": "MIT",
6
6
  "author": "Filippo Rezzadore <filipporezzadore@gmail.com>",