결제
PortOne 기반 결제 시스템의 설정과 동작 방식을 설명합니다.
개요
kindie는 PortOne(구 아임포트)을 사용하여 결제를 처리합니다. 구독 결제와 단건 결제를 모두 지원하며, 웹훅을 통해 결제 상태를 실시간으로 동기화합니다.
PortOne 설정
1. 가입 및 채널 설정
- PortOne에 가입
- 결제 연동 > 채널 관리에서 결제 채널 추가 (예: 토스페이먼츠, KG이니시스)
- 테스트 모드로 시작하여 개발 중에는 실결제 없이 테스트
2. 환경변수 설정
NEXT_PUBLIC_PORTONE_STORE_ID="your-store-id"
NEXT_PUBLIC_PORTONE_CHANNEL_KEY="your-channel-key"
PORTONE_API_SECRET="your-api-secret"NEXT_PUBLIC_PORTONE_STORE_ID— 상점 ID (클라이언트에서 사용)NEXT_PUBLIC_PORTONE_CHANNEL_KEY— 채널 키 (클라이언트에서 사용)PORTONE_API_SECRET— API 시크릿 (서버에서 결제 검증용)
결제 플로우
1. 클라이언트: PortOne SDK로 결제창 호출
2. 사용자: 결제 수단 선택 및 결제
3. PortOne: 결제 결과를 클라이언트에 반환
4. 클라이언트: paymentId를 서버로 전송
5. 서버: PortOne API로 결제 검증 (금액, 상태 확인)
6. 서버: DB에 결제 기록 저장
7. PortOne → 웹훅: 결제 상태 변경 시 서버에 알림관련 파일
lib/payments/
├── portone.ts PortOne API 클라이언트
├── verify.ts 결제 검증 로직
├── billing.ts 구독 관리
├── pricing.ts 가격 계산
└── webhook.ts 웹훅 처리
actions/
├── payments.ts 결제 서버 액션
└── subscriptions.ts 구독 서버 액션
app/api/webhooks/
└── portone/route.ts PortOne 웹훅 엔드포인트
components/payments/
├── checkout-form.tsx 단건 결제 폼
└── subscription-form.tsx 구독 결제 폼checkout-form.tsx/subscription-form.tsx는 재사용 가능한 컴포넌트이며, 기본 템플릿에 전용 /checkout/* 라우트는 포함되어 있지 않습니다.
웹훅 설정
PortOne 대시보드에서 웹훅 URL을 등록하세요:
https://your-domain.com/api/webhooks/portone웹훅은 결제 완료, 취소, 실패 등의 이벤트를 처리합니다. 서버 액션의 결제 검증과 웹훅은 이중 안전장치로 동작합니다.
구독 결제
config.ts의 plans 객체에 정의된 요금제를 기반으로 구독 결제가 동작합니다:
// config.ts
export const plans = {
free: { priceMonthly: 0, ... },
pro: { priceMonthly: 29000, priceYearly: 290000, ... },
enterprise: { priceMonthly: 99000, priceYearly: 990000, ... },
};사용자의 구독 상태는 subscriptions 테이블에서 관리되며, 대시보드의 설정 > 결제 페이지에서 확인할 수 있습니다.
현재 기본 템플릿의
/settings/billing페이지는 샘플 UI입니다. 실제 결제/구독 상태 조회 및 체크아웃 라우팅은 서비스 요구사항에 맞게 연결해야 합니다.