Skip to content

Commit

Permalink
feat: useLoadNearPayments 반영을 위한 CardPayments 컴포넌트, Payment 타입 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
bytrustu committed Apr 8, 2024
1 parent e8ae656 commit 11fed7e
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/assets/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions src/card/components/CardPayments/CardPayments.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<AppDisplay.Root>
<OverlayProvider>
<CardProvider initialOwnerCards={initialOwnerCards} cardStorageKey={cardStorageKey}>
<Funnel.Root>
<Funnel.Step index={CardPageIndex.CardPayment}>
<CardPaymentForm
orderId={orderId}
totalAmount={totalAmount}
onPaymentComplete={onPaymentComplete}
onPaymentCancel={onPaymentCancel}
onClose={onClose}
/>
</Funnel.Step>
<Funnel.Step index={CardPageIndex.CardList}>
<CardListForm />
</Funnel.Step>
<Funnel.Step index={CardPageIndex.CardAdd}>
<CardAddForm />
</Funnel.Step>
<Funnel.Step index={CardPageIndex.CardComplete}>
<CardCompleteForm />
</Funnel.Step>
</Funnel.Root>
</CardProvider>
</OverlayProvider>
</AppDisplay.Root>
);
79 changes: 79 additions & 0 deletions src/card/components/CardPayments/hook/useLoadNearPayments.tsx
Original file line number Diff line number Diff line change
@@ -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(
<CardPayments
cardStorageKey={cardStorageKey}
orderId={orderId}
totalAmount={totalAmount}
initialOwnerCards={ownerCards}
onPaymentComplete={onPaymentComplete}
onPaymentCancel={onPaymentCancel}
/>,
);
} catch (error) {
throw createError(ERROR_CODES.PAYMENT_PROCESS_ERROR, error);
}
};
};
2 changes: 2 additions & 0 deletions src/card/components/CardPayments/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './CardPayments';
export * from './hook/useLoadNearPayments';
79 changes: 79 additions & 0 deletions src/card/hooks/loadNearPayments.tsx
Original file line number Diff line number Diff line change
@@ -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(
<CardPayments
cardStorageKey={cardStorageKey}
orderId={orderId}
totalAmount={totalAmount}
initialOwnerCards={ownerCards}
onPaymentComplete={onPaymentComplete}
onPaymentCancel={onPaymentCancel}
/>,
);
} catch (error) {
throw createError(ERROR_CODES.PAYMENT_PROCESS_ERROR, error);
}
};
};
8 changes: 8 additions & 0 deletions src/card/types/PaymentForm.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PaymentResult } from './PaymentResult.type';

export type PaymentForm = {
orderId: string;
totalAmount: number;
onPaymentCancel: (paymentResult: Pick<PaymentResult, 'success' | 'message' | 'orderId'>) => void;
onPaymentComplete: (paymentResult: PaymentResult) => void;
};
10 changes: 10 additions & 0 deletions src/card/types/PaymentResult.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type PaymentResult = {
success: boolean;
message: string;
orderId: string;
totalAmount: number;
cardNumber: string;
paymentTimestamp: number;
};

export type PaymentCancel = Pick<PaymentResult, 'success' | 'message' | 'orderId'>;

0 comments on commit 11fed7e

Please sign in to comment.