@greatapps/common 1.1.782 → 1.1.784
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/components/layouts/NavBar.mjs +2 -0
- package/dist/components/layouts/NavBar.mjs.map +1 -1
- package/dist/components/ui/form/PhoneInput.mjs +1 -1
- package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
- package/dist/handlers.mjs +2 -0
- package/dist/handlers.mjs.map +1 -1
- package/dist/index.mjs +9 -1
- package/dist/index.mjs.map +1 -1
- package/dist/infra/api/client.mjs +5 -1
- package/dist/infra/api/client.mjs.map +1 -1
- package/dist/infra/api/types.mjs +1 -1
- package/dist/infra/api/types.mjs.map +1 -1
- package/dist/infra/route/safe-route-handler.mjs +4 -1
- package/dist/infra/route/safe-route-handler.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-account-not-frozen.mjs +6 -5
- package/dist/modules/auth/utils/assert-account-not-frozen.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-management-subscription.mjs +7 -14
- package/dist/modules/auth/utils/assert-management-subscription.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-paid-subscription.mjs +7 -12
- package/dist/modules/auth/utils/assert-paid-subscription.mjs.map +1 -1
- package/dist/modules/auth/utils/assert-subscription-addon.mjs +2 -10
- package/dist/modules/auth/utils/assert-subscription-addon.mjs.map +1 -1
- package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs.map +1 -1
- package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs +10 -0
- package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs.map +1 -0
- package/dist/modules/subscriptions/constants/subscription.constants.mjs +2 -0
- package/dist/modules/subscriptions/constants/subscription.constants.mjs.map +1 -1
- package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs +17 -0
- package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs.map +1 -0
- package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs +2 -13
- package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
- package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs +28 -0
- package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs.map +1 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs +26 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
- package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
- package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs +16 -0
- package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs.map +1 -0
- package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs +1 -1
- package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs.map +1 -1
- package/dist/modules/subscriptions/utils/has-paid-subscription.mjs +1 -1
- package/dist/modules/subscriptions/utils/has-paid-subscription.mjs.map +1 -1
- package/dist/modules/subscriptions/utils/read-current-subscription.mjs +21 -0
- package/dist/modules/subscriptions/utils/read-current-subscription.mjs.map +1 -0
- package/dist/server.mjs +4 -0
- package/dist/server.mjs.map +1 -1
- package/dist/store/useMdSidebarStore.mjs +17 -4
- package/dist/store/useMdSidebarStore.mjs.map +1 -1
- package/dist/utils/safeServerAction.mjs +4 -1
- package/dist/utils/safeServerAction.mjs.map +1 -1
- package/dist/utils/withAction.mjs +2 -1
- package/dist/utils/withAction.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/layouts/NavBar.tsx +2 -0
- package/src/components/ui/form/PhoneInput.tsx +1 -1
- package/src/handlers.ts +1 -0
- package/src/index.ts +7 -1
- package/src/infra/api/client.ts +11 -3
- package/src/infra/api/types.ts +7 -5
- package/src/infra/route/safe-route-handler.ts +4 -1
- package/src/modules/auth/utils/assert-account-not-frozen.ts +7 -6
- package/src/modules/auth/utils/assert-management-subscription.ts +15 -23
- package/src/modules/auth/utils/assert-paid-subscription.ts +7 -15
- package/src/modules/auth/utils/assert-subscription-addon.ts +2 -12
- package/src/modules/subscriptions/actions/get-current-subscription.action.ts +8 -0
- package/src/modules/subscriptions/constants/subscription.constants.ts +3 -0
- package/src/modules/subscriptions/handlers/current-subscription.handler.ts +16 -0
- package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +7 -17
- package/src/modules/subscriptions/hooks/use-current-subscription.hook.ts +38 -0
- package/src/modules/subscriptions/services/subscriptions.service.ts +34 -0
- package/src/modules/subscriptions/types/subscription.type.ts +2 -0
- package/src/modules/subscriptions/utils/group-subscriptions-by-product.ts +23 -0
- package/src/modules/subscriptions/utils/has-active-paid-subscription.ts +1 -2
- package/src/modules/subscriptions/utils/has-paid-subscription.ts +1 -2
- package/src/modules/subscriptions/utils/read-current-subscription.ts +34 -0
- package/src/server.ts +2 -0
- package/src/store/useMdSidebarStore.ts +20 -5
- package/src/utils/safeServerAction.ts +4 -6
- package/src/utils/withAction.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/infra/route/safe-route-handler.ts"],"sourcesContent":["import 'server-only';\n\nimport { ApiError } from '../api/types';\n\nexport async function safeRouteHandler<T>(fn: () => Promise<T>): Promise<Response> {\n try {\n const data = await fn();\n return Response.json({ success: true, data });\n } catch (error) {\n if (error instanceof ApiError) {\n return Response.json({ success: false, error: error.message }
|
|
1
|
+
{"version":3,"sources":["../../../src/infra/route/safe-route-handler.ts"],"sourcesContent":["import 'server-only';\n\nimport { ApiError } from '../api/types';\n\nexport async function safeRouteHandler<T>(fn: () => Promise<T>): Promise<Response> {\n try {\n const data = await fn();\n return Response.json({ success: true, data });\n } catch (error) {\n if (error instanceof ApiError) {\n return Response.json(\n { success: false, error: error.message, code: error.code },\n { status: error.statusCode },\n );\n }\n console.error('[safeRouteHandler] erro inesperado', error);\n return Response.json({ success: false, error: 'Erro interno do servidor' }, { status: 500 });\n }\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AAEzB,eAAsB,iBAAoB,IAAyC;AACjF,MAAI;AACF,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO,SAAS,KAAK,EAAE,SAAS,MAAM,KAAK,CAAC;AAAA,EAC9C,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,aAAO,SAAS;AAAA,QACd,EAAE,SAAS,OAAO,OAAO,MAAM,SAAS,MAAM,MAAM,KAAK;AAAA,QACzD,EAAE,QAAQ,MAAM,WAAW;AAAA,MAC7B;AAAA,IACF;AACA,YAAQ,MAAM,sCAAsC,KAAK;AACzD,WAAO,SAAS,KAAK,EAAE,SAAS,OAAO,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC7F;AACF;","names":[]}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { ApiError } from "../../../infra/api/types";
|
|
3
|
-
import {
|
|
3
|
+
import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
|
|
4
4
|
import { getAccountFreezeState } from "../../subscriptions/utils/get-account-freeze-state";
|
|
5
5
|
async function assertAccountNotFrozen() {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
let subscription;
|
|
7
|
+
try {
|
|
8
|
+
subscription = await readCurrentSubscription();
|
|
9
|
+
} catch (error) {
|
|
8
10
|
console.error(
|
|
9
11
|
"[assertAccountNotFrozen] failed to read subscription context:",
|
|
10
|
-
|
|
12
|
+
error
|
|
11
13
|
);
|
|
12
14
|
return;
|
|
13
15
|
}
|
|
14
|
-
const subscription = result.data?.data?.[0];
|
|
15
16
|
if (!subscription) return;
|
|
16
17
|
if (getAccountFreezeState(subscription) !== "frozen") return;
|
|
17
18
|
throw new ApiError(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-account-not-frozen.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-account-not-frozen.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport { readCurrentSubscription } from \"../../subscriptions/utils/read-current-subscription\";\nimport { getAccountFreezeState } from \"../../subscriptions/utils/get-account-freeze-state\";\nimport type { Subscription } from \"../../subscriptions/types/subscription.type\";\n\nexport async function assertAccountNotFrozen(): Promise<void> {\n let subscription: Subscription | null;\n\n try {\n subscription = await readCurrentSubscription();\n } catch (error) {\n console.error(\n \"[assertAccountNotFrozen] failed to read subscription context:\",\n error,\n );\n return;\n }\n\n if (!subscription) return;\n\n if (getAccountFreezeState(subscription) !== \"frozen\") return;\n\n throw new ApiError(\n \"Sua conta está com os recursos bloqueados por falta de pagamento. Regularize a assinatura para liberar.\",\n \"ACCOUNT_FROZEN\",\n 403,\n );\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AAGtC,eAAsB,yBAAwC;AAC5D,MAAI;AAEJ,MAAI;AACF,mBAAe,MAAM,wBAAwB;AAAA,EAC/C,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI,CAAC,aAAc;AAEnB,MAAI,sBAAsB,YAAY,MAAM,SAAU;AAEtD,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import "server-only";
|
|
2
|
-
import { cache } from "react";
|
|
3
2
|
import { ApiError } from "../../../infra/api/types";
|
|
4
|
-
import {
|
|
3
|
+
import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
|
|
4
|
+
import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
|
|
5
5
|
import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
if (!result.success) {
|
|
9
|
-
throw new ApiError(
|
|
10
|
-
result.error || "N\xE3o foi poss\xEDvel validar a assinatura atual",
|
|
11
|
-
"SUBSCRIPTION_CONTEXT_FAILED",
|
|
12
|
-
403
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
const subscription = result.data?.data?.[0];
|
|
6
|
+
async function assertManagementSubscription() {
|
|
7
|
+
const subscription = await readCurrentSubscription();
|
|
16
8
|
if (!subscription) {
|
|
17
9
|
throw new ApiError(
|
|
18
10
|
"Nenhuma assinatura ativa encontrada. Assine um plano para continuar.",
|
|
@@ -20,16 +12,17 @@ const assertManagementSubscription = cache(async () => {
|
|
|
20
12
|
403
|
|
21
13
|
);
|
|
22
14
|
}
|
|
15
|
+
const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
|
|
23
16
|
const expiryDate = getSubscriptionExpiryDate(subscription);
|
|
24
17
|
const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
|
|
25
|
-
if (isExpired) {
|
|
18
|
+
if (isCancelled || isExpired) {
|
|
26
19
|
throw new ApiError(
|
|
27
20
|
"Sua assinatura est\xE1 vencida. Assine um novo plano para continuar.",
|
|
28
21
|
"SUBSCRIPTION_EXPIRED",
|
|
29
22
|
403
|
|
30
23
|
);
|
|
31
24
|
}
|
|
32
|
-
}
|
|
25
|
+
}
|
|
33
26
|
export {
|
|
34
27
|
assertManagementSubscription
|
|
35
28
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-management-subscription.ts"],"sourcesContent":["import \"server-only\";\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-management-subscription.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport { readCurrentSubscription } from \"../../subscriptions/utils/read-current-subscription\";\nimport { getSubscriptionCancellationState } from \"../../subscriptions/utils/get-subscription-cancellation-state\";\nimport { getSubscriptionExpiryDate } from \"../../subscriptions/utils/get-subscription-expiry-date\";\n\n/**\n * A leitura por trás (`readCurrentSubscription`) já é deduplicada por request via\n * React.cache, então não precisa de outra camada aqui: em produção (26/08) o mesmo\n * carregamento disparava até quatro cópias da mesma pergunta em paralelo, com 727 ms,\n * 4,2 s, 5,0 s e 7,0 s, contra um p50 de 528 ms; agora é uma leitura só,\n * compartilhada pelos quatro gates.\n *\n * A checagem de cancelamento é obrigatória além da de vencimento: a corrente pode\n * vir com `active: false` (baixa por falha de pagamento) com `date_due` ainda no\n * futuro, caso que só `getSubscriptionExpiryDate` não cobre.\n */\nexport async function assertManagementSubscription(): Promise<void> {\n const subscription = await readCurrentSubscription();\n\n if (!subscription) {\n throw new ApiError(\n \"Nenhuma assinatura ativa encontrada. Assine um plano para continuar.\",\n \"SUBSCRIPTION_NOT_FOUND\",\n 403,\n );\n }\n\n const isCancelled = getSubscriptionCancellationState(subscription) === \"cancelled\";\n const expiryDate = getSubscriptionExpiryDate(subscription);\n const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();\n\n if (isCancelled || isExpired) {\n throw new ApiError(\n \"Sua assinatura está vencida. Assine um novo plano para continuar.\",\n \"SUBSCRIPTION_EXPIRED\",\n 403,\n );\n }\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AACxC,SAAS,wCAAwC;AACjD,SAAS,iCAAiC;AAa1C,eAAsB,+BAA8C;AAClE,QAAM,eAAe,MAAM,wBAAwB;AAEnD,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,iCAAiC,YAAY,MAAM;AACvE,QAAM,aAAa,0BAA0B,YAAY;AACzD,QAAM,YAAY,CAAC,CAAC,cAAc,KAAK,IAAI,KAAK,WAAW,QAAQ;AAEnE,MAAI,eAAe,WAAW;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { ApiError } from "../../../infra/api/types";
|
|
3
|
-
import {
|
|
3
|
+
import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
|
|
4
|
+
import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
|
|
4
5
|
import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
|
|
6
|
+
import { FREE_PLAN_ID } from "../../subscriptions/constants/subscription.constants";
|
|
5
7
|
async function assertPaidSubscription() {
|
|
6
|
-
const
|
|
7
|
-
if (!result.success) {
|
|
8
|
-
throw new ApiError(
|
|
9
|
-
result.error || "N\xE3o foi poss\xEDvel validar a assinatura atual",
|
|
10
|
-
"SUBSCRIPTION_CONTEXT_FAILED",
|
|
11
|
-
403
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
const subscription = result.data?.data?.[0];
|
|
8
|
+
const subscription = await readCurrentSubscription();
|
|
15
9
|
if (!subscription) {
|
|
16
10
|
throw new ApiError(
|
|
17
11
|
"Nenhuma assinatura ativa encontrada. Assine um plano para continuar.",
|
|
@@ -19,16 +13,17 @@ async function assertPaidSubscription() {
|
|
|
19
13
|
403
|
|
20
14
|
);
|
|
21
15
|
}
|
|
16
|
+
const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
|
|
22
17
|
const expiryDate = getSubscriptionExpiryDate(subscription);
|
|
23
18
|
const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
|
|
24
|
-
if (isExpired) {
|
|
19
|
+
if (isCancelled || isExpired) {
|
|
25
20
|
throw new ApiError(
|
|
26
21
|
"Sua assinatura est\xE1 vencida. Assine um novo plano para continuar.",
|
|
27
22
|
"SUBSCRIPTION_EXPIRED",
|
|
28
23
|
403
|
|
29
24
|
);
|
|
30
25
|
}
|
|
31
|
-
if (subscription.type === "free" || subscription.id_plan ===
|
|
26
|
+
if (subscription.type === "free" || subscription.id_plan === FREE_PLAN_ID) {
|
|
32
27
|
throw new ApiError(
|
|
33
28
|
"Este recurso est\xE1 dispon\xEDvel apenas para planos pagos. Fa\xE7a upgrade do seu plano para continuar.",
|
|
34
29
|
"PAID_SUBSCRIPTION_REQUIRED",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-paid-subscription.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-paid-subscription.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport { readCurrentSubscription } from \"../../subscriptions/utils/read-current-subscription\";\nimport { getSubscriptionCancellationState } from \"../../subscriptions/utils/get-subscription-cancellation-state\";\nimport { getSubscriptionExpiryDate } from \"../../subscriptions/utils/get-subscription-expiry-date\";\nimport { FREE_PLAN_ID } from \"../../subscriptions/constants/subscription.constants\";\n\nexport async function assertPaidSubscription() {\n const subscription = await readCurrentSubscription();\n\n if (!subscription) {\n throw new ApiError(\n \"Nenhuma assinatura ativa encontrada. Assine um plano para continuar.\",\n \"SUBSCRIPTION_NOT_FOUND\",\n 403,\n );\n }\n\n const isCancelled = getSubscriptionCancellationState(subscription) === \"cancelled\";\n const expiryDate = getSubscriptionExpiryDate(subscription);\n const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();\n\n if (isCancelled || isExpired) {\n throw new ApiError(\n \"Sua assinatura está vencida. Assine um novo plano para continuar.\",\n \"SUBSCRIPTION_EXPIRED\",\n 403,\n );\n }\n\n if (subscription.type === 'free' || subscription.id_plan === FREE_PLAN_ID) {\n throw new ApiError(\n \"Este recurso está disponível apenas para planos pagos. Faça upgrade do seu plano para continuar.\",\n \"PAID_SUBSCRIPTION_REQUIRED\",\n 403,\n );\n }\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AACxC,SAAS,wCAAwC;AACjD,SAAS,iCAAiC;AAC1C,SAAS,oBAAoB;AAE7B,eAAsB,yBAAyB;AAC7C,QAAM,eAAe,MAAM,wBAAwB;AAEnD,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,iCAAiC,YAAY,MAAM;AACvE,QAAM,aAAa,0BAA0B,YAAY;AACzD,QAAM,YAAY,CAAC,CAAC,cAAc,KAAK,IAAI,KAAK,WAAW,QAAQ;AAEnE,MAAI,eAAe,WAAW;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,aAAa,SAAS,UAAU,aAAa,YAAY,cAAc;AACzE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { ApiError } from "../../../infra/api/types";
|
|
3
|
-
import {
|
|
3
|
+
import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
|
|
4
4
|
async function assertSubscriptionAddon(addonId, addonLabel) {
|
|
5
|
-
const
|
|
6
|
-
if (!result.success) {
|
|
7
|
-
throw new ApiError(
|
|
8
|
-
result.error || "N\xE3o foi poss\xEDvel validar a assinatura atual",
|
|
9
|
-
"SUBSCRIPTION_CONTEXT_FAILED",
|
|
10
|
-
403
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
const subscription = result.data?.data?.[0];
|
|
5
|
+
const subscription = await readCurrentSubscription();
|
|
14
6
|
if (!subscription) {
|
|
15
7
|
throw new ApiError(
|
|
16
8
|
`Nenhuma assinatura ativa encontrada. Para acessar este recurso, \xE9 necess\xE1rio ter uma assinatura com ${addonLabel}.`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-subscription-addon.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/utils/assert-subscription-addon.ts"],"sourcesContent":["import \"server-only\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport { readCurrentSubscription } from \"../../subscriptions/utils/read-current-subscription\";\nimport type { AddonKindEnum } from \"../../subscriptions/constants/addons.constants\";\n\nexport async function assertSubscriptionAddon(\n addonId: AddonKindEnum,\n addonLabel: string,\n) {\n const subscription = await readCurrentSubscription();\n\n if (!subscription) {\n throw new ApiError(\n `Nenhuma assinatura ativa encontrada. Para acessar este recurso, é necessário ter uma assinatura com ${addonLabel}.`,\n \"NO_ACTIVE_SUBSCRIPTION\",\n 403,\n );\n }\n\n const hasAddon = subscription?.items?.some(\n (item) => item.id_addon === addonId,\n );\n\n if (!hasAddon) {\n throw new ApiError(\n `Sua assinatura não inclui ${addonLabel}. Faça upgrade do seu plano.`,\n \"ADDON_NOT_AVAILABLE\",\n 403,\n );\n }\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AAGxC,eAAsB,wBACpB,SACA,YACA;AACA,QAAM,eAAe,MAAM,wBAAwB;AAEnD,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI;AAAA,MACR,6GAAuG,UAAU;AAAA,MACjH;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,cAAc,OAAO;AAAA,IACpC,CAAC,SAAS,KAAK,aAAa;AAAA,EAC9B;AAEA,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,gCAA6B,UAAU;AAAA,MACvC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/plans/hooks/use-plan-by-id.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { apiFetch } from \"../../../infra/http/api-fetch\";\nimport type { SuccessResult } from \"../../../infra/api/types\";\nimport type { Plan } from \"../types/plan.type\";\nimport { PLANS_QUERY_KEY } from \"./list-plans.hook\";\nimport { useAuthQueryKey } from \"../../../hooks/useAuthQueryKey\";\n\nexport function usePlanById(idPlan?: number | string | null) {\n const queryClient = useQueryClient();\n const plansKey = useAuthQueryKey([...PLANS_QUERY_KEY]);\n\n return useQuery({\n queryKey: [...plansKey, idPlan],\n queryFn: ({ signal }) =>\n apiFetch<SuccessResult<Plan>>(`/api/plans/${idPlan}`, { signal }),\n initialData: () => {\n const queries = queryClient.getQueriesData<Plan[]>({\n queryKey: plansKey,\n });\n\n for (const [, data] of queries) {\n if (!data || !Array.isArray(data)) continue;\n const numericId = Number(idPlan);\n const plan = data.find(\n (p) => p.id === numericId || p.id_plan === numericId\n );\n if (plan) {\n return { success: true as const, data: plan };\n }\n }\n\n return undefined;\n },\n initialDataUpdatedAt: 0,\n enabled: !!idPlan,\n });\n}\n"],"mappings":";AAEA,SAAS,UAAU,sBAAsB;AACzC,SAAS,gBAAgB;AAGzB,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAEzB,SAAS,YAAY,QAAiC;AAC3D,QAAM,cAAc,eAAe;AACnC,QAAM,WAAW,gBAAgB,CAAC,GAAG,eAAe,CAAC;AAErD,SAAO,
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/plans/hooks/use-plan-by-id.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { apiFetch } from \"../../../infra/http/api-fetch\";\nimport type { SuccessResult } from \"../../../infra/api/types\";\nimport type { Plan } from \"../types/plan.type\";\nimport { PLANS_QUERY_KEY } from \"./list-plans.hook\";\nimport { useAuthQueryKey } from \"../../../hooks/useAuthQueryKey\";\n\nexport function usePlanById(idPlan?: number | string | null) {\n const queryClient = useQueryClient();\n const plansKey = useAuthQueryKey([...PLANS_QUERY_KEY]);\n\n return useQuery<SuccessResult<Plan>>({\n queryKey: [...plansKey, idPlan],\n queryFn: ({ signal }) =>\n apiFetch<SuccessResult<Plan>>(`/api/plans/${idPlan}`, { signal }),\n initialData: () => {\n const queries = queryClient.getQueriesData<Plan[]>({\n queryKey: plansKey,\n });\n\n for (const [, data] of queries) {\n if (!data || !Array.isArray(data)) continue;\n const numericId = Number(idPlan);\n const plan = data.find(\n (p) => p.id === numericId || p.id_plan === numericId\n );\n if (plan) {\n return { success: true as const, data: plan };\n }\n }\n\n return undefined;\n },\n initialDataUpdatedAt: 0,\n enabled: !!idPlan,\n });\n}\n"],"mappings":";AAEA,SAAS,UAAU,sBAAsB;AACzC,SAAS,gBAAgB;AAGzB,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAEzB,SAAS,YAAY,QAAiC;AAC3D,QAAM,cAAc,eAAe;AACnC,QAAM,WAAW,gBAAgB,CAAC,GAAG,eAAe,CAAC;AAErD,SAAO,SAA8B;AAAA,IACnC,UAAU,CAAC,GAAG,UAAU,MAAM;AAAA,IAC9B,SAAS,CAAC,EAAE,OAAO,MACjB,SAA8B,cAAc,MAAM,IAAI,EAAE,OAAO,CAAC;AAAA,IAClE,aAAa,MAAM;AACjB,YAAM,UAAU,YAAY,eAAuB;AAAA,QACjD,UAAU;AAAA,MACZ,CAAC;AAED,iBAAW,CAAC,EAAE,IAAI,KAAK,SAAS;AAC9B,YAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,IAAI,EAAG;AACnC,cAAM,YAAY,OAAO,MAAM;AAC/B,cAAM,OAAO,KAAK;AAAA,UAChB,CAAC,MAAM,EAAE,OAAO,aAAa,EAAE,YAAY;AAAA,QAC7C;AACA,YAAI,MAAM;AACR,iBAAO,EAAE,SAAS,MAAe,MAAM,KAAK;AAAA,QAC9C;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,sBAAsB;AAAA,IACtB,SAAS,CAAC,CAAC;AAAA,EACb,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { safeServerAction } from "../../../utils/safeServerAction";
|
|
3
|
+
import { subscriptionsService } from "../services/subscriptions.service";
|
|
4
|
+
async function getCurrentSubscriptionAction(params) {
|
|
5
|
+
return safeServerAction(() => subscriptionsService.getCurrentSubscription(params));
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
getCurrentSubscriptionAction
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=get-current-subscription.action.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/actions/get-current-subscription.action.ts"],"sourcesContent":["'use server';\n\nimport { safeServerAction } from '../../../utils/safeServerAction';\nimport { subscriptionsService } from '../services/subscriptions.service';\n\nexport async function getCurrentSubscriptionAction(params?: { id_product?: number }) {\n return safeServerAction(() => subscriptionsService.getCurrentSubscription(params));\n}\n"],"mappings":";AAEA,SAAS,wBAAwB;AACjC,SAAS,4BAA4B;AAErC,eAAsB,6BAA6B,QAAkC;AACnF,SAAO,iBAAiB,MAAM,qBAAqB,uBAAuB,MAAM,CAAC;AACnF;","names":[]}
|
|
@@ -3,10 +3,12 @@ const ACCOUNT_FREEZE_AFTER_DAYS = 7;
|
|
|
3
3
|
const FREEZE_WARNING_DAYS_BEFORE = 1;
|
|
4
4
|
const FREEZE_ELIGIBLE_WHITELABEL_ID = 1;
|
|
5
5
|
const MAX_PAYMENT_ATTEMPTS = 8;
|
|
6
|
+
const FREE_PLAN_ID = 5;
|
|
6
7
|
export {
|
|
7
8
|
ACCOUNT_FREEZE_AFTER_DAYS,
|
|
8
9
|
FREEZE_ELIGIBLE_WHITELABEL_ID,
|
|
9
10
|
FREEZE_WARNING_DAYS_BEFORE,
|
|
11
|
+
FREE_PLAN_ID,
|
|
10
12
|
MAX_PAYMENT_ATTEMPTS,
|
|
11
13
|
SUBSCRIPTION_GRACE_PERIOD_DAYS
|
|
12
14
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/constants/subscription.constants.ts"],"sourcesContent":["export const SUBSCRIPTION_GRACE_PERIOD_DAYS = 15;\n\nexport const ACCOUNT_FREEZE_AFTER_DAYS = 7;\nexport const FREEZE_WARNING_DAYS_BEFORE = 1;\n\nexport const FREEZE_ELIGIBLE_WHITELABEL_ID = 1;\n\nexport const MAX_PAYMENT_ATTEMPTS = 8;\n"],"mappings":"AAAO,MAAM,iCAAiC;AAEvC,MAAM,4BAA4B;AAClC,MAAM,6BAA6B;AAEnC,MAAM,gCAAgC;AAEtC,MAAM,uBAAuB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/constants/subscription.constants.ts"],"sourcesContent":["export const SUBSCRIPTION_GRACE_PERIOD_DAYS = 15;\n\nexport const ACCOUNT_FREEZE_AFTER_DAYS = 7;\nexport const FREEZE_WARNING_DAYS_BEFORE = 1;\n\nexport const FREEZE_ELIGIBLE_WHITELABEL_ID = 1;\n\nexport const MAX_PAYMENT_ATTEMPTS = 8;\n\n/** Plano legado \"FREE\" identificado por `id_plan`, fora do `type: 'free'` moderno. */\nexport const FREE_PLAN_ID = 5;\n"],"mappings":"AAAO,MAAM,iCAAiC;AAEvC,MAAM,4BAA4B;AAClC,MAAM,6BAA6B;AAEnC,MAAM,gCAAgC;AAEtC,MAAM,uBAAuB;AAG7B,MAAM,eAAe;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { safeRouteHandler } from "../../../infra/route/safe-route-handler";
|
|
3
|
+
import { searchParamsToParams } from "../../../infra/route/search-params";
|
|
4
|
+
import { subscriptionsService } from "../services/subscriptions.service";
|
|
5
|
+
function createCurrentSubscriptionHandler() {
|
|
6
|
+
return async function GET(req) {
|
|
7
|
+
return safeRouteHandler(
|
|
8
|
+
() => subscriptionsService.getCurrentSubscription(
|
|
9
|
+
searchParamsToParams(req.nextUrl.searchParams)
|
|
10
|
+
)
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
createCurrentSubscriptionHandler
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=current-subscription.handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/handlers/current-subscription.handler.ts"],"sourcesContent":["import 'server-only';\n\nimport type { NextRequest } from 'next/server';\nimport { safeRouteHandler } from '../../../infra/route/safe-route-handler';\nimport { searchParamsToParams } from '../../../infra/route/search-params';\nimport { subscriptionsService } from '../services/subscriptions.service';\n\nexport function createCurrentSubscriptionHandler() {\n return async function GET(req: NextRequest) {\n return safeRouteHandler(() =>\n subscriptionsService.getCurrentSubscription(\n searchParamsToParams<{ id_product?: number }>(req.nextUrl.searchParams),\n ),\n );\n };\n}\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,wBAAwB;AACjC,SAAS,4BAA4B;AACrC,SAAS,4BAA4B;AAE9B,SAAS,mCAAmC;AACjD,SAAO,eAAe,IAAI,KAAkB;AAC1C,WAAO;AAAA,MAAiB,MACtB,qBAAqB;AAAA,QACnB,qBAA8C,IAAI,QAAQ,YAAY;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { apiFetch } from "../../../infra/http/api-fetch";
|
|
4
|
-
import { SUBSCRIPTIONS_QUERY_KEY } from "../constants/query-keys.constants";
|
|
5
|
-
import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
|
|
2
|
+
import { useCurrentSubscription } from "./use-current-subscription.hook";
|
|
6
3
|
function useActiveSubscription() {
|
|
7
|
-
|
|
8
|
-
return useQuery({
|
|
9
|
-
queryKey,
|
|
10
|
-
queryFn: ({ signal }) => apiFetch("/api/subscriptions", {
|
|
11
|
-
params: { limit: 1 },
|
|
12
|
-
signal
|
|
13
|
-
}),
|
|
14
|
-
staleTime: 1e4
|
|
15
|
-
});
|
|
4
|
+
return useCurrentSubscription();
|
|
16
5
|
}
|
|
17
6
|
export {
|
|
18
7
|
useActiveSubscription
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/find-active-subscription.hook.ts"],"sourcesContent":["\"use client\";\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/find-active-subscription.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useCurrentSubscription } from \"./use-current-subscription.hook\";\n\n/**\n * @deprecated Use `useCurrentSubscription`, mesma leitura (mesma chave de\n * cache, uma única requisição); só o nome muda para o vocabulário certo\n * (\"corrente\", não \"ativa\"). Sai na 1.1.784.\n */\nexport function useActiveSubscription() {\n return useCurrentSubscription();\n}\n"],"mappings":";AAEA,SAAS,8BAA8B;AAOhC,SAAS,wBAAwB;AACtC,SAAO,uBAAuB;AAChC;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { apiFetch } from "../../../infra/http/api-fetch";
|
|
4
|
+
import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
|
|
5
|
+
import { SUBSCRIPTIONS_QUERY_KEY } from "../constants/query-keys.constants";
|
|
6
|
+
function useCurrentSubscription() {
|
|
7
|
+
const queryKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY, "current"]);
|
|
8
|
+
return useQuery({
|
|
9
|
+
queryKey,
|
|
10
|
+
queryFn: async ({ signal }) => {
|
|
11
|
+
const result = await apiFetch(
|
|
12
|
+
"/api/subscriptions/current",
|
|
13
|
+
{ signal }
|
|
14
|
+
);
|
|
15
|
+
const subscription = result.data ?? null;
|
|
16
|
+
return {
|
|
17
|
+
success: true,
|
|
18
|
+
data: subscription ? [subscription] : [],
|
|
19
|
+
total: subscription ? 1 : 0
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
staleTime: 1e4
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
useCurrentSubscription
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=use-current-subscription.hook.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/use-current-subscription.hook.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { apiFetch } from '../../../infra/http/api-fetch';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport type { PaginatedSuccessResult, SuccessResult } from '../../../infra/api/types';\nimport type { Subscription } from '../types/subscription.type';\nimport { SUBSCRIPTIONS_QUERY_KEY } from '../constants/query-keys.constants';\n\n/**\n * A assinatura corrente do plano da conta, pela regra única do backend\n * (contrato de domínio nunca é corrente; encerrada é decidida por `active`).\n *\n * Devolvida na mesma forma paginada de `useSubscriptions`/`useActiveSubscription`\n * (`{ data: [sub] }`) para não exigir mudança nos call sites que já leem\n * `data?.[0]`: só a URL e a regra por trás mudam.\n */\nexport function useCurrentSubscription() {\n const queryKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY, 'current']);\n\n return useQuery({\n queryKey,\n queryFn: async ({ signal }): Promise<PaginatedSuccessResult<Subscription>> => {\n const result = await apiFetch<SuccessResult<Subscription | null>>(\n '/api/subscriptions/current',\n { signal },\n );\n const subscription = result.data ?? null;\n\n return {\n success: true,\n data: subscription ? [subscription] : [],\n total: subscription ? 1 : 0,\n };\n },\n staleTime: 10_000,\n });\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAGhC,SAAS,+BAA+B;AAUjC,SAAS,yBAAyB;AACvC,QAAM,WAAW,gBAAgB,CAAC,GAAG,yBAAyB,SAAS,CAAC;AAExE,SAAO,SAAS;AAAA,IACd;AAAA,IACA,SAAS,OAAO,EAAE,OAAO,MAAqD;AAC5E,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,EAAE,OAAO;AAAA,MACX;AACA,YAAM,eAAe,OAAO,QAAQ;AAEpC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,CAAC,YAAY,IAAI,CAAC;AAAA,QACvC,OAAO,eAAe,IAAI;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,WAAW;AAAA,EACb,CAAC;AACH;","names":[]}
|
|
@@ -38,6 +38,32 @@ class SubscriptionsService {
|
|
|
38
38
|
success: true
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A assinatura corrente do plano da conta, pela regra única do backend
|
|
43
|
+
* (`GET .../subscriptions/action/current`): contrato de domínio nunca é
|
|
44
|
+
* corrente, encerrada é decidida por `active`. Sem `id_product`, paridade
|
|
45
|
+
* com hoje (melhor rank entre todos os produtos, nunca item/domínio).
|
|
46
|
+
*/
|
|
47
|
+
async getCurrentSubscription(params) {
|
|
48
|
+
const { id_account } = await getUserContext();
|
|
49
|
+
const query = buildQueryParams(
|
|
50
|
+
params
|
|
51
|
+
);
|
|
52
|
+
const url = `/accounts/${id_account}/subscriptions/action/current${query ? `?${query}` : ""}`;
|
|
53
|
+
const response = await api.apps.get(url);
|
|
54
|
+
if (response.status === 0) {
|
|
55
|
+
throw new ApiError(
|
|
56
|
+
response.message || "Erro ao consultar assinatura corrente",
|
|
57
|
+
"GET_CURRENT_SUBSCRIPTION_FAILED",
|
|
58
|
+
400
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
const raw = response.data[0];
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
data: raw ? SubscriptionSchema.parse(raw) : null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
41
67
|
async calculateSubscription(data) {
|
|
42
68
|
const { id_account } = await getUserContext();
|
|
43
69
|
const response = await api.apps.post(`/accounts/${id_account}/subscriptions/action/calculate`, data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/services/subscriptions.service.ts"],"sourcesContent":["import \"server-only\";\r\n\r\nimport { api } from \"../../../infra/api/client\";\r\nimport {\r\n ApiError,\r\n ApiActionResult,\r\n ApiPaginatedActionResult,\r\n PaginatedSuccessResult,\r\n SuccessResult,\r\n} from \"../../../infra/api/types\";\r\nimport { buildQueryParams } from \"../../../infra/utils/params\";\r\nimport { assertManagementPermission } from \"../../auth/utils/assert-management-permission\";\r\nimport { getUserContext } from \"../../auth/utils/get-user-context\";\r\nimport {\r\n Subscription,\r\n SubscriptionSchema,\r\n FindSubscriptionsParams,\r\n} from \"../types/subscription.type\";\r\nimport {\r\n type CalculateSubscriptionRequest,\r\n type CalculateSubscriptionResponse,\r\n type UpdateSubscriptionPlanRequest,\r\n CalculateSubscriptionResponseSchema,\r\n} from \"../types/calculate-subscription.type\";\r\nimport {\r\n SubscriptionPendingPixResponseSchema,\r\n type SubscriptionPendingPixResponse,\r\n} from \"../types/pix-pending.type\";\r\nimport type { FreezeNotice } from \"../types/freeze-notice.type\";\r\n\r\n/**\r\n * Timeout das mutations de assinatura que ESCREVEM (action/plan e action/payment).\r\n *\r\n * O default do ApiClient é 30s. Essas duas rotas caem no mesmo motor pesado do\r\n * backend (planEdit delega pra paymentEdit) — troca de método de pagamento, mandato\r\n * PIX recorrente Woovi, void-and-replace de fatura — e passam de 30s com alguma\r\n * frequência: em produção elas aparecem como `outcome=canceled` com wallTime ~29.9s.\r\n *\r\n * O problema não é o usuário ver \"Tempo excedido\": é que o abort MATA o worker no\r\n * meio da saga. Se o corte cai depois de criar o mandato Woovi e suprimir a fatura\r\n * de ciclo, sobra mandato órfão + fatura sem dunning, o usuário não recebe QR nenhum\r\n * e a próxima tentativa cria um segundo mandato. Deixar a request terminar converte\r\n * uma escrita parcial numa escrita completa.\r\n *\r\n * Não é a solução definitiva — a rota deveria ser assíncrona (202 + polling no\r\n * correlation_id). É o teto que dá folga enquanto isso.\r\n *\r\n * Só nas mutations: calculateSubscription é idempotente (não escreve nada), então\r\n * um timeout nela não deixa rastro e não precisa esperar mais.\r\n */\r\nconst SUBSCRIPTION_WRITE_TIMEOUT_MS = 60000;\r\n\r\nexport type UpdateSubscriptionPlanResponse =\r\n | Subscription\r\n | SubscriptionPendingPixResponse;\r\n\r\nclass SubscriptionsService {\r\n async listSubscriptions(\r\n params?: Partial<FindSubscriptionsParams>,\r\n ): Promise<PaginatedSuccessResult<Subscription>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | boolean | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : \"\"}`;\r\n\r\n const response =\r\n await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao listar assinaturas\",\r\n \"LIST_SUBSCRIPTIONS_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const data = response.data.map((item) => SubscriptionSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total,\r\n success: true,\r\n } satisfies PaginatedSuccessResult<Subscription>;\r\n }\r\n\r\n async calculateSubscription(\r\n data: CalculateSubscriptionRequest,\r\n ): Promise<SuccessResult<CalculateSubscriptionResponse>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<\r\n ApiActionResult<CalculateSubscriptionResponse>\r\n >(`/accounts/${id_account}/subscriptions/action/calculate`, data);\r\n\r\n console.log(\"Calculate subscription response:\", {\r\n response,\r\n data: JSON.stringify(data, null, 2),\r\n });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao calcular assinatura\",\r\n \"CALCULATE_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n data: CalculateSubscriptionResponseSchema.parse(response.data),\r\n };\r\n }\r\n\r\n async updateSubscriptionPlan(\r\n subscriptionId: number | string,\r\n data: UpdateSubscriptionPlanRequest,\r\n ): Promise<\r\n SuccessResult<UpdateSubscriptionPlanResponse | null> & { client_secret?: string }\r\n > {\r\n await assertManagementPermission('manage_subscriptions');\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<\r\n ApiActionResult<UpdateSubscriptionPlanResponse | UpdateSubscriptionPlanResponse[]> & {\r\n client_secret?: string;\r\n }\r\n >(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/plan`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar plano\",\r\n \"UPDATE_SUBSCRIPTION_PLAN_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = Array.isArray(response.data) ? response.data[0] : response.data;\r\n\r\n // Preserva resposta pix-pending (id: null, pending: true, pix: {...})\r\n // pra clientes poderem rotear o user pra fluxo dedicado de pix em vez\r\n // de tratar como subscription comum. Caso contrário, parseia como\r\n // Subscription (compatível com o comportamento antigo de devolver { id }).\r\n let parsed: UpdateSubscriptionPlanResponse | null = null;\r\n if (raw) {\r\n const pixResult = SubscriptionPendingPixResponseSchema.safeParse(raw);\r\n parsed = pixResult.success ? pixResult.data : SubscriptionSchema.parse(raw);\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsed,\r\n ...(response.client_secret ? { client_secret: response.client_secret } : {}),\r\n };\r\n }\r\n\r\n async updateSubscriptionPayment(\r\n subscriptionId: number | string,\r\n data: { id_card?: string; payment_method_id?: string; payment_method: number },\r\n ): Promise<SuccessResult<void>> {\r\n await assertManagementPermission(\"manage_subscriptions\");\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<ApiActionResult<void>>(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/payment`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar método de pagamento\",\r\n \"UPDATE_SUBSCRIPTION_PAYMENT_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n\r\n async getFreezeNotice(): Promise<FreezeNotice> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.get<ApiActionResult<FreezeNotice>>(\r\n `/accounts/${id_account}/notices/freeze`,\r\n );\r\n\r\n if (response.status === 0 || !response.data) {\r\n throw new ApiError(\r\n (response.status === 0 ? response.message : \"\") ||\r\n \"Erro ao consultar aviso de congelamento\",\r\n \"GET_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return response.data;\r\n }\r\n\r\n async ackFreezeNotice(cycle: string): Promise<SuccessResult<void>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<ApiActionResult<void>>(\r\n `/accounts/${id_account}/notices/freeze/ack`,\r\n { cycle },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao confirmar aviso de congelamento\",\r\n \"ACK_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n}\r\n\r\nexport const subscriptionsService = new SubscriptionsService();\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/services/subscriptions.service.ts"],"sourcesContent":["import \"server-only\";\r\n\r\nimport { api } from \"../../../infra/api/client\";\r\nimport {\r\n ApiError,\r\n ApiActionResult,\r\n ApiPaginatedActionResult,\r\n PaginatedSuccessResult,\r\n SuccessResult,\r\n} from \"../../../infra/api/types\";\r\nimport { buildQueryParams } from \"../../../infra/utils/params\";\r\nimport { assertManagementPermission } from \"../../auth/utils/assert-management-permission\";\r\nimport { getUserContext } from \"../../auth/utils/get-user-context\";\r\nimport {\r\n Subscription,\r\n SubscriptionSchema,\r\n FindSubscriptionsParams,\r\n} from \"../types/subscription.type\";\r\nimport {\r\n type CalculateSubscriptionRequest,\r\n type CalculateSubscriptionResponse,\r\n type UpdateSubscriptionPlanRequest,\r\n CalculateSubscriptionResponseSchema,\r\n} from \"../types/calculate-subscription.type\";\r\nimport {\r\n SubscriptionPendingPixResponseSchema,\r\n type SubscriptionPendingPixResponse,\r\n} from \"../types/pix-pending.type\";\r\nimport type { FreezeNotice } from \"../types/freeze-notice.type\";\r\n\r\n/**\r\n * Timeout das mutations de assinatura que ESCREVEM (action/plan e action/payment).\r\n *\r\n * O default do ApiClient é 30s. Essas duas rotas caem no mesmo motor pesado do\r\n * backend (planEdit delega pra paymentEdit) — troca de método de pagamento, mandato\r\n * PIX recorrente Woovi, void-and-replace de fatura — e passam de 30s com alguma\r\n * frequência: em produção elas aparecem como `outcome=canceled` com wallTime ~29.9s.\r\n *\r\n * O problema não é o usuário ver \"Tempo excedido\": é que o abort MATA o worker no\r\n * meio da saga. Se o corte cai depois de criar o mandato Woovi e suprimir a fatura\r\n * de ciclo, sobra mandato órfão + fatura sem dunning, o usuário não recebe QR nenhum\r\n * e a próxima tentativa cria um segundo mandato. Deixar a request terminar converte\r\n * uma escrita parcial numa escrita completa.\r\n *\r\n * Não é a solução definitiva — a rota deveria ser assíncrona (202 + polling no\r\n * correlation_id). É o teto que dá folga enquanto isso.\r\n *\r\n * Só nas mutations: calculateSubscription é idempotente (não escreve nada), então\r\n * um timeout nela não deixa rastro e não precisa esperar mais.\r\n */\r\nconst SUBSCRIPTION_WRITE_TIMEOUT_MS = 60000;\r\n\r\nexport type UpdateSubscriptionPlanResponse =\r\n | Subscription\r\n | SubscriptionPendingPixResponse;\r\n\r\nclass SubscriptionsService {\r\n async listSubscriptions(\r\n params?: Partial<FindSubscriptionsParams>,\r\n ): Promise<PaginatedSuccessResult<Subscription>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | boolean | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : \"\"}`;\r\n\r\n const response =\r\n await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao listar assinaturas\",\r\n \"LIST_SUBSCRIPTIONS_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const data = response.data.map((item) => SubscriptionSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total,\r\n success: true,\r\n } satisfies PaginatedSuccessResult<Subscription>;\r\n }\r\n\r\n /**\r\n * A assinatura corrente do plano da conta, pela regra única do backend\r\n * (`GET .../subscriptions/action/current`): contrato de domínio nunca é\r\n * corrente, encerrada é decidida por `active`. Sem `id_product`, paridade\r\n * com hoje (melhor rank entre todos os produtos, nunca item/domínio).\r\n */\r\n async getCurrentSubscription(\r\n params?: { id_product?: number },\r\n ): Promise<SuccessResult<Subscription | null>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(\r\n params as Record<string, string | number | undefined>,\r\n );\r\n const url = `/accounts/${id_account}/subscriptions/action/current${query ? `?${query}` : \"\"}`;\r\n\r\n const response = await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao consultar assinatura corrente\",\r\n \"GET_CURRENT_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = response.data[0];\r\n\r\n return {\r\n success: true,\r\n data: raw ? SubscriptionSchema.parse(raw) : null,\r\n };\r\n }\r\n\r\n async calculateSubscription(\r\n data: CalculateSubscriptionRequest,\r\n ): Promise<SuccessResult<CalculateSubscriptionResponse>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<\r\n ApiActionResult<CalculateSubscriptionResponse>\r\n >(`/accounts/${id_account}/subscriptions/action/calculate`, data);\r\n\r\n console.log(\"Calculate subscription response:\", {\r\n response,\r\n data: JSON.stringify(data, null, 2),\r\n });\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao calcular assinatura\",\r\n \"CALCULATE_SUBSCRIPTION_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n data: CalculateSubscriptionResponseSchema.parse(response.data),\r\n };\r\n }\r\n\r\n async updateSubscriptionPlan(\r\n subscriptionId: number | string,\r\n data: UpdateSubscriptionPlanRequest,\r\n ): Promise<\r\n SuccessResult<UpdateSubscriptionPlanResponse | null> & { client_secret?: string }\r\n > {\r\n await assertManagementPermission('manage_subscriptions');\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<\r\n ApiActionResult<UpdateSubscriptionPlanResponse | UpdateSubscriptionPlanResponse[]> & {\r\n client_secret?: string;\r\n }\r\n >(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/plan`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar plano\",\r\n \"UPDATE_SUBSCRIPTION_PLAN_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n const raw = Array.isArray(response.data) ? response.data[0] : response.data;\r\n\r\n // Preserva resposta pix-pending (id: null, pending: true, pix: {...})\r\n // pra clientes poderem rotear o user pra fluxo dedicado de pix em vez\r\n // de tratar como subscription comum. Caso contrário, parseia como\r\n // Subscription (compatível com o comportamento antigo de devolver { id }).\r\n let parsed: UpdateSubscriptionPlanResponse | null = null;\r\n if (raw) {\r\n const pixResult = SubscriptionPendingPixResponseSchema.safeParse(raw);\r\n parsed = pixResult.success ? pixResult.data : SubscriptionSchema.parse(raw);\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsed,\r\n ...(response.client_secret ? { client_secret: response.client_secret } : {}),\r\n };\r\n }\r\n\r\n async updateSubscriptionPayment(\r\n subscriptionId: number | string,\r\n data: { id_card?: string; payment_method_id?: string; payment_method: number },\r\n ): Promise<SuccessResult<void>> {\r\n await assertManagementPermission(\"manage_subscriptions\");\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.put<ApiActionResult<void>>(\r\n `/accounts/${id_account}/subscriptions/${subscriptionId}/action/payment`,\r\n data,\r\n { timeout: SUBSCRIPTION_WRITE_TIMEOUT_MS },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao atualizar método de pagamento\",\r\n \"UPDATE_SUBSCRIPTION_PAYMENT_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n\r\n async getFreezeNotice(): Promise<FreezeNotice> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.get<ApiActionResult<FreezeNotice>>(\r\n `/accounts/${id_account}/notices/freeze`,\r\n );\r\n\r\n if (response.status === 0 || !response.data) {\r\n throw new ApiError(\r\n (response.status === 0 ? response.message : \"\") ||\r\n \"Erro ao consultar aviso de congelamento\",\r\n \"GET_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return response.data;\r\n }\r\n\r\n async ackFreezeNotice(cycle: string): Promise<SuccessResult<void>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const response = await api.apps.post<ApiActionResult<void>>(\r\n `/accounts/${id_account}/notices/freeze/ack`,\r\n { cycle },\r\n );\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || \"Erro ao confirmar aviso de congelamento\",\r\n \"ACK_FREEZE_NOTICE_FAILED\",\r\n 400,\r\n );\r\n }\r\n\r\n return { success: true };\r\n }\r\n}\r\n\r\nexport const subscriptionsService = new SubscriptionsService();\r\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,WAAW;AACpB;AAAA,EACE;AAAA,OAKK;AACP,SAAS,wBAAwB;AACjC,SAAS,kCAAkC;AAC3C,SAAS,sBAAsB;AAC/B;AAAA,EAEE;AAAA,OAEK;AACP;AAAA,EAIE;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAEK;AAuBP,MAAM,gCAAgC;AAMtC,MAAM,qBAAqB;AAAA,EACzB,MAAM,kBACJ,QAC+C;AAC/C,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,QAAQ;AAAA,MACZ;AAAA,IACF;AACA,UAAM,MAAM,aAAa,UAAU,iBAAiB,QAAQ,IAAI,KAAK,KAAK,EAAE;AAE5E,UAAM,WACJ,MAAM,IAAI,KAAK,IAA4C,GAAG;AAEhE,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,SAAS,KAAK,IAAI,CAAC,SAAS,mBAAmB,MAAM,IAAI,CAAC;AAEvE,WAAO;AAAA,MACL;AAAA,MACA,OAAO,SAAS;AAAA,MAChB,SAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,uBACJ,QAC6C;AAC7C,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,QAAQ;AAAA,MACZ;AAAA,IACF;AACA,UAAM,MAAM,aAAa,UAAU,gCAAgC,QAAQ,IAAI,KAAK,KAAK,EAAE;AAE3F,UAAM,WAAW,MAAM,IAAI,KAAK,IAA4C,GAAG;AAE/E,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,SAAS,KAAK,CAAC;AAE3B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,MAAM,mBAAmB,MAAM,GAAG,IAAI;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAM,sBACJ,MACuD;AACvD,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,MAAM,IAAI,KAAK,KAE9B,aAAa,UAAU,mCAAmC,IAAI;AAEhE,YAAQ,IAAI,oCAAoC;AAAA,MAC9C;AAAA,MACA,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,IACpC,CAAC;AAED,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,oCAAoC,MAAM,SAAS,IAAI;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,MAAM,uBACJ,gBACA,MAGA;AACA,UAAM,2BAA2B,sBAAsB;AACvD,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,MAAM,IAAI,KAAK;AAAA,MAK9B,aAAa,UAAU,kBAAkB,cAAc;AAAA,MACvD;AAAA,MACA,EAAE,SAAS,8BAA8B;AAAA,IAC3C;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,QAAQ,SAAS,IAAI,IAAI,SAAS,KAAK,CAAC,IAAI,SAAS;AAMvE,QAAI,SAAgD;AACpD,QAAI,KAAK;AACP,YAAM,YAAY,qCAAqC,UAAU,GAAG;AACpE,eAAS,UAAU,UAAU,UAAU,OAAO,mBAAmB,MAAM,GAAG;AAAA,IAC5E;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,GAAI,SAAS,gBAAgB,EAAE,eAAe,SAAS,cAAc,IAAI,CAAC;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,0BACJ,gBACA,MAC8B;AAC9B,UAAM,2BAA2B,sBAAsB;AACvD,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,MAAM,IAAI,KAAK;AAAA,MAC9B,aAAa,UAAU,kBAAkB,cAAc;AAAA,MACvD;AAAA,MACA,EAAE,SAAS,8BAA8B;AAAA,IAC3C;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,kBAAyC;AAC7C,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,MAAM,IAAI,KAAK;AAAA,MAC9B,aAAa,UAAU;AAAA,IACzB;AAEA,QAAI,SAAS,WAAW,KAAK,CAAC,SAAS,MAAM;AAC3C,YAAM,IAAI;AAAA,SACP,SAAS,WAAW,IAAI,SAAS,UAAU,OAC1C;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,gBAAgB,OAA6C;AACjE,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,MAAM,IAAI,KAAK;AAAA,MAC9B,aAAa,UAAU;AAAA,MACvB,EAAE,MAAM;AAAA,IACV;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACF;AAEO,MAAM,uBAAuB,IAAI,qBAAqB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/types/subscription.type.ts"],"sourcesContent":["import z from 'zod';\r\nimport { calendarDateSchema } from '../../../infra/utils/date';\r\n\r\nexport const SubscriptionItemSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_subscription: z.union([z.number(), z.string()]),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().optional(),\r\n value: z.number().nullable(),\r\n currency: z.string(),\r\n quantity: z.number().nullable().transform((v) => v ?? 0),\r\n payment_status: z.number().nullable(),\r\n id_charge: z.union([z.number(), z.string()]).nullable(),\r\n trial: z.boolean().nullable(),\r\n trial_ends_at: z.coerce.date().nullable().optional(),\r\n});\r\n\r\nexport type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;\r\n\r\nexport const SubscriptionSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_account: z.union([z.number(), z.string()]),\r\n id_card: z.union([z.number(), z.string()]).nullable(),\r\n id_coupon: z.union([z.number(), z.string()]).nullable(),\r\n type: z.string(),\r\n payment_method: z.number().nullable(),\r\n periodicity: z.coerce.number().nullable(),\r\n value: z.number().nullable(),\r\n id_product: z.number(),\r\n id_plan: z.number().nullable(),\r\n plan_name: z.string().nullable().optional(),\r\n id_gateway: z.union([z.number(), z.string()]).nullable(),\r\n gateway_account: z.string().nullable(),\r\n date_hiring: calendarDateSchema().nullable(),\r\n date_due: calendarDateSchema().nullable(),\r\n date_cancellation: calendarDateSchema().nullable(),\r\n date_verification: calendarDateSchema().nullable(),\r\n cancellation_reason: z.string().nullable(),\r\n cancellation_description: z.string().nullable(),\r\n active: z.boolean(),\r\n charged: z.boolean(),\r\n datetime_last_update_periodicity: z.coerce.date().nullable().optional(),\r\n card_name: z.string().nullable().optional(),\r\n card_brand: z.string().nullable().optional(),\r\n card_number: z.string().nullable().optional(),\r\n card: z.object({\r\n id_card: z.union([z.number(), z.string()]).nullable().optional(),\r\n brand: z.string().nullable().optional(),\r\n expires_at: z.string().nullable().optional(),\r\n last4: z.string().nullable().optional(),\r\n name: z.string().nullable().optional(),\r\n }).nullable().optional(),\r\n /* payment_retry: tentativas de cobrança no cartão (Stripe Smart Retries / dunning),\r\n * lidas ao vivo do latest_invoice. null para boleto/pix (sem retries). Ver gapps-r3-api #229. */\r\n payment_retry: z.object({\r\n attempt_count: z.number(),\r\n next_attempt_at: z.string().nullable(),\r\n invoice_status: z.string().nullable(),\r\n }).nullable().optional(),\r\n items: z.array(SubscriptionItemSchema),\r\n pending_change: z.object({\r\n schedule_id: z.string(),\r\n schedule_status: z.string(),\r\n kind: z.string(),\r\n takes_effect_at: z.number().nullable(),\r\n takes_effect_at_iso: z.string().nullable(),\r\n new_plan: z.object({\r\n id: z.number(),\r\n name: z.string(),\r\n periodicity: z.number().nullable(),\r\n value: z.number(),\r\n value_gross: z.number(),\r\n }).nullable(),\r\n new_items: z.array(z.object({\r\n price_id: z.string(),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().nullable(),\r\n quantity: z.number(),\r\n unit_value: z.number(),\r\n value: z.number(),\r\n is_plan_base: z.boolean(),\r\n is_ai_credit: z.boolean(),\r\n })),\r\n new_limits: z.record(z.string(), z.number()).nullable().optional(),\r\n new_discounts: z.array(z.unknown()),\r\n changes: z.array(z.object({\r\n type: z.string(),\r\n id_addon: z.number().nullable().optional(),\r\n addon_name: z.string().nullable().optional(),\r\n quantity: z.number().optional(),\r\n from: z.unknown().optional(),\r\n to: z.unknown().optional(),\r\n })),\r\n }).nullable().optional(),\r\n metadata: z.record(z.string(), z.unknown()).optional(),\r\n});\r\n\r\nexport type Subscription = z.infer<typeof SubscriptionSchema>;\r\n\r\nexport interface FindSubscriptionsParams {\r\n active?: boolean;\r\n trial?: boolean;\r\n date_start?: string;\r\n date_end?: string;\r\n date_hiring_start?: string;\r\n date_hiring_end?: string;\r\n limit?: number;\r\n}\r\n"],"mappings":"AAAA,OAAO,OAAO;AACd,SAAS,0BAA0B;AAE5B,MAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACpC,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,OAAO,KAAK;AAAA,EAC5B,OAAO,EAAE,OAAO;AAAA,EAChB,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACjD,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,EAAE,OAAO;AAAA,EACnB,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC;AAAA,EACvD,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACtD,OAAO,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC5B,eAAe,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AACrD,CAAC;AAIM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACpC,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,OAAO,KAAK;AAAA,EAC5B,OAAO,EAAE,OAAO;AAAA,EAChB,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EAC5C,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACpD,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACtD,MAAM,EAAE,OAAO;AAAA,EACf,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,aAAa,EAAE,OAAO,OAAO,EAAE,SAAS;AAAA,EACxC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,YAAY,EAAE,OAAO;AAAA,EACrB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACvD,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,aAAa,mBAAmB,EAAE,SAAS;AAAA,EAC3C,UAAU,mBAAmB,EAAE,SAAS;AAAA,EACxC,mBAAmB,mBAAmB,EAAE,SAAS;AAAA,EACjD,mBAAmB,mBAAmB,EAAE,SAAS;AAAA,EACjD,qBAAqB,EAAE,OAAO,EAAE,SAAS;AAAA,EACzC,0BAA0B,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9C,QAAQ,EAAE,QAAQ;AAAA,EAClB,SAAS,EAAE,QAAQ;AAAA,EACnB,kCAAkC,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACtE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,MAAM,EAAE,OAAO;AAAA,IACb,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,IAC/D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,IACtC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,IAC3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,IACtC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA;AAAA;AAAA,EAGvB,eAAe,EAAE,OAAO;AAAA,IACtB,eAAe,EAAE,OAAO;AAAA,IACxB,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,IACrC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACvB,OAAO,EAAE,MAAM,sBAAsB;AAAA,EACrC,gBAAgB,EAAE,OAAO;AAAA,IACvB,aAAa,EAAE,OAAO;AAAA,IACtB,iBAAiB,EAAE,OAAO;AAAA,IAC1B,MAAM,EAAE,OAAO;AAAA,IACf,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,IACrC,qBAAqB,EAAE,OAAO,EAAE,SAAS;AAAA,IACzC,UAAU,EAAE,OAAO;AAAA,MACjB,IAAI,EAAE,OAAO;AAAA,MACb,MAAM,EAAE,OAAO;AAAA,MACf,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,OAAO,EAAE,OAAO;AAAA,MAChB,aAAa,EAAE,OAAO;AAAA,IACxB,CAAC,EAAE,SAAS;AAAA,IACZ,WAAW,EAAE,MAAM,EAAE,OAAO;AAAA,MAC1B,UAAU,EAAE,OAAO;AAAA,MACnB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,MAC9B,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,MAChC,UAAU,EAAE,OAAO;AAAA,MACnB,YAAY,EAAE,OAAO;AAAA,MACrB,OAAO,EAAE,OAAO;AAAA,MAChB,cAAc,EAAE,QAAQ;AAAA,MACxB,cAAc,EAAE,QAAQ;AAAA,IAC1B,CAAC,CAAC;AAAA,IACF,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,IACjE,eAAe,EAAE,MAAM,EAAE,QAAQ,CAAC;AAAA,IAClC,SAAS,EAAE,MAAM,EAAE,OAAO;AAAA,MACxB,MAAM,EAAE,OAAO;AAAA,MACf,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,MACzC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,MAC3C,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,MAC9B,MAAM,EAAE,QAAQ,EAAE,SAAS;AAAA,MAC3B,IAAI,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC3B,CAAC,CAAC;AAAA,EACJ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACvB,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AACvD,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/types/subscription.type.ts"],"sourcesContent":["import z from 'zod';\r\nimport { calendarDateSchema } from '../../../infra/utils/date';\r\n\r\nexport const SubscriptionItemSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_subscription: z.union([z.number(), z.string()]),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().optional(),\r\n value: z.number().nullable(),\r\n currency: z.string(),\r\n quantity: z.number().nullable().transform((v) => v ?? 0),\r\n payment_status: z.number().nullable(),\r\n id_charge: z.union([z.number(), z.string()]).nullable(),\r\n trial: z.boolean().nullable(),\r\n trial_ends_at: z.coerce.date().nullable().optional(),\r\n});\r\n\r\nexport type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;\r\n\r\nexport const SubscriptionSchema = z.object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_alt: z.coerce.date().nullable().optional(),\r\n datetime_del: z.coerce.date().nullable().optional(),\r\n datetime_add: z.coerce.date(),\r\n id_wl: z.number(),\r\n id_account: z.union([z.number(), z.string()]),\r\n id_card: z.union([z.number(), z.string()]).nullable(),\r\n id_coupon: z.union([z.number(), z.string()]).nullable(),\r\n type: z.string(),\r\n payment_method: z.number().nullable(),\r\n periodicity: z.coerce.number().nullable(),\r\n value: z.number().nullable(),\r\n id_product: z.number(),\r\n id_plan: z.number().nullable(),\r\n plan_name: z.string().nullable().optional(),\r\n id_gateway: z.union([z.number(), z.string()]).nullable(),\r\n gateway_account: z.string().nullable(),\r\n date_hiring: calendarDateSchema().nullable(),\r\n date_due: calendarDateSchema().nullable(),\r\n date_cancellation: calendarDateSchema().nullable(),\r\n date_verification: calendarDateSchema().nullable(),\r\n cancellation_reason: z.string().nullable(),\r\n cancellation_description: z.string().nullable(),\r\n active: z.boolean(),\r\n charged: z.boolean(),\r\n datetime_last_update_periodicity: z.coerce.date().nullable().optional(),\r\n card_name: z.string().nullable().optional(),\r\n card_brand: z.string().nullable().optional(),\r\n card_number: z.string().nullable().optional(),\r\n card: z.object({\r\n id_card: z.union([z.number(), z.string()]).nullable().optional(),\r\n brand: z.string().nullable().optional(),\r\n expires_at: z.string().nullable().optional(),\r\n last4: z.string().nullable().optional(),\r\n name: z.string().nullable().optional(),\r\n }).nullable().optional(),\r\n /* payment_retry: tentativas de cobrança no cartão (Stripe Smart Retries / dunning),\r\n * lidas ao vivo do latest_invoice. null para boleto/pix (sem retries). Ver gapps-r3-api #229. */\r\n payment_retry: z.object({\r\n attempt_count: z.number(),\r\n next_attempt_at: z.string().nullable(),\r\n invoice_status: z.string().nullable(),\r\n }).nullable().optional(),\r\n items: z.array(SubscriptionItemSchema),\r\n pending_change: z.object({\r\n schedule_id: z.string(),\r\n schedule_status: z.string(),\r\n kind: z.string(),\r\n takes_effect_at: z.number().nullable(),\r\n takes_effect_at_iso: z.string().nullable(),\r\n new_plan: z.object({\r\n id: z.number(),\r\n name: z.string(),\r\n periodicity: z.number().nullable(),\r\n value: z.number(),\r\n value_gross: z.number(),\r\n }).nullable(),\r\n new_items: z.array(z.object({\r\n price_id: z.string(),\r\n id_addon: z.number().nullable(),\r\n addon_name: z.string().nullable(),\r\n quantity: z.number(),\r\n unit_value: z.number(),\r\n value: z.number(),\r\n is_plan_base: z.boolean(),\r\n is_ai_credit: z.boolean(),\r\n })),\r\n new_limits: z.record(z.string(), z.number()).nullable().optional(),\r\n new_discounts: z.array(z.unknown()),\r\n changes: z.array(z.object({\r\n type: z.string(),\r\n id_addon: z.number().nullable().optional(),\r\n addon_name: z.string().nullable().optional(),\r\n quantity: z.number().optional(),\r\n from: z.unknown().optional(),\r\n to: z.unknown().optional(),\r\n })),\r\n }).nullable().optional(),\r\n metadata: z.record(z.string(), z.unknown()).optional(),\r\n});\r\n\r\nexport type Subscription = z.infer<typeof SubscriptionSchema>;\r\n\r\nexport interface FindSubscriptionsParams {\r\n active?: boolean;\r\n trial?: boolean;\r\n date_start?: string;\r\n date_end?: string;\r\n date_hiring_start?: string;\r\n date_hiring_end?: string;\r\n limit?: number;\r\n page?: number;\r\n id_product?: number;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function groupSubscriptionsByProduct(subscriptions) {
|
|
2
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3
|
+
for (const subscription of subscriptions) {
|
|
4
|
+
const group = groups.get(subscription.id_product);
|
|
5
|
+
if (group) {
|
|
6
|
+
group.push(subscription);
|
|
7
|
+
} else {
|
|
8
|
+
groups.set(subscription.id_product, [subscription]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return groups;
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
groupSubscriptionsByProduct
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=group-subscriptions-by-product.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/group-subscriptions-by-product.ts"],"sourcesContent":["import type { Subscription } from '../types/subscription.type';\n\n/**\n * Agrupa assinaturas por `id_product`, preservando a ordem de chegada dentro\n * de cada grupo. Base da seção \"Domínios registrados\" do painel e da aba\n * Assinatura agrupada por produto no admin.\n */\nexport function groupSubscriptionsByProduct(\n subscriptions: Subscription[],\n): Map<number, Subscription[]> {\n const groups = new Map<number, Subscription[]>();\n\n for (const subscription of subscriptions) {\n const group = groups.get(subscription.id_product);\n if (group) {\n group.push(subscription);\n } else {\n groups.set(subscription.id_product, [subscription]);\n }\n }\n\n return groups;\n}\n"],"mappings":"AAOO,SAAS,4BACd,eAC6B;AAC7B,QAAM,SAAS,oBAAI,IAA4B;AAE/C,aAAW,gBAAgB,eAAe;AACxC,UAAM,QAAQ,OAAO,IAAI,aAAa,UAAU;AAChD,QAAI,OAAO;AACT,YAAM,KAAK,YAAY;AAAA,IACzB,OAAO;AACL,aAAO,IAAI,aAAa,YAAY,CAAC,YAAY,CAAC;AAAA,IACpD;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toCalendarDate, daysBetween } from "../../../infra/utils/date";
|
|
2
2
|
import { getSubscriptionCancellationState } from "./get-subscription-cancellation-state";
|
|
3
|
-
|
|
3
|
+
import { FREE_PLAN_ID } from "../constants/subscription.constants";
|
|
4
4
|
function isOverdue(subscription) {
|
|
5
5
|
const retry = subscription.payment_retry;
|
|
6
6
|
if (retry && retry.invoice_status === "open" && retry.attempt_count > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-active-paid-subscription.ts"],"sourcesContent":["import { toCalendarDate, daysBetween } from \"../../../infra/utils/date\";\nimport { getSubscriptionCancellationState } from \"./get-subscription-cancellation-state\";\nimport type { Subscription } from \"../types/subscription.type\";\n\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-active-paid-subscription.ts"],"sourcesContent":["import { toCalendarDate, daysBetween } from \"../../../infra/utils/date\";\nimport { getSubscriptionCancellationState } from \"./get-subscription-cancellation-state\";\nimport { FREE_PLAN_ID } from \"../constants/subscription.constants\";\nimport type { Subscription } from \"../types/subscription.type\";\n\ntype SubscriptionSnapshot = Pick<\n Subscription,\n | \"type\"\n | \"active\"\n | \"id_plan\"\n | \"date_due\"\n | \"date_cancellation\"\n | \"payment_retry\"\n>;\n\n/**\n * Fatura vencida e não paga. Mesmos sinais do SubscriptionBanner:\n * - cartão: dunning do Stripe (Smart Retries) — `invoice_status === 'open'`\n * com tentativa já feita.\n * - boleto/pix (sem retries): `date_due` no passado, ou seja a renovação não\n * foi paga.\n */\nfunction isOverdue(subscription: SubscriptionSnapshot): boolean {\n const retry = subscription.payment_retry;\n if (retry && retry.invoice_status === \"open\" && retry.attempt_count > 0) {\n return true;\n }\n\n const dueDate = toCalendarDate(subscription.date_due);\n const today = toCalendarDate(new Date());\n if (!dueDate || !today) return false;\n\n return daysBetween(dueDate, today) > 0;\n}\n\n/**\n * A conta já tem assinatura paga vigente — contratar outra viraria cobrança\n * duplicada (ver Asana ID-4743). O caminho correto pra ela é trocar de plano\n * ou reativar, não passar pelo checkout de novo.\n *\n * Retorna `false` (ou seja, PODE contratar) para quem legitimamente precisa do\n * checkout mesmo já tendo uma linha de assinatura:\n * - sem assinatura;\n * - `active === false` (baixada, inclusive por falha de pagamento);\n * - free (`type` ou plano legado 5) e trial — são justamente os que fazem upgrade;\n * - cancelada cujo período já terminou (`getSubscriptionCancellationState`);\n * - em atraso — precisa contratar pra regularizar.\n *\n * Cancelamento ainda dentro do período (`pending`) conta como vigente e\n * bloqueia: o serviço continua ativo e pago, e o caminho é reativar.\n */\nexport function hasActivePaidSubscription(\n subscription: SubscriptionSnapshot | undefined | null,\n): boolean {\n if (!subscription) return false;\n if (!subscription.active) return false;\n if (subscription.type === \"free\" || subscription.id_plan === FREE_PLAN_ID) {\n return false;\n }\n if (subscription.type === \"trial\") return false;\n if (getSubscriptionCancellationState(subscription) === \"cancelled\") {\n return false;\n }\n if (isOverdue(subscription)) return false;\n\n return true;\n}\n"],"mappings":"AAAA,SAAS,gBAAgB,mBAAmB;AAC5C,SAAS,wCAAwC;AACjD,SAAS,oBAAoB;AAoB7B,SAAS,UAAU,cAA6C;AAC9D,QAAM,QAAQ,aAAa;AAC3B,MAAI,SAAS,MAAM,mBAAmB,UAAU,MAAM,gBAAgB,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,eAAe,aAAa,QAAQ;AACpD,QAAM,QAAQ,eAAe,oBAAI,KAAK,CAAC;AACvC,MAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAE/B,SAAO,YAAY,SAAS,KAAK,IAAI;AACvC;AAkBO,SAAS,0BACd,cACS;AACT,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,CAAC,aAAa,OAAQ,QAAO;AACjC,MAAI,aAAa,SAAS,UAAU,aAAa,YAAY,cAAc;AACzE,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,QAAS,QAAO;AAC1C,MAAI,iCAAiC,YAAY,MAAM,aAAa;AAClE,WAAO;AAAA,EACT;AACA,MAAI,UAAU,YAAY,EAAG,QAAO;AAEpC,SAAO;AACT;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-paid-subscription.ts"],"sourcesContent":["import type { Subscription } from '../types/subscription.type';\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/has-paid-subscription.ts"],"sourcesContent":["import type { Subscription } from '../types/subscription.type';\nimport { FREE_PLAN_ID } from '../constants/subscription.constants';\n\ntype PaidSubscriptionSnapshot = Pick<Subscription, 'type' | 'id_plan' | 'charged'>;\n\n/**\n * A conta já teve pagamento confirmado numa assinatura paga.\n *\n * Diferente de `hasActivePaidSubscription`, aqui NÃO importa se a assinatura\n * segue vigente: quem pagou e depois cancelou/atrasou continua precisando de\n * nota fiscal do que já foi cobrado. Por isso o sinal é `charged` (marcado pelo\n * backend no `bill_paid`), e não `active`/`date_due`.\n *\n * Free e trial ficam de fora — nunca houve cobrança a documentar.\n */\nexport function hasPaidSubscription(\n subscription: PaidSubscriptionSnapshot | undefined | null,\n): boolean {\n if (!subscription) return false;\n if (subscription.type === 'free' || subscription.id_plan === FREE_PLAN_ID) {\n return false;\n }\n if (subscription.type === 'trial') return false;\n\n return subscription.charged === true;\n}\n"],"mappings":"AACA,SAAS,oBAAoB;AActB,SAAS,oBACd,cACS;AACT,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,aAAa,SAAS,UAAU,aAAa,YAAY,cAAc;AACzE,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,QAAS,QAAO;AAE1C,SAAO,aAAa,YAAY;AAClC;","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { cache } from "react";
|
|
3
|
+
import { ApiError } from "../../../infra/api/types";
|
|
4
|
+
import { getCurrentSubscriptionAction } from "../actions/get-current-subscription.action";
|
|
5
|
+
const readCurrentSubscription = cache(
|
|
6
|
+
async () => {
|
|
7
|
+
const result = await getCurrentSubscriptionAction();
|
|
8
|
+
if (!result.success) {
|
|
9
|
+
throw new ApiError(
|
|
10
|
+
result.error || "N\xE3o foi poss\xEDvel validar a assinatura atual",
|
|
11
|
+
"SUBSCRIPTION_CONTEXT_FAILED",
|
|
12
|
+
403
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return result.data?.data ?? null;
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
export {
|
|
19
|
+
readCurrentSubscription
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=read-current-subscription.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/utils/read-current-subscription.ts"],"sourcesContent":["import \"server-only\";\n\nimport { cache } from \"react\";\n\nimport { ApiError } from \"../../../infra/api/types\";\nimport { getCurrentSubscriptionAction } from \"../actions/get-current-subscription.action\";\nimport type { Subscription } from \"../types/subscription.type\";\n\n/**\n * Leitura única por request de \"qual é a assinatura corrente do plano desta\n * conta\", deduplicada via React.cache. Os quatro gates de auth\n * (`assertAccountNotFrozen`, `assertManagementSubscription`,\n * `assertPaidSubscription`, `assertSubscriptionAddon`) chamam esta função em\n * vez de repetir a própria leitura cada um: eram até 4 cópias da mesma\n * pergunta disputando a mesma fila (0,7 a 7s cada, medido em produção).\n *\n * Lança em falha de leitura; `assertAccountNotFrozen` é o único chamador que\n * precisa de fail-open, e faz isso capturando o erro no próprio call site.\n */\nexport const readCurrentSubscription = cache(\n async (): Promise<Subscription | null> => {\n const result = await getCurrentSubscriptionAction();\n\n if (!result.success) {\n throw new ApiError(\n result.error || \"Não foi possível validar a assinatura atual\",\n \"SUBSCRIPTION_CONTEXT_FAILED\",\n 403,\n );\n }\n\n return result.data?.data ?? null;\n },\n);\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,aAAa;AAEtB,SAAS,gBAAgB;AACzB,SAAS,oCAAoC;AActC,MAAM,0BAA0B;AAAA,EACrC,YAA0C;AACxC,UAAM,SAAS,MAAM,6BAA6B;AAElD,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI;AAAA,QACR,OAAO,SAAS;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,MAAM,QAAQ;AAAA,EAC9B;AACF;","names":[]}
|