diff --git a/src/assets/close.svg b/src/assets/close.svg
new file mode 100644
index 000000000..bf30c21f1
--- /dev/null
+++ b/src/assets/close.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/card/components/CardPayments/CardPayments.tsx b/src/card/components/CardPayments/CardPayments.tsx
new file mode 100644
index 000000000..968519dbd
--- /dev/null
+++ b/src/card/components/CardPayments/CardPayments.tsx
@@ -0,0 +1,54 @@
+import {
+ CardAddForm,
+ CardCompleteForm,
+ CardListForm,
+ CardPageIndex,
+ CardPaymentForm,
+ CardProvider,
+ CardState,
+ PaymentForm,
+} from '@/card';
+import { AppDisplay, Funnel, OverlayProvider } from '@/shared';
+
+type CardPaymentsProps = PaymentForm & {
+ cardStorageKey: string;
+ initialOwnerCards: CardState[];
+ onClose?: () => void;
+};
+
+export const CardPayments = ({
+ cardStorageKey,
+ orderId,
+ totalAmount,
+ initialOwnerCards,
+ onPaymentComplete,
+ onPaymentCancel,
+ onClose,
+}: CardPaymentsProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/src/card/components/CardPayments/hook/useLoadNearPayments.tsx b/src/card/components/CardPayments/hook/useLoadNearPayments.tsx
new file mode 100644
index 000000000..e5f788832
--- /dev/null
+++ b/src/card/components/CardPayments/hook/useLoadNearPayments.tsx
@@ -0,0 +1,79 @@
+import { CardPayments, isValidateCardState, PaymentForm } from '@/card';
+import { useModal } from '@/shared';
+
+const ERROR_CODES = {
+ INVALID_CLIENT_ID: 'INVALID_CLIENT_ID',
+ INVALID_ORDER_ID: 'INVALID_ORDER_ID',
+ INVALID_AMOUNT: 'INVALID_AMOUNT',
+ INVALID_OWNER_CARDS: 'INVALID_OWNER_CARDS',
+ INVALID_PAYMENT_COMPLETE_CALLBACK: 'INVALID_PAYMENT_COMPLETE_CALLBACK',
+ INVALID_PAYMENT_CANCEL_CALLBACK: 'INVALID_PAYMENT_CANCEL_CALLBACK',
+ PAYMENT_PROCESS_ERROR: 'PAYMENT_PROCESS_ERROR',
+} as const;
+
+const ERROR_MESSAGES = {
+ [ERROR_CODES.INVALID_CLIENT_ID]: '유효하지 않은 CLIENT ID입니다.',
+ [ERROR_CODES.INVALID_ORDER_ID]: '유효하지 않은 주문 번호입니다.',
+ [ERROR_CODES.INVALID_AMOUNT]: '유효하지 않은 금액입니다.',
+ [ERROR_CODES.INVALID_OWNER_CARDS]: '유효하지 않은 카드 정보입니다.',
+ [ERROR_CODES.INVALID_PAYMENT_COMPLETE_CALLBACK]: '결제 완료 콜백 함수가 유효하지 않습니다.',
+ [ERROR_CODES.INVALID_PAYMENT_CANCEL_CALLBACK]: '결제 취소 콜백 함수가 유효하지 않습니다.',
+ [ERROR_CODES.PAYMENT_PROCESS_ERROR]: '결제 프로세스 중 오류가 발생했습니다.',
+};
+
+const createError = (code: keyof typeof ERROR_CODES, error?: unknown) => ({
+ code,
+ message: ERROR_MESSAGES[code],
+ error,
+});
+
+type UseLoadNearPaymentsProps = {
+ clientId: string;
+};
+
+export const useLoadNearPayments = ({ clientId }: UseLoadNearPaymentsProps) => {
+ const showModal = useModal();
+ const cardStorageKey = `near-payments-${clientId}`;
+ const ownerCards = JSON.parse(localStorage.getItem(cardStorageKey) ?? '[]');
+
+ return ({ orderId, totalAmount, onPaymentComplete, onPaymentCancel }: PaymentForm) => {
+ if (!clientId || clientId.trim() === '') {
+ throw createError(ERROR_CODES.INVALID_ORDER_ID);
+ }
+
+ if (!orderId || orderId.trim() === '') {
+ throw createError(ERROR_CODES.INVALID_ORDER_ID);
+ }
+
+ if (Number.isNaN(totalAmount) || totalAmount <= 0) {
+ throw createError(ERROR_CODES.INVALID_AMOUNT);
+ }
+
+ if (ownerCards.length > 0 && !ownerCards.every((card: any) => isValidateCardState(card))) {
+ throw createError(ERROR_CODES.INVALID_OWNER_CARDS);
+ }
+
+ if (typeof onPaymentComplete !== 'function') {
+ throw createError(ERROR_CODES.INVALID_PAYMENT_COMPLETE_CALLBACK);
+ }
+
+ if (typeof onPaymentCancel !== 'function') {
+ throw createError(ERROR_CODES.INVALID_PAYMENT_CANCEL_CALLBACK);
+ }
+
+ try {
+ return showModal(
+ ,
+ );
+ } catch (error) {
+ throw createError(ERROR_CODES.PAYMENT_PROCESS_ERROR, error);
+ }
+ };
+};
diff --git a/src/card/components/CardPayments/index.ts b/src/card/components/CardPayments/index.ts
new file mode 100644
index 000000000..e4213392f
--- /dev/null
+++ b/src/card/components/CardPayments/index.ts
@@ -0,0 +1,2 @@
+export * from './CardPayments';
+export * from './hook/useLoadNearPayments';
diff --git a/src/card/hooks/loadNearPayments.tsx b/src/card/hooks/loadNearPayments.tsx
new file mode 100644
index 000000000..e5f788832
--- /dev/null
+++ b/src/card/hooks/loadNearPayments.tsx
@@ -0,0 +1,79 @@
+import { CardPayments, isValidateCardState, PaymentForm } from '@/card';
+import { useModal } from '@/shared';
+
+const ERROR_CODES = {
+ INVALID_CLIENT_ID: 'INVALID_CLIENT_ID',
+ INVALID_ORDER_ID: 'INVALID_ORDER_ID',
+ INVALID_AMOUNT: 'INVALID_AMOUNT',
+ INVALID_OWNER_CARDS: 'INVALID_OWNER_CARDS',
+ INVALID_PAYMENT_COMPLETE_CALLBACK: 'INVALID_PAYMENT_COMPLETE_CALLBACK',
+ INVALID_PAYMENT_CANCEL_CALLBACK: 'INVALID_PAYMENT_CANCEL_CALLBACK',
+ PAYMENT_PROCESS_ERROR: 'PAYMENT_PROCESS_ERROR',
+} as const;
+
+const ERROR_MESSAGES = {
+ [ERROR_CODES.INVALID_CLIENT_ID]: '유효하지 않은 CLIENT ID입니다.',
+ [ERROR_CODES.INVALID_ORDER_ID]: '유효하지 않은 주문 번호입니다.',
+ [ERROR_CODES.INVALID_AMOUNT]: '유효하지 않은 금액입니다.',
+ [ERROR_CODES.INVALID_OWNER_CARDS]: '유효하지 않은 카드 정보입니다.',
+ [ERROR_CODES.INVALID_PAYMENT_COMPLETE_CALLBACK]: '결제 완료 콜백 함수가 유효하지 않습니다.',
+ [ERROR_CODES.INVALID_PAYMENT_CANCEL_CALLBACK]: '결제 취소 콜백 함수가 유효하지 않습니다.',
+ [ERROR_CODES.PAYMENT_PROCESS_ERROR]: '결제 프로세스 중 오류가 발생했습니다.',
+};
+
+const createError = (code: keyof typeof ERROR_CODES, error?: unknown) => ({
+ code,
+ message: ERROR_MESSAGES[code],
+ error,
+});
+
+type UseLoadNearPaymentsProps = {
+ clientId: string;
+};
+
+export const useLoadNearPayments = ({ clientId }: UseLoadNearPaymentsProps) => {
+ const showModal = useModal();
+ const cardStorageKey = `near-payments-${clientId}`;
+ const ownerCards = JSON.parse(localStorage.getItem(cardStorageKey) ?? '[]');
+
+ return ({ orderId, totalAmount, onPaymentComplete, onPaymentCancel }: PaymentForm) => {
+ if (!clientId || clientId.trim() === '') {
+ throw createError(ERROR_CODES.INVALID_ORDER_ID);
+ }
+
+ if (!orderId || orderId.trim() === '') {
+ throw createError(ERROR_CODES.INVALID_ORDER_ID);
+ }
+
+ if (Number.isNaN(totalAmount) || totalAmount <= 0) {
+ throw createError(ERROR_CODES.INVALID_AMOUNT);
+ }
+
+ if (ownerCards.length > 0 && !ownerCards.every((card: any) => isValidateCardState(card))) {
+ throw createError(ERROR_CODES.INVALID_OWNER_CARDS);
+ }
+
+ if (typeof onPaymentComplete !== 'function') {
+ throw createError(ERROR_CODES.INVALID_PAYMENT_COMPLETE_CALLBACK);
+ }
+
+ if (typeof onPaymentCancel !== 'function') {
+ throw createError(ERROR_CODES.INVALID_PAYMENT_CANCEL_CALLBACK);
+ }
+
+ try {
+ return showModal(
+ ,
+ );
+ } catch (error) {
+ throw createError(ERROR_CODES.PAYMENT_PROCESS_ERROR, error);
+ }
+ };
+};
diff --git a/src/card/types/PaymentForm.type.ts b/src/card/types/PaymentForm.type.ts
new file mode 100644
index 000000000..f097f0e9d
--- /dev/null
+++ b/src/card/types/PaymentForm.type.ts
@@ -0,0 +1,8 @@
+import { PaymentResult } from './PaymentResult.type';
+
+export type PaymentForm = {
+ orderId: string;
+ totalAmount: number;
+ onPaymentCancel: (paymentResult: Pick) => void;
+ onPaymentComplete: (paymentResult: PaymentResult) => void;
+};
diff --git a/src/card/types/PaymentResult.type.ts b/src/card/types/PaymentResult.type.ts
new file mode 100644
index 000000000..caf5d45be
--- /dev/null
+++ b/src/card/types/PaymentResult.type.ts
@@ -0,0 +1,10 @@
+export type PaymentResult = {
+ success: boolean;
+ message: string;
+ orderId: string;
+ totalAmount: number;
+ cardNumber: string;
+ paymentTimestamp: number;
+};
+
+export type PaymentCancel = Pick;