Orders API

주문 생성 및 주문 관리 API 문서입니다.

모든 Orders API는 인증이 필요합니다. API Key를 Authorization: Bearer <API_KEY> 헤더로 제공하세요.

주문 상태

주문 상태는 문자열 enum(orderStatus)과 한글 표시 문자열(orderStatusDisplay)로 제공됩니다. 클라이언트 분기는 orderStatus로, UI 표시는 orderStatusDisplay로 하세요. 항목 단위 상태도 동일 규칙(itemStatus·itemStatusDisplay)이 적용됩니다.

Scope 컬럼은 해당 상태가 주문 전체(Order)·항목(Item)·양쪽(Order·Item) 중 어디에 나타나는지 표시합니다.

orderStatus (문자열)orderStatusDisplay (한글)Scope의미
PAID결제완료Order·Item충전금 차감 완료 (초기 안정 상태)
PDF_READYPDF준비완료Item출력용 PDF 생성 완료
CONFIRMED제작확정Order·Item제작건 확정 (출력일 배정)
IN_PRODUCTION제작중Order·Item인쇄·제본·포장 진행 중
COMPLETED제작완료Item항목 제작 완료 (개별)
PRODUCTION_COMPLETE전체제작완료Order모든 항목 제작 완료 (발송 대기)
SHIPPED발송완료Order·Item택배 발송됨
DELIVERED배송완료Order고객 수령 확인
CANCELLED취소Order·Item취소 (환불 없음)
CANCELLED_REFUND취소환불Order취소 + 충전금 환불 (부분 취소 귀결 포함)
ERROR오류Order·Item오류 상태

상태 전이 규칙

주문 상태는 정해진 흐름에 따라 전이됩니다. 진행 주체 컬럼은 누가 전이를 트리거하는지 표기합니다.

  • 파트너 — 파트너가 직접 엔드포인트 호출
  • 자동 — 시스템이 조건 충족 시 즉시 전이 (별도 호출·웹훅 없음)
  • 제작 처리 — SweetBook 측 제작·발송 단계 진행. 파트너는 웹훅 이벤트로 상태 변화를 수신
FROMTO진행 주체파트너 엔드포인트웹훅 이벤트
(신규)PAID파트너POST /ordersorder.created
PAIDPDF_READY자동
PDF_READYCONFIRMED제작 처리production.confirmed
CONFIRMEDIN_PRODUCTION제작 처리production.started
IN_PRODUCTIONCOMPLETED (항목)제작 처리— (항목 단위, 별도 이벤트 없음)
COMPLETED (모든 항목)PRODUCTION_COMPLETE자동 집계production.completed
PRODUCTION_COMPLETESHIPPED제작 처리shipping.departed
SHIPPEDDELIVERED제작 처리shipping.delivered
PAID / PDF_READYCANCELLED_REFUND파트너POST /orders/{uid}/cancel (전체)
POST /orders/{uid}/items/{uid}/cancel (부분)
order.cancelled
order.item_cancelled

파트너 가능 액션

액션가능 상태설명
주문 전체 취소PAID, PDF_READY주문 전체 취소, 결제 금액 전액(배송비/포장비 포함) 환불
주문 항목 부분 취소PAID, PDF_READY항목 단위로 취소, 남은 항목으로 재계산 → 차액 환불. 마지막 항목까지 취소되면 전체 취소로 자동 귀결
배송지 변경PAID, PDF_READY, CONFIRMED발송 전까지만 배송지 변경 가능

주문 목록 조회

GET/orders

파트너의 주문 목록을 조회합니다. 기간 및 상태 필터를 조합하여 사용할 수 있습니다.

Query 파라미터

파라미터타입필수설명
limitint-반환할 항목 수 (기본 20, 최대 100)
offsetint-건너뛸 항목 수 (기본 0)
statusstring | int-주문 상태로 필터링. 문자열 enum(예: PAID, CANCELLED_REFUND, 대소문자 무관) 또는 숫자 코드(예: 20) 모두 수용
fromstring-조회 시작일 (ISO 8601, 예: 2026-01-01T00:00:00Z)
tostring-조회 종료일 (ISO 8601, 예: 2026-12-31T23:59:59Z)

