@hook-sdk/template 0.6.0 → 0.7.1

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.
package/dist/index.d.cts CHANGED
@@ -267,14 +267,29 @@ interface UseResetFormResult {
267
267
  declare function useResetForm(): UseResetFormResult;
268
268
 
269
269
  type SubscriptionStatus = 'active' | 'trialing' | 'expired' | 'canceled' | 'past_due' | 'pending' | 'none';
270
+ type PaymentMethod = 'card' | 'pix-auto' | 'pix-once';
271
+ interface PixPending {
272
+ method: 'pix-auto' | 'pix-once';
273
+ qrCodePayload: string | null;
274
+ qrCodeBase64: string | null;
275
+ expiresAt: string | null;
276
+ }
270
277
  /**
271
278
  * Hook headless pro Paywall. Expõe status atual da subscription + ação
272
- * `checkout(cpf)` que chama `POST /payments/checkout/card` e redireciona
273
- * o browser pro `invoiceUrl` hosted do Asaas. Após o checkout, Asaas
274
- * redireciona de volta com query string indicativa; subscription real
275
- * é atualizada via webhook (`PAYMENT_RECEIVED` / `SUBSCRIPTION_ACTIVATED`).
279
+ * `checkout({cpf, cycle, method})` com 3 métodos:
280
+ *
281
+ * - **card** (default): cria subscription Asaas recorrente, redireciona pro
282
+ * invoiceUrl hosted. Após pagar, Asaas redireciona de volta e webhook
283
+ * atualiza DB.
284
+ * - **pix-auto**: Pix Automático Jornada 3 BACEN. User escaneia QR 1x no app
285
+ * do banco, autoriza débito recorrente. Retorna QR via `pixPending`
286
+ * (hook fica populado até user confirmar no banco).
287
+ * - **pix-once**: Pix avulso. User paga QR único, ganha 1 ciclo. Sem
288
+ * recorrência automática — renova voltando no app. Retorna QR via
289
+ * `pixPending`.
276
290
  *
277
- * MVP: checkout cartão. Pix vem depois (gatilho documentado em P10.7).
291
+ * Subscription real é atualizada via webhook (`PAYMENT_RECEIVED` ou
292
+ * `PIX_AUTOMATIC_RECURRING_AUTHORIZATION_ACTIVATED`).
278
293
  */
279
294
  declare function usePaywallState(): {
280
295
  status: SubscriptionStatus;
@@ -283,10 +298,13 @@ declare function usePaywallState(): {
283
298
  checkout: (args: {
284
299
  cpf: string;
285
300
  cycle?: "MONTHLY" | "YEARLY";
301
+ method?: PaymentMethod;
286
302
  }) => Promise<void>;
287
303
  cancel: () => Promise<void>;
288
304
  opening: boolean;
289
305
  error: Error | null;
306
+ pixPending: PixPending | null;
307
+ dismissPix: () => void;
290
308
  };
291
309
 
292
310
  /**
@@ -348,6 +366,37 @@ declare function monthlyFromYearly(yearlyCents: number | null | undefined): numb
348
366
  * dailyFromYearly(23988) = 66 → formatBRL = "R$ 0,66"
349
367
  */
350
368
  declare function dailyFromYearly(yearlyCents: number | null | undefined): number;
369
+ /**
370
+ * Deriva o preço-ancoragem (valor "cheio" riscado) como múltiplo do preço
371
+ * real. Serve pra ancoragem psicológica em telas de paywall: mostra um valor
372
+ * maior riscado antes do preço real pra o usuário sentir desconto.
373
+ *
374
+ * Com `anchorMultiplier` no `paywall_config`, o creator mantém só o preço
375
+ * real no DB; a ancoragem acompanha automaticamente se o preço muda.
376
+ *
377
+ * Retorna `null` quando o multiplier não é utilizável (falta, ≤ 1, NaN,
378
+ * Infinity). Caller deve cair no fallback (valor absoluto em
379
+ * `paywall_config.anchorPriceCents`) ou pular a renderização da ancoragem.
380
+ *
381
+ * Examples:
382
+ * computeAnchorCents(1999, 2.5) = 4998
383
+ * computeAnchorCents(1999, undefined) = null
384
+ * computeAnchorCents(1999, 1) = null // 1× não ancora nada
385
+ * computeAnchorCents(1999, 0) = null
386
+ * computeAnchorCents(1999, NaN) = null
387
+ */
388
+ declare function computeAnchorCents(baseCents: number, multiplier: number | null | undefined): number | null;
389
+ /**
390
+ * Percentual de desconto (anchor → real), arredondado pra baixo pra nunca
391
+ * overstate. Retorna 0 quando anchor ≤ real (sem desconto a mostrar).
392
+ *
393
+ * Examples:
394
+ * discountPercent(4998, 1999) = 60 // (4998-1999)/4998 = 0.60...
395
+ * discountPercent(3998, 1999) = 50
396
+ * discountPercent(1000, 2000) = 0 // real > anchor
397
+ * discountPercent(2000, 2000) = 0
398
+ */
399
+ declare function discountPercent(anchorCents: number, realCents: number): number;
351
400
 
352
401
  /**
353
402
  * Escape hatch pra screens FORA do fluxo de auth (ex: settings/trocar-senha).
@@ -554,4 +603,4 @@ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
554
603
  */
555
604
  declare function shouldShowPermanentOption(state: InstallState): boolean;
556
605
 
557
- export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
606
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type PaymentMethod, type PixPending, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, computeAnchorCents, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, discountPercent, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.d.ts CHANGED
@@ -267,14 +267,29 @@ interface UseResetFormResult {
267
267
  declare function useResetForm(): UseResetFormResult;
268
268
 
269
269
  type SubscriptionStatus = 'active' | 'trialing' | 'expired' | 'canceled' | 'past_due' | 'pending' | 'none';
270
+ type PaymentMethod = 'card' | 'pix-auto' | 'pix-once';
271
+ interface PixPending {
272
+ method: 'pix-auto' | 'pix-once';
273
+ qrCodePayload: string | null;
274
+ qrCodeBase64: string | null;
275
+ expiresAt: string | null;
276
+ }
270
277
  /**
271
278
  * Hook headless pro Paywall. Expõe status atual da subscription + ação
272
- * `checkout(cpf)` que chama `POST /payments/checkout/card` e redireciona
273
- * o browser pro `invoiceUrl` hosted do Asaas. Após o checkout, Asaas
274
- * redireciona de volta com query string indicativa; subscription real
275
- * é atualizada via webhook (`PAYMENT_RECEIVED` / `SUBSCRIPTION_ACTIVATED`).
279
+ * `checkout({cpf, cycle, method})` com 3 métodos:
280
+ *
281
+ * - **card** (default): cria subscription Asaas recorrente, redireciona pro
282
+ * invoiceUrl hosted. Após pagar, Asaas redireciona de volta e webhook
283
+ * atualiza DB.
284
+ * - **pix-auto**: Pix Automático Jornada 3 BACEN. User escaneia QR 1x no app
285
+ * do banco, autoriza débito recorrente. Retorna QR via `pixPending`
286
+ * (hook fica populado até user confirmar no banco).
287
+ * - **pix-once**: Pix avulso. User paga QR único, ganha 1 ciclo. Sem
288
+ * recorrência automática — renova voltando no app. Retorna QR via
289
+ * `pixPending`.
276
290
  *
277
- * MVP: checkout cartão. Pix vem depois (gatilho documentado em P10.7).
291
+ * Subscription real é atualizada via webhook (`PAYMENT_RECEIVED` ou
292
+ * `PIX_AUTOMATIC_RECURRING_AUTHORIZATION_ACTIVATED`).
278
293
  */
279
294
  declare function usePaywallState(): {
280
295
  status: SubscriptionStatus;
@@ -283,10 +298,13 @@ declare function usePaywallState(): {
283
298
  checkout: (args: {
284
299
  cpf: string;
285
300
  cycle?: "MONTHLY" | "YEARLY";
301
+ method?: PaymentMethod;
286
302
  }) => Promise<void>;
287
303
  cancel: () => Promise<void>;
288
304
  opening: boolean;
289
305
  error: Error | null;
306
+ pixPending: PixPending | null;
307
+ dismissPix: () => void;
290
308
  };
291
309
 
292
310
  /**
@@ -348,6 +366,37 @@ declare function monthlyFromYearly(yearlyCents: number | null | undefined): numb
348
366
  * dailyFromYearly(23988) = 66 → formatBRL = "R$ 0,66"
349
367
  */
350
368
  declare function dailyFromYearly(yearlyCents: number | null | undefined): number;
369
+ /**
370
+ * Deriva o preço-ancoragem (valor "cheio" riscado) como múltiplo do preço
371
+ * real. Serve pra ancoragem psicológica em telas de paywall: mostra um valor
372
+ * maior riscado antes do preço real pra o usuário sentir desconto.
373
+ *
374
+ * Com `anchorMultiplier` no `paywall_config`, o creator mantém só o preço
375
+ * real no DB; a ancoragem acompanha automaticamente se o preço muda.
376
+ *
377
+ * Retorna `null` quando o multiplier não é utilizável (falta, ≤ 1, NaN,
378
+ * Infinity). Caller deve cair no fallback (valor absoluto em
379
+ * `paywall_config.anchorPriceCents`) ou pular a renderização da ancoragem.
380
+ *
381
+ * Examples:
382
+ * computeAnchorCents(1999, 2.5) = 4998
383
+ * computeAnchorCents(1999, undefined) = null
384
+ * computeAnchorCents(1999, 1) = null // 1× não ancora nada
385
+ * computeAnchorCents(1999, 0) = null
386
+ * computeAnchorCents(1999, NaN) = null
387
+ */
388
+ declare function computeAnchorCents(baseCents: number, multiplier: number | null | undefined): number | null;
389
+ /**
390
+ * Percentual de desconto (anchor → real), arredondado pra baixo pra nunca
391
+ * overstate. Retorna 0 quando anchor ≤ real (sem desconto a mostrar).
392
+ *
393
+ * Examples:
394
+ * discountPercent(4998, 1999) = 60 // (4998-1999)/4998 = 0.60...
395
+ * discountPercent(3998, 1999) = 50
396
+ * discountPercent(1000, 2000) = 0 // real > anchor
397
+ * discountPercent(2000, 2000) = 0
398
+ */
399
+ declare function discountPercent(anchorCents: number, realCents: number): number;
351
400
 
352
401
  /**
353
402
  * Escape hatch pra screens FORA do fluxo de auth (ex: settings/trocar-senha).
@@ -554,4 +603,4 @@ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
554
603
  */
555
604
  declare function shouldShowPermanentOption(state: InstallState): boolean;
556
605
 
557
- export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
606
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type PaymentMethod, type PixPending, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, computeAnchorCents, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, discountPercent, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.js CHANGED
@@ -98,6 +98,7 @@ function usePaywallState() {
98
98
  const { subscription } = useHook2();
99
99
  const [opening, setOpening] = useState2(false);
100
100
  const [error, setError] = useState2(null);
101
+ const [pixPending, setPixPending] = useState2(null);
101
102
  const status = subscription.status();
102
103
  const daysLeftInTrial = subscription.daysLeftInTrial();
103
104
  const initialLoadComplete = subscription.initialLoadComplete;
@@ -105,12 +106,37 @@ function usePaywallState() {
105
106
  async (args) => {
106
107
  setOpening(true);
107
108
  setError(null);
109
+ setPixPending(null);
110
+ const method = args.method ?? "card";
111
+ const cycle = args.cycle ?? "MONTHLY";
108
112
  try {
109
- const result = await subscription.checkoutCard({
110
- cpf: args.cpf,
111
- cycle: args.cycle ?? "MONTHLY"
112
- });
113
- window.location.href = result.invoiceUrl;
113
+ if (method === "card") {
114
+ const result = await subscription.checkoutCard({ cpf: args.cpf, cycle });
115
+ window.location.href = result.invoiceUrl;
116
+ return;
117
+ }
118
+ if (method === "pix-auto") {
119
+ const result = await subscription.checkoutPixAuto({ cpf: args.cpf, cycle });
120
+ setPixPending({
121
+ method: "pix-auto",
122
+ qrCodePayload: result.qrCodePayload,
123
+ qrCodeBase64: result.qrCodeBase64,
124
+ expiresAt: null
125
+ });
126
+ setOpening(false);
127
+ return;
128
+ }
129
+ if (method === "pix-once") {
130
+ const result = await subscription.checkoutPixOnce({ cpf: args.cpf, cycle });
131
+ setPixPending({
132
+ method: "pix-once",
133
+ qrCodePayload: result.qrCodePayload,
134
+ qrCodeBase64: result.qrCodeBase64,
135
+ expiresAt: result.expiresAt
136
+ });
137
+ setOpening(false);
138
+ return;
139
+ }
114
140
  } catch (err) {
115
141
  setError(err);
116
142
  setOpening(false);
@@ -126,7 +152,18 @@ function usePaywallState() {
126
152
  setError(err);
127
153
  }
128
154
  }, [subscription]);
129
- return { status, daysLeftInTrial, initialLoadComplete, checkout, cancel, opening, error };
155
+ const dismissPix = useCallback(() => setPixPending(null), []);
156
+ return {
157
+ status,
158
+ daysLeftInTrial,
159
+ initialLoadComplete,
160
+ checkout,
161
+ cancel,
162
+ opening,
163
+ error,
164
+ pixPending,
165
+ dismissPix
166
+ };
130
167
  }
131
168
 
132
169
  // src/internal/SubscriptionGate.tsx
@@ -2351,6 +2388,16 @@ function dailyFromYearly(yearlyCents) {
2351
2388
  if (yearlyCents === null || yearlyCents === void 0) return 0;
2352
2389
  return Math.round(yearlyCents / 365);
2353
2390
  }
2391
+ function computeAnchorCents(baseCents, multiplier) {
2392
+ if (multiplier === null || multiplier === void 0) return null;
2393
+ if (!Number.isFinite(multiplier)) return null;
2394
+ if (multiplier <= 1) return null;
2395
+ return Math.round(baseCents * multiplier);
2396
+ }
2397
+ function discountPercent(anchorCents, realCents) {
2398
+ if (anchorCents <= realCents) return 0;
2399
+ return Math.floor((anchorCents - realCents) / anchorCents * 100);
2400
+ }
2354
2401
 
2355
2402
  // src/hooks/useAuthPrimitives.ts
2356
2403
  import { useEffect as useEffect9 } from "react";
@@ -2454,12 +2501,14 @@ export {
2454
2501
  InstallSplash,
2455
2502
  LoadingState,
2456
2503
  PushPrompt2 as PushPrompt,
2504
+ computeAnchorCents,
2457
2505
  dailyFromYearly,
2458
2506
  detectAndroidBrowser,
2459
2507
  detectIOSBrowser,
2460
2508
  detectInAppApp,
2461
2509
  detectPlatform,
2462
2510
  detectStandalone,
2511
+ discountPercent,
2463
2512
  formatBRL,
2464
2513
  monthlyFromYearly,
2465
2514
  shouldBlockInstall,