Payvit 개발자 가이드 v1.0

온라인 VAN 결제 시스템 — 카드 단말기 수수료 그대로, 온라인 결제로

도메인: https://api.getpayvit.com

🏪 ONLINE VAN PAYMENT

PG 마진 없는 카드사 직접 가맹등록
법정 우대수수료 그대로 적용

Payvit 가맹점은 PG 의 하위가맹점이 아니라 카드사와 직접 계약된 VAN 가맹점으로 등록됩니다. 결제금은 카드사에서 가맹점 통장으로 직접 정산되며, 여신전문금융업법상 법정 우대수수료가 그대로 적용되어 온라인 결제도 카드 단말기 수수료로 받을 수 있습니다.

카드 종류 (영세가맹점 기준) Payvit 일반 PG (토스 / 이니시스 등) 격차
체크카드 0.15% 3.2 ~ 5.5% 최대 약 32배
신용카드 0.40% 3.2 ~ 5.5% 약 8 ~ 14배
간편결제 (카카오/네이버페이) 0.15%~ 3.9 ~ 6.5% 최대 약 43배
  • 카드사 → 가맹점 통장 직접 정산 (PG 마진 없음, 영업일 D+1부터)
  • 사업자등록증 1장으로 기존 카드 가맹번호 자동 연결 (신규 등록·심사 X)
  • 가입비 22만원 (1회), 그 외 월 사용료·이체수수료·API 호출비 0원
  • 월 매출 3천만원 → 연 약 1,341만원 수수료 절약 (체크 50% / 신용 50%, PG 평균 4% 비교)

시작하기

사전 준비

Payvit 은 이미 보유한 카드사 가맹번호를 그대로 온라인 결제에 연결합니다. 신규 가맹등록·신용심사 단계가 없으므로 사업자등록증 1장으로 분 단위 시작 가능합니다.

단계설명담당
1가맹점 신청 폼 작성 (사업자등록증 업로드, 5분)가맹점
2기존 카드사 가맹번호 자동 매칭 + 간편결제 하위가맹점 등록 (1분)Payvit (자동)
3가맹점 ID + API Key 발급Payvit (자동)
4가입비 22만원 (1회, 부가세 별도) 결제가맹점
5API Key 로 연동 개발 시작가맹점 개발자
비용 안내: 가입비 22만원 (1회, 부가세 별도) 외 월 사용료·이체수수료·API 호출비는 발생하지 않습니다. 결제 수수료는 카드사가 가맹점에 직접 적용하는 법정 우대수수료 (영세 체크 0.15% / 신용 0.40%) 그대로.

10분 퀵스타트

Step 1. 결제 생성 API를 호출하여 결제 링크를 받습니다.

cURL
curl -X POST https://api.getpayvit.com/api/payment/create \
  -H "Content-Type: application/json" \
  -H "X-API-Key: {발급받은 API Key}" \
  -d '{
    "merchantId": "{가맹점 ID}",
    "orderId": "ORDER-001",
    "productName": "테스트 상품",
    "price": 1000
  }'

Step 2. 응답의 paymentLink를 고객에게 전달합니다.

Response (JSON)
{
  "resultCode": "00",
  "data": {
    "paymentId": "enc_abc123...",
    "paymentLink": "https://api.getpayvit.com/pay/enc_abc123...",
    "merchantId": "my-store",
    "merchantNm": "내 가게",
    "price": "1000"
  }
}
paymentLink를 문자, 카카오톡, 앱 푸시 등으로 고객에게 전달하세요. 고객이 클릭하면 결제창이 자동으로 표시됩니다.

Step 3. 결제 완료 시 가맹점 webhookUrl로 결과가 자동 전달됩니다.

Webhook Payload (Payvit → 가맹점)
{
  "paymentId": "enc_abc123...",
  "orderId": "ORDER-001",
  "merchantId": "my-store",
  "payStatus": "20",
  "price": 1000,
  "approvalNum": "12345678",
  "approvalTime": "20260410173025",
  "paymentCompCode": "KKP2"
}

AI / 기계 판독 스펙

ChatGPT, Claude, Gemini, Cursor 등 AI 코드 생성 도구에 아래 두 파일을 첨부하면 Payvit 연동 코드를 자동으로 생성할 수 있습니다.

