@hook-sdk/template 0.8.1 → 0.9.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.
- package/dist/index.cjs +538 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -17
- package/dist/index.d.ts +30 -17
- package/dist/index.js +555 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -62,7 +62,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
62
62
|
|
|
63
63
|
// src/AppRoot.tsx
|
|
64
64
|
var import_react15 = require("react");
|
|
65
|
-
var
|
|
65
|
+
var import_sdk10 = require("@hook-sdk/sdk");
|
|
66
66
|
|
|
67
67
|
// src/internal/TemplateConfigContext.tsx
|
|
68
68
|
var import_react = require("react");
|
|
@@ -157,50 +157,66 @@ function AuthGate({ Login, Signup, Forgot, Reset, children }) {
|
|
|
157
157
|
var import_react3 = require("react");
|
|
158
158
|
var import_sdk2 = require("@hook-sdk/sdk");
|
|
159
159
|
function usePaywallState() {
|
|
160
|
-
const { subscription } = (0, import_sdk2.useHook)();
|
|
160
|
+
const { subscription, plan } = (0, import_sdk2.useHook)();
|
|
161
161
|
const [opening, setOpening] = (0, import_react3.useState)(false);
|
|
162
162
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
163
163
|
const [pixPending, setPixPending] = (0, import_react3.useState)(null);
|
|
164
164
|
const status = subscription.status();
|
|
165
165
|
const daysLeftInTrial = subscription.daysLeftInTrial();
|
|
166
166
|
const initialLoadComplete = subscription.initialLoadComplete;
|
|
167
|
+
const availableMethods = (0, import_react3.useMemo)(
|
|
168
|
+
() => ["card", "pix-auto"],
|
|
169
|
+
[]
|
|
170
|
+
);
|
|
171
|
+
const priceCents = plan.data?.priceCents ?? 0;
|
|
172
|
+
const yearlyPriceCents = plan.data?.yearlyPriceCents ?? null;
|
|
173
|
+
const monthlyEquivalent = (0, import_react3.useCallback)(
|
|
174
|
+
(cycle) => {
|
|
175
|
+
if (cycle === "YEARLY" && yearlyPriceCents) {
|
|
176
|
+
return Math.round(yearlyPriceCents / 12);
|
|
177
|
+
}
|
|
178
|
+
return priceCents;
|
|
179
|
+
},
|
|
180
|
+
[priceCents, yearlyPriceCents]
|
|
181
|
+
);
|
|
167
182
|
const checkout = (0, import_react3.useCallback)(
|
|
168
183
|
async (args) => {
|
|
169
184
|
setOpening(true);
|
|
170
185
|
setError(null);
|
|
171
186
|
setPixPending(null);
|
|
172
|
-
const method = args.method ?? "card";
|
|
173
|
-
const cycle = args.cycle ?? "MONTHLY";
|
|
174
187
|
try {
|
|
175
|
-
if (method === "card") {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
expiresAt: null,
|
|
187
|
-
paid: false
|
|
188
|
+
if (args.method === "card") {
|
|
189
|
+
if (!args.card || !args.holderInfo) {
|
|
190
|
+
throw new Error('card and holderInfo are required when method is "card"');
|
|
191
|
+
}
|
|
192
|
+
await subscription.checkout({
|
|
193
|
+
method: "card",
|
|
194
|
+
cycle: args.cycle,
|
|
195
|
+
cpf: args.cpf,
|
|
196
|
+
card: args.card,
|
|
197
|
+
holderInfo: args.holderInfo,
|
|
198
|
+
...args.remoteIp ? { remoteIp: args.remoteIp } : {}
|
|
188
199
|
});
|
|
200
|
+
await subscription.refresh();
|
|
189
201
|
setOpening(false);
|
|
190
202
|
return;
|
|
191
203
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
paid: false
|
|
200
|
-
});
|
|
201
|
-
setOpening(false);
|
|
202
|
-
return;
|
|
204
|
+
const result = await subscription.checkout({
|
|
205
|
+
method: "pix-auto",
|
|
206
|
+
cycle: args.cycle,
|
|
207
|
+
cpf: args.cpf
|
|
208
|
+
});
|
|
209
|
+
if (result.method !== "pix-auto") {
|
|
210
|
+
throw new Error(`unexpected checkout result method: ${result.method}`);
|
|
203
211
|
}
|
|
212
|
+
setPixPending({
|
|
213
|
+
method: "pix-auto",
|
|
214
|
+
qrCodePayload: result.qrCodePayload,
|
|
215
|
+
qrCodeBase64: result.qrCodeBase64,
|
|
216
|
+
expiresAt: null,
|
|
217
|
+
paid: false
|
|
218
|
+
});
|
|
219
|
+
setOpening(false);
|
|
204
220
|
} catch (err) {
|
|
205
221
|
setError(err);
|
|
206
222
|
setOpening(false);
|
|
@@ -253,7 +269,9 @@ function usePaywallState() {
|
|
|
253
269
|
opening,
|
|
254
270
|
error,
|
|
255
271
|
pixPending,
|
|
256
|
-
dismissPix
|
|
272
|
+
dismissPix,
|
|
273
|
+
availableMethods,
|
|
274
|
+
monthlyEquivalent
|
|
257
275
|
};
|
|
258
276
|
}
|
|
259
277
|
|
|
@@ -2198,33 +2216,429 @@ function DefaultResetScreen({ onNavigate }) {
|
|
|
2198
2216
|
|
|
2199
2217
|
// src/defaults/DefaultPaywall.tsx
|
|
2200
2218
|
var import_react14 = require("react");
|
|
2219
|
+
|
|
2220
|
+
// src/hooks/usePlan.ts
|
|
2221
|
+
var import_sdk9 = require("@hook-sdk/sdk");
|
|
2222
|
+
function usePlan() {
|
|
2223
|
+
const { plan } = (0, import_sdk9.useHook)();
|
|
2224
|
+
return plan;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
// src/utils/price.ts
|
|
2228
|
+
function formatBRL(cents) {
|
|
2229
|
+
if (cents === null || cents === void 0) return "";
|
|
2230
|
+
const reais = cents / 100;
|
|
2231
|
+
return new Intl.NumberFormat("pt-BR", {
|
|
2232
|
+
style: "currency",
|
|
2233
|
+
currency: "BRL"
|
|
2234
|
+
}).format(reais);
|
|
2235
|
+
}
|
|
2236
|
+
function monthlyFromYearly(yearlyCents) {
|
|
2237
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2238
|
+
return Math.round(yearlyCents / 12);
|
|
2239
|
+
}
|
|
2240
|
+
function dailyFromYearly(yearlyCents) {
|
|
2241
|
+
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2242
|
+
return Math.round(yearlyCents / 365);
|
|
2243
|
+
}
|
|
2244
|
+
function computeAnchorCents(baseCents, multiplier) {
|
|
2245
|
+
if (multiplier === null || multiplier === void 0) return null;
|
|
2246
|
+
if (!Number.isFinite(multiplier)) return null;
|
|
2247
|
+
if (multiplier <= 1) return null;
|
|
2248
|
+
return Math.round(baseCents * multiplier);
|
|
2249
|
+
}
|
|
2250
|
+
function discountPercent(anchorCents, realCents) {
|
|
2251
|
+
if (anchorCents <= realCents) return 0;
|
|
2252
|
+
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
// src/defaults/DefaultPaywall.tsx
|
|
2201
2256
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2202
2257
|
function DefaultPaywall() {
|
|
2203
2258
|
const config = useTemplateConfig();
|
|
2204
|
-
const
|
|
2259
|
+
const plan = usePlan();
|
|
2260
|
+
const {
|
|
2261
|
+
checkout,
|
|
2262
|
+
opening,
|
|
2263
|
+
error,
|
|
2264
|
+
availableMethods,
|
|
2265
|
+
monthlyEquivalent,
|
|
2266
|
+
pixPending,
|
|
2267
|
+
dismissPix
|
|
2268
|
+
} = usePaywallState();
|
|
2205
2269
|
const p = config.subscription.paywall_config;
|
|
2270
|
+
const paywallCfg = plan.data?.paywallConfig ?? {};
|
|
2271
|
+
const anchorMultiplier = paywallCfg.anchorMultiplier ?? p.anchorMultiplier;
|
|
2272
|
+
const anchorPriceCents = paywallCfg.anchorPriceCents ?? p.anchorPriceCents;
|
|
2273
|
+
const [cycle, setCycle] = (0, import_react14.useState)("MONTHLY");
|
|
2274
|
+
const [method, setMethod] = (0, import_react14.useState)("card");
|
|
2206
2275
|
const [cpf, setCpf] = (0, import_react14.useState)("");
|
|
2276
|
+
const [cardNumber, setCardNumber] = (0, import_react14.useState)("");
|
|
2277
|
+
const [cardHolderName, setCardHolderName] = (0, import_react14.useState)("");
|
|
2278
|
+
const [cardExpiryMonth, setCardExpiryMonth] = (0, import_react14.useState)("");
|
|
2279
|
+
const [cardExpiryYear, setCardExpiryYear] = (0, import_react14.useState)("");
|
|
2280
|
+
const [cardCcv, setCardCcv] = (0, import_react14.useState)("");
|
|
2281
|
+
const [holderName, setHolderName] = (0, import_react14.useState)("");
|
|
2282
|
+
const [holderEmail, setHolderEmail] = (0, import_react14.useState)("");
|
|
2283
|
+
const [holderPostalCode, setHolderPostalCode] = (0, import_react14.useState)("");
|
|
2284
|
+
const [holderAddressNumber, setHolderAddressNumber] = (0, import_react14.useState)("");
|
|
2285
|
+
const [holderPhone, setHolderPhone] = (0, import_react14.useState)("");
|
|
2286
|
+
const trialDays = plan.data?.trialDays ?? 0;
|
|
2287
|
+
const monthlyCents = plan.data?.priceCents ?? 0;
|
|
2288
|
+
const yearlyCents = plan.data?.yearlyPriceCents ?? null;
|
|
2289
|
+
const activeCents = cycle === "YEARLY" && yearlyCents ? monthlyEquivalent("YEARLY") : monthlyCents;
|
|
2290
|
+
const pct = (0, import_react14.useMemo)(() => {
|
|
2291
|
+
if (!yearlyCents || !monthlyCents) return 0;
|
|
2292
|
+
const derived = Math.round((1 - yearlyCents / 12 / monthlyCents) * 100);
|
|
2293
|
+
return Math.max(0, derived);
|
|
2294
|
+
}, [monthlyCents, yearlyCents]);
|
|
2295
|
+
const anchorBaseCents = cycle === "YEARLY" && yearlyCents ? monthlyFromYearly(yearlyCents) : monthlyCents;
|
|
2296
|
+
const anchorCents = computeAnchorCents(anchorBaseCents, anchorMultiplier) ?? (anchorPriceCents && anchorPriceCents > anchorBaseCents ? anchorPriceCents : null);
|
|
2297
|
+
const anchorDiscount = anchorCents ? discountPercent(anchorCents, activeCents) : 0;
|
|
2207
2298
|
const cpfDigits = cpf.replace(/\D/g, "");
|
|
2208
|
-
const
|
|
2209
|
-
|
|
2299
|
+
const cardFieldsFilled = cardNumber.replace(/\s/g, "").length >= 13 && cardHolderName.trim().length > 0 && cardExpiryMonth.length === 2 && cardExpiryYear.length >= 2 && cardCcv.length >= 3 && holderName.trim().length > 0 && /.+@.+\..+/.test(holderEmail) && holderPostalCode.replace(/\D/g, "").length === 8 && holderAddressNumber.trim().length > 0;
|
|
2300
|
+
const canCheckout = cpfDigits.length === 11 && !opening && (method === "pix-auto" || cardFieldsFilled);
|
|
2301
|
+
const ctaLabel = (0, import_react14.useMemo)(() => {
|
|
2302
|
+
if (opening) return "Abrindo\u2026";
|
|
2303
|
+
if (trialDays > 0) return `Comece trial de ${trialDays} dias gr\xE1tis`;
|
|
2304
|
+
return p.cta ?? "Assinar agora";
|
|
2305
|
+
}, [opening, trialDays, p.cta]);
|
|
2306
|
+
const footer = (0, import_react14.useMemo)(() => {
|
|
2307
|
+
if (trialDays > 0) {
|
|
2308
|
+
return `Sem cobran\xE7a agora. Cobran\xE7a autom\xE1tica em ${trialDays} dias. Cancele quando quiser.`;
|
|
2309
|
+
}
|
|
2310
|
+
return "Cobran\xE7a imediata. Cancele quando quiser.";
|
|
2311
|
+
}, [trialDays]);
|
|
2312
|
+
const yearSuffix = cardExpiryYear.length === 2 ? `20${cardExpiryYear}` : cardExpiryYear;
|
|
2313
|
+
const phoneDigits = holderPhone.replace(/\D/g, "");
|
|
2314
|
+
const postalDigits = holderPostalCode.replace(/\D/g, "");
|
|
2315
|
+
const submit = () => {
|
|
2316
|
+
if (method === "card") {
|
|
2317
|
+
void checkout({
|
|
2318
|
+
cpf: cpfDigits,
|
|
2319
|
+
cycle,
|
|
2320
|
+
method: "card",
|
|
2321
|
+
card: {
|
|
2322
|
+
number: cardNumber.replace(/\s/g, ""),
|
|
2323
|
+
holderName: cardHolderName.trim(),
|
|
2324
|
+
expiryMonth: cardExpiryMonth,
|
|
2325
|
+
expiryYear: yearSuffix,
|
|
2326
|
+
ccv: cardCcv
|
|
2327
|
+
},
|
|
2328
|
+
holderInfo: {
|
|
2329
|
+
name: holderName.trim(),
|
|
2330
|
+
email: holderEmail.trim(),
|
|
2331
|
+
cpfCnpj: cpfDigits,
|
|
2332
|
+
postalCode: postalDigits,
|
|
2333
|
+
addressNumber: holderAddressNumber.trim(),
|
|
2334
|
+
...phoneDigits ? { phone: phoneDigits } : {}
|
|
2335
|
+
}
|
|
2336
|
+
});
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2339
|
+
void checkout({ cpf: cpfDigits, cycle, method: "pix-auto" });
|
|
2340
|
+
};
|
|
2341
|
+
const inputStyle = {
|
|
2342
|
+
width: "100%",
|
|
2343
|
+
padding: 10,
|
|
2344
|
+
fontSize: 14,
|
|
2345
|
+
borderRadius: 8,
|
|
2346
|
+
border: "1px solid #ccc",
|
|
2347
|
+
boxSizing: "border-box"
|
|
2348
|
+
};
|
|
2349
|
+
const labelStyle = { display: "block", fontSize: 13, opacity: 0.75, marginBottom: 4 };
|
|
2350
|
+
const fieldGroup = { marginBottom: 10, textAlign: "left" };
|
|
2351
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("main", { style: { padding: 24, maxWidth: 480, margin: "0 auto", textAlign: "center" }, children: [
|
|
2210
2352
|
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h1", { style: { marginBottom: 8 }, children: p.title }),
|
|
2211
2353
|
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
|
|
2212
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.
|
|
2354
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2355
|
+
"div",
|
|
2356
|
+
{
|
|
2357
|
+
role: "group",
|
|
2358
|
+
"aria-label": "Per\xEDodo de cobran\xE7a",
|
|
2359
|
+
style: { display: "flex", gap: 8, marginBottom: 16 },
|
|
2360
|
+
children: [
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2362
|
+
"button",
|
|
2363
|
+
{
|
|
2364
|
+
type: "button",
|
|
2365
|
+
"aria-pressed": cycle === "MONTHLY",
|
|
2366
|
+
onClick: () => setCycle("MONTHLY"),
|
|
2367
|
+
style: {
|
|
2368
|
+
flex: 1,
|
|
2369
|
+
padding: 12,
|
|
2370
|
+
borderRadius: 8,
|
|
2371
|
+
border: cycle === "MONTHLY" ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2372
|
+
background: cycle === "MONTHLY" ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2373
|
+
cursor: "pointer"
|
|
2374
|
+
},
|
|
2375
|
+
children: "Mensal"
|
|
2376
|
+
}
|
|
2377
|
+
),
|
|
2378
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2379
|
+
"button",
|
|
2380
|
+
{
|
|
2381
|
+
type: "button",
|
|
2382
|
+
"aria-pressed": cycle === "YEARLY",
|
|
2383
|
+
onClick: () => setCycle("YEARLY"),
|
|
2384
|
+
disabled: !yearlyCents,
|
|
2385
|
+
style: {
|
|
2386
|
+
flex: 1,
|
|
2387
|
+
padding: 12,
|
|
2388
|
+
borderRadius: 8,
|
|
2389
|
+
border: cycle === "YEARLY" ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2390
|
+
background: cycle === "YEARLY" ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2391
|
+
cursor: yearlyCents ? "pointer" : "not-allowed",
|
|
2392
|
+
opacity: yearlyCents ? 1 : 0.5
|
|
2393
|
+
},
|
|
2394
|
+
children: [
|
|
2395
|
+
"Anual",
|
|
2396
|
+
pct > 0 ? ` \u2212${pct}%` : ""
|
|
2397
|
+
]
|
|
2398
|
+
}
|
|
2399
|
+
)
|
|
2400
|
+
]
|
|
2401
|
+
}
|
|
2402
|
+
),
|
|
2403
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { marginBottom: 8 }, children: [
|
|
2404
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { fontSize: 32, fontWeight: 700, lineHeight: 1 }, children: [
|
|
2405
|
+
formatBRL(activeCents),
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 16, fontWeight: 400, opacity: 0.7 }, children: "/m\xEAs" })
|
|
2407
|
+
] }),
|
|
2408
|
+
anchorCents && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { fontSize: 14, opacity: 0.6, marginTop: 4 }, children: [
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { textDecoration: "line-through" }, children: formatBRL(anchorCents) }),
|
|
2410
|
+
anchorDiscount > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { style: { marginLeft: 6 }, children: [
|
|
2411
|
+
"\u2212",
|
|
2412
|
+
anchorDiscount,
|
|
2413
|
+
"%"
|
|
2414
|
+
] })
|
|
2415
|
+
] }),
|
|
2416
|
+
cycle === "YEARLY" && yearlyCents && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { fontSize: 12, opacity: 0.6, marginTop: 4 }, children: [
|
|
2417
|
+
"Cobrado ",
|
|
2418
|
+
formatBRL(yearlyCents),
|
|
2419
|
+
" por ano"
|
|
2420
|
+
] })
|
|
2421
|
+
] }),
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", margin: "24px 0" }, children: p.benefits.map((b) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
|
|
2213
2423
|
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
|
|
2214
2424
|
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: b })
|
|
2215
2425
|
] }, b)) }),
|
|
2216
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.
|
|
2217
|
-
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", style: { display: "flex", gap: 8, marginBottom: 16 }, children: availableMethods.map((m) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2427
|
+
"button",
|
|
2428
|
+
{
|
|
2429
|
+
type: "button",
|
|
2430
|
+
role: "tab",
|
|
2431
|
+
"aria-selected": method === m,
|
|
2432
|
+
onClick: () => setMethod(m),
|
|
2433
|
+
style: {
|
|
2434
|
+
flex: 1,
|
|
2435
|
+
padding: 10,
|
|
2436
|
+
borderRadius: 8,
|
|
2437
|
+
border: method === m ? "2px solid var(--hook-color-primary)" : "1px solid #ccc",
|
|
2438
|
+
background: method === m ? "var(--hook-color-primary-soft, #eef)" : "white",
|
|
2439
|
+
cursor: "pointer"
|
|
2440
|
+
},
|
|
2441
|
+
children: m === "card" ? "\u{1F4B3} Cart\xE3o" : "\u{1F4F1} Pix Autom\xE1tico"
|
|
2442
|
+
},
|
|
2443
|
+
m
|
|
2444
|
+
)) }),
|
|
2445
|
+
method === "card" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2446
|
+
"fieldset",
|
|
2447
|
+
{
|
|
2448
|
+
"data-testid": "paywall-card-form",
|
|
2449
|
+
style: { border: "none", padding: 0, marginBottom: 16 },
|
|
2450
|
+
children: [
|
|
2451
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("legend", { style: { fontSize: 13, opacity: 0.75, marginBottom: 8 }, children: "Dados do cart\xE3o" }),
|
|
2452
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2453
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-card-number", style: labelStyle, children: "N\xFAmero do cart\xE3o" }),
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2455
|
+
"input",
|
|
2456
|
+
{
|
|
2457
|
+
id: "pw-card-number",
|
|
2458
|
+
"data-testid": "pw-card-number",
|
|
2459
|
+
type: "text",
|
|
2460
|
+
inputMode: "numeric",
|
|
2461
|
+
autoComplete: "cc-number",
|
|
2462
|
+
placeholder: "0000 0000 0000 0000",
|
|
2463
|
+
value: cardNumber,
|
|
2464
|
+
onChange: (e) => setCardNumber(e.target.value),
|
|
2465
|
+
style: inputStyle
|
|
2466
|
+
}
|
|
2467
|
+
)
|
|
2468
|
+
] }),
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2470
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-card-holder", style: labelStyle, children: "Nome impresso no cart\xE3o" }),
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2472
|
+
"input",
|
|
2473
|
+
{
|
|
2474
|
+
id: "pw-card-holder",
|
|
2475
|
+
"data-testid": "pw-card-holder",
|
|
2476
|
+
type: "text",
|
|
2477
|
+
autoComplete: "cc-name",
|
|
2478
|
+
placeholder: "NOME SOBRENOME",
|
|
2479
|
+
value: cardHolderName,
|
|
2480
|
+
onChange: (e) => setCardHolderName(e.target.value),
|
|
2481
|
+
style: inputStyle
|
|
2482
|
+
}
|
|
2483
|
+
)
|
|
2484
|
+
] }),
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 8, marginBottom: 10 }, children: [
|
|
2486
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-card-exp-m", style: labelStyle, children: "M\xEAs" }),
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2489
|
+
"input",
|
|
2490
|
+
{
|
|
2491
|
+
id: "pw-card-exp-m",
|
|
2492
|
+
"data-testid": "pw-card-exp-m",
|
|
2493
|
+
type: "text",
|
|
2494
|
+
inputMode: "numeric",
|
|
2495
|
+
autoComplete: "cc-exp-month",
|
|
2496
|
+
placeholder: "MM",
|
|
2497
|
+
maxLength: 2,
|
|
2498
|
+
value: cardExpiryMonth,
|
|
2499
|
+
onChange: (e) => setCardExpiryMonth(e.target.value.replace(/\D/g, "")),
|
|
2500
|
+
style: inputStyle
|
|
2501
|
+
}
|
|
2502
|
+
)
|
|
2503
|
+
] }),
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2505
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-card-exp-y", style: labelStyle, children: "Ano" }),
|
|
2506
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2507
|
+
"input",
|
|
2508
|
+
{
|
|
2509
|
+
id: "pw-card-exp-y",
|
|
2510
|
+
"data-testid": "pw-card-exp-y",
|
|
2511
|
+
type: "text",
|
|
2512
|
+
inputMode: "numeric",
|
|
2513
|
+
autoComplete: "cc-exp-year",
|
|
2514
|
+
placeholder: "AA",
|
|
2515
|
+
maxLength: 4,
|
|
2516
|
+
value: cardExpiryYear,
|
|
2517
|
+
onChange: (e) => setCardExpiryYear(e.target.value.replace(/\D/g, "")),
|
|
2518
|
+
style: inputStyle
|
|
2519
|
+
}
|
|
2520
|
+
)
|
|
2521
|
+
] }),
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2523
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-card-cvv", style: labelStyle, children: "CVV" }),
|
|
2524
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2525
|
+
"input",
|
|
2526
|
+
{
|
|
2527
|
+
id: "pw-card-cvv",
|
|
2528
|
+
"data-testid": "pw-card-cvv",
|
|
2529
|
+
type: "text",
|
|
2530
|
+
inputMode: "numeric",
|
|
2531
|
+
autoComplete: "cc-csc",
|
|
2532
|
+
placeholder: "123",
|
|
2533
|
+
maxLength: 4,
|
|
2534
|
+
value: cardCcv,
|
|
2535
|
+
onChange: (e) => setCardCcv(e.target.value.replace(/\D/g, "")),
|
|
2536
|
+
style: inputStyle
|
|
2537
|
+
}
|
|
2538
|
+
)
|
|
2539
|
+
] })
|
|
2540
|
+
] }),
|
|
2541
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("legend", { style: { fontSize: 13, opacity: 0.75, marginBottom: 8, marginTop: 8 }, children: "Dados do titular" }),
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-holder-name", style: labelStyle, children: "Nome completo" }),
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2545
|
+
"input",
|
|
2546
|
+
{
|
|
2547
|
+
id: "pw-holder-name",
|
|
2548
|
+
"data-testid": "pw-holder-name",
|
|
2549
|
+
type: "text",
|
|
2550
|
+
autoComplete: "name",
|
|
2551
|
+
placeholder: "Nome Sobrenome",
|
|
2552
|
+
value: holderName,
|
|
2553
|
+
onChange: (e) => setHolderName(e.target.value),
|
|
2554
|
+
style: inputStyle
|
|
2555
|
+
}
|
|
2556
|
+
)
|
|
2557
|
+
] }),
|
|
2558
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-holder-email", style: labelStyle, children: "E-mail" }),
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2561
|
+
"input",
|
|
2562
|
+
{
|
|
2563
|
+
id: "pw-holder-email",
|
|
2564
|
+
"data-testid": "pw-holder-email",
|
|
2565
|
+
type: "email",
|
|
2566
|
+
autoComplete: "email",
|
|
2567
|
+
placeholder: "voce@email.com",
|
|
2568
|
+
value: holderEmail,
|
|
2569
|
+
onChange: (e) => setHolderEmail(e.target.value),
|
|
2570
|
+
style: inputStyle
|
|
2571
|
+
}
|
|
2572
|
+
)
|
|
2573
|
+
] }),
|
|
2574
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 8, marginBottom: 10 }, children: [
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2576
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-holder-cep", style: labelStyle, children: "CEP" }),
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2578
|
+
"input",
|
|
2579
|
+
{
|
|
2580
|
+
id: "pw-holder-cep",
|
|
2581
|
+
"data-testid": "pw-holder-cep",
|
|
2582
|
+
type: "text",
|
|
2583
|
+
inputMode: "numeric",
|
|
2584
|
+
autoComplete: "postal-code",
|
|
2585
|
+
placeholder: "00000-000",
|
|
2586
|
+
value: holderPostalCode,
|
|
2587
|
+
onChange: (e) => setHolderPostalCode(e.target.value),
|
|
2588
|
+
style: inputStyle
|
|
2589
|
+
}
|
|
2590
|
+
)
|
|
2591
|
+
] }),
|
|
2592
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, textAlign: "left" }, children: [
|
|
2593
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-holder-addr-n", style: labelStyle, children: "N\xFAmero" }),
|
|
2594
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2595
|
+
"input",
|
|
2596
|
+
{
|
|
2597
|
+
id: "pw-holder-addr-n",
|
|
2598
|
+
"data-testid": "pw-holder-addr-n",
|
|
2599
|
+
type: "text",
|
|
2600
|
+
inputMode: "numeric",
|
|
2601
|
+
placeholder: "123",
|
|
2602
|
+
value: holderAddressNumber,
|
|
2603
|
+
onChange: (e) => setHolderAddressNumber(e.target.value),
|
|
2604
|
+
style: inputStyle
|
|
2605
|
+
}
|
|
2606
|
+
)
|
|
2607
|
+
] })
|
|
2608
|
+
] }),
|
|
2609
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2610
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-holder-phone", style: labelStyle, children: "Telefone (opcional)" }),
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2612
|
+
"input",
|
|
2613
|
+
{
|
|
2614
|
+
id: "pw-holder-phone",
|
|
2615
|
+
"data-testid": "pw-holder-phone",
|
|
2616
|
+
type: "tel",
|
|
2617
|
+
inputMode: "tel",
|
|
2618
|
+
autoComplete: "tel",
|
|
2619
|
+
placeholder: "(11) 99999-9999",
|
|
2620
|
+
value: holderPhone,
|
|
2621
|
+
onChange: (e) => setHolderPhone(e.target.value),
|
|
2622
|
+
style: inputStyle
|
|
2623
|
+
}
|
|
2624
|
+
)
|
|
2625
|
+
] })
|
|
2626
|
+
]
|
|
2627
|
+
}
|
|
2628
|
+
),
|
|
2629
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: fieldGroup, children: [
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "pw-cpf", style: labelStyle, children: "Seu CPF" }),
|
|
2218
2631
|
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2219
2632
|
"input",
|
|
2220
2633
|
{
|
|
2634
|
+
id: "pw-cpf",
|
|
2221
2635
|
"data-testid": "paywall-cpf",
|
|
2222
2636
|
type: "text",
|
|
2223
2637
|
inputMode: "numeric",
|
|
2224
2638
|
placeholder: "000.000.000-00",
|
|
2225
2639
|
value: cpf,
|
|
2226
2640
|
onChange: (e) => setCpf(e.target.value),
|
|
2227
|
-
style:
|
|
2641
|
+
style: inputStyle
|
|
2228
2642
|
}
|
|
2229
2643
|
)
|
|
2230
2644
|
] }),
|
|
@@ -2234,7 +2648,7 @@ function DefaultPaywall() {
|
|
|
2234
2648
|
{
|
|
2235
2649
|
"data-testid": "paywall-cta",
|
|
2236
2650
|
type: "button",
|
|
2237
|
-
onClick:
|
|
2651
|
+
onClick: submit,
|
|
2238
2652
|
disabled: !canCheckout,
|
|
2239
2653
|
style: {
|
|
2240
2654
|
width: "100%",
|
|
@@ -2245,13 +2659,93 @@ function DefaultPaywall() {
|
|
|
2245
2659
|
borderRadius: 8,
|
|
2246
2660
|
opacity: canCheckout ? 1 : 0.5,
|
|
2247
2661
|
fontSize: 16,
|
|
2248
|
-
fontWeight: 600
|
|
2662
|
+
fontWeight: 600,
|
|
2663
|
+
cursor: canCheckout ? "pointer" : "not-allowed"
|
|
2249
2664
|
},
|
|
2250
|
-
children:
|
|
2665
|
+
children: ctaLabel
|
|
2251
2666
|
}
|
|
2252
2667
|
),
|
|
2253
|
-
|
|
2254
|
-
p.
|
|
2668
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { opacity: 0.55, marginTop: 16, fontSize: 12 }, children: footer }),
|
|
2669
|
+
p.priceHint && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { opacity: 0.6, marginTop: 8, fontSize: 12 }, children: p.priceHint }),
|
|
2670
|
+
p.footerNote && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { opacity: 0.5, marginTop: 8, fontSize: 12 }, children: p.footerNote }),
|
|
2671
|
+
pixPending && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2672
|
+
"div",
|
|
2673
|
+
{
|
|
2674
|
+
"data-testid": "paywall-pix-modal",
|
|
2675
|
+
role: "dialog",
|
|
2676
|
+
"aria-label": "Pagamento Pix pendente",
|
|
2677
|
+
style: {
|
|
2678
|
+
position: "fixed",
|
|
2679
|
+
inset: 0,
|
|
2680
|
+
background: "rgba(0,0,0,0.6)",
|
|
2681
|
+
display: "flex",
|
|
2682
|
+
alignItems: "center",
|
|
2683
|
+
justifyContent: "center",
|
|
2684
|
+
padding: 24,
|
|
2685
|
+
zIndex: 1e3
|
|
2686
|
+
},
|
|
2687
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: {
|
|
2688
|
+
background: "#fff",
|
|
2689
|
+
borderRadius: 12,
|
|
2690
|
+
padding: 24,
|
|
2691
|
+
maxWidth: 360,
|
|
2692
|
+
width: "100%",
|
|
2693
|
+
textAlign: "center"
|
|
2694
|
+
}, children: [
|
|
2695
|
+
pixPending.paid ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
2696
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { style: { marginTop: 0 }, children: "Pagamento confirmado!" }),
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { opacity: 0.7 }, children: "Liberando acesso\u2026" })
|
|
2698
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
2699
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { style: { marginTop: 0, fontSize: 18 }, children: "Pague com Pix Autom\xE1tico" }),
|
|
2700
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { fontSize: 13, opacity: 0.75 }, children: "Escaneie o QR Code no app do seu banco pra autorizar o d\xE9bito recorrente." }),
|
|
2701
|
+
pixPending.qrCodeBase64 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2702
|
+
"img",
|
|
2703
|
+
{
|
|
2704
|
+
"data-testid": "pix-qr-image",
|
|
2705
|
+
alt: "QR Code Pix",
|
|
2706
|
+
src: `data:image/png;base64,${pixPending.qrCodeBase64}`,
|
|
2707
|
+
style: { width: "100%", maxWidth: 240, margin: "12px auto", display: "block" }
|
|
2708
|
+
}
|
|
2709
|
+
),
|
|
2710
|
+
pixPending.qrCodePayload && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2711
|
+
"textarea",
|
|
2712
|
+
{
|
|
2713
|
+
"data-testid": "pix-qr-payload",
|
|
2714
|
+
readOnly: true,
|
|
2715
|
+
value: pixPending.qrCodePayload,
|
|
2716
|
+
style: {
|
|
2717
|
+
width: "100%",
|
|
2718
|
+
minHeight: 72,
|
|
2719
|
+
padding: 8,
|
|
2720
|
+
fontSize: 11,
|
|
2721
|
+
fontFamily: "monospace",
|
|
2722
|
+
borderRadius: 6,
|
|
2723
|
+
border: "1px solid #ccc",
|
|
2724
|
+
resize: "none"
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
),
|
|
2728
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { style: { fontSize: 11, opacity: 0.55, marginTop: 12 }, children: "Aguardando confirma\xE7\xE3o do banco\u2026 Pode levar alguns segundos." })
|
|
2729
|
+
] }),
|
|
2730
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2731
|
+
"button",
|
|
2732
|
+
{
|
|
2733
|
+
type: "button",
|
|
2734
|
+
onClick: dismissPix,
|
|
2735
|
+
style: {
|
|
2736
|
+
marginTop: 16,
|
|
2737
|
+
padding: "8px 16px",
|
|
2738
|
+
border: "1px solid #ccc",
|
|
2739
|
+
borderRadius: 6,
|
|
2740
|
+
background: "white",
|
|
2741
|
+
cursor: "pointer"
|
|
2742
|
+
},
|
|
2743
|
+
children: "Fechar"
|
|
2744
|
+
}
|
|
2745
|
+
)
|
|
2746
|
+
] })
|
|
2747
|
+
}
|
|
2748
|
+
)
|
|
2255
2749
|
] });
|
|
2256
2750
|
}
|
|
2257
2751
|
|
|
@@ -2259,7 +2753,7 @@ function DefaultPaywall() {
|
|
|
2259
2753
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2260
2754
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
2261
2755
|
function PaymentReturnHandler({ children }) {
|
|
2262
|
-
const { subscription } = (0,
|
|
2756
|
+
const { subscription } = (0, import_sdk10.useHook)();
|
|
2263
2757
|
const subRef = (0, import_react15.useRef)(subscription);
|
|
2264
2758
|
subRef.current = subscription;
|
|
2265
2759
|
const runIdRef = (0, import_react15.useRef)(0);
|
|
@@ -2368,7 +2862,7 @@ function AppRoot({
|
|
|
2368
2862
|
|
|
2369
2863
|
// src/hooks/usePush.ts
|
|
2370
2864
|
var import_react16 = require("react");
|
|
2371
|
-
var
|
|
2865
|
+
var import_sdk11 = require("@hook-sdk/sdk");
|
|
2372
2866
|
function detectIosNeedsInstall() {
|
|
2373
2867
|
if (typeof navigator === "undefined" || typeof window === "undefined") return false;
|
|
2374
2868
|
const ua = navigator.userAgent || "";
|
|
@@ -2394,7 +2888,7 @@ function deriveState(push) {
|
|
|
2394
2888
|
return { kind: "prompt" };
|
|
2395
2889
|
}
|
|
2396
2890
|
function usePush() {
|
|
2397
|
-
const { push } = (0,
|
|
2891
|
+
const { push } = (0, import_sdk11.useHook)();
|
|
2398
2892
|
const [state, setState] = (0, import_react16.useState)(() => deriveState(push));
|
|
2399
2893
|
(0, import_react16.useEffect)(() => {
|
|
2400
2894
|
setState(deriveState(push));
|
|
@@ -2476,41 +2970,6 @@ function EmptyState({ title, description, action }) {
|
|
|
2476
2970
|
] });
|
|
2477
2971
|
}
|
|
2478
2972
|
|
|
2479
|
-
// src/hooks/usePlan.ts
|
|
2480
|
-
var import_sdk11 = require("@hook-sdk/sdk");
|
|
2481
|
-
function usePlan() {
|
|
2482
|
-
const { plan } = (0, import_sdk11.useHook)();
|
|
2483
|
-
return plan;
|
|
2484
|
-
}
|
|
2485
|
-
|
|
2486
|
-
// src/utils/price.ts
|
|
2487
|
-
function formatBRL(cents) {
|
|
2488
|
-
if (cents === null || cents === void 0) return "";
|
|
2489
|
-
const reais = cents / 100;
|
|
2490
|
-
return new Intl.NumberFormat("pt-BR", {
|
|
2491
|
-
style: "currency",
|
|
2492
|
-
currency: "BRL"
|
|
2493
|
-
}).format(reais);
|
|
2494
|
-
}
|
|
2495
|
-
function monthlyFromYearly(yearlyCents) {
|
|
2496
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2497
|
-
return Math.round(yearlyCents / 12);
|
|
2498
|
-
}
|
|
2499
|
-
function dailyFromYearly(yearlyCents) {
|
|
2500
|
-
if (yearlyCents === null || yearlyCents === void 0) return 0;
|
|
2501
|
-
return Math.round(yearlyCents / 365);
|
|
2502
|
-
}
|
|
2503
|
-
function computeAnchorCents(baseCents, multiplier) {
|
|
2504
|
-
if (multiplier === null || multiplier === void 0) return null;
|
|
2505
|
-
if (!Number.isFinite(multiplier)) return null;
|
|
2506
|
-
if (multiplier <= 1) return null;
|
|
2507
|
-
return Math.round(baseCents * multiplier);
|
|
2508
|
-
}
|
|
2509
|
-
function discountPercent(anchorCents, realCents) {
|
|
2510
|
-
if (anchorCents <= realCents) return 0;
|
|
2511
|
-
return Math.floor((anchorCents - realCents) / anchorCents * 100);
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
2973
|
// src/hooks/useAuthPrimitives.ts
|
|
2515
2974
|
var import_react17 = require("react");
|
|
2516
2975
|
var import_sdk12 = require("@hook-sdk/sdk");
|