diff --git a/apps/customer/src/app/payment/success/page.tsx b/apps/customer/src/app/payment/success/page.tsx index b0027d3..f1c979e 100644 --- a/apps/customer/src/app/payment/success/page.tsx +++ b/apps/customer/src/app/payment/success/page.tsx @@ -1,9 +1,10 @@ "use client"; -import { Suspense, useEffect, useMemo, useState } from "react"; +import { Suspense, useEffect, useMemo, useRef, useState } from "react"; import { useSearchParams } from "next/navigation"; import type { StoreRandomBoxRespDTO, StoreRespDTO } from "@compasser/api"; import { useApproveKakaoPayMutation } from "@/shared/queries/mutation/payment/useApproveKakaoPayMutation"; +import { useCompleteAdminSettlementMutation } from "@/shared/queries/mutation/admin-settlement/useCompleteAdminSettlementMutation"; import PurchaseCompleteModal from "@/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal"; interface PendingPayment { @@ -15,7 +16,12 @@ interface PendingPayment { function PaymentSuccessContent() { const searchParams = useSearchParams(); + const approveKakaoPayMutation = useApproveKakaoPayMutation(); + const completeAdminSettlementMutation = useCompleteAdminSettlementMutation(); + + const hasRequestedRef = useRef(false); + const [pendingPayment, setPendingPayment] = useState( null, ); @@ -29,10 +35,14 @@ function PaymentSuccessContent() { const pgToken = searchParams.get("pg_token"); useEffect(() => { + if (hasRequestedRef.current) return; + const savedPayment = sessionStorage.getItem("pendingPayment"); if (!savedPayment || !reservationId || !pgToken) return; + hasRequestedRef.current = true; + const parsedPayment = JSON.parse(savedPayment) as PendingPayment; setPendingPayment(parsedPayment); @@ -44,12 +54,24 @@ function PaymentSuccessContent() { }, { onSuccess: () => { - sessionStorage.removeItem("pendingPayment"); - setIsCompleteModalOpen(true); + completeAdminSettlementMutation.mutate( + { + storeId: parsedPayment.store.storeId, + body: { + reservationIds: [reservationId], + }, + }, + { + onSettled: () => { + sessionStorage.removeItem("pendingPayment"); + setIsCompleteModalOpen(true); + }, + }, + ); }, }, ); - }, [reservationId, pgToken, approveKakaoPayMutation]); + }, [reservationId, pgToken]); if (approveKakaoPayMutation.isPending) { return
결제 승인 처리 중...
; diff --git a/apps/customer/src/shared/api/api.ts b/apps/customer/src/shared/api/api.ts index e643a7f..3f6c68f 100644 --- a/apps/customer/src/shared/api/api.ts +++ b/apps/customer/src/shared/api/api.ts @@ -9,6 +9,7 @@ import { createMemberModule, createOrderModule, createPaymentModule, + createAdminSettlementModule, } from "@compasser/api"; const tokenStore: TokenStore = { @@ -44,4 +45,5 @@ export const authModule = createAuthModule(compasserApi); export const memberModule = createMemberModule(compasserApi); export const storeModule = createStoreModule(compasserApi); export const orderModule = createOrderModule(compasserApi); -export const paymentModule = createPaymentModule(compasserApi); \ No newline at end of file +export const paymentModule = createPaymentModule(compasserApi); +export const adminSettlementModule = createAdminSettlementModule(compasserApi); \ No newline at end of file diff --git a/apps/customer/src/shared/queries/mutation/admin-settlement/useCompleteAdminSettlementMutation.ts b/apps/customer/src/shared/queries/mutation/admin-settlement/useCompleteAdminSettlementMutation.ts new file mode 100644 index 0000000..d4fb747 --- /dev/null +++ b/apps/customer/src/shared/queries/mutation/admin-settlement/useCompleteAdminSettlementMutation.ts @@ -0,0 +1,12 @@ +"use client"; + +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { adminSettlementModule } from "@/shared/api/api"; + +export const useCompleteAdminSettlementMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + ...adminSettlementModule.mutations.completeSettlement(queryClient), + }); +}; \ No newline at end of file diff --git a/apps/owner/src/app/signup/business/page.tsx b/apps/owner/src/app/signup/business/page.tsx index f9974b3..b4ea32e 100644 --- a/apps/owner/src/app/signup/business/page.tsx +++ b/apps/owner/src/app/signup/business/page.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { Input, Button } from "@compasser/design-system"; -import { useVerifyBusinessMutation } from "@/shared/queries/mutation/auth/useVerifyBusinessMutation"; +import { useUpgradeToStoreManagerMutation } from "@/shared/queries/mutation/auth/useUpgradeToStoreManagerMutation"; import { useOwnerSignupStore } from "@/shared/stores/ownerSignup.store"; import { normalizeBusinessNumber, @@ -13,7 +13,7 @@ import { export default function BusinessSignupPage() { const router = useRouter(); - const verifyMutation = useVerifyBusinessMutation(); + const upgradeMutation = useUpgradeToStoreManagerMutation(); const signupCompleted = useOwnerSignupStore((s) => s.signupCompleted); const email = useOwnerSignupStore((s) => s.email); @@ -65,26 +65,20 @@ export default function BusinessSignupPage() { return; } - verifyMutation.mutate( - { - businessLicenseNumber: value, - email, + upgradeMutation.mutate(undefined, { + onSuccess: (res) => { + if (res.alreadyUpgraded) { + setError("이미 사업자 등록이 완료된 계정입니다."); + return; + } + + setBusinessCompleted(); + router.push("/signup/register"); }, - { - onSuccess: (res) => { - if (res.alreadyUpgraded) { - setError("이미 사업자 등록이 완료된 계정입니다."); - return; - } - - setBusinessCompleted(); - router.push("/signup/register"); - }, - onError: () => { - setError("사업자 번호 인증에 실패했습니다."); - }, + onError: () => { + setError("점장 승격 처리에 실패했습니다."); }, - ); + }); }; return ( @@ -115,9 +109,9 @@ export default function BusinessSignupPage() { size="lg" variant="primary" onClick={handleNext} - disabled={verifyMutation.isPending} + disabled={upgradeMutation.isPending} > - {verifyMutation.isPending ? "확인 중..." : "다음으로"} + {upgradeMutation.isPending ? "확인 중..." : "다음으로"} diff --git a/apps/owner/src/shared/queries/mutation/auth/useUpgradeToStoreManagerMutation.ts b/apps/owner/src/shared/queries/mutation/auth/useUpgradeToStoreManagerMutation.ts new file mode 100644 index 0000000..0cb54a8 --- /dev/null +++ b/apps/owner/src/shared/queries/mutation/auth/useUpgradeToStoreManagerMutation.ts @@ -0,0 +1,12 @@ +"use client"; + +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { ownerModule } from "@/shared/api/api"; + +export const useUpgradeToStoreManagerMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + ...ownerModule.mutations.upgradeToStoreManager(queryClient), + }); +}; \ No newline at end of file diff --git a/apps/owner/src/shared/queries/mutation/auth/useVerifyBusinessMutation.ts b/apps/owner/src/shared/queries/mutation/auth/useVerifyBusinessMutation.ts index 7112468..e667621 100644 --- a/apps/owner/src/shared/queries/mutation/auth/useVerifyBusinessMutation.ts +++ b/apps/owner/src/shared/queries/mutation/auth/useVerifyBusinessMutation.ts @@ -6,7 +6,7 @@ import { ownerModule } from "@/shared/api/api"; export const useVerifyBusinessMutation = () => { const queryClient = useQueryClient(); - return useMutation( - ownerModule.mutations.verifyBizAndUpgrade(queryClient), - ); + return useMutation({ + ...ownerModule.mutations.verifyBizAndUpgrade(queryClient), + }); }; \ No newline at end of file diff --git a/packages/api/src/domains/admin-settlement.ts b/packages/api/src/domains/admin-settlement.ts new file mode 100644 index 0000000..a4868c8 --- /dev/null +++ b/packages/api/src/domains/admin-settlement.ts @@ -0,0 +1,59 @@ +// packages/api/src/domains/admin-settlement.ts + +import type { QueryClient } from "@tanstack/react-query"; + +import { createMutationWithCache } from "../core/mutation"; +import type { CompasserApi } from "../core/types"; +import type { + AdminSettlementCompleteReqDTO, + AdminSettlementCompleteResponse, +} from "../models/admin-settlement"; + +export interface CompleteAdminSettlementParams { + storeId: number; + body: AdminSettlementCompleteReqDTO; +} + +export const createAdminSettlementModule = (api: CompasserApi) => { + const keys = { + all: ["admin-settlement"] as const, + complete: (storeId: number) => [...keys.all, "complete", storeId] as const, + }; + + const requests = { + completeSettlement: async ({ + storeId, + body, + }: CompleteAdminSettlementParams) => { + const { data } = + await api.privateClient.post( + `/admin/settlements/${storeId}/complete`, + body, + ); + + return data; + }, + }; + + const mutations = { + completeSettlement: (queryClient: QueryClient) => + createMutationWithCache({ + queryClient, + mutationKey: [...keys.all, "complete"], + mutationFn: requests.completeSettlement, + getActions: (response, variables) => [ + { + type: "set", + queryKey: keys.complete(variables.storeId), + value: response, + }, + { + type: "invalidate", + queryKey: keys.all, + }, + ], + }), + }; + + return { keys, requests, mutations }; +}; \ No newline at end of file diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 25399c9..32d5350 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -13,6 +13,7 @@ export * from "./models/random-box"; export * from "./models/store"; export * from "./models/storeManager"; export * from "./models/store-image"; +export * from "./models/admin-settlement"; export * from "./domains/auth"; export * from "./domains/health"; export * from "./domains/member"; @@ -23,4 +24,5 @@ export * from "./domains/random-box"; export * from "./domains/store"; export * from "./domains/store-image"; export * from "./domains/store-manager"; +export * from "./domains/admin-settlement"; export * from "./domains/index"; \ No newline at end of file diff --git a/packages/api/src/models/admin-settlement.ts b/packages/api/src/models/admin-settlement.ts new file mode 100644 index 0000000..225fd04 --- /dev/null +++ b/packages/api/src/models/admin-settlement.ts @@ -0,0 +1,16 @@ +import type { ApiResponse } from "../core/types"; + +export interface AdminSettlementCompleteReqDTO { + reservationIds: number[]; +} + +export interface AdminSettlementCompleteRespDTO { + storeId: number; + storeName: string; + count: number; + totalAmount: number; + message: string; +} + +export type AdminSettlementCompleteResponse = + ApiResponse; \ No newline at end of file