sayren Docs

상품·옵션·재고 관리

상품 등록, 옵션 조합별 재고, 판매 상태와 즉시할인

옵션이 있는 상품을 등록하고 조합별 재고·판매 상태·즉시할인을 관리합니다.

준비

  • 권한: 소유자·관리자·스태프 또는 product:rw
  • 카테고리: 상점 관리 › 상품 › 카테고리에서 먼저 만듭니다.

1. 상품 등록

상점 플랫폼에서 상점 관리 › 상품 › 상품 등록으로 이동합니다(소유자·관리자·스태프).

  1. 카테고리를 고릅니다. 하위 카테고리가 없는 카테고리만 됩니다.
  2. 아래 섹션을 채웁니다. 배송이 필요 없는 상품은 배송 정책에서 배송 없음을 고릅니다. 8절
  3. **[등록]**을 누릅니다. 수정 화면에서는 **[저장]**입니다.
섹션입력
상품명100자까지
이미지10장까지. 첫 장이 대표입니다. 상품 이미지
판매 정보판매가(최소 10원, 10원 단위), 정가(할인 전 가격 표시용), 재고
상세 설명**[상세설명 작성]**을 누르면 새 창에 편집기가 열립니다
옵션옵션 축과 값, 조합별 재고. 옵션이 없으면 비워 둡니다
배송 정책배송 있음/없음. 배송 있음이면 배송비·출고지·반품지. 7절
환불 정책비우면 상점 기본을 씁니다. 6절

완료 기준: 상점 관리 › 상품 › 상품 목록과 스토어프론트 목록에 상품이 판매중으로 보입니다.

API: POST /v1/products

2. 옵션과 조합

옵션은 축(예: 색상)과 값(예: 블랙)입니다. 모든 조합(예: 블랙 / M)이 자동으로 만들어집니다.

축은 3개, 축당 값은 100개, 조합은 500개까지입니다.

조합 항목설명
판매끄면 구매자 화면에 품절로 보이고 주문할 수 없습니다
추가금액판매가에 더하는 금액(원)
재고판매 가능 수량
SKU 정보바코드·판매자 코드·원가·무게. 내부 관리용 선택 입력
  • [값 일괄 추가]: 값을 쉼표로 이어 입력합니다(예: 블랙, 화이트, 레드).
  • [재고 일괄 적용]: 모든 조합의 재고를 같은 수로 덮어씁니다.
  • 주문에 담긴 조합은 지울 수 없습니다. 삭제 대신 판매를 끕니다.

추가 선택(재고 없이 값별 추가금만 있는 옵션)·직접 입력·정렬 순서·저장 옵션은 상품 옵션 구성을 봅니다.

API: 옵션 구조 전체 교체는 PUT /v1/products/{productId}/options입니다. 스토어프론트·주문의 optionId는 조합의 variantId입니다.

3. 재고 변경

재고는 조합 단위입니다. 상품 수정 화면의 옵션 섹션에서 바꾸거나 API로 한 조합씩 바꿉니다.

curl -X PATCH https://api.sayren.app/v1/products/{productId}/variants/{variantId}/stock \
  -H "Authorization: Bearer {계정 토큰}" -H "X-Store-Id: {storeId}" -H "Content-Type: application/json" \
  -d '{ "stockQuantity": 30 }'   # 절대값. 증감은 { "adjustment": 20 }
  • stockQuantity와 adjustment 중 하나만 보냅니다.
  • 결제되면 줄고, 취소·반품이 끝나면 늘어납니다.
  • 0이 되면 품절로 표시되고 웹훅 PRODUCT.OUT_OF_STOCK이 나갑니다.

날짜별 재고(숙박)

객실처럼 날마다 재고와 가격이 다른 상품은 등록할 때 inventoryMode: "DATED"로 만듭니다. 이 상품은 조합 재고를 쓰지 않고 조합 × 날짜 달력이 재고·가격입니다. 판매 이력이 생기면 재고 방식을 바꿀 수 없습니다(409 INVENTORY_MODE_LOCKED).

PUT /v1/products/{productId}/calendar
{ "variantIds": ["var_…"], "from": "2026-11-01", "to": "2026-11-30", "stock": 3, "price": 120000, "daysOfWeek": [5, 6] }