Request 예시

Request
curl -X GET 'https://api-sandbox.sweetbook.com/v1/orders?limit=20&offset=0&status=PAID&from=2026-01-01T00:00:00Z&to=2026-12-31T23:59:59Z' \
  -H 'Authorization: Bearer {YOUR_API_KEY}'

Response 예시

Response (v1.2 평탄화 구조)
{
  "success": true,
  "message": "성공",
  "data": [
    {
      "orderUid": "or_3eAx********",
      "accountUid": "u_6b0f********************",
      "accountName": "샘플출판사",
      "accountOrganizationName": null,
      "orderType": "NORMAL",
      "externalRef": "PARTNER-ORDER-001",
      "orderStatus": "PAID",
      "orderStatusDisplay": "결제완료",
      "totalAmount": 5000.00,
      "paidCreditAmount": 5500.00,
      "paymentMethod": "CREDIT",
      "itemCount": 1,
      "recipientName": "김영수",
      "isTest": true,
      "orderedAt": "2026-02-19T01:10:47.000Z",
      "createdAt": "2026-02-19T01:10:47.000Z"
    }
  ],
  "pagination": {
    "total": 42,
    "limit": 20,
    "offset": 0,
    "hasNext": true
  }
}

HTTP 상태 코드

코드errorCode설명
200 OK조회 성공
400 Bad RequestERR_VALIDATION_FAILEDstatus 파라미터가 허용되지 않는 값
401 UnauthorizedERR_UNAUTHORIZED인증 실패
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류

주문 상세 조회

GET/orders/{orderUid}

특정 주문의 상세 정보를 조회합니다. 주문 항목, 배송 정보, 현재 상태가 모두 포함됩니다.

Path 파라미터

파라미터타입필수설명
orderUidstringO조회할 주문 UID

Request 예시

Request
curl -X GET 'https://api-sandbox.sweetbook.com/v1/orders/or_3eAx********' \
  -H 'Authorization: Bearer {YOUR_API_KEY}'

Response 예시

Response
{
  "success": true,
  "message": "성공",
  "data": {
    "orderUid": "or_3eAx********",
    "orderType": "NORMAL",
    "orderStatus": "PAID",
    "orderStatusDisplay": "결제완료",
    "externalRef": "PARTNER-ORDER-001",
    "totalProductAmount": 60400.00,
    "totalShippingFee": 3000.00,
    "totalPackagingFee": 0.00,
    "totalAmount": 63400.00,
    "paidCreditAmount": 69740.00,
    "paymentMethod": "CREDIT",
    "recipientName": "김영수",
    "recipientPhone": "010-0000-0000",
    "postalCode": "06101",
    "address1": "서울시 강남구 테헤란로 123",
    "address2": "4층 401호",
    "orderedAt": "2026-02-19T01:10:47.000Z",
    "items": [
      {
        "itemUid": "oi_aB3c********",
        "bookUid": "bk_abc123",
        "bookTitle": "우리 아이 성장앨범",
        "quantity": 1,
        "unitPrice": 60400.00,
        "itemAmount": 60400.00,
        "itemStatus": "PAID",
        "itemStatusDisplay": "결제완료"
      }
    ]
  }
}

HTTP 상태 코드

코드errorCode설명
200 OK조회 성공
401 UnauthorizedERR_UNAUTHORIZED인증 실패
404 Not FoundERR_NOT_FOUND주문을 찾을 수 없음
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류

주문 생성

POST/orders

FINALIZED 상태의 책을 대상으로 주문을 생성합니다. 충전금이 즉시 차감됩니다.

충전금 이중 차감 방지를 위해 Idempotency-Key 헤더를 반드시 포함하세요. 동일한 키로 재시도하면 이전 응답이 그대로 반환됩니다. 자세한 내용은 API 공통 사항 > 멱등성을 참고하세요.