파일용도대상
/openapi.yaml OpenAPI 3.1 스펙 — 모든 endpoint / request / response schema 정의 AI / Postman / Swagger UI / OpenAPI Generator (SDK 자동 생성)
/llms.txt LLM 친화 요약 (llmstxt.org 표준) — 핵심 endpoint + 문서 링크 1페이지 AI 챗봇이 빠르게 컨텍스트 파악할 때 첫 진입점

활용 예시

ChatGPT / Claude 프롬프트 예시
다음 OpenAPI 스펙을 참고해서 Payvit 결제 연동 코드를 Node.js + Express 로 작성해줘:
https://api.getpayvit.com/openapi.yaml

요구사항:
- POST /api/payment/create 호출하여 paymentLink 발급
- /webhook endpoint 에서 결제 완료 수신 (HTTP 200 + {"resultCode":"OK"} 응답)
- extraData 에 내부 주문번호 JSON 으로 넣고 webhook 에서 그대로 매칭
SDK 자동 생성 (OpenAPI Generator)
# TypeScript SDK 자동 생성
npx @openapitools/openapi-generator-cli generate \
  -i https://api.getpayvit.com/openapi.yaml \
  -g typescript-fetch \
  -o ./payvit-sdk

# Python SDK 자동 생성
npx @openapitools/openapi-generator-cli generate \
  -i https://api.getpayvit.com/openapi.yaml \
  -g python \
  -o ./payvit-sdk-python
왜 두 파일을 따로 두나요?
openapi.yaml 은 endpoint 한 줄까지 기계가 정확히 읽어낼 수 있는 스펙이고, llms.txt 는 AI 가 첫 1초에 "이 서비스가 뭐 하는 거고 어디에 뭐가 있다" 를 파악하는 네비게이션입니다. AI 챗봇은 먼저 llms.txt 로 전체 그림을 잡고, 구체 endpoint 가 필요해지면 openapi.yaml 을 fetch 해서 정확한 schema 를 사용합니다.

인증

모든 API 요청에 X-API-Key 헤더가 필수입니다.

Header
X-API-Key: {발급받은 API Key}
인증 모드설명접근 범위
MERCHANT가맹점별 API Key본인 가맹점 데이터만
ADMIN글로벌 관리자 Key전체 가맹점 데이터
API Key 보안: API Key는 서버 사이드에서만 사용하세요. 클라이언트(브라우저/앱)에 노출하면 안 됩니다.

결제 흐름

1
결제 생성
POST /api/payment/create 호출 → paymentLink 반환
가맹점 서버Payvit
2
결제 링크 전달
paymentLink를 고객에게 전달 (문자, 카카오톡, 앱 푸시 등)
가맹점고객
3
결제창 진입 + 결제수단 선택
고객이 링크 클릭 → Payvit 결제창 표시 → 카드/간편결제 선택 + 약관 동의
고객Payvit 결제창
4
결제앱 인증 + 승인
결제앱(카카오/신한 등) 실행 → 인증 → Payvit이 NICE에 자동 승인 요청
고객PayvitNICE
5
웹훅 전달 (결제 결과)
Payvit → 가맹점 webhookUrl로 결제 결과 자동 POST (비동기)
Payvit가맹점 서버
6
결제 완료 + 영수증
고객에게 결제 완료 페이지 표시 (승인번호, 카드사, 금액 등)
Payvit고객

결제 상태 코드

코드상태설명
00대기결제 생성됨, 고객 미진입
10인증완료결제앱 인증 완료, 승인 대기
20승인완료결제 성공 (최종)
30취소결제 취소/환불 완료 (최종)
99실패인증/승인 실패 (최종)

지원 결제수단

분류코드이름
간편결제KKP2카카오페이
간편결제NAVER네이버페이 (등록 즉시 결제 가능. NaverPay 측 완전 반영은 D+2일 권고 — 그 전엔 네이버플레이스 미노출 + 프로모션 미적용)
카드SHINHAN신한카드
카드SAMSUNG삼성카드
카드HYUNDAI현대카드
카드HANA하나카드
카드KBPAYKB국민카드
카드WOORI우리카드
카드BCBC카드

통합 가맹점 등록

프랜차이즈/플랫폼에서 업체 등록 시 1회 호출로 가맹점 + NiceVAN + NaverPay까지 완료합니다. ADMIN Key 필수.