GET /v1/products/{productId}/calendar?from=2026-11-01&to=2026-11-30
  • 날짜는 한국 날짜(YYYY-MM-DD)이고 기간은 끝 날짜를 포함합니다. 한 번에 최대 400일입니다.
  • stock·price는 보낸 것만 바뀝니다. price: null이면 그날 가격을 판매가(조합 추가금 포함)로 되돌립니다. daysOfWeek(0=일요일~6=토요일)를 주면 그 요일만 바뀝니다.
  • 달력에 없는 날은 재고 0이라 판매하지 않습니다.
  • 구매자는 GET /storefront/v1/products/{productId}/calendar?from=&to=로 날마다 예약 가능 여부와 가격을 봅니다(최대 120일, 재고 수는 보이지 않습니다).
  • 장바구니 담기와 바로 구매에 stay: { "checkIn", "checkOut" }를 함께 보냅니다. 체크인 날부터 체크아웃 전날까지가 박이고(최대 30박) 단가는 박마다 가격의 합입니다. 결제하면 박마다 재고가 줄고 취소하면 그 날들이 돌아옵니다.
  • 기간이 빠지면 400 STAY_REQUIRED, 일반 상품에 기간을 주면 400 STAY_NOT_APPLICABLE, 재고가 없는 날이 끼면 409 DATES_UNAVAILABLE입니다.
  • 주문상품 응답의 stay에 체크인·체크아웃과 박마다 주문 시점 가격(nights)이 남습니다.

4. 판매 상태

상태구매자 화면
판매중보이고 주문할 수 있음
품절 · 판매중지 · 판매종료목록과 상세에 보이지 않음

상점 플랫폼에서 상점 관리 › 상품 › 상품 목록으로 이동합니다(소유자·관리자·스태프).

  1. 상품을 체크합니다.
  2. 상태를 고르고 **[선택 N개 상태 변경]**을 누릅니다.

API: POST /v1/products/bulk/status (한 번에 100건)

5. 즉시할인

상점 플랫폼에서 상점 관리 › 상품 › 상품 목록으로 이동합니다(소유자·관리자·스태프).

  1. 목록에서 상품을 선택합니다.
  2. 즉시할인 섹션에서 할인 방식(정률 % · 정액 원)과 할인 값, 시작일·종료일을 정합니다.
  3. **[할인 저장]**을 누릅니다. 상품 저장과 따로 저장됩니다. 없애려면 **[할인 삭제]**입니다.

스토어프론트에 할인가와 할인율이 표시됩니다. API: PUT /v1/products/{productId}/discount

할인의 기준은 판매가입니다. 정가는 비교용 표시 값이라 할인 계산과 결제 금액에 쓰지 않습니다. 구매자가 내는 가격은 할인 기간에는 할인가, 그 밖에는 판매가입니다. 스토어프론트 상품 응답의 originalPrice가 정가이고, 기본 템플릿은 정가가 구매자 가격보다 클 때 정가 취소선과 정가 기준 할인율을 표시합니다. 가격 표시

설정된 할인은 상품 상세(GET /v1/products/{productId})의 discount에도 실립니다. 형태는 GET /v1/products/{productId}/discount의 응답과 같고, 할인이 없으면 null입니다. 할인 조회는 할인이 없을 때 404 DISCOUNT_NOT_FOUND를 돌려주므로, 할인 유무를 확인할 때는 상품 상세를 쓰십시오.

6. 환불 정책 덮어쓰기

상품의 환불 정책 섹션이나 요청의 refundPolicy로 상점 기본 정책을 상품별로 덮어씁니다. 준 항목만 덮어쓰고, 생략하거나 null이면 상점 기본을 씁니다. 항목의 뜻과 계산은 환불 정책과 배송비에 있습니다.

{ "refundPolicy": { "returnDeliveryFee": 5000, "exchangeDeliveryFee": 5000, "preShipmentDeliveryFeeRefund": false } }

PUT /v1/products/{productId}에서 refundPolicy를 생략하면 저장된 덮어쓰기가 지워집니다(전체 교체). 유지하려면 조회 응답의 refundPolicy를 다시 보냅니다.

  • 상품의 반품·교환 배송비는 refundPolicy의 returnDeliveryFee·exchangeDeliveryFee로만 정합니다. 옛 필드 returnFee·exchangeFee는 없어졌고 보내면 400입니다.
  • 배송 없는 상품(MANUAL)에 returnDeliveryFee·exchangeDeliveryFee를 주면 400 REFUND_POLICY_NOT_APPLICABLE입니다. PATCH로 배송 상품을 배송 없는 상품으로 바꾸면서 refundPolicy를 생략하면 저장된 두 항목을 지웁니다(PUT에서 생략하면 덮어쓰기 전체가 지워집니다).
  • 주문의 환불은 결제 시점에 굳힌 정책으로 계산합니다. 상품의 정책을 나중에 바꿔도 이미 들어온 주문의 환불 금액은 바뀌지 않습니다.
  • 조회 응답의 refundPolicy는 덮어쓴 항목만 담고, 없으면 null입니다. 모르는 항목 이름은 400입니다.
  • 한 주문에 정책이 다른 상품이 섞이면 상품별로 계산합니다.