Request Body

필드타입필수설명
itemsarrayO주문 항목 목록 (최소 1개)
items[].bookUidstringO책 UID (FINALIZED 상태여야 함)
items[].quantityintO수량 (1~100)
shippingobjectO배송지 정보
shipping.recipientNamestringO수령인 (최대 100자)
shipping.recipientPhonestringO연락처 (최대 20자)
shipping.postalCodestringO우편번호 (최대 10자)
shipping.address1stringO주소 (최대 200자)
shipping.address2string-상세주소 (최대 200자)
shipping.memostring-배송 메모 (최대 200자)
externalRefstring-파트너 외부 참조 식별자 (최대 100자)

처리 로직

  1. 각 bookUid 유효성 검증 (존재, 파트너 소유, FINALIZED 상태)
  2. 각 책의 bookSpecUid로 가격 계산
  3. 배송비(3,000원/주문) + 상품금액 합산
  4. 충전금 잔액 확인 후 차감
  5. 주문 생성

Request 예시

Request headers
Authorization: Bearer {YOUR_API_KEY}
Content-Type: application/json
Idempotency-Key: unique-request-id-12345
Request body
{
  "items": [
    { "bookUid": "bk_abc123", "quantity": 1 },
    { "bookUid": "bk_def456", "quantity": 2 }
  ],
  "shipping": {
    "recipientName": "김영수",
    "recipientPhone": "010-0000-0000",
    "postalCode": "06101",
    "address1": "서울시 강남구 테헤란로 123",
    "address2": "4층 401호",
    "memo": "부재시 경비실"
  },
  "externalRef": "PARTNER-ORDER-001"
}

Response 예시

Response
{
  "success": true,
  "message": "주문이 생성되었습니다",
  "data": {
    "orderUid": "or_3eAx********",
    "orderType": "NORMAL",
    "orderStatus": "PAID",
    "orderStatusDisplay": "결제완료",
    "isTest": true,
    "totalProductAmount": 60400.00,
    "totalShippingFee": 3000.00,
    "totalPackagingFee": 0.00,
    "totalAmount": 63400.00,
    "paidCreditAmount": 69740.00,
    "paymentMethod": "CREDIT",
    "creditBalanceAfter": 930260.00,
    "recipientName": "김영수",
    "recipientPhone": "010-0000-0000",
    "postalCode": "06101",
    "address1": "서울시 강남구 테헤란로 123",
    "address2": "4층 401호",
    "shippingMemo": "부재시 경비실",
    "orderedAt": "2026-02-19T01:10:47.000Z",
    "paidAt": "2026-02-19T01:10:47.000Z",
    "items": [
      {
        "itemUid": "oi_aB3c********",
        "bookUid": "bk_abc123",
        "bookTitle": "우리 아이 성장앨범",
        "bookSpecUid": "bs_spec001",
        "bookSpecName": "포토북 A4",
        "quantity": 1,
        "pageCount": 24,
        "unitPrice": 60400.00,
        "itemAmount": 60400.00,
        "itemStatus": "PAID",
        "itemStatusDisplay": "결제완료"
      }
    ]
  }
}

HTTP 상태 코드

코드errorCode설명
201 Created주문 생성 성공
400 Bad RequestERR_VALIDATION_FAILED유효성 검증 실패 (Book 미존재, 미FINALIZED 등)
401 UnauthorizedERR_UNAUTHORIZED인증 실패
402 Payment RequiredERR_INSUFFICIENT_CREDIT충전금 잔액 부족
422 Unprocessable EntityERR_IDEMPOTENCY_KEY_MISMATCH동일 Idempotency-Key로 다른 본문 재요청
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류

402 충전금 부족: ERR_INSUFFICIENT_CREDIT가 반환되며 data에 진단 객체(required·balance·currency)가 포함됩니다. 응답 6필드 shape, 다른 errorCode, 처리 가이드는 에러 코드 & 트러블슈팅 — ERR_INSUFFICIENT_CREDIT을 참조하세요.