POST /api/merchant/register-full
Content-Type: multipart/form-data
X-API-Key: {ADMIN API Key}

Request (multipart/form-data)

필드타입필수설명
merchantIdString필수가맹점 ID (예: stb-gangnam)
merchantNmString필수업체명
bizNoString필수사업자번호 10자 (하이픈 허용)
bizTypeString선택사업자 유형 (AN=개인, CN=법인). 기본 AN
ownerNmString필수대표자명 (사업자등록증 상 첫 번째 기재 대표자 1명)
ownerBirthString필수대표자 개인 생년월일 YYYYMMDD(8자) 또는 YYMMDD(6자). 법인도 대표이사 생년월일 — 법인번호 X
corpRegNoString법인 필수법인등록번호 13자 (bizType=CN 일 때 필수, 하이픈 허용)
ownerPhoneString필수대표자 연락처 (하이픈 허용)
addrString필수사업장 주소
addrDetailString선택상세주소
zipnoString선택우편번호
emailString선택이메일
webhookUrlString선택웹훅 URL (미입력 시 그룹 기본값)
merchantGroupIdString선택소속 그룹 ID
contractFileFile필수사업자등록증 (PDF/JPG/PNG)
⚠️ 법인사업자 주의
ownerBirth 는 개인/법인 공통 대표자 개인 생년월일 필드입니다. 법인이어도 대표이사 본인의 생년월일(8자 YYYYMMDD)을 넣어주세요. 법인등록번호(13자리)는 반드시 별도 corpRegNo 필드로 분리 전송해야 합니다. 혼동 시 NICE 측 DB 제약으로 ORA-12899 / CONT_0012 에러가 발생하며, Payvit 서버는 validation error(98)로 즉시 차단합니다.
👥 공동대표 가맹점
사업자등록증에 공동대표(2인 이상)가 기재되어 있어도 NICE API 의 ownerNm / ownerBirth단일값 필드입니다. 사업자등록증 상 첫 번째 기재 대표자 1명만 입력하면 됩니다. NICE 측에서 사업자등록증 파일과 별도 검증하지 않으므로 심사 통과에 문제 없습니다 (2026-04-16 NICE 정보개발실 안내).

Response (전체 성공)

JSON
{
  "resultCode": "00",
  "data": {
    "merchantId": "stb-gangnam",
    "apiKey": "a1b2c3d4e5f6...",
    "catId": "6126530",
    "mid": "NICE-MID-001",
    "naverMerchantId": "LINKPstbgangnam",
    "nicevanStatus": "2",
    "steps": {
      "merchant":  { "success": true,  "message": "가맹점 등록 완료" },
      "catId":     { "success": true,  "message": "CAT-ID 할당 완료: 6126530" },
      "contract":  { "success": true,  "message": "계약서 등록 완료" },
      "affInfo":   { "success": true,  "message": "가맹점번호 조회 완료" },
      "nicevan":   { "success": true,  "message": "NiceVAN 등록 완료 (MID: NICE-MID-001)" },
      "naverPay":  { "success": true,  "message": "NaverPay 등록 완료: LINKPstbgangnam" }
    }
  }
}

Response (부분 실패 — 계약서 실패 시)

JSON
{
  "resultCode": "00",
  "data": {
    "merchantId": "stb-gangnam",
    "apiKey": "a1b2c3d4e5f6...",
    "catId": "6126530",
    "mid": "",
    "naverMerchantId": "",
    "nicevanStatus": "0",
    "steps": {
      "merchant":  { "success": true,  "message": "가맹점 등록 완료" },
      "catId":     { "success": true,  "message": "CAT-ID 할당 완료: 6126530" },
      "contract":  { "success": false, "message": "CONT_9999: 파일 형식 오류" },
      "affInfo":   { "success": false, "message": "미실행 (계약서 등록 실패)" },
      "nicevan":   { "success": false, "message": "미실행 (계약서 등록 실패)" },
      "naverPay":  { "success": false, "message": "미실행 (계약서 등록 실패)" }
    }
  }
}

steps 항목

