@greatapps/common 1.1.605 → 1.1.607
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/modals/cards/AddCardModal.mjs +150 -64
- package/dist/components/modals/cards/AddCardModal.mjs.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/dist/modules/cards/actions/create-setup-intent.action.mjs +10 -0
- package/dist/modules/cards/actions/create-setup-intent.action.mjs.map +1 -0
- package/dist/modules/cards/hooks/create-setup-intent.hook.mjs +13 -0
- package/dist/modules/cards/hooks/create-setup-intent.hook.mjs.map +1 -0
- package/dist/modules/cards/services/cards.service.mjs +18 -1
- package/dist/modules/cards/services/cards.service.mjs.map +1 -1
- package/dist/modules/cards/types.mjs +8 -2
- package/dist/modules/cards/types.mjs.map +1 -1
- package/dist/server.mjs +2 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/modals/cards/AddCardModal.tsx +159 -68
- package/src/index.ts +3 -0
- package/src/modules/cards/actions/create-setup-intent.action.ts +8 -0
- package/src/modules/cards/hooks/create-setup-intent.hook.ts +11 -0
- package/src/modules/cards/services/cards.service.ts +23 -1
- package/src/modules/cards/types.ts +9 -1
- package/src/server.ts +1 -0
|
@@ -4,8 +4,25 @@ import { ApiError } from "../../../infra/api/types";
|
|
|
4
4
|
import { buildQueryParams } from "../../../infra/utils/params";
|
|
5
5
|
import { assertManagementPermission } from "../../auth/utils/assert-management-permission";
|
|
6
6
|
import { getUserContext } from "../../auth/utils/get-user-context";
|
|
7
|
-
import { CardSchema } from "../types";
|
|
7
|
+
import { CardSchema, SetupIntentSchema } from "../types";
|
|
8
8
|
class CardsService {
|
|
9
|
+
async createSetupIntent() {
|
|
10
|
+
await assertManagementPermission("manage_cards");
|
|
11
|
+
const { id_account } = await getUserContext();
|
|
12
|
+
const response = await api.apps.post(
|
|
13
|
+
`/accounts/${id_account}/cards/action/setup-intent`,
|
|
14
|
+
{}
|
|
15
|
+
);
|
|
16
|
+
if (response.status === 0) {
|
|
17
|
+
throw new ApiError(
|
|
18
|
+
response.message || "Erro ao criar SetupIntent",
|
|
19
|
+
"CREATE_SETUP_INTENT_FAILED",
|
|
20
|
+
400
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const data = SetupIntentSchema.parse(response.data[0]);
|
|
24
|
+
return { success: true, data };
|
|
25
|
+
}
|
|
9
26
|
async listCards() {
|
|
10
27
|
await assertManagementPermission("view_cards");
|
|
11
28
|
const { id_account } = await getUserContext();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/cards/services/cards.service.ts"],"sourcesContent":["import 'server-only';\n\nimport { api } from '../../../infra/api/client';\nimport { ApiError, ApiActionResult, ApiPaginatedActionResult, PaginatedSuccessResult, SuccessResult } from '../../../infra/api/types';\nimport { buildQueryParams } from '../../../infra/utils/params';\nimport { assertManagementPermission } from '../../auth/utils/assert-management-permission';\nimport { getUserContext } from '../../auth/utils/get-user-context';\nimport { Card, CardSchema, CreateCardRequest } from '../types';\n\nclass CardsService {\n async listCards(): Promise<PaginatedSuccessResult<Card>> {\n await assertManagementPermission('view_cards');\n const { id_account } = await getUserContext();\n\n const query = buildQueryParams({ id_account });\n const response = await api.apps.get<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards?${query}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar cartões',\n 'LIST_CARDS_FAILED',\n 400\n );\n }\n\n const data = response.data.map((item) => CardSchema.parse(item));\n\n return { data, total: response.total, success: true };\n }\n\n async findCardById(id: number): Promise<SuccessResult<Card>> {\n await assertManagementPermission('view_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.get<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards/${id}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao buscar cartão',\n 'FIND_CARD_BY_ID_FAILED',\n 400\n );\n }\n\n if (!response.data?.length) {\n throw new ApiError('Cartão não encontrado', 'CARD_NOT_FOUND', 404);\n }\n\n const data = CardSchema.parse(response.data[0]);\n\n return { success: true, data };\n }\n\n async createCard(data: CreateCardRequest): Promise<SuccessResult<Card>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.post<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards`,\n data\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao criar cartão', 'CREATE_CARD_FAILED', 400);\n }\n\n const cardData = CardSchema.parse(response.data[0]);\n\n return { success: true, data: cardData };\n }\n\n async setDefaultCard(cardId: string): Promise<SuccessResult<Card>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.put<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards/${cardId}`,\n { set_default: true }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao definir cartão padrão',\n 'SET_DEFAULT_CARD_FAILED',\n 400\n );\n }\n\n const data = CardSchema.parse(response.data[0]);\n\n return { success: true, data };\n }\n\n async deleteCard(id: string): Promise<SuccessResult<void>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.delete<ApiActionResult<void>>(\n `/accounts/${id_account}/cards/${id}`\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao excluir cartão', 'DELETE_CARD_FAILED', 400);\n }\n\n return { success: true };\n }\n}\n\nexport const cardsService = new CardsService();\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,WAAW;AACpB,SAAS,gBAAkG;AAC3G,SAAS,wBAAwB;AACjC,SAAS,kCAAkC;AAC3C,SAAS,sBAAsB;AAC/B,SAAe,
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/cards/services/cards.service.ts"],"sourcesContent":["import 'server-only';\n\nimport { api } from '../../../infra/api/client';\nimport { ApiError, ApiActionResult, ApiPaginatedActionResult, PaginatedSuccessResult, SuccessResult } from '../../../infra/api/types';\nimport { buildQueryParams } from '../../../infra/utils/params';\nimport { assertManagementPermission } from '../../auth/utils/assert-management-permission';\nimport { getUserContext } from '../../auth/utils/get-user-context';\nimport { Card, CardSchema, CreateCardRequest, SetupIntent, SetupIntentSchema } from '../types';\n\nclass CardsService {\n async createSetupIntent(): Promise<SuccessResult<SetupIntent>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.post<ApiPaginatedActionResult<SetupIntent>>(\n `/accounts/${id_account}/cards/action/setup-intent`,\n {}\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao criar SetupIntent',\n 'CREATE_SETUP_INTENT_FAILED',\n 400\n );\n }\n\n const data = SetupIntentSchema.parse(response.data[0]);\n\n return { success: true, data };\n }\n\n async listCards(): Promise<PaginatedSuccessResult<Card>> {\n await assertManagementPermission('view_cards');\n const { id_account } = await getUserContext();\n\n const query = buildQueryParams({ id_account });\n const response = await api.apps.get<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards?${query}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao listar cartões',\n 'LIST_CARDS_FAILED',\n 400\n );\n }\n\n const data = response.data.map((item) => CardSchema.parse(item));\n\n return { data, total: response.total, success: true };\n }\n\n async findCardById(id: number): Promise<SuccessResult<Card>> {\n await assertManagementPermission('view_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.get<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards/${id}`\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao buscar cartão',\n 'FIND_CARD_BY_ID_FAILED',\n 400\n );\n }\n\n if (!response.data?.length) {\n throw new ApiError('Cartão não encontrado', 'CARD_NOT_FOUND', 404);\n }\n\n const data = CardSchema.parse(response.data[0]);\n\n return { success: true, data };\n }\n\n async createCard(data: CreateCardRequest): Promise<SuccessResult<Card>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.post<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards`,\n data\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao criar cartão', 'CREATE_CARD_FAILED', 400);\n }\n\n const cardData = CardSchema.parse(response.data[0]);\n\n return { success: true, data: cardData };\n }\n\n async setDefaultCard(cardId: string): Promise<SuccessResult<Card>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.put<ApiPaginatedActionResult<Card>>(\n `/accounts/${id_account}/cards/${cardId}`,\n { set_default: true }\n );\n\n if (response.status === 0) {\n throw new ApiError(\n response.message || 'Erro ao definir cartão padrão',\n 'SET_DEFAULT_CARD_FAILED',\n 400\n );\n }\n\n const data = CardSchema.parse(response.data[0]);\n\n return { success: true, data };\n }\n\n async deleteCard(id: string): Promise<SuccessResult<void>> {\n await assertManagementPermission('manage_cards');\n const { id_account } = await getUserContext();\n\n const response = await api.apps.delete<ApiActionResult<void>>(\n `/accounts/${id_account}/cards/${id}`\n );\n\n if (response.status === 0) {\n throw new ApiError(response.message || 'Erro ao excluir cartão', 'DELETE_CARD_FAILED', 400);\n }\n\n return { success: true };\n }\n}\n\nexport const cardsService = new CardsService();\n"],"mappings":"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","names":[]}
|
|
@@ -22,15 +22,21 @@ const CardSchema = z.object({
|
|
|
22
22
|
datetime_del: z.union([z.date(), z.coerce.date()]).nullable().optional()
|
|
23
23
|
});
|
|
24
24
|
const CreateCardRequestSchema = z.object({
|
|
25
|
-
|
|
25
|
+
setup_intent_id: z.string().min(1, "setup_intent_id \xE9 obrigat\xF3rio"),
|
|
26
|
+
name: z.string().min(1, "name \xE9 obrigat\xF3rio"),
|
|
26
27
|
set_default: z.boolean().optional()
|
|
27
28
|
});
|
|
29
|
+
const SetupIntentSchema = z.object({
|
|
30
|
+
client_secret: z.string(),
|
|
31
|
+
setup_intent_id: z.string()
|
|
32
|
+
});
|
|
28
33
|
const FindCardsParamsSchema = PaginationParamsSchema.extend(SearchParamsSchema.shape).extend(
|
|
29
34
|
SortParamsSchema.shape
|
|
30
35
|
);
|
|
31
36
|
export {
|
|
32
37
|
CardSchema,
|
|
33
38
|
CreateCardRequestSchema,
|
|
34
|
-
FindCardsParamsSchema
|
|
39
|
+
FindCardsParamsSchema,
|
|
40
|
+
SetupIntentSchema
|
|
35
41
|
};
|
|
36
42
|
//# sourceMappingURL=types.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/modules/cards/types.ts"],"sourcesContent":["import z from 'zod';\n\nimport {\n PaginationParamsSchema,\n SearchParamsSchema,\n SortParamsSchema,\n} from '../../infra/api/types';\n\nexport const CardSchema = z.object({\n id: z.union([z.number(), z.string()]),\n id_wl: z.number(),\n id_account: z.union([z.number(), z.string()]),\n id_gateway: z.union([z.number(), z.string()]).optional(),\n gateway_account: z.string(),\n gateway_card: z.string(),\n brand: z.string(),\n name: z.string(),\n number: z.string().nullable(),\n date: z.string().nullable(),\n deleted: z\n .number()\n .transform((val) => val === 1)\n .optional()\n .default(false),\n is_default: z.boolean().optional().default(false),\n datetime_add: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n datetime_alt: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n datetime_del: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n});\n\nexport type Card = z.infer<typeof CardSchema>;\n\nexport const CreateCardRequestSchema = z.object({\n
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/cards/types.ts"],"sourcesContent":["import z from 'zod';\n\nimport {\n PaginationParamsSchema,\n SearchParamsSchema,\n SortParamsSchema,\n} from '../../infra/api/types';\n\nexport const CardSchema = z.object({\n id: z.union([z.number(), z.string()]),\n id_wl: z.number(),\n id_account: z.union([z.number(), z.string()]),\n id_gateway: z.union([z.number(), z.string()]).optional(),\n gateway_account: z.string(),\n gateway_card: z.string(),\n brand: z.string(),\n name: z.string(),\n number: z.string().nullable(),\n date: z.string().nullable(),\n deleted: z\n .number()\n .transform((val) => val === 1)\n .optional()\n .default(false),\n is_default: z.boolean().optional().default(false),\n datetime_add: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n datetime_alt: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n datetime_del: z.union([z.date(), z.coerce.date()]).nullable().optional(),\n});\n\nexport type Card = z.infer<typeof CardSchema>;\n\nexport const CreateCardRequestSchema = z.object({\n setup_intent_id: z.string().min(1, 'setup_intent_id é obrigatório'),\n name: z.string().min(1, 'name é obrigatório'),\n set_default: z.boolean().optional(),\n});\n\nexport type CreateCardRequest = z.infer<typeof CreateCardRequestSchema>;\n\nexport const SetupIntentSchema = z.object({\n client_secret: z.string(),\n setup_intent_id: z.string(),\n});\n\nexport type SetupIntent = z.infer<typeof SetupIntentSchema>;\n\nexport const FindCardsParamsSchema = PaginationParamsSchema.extend(SearchParamsSchema.shape).extend(\n SortParamsSchema.shape\n);\n\nexport type FindCardsParams = z.infer<typeof FindCardsParamsSchema>;\n"],"mappings":"AAAA,OAAO,OAAO;AAEd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,aAAa,EAAE,OAAO;AAAA,EACjC,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACpC,OAAO,EAAE,OAAO;AAAA,EAChB,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EAC5C,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACvD,iBAAiB,EAAE,OAAO;AAAA,EAC1B,cAAc,EAAE,OAAO;AAAA,EACvB,OAAO,EAAE,OAAO;AAAA,EAChB,MAAM,EAAE,OAAO;AAAA,EACf,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,SAAS,EACN,OAAO,EACP,UAAU,CAAC,QAAQ,QAAQ,CAAC,EAC5B,SAAS,EACT,QAAQ,KAAK;AAAA,EAChB,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,EAChD,cAAc,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACvE,cAAc,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACvE,cAAc,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AACzE,CAAC;AAIM,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,iBAAiB,EAAE,OAAO,EAAE,IAAI,GAAG,qCAA+B;AAAA,EAClE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,0BAAoB;AAAA,EAC5C,aAAa,EAAE,QAAQ,EAAE,SAAS;AACpC,CAAC;AAIM,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,eAAe,EAAE,OAAO;AAAA,EACxB,iBAAiB,EAAE,OAAO;AAC5B,CAAC;AAIM,MAAM,wBAAwB,uBAAuB,OAAO,mBAAmB,KAAK,EAAE;AAAA,EAC3F,iBAAiB;AACnB;","names":[]}
|
package/dist/server.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import { updateSubscriptionPlanAction } from "./modules/subscriptions/actions/up
|
|
|
25
25
|
import { updateSubscriptionPaymentAction } from "./modules/subscriptions/actions/update-subscription-payment.action";
|
|
26
26
|
import { listCardsAction } from "./modules/cards/actions/list-cards.action";
|
|
27
27
|
import { createCardAction } from "./modules/cards/actions/create-card.action";
|
|
28
|
+
import { createSetupIntentAction } from "./modules/cards/actions/create-setup-intent.action";
|
|
28
29
|
import { listChargesAction } from "./modules/charges/actions/list-charges.action";
|
|
29
30
|
import { findChargeByIdAction } from "./modules/charges/actions/find-charge-by-id.action";
|
|
30
31
|
import { chargeActionAction } from "./modules/charges/actions/charge-action.action";
|
|
@@ -96,6 +97,7 @@ export {
|
|
|
96
97
|
createAccountUserAction,
|
|
97
98
|
createCardAction,
|
|
98
99
|
createProjectAction,
|
|
100
|
+
createSetupIntentAction,
|
|
99
101
|
deleteAccountAction,
|
|
100
102
|
deleteAccountUserAction,
|
|
101
103
|
deleteProjectAction,
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { chargesService } from './modules/charges/services/charges.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { listChargesAction } from './modules/charges/actions/list-charges.action';\r\nexport { findChargeByIdAction } from './modules/charges/actions/find-charge-by-id.action';\r\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { chargesService } from './modules/charges/services/charges.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\r\nexport { listChargesAction } from './modules/charges/actions/list-charges.action';\r\nexport { findChargeByIdAction } from './modules/charges/actions/find-charge-by-id.action';\r\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
|
package/package.json
CHANGED
|
@@ -6,11 +6,13 @@ import { Button } from '../../ui/buttons/Button';
|
|
|
6
6
|
import { Toast } from '../../ui/feedback/Toast';
|
|
7
7
|
import { useModalManager } from '../../../store/useModalManager';
|
|
8
8
|
import { useCreateCard } from '../../../modules/cards/hooks/create-card.hook';
|
|
9
|
+
import { useCreateSetupIntent } from '../../../modules/cards/hooks/create-setup-intent.hook';
|
|
9
10
|
import { useUpdateAccount } from '../../../modules/accounts/hooks/useAccountManagement';
|
|
10
11
|
import { useExternalContracting } from '../../../providers/whitelabel.provider';
|
|
11
12
|
import { CardFormFields, BillingFormFields, cardFormSchema } from './CardFormFields';
|
|
12
13
|
import type { CardFormData, StripeElementsStatus } from './CardFormFields';
|
|
13
|
-
import { IconLoader2, IconX, IconCreditCard } from '@tabler/icons-react';
|
|
14
|
+
import { IconLoader2, IconX, IconCreditCard, IconCheck, IconChevronRight } from '@tabler/icons-react';
|
|
15
|
+
import { cn } from '../../../infra/utils/clsx';
|
|
14
16
|
import { useForm } from 'react-hook-form';
|
|
15
17
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
16
18
|
import { CardNumberElement, useElements, useStripe } from '@stripe/react-stripe-js';
|
|
@@ -22,6 +24,49 @@ const INITIAL_STRIPE_STATUS: StripeElementsStatus = {
|
|
|
22
24
|
cardCvc: false,
|
|
23
25
|
};
|
|
24
26
|
|
|
27
|
+
const STEPS = [
|
|
28
|
+
{ number: 1, label: 'Cobrança' },
|
|
29
|
+
{ number: 2, label: 'Cartão' },
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
function CardStepper({ currentStep }: { readonly currentStep: number }) {
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex items-center justify-center gap-2 px-4 py-3 border-b border-zinc-200 lg:border-b-0">
|
|
35
|
+
{STEPS.map((step, idx) => {
|
|
36
|
+
const isCompleted = currentStep > step.number;
|
|
37
|
+
const isCurrent = currentStep === step.number;
|
|
38
|
+
const isPending = !isCompleted && !isCurrent;
|
|
39
|
+
return (
|
|
40
|
+
<div key={step.number} className="flex items-center gap-2">
|
|
41
|
+
<div
|
|
42
|
+
className={cn(
|
|
43
|
+
'flex items-center justify-center size-6 rounded-full paragraph-xsmall-semibold',
|
|
44
|
+
isCompleted && 'bg-green-500 text-white',
|
|
45
|
+
isCurrent && 'bg-primary text-white',
|
|
46
|
+
isPending && 'border border-zinc-200 text-zinc-400',
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
{isCompleted ? <IconCheck size={14} stroke={3} /> : step.number}
|
|
50
|
+
</div>
|
|
51
|
+
<span
|
|
52
|
+
className={cn(
|
|
53
|
+
'paragraph-xsmall-semibold',
|
|
54
|
+
(isCompleted || isCurrent) && 'text-zinc-950',
|
|
55
|
+
isPending && 'text-zinc-400',
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{step.label}
|
|
59
|
+
</span>
|
|
60
|
+
{idx < STEPS.length - 1 && (
|
|
61
|
+
<IconChevronRight size={16} className="text-zinc-300 ml-1" />
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
})}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
25
70
|
export default function AddCardModal() {
|
|
26
71
|
const { activeModal, modalData, closeModal } = useModalManager();
|
|
27
72
|
const isOpen = activeModal === 'addCardModal';
|
|
@@ -29,9 +74,11 @@ export default function AddCardModal() {
|
|
|
29
74
|
const [currentStep, setCurrentStep] = useState(1);
|
|
30
75
|
const [stripeStatus, setStripeStatus] = useState<StripeElementsStatus>(INITIAL_STRIPE_STATUS);
|
|
31
76
|
const [stripeErrors, setStripeErrors] = useState<Record<string, string | undefined>>({});
|
|
77
|
+
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
32
78
|
|
|
33
79
|
const onCardAdded = modalData.onCardAdded as ((cardId: string) => void) | undefined;
|
|
34
80
|
|
|
81
|
+
const createSetupIntentMutation = useCreateSetupIntent();
|
|
35
82
|
const createCardMutation = useCreateCard();
|
|
36
83
|
const updateAccountMutation = useUpdateAccount();
|
|
37
84
|
const elements = useElements();
|
|
@@ -52,6 +99,7 @@ export default function AddCardModal() {
|
|
|
52
99
|
setCurrentStep(1);
|
|
53
100
|
setStripeStatus(INITIAL_STRIPE_STATUS);
|
|
54
101
|
setStripeErrors({});
|
|
102
|
+
setIsAuthenticating(false);
|
|
55
103
|
closeModal();
|
|
56
104
|
}, [form, closeModal]);
|
|
57
105
|
|
|
@@ -66,65 +114,97 @@ export default function AddCardModal() {
|
|
|
66
114
|
);
|
|
67
115
|
|
|
68
116
|
const handleContinue = useCallback(async () => {
|
|
69
|
-
const
|
|
117
|
+
const isValid = await form.trigger([
|
|
118
|
+
'fullName',
|
|
119
|
+
'cep',
|
|
120
|
+
'street',
|
|
121
|
+
'streetNumber',
|
|
122
|
+
'neighborhood',
|
|
123
|
+
'city',
|
|
124
|
+
'state',
|
|
125
|
+
]);
|
|
126
|
+
if (!isValid) return;
|
|
127
|
+
setCurrentStep(2);
|
|
128
|
+
}, [form]);
|
|
129
|
+
|
|
130
|
+
const handleBack = useCallback(() => {
|
|
131
|
+
setCurrentStep(1);
|
|
132
|
+
}, []);
|
|
70
133
|
|
|
134
|
+
async function handleSubmit(data: CardFormData) {
|
|
71
135
|
const newStripeErrors: Record<string, string | undefined> = {};
|
|
72
136
|
if (!stripeStatus.cardNumber) newStripeErrors.cardNumber = 'Número do cartão é obrigatório';
|
|
73
137
|
if (!stripeStatus.cardExpiry) newStripeErrors.cardExpiry = 'Data de validade é obrigatória';
|
|
74
138
|
if (!stripeStatus.cardCvc) newStripeErrors.cardCvc = 'Código de segurança é obrigatório';
|
|
75
139
|
setStripeErrors(newStripeErrors);
|
|
76
140
|
|
|
77
|
-
const stripeValid =
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
form.clearErrors(['fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);
|
|
81
|
-
setCurrentStep(2);
|
|
82
|
-
}
|
|
83
|
-
}, [form, stripeStatus]);
|
|
84
|
-
|
|
85
|
-
const handleBack = useCallback(() => {
|
|
86
|
-
setCurrentStep(1);
|
|
87
|
-
}, []);
|
|
88
|
-
|
|
89
|
-
async function handleSubmit(data: CardFormData) {
|
|
141
|
+
const stripeValid =
|
|
142
|
+
stripeStatus.cardNumber && stripeStatus.cardExpiry && stripeStatus.cardCvc;
|
|
143
|
+
if (!stripeValid) return;
|
|
90
144
|
if (!stripe || !elements) return;
|
|
91
145
|
|
|
92
146
|
const cardElement = elements.getElement(CardNumberElement);
|
|
93
147
|
if (!cardElement) return;
|
|
94
148
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
149
|
+
setIsAuthenticating(true);
|
|
150
|
+
try {
|
|
151
|
+
const intentResult = await createSetupIntentMutation.mutateAsync();
|
|
152
|
+
const intent = intentResult?.data;
|
|
153
|
+
if (!intent?.client_secret || !intent?.setup_intent_id) {
|
|
154
|
+
toast.custom(
|
|
155
|
+
(t) => (
|
|
156
|
+
<Toast
|
|
157
|
+
variant="error"
|
|
158
|
+
message="Não foi possível iniciar a autenticação do cartão, tente novamente."
|
|
159
|
+
toastId={t}
|
|
160
|
+
/>
|
|
161
|
+
),
|
|
162
|
+
{ duration: 5000 }
|
|
163
|
+
);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { setupIntent, error } = await stripe.confirmCardSetup(intent.client_secret, {
|
|
168
|
+
payment_method: {
|
|
169
|
+
card: cardElement,
|
|
170
|
+
billing_details: { name: data.name },
|
|
107
171
|
},
|
|
108
|
-
}
|
|
109
|
-
});
|
|
172
|
+
});
|
|
110
173
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
174
|
+
if (error) {
|
|
175
|
+
toast.custom(
|
|
176
|
+
(t) => (
|
|
177
|
+
<Toast
|
|
178
|
+
variant="error"
|
|
179
|
+
message={error.message || 'Falha na autenticação do cartão.'}
|
|
180
|
+
toastId={t}
|
|
181
|
+
/>
|
|
182
|
+
),
|
|
183
|
+
{ duration: 5000 }
|
|
184
|
+
);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (setupIntent?.status !== 'succeeded') {
|
|
189
|
+
toast.custom(
|
|
190
|
+
(t) => (
|
|
191
|
+
<Toast
|
|
192
|
+
variant="error"
|
|
193
|
+
message={`Autenticação não concluída (status: ${setupIntent?.status ?? 'desconhecido'}).`}
|
|
194
|
+
toastId={t}
|
|
195
|
+
/>
|
|
196
|
+
),
|
|
197
|
+
{ duration: 5000 }
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
124
201
|
|
|
125
|
-
try {
|
|
126
202
|
const [cardResult] = await Promise.all([
|
|
127
|
-
createCardMutation.mutateAsync({
|
|
203
|
+
createCardMutation.mutateAsync({
|
|
204
|
+
setup_intent_id: intent.setup_intent_id,
|
|
205
|
+
name: data.name,
|
|
206
|
+
set_default: true,
|
|
207
|
+
}),
|
|
128
208
|
updateAccountMutation.mutateAsync({
|
|
129
209
|
financial_document_type: data.personType === 'pf' ? 1 : 2,
|
|
130
210
|
financial_document: data.personType === 'pf' ? data.cpf : data.cnpj,
|
|
@@ -159,6 +239,8 @@ export default function AddCardModal() {
|
|
|
159
239
|
),
|
|
160
240
|
{ duration: 5000 }
|
|
161
241
|
);
|
|
242
|
+
} finally {
|
|
243
|
+
setIsAuthenticating(false);
|
|
162
244
|
}
|
|
163
245
|
}
|
|
164
246
|
|
|
@@ -169,15 +251,7 @@ export default function AddCardModal() {
|
|
|
169
251
|
|
|
170
252
|
const watchedValues = form.watch(['name', 'country', 'personType', 'cpf', 'cnpj', 'fullName', 'cep', 'street', 'streetNumber', 'neighborhood', 'city', 'state']);
|
|
171
253
|
const [name, country, personType, cpf, cnpj, fullName, cep, street, streetNumber, neighborhood, city, state] = watchedValues;
|
|
172
|
-
const
|
|
173
|
-
!!name &&
|
|
174
|
-
!!country &&
|
|
175
|
-
!!personType &&
|
|
176
|
-
(personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&
|
|
177
|
-
stripeStatus.cardNumber &&
|
|
178
|
-
stripeStatus.cardExpiry &&
|
|
179
|
-
stripeStatus.cardCvc;
|
|
180
|
-
const isStep2Valid =
|
|
254
|
+
const isBillingValid =
|
|
181
255
|
!!fullName &&
|
|
182
256
|
!!cep && cep.length >= 9 &&
|
|
183
257
|
!!street &&
|
|
@@ -185,6 +259,14 @@ export default function AddCardModal() {
|
|
|
185
259
|
!!neighborhood &&
|
|
186
260
|
!!city &&
|
|
187
261
|
!!state;
|
|
262
|
+
const isCardValid =
|
|
263
|
+
!!name &&
|
|
264
|
+
!!country &&
|
|
265
|
+
!!personType &&
|
|
266
|
+
(personType === 'pf' ? !!cpf && cpf.length >= 14 : !!cnpj && cnpj.length >= 18) &&
|
|
267
|
+
stripeStatus.cardNumber &&
|
|
268
|
+
stripeStatus.cardExpiry &&
|
|
269
|
+
stripeStatus.cardCvc;
|
|
188
270
|
|
|
189
271
|
if (isExternalContracting) {
|
|
190
272
|
return (
|
|
@@ -228,7 +310,7 @@ export default function AddCardModal() {
|
|
|
228
310
|
<DialogHeader className="p-0">
|
|
229
311
|
<div className="flex items-center justify-center px-4 py-3 relative border-b border-zinc-200 lg:border-b-0">
|
|
230
312
|
<DialogTitle className="paragraph-medium-semibold text-zinc-950 text-center">
|
|
231
|
-
|
|
313
|
+
Adicionar cartão
|
|
232
314
|
</DialogTitle>
|
|
233
315
|
<Button
|
|
234
316
|
type="button"
|
|
@@ -241,28 +323,28 @@ export default function AddCardModal() {
|
|
|
241
323
|
</div>
|
|
242
324
|
</DialogHeader>
|
|
243
325
|
|
|
244
|
-
{
|
|
245
|
-
<div className="h-1 bg-zinc-100 w-full">
|
|
246
|
-
<div
|
|
247
|
-
className="h-full transition-all duration-300 ease-in-out bg-primary"
|
|
248
|
-
style={{ width: currentStep === 1 ? '50%' : '100%' }}
|
|
249
|
-
/>
|
|
250
|
-
</div>
|
|
326
|
+
<CardStepper currentStep={currentStep} />
|
|
251
327
|
|
|
252
328
|
<form
|
|
253
329
|
onSubmit={form.handleSubmit(handleSubmit)}
|
|
254
330
|
className="flex flex-col flex-1 lg:flex-none"
|
|
255
331
|
>
|
|
256
332
|
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 1 ? 'hidden' : ''}`}>
|
|
333
|
+
<h2 className="font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950">
|
|
334
|
+
Adicione suas informações de cobrança
|
|
335
|
+
</h2>
|
|
336
|
+
<BillingFormFields form={form} />
|
|
337
|
+
</div>
|
|
338
|
+
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>
|
|
339
|
+
<h2 className="font-[family-name:var(--font-outfit)] text-[18px] leading-tight font-semibold text-zinc-950">
|
|
340
|
+
Adicione informações do seu cartão
|
|
341
|
+
</h2>
|
|
257
342
|
<CardFormFields
|
|
258
343
|
form={form}
|
|
259
344
|
onStripeChange={handleStripeChange}
|
|
260
345
|
stripeErrors={stripeErrors}
|
|
261
346
|
/>
|
|
262
347
|
</div>
|
|
263
|
-
<div className={`flex flex-col gap-6 lg:gap-8 p-4 lg:p-5 flex-1 lg:flex-none ${currentStep !== 2 ? 'hidden' : ''}`}>
|
|
264
|
-
<BillingFormFields form={form} />
|
|
265
|
-
</div>
|
|
266
348
|
|
|
267
349
|
<div className="flex justify-between items-center p-4 lg:p-5 border-t border-zinc-200 gap-2 mt-auto lg:mt-0">
|
|
268
350
|
{currentStep === 1 ? (
|
|
@@ -274,7 +356,7 @@ export default function AddCardModal() {
|
|
|
274
356
|
type="button"
|
|
275
357
|
className="h-10!"
|
|
276
358
|
onClick={handleContinue}
|
|
277
|
-
disabled={!
|
|
359
|
+
disabled={!isBillingValid}
|
|
278
360
|
>
|
|
279
361
|
Continuar
|
|
280
362
|
</Button>
|
|
@@ -284,9 +366,18 @@ export default function AddCardModal() {
|
|
|
284
366
|
<Button variant="secondary" type="button" onClick={handleBack} className="h-10!">
|
|
285
367
|
Voltar
|
|
286
368
|
</Button>
|
|
287
|
-
<Button
|
|
288
|
-
|
|
289
|
-
|
|
369
|
+
<Button
|
|
370
|
+
type="submit"
|
|
371
|
+
className="h-10!"
|
|
372
|
+
disabled={isSubmitting || isAuthenticating || !isCardValid}
|
|
373
|
+
loading={isAuthenticating || isSubmitting}
|
|
374
|
+
>
|
|
375
|
+
{isAuthenticating && <IconLoader2 className="size-4 animate-spin" />}
|
|
376
|
+
{isAuthenticating
|
|
377
|
+
? 'Autenticando...'
|
|
378
|
+
: isSubmitting
|
|
379
|
+
? 'Adicionando cartão...'
|
|
380
|
+
: 'Adicionar cartão'}
|
|
290
381
|
</Button>
|
|
291
382
|
</>
|
|
292
383
|
)}
|
package/src/index.ts
CHANGED
|
@@ -77,6 +77,7 @@ export { useUpdateSubscriptionPayment } from "./modules/subscriptions/hooks/upda
|
|
|
77
77
|
export { useCards, CARDS_QUERY_KEY } from "./modules/cards/hooks/cards.hook";
|
|
78
78
|
export { useCardById } from "./modules/cards/hooks/card-by-id.hook";
|
|
79
79
|
export { useCreateCard } from "./modules/cards/hooks/create-card.hook";
|
|
80
|
+
export { useCreateSetupIntent } from "./modules/cards/hooks/create-setup-intent.hook";
|
|
80
81
|
export { useSetDefaultCard } from "./modules/cards/hooks/set-default-card.hook";
|
|
81
82
|
export { useDeleteCard } from "./modules/cards/hooks/delete-card.hook";
|
|
82
83
|
export { useDeleteConfirmation } from "./modules/cards/hooks/delete-confirmation.hook";
|
|
@@ -134,11 +135,13 @@ export type {
|
|
|
134
135
|
Card as PaymentCard,
|
|
135
136
|
CreateCardRequest,
|
|
136
137
|
FindCardsParams,
|
|
138
|
+
SetupIntent,
|
|
137
139
|
} from "./modules/cards/types";
|
|
138
140
|
export {
|
|
139
141
|
CardSchema as PaymentCardSchema,
|
|
140
142
|
CreateCardRequestSchema,
|
|
141
143
|
FindCardsParamsSchema,
|
|
144
|
+
SetupIntentSchema,
|
|
142
145
|
} from "./modules/cards/types";
|
|
143
146
|
export type {
|
|
144
147
|
Charge,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { safeMutationAction } from '../../../utils/safeMutationAction';
|
|
4
|
+
import { cardsService } from '../services/cards.service';
|
|
5
|
+
|
|
6
|
+
export async function createSetupIntentAction() {
|
|
7
|
+
return safeMutationAction(() => cardsService.createSetupIntent());
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMutation } from '@tanstack/react-query';
|
|
4
|
+
import { withAction } from '../../../utils/withAction';
|
|
5
|
+
import { createSetupIntentAction } from '../actions/create-setup-intent.action';
|
|
6
|
+
|
|
7
|
+
export function useCreateSetupIntent() {
|
|
8
|
+
return useMutation({
|
|
9
|
+
mutationFn: () => withAction(createSetupIntentAction)(),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -5,9 +5,31 @@ import { ApiError, ApiActionResult, ApiPaginatedActionResult, PaginatedSuccessRe
|
|
|
5
5
|
import { buildQueryParams } from '../../../infra/utils/params';
|
|
6
6
|
import { assertManagementPermission } from '../../auth/utils/assert-management-permission';
|
|
7
7
|
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
8
|
-
import { Card, CardSchema, CreateCardRequest } from '../types';
|
|
8
|
+
import { Card, CardSchema, CreateCardRequest, SetupIntent, SetupIntentSchema } from '../types';
|
|
9
9
|
|
|
10
10
|
class CardsService {
|
|
11
|
+
async createSetupIntent(): Promise<SuccessResult<SetupIntent>> {
|
|
12
|
+
await assertManagementPermission('manage_cards');
|
|
13
|
+
const { id_account } = await getUserContext();
|
|
14
|
+
|
|
15
|
+
const response = await api.apps.post<ApiPaginatedActionResult<SetupIntent>>(
|
|
16
|
+
`/accounts/${id_account}/cards/action/setup-intent`,
|
|
17
|
+
{}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (response.status === 0) {
|
|
21
|
+
throw new ApiError(
|
|
22
|
+
response.message || 'Erro ao criar SetupIntent',
|
|
23
|
+
'CREATE_SETUP_INTENT_FAILED',
|
|
24
|
+
400
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const data = SetupIntentSchema.parse(response.data[0]);
|
|
29
|
+
|
|
30
|
+
return { success: true, data };
|
|
31
|
+
}
|
|
32
|
+
|
|
11
33
|
async listCards(): Promise<PaginatedSuccessResult<Card>> {
|
|
12
34
|
await assertManagementPermission('view_cards');
|
|
13
35
|
const { id_account } = await getUserContext();
|
|
@@ -31,12 +31,20 @@ export const CardSchema = z.object({
|
|
|
31
31
|
export type Card = z.infer<typeof CardSchema>;
|
|
32
32
|
|
|
33
33
|
export const CreateCardRequestSchema = z.object({
|
|
34
|
-
|
|
34
|
+
setup_intent_id: z.string().min(1, 'setup_intent_id é obrigatório'),
|
|
35
|
+
name: z.string().min(1, 'name é obrigatório'),
|
|
35
36
|
set_default: z.boolean().optional(),
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
export type CreateCardRequest = z.infer<typeof CreateCardRequestSchema>;
|
|
39
40
|
|
|
41
|
+
export const SetupIntentSchema = z.object({
|
|
42
|
+
client_secret: z.string(),
|
|
43
|
+
setup_intent_id: z.string(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type SetupIntent = z.infer<typeof SetupIntentSchema>;
|
|
47
|
+
|
|
40
48
|
export const FindCardsParamsSchema = PaginationParamsSchema.extend(SearchParamsSchema.shape).extend(
|
|
41
49
|
SortParamsSchema.shape
|
|
42
50
|
);
|
package/src/server.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { updateSubscriptionPlanAction } from './modules/subscriptions/actions/up
|
|
|
31
31
|
export { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';
|
|
32
32
|
export { listCardsAction } from './modules/cards/actions/list-cards.action';
|
|
33
33
|
export { createCardAction } from './modules/cards/actions/create-card.action';
|
|
34
|
+
export { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';
|
|
34
35
|
export { listChargesAction } from './modules/charges/actions/list-charges.action';
|
|
35
36
|
export { findChargeByIdAction } from './modules/charges/actions/find-charge-by-id.action';
|
|
36
37
|
export { chargeActionAction } from './modules/charges/actions/charge-action.action';
|