카드 결제

PROD-ORDER-PAY-DETAIL · /products/order/[orderNo]/pay

기능
3
API 호출
0
가드레일
6
id 가 테스트 이름
구현 단계
10 / 10
완료수강신청 · 결제wvctesol-api :8080서버 컴포넌트동적 라우트95

개발 스펙

이 화면에서 할 수 있어야 하는 것 (2)

  • NICEPAY 결제창이 뜬다.
  • PG 설정이 없으면 무엇이 필요한지 안내한다.

비즈니스 규칙 (4)

  • 요청 해시 = sha256(EdiDate + MID + Amt + MerchantKey).
    근거: products_record01.php:88
  • 인증 검증 = sha256(AuthToken + MID + Amt + MerchantKey).
    근거: nicepayResult_utf.php:74
  • 승인 해시 = sha256(AuthToken + MID + Amt + EdiDate + MerchantKey), 성공코드 3001.
    근거: nicepayResult_utf.php:87
  • 인증만으로는 결제가 끝나지 않는다. 승인(NextAppURL)까지 성공해야 매출이 잡힌다.

API (2)

메서드경로권한모듈 · 설명
POST/api/commerce/payments/nicepay/return공개
결제창 ReturnURL. 서명 검증 · 금액 확인 · 승인 호출 · TID 저장.
domains:commerce:commerce-web
400 SIGNATURE_MISMATCH
400 AMOUNT_MISMATCH
400 APPROVE_FAILED
400 PG_NOT_CONFIGURED
409 ALREADY_PAID
POST/api/commerce/payments/nicepay/return공개
NICEPAY ReturnURL. 인증 검증 → 승인 호출 → 상태 반영.
domains:commerce:commerce-web
결제 실패 시 주문 화면으로 사유와 함께 리다이렉트

가드레일 (6) — id 가 곧 테스트 이름이다

id반드시 만족해야 하는 것등급
pay-no-double-approval
이미 결제된 주문을 두 번 승인하면 안 된다.
결제창에서 뒤로 갔다 다시 오는 일이 있다.
high
pay-fails-loudly-without-config
상점키가 없으면 조용히 성공시키지 말고 거부해야 한다.
critical
pay-verify-signature
서명 검증에 실패한 인증 결과는 승인으로 넘어가면 안 된다.
critical
pay-amount-match
PG 가 돌려준 금액이 주문 금액과 다르면 승인하지 않아야 한다.
critical
pay-store-tid
승인 성공 시 TID 를 반드시 저장해야 한다. 없으면 나중에 환불할 수 없다.
레거시 카드 결제 417건이 TID 가 없어 자동 취소가 안 된다.
critical
pay-disabled-without-key
PG 키가 없으면 결제창을 열지 않고 안내해야 한다. 무통장 흐름은 영향받지 않아야 한다.
high

구현 계획 (10/10)

  1. PaymentGateway 에 verifyAuthSignature · approve 추가commerce-domain
  2. ApprovePaymentUseCase — 검증 순서를 원본대로commerce-app
  3. NicepayGateway.approve — 승인 호출과 응답 서명 확인commerce-infrastructure
  4. POST /payments/nicepay/return 구현commerce-web
  5. 앱 ReturnURL 라우트를 서버로 전달wvctesol
  6. Order · OrderState · Entitlement · 포트 2개commerce-domain
  7. PlaceOrderUseCase · AdminOrderUseCasecommerce-app
  8. OrderRepositoryAdapter · NicepayGatewaycommerce-infrastructure
  9. /api/commerce/payments/nicepay/return 구현commerce-web
  10. Next.js 를 BFF 로 전환 — Prisma 직접 접근 제거wvctesol

서버 사이드 구현 계획 — 자바 객체별 요구사항 (7/7)

객체모듈책임 · 지켜야 할 것
ApprovePaymentUseCase
service
commerce-app
카드 승인. 원본 nicepayResult_utf.php 의 판정을 옮겼다.
  • 순서를 지킨다 — 인증코드 → 금액 → 서명 → 승인 호출 → 응답 금액 재확인.
  • 금액은 세션이 아니라 DB 의 주문과 맞춘다. 세션은 흔들 수 있고 서버가 둘이면 공유되지 않는다.
  • 서명이 틀리면 승인을 호출조차 하지 않는다.
  • markApproved 에 TID 를 반드시 넘긴다 — 없으면 환불이 불가능해진다.
  • 이미 success 이면 ConflictException. 결제창 뒤로가기로 두 번 오는 경우가 있다.
  • 상점키가 없으면 DomainException 으로 거부한다. 조용히 성공시키지 않는다.
  • 수강권 활성화는 원본에 없던 동작이다 — 원본은 관리자가 직접 켰다.