단계key설명
1merchant가맹점 DB 등록 + API Key 발급
2catIdNiceVAN용 CAT-ID 자동 할당
3contractNiceVAN 사업자등록증 업로드
4affInfoNiceVAN 카드사 가맹점번호 조회
5nicevanNiceVAN 가맹점 자동 등록 (MID 발급)
6naverPayNaverPay 하위가맹점 등록
부분 실패해도 resultCode: "00"입니다 (가맹점 DB 등록은 성공). stepssuccess 필드로 각 단계를 개별 확인하세요. 실패 시 admin에서 수동 재시도 가능합니다.

nicevanStatus (등록 후 결제 가능 시점)

상태결제 가능
0미등록 (NiceVAN 단계 실패)X
2등록완료 (BIZ_0000 응답으로 자동 전환, 즉시 결제 가능)O

1 값(레거시 "심사대기")은 더 이상 자동 부여되지 않습니다. 카드 가맹등록 성공 응답 = 즉시 결제 가능.

register-full 성공 = 즉시 결제 가능이 아닙니다. NICE 심사 완료 후 Payvit admin에서 등록완료(2)로 전환해야 결제가 가능합니다.

부분 실패 시 개별 재시도

register-full에서 NiceVAN이나 NaverPay가 실패해도 가맹점 + API Key는 발급된 상태입니다. 실패한 단계만 별도 API로 재시도할 수 있습니다.

실패 단계재시도 API설명
contract / affInfo / nicevan POST /api/merchant/{id}/register NiceVAN 3단계 전체 재시도 (multipart, 사업자등록증 재첨부)
naverPay만 실패 POST /api/merchant/{id}/register-naver NaverPay만 단독 재시도 (JSON, MID 발급 전제)
NiceVAN 재시도는 계약서 등록부터 3단계를 처음부터 다시 실행합니다 (중간 이어하기 불가). 이미 할당된 CAT-ID는 재사용됩니다. 모든 재시도 API는 ADMIN Key 필수입니다.

결제 생성

POST /api/payment/create
Content-Type: application/json
X-API-Key: {API Key}

Request Body

필드타입필수설명
merchantIdString필수가맹점 ID
orderIdString필수가맹점 주문번호 (고유값 권장)
productNameString필수결제 품목명
priceInteger필수결제 금액 (원)
extraDataString선택추가 데이터 (JSON 권장)

extraData 활용 (JSON)

JSON으로 넣으면 결제창에 "상세 정보" 카드로 자동 표시됩니다.

extraData 예시
// 학원
{"수강생":"박시훈","반":"심화반","기간":"2026.03.01~03.31"}

// 쇼핑몰
{"배송지":"서울 강남구","수량":"2개","옵션":"블랙 L"}

Response

JSON
{
  "resultCode": "00",
  "data": {
    "paymentId": "enc_abc123...",
    "paymentLink": "https://api.getpayvit.com/pay/enc_abc123...",
    "price": "250000",
    "merchantId": "biz-gangnam",
    "merchantNm": "솔레아스 강남"
  }
}
paymentLink 하나만으로 결제수단 선택 + 결제 + 결과 화면까지 자동 처리됩니다.

결제 조회

GET /api/payment/{paymentId}
X-API-Key: {API Key}
Response
{
  "resultCode": "00",
  "data": {
    "paymentId": "enc_abc123...",
    "merchantId": "biz-gangnam",
    "orderId": "ORD-2026-001",
    "productName": "3월 학원비",
    "price": 250000,
    "payStatus": "20",
    "paymentCompCode": "KKP2",
    "webhookSent": "1",
    "regTs": "2026-04-10T17:30:00",
    "payTs": "2026-04-10T17:32:15"
  }
}

결제 취소 (환불)

승인완료(payStatus=20) 상태의 결제만 취소 가능합니다.

POST /api/payment/{paymentId}/cancel
Content-Type: application/json
X-API-Key: {API Key}
Response (성공)
{
  "resultCode": "00",
  "data": {
    "paymentId": "enc_abc123...",
    "approvalNum": "87654321",
    "approvalTime": "20260410180500"
  }
}
GET /api/payment/search
X-API-Key: {API Key}

?orderId=ORD-001&payStatus=20&startDate=2026-04-01&endDate=2026-04-10&page=1&size=20
파라미터타입필수설명
orderIdString선택주문번호 (부분 검색)
payStatusString선택결제 상태 (00/20/30/97/99)
startDateString선택시작일 (yyyy-MM-dd)
endDateString선택종료일 (yyyy-MM-dd)
pageInteger선택페이지 (기본 1)
sizeInteger선택페이지 크기 (기본 20)

