Payvit 개발자 가이드 v1.0
온라인 VAN 결제 시스템 — 카드 단말기 수수료 그대로, 온라인 결제로
도메인: https://api.getpayvit.com
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회, 부가세 별도) 결제 | 가맹점 |
| 5 | API Key 로 연동 개발 시작 | 가맹점 개발자 |
10분 퀵스타트
Step 1. 결제 생성 API를 호출하여 결제 링크를 받습니다.
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를 고객에게 전달합니다.
{
"resultCode": "00",
"data": {
"paymentId": "enc_abc123...",
"paymentLink": "https://api.getpayvit.com/pay/enc_abc123...",
"merchantId": "my-store",
"merchantNm": "내 가게",
"price": "1000"
}
}
Step 3. 결제 완료 시 가맹점 webhookUrl로 결과가 자동 전달됩니다.
{
"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 챗봇이 빠르게 컨텍스트 파악할 때 첫 진입점 |
활용 예시
다음 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 에서 그대로 매칭
# 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 헤더가 필수입니다.
X-API-Key: {발급받은 API Key}
| 인증 모드 | 설명 | 접근 범위 |
|---|---|---|
MERCHANT | 가맹점별 API Key | 본인 가맹점 데이터만 |
ADMIN | 글로벌 관리자 Key | 전체 가맹점 데이터 |
결제 흐름
POST /api/payment/create 호출 → paymentLink 반환paymentLink를 고객에게 전달 (문자, 카카오톡, 앱 푸시 등)webhookUrl로 결제 결과 자동 POST (비동기)결제 상태 코드
| 코드 | 상태 | 설명 |
|---|---|---|
00 | 대기 | 결제 생성됨, 고객 미진입 |
10 | 인증완료 | 결제앱 인증 완료, 승인 대기 |
20 | 승인완료 | 결제 성공 (최종) |
30 | 취소 | 결제 취소/환불 완료 (최종) |
99 | 실패 | 인증/승인 실패 (최종) |
지원 결제수단
| 분류 | 코드 | 이름 |
|---|---|---|
| 간편결제 | KKP2 | 카카오페이 |
| 간편결제 | NAVER | 네이버페이 (등록 즉시 결제 가능. NaverPay 측 완전 반영은 D+2일 권고 — 그 전엔 네이버플레이스 미노출 + 프로모션 미적용) |
| 카드 | SHINHAN | 신한카드 |
| 카드 | SAMSUNG | 삼성카드 |
| 카드 | HYUNDAI | 현대카드 |
| 카드 | HANA | 하나카드 |
| 카드 | KBPAY | KB국민카드 |
| 카드 | WOORI | 우리카드 |
| 카드 | BC | BC카드 |
통합 가맹점 등록
프랜차이즈/플랫폼에서 업체 등록 시 1회 호출로 가맹점 + NiceVAN + NaverPay까지 완료합니다. ADMIN Key 필수.
Content-Type: multipart/form-data
X-API-Key: {ADMIN API Key}
Request (multipart/form-data)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
merchantId | String | 필수 | 가맹점 ID (예: stb-gangnam) |
merchantNm | String | 필수 | 업체명 |
bizNo | String | 필수 | 사업자번호 10자 (하이픈 허용) |
bizType | String | 선택 | 사업자 유형 (AN=개인, CN=법인). 기본 AN |
ownerNm | String | 필수 | 대표자명 (사업자등록증 상 첫 번째 기재 대표자 1명) |
ownerBirth | String | 필수 | 대표자 개인 생년월일 YYYYMMDD(8자) 또는 YYMMDD(6자). 법인도 대표이사 생년월일 — 법인번호 X |
corpRegNo | String | 법인 필수 | 법인등록번호 13자 (bizType=CN 일 때 필수, 하이픈 허용) |
ownerPhone | String | 필수 | 대표자 연락처 (하이픈 허용) |
addr | String | 필수 | 사업장 주소 |
addrDetail | String | 선택 | 상세주소 |
zipno | String | 선택 | 우편번호 |
email | String | 선택 | 이메일 |
webhookUrl | String | 선택 | 웹훅 URL (미입력 시 그룹 기본값) |
merchantGroupId | String | 선택 | 소속 그룹 ID |
contractFile | File | 필수 | 사업자등록증 (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 (전체 성공)
{
"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 (부분 실패 — 계약서 실패 시)
{
"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 | 설명 |
|---|---|---|
| 1 | merchant | 가맹점 DB 등록 + API Key 발급 |
| 2 | catId | NiceVAN용 CAT-ID 자동 할당 |
| 3 | contract | NiceVAN 사업자등록증 업로드 |
| 4 | affInfo | NiceVAN 카드사 가맹점번호 조회 |
| 5 | nicevan | NiceVAN 가맹점 자동 등록 (MID 발급) |
| 6 | naverPay | NaverPay 하위가맹점 등록 |
resultCode: "00"입니다 (가맹점 DB 등록은 성공).
steps의 success 필드로 각 단계를 개별 확인하세요.
실패 시 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 발급 전제) |
결제 생성
Content-Type: application/json
X-API-Key: {API Key}
Request Body
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
merchantId | String | 필수 | 가맹점 ID |
orderId | String | 필수 | 가맹점 주문번호 (고유값 권장) |
productName | String | 필수 | 결제 품목명 |
price | Integer | 필수 | 결제 금액 (원) |
extraData | String | 선택 | 추가 데이터 (JSON 권장) |
extraData 활용 (JSON)
JSON으로 넣으면 결제창에 "상세 정보" 카드로 자동 표시됩니다.
// 학원
{"수강생":"박시훈","반":"심화반","기간":"2026.03.01~03.31"}
// 쇼핑몰
{"배송지":"서울 강남구","수량":"2개","옵션":"블랙 L"}
Response
{
"resultCode": "00",
"data": {
"paymentId": "enc_abc123...",
"paymentLink": "https://api.getpayvit.com/pay/enc_abc123...",
"price": "250000",
"merchantId": "biz-gangnam",
"merchantNm": "솔레아스 강남"
}
}
결제 조회
X-API-Key: {API Key}
{
"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) 상태의 결제만 취소 가능합니다.
Content-Type: application/json
X-API-Key: {API Key}
{
"resultCode": "00",
"data": {
"paymentId": "enc_abc123...",
"approvalNum": "87654321",
"approvalTime": "20260410180500"
}
}
결제 내역 검색
X-API-Key: {API Key}
?orderId=ORD-001&payStatus=20&startDate=2026-04-01&endDate=2026-04-10&page=1&size=20
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
orderId | String | 선택 | 주문번호 (부분 검색) |
payStatus | String | 선택 | 결제 상태 (00/20/30/97/99) |
startDate | String | 선택 | 시작일 (yyyy-MM-dd) |
endDate | String | 선택 | 종료일 (yyyy-MM-dd) |
page | Integer | 선택 | 페이지 (기본 1) |
size | Integer | 선택 | 페이지 크기 (기본 20) |
웹훅
결제 완료 시 Payvit이 가맹점 webhookUrl로 결과를 자동 POST 합니다.
웹훅 수신 구현 (Java)
@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)
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 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 관리자가 가맹점 설정에서 템플릿을 지정합니다. 가맹점 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 키는 본인 가맹점 데이터만 접근 가능합니다.
가맹점 신청
가맹점 등록은 온라인 신청 → 관리자 승인 방식으로 진행됩니다.
신청 방법
https://api.getpayvit.com/apply그룹 소속:
https://api.getpayvit.com/apply/{초대코드}(초대코드는 본사에서 Payvit 관리자에게 발급받아 지점에 공유)
신청 시 필요 정보
| 항목 | 필수 | 설명 |
|---|---|---|
가맹점 ID | 필수 | 영문/숫자/하이픈 (예: stb-gangnam) |
가맹점명 | 필수 | 지점명 (예: 스포츠트라이브 강남점) |
사업자번호 | 필수 | 10자리 숫자 |
대표자명 | 필수 | |
연락처 | 필수 | 휴대전화 번호 |
대표자 생년월일 | 선택 | YYYYMMDD (NiceVAN 등록 시 필요) |
사업장 주소 | 선택 | |
이메일 | 선택 | |
웹훅 URL | 선택 | 나중에 설정 가능 |
사업자등록증 | 필수 | PDF / JPG / PNG (NiceVAN 등록에 사용) |
/apply/{초대코드}로 접속하면 그룹이 자동 매칭되고, 승인 시 그룹 설정(템플릿, 채널 등)이 자동 상속됩니다.
- 가맹점 등록 + API Key 발급
- 기본 결제 채널 초기화
- NiceVAN 계약서 등록 (S3에서 사업자등록증 자동 전달)
- NiceVAN 가맹점번호 조회 + 자동 등록 (CAT-ID/MID 발급)
- NaverPay 하위가맹점 자동 등록
| 상태 | 의미 | 결제 |
|---|---|---|
| 미등록 (0) | NiceVAN 미실행 또는 어느 단계에서 실패 | 불가 |
| 등록완료 (2) | BIZ_0000 응답으로 자동 전환 (즉시 결제 가능) | 가능 |
※ 1 값(레거시 "심사대기")은 더 이상 자동 부여되지 않습니다. NaverPay 도 등록 즉시 결제 가능 — NaverPay 측 완전 반영(네이버플레이스 노출 + 프로모션 적용)은 D+2일 권고.
연동 체크리스트
- API Key 발급 완료
- 결제 생성 API 호출 성공 (paymentLink 수신 확인)
- paymentLink로 결제창 접속 확인
- 웹훅 수신 endpoint 구현 (HTTP 200 응답)
- 웹훅 수신 테스트 완료
- 결제 조회 API 호출 성공
- 결제 취소 API 호출 성공 (필요 시)
- 에러 처리 구현 (resultCode 분기)