주문 전체 취소

POST/orders/{orderUid}/cancel

PAID 또는 PDF_READY 상태의 주문 전체를 취소합니다. 결제된 충전금은 배송비/포장비 포함 전액 환불됩니다.

취소 조건

  • PAID 또는 PDF_READY 상태이고 NORMAL 유형의 주문만 취소 가능합니다
  • 제작이 시작된 주문(CONFIRMED 이후)은 취소할 수 없습니다
  • 취소 시 결제 금액이 충전금으로 전액 환불됩니다

Request Body

필드타입필수설명
cancelReasonstringO취소 사유 (최대 500자)

Request 예시

Request body
{
  "cancelReason": "고객 변심"
}

HTTP 상태 코드

코드errorCode설명
200 OK전체 취소 성공 (변경된 주문 상세 정보 반환, orderStatus: "CANCELLED_REFUND")
400 Bad RequestERR_VALIDATION_FAILEDcancelReason 누락, 제작확정 이후 상태(PAID/PDF_READY가 아님), 이미 취소된 주문 등
401 UnauthorizedERR_UNAUTHORIZED인증 실패
404 Not FoundERR_NOT_FOUND주문 없음
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류

일부 항목만 취소하려면 아래 주문 항목 부분 취소 API를 사용하세요.

주문 항목 부분 취소

POST/orders/{orderUid}/items/{itemUid}/cancel

주문에 포함된 특정 항목을 단일 단위로 취소합니다. 한 번 호출에 한 항목씩 처리하며, 여러 항목을 취소하려면 반복 호출합니다.

동작

  • 남은 항목으로 가격을 재계산하여 차액을 충전금으로 환불합니다
  • 부분 취소는 주문 상태(orderStatus)를 변경하지 않습니다 — 남은 항목은 제작확정 등 다음 단계로 정상 진행됩니다
  • 취소된 항목의 itemStatusCANCELLED_REFUND로 전이됩니다
  • 마지막 항목까지 취소되면 주문 전체가 자동으로 CANCELLED_REFUND로 전이되며(fullCancel: true), 배송비/포장비 포함 잔액이 환불됩니다

취소 조건

  • 주문이 NORMAL 유형이고 PAID 또는 PDF_READY 상태여야 합니다
  • 대상 항목(itemStatus)도 PAID 또는 PDF_READY여야 하며, 이미 취소된 항목은 재취소할 수 없습니다
  • 제작이 시작된 주문(CONFIRMED 이후)은 항목 부분 취소도 불가합니다

여러 항목 취소 시 주의

여러 항목을 한 번에 취소하려면 항목별로 순차 호출하세요. Promise.all 같은 병렬 호출은 권장하지 않습니다 — 서버가 항목 상태와 주문 금액을 항목 단위로 직렬 재계산하므로, 병렬 호출은 락 경합·재계산 불일치를 유발할 수 있습니다. 네트워크 재시도 시에는 동일한 Idempotency-Key를 재사용해 중복 환불을 방지하세요.

Request Body

필드타입필수설명
cancelReasonstringO취소 사유 (최대 500자)

Request 예시

Request
curl -X POST 'https://api-sandbox.sweetbook.com/v1/orders/{orderUid}/items/{itemUid}/cancel' \
  -H 'Authorization: Bearer {YOUR_API_KEY}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cancel-item-001' \
  -d '{
  "cancelReason": "고객 요청 (1권 제외)"
}'

응답 필드

필드타입설명
orderUidstring대상 주문 UID
orderStatusstring처리 후 주문 상태 (문자열 enum). 부분 취소 시 기존 상태 유지(PAID / PDF_READY), 전체 취소 귀결 시 CANCELLED_REFUND
orderStatusDisplaystring주문 상태 한글 표시 문자열
fullCancelboolean이번 호출로 전체 취소로 귀결됐는지 여부 (마지막 항목 취소 시 true)
refundAmountnumber이번 호출로 환불된 금액 (원, decimal)
refundTransactionIdnumber이번 환불의 충전금 거래 ID. 환불액이 0이면 null
cancelledItemobject이번 호출로 취소된 항목 정보 (itemUid, bookUid, quantity, itemAmount)
remainingItemUidsstring[]남은(살아있는) 항목의 itemUid 목록
orderobject처리 후 주문 상세 (주문 상세 조회와 동일 스키마)

