개발자/시작하기
공통 규칙
7개 엔드포인트에 모두 적용되는 규칙입니다.
#요청
- 기본 URL:
https://b2b.cardv.net/api/v1(Live),https://sandbox.cardv.net/api/v1(Sandbox) - 경로 끝에 슬래시를 붙이지 않습니다.
/api/v1/orders/가 아니라/api/v1/orders를 쓰세요. - JSON 본문은 UTF-8로 보내고
Content-Type: application/json을 지정합니다. - 금액은
"9.2500"처럼 문자열로 보냅니다. 반올림 오차를 막기 위해서입니다. AcmeShop-CardV/1.4처럼 알아보기 쉬운User-Agent를 지정하세요.
#금액과 시간
- 금액은 소수점 넷째 자리까지 있는 문자열입니다. 예:
"merchant_price": "9.2500" - 금액은 decimal 타입으로 읽으세요. 부동소수점(float)은 쓰면 안 됩니다.
- 결제는 지갑 통화로 이뤄집니다(
GET/account의default_currency, 현재는 USD). face_currency는 카드에 표시된 통화로, 지갑 통화와 다를 수 있습니다.- 원가 계산에는 항상
merchant_price를 쓰세요.price_label같은 값은 화면 표시용입니다. - 모든 시각은 UTC 기준 ISO 8601 형식입니다. 예:
2026-09-29T08:15:30.123456Z - 초 단위 소수 자릿수는 달라질 수 있으니, 제대로 된 ISO 8601 파서를 쓰세요.
- 서명에 쓰는
X-Timestamp는 Unix 시간(초)입니다.
#ID
| 구분 | 예시 | 설명 |
|---|---|---|
| Merchant ID | M00000001 | 바뀌지 않습니다. |
| SKU ID | S000456 | 가격 조회와 주문에 씁니다. |
| 상품 ID | P000123 | SKU가 속한 상품입니다. |
| CardV 주문 ID | O-00001234 | 주문을 조회할 때 씁니다. |
| 자체 주문번호 | SHOP-10001 | external_order_id. 1~120자이며 중복될 수 없습니다. |
- ID는 문자열로 저장하고, 형식을 해석하지 마세요. 길이가 늘어날 수 있습니다.
- 주문에는 숫자형
id도 있지만 쓰지 마세요.order_id를 쓰세요. - 자체 주문번호에는
A–Z a–z 0–9 - _ .만 쓰세요.
#페이지 나누기
페이지 단위로 나눠 받는 엔드포인트는 GET/skus뿐입니다. limit과 offset을 보냅니다.
GET /api/v1/skus?limit=100&offset=200{"count": 1234, "limit": 100, "results": ["..."], "filter_options": {}}limit기본값은 100, 최대값은 500입니다. 500보다 크면 500으로 처리됩니다.count는 조건에 맞는 전체 개수입니다.offset이count에 도달할 때까지 계속 요청하세요.limit이나offset이 음수이거나 숫자가 아니면 HTTP 400이 반환됩니다.- 존재하지 않는 필터 값을 보내면 오류 대신 빈 목록이 반환됩니다.
#요청 한도
기본 한도는 계정 전체 기준 분당 60회입니다. 모든 API 키와 포털 사용자가 이 한도를 함께 씁니다. 등급에 따라 한도가 다를 수 있습니다.
1분은 시계의
:00초부터 계산합니다. 거절된 요청도 횟수에 포함됩니다.한도를 넘으면 HTTP 429와 함께
Retry-After헤더(기다려야 할 초)가 반환됩니다.{"detail": "Merchant API rate limit exceeded. Expected available in 23 seconds."}한도를 넘지 않으려면 SKU 목록을 캐시하고, 짧은 간격의 폴링 대신 Webhook을 쓰고, 429를 받을 때마다 조금 더 길게 기다리세요.
#오류
먼저 HTTP 상태 코드를 확인한 다음 JSON 본문을 읽으세요. 무엇이 잘못됐는지는 본문의 키로 판단합니다. 메시지 문구에 의존하지 마세요.
인증, 권한, 조회 실패(not found), 요청 한도 오류는 detail 키를 씁니다.
{"detail": "Order not found."}주문과 가격 조회 오류는 문제가 된 필드 이름을 키로 씁니다.
{"balance": "Insufficient available balance."}주문 항목의 형식이 잘못되면 항목별로 알려 줍니다. 순서는 보낸 items와 같습니다.
{"items": [{}, {"quantity": ["Ensure this value is greater than or equal to 1."]}]}| 키 | 발생 위치 | 해결 방법 |
|---|---|---|
detail | 전체 | 아래 상태 코드 표를 참고하세요. |
items | POST/orders | 해당 항목을 고치세요. 가격이 바뀌었다면 다시 조회하세요. |
balance | POST/orders | 포털에서 지갑을 충전하세요. |
risk | POST/orders | 주문 한도에 걸렸습니다. CardV에 문의하세요. |
external_order_id | POST/orders | 주문번호가 없거나, 너무 길거나, 다른 주문에 이미 쓰였습니다. |
wallet | POST/orders | 사용 가능한 지갑이 없습니다. CardV에 문의하세요. |
quantity, amount | 가격 조회 | 허용 범위를 벗어났거나 숫자가 아닙니다. |
limit, offset | GET/skus | 올바른 숫자가 아닙니다. |
JSON이 아닌 오류도 일부 있습니다.
error code: 1010같은 일반 텍스트와 함께 오는 HTTP 403은 CardV 앞단의 네트워크 서비스에서 막힌 것입니다. 요청이 CardV까지 도달하지 않았습니다. 서버 IP와User-Agent를 CardV에 알려 주세요.- 존재하지 않는 경로(404)나 프록시 오류(5xx)는 HTML을 반환할 수 있습니다.
오류를 로그로 남길 때 API 키, 서명, 코드는 절대 기록하지 마세요.
#HTTP 상태 코드
| 상태 | 의미 | 재시도 |
|---|---|---|
| 200 | 성공. POST/orders에서는 이미 있는 주문이라는 뜻입니다. | 필요 없음 |
| 201 | 새 주문이 생성됐습니다. | 필요 없음 |
| 400 | 요청이 거절됐습니다. 결제되지 않았습니다. | 수정 후 |
| 403 | 인증 정보, 서명, IP 문제 또는 포털 전용 엔드포인트입니다. | 수정 후 |
| 404 | 찾을 수 없거나 귀사 계정에 공개되지 않았습니다. | 안 함 |
| 405 | 이 경로에 맞지 않는 메서드입니다. | 안 함 |
| 429 | 요청이 너무 많습니다. | Retry-After 후 |
| 5xx 또는 타임아웃 | 서버나 네트워크 문제입니다. 주문은 생성됐을 수 있습니다. | 예, 아래 참고 |
POST/orders를 재시도할 때는 반드시 같은 본문과 같은 주문번호를 쓰세요.
안전한 재시도 절차를 참고하세요.
#응답
brand_logo_url과image_url은 CardV가 호스팅하는 이미지의 전체 URL이거나""입니다. 공개 이미지이므로 캐시해도 됩니다.- 주문의
invoice_url과delivery_file_url은/orders/O-00001234/invoice같은 경로이며, 아직 없으면""입니다. 포털 전용이므로 API 키로 호출하면 HTTP 403이 반환됩니다. 인보이스와 코드 CSV 파일은 포털에서 확인하세요.
#호환성
- 모르는 필드는 무시하세요. CardV는 API 버전을 바꾸지 않고 필드를 추가합니다.
- 새 상태 값이 생길 수 있습니다. 모르는 상태는 "아직 완료되지 않음"으로 처리하세요.
- JSON 키의 순서나 메시지 문구에 의존하지 마세요.
연동 관련 문의는 Merchant ID와 주문 ID 또는 요청 ID를 포함해 [email protected]로 보내 주세요.