@hook-sdk/template 0.28.9 → 0.28.10

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
@@ -61,6 +61,13 @@ type PaywallConfig = {
61
61
  };
62
62
  checkoutMethods: CheckoutMethod[];
63
63
  requiresCpf: boolean;
64
+ /**
65
+ * When true, CheckoutPageDefault coleta CEP + número do endereço (card-path)
66
+ * e envia ambos como `cardHolderInfo.postalCode`/`addressNumber`.
67
+ * Default false → ship placeholder `01001000`/`100` (legacy behavior).
68
+ * Apps que precisam de NF-e ou antifraude refinada devem ativar.
69
+ */
70
+ collectCep?: boolean;
64
71
  cancelWindowDays?: number;
65
72
  errorMessages: 'default' | 'custom';
66
73
  };
@@ -500,6 +507,10 @@ interface UseCheckoutFormResult {
500
507
  setPhone: (v: string) => void;
501
508
  cpf: string;
502
509
  setCpf: (v: string) => void;
510
+ postalCode: string;
511
+ setPostalCode: (v: string) => void;
512
+ addressNumber: string;
513
+ setAddressNumber: (v: string) => void;
503
514
  method: CheckoutFormMethod;
504
515
  setMethod: (m: CheckoutFormMethod) => void;
505
516
  cycle: CheckoutFormCycle;
@@ -511,11 +522,15 @@ interface UseCheckoutFormResult {
511
522
  emailConfirmError: string | null;
512
523
  phoneError: string | null;
513
524
  cpfError: string | null;
525
+ postalCodeError: string | null;
526
+ addressNumberError: string | null;
514
527
  markNameTouched: () => void;
515
528
  markEmailTouched: () => void;
516
529
  markEmailConfirmTouched: () => void;
517
530
  markPhoneTouched: () => void;
518
531
  markCpfTouched: () => void;
532
+ markPostalCodeTouched: () => void;
533
+ markAddressNumberTouched: () => void;
519
534
  /**
520
535
  * Result of the debounced server-side email-exists check, used to swap
521
536
  * the CTA into "Email já cadastrado · Entrar" on the checkout form.
@@ -539,8 +554,15 @@ interface UseCheckoutFormResult {
539
554
  interface UseCheckoutFormArgs {
540
555
  defaultMethod: CheckoutFormMethod;
541
556
  defaultCycle: CheckoutFormCycle;
557
+ /**
558
+ * Quando true, o submit envia postalCode + addressNumber reais coletados
559
+ * via setPostalCode/setAddressNumber. Quando false (default), envia
560
+ * placeholder `01001000`/`100`. CheckoutPageDefault liga via config
561
+ * `paywall.collectCep`.
562
+ */
563
+ collectCep?: boolean;
542
564
  }
543
- declare function useCheckoutForm(args: UseCheckoutFormArgs): UseCheckoutFormResult;
565
+ declare function useCheckoutForm(hookArgs: UseCheckoutFormArgs): UseCheckoutFormResult;
544
566
 
545
567
  type SubscriptionStatus = 'active' | 'trialing' | 'expired' | 'canceled' | 'past_due' | 'pending' | 'none';
546
568
  type PaymentMethod = CheckoutMethod$1;
@@ -1082,6 +1104,7 @@ declare const AppConfigSchema: z.ZodObject<{
1082
1104
  "pix-once": "pix-once";
1083
1105
  }>>;
1084
1106
  requiresCpf: z.ZodBoolean;
1107
+ collectCep: z.ZodOptional<z.ZodBoolean>;
1085
1108
  cancelWindowDays: z.ZodOptional<z.ZodNumber>;
1086
1109
  errorMessages: z.ZodEnum<{
1087
1110
  default: "default";
package/dist/index.d.ts CHANGED
@@ -61,6 +61,13 @@ type PaywallConfig = {
61
61
  };
62
62
  checkoutMethods: CheckoutMethod[];
63
63
  requiresCpf: boolean;
64
+ /**
65
+ * When true, CheckoutPageDefault coleta CEP + número do endereço (card-path)
66
+ * e envia ambos como `cardHolderInfo.postalCode`/`addressNumber`.
67
+ * Default false → ship placeholder `01001000`/`100` (legacy behavior).
68
+ * Apps que precisam de NF-e ou antifraude refinada devem ativar.
69
+ */
70
+ collectCep?: boolean;
64
71
  cancelWindowDays?: number;
65
72
  errorMessages: 'default' | 'custom';
66
73
  };
@@ -500,6 +507,10 @@ interface UseCheckoutFormResult {
500
507
  setPhone: (v: string) => void;
501
508
  cpf: string;
502
509
  setCpf: (v: string) => void;
510
+ postalCode: string;
511
+ setPostalCode: (v: string) => void;
512
+ addressNumber: string;
513
+ setAddressNumber: (v: string) => void;
503
514
  method: CheckoutFormMethod;
504
515
  setMethod: (m: CheckoutFormMethod) => void;
505
516
  cycle: CheckoutFormCycle;
@@ -511,11 +522,15 @@ interface UseCheckoutFormResult {
511
522
  emailConfirmError: string | null;
512
523
  phoneError: string | null;
513
524
  cpfError: string | null;
525
+ postalCodeError: string | null;
526
+ addressNumberError: string | null;
514
527
  markNameTouched: () => void;
515
528
  markEmailTouched: () => void;
516
529
  markEmailConfirmTouched: () => void;
517
530
  markPhoneTouched: () => void;
518
531
  markCpfTouched: () => void;
532
+ markPostalCodeTouched: () => void;
533
+ markAddressNumberTouched: () => void;
519
534
  /**
520
535
  * Result of the debounced server-side email-exists check, used to swap
521
536
  * the CTA into "Email já cadastrado · Entrar" on the checkout form.
@@ -539,8 +554,15 @@ interface UseCheckoutFormResult {
539
554
  interface UseCheckoutFormArgs {
540
555
  defaultMethod: CheckoutFormMethod;
541
556
  defaultCycle: CheckoutFormCycle;
557
+ /**
558
+ * Quando true, o submit envia postalCode + addressNumber reais coletados
559
+ * via setPostalCode/setAddressNumber. Quando false (default), envia
560
+ * placeholder `01001000`/`100`. CheckoutPageDefault liga via config
561
+ * `paywall.collectCep`.
562
+ */
563
+ collectCep?: boolean;
542
564
  }
543
- declare function useCheckoutForm(args: UseCheckoutFormArgs): UseCheckoutFormResult;
565
+ declare function useCheckoutForm(hookArgs: UseCheckoutFormArgs): UseCheckoutFormResult;
544
566
 
545
567
  type SubscriptionStatus = 'active' | 'trialing' | 'expired' | 'canceled' | 'past_due' | 'pending' | 'none';
546
568
  type PaymentMethod = CheckoutMethod$1;
@@ -1082,6 +1104,7 @@ declare const AppConfigSchema: z.ZodObject<{
1082
1104
  "pix-once": "pix-once";
1083
1105
  }>>;
1084
1106
  requiresCpf: z.ZodBoolean;
1107
+ collectCep: z.ZodOptional<z.ZodBoolean>;
1085
1108
  cancelWindowDays: z.ZodOptional<z.ZodNumber>;
1086
1109
  errorMessages: z.ZodEnum<{
1087
1110
  default: "default";
package/dist/index.js CHANGED
@@ -58,6 +58,7 @@ var PaywallNonFreeSchema = z.object({
58
58
  }).optional(),
59
59
  checkoutMethods: z.array(z.enum(["card", "pix-auto", "pix-once"])).min(1),
60
60
  requiresCpf: z.boolean(),
61
+ collectCep: z.boolean().optional(),
61
62
  cancelWindowDays: z.number().int().nonnegative().optional(),
62
63
  errorMessages: z.enum(["default", "custom"])
63
64
  });
@@ -2864,15 +2865,17 @@ function mapSdkError(err) {
2864
2865
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2865
2866
  var PHONE_RE = /^[0-9()+\-\s]{8,20}$/;
2866
2867
  var CHECK_DEBOUNCE_MS = 400;
2867
- function useCheckoutForm(args) {
2868
+ function useCheckoutForm(hookArgs) {
2868
2869
  const { auth } = useHook10();
2869
2870
  const [name, setName] = useState9("");
2870
2871
  const [email, setEmail] = useState9("");
2871
2872
  const [emailConfirm, setEmailConfirm] = useState9("");
2872
2873
  const [phone, setPhone] = useState9("");
2873
2874
  const [cpf, setCpf] = useState9("");
2874
- const [method, setMethod] = useState9(args.defaultMethod);
2875
- const [cycle, setCycle] = useState9(args.defaultCycle);
2875
+ const [postalCode, setPostalCode] = useState9("");
2876
+ const [addressNumber, setAddressNumber] = useState9("");
2877
+ const [method, setMethod] = useState9(hookArgs.defaultMethod);
2878
+ const [cycle, setCycle] = useState9(hookArgs.defaultCycle);
2876
2879
  const [card, setCardState] = useState9({
2877
2880
  number: "",
2878
2881
  expiryMonth: "",
@@ -2888,6 +2891,8 @@ function useCheckoutForm(args) {
2888
2891
  const [touchedEmailConfirm, setTouchedEmailConfirm] = useState9(false);
2889
2892
  const [touchedPhone, setTouchedPhone] = useState9(false);
2890
2893
  const [touchedCpf, setTouchedCpf] = useState9(false);
2894
+ const [touchedPostalCode, setTouchedPostalCode] = useState9(false);
2895
+ const [touchedAddressNumber, setTouchedAddressNumber] = useState9(false);
2891
2896
  const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
2892
2897
  const [submitting, setSubmitting] = useState9(false);
2893
2898
  const [error, setError] = useState9(null);
@@ -2941,13 +2946,29 @@ function useCheckoutForm(args) {
2941
2946
  const ok = mod11(digits, 9) === digits[9] && mod11(digits, 10) === digits[10];
2942
2947
  return ok ? null : "CPF inv\xE1lido.";
2943
2948
  }, [cpf]);
2949
+ const validatePostalCode = useMemo4(() => {
2950
+ if (!hookArgs.collectCep) return null;
2951
+ if (postalCode.length === 0) return null;
2952
+ const digits = postalCode.replace(/\D/g, "");
2953
+ if (digits.length !== 8) return "CEP deve ter 8 d\xEDgitos.";
2954
+ return null;
2955
+ }, [hookArgs.collectCep, postalCode]);
2956
+ const validateAddressNumber = useMemo4(() => {
2957
+ if (!hookArgs.collectCep) return null;
2958
+ if (addressNumber.length === 0) return null;
2959
+ if (addressNumber.trim().length === 0) return "N\xFAmero obrigat\xF3rio.";
2960
+ return null;
2961
+ }, [hookArgs.collectCep, addressNumber]);
2944
2962
  const nameError = touchedName || formSubmitAttempted ? validateName : null;
2945
2963
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2946
2964
  const emailConfirmError = touchedEmailConfirm || formSubmitAttempted ? validateEmailConfirm : null;
2947
2965
  const phoneError = touchedPhone || formSubmitAttempted ? validatePhone : null;
2948
2966
  const cpfError = touchedCpf || formSubmitAttempted ? validateCpf : null;
2967
+ const postalCodeError = touchedPostalCode || formSubmitAttempted ? validatePostalCode : null;
2968
+ const addressNumberError = touchedAddressNumber || formSubmitAttempted ? validateAddressNumber : null;
2949
2969
  const phoneOk = method === "pix-auto" ? phone === "" || PHONE_RE.test(phone) : PHONE_RE.test(phone);
2950
- const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && phoneOk && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2970
+ const cepGateOk = !hookArgs.collectCep || method !== "card" || postalCode.replace(/\D/g, "").length === 8 && addressNumber.trim().length > 0;
2971
+ const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && phoneOk && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && cepGateOk && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2951
2972
  const submit = useCallback6(async () => {
2952
2973
  setFormSubmitAttempted(true);
2953
2974
  setError(null);
@@ -2956,9 +2977,9 @@ function useCheckoutForm(args) {
2956
2977
  if (!canSubmit) return null;
2957
2978
  setSubmitting(true);
2958
2979
  try {
2959
- let args2;
2980
+ let args;
2960
2981
  if (method === "card") {
2961
- args2 = {
2982
+ args = {
2962
2983
  method: "card",
2963
2984
  name: name.trim(),
2964
2985
  email,
@@ -2977,21 +2998,17 @@ function useCheckoutForm(args) {
2977
2998
  name: card.holderName || name.trim(),
2978
2999
  email,
2979
3000
  cpfCnpj: cpf.replace(/\D/g, ""),
2980
- // Plan-V 0.28.4 Asaas's `creditCardHolderInfo.postalCode`
2981
- // rejects all-zeros with `tokenize_failed:invalid_holderInfo`
2982
- // ("O CEP informado é inválido."). The default CheckoutPage
2983
- // doesn't collect CEP from the user (reference design skipped
2984
- // it), so ship a known-valid placeholder. Apps that need real
2985
- // customer addresses must override CheckoutPageDefault and
2986
- // collect CEP — see personalburn's PaywallStepPagamento for
2987
- // the pattern.
2988
- postalCode: "01001000",
2989
- addressNumber: "100",
3001
+ // collectCep=true CEP/número reais do form. Senão placeholder
3002
+ // válido (Asaas rejeita all-zeros, então shipping 01001000/100
3003
+ // que são válidos mas anonimizam). Apps com NF-e ou antifraude
3004
+ // refinada devem setar paywall.collectCep no app.config.json.
3005
+ postalCode: hookArgs.collectCep ? postalCode.replace(/\D/g, "") : "01001000",
3006
+ addressNumber: hookArgs.collectCep ? addressNumber.trim() : "100",
2990
3007
  phone
2991
3008
  }
2992
3009
  };
2993
3010
  } else {
2994
- args2 = {
3011
+ args = {
2995
3012
  method: "pix-auto",
2996
3013
  name: name.trim(),
2997
3014
  email,
@@ -3001,7 +3018,7 @@ function useCheckoutForm(args) {
3001
3018
  cycle
3002
3019
  };
3003
3020
  }
3004
- const result = await auth.subscribeAnonymous(args2);
3021
+ const result = await auth.subscribeAnonymous(args);
3005
3022
  return result;
3006
3023
  } catch (err) {
3007
3024
  if (err instanceof EmailTakenError) {
@@ -3027,6 +3044,10 @@ function useCheckoutForm(args) {
3027
3044
  setPhone,
3028
3045
  cpf,
3029
3046
  setCpf,
3047
+ postalCode,
3048
+ setPostalCode,
3049
+ addressNumber,
3050
+ setAddressNumber,
3030
3051
  method,
3031
3052
  setMethod,
3032
3053
  cycle,
@@ -3038,11 +3059,15 @@ function useCheckoutForm(args) {
3038
3059
  emailConfirmError,
3039
3060
  phoneError,
3040
3061
  cpfError,
3062
+ postalCodeError,
3063
+ addressNumberError,
3041
3064
  markNameTouched: () => setTouchedName(true),
3042
3065
  markEmailTouched: () => setTouchedEmail(true),
3043
3066
  markEmailConfirmTouched: () => setTouchedEmailConfirm(true),
3044
3067
  markPhoneTouched: () => setTouchedPhone(true),
3045
3068
  markCpfTouched: () => setTouchedCpf(true),
3069
+ markPostalCodeTouched: () => setTouchedPostalCode(true),
3070
+ markAddressNumberTouched: () => setTouchedAddressNumber(true),
3046
3071
  emailStatus,
3047
3072
  submit,
3048
3073
  submitting,
@@ -3869,10 +3894,12 @@ function detectCardBrand(num) {
3869
3894
  function CheckoutPageDefault() {
3870
3895
  const navigate = useNavigate2();
3871
3896
  const plan = usePlan();
3897
+ const config = useAppConfig();
3872
3898
  const intent = useMemo6(readIntent, []);
3873
3899
  const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
3874
3900
  const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
3875
- const form = useCheckoutForm({ defaultMethod, defaultCycle });
3901
+ const collectCep = config.paywall.mode !== "free" && config.paywall.collectCep === true;
3902
+ const form = useCheckoutForm({ defaultMethod, defaultCycle, collectCep });
3876
3903
  const [expiryMmAa, setExpiryMmAa] = useState11("");
3877
3904
  useEffect13(() => {
3878
3905
  const { month, year } = parseExpiryMmAa(expiryMmAa);
@@ -4056,6 +4083,45 @@ function CheckoutPageDefault() {
4056
4083
  }
4057
4084
  ),
4058
4085
  !form.phoneError && /* @__PURE__ */ jsx48(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
4086
+ ] }) : null,
4087
+ collectCep && form.method === "card" ? /* @__PURE__ */ jsxs29(Fragment7, { children: [
4088
+ /* @__PURE__ */ jsxs29("div", { children: [
4089
+ /* @__PURE__ */ jsx48(FieldLabel, { children: "CEP" }),
4090
+ /* @__PURE__ */ jsx48(
4091
+ FieldInput,
4092
+ {
4093
+ type: "text",
4094
+ inputMode: "numeric",
4095
+ autoComplete: "postal-code",
4096
+ placeholder: "00000-000",
4097
+ value: form.postalCode,
4098
+ onChange: (v) => {
4099
+ const digits = v.replace(/\D/g, "").slice(0, 8);
4100
+ const formatted = digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits;
4101
+ form.setPostalCode(formatted);
4102
+ },
4103
+ onBlur: form.markPostalCodeTouched,
4104
+ error: form.postalCodeError,
4105
+ valid: !!form.postalCode && !form.postalCodeError
4106
+ }
4107
+ )
4108
+ ] }),
4109
+ /* @__PURE__ */ jsxs29("div", { children: [
4110
+ /* @__PURE__ */ jsx48(FieldLabel, { children: "N\xFAmero" }),
4111
+ /* @__PURE__ */ jsx48(
4112
+ FieldInput,
4113
+ {
4114
+ type: "text",
4115
+ inputMode: "numeric",
4116
+ placeholder: "100",
4117
+ value: form.addressNumber,
4118
+ onChange: form.setAddressNumber,
4119
+ onBlur: form.markAddressNumberTouched,
4120
+ error: form.addressNumberError,
4121
+ valid: !!form.addressNumber && !form.addressNumberError
4122
+ }
4123
+ )
4124
+ ] })
4059
4125
  ] }) : null
4060
4126
  ] })
4061
4127
  ] }),