Response 예시

Response (200 OK) — 부분 취소 (남은 항목 존재)
{
  "success": true,
  "message": "선택한 항목이 부분 취소되었습니다",
  "data": {
    "orderUid": "or_2lnj********",
    "orderStatus": "PAID",
    "orderStatusDisplay": "결제완료",
    "fullCancel": false,
    "refundAmount": 110.00,
    "refundTransactionId": 784,
    "cancelledItem": {
      "itemUid": "oi_6bGM********",
      "bookUid": "bk_3lOO********",
      "quantity": 1,
      "itemAmount": 100.00
    },
    "remainingItemUids": ["oi_1Nrc********"],
    "order": {
      "orderUid": "or_2lnj********",
      "orderStatus": "PAID",
      "orderStatusDisplay": "결제완료",
      "totalAmount": 3110.00,
      "paidCreditAmount": 3420.00,
      "refundAmount": 110.00,
      "items": [
        { "itemUid": "oi_6bGM********", "itemStatus": "CANCELLED_REFUND", "itemStatusDisplay": "취소환불" },
        { "itemUid": "oi_1Nrc********", "itemStatus": "PAID", "itemStatusDisplay": "결제완료" }
      ]
    }
  }
}
Response (200 OK) — 마지막 항목 → 전체 취소 귀결
{
  "success": true,
  "message": "마지막 항목까지 취소되어 주문이 전체 취소되었습니다",
  "data": {
    "orderUid": "or_184U********",
    "orderStatus": "CANCELLED_REFUND",
    "orderStatusDisplay": "취소환불",
    "fullCancel": true,
    "refundAmount": 3410.00,
    "refundTransactionId": 782,
    "cancelledItem": {
      "itemUid": "oi_34Hu********",
      "bookUid": "bk_2IyK********",
      "quantity": 1,
      "itemAmount": 100.00
    },
    "remainingItemUids": [],
    "order": { "orderStatus": "CANCELLED_REFUND", "cancelledAt": "2026-04-22T01:21:38.000Z" }
  }
}

HTTP 상태 코드

코드errorCode설명
200 OK부분 취소 성공 (전체 취소 귀결 포함)
400 Bad RequestERR_VALIDATION_FAILEDcancelReason 누락, 이미 취소된 항목, 주문에 포함되지 않은 itemUid, 제작확정 이후 상태(PAID/PDF_READY가 아님) 등
401 UnauthorizedERR_UNAUTHORIZED인증 실패
404 Not FoundERR_NOT_FOUND주문 또는 항목 없음
422 Unprocessable EntityERR_IDEMPOTENCY_KEY_MISMATCH동일 Idempotency-Key로 다른 본문 재요청
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류

모든 실패 응답은 6필드 shape(success·errorCode·message·data·errors·fieldErrors)를 따릅니다. errors[0]에는 사용자 표시용 한글 메시지가 포함됩니다(예: 이미 취소된 항목입니다: {itemUid}, 주문에 포함되지 않은 항목입니다: {itemUid}, 제작확정 이전(PAID/PDF_READY) 상태의 주문만 부분 취소할 수 있습니다.). errorCode·응답 shape 상세는 에러 코드 & 트러블슈팅을 참고하세요.

부분 취소 — 자주 묻는 질문 (FAQ)

