BTSCON PUBLIC API · BTSCON-COUPON-API/1

모바일쿠폰 공용 API

BTSCON과 계약한 외부 서비스가 상품을 조회하고, 쿠폰을 주문·조회·취소할 때 사용하는 공통 규약입니다.

Base URL: https://api.btscon.co.kr/api/reseller

1. 이용 준비

  1. BTSCON 관리자가 연동 계정과 허용 발신 IP/CIDR를 등록합니다.
  2. 운영용 API Key와 HMAC Secret을 발급받아 서로 다른 비밀값으로 보관합니다.
  3. BTSCON 관리자가 해당 계정에 판매할 상품을 등록하고 활성화합니다.
  4. GET /ping과 GET /catalog로 연결 및 상품 노출을 확인합니다.

테스트 키의 상품 목록에는 BTSCON 관리자가 테스트용으로 등록하고 활성화한 상품만 표시됩니다. 운영과 테스트 모두 같은 /api/reseller/* 경로를 사용합니다. 구형 내부 경로 /api/test/reseller/*는 신규 연동에 사용하지 않습니다.

API Key와 HMAC Secret은 브라우저·앱·공개 저장소에 넣지 말고 서버 환경변수 또는 비밀 저장소에서만 사용하십시오. 요청 로그에는 인증 헤더, 휴대폰, 쿠폰번호와 PIN을 마스킹해야 합니다.

2. 인증과 서명

x-api-key: {API_KEY}
x-bts-ts: {UTC Unix seconds}
x-bts-nonce: {16~128자 요청 고유값}
x-bts-sign: {HMAC-SHA256 lowercase hex}
Content-Type: application/json

HMAC 원문은 아래 6개 값을 LF(\n)로 연결하며 마지막 LF는 붙이지 않습니다.

{timestamp}
{nonce}
{UPPERCASE_METHOD}
{request_path}
{raw_query_string}
{sha256_hex(raw_body_bytes)}

JSON은 직렬화한 바이트를 먼저 확정한 뒤 그 바이트를 서명하고 그대로 전송해야 합니다. Timestamp 허용 오차는 ±300초이며 nonce는 재사용할 수 없습니다.

본문은 최대 64KiB입니다. 인증값은 예시·화면·오류 메시지에 출력하지 말고 유출이 의심되면 즉시 폐기 후 재발급해야 합니다.

3. 공용 경로

메서드경로용도
GET/api/reseller/ping인증 및 연결 확인
GET/api/reseller/catalog?limit=100&offset=0이 계정에 활성화된 상품 목록
GET/api/reseller/products/{goodsId}상품 상세 조회
POST/api/reseller/orders쿠폰 발행 및 구매자 문자 발송 접수
GET/api/reseller/orders/{orderId}쿠폰 발행 및 문자 발송 결과 조회
POST/api/reseller/orders/{orderId}/cancel발행 쿠폰 취소

4. 상품 조회

GET /catalog은 계정에 배정된 활성 상품만 반환합니다. limit은 1~200이며 offset은 0 이상입니다.

{
  "items": [
    {
      "goodsId": "BTS-T-7F4A9C2E81D643B5A0C7E219",
      "goodsName": "모바일쿠폰 1만원권",
      "brandName": "테스트 브랜드",
      "brandImageUrl": "https://api.btscon.co.kr/public/reseller/products/BTS-T-7F4A9C2E81D643B5A0C7E219/brand-image",
      "categoryName": "모바일쿠폰",
      "description": "테스트 주문용 상품입니다.",
      "imageUrl": "https://api.btscon.co.kr/public/reseller/products/BTS-T-7F4A9C2E81D643B5A0C7E219/image",
      "validDate": "20271231",
      "validDays": "365",
      "cancelable": true,
      "refundable": true,
      "extendable": false,
      "sellPrice": 9000,
      "consumerPrice": 10000,
      "sellEndDate": null,
      "status": "active"
    }
  ],
  "limit": 100,
  "offset": 0
}

GET /products/{goodsId}는 같은 상품 형식을 item에 담아 반환합니다. 상품·브랜드 이미지는 불투명 BTSCON 상품 코드로 만든 imageUrl·brandImageUrl 프록시 주소만 제공되며 저장 경로, 외부 원본 주소, 내부 상품·브랜드 코드는 응답에 포함되지 않습니다. 운영 키와 테스트 키는 같은 필드 구조를 사용합니다. 테스트 상품의 원본 이미지가 없거나 만료된 경우에는 실제 쿠폰으로 오인하지 않도록 표시된 BTSCON 샘플 이미지를 반환합니다.

5. 쿠폰 발행 및 구매자 문자 발송

POST /api/reseller/orders를 호출하면 BTSCON이 쿠폰을 발행하고 receiver에 입력한 구매자 휴대폰 번호로 모바일쿠폰 문자를 발송합니다. 호출 업체가 별도로 문자를 발송할 필요는 없습니다.

orderId는 호출 업체 내부에서 영구적으로 유일한 주문번호를 사용하십시오. 응답이 유실되면 새 주문번호로 재발행하지 말고 같은 번호를 조회합니다.

필드의미규칙
orderId호출 업체 주문번호주문마다 영구적으로 유일
goodsId발행할 상품 코드상품 조회 응답의 goodsId 사용
receiver쿠폰 문자를 받을 구매자 휴대폰 번호숫자 10~11자리
sender사전 승인된 발신번호숫자 8~16자리
title문자 제목1~100자
msg구매자에게 보낼 문자 내용1~2,000자

요청

POST /api/reseller/orders

{
  "orderId": "SHOP-ORDER-20260909-0001",
  "goodsId": "BTS-T-7F4A9C2E81D643B5A0C7E219",
  "receiver": "01012345678",
  "sender": "0212345678",
  "title": "모바일쿠폰",
  "msg": "구매하신 모바일쿠폰입니다."
}

접수 응답

{
  "orderId": "SHOP-ORDER-20260909-0001",
  "clientOrderId": "SHOP-ORDER-20260909-0001",
  "status": "queued"
}

queued는 쿠폰 발행과 문자 발송 작업이 접수된 상태이며 최종 성공을 뜻하지 않습니다. GET /api/reseller/orders/{orderId}로 최종 상태를 확인하십시오.

운영 주문은 발급 전에 공급대금을 확보합니다. 잔액 부족은 409 insufficient_balance, 같은 주문번호의 내용 변경은 409 idempotency_conflict로 거부합니다. 같은 내용의 재요청은 기존 주문을 반환하며 실패·취소된 번호도 새 발급에 재사용할 수 없습니다. 개별 sandbox 키의 실제 발급은 차단하며 공용 테스트 키는 테스트 전용 주문만 처리합니다.

테스트 키로 주문하면 형식과 상태 전이만 검증하며 구매자 휴대폰으로 실제 문자를 발송하지 않습니다.

6. 주문 조회

주문 조회의 expiresAt은 발급된 쿠폰별 실제 사용 만료시각(ISO 8601, +09:00), expiresOn은 한국 기준 만료일입니다. expiryTimezone=Asia/Seoul, expiryInclusive=true이므로 해당 시각까지 사용 가능합니다. expiryBasis=coupon_end_datetime은 쿠폰 만료시각 원본, coupon_end_date는 쿠폰 만료일 원본의 당일 23:59:59 기준입니다. 기존 발급 쿠폰도 같은 주문번호로 조회할 수 있습니다. 쿠폰별 만료정보가 없으면 날짜는 null, 기준은 unavailable이며 상품 공통 유효일수로 추정하지 않습니다. 만료일은 사용·취소 여부와 별개이므로 주문 상태도 확인하세요. 테스트 주문에는 실제 만료일이 없습니다.

2026-10-24 추가: 기존 주문번호로 다시 조회하여 고객 쿠폰 상세·이미지의 사용기한을 보완합니다. 날짜가 null이면 ‘사용기한 확인 중’으로 표시합니다. 기존 API 경로와 인증 방식은 같습니다.

추가 응답 필드형식·의미
expiresAtISO 8601 만료시각 또는 null. 예: 2026-12-23T23:59:59+09:00
expiresOn한국시간 기준 YYYY-MM-DD 만료일 또는 null
expiryTimezoneAsia/Seoul
expiryInclusivetrue: 명시된 만료시각까지 포함
expiryBasiscoupon_end_datetime: 원본 만료시각 / coupon_end_date: 원본 만료일의 23:59:59 / unavailable: 확인 불가
GET /api/reseller/orders/SHOP-ORDER-20260909-0001

{
  "orderId": "SHOP-ORDER-20260909-0001",
  "goodsId": "BTS-T-7F4A9C2E81D643B5A0C7E219",
  "status": "sent",
  "tradeType": "ISSUE",
  "tradeCode": "02",
  "tradeDate": "20260909153000",
  "sellPrice": 9000,
  "expiresAt": "2026-12-23T23:59:59+09:00",
  "expiresOn": "2026-12-23",
  "expiryTimezone": "Asia/Seoul",
  "expiryInclusive": true,
  "expiryBasis": "coupon_end_datetime"
}

sent는 쿠폰 발행 및 구매자 문자 발송 처리가 성공한 상태입니다. HTTP 오류나 응답 유실 시에도 먼저 이 경로로 기존 주문 상태를 확인해야 합니다.

쿠폰번호, PIN, 바코드는 receiver의 구매자 문자로만 전달하며 공용 API 응답에는 포함하지 않습니다.

7. 쿠폰 취소

취소 가능 상품에 한해 호출합니다. 같은 cancelRequestId로 재시도할 수 있으며, 실제 취소 확인 후에만 정산이 복원됩니다.

POST /api/reseller/orders/SHOP-ORDER-20260909-0001/cancel

{
  "cancelRequestId": "SHOP-CANCEL-20260909-0001",
  "reason": "고객 주문 취소"
}
{
  "orderId": "SHOP-ORDER-20260909-0001",
  "status": "cancelled",
  "tradeCode": "03",
  "changed": true,
  "cancelRequestId": "SHOP-CANCEL-20260909-0001"
}

8. 재시도와 오류

상태의미
requested주문 생성
queued쿠폰 발행 및 문자 발송 작업 접수
issued쿠폰 발행 완료, 문자 발송 준비 중
delivery_pending문자 발송 요청 완료, 결과 확인 중
sent쿠폰 발행 및 문자 발송 처리 성공
delivery_failed쿠폰은 발행됐으나 문자 발송 실패
failed쿠폰 발행 실패
issuance_unknown발행 여부 미확정. 기존 주문으로 상태 확인·대사하며 자동 재발급·환불을 하지 않습니다.
cancelled쿠폰 취소 완료

408, 429, 500, 502, 503, 504는 같은 주문번호와 요청 내용으로 지수 백오프 재시도합니다. 400, 401, 403, 409는 요청 또는 운영 설정을 확인한 뒤 처리합니다.

{
  "error": "error_code"
}

대표 오류: missing_api_key, invalid_api_key, ip_not_allowed, invalid_hmac_signature, hmac_nonce_reused, product_not_found, order_not_found, duplicate_order_id, cancel_in_progress.