웹훅

결제 완료 시 Payvit이 가맹점 webhookUrl로 결과를 자동 POST 합니다.

웹훅 수신 구현 (Java)

Java (Spring Boot)
@PostMapping("/api/payment/webhook")
public ResponseEntity<Map<String, Object>> receiveWebhook(
        @RequestBody Map<String, Object> payload) {

    String paymentId = (String) payload.get("paymentId");
    String orderId   = (String) payload.get("orderId");
    int price        = (int) payload.get("price");
    String payStatus = (String) payload.get("payStatus");

    // 주문 상태 업데이트
    orderService.updatePaymentStatus(orderId, payStatus, price);

    // 응답 (HTTP 200 필수)
    Map<String, Object> response = new HashMap<>();
    response.put("resultCode", "OK");
    return ResponseEntity.ok(response);
}

웹훅 수신 구현 (Node.js)

JavaScript (Express)
app.post('/api/payment/webhook', (req, res) => {
    const { paymentId, orderId, price, payStatus } = req.body;

    // 주문 상태 업데이트
    updateOrder(orderId, payStatus, price);

    // 응답 (HTTP 200 필수)
    res.json({ resultCode: 'OK' });
});

웹훅 재시도 정책

항목
최초 발송결제 승인 직후 (비동기)
자동 재시도5분 후 1회
최대 시도2회 (최초 + 재시도 1회)
수동 재발송Payvit 관리자에게 요청
타임아웃connect 5초 / read 10초
웹훅 URL 미설정: 웹훅 URL이 없어도 결제는 정상 동작합니다. Payload는 Payvit DB에 저장되며, 나중에 URL 설정 후 관리자가 재발송할 수 있습니다.

프랜차이즈 웹훅 분기 패턴

다수의 가맹점을 하나의 서버에서 관리하는 경우, 웹훅 URL 1개로 모든 가맹점의 결제를 수신하고 주요 키값으로 분기합니다.

분기 키용도예시
merchantId어느 가맹점의 결제인지"biz-1" = 1번 업체
orderId어느 주문인지"PAY-1-456" = 1번 업체, 456번 결제
extraData결제 생성 시 넣은 커스텀 데이터{"clubNo":"1","studentNo":"789"}
권장: 결제 생성 시 extraData에 내부 식별자를 JSON으로 넣으면, 웹훅에서 그대로 돌려받아 내부 데이터와 매칭할 수 있습니다.

결제창 (호스팅)

Payvit이 호스팅하는 결제 화면입니다. 가맹점은 paymentLink만 고객에게 전달하면 됩니다.

결제창 브랜딩

필드설명기본값
displayName결제창 표시 이름가맹점명
logoUrl상단 로고 URL없음
brandColor강조 색상#e94560

결제창 상태별 페이지

상태표시 페이지
결제 대기결제수단 선택 화면
결제 진행 중로딩 + 폴링 화면
결제 완료영수증 (승인번호, 카드사 등)
결제 실패실패 안내 + 재시도 버튼
이미 결제됨"이미 결제가 완료되었습니다"
청구서 만료"청구서가 만료되었습니다"
결제 없음"결제 정보를 찾을 수 없습니다"

결제창 템플릿

가맹점별로 결제창 디자인을 선택할 수 있습니다.

템플릿스타일비용
default기본 — 카드형, 브랜드 색상무료
modern모던 — 보라 그라데이션, 글래스모피즘무료
minimal미니멀 — 순백, 텍스트 중심무료
custom-{업체}화이트라벨 — 업체 전용 디자인유료
화이트라벨 서비스: 업체 고유의 결제창 디자인이 필요하면 Payvit 관리자에게 문의하세요. 업체 브랜드에 맞는 전용 결제창을 제작해드립니다. (피그마/이미지 시안 제공 필요)

템플릿 설정

Payvit 관리자가 가맹점 설정에서 템플릿을 지정합니다. 가맹점 API에서 직접 변경은 불가합니다.

에러 코드

