@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.cjs CHANGED
@@ -169,6 +169,7 @@ var PaywallNonFreeSchema = import_zod.z.object({
169
169
  }).optional(),
170
170
  checkoutMethods: import_zod.z.array(import_zod.z.enum(["card", "pix-auto", "pix-once"])).min(1),
171
171
  requiresCpf: import_zod.z.boolean(),
172
+ collectCep: import_zod.z.boolean().optional(),
172
173
  cancelWindowDays: import_zod.z.number().int().nonnegative().optional(),
173
174
  errorMessages: import_zod.z.enum(["default", "custom"])
174
175
  });
@@ -2972,15 +2973,17 @@ function mapSdkError(err) {
2972
2973
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2973
2974
  var PHONE_RE = /^[0-9()+\-\s]{8,20}$/;
2974
2975
  var CHECK_DEBOUNCE_MS = 400;
2975
- function useCheckoutForm(args) {
2976
+ function useCheckoutForm(hookArgs) {
2976
2977
  const { auth } = (0, import_sdk13.useHook)();
2977
2978
  const [name, setName] = (0, import_react18.useState)("");
2978
2979
  const [email, setEmail] = (0, import_react18.useState)("");
2979
2980
  const [emailConfirm, setEmailConfirm] = (0, import_react18.useState)("");
2980
2981
  const [phone, setPhone] = (0, import_react18.useState)("");
2981
2982
  const [cpf, setCpf] = (0, import_react18.useState)("");
2982
- const [method, setMethod] = (0, import_react18.useState)(args.defaultMethod);
2983
- const [cycle, setCycle] = (0, import_react18.useState)(args.defaultCycle);
2983
+ const [postalCode, setPostalCode] = (0, import_react18.useState)("");
2984
+ const [addressNumber, setAddressNumber] = (0, import_react18.useState)("");
2985
+ const [method, setMethod] = (0, import_react18.useState)(hookArgs.defaultMethod);
2986
+ const [cycle, setCycle] = (0, import_react18.useState)(hookArgs.defaultCycle);
2984
2987
  const [card, setCardState] = (0, import_react18.useState)({
2985
2988
  number: "",
2986
2989
  expiryMonth: "",
@@ -2996,6 +2999,8 @@ function useCheckoutForm(args) {
2996
2999
  const [touchedEmailConfirm, setTouchedEmailConfirm] = (0, import_react18.useState)(false);
2997
3000
  const [touchedPhone, setTouchedPhone] = (0, import_react18.useState)(false);
2998
3001
  const [touchedCpf, setTouchedCpf] = (0, import_react18.useState)(false);
3002
+ const [touchedPostalCode, setTouchedPostalCode] = (0, import_react18.useState)(false);
3003
+ const [touchedAddressNumber, setTouchedAddressNumber] = (0, import_react18.useState)(false);
2999
3004
  const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
3000
3005
  const [submitting, setSubmitting] = (0, import_react18.useState)(false);
3001
3006
  const [error, setError] = (0, import_react18.useState)(null);
@@ -3049,13 +3054,29 @@ function useCheckoutForm(args) {
3049
3054
  const ok = mod11(digits, 9) === digits[9] && mod11(digits, 10) === digits[10];
3050
3055
  return ok ? null : "CPF inv\xE1lido.";
3051
3056
  }, [cpf]);
3057
+ const validatePostalCode = (0, import_react18.useMemo)(() => {
3058
+ if (!hookArgs.collectCep) return null;
3059
+ if (postalCode.length === 0) return null;
3060
+ const digits = postalCode.replace(/\D/g, "");
3061
+ if (digits.length !== 8) return "CEP deve ter 8 d\xEDgitos.";
3062
+ return null;
3063
+ }, [hookArgs.collectCep, postalCode]);
3064
+ const validateAddressNumber = (0, import_react18.useMemo)(() => {
3065
+ if (!hookArgs.collectCep) return null;
3066
+ if (addressNumber.length === 0) return null;
3067
+ if (addressNumber.trim().length === 0) return "N\xFAmero obrigat\xF3rio.";
3068
+ return null;
3069
+ }, [hookArgs.collectCep, addressNumber]);
3052
3070
  const nameError = touchedName || formSubmitAttempted ? validateName : null;
3053
3071
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
3054
3072
  const emailConfirmError = touchedEmailConfirm || formSubmitAttempted ? validateEmailConfirm : null;
3055
3073
  const phoneError = touchedPhone || formSubmitAttempted ? validatePhone : null;
3056
3074
  const cpfError = touchedCpf || formSubmitAttempted ? validateCpf : null;
3075
+ const postalCodeError = touchedPostalCode || formSubmitAttempted ? validatePostalCode : null;
3076
+ const addressNumberError = touchedAddressNumber || formSubmitAttempted ? validateAddressNumber : null;
3057
3077
  const phoneOk = method === "pix-auto" ? phone === "" || PHONE_RE.test(phone) : PHONE_RE.test(phone);
3058
- 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);
3078
+ const cepGateOk = !hookArgs.collectCep || method !== "card" || postalCode.replace(/\D/g, "").length === 8 && addressNumber.trim().length > 0;
3079
+ 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);
3059
3080
  const submit = (0, import_react18.useCallback)(async () => {
3060
3081
  setFormSubmitAttempted(true);
3061
3082
  setError(null);
@@ -3064,9 +3085,9 @@ function useCheckoutForm(args) {
3064
3085
  if (!canSubmit) return null;
3065
3086
  setSubmitting(true);
3066
3087
  try {
3067
- let args2;
3088
+ let args;
3068
3089
  if (method === "card") {
3069
- args2 = {
3090
+ args = {
3070
3091
  method: "card",
3071
3092
  name: name.trim(),
3072
3093
  email,
@@ -3085,21 +3106,17 @@ function useCheckoutForm(args) {
3085
3106
  name: card.holderName || name.trim(),
3086
3107
  email,
3087
3108
  cpfCnpj: cpf.replace(/\D/g, ""),
3088
- // Plan-V 0.28.4 Asaas's `creditCardHolderInfo.postalCode`
3089
- // rejects all-zeros with `tokenize_failed:invalid_holderInfo`
3090
- // ("O CEP informado é inválido."). The default CheckoutPage
3091
- // doesn't collect CEP from the user (reference design skipped
3092
- // it), so ship a known-valid placeholder. Apps that need real
3093
- // customer addresses must override CheckoutPageDefault and
3094
- // collect CEP — see personalburn's PaywallStepPagamento for
3095
- // the pattern.
3096
- postalCode: "01001000",
3097
- addressNumber: "100",
3109
+ // collectCep=true CEP/número reais do form. Senão placeholder
3110
+ // válido (Asaas rejeita all-zeros, então shipping 01001000/100
3111
+ // que são válidos mas anonimizam). Apps com NF-e ou antifraude
3112
+ // refinada devem setar paywall.collectCep no app.config.json.
3113
+ postalCode: hookArgs.collectCep ? postalCode.replace(/\D/g, "") : "01001000",
3114
+ addressNumber: hookArgs.collectCep ? addressNumber.trim() : "100",
3098
3115
  phone
3099
3116
  }
3100
3117
  };
3101
3118
  } else {
3102
- args2 = {
3119
+ args = {
3103
3120
  method: "pix-auto",
3104
3121
  name: name.trim(),
3105
3122
  email,
@@ -3109,7 +3126,7 @@ function useCheckoutForm(args) {
3109
3126
  cycle
3110
3127
  };
3111
3128
  }
3112
- const result = await auth.subscribeAnonymous(args2);
3129
+ const result = await auth.subscribeAnonymous(args);
3113
3130
  return result;
3114
3131
  } catch (err) {
3115
3132
  if (err instanceof import_sdk13.EmailTakenError) {
@@ -3135,6 +3152,10 @@ function useCheckoutForm(args) {
3135
3152
  setPhone,
3136
3153
  cpf,
3137
3154
  setCpf,
3155
+ postalCode,
3156
+ setPostalCode,
3157
+ addressNumber,
3158
+ setAddressNumber,
3138
3159
  method,
3139
3160
  setMethod,
3140
3161
  cycle,
@@ -3146,11 +3167,15 @@ function useCheckoutForm(args) {
3146
3167
  emailConfirmError,
3147
3168
  phoneError,
3148
3169
  cpfError,
3170
+ postalCodeError,
3171
+ addressNumberError,
3149
3172
  markNameTouched: () => setTouchedName(true),
3150
3173
  markEmailTouched: () => setTouchedEmail(true),
3151
3174
  markEmailConfirmTouched: () => setTouchedEmailConfirm(true),
3152
3175
  markPhoneTouched: () => setTouchedPhone(true),
3153
3176
  markCpfTouched: () => setTouchedCpf(true),
3177
+ markPostalCodeTouched: () => setTouchedPostalCode(true),
3178
+ markAddressNumberTouched: () => setTouchedAddressNumber(true),
3154
3179
  emailStatus,
3155
3180
  submit,
3156
3181
  submitting,
@@ -3977,10 +4002,12 @@ function detectCardBrand(num) {
3977
4002
  function CheckoutPageDefault() {
3978
4003
  const navigate = (0, import_react_router_dom3.useNavigate)();
3979
4004
  const plan = usePlan();
4005
+ const config = useAppConfig();
3980
4006
  const intent = (0, import_react23.useMemo)(readIntent, []);
3981
4007
  const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
3982
4008
  const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
3983
- const form = useCheckoutForm({ defaultMethod, defaultCycle });
4009
+ const collectCep = config.paywall.mode !== "free" && config.paywall.collectCep === true;
4010
+ const form = useCheckoutForm({ defaultMethod, defaultCycle, collectCep });
3984
4011
  const [expiryMmAa, setExpiryMmAa] = (0, import_react23.useState)("");
3985
4012
  (0, import_react23.useEffect)(() => {
3986
4013
  const { month, year } = parseExpiryMmAa(expiryMmAa);
@@ -4164,6 +4191,45 @@ function CheckoutPageDefault() {
4164
4191
  }
4165
4192
  ),
4166
4193
  !form.phoneError && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(FieldHint, { children: "Usado pra confirmar pagamento e tratar disputas." })
4194
+ ] }) : null,
4195
+ collectCep && form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
4196
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { children: [
4197
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(FieldLabel, { children: "CEP" }),
4198
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
4199
+ FieldInput,
4200
+ {
4201
+ type: "text",
4202
+ inputMode: "numeric",
4203
+ autoComplete: "postal-code",
4204
+ placeholder: "00000-000",
4205
+ value: form.postalCode,
4206
+ onChange: (v) => {
4207
+ const digits = v.replace(/\D/g, "").slice(0, 8);
4208
+ const formatted = digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits;
4209
+ form.setPostalCode(formatted);
4210
+ },
4211
+ onBlur: form.markPostalCodeTouched,
4212
+ error: form.postalCodeError,
4213
+ valid: !!form.postalCode && !form.postalCodeError
4214
+ }
4215
+ )
4216
+ ] }),
4217
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { children: [
4218
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(FieldLabel, { children: "N\xFAmero" }),
4219
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
4220
+ FieldInput,
4221
+ {
4222
+ type: "text",
4223
+ inputMode: "numeric",
4224
+ placeholder: "100",
4225
+ value: form.addressNumber,
4226
+ onChange: form.setAddressNumber,
4227
+ onBlur: form.markAddressNumberTouched,
4228
+ error: form.addressNumberError,
4229
+ valid: !!form.addressNumber && !form.addressNumberError
4230
+ }
4231
+ )
4232
+ ] })
4167
4233
  ] }) : null
4168
4234
  ] })
4169
4235
  ] }),