Q부분 취소 후 orderStatusPAID 그대로인 것이 맞나요?
A네, 의도된 동작입니다. 부분 취소는 "일부 항목 제거"이지 주문 자체의 상태 전이가 아닙니다. 남은 항목은 정상적으로 제작확정 등 다음 단계로 진행될 수 있도록 orderStatus는 그대로 유지되며, 취소 이력은 항목별 itemStatus === "CANCELLED_REFUND"로 확인합니다. 마지막 항목까지 취소되면 그 시점에 주문 전체가 CANCELLED_REFUND로 전이됩니다.
QcancelledItem.itemAmountrefundAmount가 왜 다른가요?
A두 값은 서로 다른 기준입니다. cancelledItem.itemAmount는 취소된 항목의 순수 상품 금액(unitPrice × quantity)으로, 주문 시점의 스냅샷 값입니다. refundAmount남은 항목으로 재계산한 차액으로, 포장비·배송비·부가세(10%) 차액까지 모두 반영된 이번 호출의 총 환불액입니다. 정산/회계 검증은 refundAmount를, 항목 단위 수치 비교는 itemAmount를 사용하세요.
Q환불 금액이 예상과 다를 수 있나요?
A환불액은 남은 항목을 기준으로 호출 시점의 현재 가격 정책으로 재계산합니다. 주문 생성 이후 해당 업체의 포장비·배송비 규칙이나 커스텀 가격이 변경되었다면 환불액이 달라질 수 있습니다. 정산 검증 시에는 응답의 refundAmountorder 객체에 반환된 갱신된 금액 필드를 함께 확인하세요. 가격 정책 변경으로 환불액이 음수가 되는 경우에는 400 Bad Request가 반환됩니다.
Q부분 취소 이력이 있는 주문인지 UI에서 어떻게 판단하나요?
A주문 상세 조회(GET /orders/{orderUid}) 응답의 items[]itemStatus === "CANCELLED_REFUND"인 항목이 하나라도 있고 동시에 orderStatus"PAID" 또는 "PDF_READY"라면 "부분 취소 이력 있음"으로 판단할 수 있습니다.
Q충전금 환불 이력은 어떻게 조회하나요?
AGET /credits/transactions로 조회할 때 reasonCode=13 (ORDER_MODIFY_REFUND)인 거래가 부분 취소 환불입니다. memo 필드에 해당 orderUid가 포함되어 있어 주문과 역추적할 수 있습니다.
Q같은 Idempotency-Key로 재호출하면 어떻게 되나요?
A첫 요청의 응답을 그대로 반환합니다(중복 환불 없음). 네트워크 타임아웃 등으로 재시도할 때는 반드시 동일한 키를 사용하세요. 키를 새로 생성해 재호출하면 서버는 별개 요청으로 처리합니다.
Q부분 취소 웹훅은 어떻게 받나요?
A매 호출마다 order.item_cancelled 이벤트가 발송됩니다. 마지막 항목을 취소해 전체 취소로 귀결되면 그 시점에 기존 전체 취소 이벤트 order.cancelled추가로 발송됩니다(구독자 호환). 두 이벤트를 함께 구독하는 것을 권장합니다.

배송지 변경

PATCH/orders/{orderUid}/shipping

PAID ~ CONFIRMED 상태에서 배송지를 변경할 수 있습니다. 발송(SHIPPED) 이후에는 변경이 불가합니다. 변경할 필드만 전달합니다.

Request Body

지원 필드: recipientName, recipientPhone, postalCode, address1, address2, shippingMemo (변경할 필드만 부분 전달)

필드타입필수설명
recipientNamestring수령인 (최대 100자)
recipientPhonestring연락처 (최대 20자)
postalCodestring우편번호 (최대 10자)
address1string주소 (최대 200자)
address2string상세주소 (최대 200자)
shippingMemostring배송 메모 (최대 200자)

Request 예시

Request body
{
  "recipientName": "김영수",
  "address1": "서울시 서초구 반포대로 100"
}

HTTP 상태 코드

코드errorCode설명
200 OK변경 성공
400 Bad RequestERR_VALIDATION_FAILED지원되지 않는 필드, 발송(SHIPPED) 이후 변경 시도 등
401 UnauthorizedERR_UNAUTHORIZED인증 실패
404 Not FoundERR_NOT_FOUND주문 없음
500 Internal Server ErrorERR_INTERNAL_ERROR서버 오류