resultCode설명대응
00성공
98입력 검증 오류필수 파라미터 확인
99비즈니스 오류message 확인
-1시스템 오류재시도 또는 관리자 문의
401인증 실패API Key 확인

주요 에러 메시지

상황message
중복 결제"이미 승인 완료된 주문입니다"
미등록 가맹점"가맹점 정보를 찾을 수 없습니다"
서버 연결 불가"결제 서버와 연결할 수 없습니다"
OTP 만료"결제 인증 시간이 만료되었습니다"

FAQ

Q. 같은 주문번호로 결제를 다시 만들 수 있나요?

이전 결제가 승인완료(20)이면 불가합니다. 취소(30) 또는 실패(99) 후에는 같은 orderId로 재생성 가능합니다.

Q. 결제 링크의 유효기간이 있나요?

가맹점별 billExpireHours 설정에 따릅니다. 미설정이면 무제한.

Q. extraData에 뭘 넣을 수 있나요?

자유 형식 텍스트입니다. JSON으로 넣으면 결제창에 키-값 형태로 자동 표시됩니다.

Q. MERCHANT 키로 다른 가맹점 결제를 조회할 수 있나요?

불가합니다. MERCHANT 키는 본인 가맹점 데이터만 접근 가능합니다.

가맹점 신청

가맹점 등록은 온라인 신청 → 관리자 승인 방식으로 진행됩니다.

신청 방법

1
신청 페이지 접속
독립 가맹점: https://api.getpayvit.com/apply
그룹 소속: https://api.getpayvit.com/apply/{초대코드}
(초대코드는 본사에서 Payvit 관리자에게 발급받아 지점에 공유)
2
정보 입력 + 사업자등록증 첨부 + 신청
가맹점 ID, 가맹점명, 사업자번호, 대표자명, 연락처 + 사업자등록증(PDF/JPG) 첨부
3
관리자 검토 + 승인
Payvit 관리자가 사업자등록증 확인 → 승인 시 가맹점 등록 + API Key + NiceVAN 계약서 자동 처리
4
연동 시작
발급받은 API Key로 결제 API 연동 시작

신청 시 필요 정보

항목필수설명
가맹점 ID필수영문/숫자/하이픈 (예: stb-gangnam)
가맹점명필수지점명 (예: 스포츠트라이브 강남점)
사업자번호필수10자리 숫자
대표자명필수
연락처필수휴대전화 번호
대표자 생년월일선택YYYYMMDD (NiceVAN 등록 시 필요)
사업장 주소선택
이메일선택
웹훅 URL선택나중에 설정 가능
사업자등록증필수PDF / JPG / PNG (NiceVAN 등록에 사용)
그룹 소속 신청: 본사에서 Payvit 관리자에게 초대코드를 발급받아 지점에 공유하세요. 각 지점이 /apply/{초대코드}로 접속하면 그룹이 자동 매칭되고, 승인 시 그룹 설정(템플릿, 채널 등)이 자동 상속됩니다.
승인 시 자동 처리: 관리자가 [승인] 클릭 한 번으로 아래가 모두 자동 실행됩니다.
  1. 가맹점 등록 + API Key 발급
  2. 기본 결제 채널 초기화
  3. NiceVAN 계약서 등록 (S3에서 사업자등록증 자동 전달)
  4. NiceVAN 가맹점번호 조회 + 자동 등록 (CAT-ID/MID 발급)
  5. NaverPay 하위가맹점 자동 등록
NiceVAN 등록에 실패해도 가맹점 등록은 유지되며, admin에서 수동 재시도 가능합니다.
NiceVAN 심사 프로세스: API 호출 성공 = 즉시 결제 가능이 아닙니다.
상태의미결제
미등록 (0)NiceVAN 미실행 또는 어느 단계에서 실패불가
등록완료 (2)BIZ_0000 응답으로 자동 전환 (즉시 결제 가능)가능

1 값(레거시 "심사대기")은 더 이상 자동 부여되지 않습니다. NaverPay 도 등록 즉시 결제 가능 — NaverPay 측 완전 반영(네이버플레이스 노출 + 프로모션 적용)은 D+2일 권고.

연동 체크리스트

Claude Code 연동: 이 가이드를 Claude Code에게 공유하면 연동 코드를 자동으로 생성할 수 있습니다.
Powered by Payvit (하드앤소프트)