7. 배송비와 출고지·반품지

배송 상품은 배송 정책 섹션에서 배송비와 출고지·반품지를 정합니다. 배송비를 비우면 그 시점의 상점 기본 배송비가 저장됩니다. API에서는 fulfillment.shipping입니다(8절). 같은 출고지 + 같은 배송 정책인 상품은 배송비가 한 번 붙습니다. 필드 규칙은 상품의 배송 정책에 있습니다.

8. 상품 유형

상품의 유형과 유형별 속성은 fulfillment 하나에 있습니다. type이 유형이고 같은 이름의 키가 그 유형의 속성입니다. 유형은 배송 상품(SHIPPING)과 배송 없는 상품(MANUAL) 둘입니다(비실물 상품).

type상점 플랫폼 배송 정책속성 키
SHIPPING배송 있음shipping: deliveryType · deliveryFee · conditionalFreeAmount · shippingAddressId · returnAddressId
MANUAL배송 없음없음. { "type": "MANUAL" }만 보냅니다
{
  "fulfillment": {
    "type": "SHIPPING",
    "shipping": { "deliveryType": "PAID", "deliveryFee": 3000, "conditionalFreeAmount": null,
                  "shippingAddressId": null, "returnAddressId": null }
  },
  "fulfillmentTypeLocked": false
}

등록과 수정

  • 등록(POST /v1/products): fulfillment를 생략하면 배송 상품입니다. 유형 안의 필드도 생략할 수 있고 기본값을 씁니다(예: { "type": "SHIPPING" }은 유료 배송). 배송비를 생략하면 상점 기본 배송비, 주소를 생략하면 그 시점 상점 기본 주소를 저장합니다.
  • 수정(PUT·PATCH /v1/products/{productId}): fulfillment를 생략하면 바꾸지 않습니다. 주면 통째로 바꾸므로 유형 안의 필드를 모두 보내야 합니다. 현재 값과 합치지 않습니다. 조회 응답의 fulfillment를 고쳐 보내면 됩니다.
  • 주소의 null은 비워 두고 상점 기본 주소를 따른다는 뜻입니다.
  • 요청 최상위는 정한 필드만 받습니다. deliveryFee·fulfillmentType·returnFee 같은 옛 평면 필드를 보내면 조용히 무시하지 않고 400입니다.
  • 응답의 fulfillmentTypeLocked가 true면 판매 이력이 있어 유형을 바꿀 수 없습니다(409 FULFILLMENT_TYPE_LOCKED). 같은 유형 안의 속성은 바꿀 수 있습니다.
  • 다른 요청이 먼저 유형을 바꿨으면 409 PRODUCT_CHANGED입니다. 상품을 다시 조회한 뒤 보냅니다.
  • 응답의 type은 값이 늘어날 수 있습니다. SDK는 모르는 유형을 { "type": "OTHER", "rawType": "…" }로 읽습니다.

상점 플랫폼에서는 배송 정책 섹션 맨 위의 배송 있음/없음으로 유형을 고릅니다. 배송 없음으로 바꾸면 배송비·출고지·반품지 입력이 사라지므로 입력한 값이 있으면 확인을 받습니다.

자주 막히는 문제

증상해결
400 INVALID_PRICE_UNIT판매가를 10원 단위로 입력합니다
CATEGORY_NOT_LEAF하위 카테고리가 없는 가장 아래 카테고리를 고릅니다
조합을 지울 수 없다는 오류주문에 담긴 조합입니다. 판매를 끕니다
스토어프론트에 상품이 안 보임상태가 판매중인지 확인합니다
상품 수정이 400(옛 필드)배송비·유형 필드를 fulfillment 안으로 옮깁니다. 8절
409 DATES_UNAVAILABLE고른 기간에 달력 재고가 없는 날이 있습니다. 달력을 확인합니다
409 FULFILLMENT_TYPE_LOCKED판매 이력이 있는 상품입니다. 배송 있음/없음을 바꾸려면 새 상품으로 등록합니다

다음 단계

이 페이지 목차