PlaceOrderUseCase
service
commerce-app
주문 생성.
  • 금액을 요청에서 받지 않는다. 상품 코드로 서버가 다시 읽는 것이 유일한 출처다.
  • 연락처는 숫자만 남긴다. 원본도 하이픈 없이 저장한다.
  • 비로그인 주문을 허용한다 — memberId 가 null 일 수 있다.
AdminOrderUseCase
service
commerce-app
입금확인 · 환불.
  • 모든 진입점에서 RequireAdmin.check 를 부른다. 화면 가드만으로는 부족하다.
  • 카드 환불은 PG 취소가 성공해야만 상태를 바꾼다. 실패하면 예외를 던져 트랜잭션을 되돌린다.
  • TID 가 없는 레거시 카드 주문은 막고 안내한다. 상태를 건드리지 않는다.
  • 이미 cancel 인 주문은 409 로 거부한다.
Entitlement
class
commerce-domain
상품 코드 → 수강권 플래그 매핑.
  • 원본에는 이 매핑이 없다 — 결제가 회원 플래그를 건드리지 않았고 관리자가 직접 켰다.
  • tesoltec 은 pay_yn 과 pay_tec_yn 을 모두 연다.
  • 컬럼 이름이 여기서만 나온다. 사용자 입력이 SQL 식별자가 되지 않는다.
OrderState
enum
commerce-domain
주문 상태.
  • 무통장도 입금 확인 전까지 READY 다. 원본은 즉시 success 였지만 확인 UI 가 없었기 때문이다.
  • 알 수 없는 값은 READY 로 읽는다 — 레거시 행에 빈 state 가 있다.
NicepayGateway
class
commerce-infrastructure
NICEPAY 취소 연동.
  • SignData = sha256(MID + CancelAmt + EdiDate + MerchantKey), 성공코드 2001.
  • 상점키는 환경변수로만 받는다. 없으면 available()=false 이고 카드 환불을 시도조차 하지 않는다.
  • 도메인은 이 클래스를 모른다 — PaymentGateway 포트로만 만난다.
OrderRepositoryAdapter
class
commerce-infrastructure
payment 테이블 조작.
  • 원본 insertPayment 처럼 cost 와 amount 에 같은 값을 넣는다.
  • setEntitlement 의 컬럼 이름은 Entitlement 매핑에서만 온다.

테스트 방법 (10)

가드레일종류확인 방법
pay-verify-signatureunit서명이 틀리면 승인 호출 자체를 하지 않는다
pay-amount-matchunit결제창 금액과 승인 응답 금액을 각각 주문과 대조한다
pay-store-tidunit승인 성공 시 markApproved 에 TID 가 전달된다
pay-no-double-approvalunitstate=success 인 주문 승인 → 409 ALREADY_PAID
pay-fails-loudly-without-configintegration상점키 없이 호출 → 400 PG_NOT_CONFIGURED, 주문 상태 변화 없음
pay-verify-signaturee2eSignature 를 임의 값으로 바꿔 콜백 POST → 승인 호출 없이 실패 리다이렉트
pay-amount-matche2eAmt 를 주문 금액과 다르게 보내 콜백 → 승인하지 않음
pay-store-tide2e승인 성공 후 payment.tid 가 비어 있지 않음
pay-no-double-approvale2e같은 콜백을 2회 전송 → state 는 success 1회, 수강권 중복 처리 없음
pay-disabled-without-keye2eNICEPAY 환경변수를 지운 상태에서 카드 결제 화면 → 안내 문구, 무통장 주문은 정상

스냅샷

PROD-ORDER-PAY-DETAIL 스냅샷

기능 목록 (3)

입력
  • ·1개
  • ·입력 필드1개
상태
  • ·화면 상태status

화면 이동

← 이 화면으로 오는 곳
이 화면에서 가는 곳 →

연결

API

직접 호출하는 API 가 없습니다

소스
src/app/products/order/[orderNo]/pay/page.tsx