sayren Docs

변경 이력

관리 API·스토어프론트 API·SDK의 공개 변경 사항

연동에 영향을 주는 변경을 날짜순(최신이 위)으로 정리합니다. 호환되지 않는 변경에는 호환성 변경을 표시합니다.

2026-10-13

서비스 주소 이전 (sayren.io, sayren 0.9.0 · @sayren/mcp 0.24.0 · @sayren/app 0.9.0)

호환성 변경이 있습니다. 서비스 도메인을 sayren.app에서 sayren.io로 옮깁니다.

  • 변경(호환성 변경): API 주소가 https://api.sayren.io입니다. 관리 API는 https://api.sayren.io/v1, 스토어프론트 API는 https://api.sayren.io/storefront/v1입니다.
  • 변경(호환성 변경): 계정 토큰의 발급자(iss)가 https://accounts.sayren.io, 대상(aud)이 https://api.sayren.io·https://mcp.sayren.io/mcp입니다. 옛 주소로 발급된 토큰은 받지 않으므로 다시 로그인하거나 액세스 토큰을 새로 발급하십시오.
  • 변경: 원격 MCP는 https://mcp.sayren.io/mcp, 문서는 https://docs.sayren.io, OpenAPI 문서는 https://api.sayren.io/openapi/store.json·https://api.sayren.io/openapi/storefront.json입니다.
  • 변경: sayren CLI·@sayren/mcp·@sayren/app의 기본 api 주소가 https://api.sayren.io입니다. .mcp.json 등 연결 설정에 옛 주소를 적었다면 새 주소로 바꾸십시오.

2026-10-08

앱 매니페스트 파일 이름 (sayren 0.8.1)

앱 매니페스트

  • 변경: 앱 매니페스트 파일 이름이 manifest.json입니다. npx sayren app init이 이 이름으로 만듭니다.
  • 옛 이름 sayren.app.json은 manifest.json이 없을 때 계속 읽고, CLI가 이름을 바꾸라는 경고를 냅니다.

개발자 서버에서 실행·에이전트 도구 (@sayren/store-sdk 0.30.13 · @sayren/app 0.8.0 · sayren 0.8.0)

개발자 서버에서 실행 · 에이전트 도구

  • 추가: 매니페스트 runtime: "external"·external.url — 앱 코드를 개발자 서버에서 실행합니다. 호출에는 서명(x-sayren-signature)이 붙고 공개 키는 /.well-known/sayren-apps-jwks.json입니다. @sayren/app createExternalHandler가 확인합니다.
  • 추가: 매니페스트 mcp.tools와 defineApp({ tools }) — 상점의 AI 에이전트가 쓰는 앱 도구. 관리 API GET /v1/apps/tools·POST /v1/apps/installations/{id}/tools/{name}(SDK appTools).
  • 추가: 설치·앱 상세 응답 tools·externalHost. 도구 추가·실행 호스트 변경은 재동의가 필요합니다.

주문 흐름에서 앱 쓰기 (@sayren/store-sdk 0.30.13 · @sayren/app 0.8.0 · sayren 0.8.0)

주문 흐름에서 앱 쓰기

  • 추가: 매니페스트 flow.notifications·flow.events와 defineApp({ flows }) — 주문 흐름의 알림 블록이 앱으로 알림과 매개변수 값(셀러가 식으로 연결)을 보냅니다.
  • 추가: 흐름 알림 블록 channel: "app.<handle>.<알림>"과 params(매개변수 → 식). 웹훅 알림은 그대로 event가 필요합니다.
  • 추가: 앱 사건 POST /v1/order-items/{id}/events/app.<handle>.<사건> — 그 앱의 설치 토큰으로만, 매니페스트 flow.events에 있는 이름만 받습니다(아니면 403 FLOW_EVENT_APP_FORBIDDEN). SDK orderItems.emitEvent가 app. 키를 받습니다.
  • 추가: 설치 응답 flowNotifications·flowEvents. 로컬에서는 npx sayren app trigger --flow <이름>.

앱 공개 범위 — 비공개 앱·퍼블릭 앱

앱 공개 범위

  • 추가: 비공개 앱 — 만들 때 상점 하나를 정하면(sayren app init --private-store <storeId>, 개발자 API privateStoreId) 그 상점에만 보이고 설치됩니다.
  • 추가: 공개 신청과 운영자 심사 — 승인된 앱은 모든 상점의 앱 목록에 보입니다. 개발자 앱 응답 privateStoreId·listingReview.
  • 추가: 앱 목록·상세 응답 visibility(DEVELOPMENT · PRIVATE · PUBLIC).

앱 공개 HTTP·예약 작업·저장소 (@sayren/store-sdk 0.30.12 · @sayren/app 0.7.0 · sayren 0.7.0)

앱 코드

  • 추가: 매니페스트 http.public — 적은 HTTP 핸들러를 https://{handle}.sayrenapps.com/hooks/{설치 id}/…로 외부에서 부를 수 있습니다(메일 링크·외부 서비스 콜백). ctx.publicUrl(path)가 주소를 만듭니다.
  • 추가: 매니페스트 schedules와 defineApp({ schedules }) — 설치마다 15분~하루 간격으로 실행되는 작업입니다. 로컬에서는 npx sayren app trigger --schedule <이름>.
  • 추가: ctx.storage — 설치별 키-값 저장소(1,000키·1MB). 관리 API GET·PUT·DELETE /v1/app/storage/{key}·GET /v1/app/storage(앱 설치 토큰 전용, SDK app.storage).
  • 추가: 설치 상세·앱 상세 응답 publicHttp·schedules. 공개 HTTP·예약 작업이 늘어난 버전은 재동의가 필요합니다.

앱 화면·HTTP 핸들러

상점 플랫폼 앱 화면

  • 추가: 매니페스트 ui.adminPage를 적은 앱은 상점 플랫폼 **앱 › 설치한 앱 › {앱}**의 앱 화면 탭에 열립니다. 화면 주소는 https://{handle}.sayrenapps.com입니다.
  • 추가: 앱 화면이 @sayren/app/ui의 app.fetch로 앱 코드의 http 핸들러를 부릅니다. 핸들러의 ctx.user에 화면을 연 사람의 id와 역할이 들어 있습니다.

앱 권한 업데이트·실행 기록 (@sayren/store-sdk 0.30.11 · @sayren/app 0.6.1 · sayren 0.6.1)

앱 설치와 관리

  • 추가: 설치 상세 pendingVersion(재동의를 기다리는 새 버전의 권한·외부 호스트·이벤트). 없으면 null입니다.
  • 추가: POST /v1/apps/installations/{installationId}/consent(SDK apps.installations.consent) — 권한 업데이트에 동의하고 새 버전으로 옮깁니다. 앱에 APP.SCOPES_UPDATED가 갑니다. 상점 플랫폼 전용입니다.
  • 추가: GET /v1/apps/installations/{installationId}/activity(SDK apps.installations.activity) — 최근 이벤트 전송 결과 50건과 최근 7일 관리 API 호출 수.
  • 추가: 구매자가 탈퇴하면 고객 정보 권한(customer:*)으로 설치된 앱에 APP.CUSTOMER_REDACT가 갑니다.
  • 추가: 실행 한도 — 앱 전체 동시 실행 20, 설치당 분당 이벤트 300. 넘은 이벤트는 시도로 세지 않고 뒤로 미룹니다.
  • 추가: 24시간 동안 모든 호출이 앱 오류로 실패한 앱은 자동으로 정지되고 개발자 조직 구성원에게 메일이 갑니다.
  • 수정: 앱 로그·오류 메시지에 섞인 설치 토큰·비밀 값·비밀 설정을 가려서 저장합니다. 호출 기록을 7일 뒤 지웁니다.
  • 수정: sayren app init의 예제 코드가 event.data를 캐스팅하고 없는 필드(order.totalAmount)를 읽던 문제. order.payment.totalAmount를 씁니다.

2026-10-05

테스트 결제 주문 리뷰 설정·주문상품 리뷰 작성 가능 (@sayren/store-sdk 0.30.8 · @sayren/storefront-sdk 0.20.17)

  • 추가: 상점 testOrderReviewsAllowed(기본 false). PATCH /v1/store로 켜면 테스트 결제 주문에서도 구매자가 리뷰를 쓸 수 있습니다. 끄면 이전처럼 409 REVIEW_NOT_ALLOWED_FOR_TEST_ORDER입니다.
  • 추가: 스토어프론트 내 주문의 주문상품 reviewWritable. 로그인 회원이 지금 리뷰를 쓸 수 있으면 true입니다(구매확정, 작성 기한 안, 아직 쓰지 않음, 테스트 결제 주문이면 상점 허용). 리뷰 버튼은 이 값으로 그립니다.

배송·주문 연락처 형식 (@sayren/storefront-sdk 0.20.16)

  • 변경: 배송지 phone과 비회원 주문자 phone이 휴대폰 외에 050 안심번호(0503~0508)·070·지역번호 전화도 받습니다(숫자만). 회원 가입 휴대폰은 그대로 휴대폰만 받습니다. SDK CONTACT_PHONE_PATTERN.

흐름 기록 행동 이름·묶음에서 먼저 취소된 상품

주문 흐름

  • 추가: 흐름 실행 기록 trigger.label — 셀러·구매자 행동의 버튼 이름(예: 「접수」). 이름이 없는 상점 행동은 키만 옵니다.
  • 변경: 묶음 진행에서 취소·반품 클레임으로 먼저 빠진 주문상품은 묶음이 거절 등 다른 단계로 끝나도 흐름 보기가 「취소」(반품이면 반품)입니다. 끝난 주문상품의 moveTargets는 빈 배열입니다.
  • 변경: 주문제작형 프리셋 4 — 시안 확인 기한 초과·수정 요청 한도 초과 단계 이름을 「구매자 연락 필요」(구매자 「판매자 확인 중」)로 바꾸고 구매자 안내문과 취소 요청 권한을 더했습니다. 이미 만든 흐름은 그대로입니다.

즉시 취소·묶음 클레임·판매자 취소 미리보기 (@sayren/store-sdk 0.30.7 · @sayren/storefront-sdk 0.20.15)

주문 흐름

  • 변경: 흐름 권한이 「주문 취소」(즉시)인 단계에서 구매자가 취소를 신청하면 판매자 승인 없이 바로 취소·환불합니다. 클레임 접수 응답 approved: true, 흐름 행동 응답 claim.approved: true입니다. 이전에는 승인 대기 클레임으로 남았습니다.
  • 추가: 흐름 설정 requires.groupClaim(주문 묶음 진행에서 한 상품의 취소·반품 중). CONTINUE면 묶음의 다른 상품은 그대로 진행하고 승인된 상품만 빠집니다. PAUSE(기본)면 처리할 때까지 묶음 전체의 기한·행동이 멈춥니다. 음식 배달형 5는 CONTINUE입니다.
  • 변경: 묶음 진행에서 취소·반품 클레임의 행동(요청 철회)은 그 클레임의 주문상품에만 옵니다. 묶음에서 전량 취소돼 빠진 주문상품의 흐름 보기는 「취소」 상태이고 행동이 없습니다.
  • 추가: GET /v1/order-items/{orderItemId}/cancel-preview(SDK orderItems.cancelPreview) — 판매자 취소를 하면 돌려줄 현금·적립금·배송비.
  • 추가: 흐름 행동 attachments(REQUIRED·OPTIONAL, 첨부를 받는 행동만). 흐름 상태 입력 값 display(선택지 이름 등 보이는 값). 입력 칸 options가 { value, label }을 받습니다.
  • 추가: 구매자 주문 payment.pointRefundedAmount(취소·반품으로 돌려준 적립금).
  • 변경: 판매자가 흐름 행동으로 주문을 거절·취소하면 흐름이 그 행동의 도착 단계(예: 「주문 거절」)와 입력 사유로 끝납니다. 이전에는 「주문 취소」로 끝났습니다.
  • 수정: 적립금으로 낸 배송비가 전량 판매자 취소에서 돌아오지 않던 문제.

기본 흐름 시드·흐름 만들기 순서 (@sayren/store-sdk 0.30.6)

주문 흐름

  • 변경: 새 상점은 기본 흐름 두 개(일반 배송형·배송 없는 상품 기본형)만 상점 흐름으로 시드합니다. 이전에는 주문제작형·티켓형·숙박 예약형·음식 배달형을 게시된 흐름으로 미리 만들었습니다. 이미 만들어진 흐름은 그대로입니다.
  • 추가: 흐름 요약 defaultFor(기본 흐름이면 SHIPPING·MANUAL). 기본 흐름의 초안 저장·게시·버전 옮기기는 409 FLOW_DEFAULT_LOCKED입니다. 흐름을 지정하지 않은 상품은 상점의 기본 흐름을 따릅니다.
  • 추가: GET /v1/flow-presets/{key}(프리셋 그래프·흐름 설정, SDK commerceFlows.preset). 흐름 생성 POST /v1/commerce-flows가 graph·definition을 받습니다 — 편집한 그래프로 처음부터 만듭니다.

구매자 주문의 환불 금액·클레임 중 흐름 행동 (@sayren/storefront-sdk 0.20.14)

  • 추가: 구매자 주문 payment.refundedAmount(환불된 결제 금액)·payment.refundPendingAmount(판매자가 승인했지만 아직 환불하지 않은 금액).
  • 변경: 취소·반품·교환이 진행 중인 주문상품은 흐름 행동(구매확정·셀러 버튼 등)이 enabled: false로 옵니다(disabled.reason은 CONDITION). 실행하면 거절되던 행동을 미리 꺼서 보입니다. 클레임 쪽 행동(요청 철회)은 그대로 켜져 있습니다.
  • 변경: 배송 뒤 반품으로 끝난 주문상품의 흐름 상태 이름이 「반품 완료」(셀러 「반품」)입니다. 이전에는 「주문 취소」였습니다.

권한 표 수수료·신청 안내·환불 대기 (@sayren/store-sdk 0.30.5 · @sayren/storefront-sdk 0.20.13)

주문 흐름

  • 추가: 스토어프론트 단계 요약 step.claimable(지금 신청할 수 있는 취소·반품·교환과 기한).
  • 추가: 판매자 취소 holdRefund, 응답 refundHeld. 환불 이력 status에 HELD(환불 대기)·PROCESSING. POST /v1/payments/refunds/{refundId}/process(SDK payments.processRefund).
  • 추가: 흐름 셀러 권한 SELLER_CANCEL_HOLD(주문 취소, 환불은 따로).
  • 추가: 클레임 승인 holdRefund, 응답 refundHeld. 결제 목록 refundHeld=true 조건과 결제 heldRefundAmount.
  • 추가: GET /v1/flow-node-types의 platformActions(권한 표에 켤 수 있는 플랫폼 행동 — 키·이름·기본 방식·하위 흐름·기간·수수료 가능 여부).
  • 변경: 행동 입력 칸은 그래프 안에 둡니다 — 셀러 확인 노드 버튼의 input, 셀러·구매자 행동 연결선 조건(on)의 input(입력 칸 목록). 흐름 정의의 actionInputs는 쓰지 않고, 옛 흐름은 읽을 때 버튼·연결선으로 옮겨집니다. 연결선 조건의 문자열 input 힌트는 없앴습니다. 음식 배달형 4.
  • 변경: 그래프 형식이 {"$var": …}를 받지 않습니다(기간은 ISO 8601 문자열).

흐름은 빈 흐름을 편집한 그래프 하나

주문 흐름

  • 변경: 흐름 만들기는 이름·키만 받습니다. 프리셋 설치 값(variables)은 무시하고, 기한·횟수 같은 값은 그래프의 노드 설정 안에 실제 값으로 들어 있습니다. 프리셋 요약의 variables는 늘 빈 목록입니다.
  • 변경: 흐름 정의의 커스텀 필드(fields)·이름 붙인 식(expressions)·주문 입력·속성 선언(requires.orderInputs 등)을 쓰지 않습니다. 데이터 블록은 선언 없이 칸을 저장하고, 식은 오프셋 있는 ISO 일시 문자열을 시각으로 읽습니다. 흐름 데이터(flowState.fields)는 셀러에게만 보입니다.
  • 이미 만든 흐름은 읽을 때 설치 값·이름 붙인 식이 노드 설정으로 펼쳐지고, 다시 게시하면 그 형태로 저장됩니다. 진행 중 주문은 결제 때 버전으로 그대로 진행합니다.
  • 프리셋 버전: 주문제작형 3, 음식 배달형 3, 티켓형 3, 숙박 예약형 2.

승인 요청 작업 이름·미리보기 (@sayren/store-sdk 0.30.4)

필드 추가와 오류를 앞당겨 주는 변경입니다. 에이전트 쓰기 승인

  • 추가: 승인 요청 operationName(작업 이름), 미리보기 targetName(대상 이름).
  • 추가: 흐름 게시·구매자 적립금 지급·차감·적립 정책 수정의 미리보기 facts·changes.
  • 변경: 실행하면 실패할 요청(흐름 게시 차단 오류, 적립금 소멸 시각 없음, 잔액 부족)은 드라이런·승인 요청 단계에서 실행과 같은 오류를 줍니다. 승인해도 실패할 승인 요청을 만들지 않습니다.
  • 변경: 수동 지급의 400 POINT_EXPIRY_REQUIRED 문구가 원인(소멸 시각을 정할 수 없음)과 해결(expiresAt 또는 정책 expiryDays)을 알려 줍니다.

신청 전 미리보기·흐름 판정 (@sayren/storefront-sdk 0.20.12)

취소·반품·교환

  • 추가: POST /storefront/v1/me/order-items/{orderItemId}/claims/preview(SDK myClaims.preview). 예상 환불·반품 배송비·취소 수수료(deductAmount)를 접수 없이 줍니다.
  • 변경: 클레임 API(POST /storefront/v1/me/order-items/{orderItemId}/claims)도 주문 흐름의 판정을 따릅니다. 흐름이 그 단계에서 신청을 받지 않으면 409 INVALID_STATUS, 흐름 기한이 지났으면 409 PERIOD_EXPIRED입니다. 지금까지는 상태만 보고 받았습니다.
  • 수정: 클레임 API로 접수해도 흐름이 정한 취소 수수료(숙박 차감표·티켓 수수료 등)를 예상 환불과 승인 기본 차감에 반영합니다. 지금까지는 흐름 행동으로 접수할 때만 반영했습니다.

프리셋 판매자 취소

주문 흐름

  • 변경: 티켓형(2)·주문제작형(2)·숙박 예약형(1) 프리셋의 발송·제공 전 상태에 판매자 취소(SELLER_CANCEL)가 켜집니다. 이미 설치한 흐름은 복사본이라 바뀌지 않습니다. 편집기 권한 표에서 켭니다.
  • 변경: 음식 배달형(2)은 접수 뒤 조리 중·배달 대기에서 셀러 행동 「가게 사정 취소」(STORE_CANCEL)로 묶음 전체를 취소합니다(PG 취소 1회). 묶음 진행이라 상품별 판매자 취소는 두지 않습니다.

구매자 요청 철회 행동

주문 흐름

  • 수정: 정해진 시각 타이머(at, 체크인·관람일)는 취소·반품이 진행되는 동안 멈추지 않습니다. 지금까지는 철회·거절 뒤 그 시간만큼 기한이 밀렸습니다. 기간 타이머(after)는 그대로 멈춥니다.
  • 수정: 이름 없는 플랫폼 행동이 키 대신 기본 이름(예: 요청 철회)으로 옵니다.
  • 추가: 취소·반품·교환 요청이 「요청」 상태면 구매자 주문상품 actions에 WITHDRAW_REQUEST(요청 철회)가 옵니다. POST /storefront/v1/me/order-items/{orderItemId}/actions/WITHDRAW_REQUEST(비회원은 비회원 행동 경로)로 철회합니다.

2026-10-04

흐름 진행 단위 게시 반영

주문 흐름

  • 수정: 초안 definition.requires.progression·fulfillmentMethods를 바꿔 게시해도 흐름의 progression·fulfillmentMethods가 만들 때 값 그대로였습니다. 이제 게시한 버전의 값을 따르고, 진행 단위는 버전마다 고정됩니다.
  • 추가: 게시 검사 코드 REQUIRES_INVALID, 버전 옮기기 409 FLOW_PROGRESSION_MISMATCH(details.from·details.to).

플랫폼 행동 입력 칸·이전 자료 (@sayren/store-sdk 0.30.3 · @sayren/storefront-sdk 0.20.11)

필드·값 추가만 있습니다. 주문 흐름

  • 추가: 흐름 조회 actions[].input이 송장 등록(DISPATCH: 배송 방법·택배사·운송장 번호)과 판매자 취소(SELLER_CANCEL: 사유·상세)의 칸을 싣습니다.
  • 추가: 입력 칸 options의 값·이름 쌍({ value, label }), 조건부 칸 when({ key, in }).
  • 추가: 흐름 상태·단계 earlierAttachments(앞 상태에서 붙인 자료). attachments는 그 자료를 붙여 도착한 상태에서만 옵니다.
  • 변경: 409 ACTION_NOT_AVAILABLE·409 FLOW_MOVE_NOT_ALLOWED의 문구에 코드를 싣지 않습니다. 코드는 details.key·details.reason에 있습니다.

버튼 입력 칸·구매자 단계 요약 (@sayren/store-sdk 0.30.2 · @sayren/storefront-sdk 0.20.10)

필드·값 추가만 있습니다. 주문 흐름

  • 추가: 스토어프론트 주문상품 step(type·label·message·deadlineAt·attachments·inputs·actions·data.delivery).
  • 추가: 흐름 상태 inputs(앞 행동의 입력값, 관리·스토어프론트).
  • 추가: 행동 입력 형식 textarea·radio·checkbox·boolean, 칸 placeholder. 셀러 확인 버튼 buttons[].input, 구매자 확인 inputs.APPROVE·inputs.REQUEST_CHANGE.
  • 추가: 행동 입력 검사 400 FLOW_ACTION_INPUT_INVALID(details.issues). 지금까지는 입력 칸과 맞지 않는 값도 받았습니다.
  • 추가: 스토어프론트 SDK cart.createInputImageUpload(POST /storefront/v1/uploads/order-inputs).

주문 흐름 확장 (@sayren/store-sdk 0.30.1 · @sayren/storefront-sdk 0.20.9)

필드·엔드포인트·값 추가만 있습니다. 주문 흐름

  • 추가: 상태 직접 이동 POST /v1/order-items/{orderItemId}/flow/move, 흐름 조회의 moveTargets·branches, 실행 기록 GET /v1/order-items/{orderItemId}/flow/runs·GET /v1/flow-runs/{runId}.
  • 추가: 주문 전체 행동 POST /v1/orders/{orderId}/actions/{key}, POST /storefront/v1/me/orders/{orderId}/actions/{key}, POST /storefront/v1/guest/orders/{orderId}/actions/{key}.
  • 추가: 주문 progressStatus(관리 목록·상세, 내 주문), 내 주문 주문상품 flowBranches.
  • 추가: 버전 GET /v1/commerce-flows/{flowId}/versions/{version}·POST …/versions/{version}/migrate, 흐름 조회 versions[].inProgress.
  • 추가: 시뮬레이션 POST /v1/commerce-flows/{flowId}/draft/simulate, 과거 주문 재생 POST …/draft/replay, 노드 종류 GET /v1/flow-node-types.
  • 추가: 노드 하위 흐름 호출·병렬 분기·합류·반복 횟수 조건·알림·주문 동기화, 그래프 notes(메모). 게시 검사 코드 CALL_TARGET_MISSING·CALL_TARGET_UNPUBLISHED·CALL_CYCLE·FORK_JOIN_INVALID.
  • 추가: 웹훅 ORDER.FLOW_STATE_CHANGED. 구독하려면 이 버전 이상의 SDK로 웹훅을 파싱하십시오.
  • 변경: 권한 표에 이름이 없는 플랫폼 행동(취소 요청·판매자 취소 등)에 기본 이름이 붙습니다.
  • 삭제: 견적형 프리셋(quote). 설치한 적이 없는 준비 중 프리셋이었습니다.

흐름 지정을 상품·카테고리로 (@sayren/store-sdk 0.30.0)

호환성 변경이 있습니다. 할당 규칙(조건식)을 없애고 흐름을 상품·카테고리에 지정합니다. 주문 흐름

  • 삭제(호환성 변경): GET·PUT /v1/commerce-flows/assignment-rules, POST /v1/commerce-flows/assignment-rules/test. 이제 410 FLOW_ASSIGNMENT_RULES_RETIRED로 답합니다. SDK commerceFlows.rules·saveRules·testRules와 할당 규칙 스키마를 지웠습니다.
  • 변경(호환성 변경): GET /v1/products/{productId}/flow의 source가 PRODUCT·CATEGORY·PLATFORM입니다(RULE·ruleId 없음).
  • 추가: 상품 flowId(등록·수정·일부 수정·응답), 카테고리 flowId(일부 수정·응답, 맨 아래 카테고리만).
  • 변경: 일반 배송형 기본 흐름의 상태 이름이 결제완료 → 배송준비중 → 배송중 → 배송완료 → 구매확정입니다.

주문 흐름 (#115) (@sayren/store-sdk 0.29.8 · @sayren/storefront-sdk 0.20.8)

필드·엔드포인트·값 추가만 있습니다. 주문 흐름은 순차적으로 열리고, 열리기 전에는 모든 주문이 기본 흐름으로 처리됩니다. 주문 흐름

  • 추가: 관리 API GET /v1/flow-presets, /v1/commerce-flows(설치·조회·초안 저장·검사·게시), /v1/commerce-flows/assignment-rules(할당 규칙·시험), /v1/commerce-flows/secrets. 스코프 flow:r·flow:rw.
  • 추가: 주문상품 흐름 GET /v1/order-items/{orderItemId}/flow, 행동 POST /v1/order-items/{orderItemId}/actions/{key}, 사건 POST /v1/order-items/{orderItemId}/events/{custom.…}.
  • 추가: 스토어프론트 주문 상세 주문상품의 flowState·actions, 구매자 행동 POST /me/order-items/{orderItemId}/actions/{key}, 비회원 POST /guest/orders/{orderId}/items/{orderItemId}/actions/{key}.
  • 추가: 상품 직접 입력의 key·type(datetime·select·image 등)·options, 상품 fulfillment.methods(택배·직접 배달·방문 수령), 주문서 fulfillmentMethod·pickupLocationId, 픽업 장소 /v1/pickup-locations, 조합 attributes.
  • 추가: 업로드 purpose: "flow-attachment", 웹훅 ORDER.FLOW_NOTIFICATION.

날짜별 재고·숙박 예약형 (#118) (@sayren/store-sdk 0.29.8 · @sayren/storefront-sdk 0.20.8)

필드·엔드포인트·값 추가만 있습니다. 상품 › 날짜별 재고

  • 추가: 상품 inventoryMode(STANDARD·DATED), 달력 GET·PUT /v1/products/{productId}/calendar, 스토어프론트 GET /products/{productId}/calendar.
  • 추가: 장바구니 담기·바로 구매의 stay(checkIn·checkOut), 장바구니·주문서·주문상품 응답의 stay(박마다 날짜·가격).
  • 추가: 오류 400 STAY_REQUIRED·STAY_NOT_APPLICABLE·STAY_INVALID, 409 DATES_UNAVAILABLE·INVENTORY_MODE_LOCKED·INVENTORY_MODE_NOT_DATED.
  • 추가: 주문 흐름 프리셋 stay(숙박 예약형).
  • 추가: SDK products.getCalendar·products.setCalendar(store-sdk), catalog.getProductCalendar(storefront-sdk), 스키마 stayRangeSchema·staySnapshotSchema·calendarDaySchema·stayRequestSchema·stayViewSchema.

2026-10-02

쿠폰 받기·회원가입 쿠폰 (@sayren/store-sdk 0.29.7 · @sayren/storefront-sdk 0.20.7)

필드·엔드포인트·값 추가만 있습니다. 쿠폰

  • 추가: 쿠폰 발급 방식 issueMethod에 DOWNLOAD(구매자가 스토어프론트에서 받기)·SIGNUP(회원가입 자동 지급). 관리 API POST /v1/coupons·GET /v1/coupons?issueMethod=가 받습니다. 두 방식은 회원 전용이고 code·perMemberUseLimit을 받지 않습니다. SIGNUP의 perMemberIssueLimit은 1만 받습니다. 응답 issueMethod는 원래 문자열이라 기존 SDK도 읽습니다.
  • 추가: 스토어프론트 GET /coupons — 받을 수 있는 다운로드 쿠폰 목록(페이지, productId로 상품 대상 쿠폰만). 회원 토큰이 있으면 downloaded·downloadable을 싣습니다.
  • 추가: 스토어프론트 POST /coupons/{couponId}/download — 쿠폰 받기(회원). 응답은 내 쿠폰 항목 한 장입니다. 오류 409 COUPON_ALREADY_DOWNLOADED·COUPON_SOLD_OUT·COUPON_NOT_STARTED.
  • 추가: 내 쿠폰 GET /me/coupons 항목과 관리 API 발급 내역 GET /v1/coupons/{couponId}/issues 항목의 source(MANUAL·DOWNLOAD·SIGNUP).
  • 추가: SDK catalog.listCoupons(params)·me.downloadCoupon(couponId), 스키마 storefrontCouponSchema.
  • 변경: 회원가입(이메일 가입·소셜 로그인 첫 가입)에 사용 중인 회원가입 쿠폰을 1장씩 지급합니다. 가입 응답 형태는 그대로입니다.

주문서 상품 대표 이미지 (@sayren/storefront-sdk 0.20.6)

필드 추가만 있습니다.

  • 추가: 스토어프론트 주문서 응답(POST /checkout)의 items[].thumbnailUrl — 주문서 시점의 상품 대표 이미지입니다. 없으면 null이고, 장바구니 줄의 thumbnailUrl과 같은 값입니다.

주문·상품 번호 (@sayren/store-sdk 0.29.6 · @sayren/storefront-sdk 0.20.5 · @sayren/mcp 0.23.7, 템플릿 0.2.15)

필드 추가와 조회 추가만 있습니다. 기존 id(orderId·productId 등)와 경로는 그대로입니다. 주문·상품 번호

  • 추가: 주문번호 orderNo(14자리 숫자), 상품주문번호 orderItemNo, 클레임번호 claimNo, 상품번호 productNo(상점별 1부터), 조합 번호 variants[].variantNo(상점 안에서 유일), 장바구니 items[].productNo. 관리 API·스토어프론트 API의 주문·클레임·상품 응답과 웹훅 ORDER.*(data.orderNo)·CLAIM.*(data.claimNo)에 실립니다. 기존 주문·상품에도 번호가 채워져 있습니다.
  • 추가: 관리 API GET /v1/orders?orderNo= — 기간 없이 주문번호로 찾습니다(하이픈 허용). orderNo가 있으면 orderedFrom을 생략할 수 있습니다. keyword도 주문번호를 받습니다.
  • 추가: 관리 API GET /v1/products?productNo=.
  • 추가: 스토어프론트 GET /products/{productId}(리뷰·문의 목록 포함)와 비회원 주문 조회 POST /guest/orders/{orderId}가 경로에 숫자 번호(productNo·orderNo)를 받습니다. 상품번호는 그 상점 안에서 찾습니다.
  • 변경: 새 결제의 PG 주문번호(토스페이먼츠 orderId·포트원 paymentId)가 {주문번호}-{시도 순번}입니다. 결제 내역 API의 pgOrderId가 이 값입니다. 이전 결제는 그대로 결제 시도 id입니다.
  • 추가: SDK formatOrderNo(4자리 하이픈 표기)·parseOrderNo.
  • 추가: 템플릿 0.2.15 주문 내역·주문 상세·비회원 주문 조회에 주문번호(4자리 하이픈 표기)와 상품주문번호.
  • 변경: 템플릿 0.2.15 상품 주소가 /products/{productNo}입니다. 예전 주소(/products/prod_…)는 상품번호 주소로 301 이동합니다. MCP 규칙 product-link(권고)가 상품 id 링크를 알립니다.

MCP 대화에서 승인 요청 처리 (@sayren/mcp 0.23.6)

필드 추가와 권한 완화만 있습니다. MCP 대화에서 승인하기

  • 추가: MCP 도구 list_approvals(승인 요청 목록·한 건)와 decide_approval(승인·거절). 원격 MCP에서는 MCP 클라이언트가 확인 창(elicitation)을 띄우고 사람이 수락했을 때만 결정합니다. 확인 창을 지원하지 않는 클라이언트와 로컬 MCP는 승인 화면 링크만 알려 줍니다.
  • 변경: 승인 요청 목록 GET /v1/approval-requests와 남의 요청 단건 조회를 원격 MCP·AI 에이전트용 토큰의 OWNER·ADMIN도 씁니다(이전에는 상점 플랫폼 로그인만). 승인 API approve·reject는 여전히 에이전트에게 403 AGENT_OPERATION_DENIED입니다.
  • 추가: 원격 MCP 쓰기 도구의 승인 대기 안내에 decide_approval. 감사 로그 상세 기록 APPROVAL_DECIDED(metadata.via = MCP_ELICITATION).

버전의 기준 템플릿 버전 (@sayren/mcp 0.23.5, 템플릿 0.2.14)

필드 추가와 값 판정 수정입니다. 템플릿으로 쇼핑몰 열기

  • 수정: 편집으로 만든 버전의 templateVersion이 그 시점 카탈로그 버전으로 적혀 파일을 바꾸지 않아도 카탈로그가 오를 때마다 함께 올랐습니다. 이제 버전 파일의 package.json sayren.templateVersion이고, 없으면 발행 버전의 값을 이어받습니다. 이미 만든 버전의 값은 그대로입니다.
  • 추가: 템플릿 0.2.14 package.json의 sayren 필드(template·templateVersion). sayren-mcp create·원격 get_template_file도 같은 값을 싣습니다.

컬렉션 홈 섹션 (@sayren/mcp 0.23.4, 템플릿 0.2.13)

API 변경은 없습니다. 컬렉션

  • 추가: 템플릿 0.2.13 레이아웃 상품 섹션의 source에 { "collection": "주소" }. 섹션이 GET /collections/{slug}/products로 컬렉션 상품을 그리고, 컬렉션이 없거나 숨김·기간 밖이면 섹션을 그리지 않습니다. moreLink의 「더 보기」는 /collections/{slug}로 갑니다. 옛 레이아웃은 그대로 읽힙니다.
  • 추가: MCP 편집 안내(get_editing_guide·get_scaffold_plan의 extensionPoints)에 컬렉션 섹션 쓰는 법.

적립금 화면 안내 (@sayren/storefront-sdk 0.20.4 · @sayren/mcp 0.23.4, 템플릿 0.2.13)

필드 추가만 있습니다. 적립금 · 결제 연동

  • 추가: 스토어프론트 상품 상세 GET /products/{productId}의 purchasePoint. 지금 가격 1개 기준 구매 적립 예정액을 서버가 상점·상품 적립률로 계산합니다. 구매 적립이 꺼져 있거나 적립하지 않는 상품이면 null입니다. 옛 서버 응답은 SDK가 null로 읽습니다.
  • 추가: 템플릿 0.2.13 상품 상세 「구매 시 n원 적립」, 후기 블록의 리뷰 혜택 안내(GET /point-policy), 내 적립금 화면(/points, 잔액·30일 안 소멸 예정·내역).
  • 문서: 결제 연동 가이드 맨 앞에 「5분 요약」(SDK 주문 예시, 스토어프론트는 정보만 넘기는 원칙).

적립금 전액 결제 (@sayren/store-sdk 0.29.5 · @sayren/storefront-sdk 0.20.3 · @sayren/mcp 0.23.3, 템플릿 0.2.12)

필드 추가와 요청 완화만 있습니다. 적립금 · 결제 흐름

  • 변경: 적립금이 결제 금액 전체를 덮으면 0원 결제입니다. 남는 결제 금액이 1~99원일 때만 적립금을 줄여 100원을 남깁니다(points.adjustReason MIN_PAYMENT). 전액 사용은 사용 단위를 보지 않습니다. store-sdk pointUseOf·quotePricing이 같은 규칙입니다.
  • 추가: 금액 미리보기 응답의 paymentRequired. false면 결제수단으로 낼 금액이 없습니다. 판단은 서버가 하고, 스토어프론트는 금액을 계산하지 않고 이 값으로 결제수단 영역을 숨깁니다. payable은 0원(적립금 전액)도 true입니다.
  • 변경: 결제 시작 요청의 option이 선택입니다. 결제가 필요 없는 주문은 생략하고, 결제가 필요한데 생략하면 400 VALIDATION_FAILED입니다. 0원 결제는 결제창 없이 그 요청에서 주문을 만들고 응답이 status: "completed"·orderId, payUrl·attemptId·option이 null입니다. 주문의 payment.method·approvedMethod는 POINT, totalAmount는 0입니다. 0원 결제는 재시도할 수 없고(409 ALREADY_PROCESSED) 현금영수증을 신청하면 400 CASH_RECEIPT_NOT_AVAILABLE입니다. 취소·반품은 현금 환불 없이 적립금만 돌려줍니다.
  • 추가: 결제 시작의 헤더 Idempotency-Key(선택). 같은 키의 재요청은 처음 결과를, 같은 키의 동시 요청은 409 IDEMPOTENCY_KEY_IN_USE를 돌려줍니다.
  • 수정: SDK payments.start가 pointAmount를 결제 시작 본문에 싣습니다(이전에는 빠져 브라우저 도우미로는 적립금을 쓸 수 없었습니다). option은 선택입니다.
  • 추가: 템플릿 0.2.12 주문서가 paymentRequired가 false면 결제수단을 숨기고 「적립금으로 결제」로 바로 주문합니다.
  • 수정: 템플릿 0.2.12 컬렉션 화면이 컬렉션 기본 정렬(productSort, latest 같은 값)을 정렬 선택에 맞게 보입니다. 이전에는 늘 「진열순」으로 보였습니다(상품 순서는 맞았습니다).

판매량 상위 컬렉션 (@sayren/store-sdk 0.29.4 · @sayren/storefront-sdk 0.20.2)

필드 추가뿐입니다. 판매량을 사용하지 않는 컬렉션의 동작은 그대로입니다. 컬렉션

  • 추가: 조건 { field: "bestSelling", days: 7 | 30 | 90, top: 1~100 } — 다른 조건을 만족하는 판매 중 상품 중 기간 판매 수량 상위 N개입니다. 판매 수량은 실결제 주문의 결제 수량 − 취소·반품 수량이고 날짜는 주문 시각의 한국 날짜입니다. 판매가 없는 상품은 담지 않고, 동률은 최근 등록 → 상품 id 순입니다. 집계는 최대 10분 늦습니다.
  • 추가: 상품 정렬 BEST_SELLING(판매량순)을 만들기·수정·미리보기에서 받습니다. 판매량 상위 조건이 있으면 정렬을 생략했을 때 기본값입니다. 기간은 그 조건의 기간, 없으면 30일입니다.
  • 추가: 컬렉션 응답·미리보기 응답의 salesComputedAt(판매량 기준 시각), 스토어프론트 GET /collections/{slug}의 salesComputedAt. 판매량을 사용하지 않거나 집계 전이면 null입니다.
  • 추가: store-sdk 순수 함수 rankBestSelling·compareBySales·bestSellingRuleOf, 상수 COLLECTION_BEST_SELLING_DAYS·MAX_COLLECTION_BEST_SELLING_TOP.
  • 스토어프론트 정렬 값은 그대로입니다. 판매량순 컬렉션의 productSort는 manual이고 sort=manual이 판매량 순입니다.

적립금 결제 사용·취소 환불 (@sayren/store-sdk 0.29.3 · @sayren/storefront-sdk 0.20.1 · @sayren/mcp 0.23.2, 템플릿 0.2.11)

필드 추가만 있습니다. 적립금 사용이 준비 중인 동안에는 정책 조회의 useAvailable이 false이고 적립금 사용 요청이 409 POINTS_UNAVAILABLE입니다. 적립금

  • 추가: 적립금 정책의 use(enabled·minBalance·minAmount·unit·maxRatioBp·includeDeliveryFee)와 조회의 useAvailable. 필드가 없는 이전 응답은 끔으로 읽습니다.
  • 추가: 주문서 응답의 points(잔액·최대 사용액·사용 단위·최소 사용액·최소 보유액, 비회원·사용 끔이면 null), amounts.pointAmount.
  • 추가: 금액 미리보기·결제 시작 요청의 pointAmount. 미리보기 응답의 amounts.pointAmount·items[].pointAllocation·points(balance·maxUsable·adjustReason·rejectReason). 적용 순서는 즉시할인 → 쿠폰 → 적립금이고 결제 금액은 100원 이상 남깁니다(전액 결제는 위 「적립금 전액 결제」). 오류는 400 POINT_NOT_APPLICABLE(details.reason)·409 POINT_BALANCE_CHANGED·409 POINTS_UNAVAILABLE입니다.
  • 추가: 주문의 payment.pointAmount·items[].pointAllocation(관리 API·구매자 주문 조회), 주문 웹훅 데이터의 pointAmount.
  • 추가: 직권취소·클레임 승인 응답, 클레임 웹훅(CLAIM.*)·취소 웹훅의 pointRefundAmount, 구매자 클레임 접수 응답의 pointRefundAmount, 클레임 조회의 expectedPointRefundAmount, 결제 내역 환불의 pointRefundAmount. refundAmount는 현금 환불만 뜻합니다. 적립금을 쓰지 않은 주문은 모두 0입니다.
  • 추가: 적립금 내역 유형 USE_RESERVE(결제 사용)·USE_RELEASE(결제 실패·만료로 되돌림)·REFUND_RESTORE(환불 반환), 적립 묶음 출처 REFUND_RESTORE.
  • 추가: store-sdk 순수 함수 pointUseOf·proratedPointRefund·splitRefund·pointRestoreExpiresAtOf, quotePricing 입력 points·결과 pointAmount. proratedPaidRefundBase는 적립금 몫(pointAllocation)을 뺀 현금 기준입니다.
  • 추가: 템플릿 0.2.11 주문서에 적립금 칸(적용·전액 사용·빼기)이 생겼습니다. 회원이고 상점이 사용을 켰을 때만 보입니다.

조건 자동 컬렉션 (@sayren/store-sdk 0.29.2)

필드 추가뿐입니다. 기존 직접 선택 컬렉션의 동작은 그대로입니다. 컬렉션

  • 추가: POST /v1/collections가 type: "AUTOMATIC"과 조건 rules(모두 만족, 최대 10개)를 받습니다. 조건은 등록일(createdWithinDays, 1~365일)·카테고리(category, 하위 포함, 20개까지)· 가격(price, 즉시할인 반영 판매가, min·max)·할인 중(discounted)입니다. 같은 조건은 한 번만 사용합니다. 조회할 때마다 조건을 만족하는 판매 중 상품을 계산하고 결과를 저장하지 않습니다. PATCH의 rules로 조건을 통째로 바꿉니다. 정렬을 생략하면 조건 자동은 LATEST이고 MANUAL은 받지 않습니다(400 COLLECTION_SORT_INVALID).
  • 추가: 컬렉션 응답의 rules(직접 선택은 null). 조건 자동의 productCount는 지금 조건을 만족하는 판매 중 상품 수이고, GET /v1/collections/{collectionId}/products는 그 상품을 기본 정렬 순으로 돌려줍니다(position은 순위, addedAt은 상품 등록 시각).
  • 추가: POST /v1/collections/preview(스코프 category:r) — 저장하지 않고 조건으로 나오는 상품 수와 앞 20개를 봅니다. 데이터를 바꾸지 않아 AI 에이전트의 쓰기 횟수에 세지 않습니다. SDK collections.preview, 순수 함수 matchesCollectionRules·collectionRulesPredicate.
  • 추가: 오류 400 COLLECTION_RULES_REQUIRED·COLLECTION_RULES_NOT_ALLOWED·COLLECTION_PRODUCTS_NOT_ALLOWED·COLLECTION_CATEGORY_NOT_FOUND·COLLECTION_SORT_INVALID. 조건 자동 컬렉션에 상품을 담거나 빼면 409 COLLECTION_NOT_MANUAL입니다.
  • 변경: 조건 자동 컬렉션이 조건으로 사용하는 카테고리는 지울 수 없습니다(DELETE /v1/categories/{categoryId} → 409 CATEGORY_IN_USE, error.details.collectionIds). strategy=cascade는 하위 카테고리까지 봅니다. 조건 자동 컬렉션이 없는 상점은 영향이 없습니다.
  • 변경: SDK collectionCreateRequestSchema가 productSort 기본값을 채우지 않습니다. 서버가 유형에 맞춰 채웁니다(직접 선택 MANUAL). 보내는 요청의 결과는 같습니다.
  • 스토어프론트 API 응답 형태는 그대로입니다. 조건 자동 컬렉션의 GET /collections/{slug}/products는 정한 순서가 없어 sort=manual을 기본 정렬로 봅니다.

적립금 기존 잔액 이전

API 형태는 그대로입니다. 잔액은 바뀌지 않습니다.

  • 변경: 적립금 원장 이전에 쌓인 잔액을 기존 적립금으로 옮깁니다. 구매자·상점 적립금 내역에 유형 OPENING(기존 적립금) 한 줄이 생기고, 잔액 조회의 적립 묶음(lots)에 출처 OPENING이 보입니다. 기존 적립금의 소멸 시각은 적립금 설정을 연 날 + 상점 유효 기간이고, 유효 기간을 설정하지 않은 상점은 설정할 때까지 소멸하지 않습니다. 적립금

적립금 — 리뷰 적립 기본값 끔·정책·구매 적립·소멸·조정 (@sayren/store-sdk 0.29.1)

동작 변경이 있습니다. 기존 필드의 이름과 형태는 그대로입니다. 적립금

  • 변경(동작 변경): 리뷰를 작성하면 주던 적립금(리뷰 500원, 사진 리뷰 500원 추가)을 더 주지 않습니다. 적립금은 상점이 정책을 설정해 켤 때만 지급하고, 정책이 없으면 모두 끔입니다. 리뷰 작성 응답(POST /me/order-items/{orderItemId}/review)의 rewardPoint는 정책이 꺼져 있으면 0입니다. 이미 지급된 적립금은 그대로이고, 구매자가 리뷰를 삭제하면 지금처럼 그 리뷰로 받은 금액을 회수합니다(잔액이 모자라면 0까지). 같은 리뷰를 동시에 두 번 삭제해도 한 번만 회수합니다.
  • 추가: GET /v1/store/point-policy(스코프 promotion:r)·PUT /v1/store/point-policy(promotion:rw). 리뷰 적립(reviewReward, 사진은 추가분)·구매 적립(purchaseEarn.rateBp, 만분율 03000)·유효 기간(expiryDays, 301825일)입니다. 적립을 하나라도 켜려면 유효 기간이 있어야 합니다(400 POINT_EXPIRY_REQUIRED). 적립금 설정이 준비 중이면 저장이 409 POINTS_UNAVAILABLE이고 조회의 available이 false입니다. 조회의 notice는 상점에 보일 안내입니다(REVIEW_REWARD_PAUSED·POINTS_AVAILABLE·null).
  • 추가: 상품의 pointEarnRateBp(등록·전체 수정·부분 수정·조회). null이면 상점 기본 적립률, 0이면 적립 안 함입니다. 전체 수정(PUT)에서 생략하면 지금 값을 유지합니다. 구매 적립은 구매확정 때 실결제 금액(배송비 제외) × 결제 시점 적립률이고, 비회원·테스트 결제 주문은 적립하지 않습니다.
  • 추가: 구매자 적립금 GET /v1/customers/{customerId}/points·/points/history(customer:r), 상점 전체 내역 GET /v1/points/history(promotion:r), 지급·차감 POST /v1/customers/{customerId}/points/adjustments(promotion:rw, Idempotency-Key 필수, 409 POINT_BALANCE_INSUFFICIENT). 소멸 시각이 지난 적립금은 소멸하고 내역에 EXPIRE로 남습니다.
  • 에이전트의 정책 수정·지급·차감·상품 적립률 변경은 승인 대상입니다. SDK store.getPointPolicy·updatePointPolicy, customers.getPoints·listPointHistory·adjustPoints, points.listHistory, 순수 함수 resolvePointPolicy·reviewRewardPointOf·purchaseEarnRateOf·pointEarnOf·pointExpiresAtOf.

결제창 없는 결제 완료·응답 결제수단 확장 (@sayren/storefront-sdk 0.20.0)

호환성 변경이 있습니다. 적립금(준비 중)으로 결제 금액 전체를 내는 주문을 위해 SDK를 먼저 바꿉니다. 지금 서버 응답은 그대로입니다.

  • 변경(호환성 변경): 주문 응답의 payment.method(GET /me/orders, 비회원 주문 조회)를 이 SDK가 모르는 값도 받는 문자열로 읽습니다. 적립금 전액 결제 주문은 POINT가 옵니다. 0.19 이하 SDK는 POINT를 받으면 주문 파싱에 실패하므로, 적립금을 켜기 전에 0.20 이상으로 올리십시오. TypeScript 타입은 ResponsePaymentMethod(PaymentMethod | string)입니다. 결제 옵션(요청)의 method는 그대로 enum입니다.
  • 변경(호환성 변경): 결제 시작·재시도 응답(POST /checkout/{checkoutId}/payment, POST /payments/{paymentId}/attempts)의 payUrl·attemptId·option이 nullable입니다. 결제창 없이 끝난 결제(status: "completed")에서 null입니다. 지금 서버는 늘 값을 싣습니다.
  • 추가: 결제 시작 응답의 status(pending 결제창을 엶 · completed 결제창 없이 결제 완료)와 orderId(completed일 때 주문 번호, 그 밖에는 null). 필드가 없는 응답은 pending·null로 읽습니다.
  • 변경: payments.start·retry·open은 결제 시작 응답이 completed면 결제창을 열지 않고(미리 연 팝업은 닫습니다) 바로 COMPLETED 결과를 돌려줍니다. 모르는 status나 payUrl이 없는 응답은 결제 상태 조회(GET /payments/{paymentId})로 결과를 정합니다. SDK 결과로 화면을 분기하는 코드는 그대로 동작합니다. payUrl을 직접 여는 코드는 status를 먼저 보십시오. 결제 흐름
  • 추가: 적립금 조회 GET /point-policy(인증 없음, 리뷰·구매 적립과 리뷰 작성·수정 기한 review)·GET /me/points(잔액·30일 안 소멸 예정)·GET /me/points/history. SDK catalog.getPointPolicy, member.getPoints·listPointHistory. 적립금

스토어프론트 템플릿 컬렉션 화면 (@sayren/mcp 0.23.1, 템플릿 0.2.10)

API 변경은 없습니다.

  • 추가: 템플릿 라우트 /collections/$slug(대표 이미지·제목·설명, 상품 그리드, 정렬 ?sort=·페이지 ?page=, 정렬을 생략하면 컬렉션 기본 정렬)와 /collections(노출 중인 컬렉션 모음). 404 COLLECTION_NOT_FOUND는 없는 화면으로 그립니다. 목록 보기 이벤트의 listId는 collection:{slug}입니다.
  • 추가: 레이아웃 설정(src/site/layout.json)의 사이트 안 경로에 /collections와 그 아래 경로를 쓸 수 있습니다. 옛 설정은 그대로 유효합니다.

컬렉션 (@sayren/store-sdk 0.29.0 · @sayren/storefront-sdk 0.19.0)

새 리소스입니다. 기존 필드의 이름과 형태는 그대로입니다.

  • 추가: 관리 API /v1/collections(스코프 category:r·category:rw). 목록(q·type·state·productId, 응답 stateCounts)·만들기·조회· 수정(PATCH, 준 항목만)·삭제와 담긴 상품 보기·바꾸기(PUT)·붙이기(POST, 500개)·빼기(DELETE …/products/{productId}). 지금은 직접 선택(MANUAL)만 만들고 유형은 바꿀 수 없습니다. 쓰기마다 revision이 오르고, 본문에 읽은 revision을 주면 그 사이 바뀐 경우 409 COLLECTION_CHANGED입니다. 상점당 200개, 컬렉션당 상품 1,000개입니다. 상품을 삭제하면 컬렉션에서도 빠집니다. SDK collections.*. 컬렉션
  • 추가: 스토어프론트 API GET /collections·GET /collections/{slug}·GET /collections/{slug}/products. 노출이 켜져 있고 기간 안인 컬렉션만 보이고, 숨김·기간 밖·없음은 모두 404 COLLECTION_NOT_FOUND입니다. 상품은 판매 중만 ProductCard 페이지로 오고 sort(manual과 상품 검색 정렬)·minPrice· maxPrice·page·size를 받습니다. SDK catalog.listCollections·getCollection·listCollectionProducts. 컬렉션
  • 추가: 오류 코드 COLLECTION_NOT_FOUND·COLLECTION_SLUG_TAKEN·COLLECTION_LIMIT_EXCEEDED·COLLECTION_PRODUCT_LIMIT_EXCEEDED·COLLECTION_CHANGED· COLLECTION_TYPE_IMMUTABLE·COLLECTION_PERIOD_INVALID·COLLECTION_PRODUCT_NOT_FOUND·COLLECTION_NOT_MANUAL.

원격 MCP 템플릿 원본 읽기 (@sayren/mcp 0.23.0)

API 변경은 없습니다.

  • 추가: 원격 MCP(https://mcp.sayren.app/mcp)의 list_template_files·get_template_file. 호스팅 카탈로그 템플릿의 지금 버전(새 사이트의 첫 작업 트리와 같은 파일)을 읽기만 합니다. 입력 templateId(생략하면 기본 템플릿)·version(지금 버전만, 다르면 오류)을 받고, 목록 응답에 templateId·version· templates(고를 수 있는 템플릿)가 옵니다. 이진 파일은 path·encoding(base64)·content JSON입니다. 원격 MCP
  • 변경: 로컬 MCP(@sayren/mcp)의 두 도구는 같은 입력 이름을 갖지만 npm 패키지에 든 템플릿 하나만 줍니다. templateId·version을 주면 원격 MCP를 안내하는 오류입니다. 인자 없이 부르는 기존 호출은 그대로입니다.

스토어프론트 CLI (sayren 0.4.0)

API 변경은 없습니다.

  • 추가: npx sayren init [폴더] [--template <id>] [--store <storeId>] [--yes]. 템플릿을 골라 호스팅 사이트를 만들고(POST /v1/storefront/site) pull과 같은 폴더(.mcp.json·.env·.sayren/state.json)로 받습니다. 사이트가 이미 있으면 만들지 않고 pull을 안내합니다. 비대화형은 --template과 --yes가 필요합니다. 사이트 만들기
  • 변경: npx sayren deploy가 올리기 전에 로컬 빌드를 합니다. 의존성이 설치된 폴더(node_modules가 있는 폴더)면 package.json의 build 스크립트를 npm run build로 돌리고, 실패하면 빌드 출력의 마지막 줄들을 보여 주고 올리지 않습니다(종료 코드 1). 의존성을 설치하지 않은 폴더는 알리고 건너뜁니다. --skip-build로 끕니다. 서버 빌드는 그대로 합니다. CLI로 호스팅 사이트 고치기

결제수단 반복 실패 경고 (@sayren/store-sdk 0.28.3)

필드 추가만 있습니다. 기존 필드의 이름과 형태는 그대로입니다.

  • 추가: 결제 설정 조회(GET /v1/store/payment-settings)의 warnings. 같은 PG·결제수단·간편결제사가 PG 설정 문제(구매자가 고칠 수 없는 실패)로 최근 24시간에 3번 이상 실패한 조합이고, 항목은 provider·method·easyPayProvider·code(PG 실패 코드, 없으면 null)·count·lastAt· message(마지막 PG 원문 사유)입니다. 마지막 실패가 최근인 순이고 없으면 빈 배열입니다. 판정 기준은 바뀔 수 있습니다. SDK 타입 PaymentFailureWarning. PG 연결하기

출고지·반품지 주소록 조회 개방

  • 변경: 주소록 목록(GET /v1/seller-addresses)·상세(GET /v1/seller-addresses/{addressId})를 store:r 스코프만으로 부를 수 있습니다. 전에는 상점 플랫폼으로 로그인한 소유자·관리자만 불렀고, 액세스 토큰·연동 앱·AI 에이전트는 403 INSUFFICIENT_ROLE이었습니다. 응답 형태는 그대로입니다. 등록·수정·삭제·기본 지정은 지금처럼 상점 플랫폼으로 로그인한 소유자·관리자만 합니다. 배송비와 출고지·반품지

템플릿 링크 미리 불러오기·자산 캐시 (@sayren/mcp 0.22.2)

API 변경은 없습니다.

  • 변경: create로 받는 스토어프론트 템플릿(0.2.9)의 라우터가 defaultPreload: "intent", defaultPreloadStaleTime: 30000입니다. 링크에 마우스를 올리거나 포커스하면 다음 화면의 loader가 스토어프론트 API를 미리 부르고, 30초 안에 같은 링크로 다시 부르지 않습니다. 주문서(/checkout)·주문 완료 라우트는 preload: false입니다. loader에서 주문서 세션 생성처럼 상태를 바꾸는 호출을 하면 그 라우트에 preload: false를 두십시오.
  • 추가: 템플릿 public/_headers. 해시가 붙은 빌드 파일(/assets/*)에 Cache-Control: public, max-age=31536000, immutable을 줍니다. HTML과 해시 없는 파일(파비콘 등)은 그대로 max-age=0, must-revalidate입니다. public/assets/에는 파일을 두지 마십시오.

결제 실패의 결제수단 사용 불가 안내 (@sayren/storefront-sdk 0.18.4)

필드 추가만 있습니다. 기존 필드의 이름과 형태는 그대로입니다.

  • 추가: 결제 상태(GET /payments/{paymentId})의 lastFailure.optionUnavailable. 상점의 PG 설정 문제처럼 구매자가 고칠 수 없는 실패면 true이고, 이때 lastFailure.message는 「이 결제수단은 지금 사용할 수 없습니다. 다른 결제수단을 선택하십시오」입니다. 같은 결제 옵션으로 다시 시도해도 실패하므로 다른 결제수단을 고르게 하십시오. PG 원문 사유는 상점 플랫폼 결제 내역에 남습니다. SDK 결제 결과(FAILED)에도 optionUnavailable이 실립니다. 결제 흐름

리뷰 랭킹순 정렬 기준

  • 변경: 스토어프론트 상품 리뷰 목록(GET /products/{productId}/reviews)의 sort=helpful(랭킹순)이 평점 높은 순과 같던 것을 바꿨습니다. 사진 있는 리뷰를 먼저, 그다음 평점 높은 순, 평점이 같으면 최신 순입니다. 도움돼요 수는 반영하지 않습니다. 정렬 값(latest·ratingDesc·ratingAsc·helpful)은 그대로입니다. 카탈로그

2026-10-01

상점 정보 수정 드라이런의 변경 미리보기

  • 변경: 상점 정보 수정(PATCH /v1/store)의 드라이런(x-sayren-dry-run: 1)과 승인 요청의 preview.changes에 변경 전후를 싣습니다. 본문에 준 필드 중 값이 달라지는 것만 오고, 배송비·환불 정책은 항목마다(shippingPolicy.freeThreshold 등) 나뉩니다. 판정은 그대로 승인 대기입니다. 드라이런

리뷰 옵션별 필터·내가 쓴 리뷰·리뷰 사진 (@sayren/storefront-sdk 0.18.3)

필드 추가만 있습니다. 기존 필드의 이름과 형태는 그대로입니다.

  • 추가: 스토어프론트 상품 리뷰 목록(GET /products/{productId}/reviews)의 쿼리 variantId와 리뷰 항목의 variantId. 상품 상세 variants[].variantId와 같은 값이고, 주면 그 옵션(조합)으로 산 리뷰만 옵니다. 주문 정보를 찾지 못한 리뷰는 null입니다. summary는 필터와 무관하게 상품 전체 기준입니다. SDK catalog.listProductReviews(productId, { variantId }). 카탈로그
  • 추가: 스토어프론트 GET /me/reviews(구매자 로그인) — 내가 쓴 리뷰를 최신순으로 돌려줍니다. 항목은 reviewId·productId·productName· thumbnailUrl·optionName·rating·content·images·sellerReply·blinded·editable(작성 후 30일까지)·editableUntil·createdAt입니다. SDK reviews.listMine({ page, size }). 회원 · 리뷰 · 문의
  • 추가: 스토어프론트 POST /me/uploads(구매자 로그인) — 리뷰 사진 업로드 자리를 발급합니다. 본문은 filename·purpose(review-image, 생략 가능)· size이고 응답은 url·uploadUrl입니다. uploadUrl에 파일을 PUT한 뒤 url을 리뷰 작성·수정의 images에 넣습니다. 형식·크기는 상품 이미지와 같고(400 UNSUPPORTED_FILE_TYPE·413 FILE_TOO_LARGE), 회원마다 발급 한도가 있습니다(429 TOO_MANY_REQUESTS). SDK reviews.createImageUpload.

상품 이미지 일부 수정, 드라이런 본문 검증

  • 추가: PATCH /v1/products/{productId}의 images. 준 목록으로 이미지를 통째로 바꾸고 첫 번째가 대표입니다. 항목 형식은 등록과 같습니다. 에이전트 정책은 그대로이고 이미지만 바꾸면 승인 없이 실행됩니다. 상품 이미지 등록
  • 변경: x-sayren-dry-run: 1 요청도 실행과 같은 본문 검증을 거칩니다. 형식이 맞지 않으면 판정 대신 400 VALIDATION_FAILED입니다.

MCP 상품 이미지 업로드 (@sayren/mcp 0.22.0)

  • 추가: MCP 도구 upload_image(로컬·원격 공통, product:rw). 입력은 sourceUrl(https) 또는 base64 + contentType 중 하나와 선택 filename이고, 출력은 url·contentType·size입니다. POST /v1/uploads(purpose: product-image)로 자리를 받아 올리므로 형식·크기 제한이 같습니다. MCP 도구 레퍼런스

사업자 정보·상품 정가·상품 문의 유형 (@sayren/store-sdk 0.28.2 · @sayren/storefront-sdk 0.18.2)

필드 추가만 있습니다. 기존 필드의 이름과 형태는 그대로입니다.

  • 추가: PATCH /v1/store·GET /v1/store의 사업자 정보 — businessName(상호)·representativeName(대표자명)·mailOrderSalesNumber (통신판매업 신고번호)·businessAddress(사업장 주소)·customerCenterEmail(고객센터 이메일)·privacyOfficerName·privacyOfficerEmail (개인정보 보호책임자). 정하지 않았으면 null이고, PATCH에 null을 주면 지웁니다.
  • 추가: PATCH /v1/store의 businessNumber(사업자등록번호). 숫자 10자리이고 하이픈을 넣어도 됩니다. 숫자만 저장합니다. 형식이 틀리면 400입니다. customerCenterPhone은 null로 지울 수 있습니다.
  • 추가: 스토어프론트 GET /store의 businessName·representativeName·businessNumber·mailOrderSalesNumber·businessAddress· customerCenterEmail·privacyOfficerName·privacyOfficerEmail. 푸터 표기용입니다. 상점 공개 설정
  • 추가: 스토어프론트 상품 목록·상세(GET /products·GET /products/{productId}, 찜 목록)의 originalPrice(정가). 비교용 표시 값이고 결제 금액은 discountedPrice ?? salePrice입니다. 정가가 없으면 null입니다. 가격 표시
  • 추가: 스토어프론트 상품 문의 목록(GET /products/{productId}/inquiries)과 내 상품 문의(GET /me/inquiries)의 productId·productName· thumbnailUrl(상품 대표 이미지)·category(문의 유형). 상품이 지워졌으면 productName·thumbnailUrl은 null입니다.
  • 추가: 상품 문의 작성(POST /products/{productId}/inquiries)의 category(PRODUCT·DELIVERY·RETURN·EXCHANGE·ETC). 생략하면 PRODUCT입니다.
  • MCP 템플릿: 상품 카드·상세가 정가 취소선과 정가 기준 할인율을 표시하고, 푸터가 사업자 정보를 표시합니다(값이 없는 항목은 표시하지 않습니다).

원격 MCP에서 상점 정보 수정 요청

  • 변경: 원격 MCP(https://mcp.sayren.app/mcp) 연결이 store:rw를 받을 수 있습니다. 보호 리소스 메타데이터 scopes_supported에 더했습니다. 상점 정보 수정(PATCH /v1/store)은 AI 에이전트 요청이면 승인 대기(202 APPROVAL_REQUIRED)입니다. 이미 연결한 도구는 다시 연결해야 받습니다.
  • 변경: 구매자 로그인 설정(/v1/store/login-settings·/v1/store/auth-providers/**)에 x-console-only: true를 더했습니다. 상점 플랫폼에서만 합니다.
  • 변경: AI 에이전트 요청 중 승인해도 실행되지 않던 쓰기를 승인 대기 대신 403 AGENT_OPERATION_DENIED로 거부합니다 — 구매자 로그인 방식 변경 (PATCH /v1/store/login-settings·PATCH /v1/store/auth-providers/{provider}), 약관 버전·주소(termsVersion·privacyVersion·termsUrl·privacyUrl)를 바꾸는 상점 수정. 약관 필드가 없는 상점 수정은 그대로 승인 대기입니다.
  • MCP: list_operations가 토큰에 없는 스코프가 필요한 오퍼레이션에 토큰 스코프 없음을 붙이고, describe_operation이 missingTokenScopes를 줍니다. 403 INSUFFICIENT_ROLE 결과에 다시 연결해 권한을 늘리라는 다음 할 일을 붙입니다.

요금제 개편 — Free·Pro·프리미엄·엔터프라이즈 (@sayren/store-sdk 0.28.0 · @sayren/mcp 0.20.0)

요금제를 4단계로 다시 나눴습니다. 공개 API의 변경은 가산입니다. 베타 기간에는 새 한도를 관측만 합니다. 요금제와 한도

  • 추가: GET /v1/public/plans — 인증 없는 요금제 비교표(가격·부가세 포함 표시가·기능 키 값). 5분 캐시할 수 있습니다. store-sdk plans.listPublic(). 요금제 값은 바뀔 수 있으니 코드에 수치를 두지 말고 이 응답을 읽으십시오. 요금제 비교표 API
  • 추가: 요금제 코드 PREMIUM. BASIC·BUSINESS는 더 쓰지 않습니다(베이직 → Pro, 프로 → 프리미엄, 비즈니스 → 엔터프라이즈로 옮겼습니다).
  • 호환성 주의: @sayren/store-sdk 0.27.0 이하는 PREMIUM 코드를 몰라 프리미엄 상점에서 subscription.get()의 응답 검증이 실패합니다. 0.28.0으로 올리십시오. 0.28.0부터 응답의 요금제 코드(plan.code, 오류 details.plan)와 지원 등급은 모르는 값도 문자열로 받습니다.
  • 추가: GET /v1/store/subscription의 limits.aiReadsMonthly·storefrontRequestsMonthly·apiLogRetentionDays·auditLogRetentionDays, features.adminApi·analytics, usage.aiReads·storefrontRequests, aiReads(MCP 읽기 한도 요약).
  • 변경: 라이브 주문·메일·소셜 로그인 공급자·구성원 석은 한도가 없습니다. limits.liveOrdersMonthly·emailsMonthly·socialProviders·seatsIncluded는 null이고 features.storeClone·removeBadge는 항상 false입니다. 필드는 그대로 둡니다.
  • 추가: 403 PLAN_FEATURE_REQUIRED — 관리 API(액세스 토큰·외부 앱)와 애널리틱스 조회는 Pro부터입니다. 상점 플랫폼·CLI·MCP는 모든 요금제에서 씁니다.
  • 추가: 429 PLAN_AI_READS_EXCEEDED — MCP 읽기 월 한도. MCP 요청은 관리 API 요청으로 세지 않고 MCP 읽기·쓰기로 셉니다.
  • 변경: 계정당 상점은 요금제와 무관하게 3개입니다(409 STORE_LIMIT_EXCEEDED, details.reason: OWNED_STORES). 계정당 무료 상점 1개 규칙 (FREE_STORES)은 없앴습니다.
  • 변경: 웹훅 구독 상한은 요금제 값입니다(Free 0·Pro 5·프리미엄 20). API 로그·감사 로그 보관 기간은 요금제 값입니다.
  • 없어짐: 상점 복제(상점 플랫폼). 새 상점은 템플릿이나 빠른 시작으로 만듭니다.
  • MCP 0.20.0: get_store_context의 plan.aiReads(남은 MCP 읽기 수). MCP 읽기 한도 오류에 다음 할 일을 붙입니다.

파괴적 변경 — 셀러 토큰을 Sayren 계정 토큰으로 일원화 (@sayren/store-sdk 0.27.0 · @sayren/mcp 0.19.0 · sayren 0.3.0)

관리 API는 Sayren 계정이 발급한 토큰(로그인 토큰·액세스 토큰)만 받습니다. 옛 토큰과 인증 경로는 함께 쓰는 기간 없이 끊었습니다. 인증과 계정 토큰

  • 호환성 변경: 없어진 토큰 — storeToken, 상점 API 토큰(sy_pat_…), client_credentials(서버 간 연동)로 받은 토큰. 이 토큰으로 부르면 401 TOKEN_TYPE_RETIRED입니다.
  • 호환성 변경: 없어진 경로 — /v1/oauth2/authorize·/v1/oauth2/token(authorization_code·refresh_token·token-exchange·client_credentials)· /v1/oauth2/revoke·/v1/oauth2/logout·/v1/oauth2/register(클라이언트 등록), /v1/store/api-tokens/**, /v1/store/api-clients/**. 부르면 410 TOKEN_TYPE_RETIRED입니다. /.well-known/oauth-authorization-server와 /.well-known/jwks.json도 없어졌습니다.
  • 호환성 변경: 구매자 위임 token-exchange(member-uid·member-ref)를 없앴습니다. POST /v1/customers/delegations를 쓰십시오.
  • 호환성 변경: 관리 API의 상점 리소스 요청에는 X-Store-Id가 필요합니다. 없으면 400 STORE_ID_REQUIRED, 토큰의 상점 범위 밖이면 403 STORE_NOT_GRANTED입니다.
  • 상점 플랫폼: 개발자 › 자격 증명 › API 토큰·앱이 없어졌습니다. 액세스 토큰은 Sayren 계정 › 액세스 토큰, 로그인으로 연결한 앱은 Sayren 계정 › 연결된 앱에서 관리합니다.
  • 추가: GET /.well-known/oauth-protected-resource(RFC 9728). 관리 API의 인가 서버(authorization_servers)는 Sayren 계정입니다.
  • store-sdk 0.27.0: createStoreClient({ baseUrl, accessToken, storeId }). 옵션 storeToken·userToken·auth.apiToken, apiTokens·apiClients, auth.issueStoreTokenByClientCredentials·auth.revoke, exchangeStoreTokenViaOAuth를 없앴습니다. createStoreSession은 authServer (Sayren 계정의 authorizeUrl·tokenUrl·revokeUrl과 resource)가 필요하고, 가입 화면은 prompt: "create"입니다(screen: "signup" 삭제). @sayren/store-sdk/server는 delegateMember만 내보냅니다(delegateMemberByUid·delegateMemberByEmail 삭제). Store SDK
  • 호환성 변경: MCP 0.19.0 — SAYREN_TOKEN은 Sayren 계정 액세스 토큰만 받습니다. 옛 API 토큰(sy_pat_…)을 넣으면 API를 부르지 않고 오류로 안내합니다. 다룰 상점은 늘 정해야 하고 SAYREN_STORE_ID로 고릅니다(토큰의 상점 범위가 하나면 생략). sayren-mcp create가 쓰는 .mcp.json에는 원격 MCP 주소와 상점 id만 있고 토큰은 없습니다. MCP 연결하기
  • 호환성 변경: CLI 0.3.0 — 토큰은 --token → SAYREN_TOKEN → 로그인 정보(npx sayren login, 브라우저·기기 코드) 순서로 읽고 .mcp.json의 토큰은 읽지 않습니다. 옛 API 토큰(sy_pat_…)은 받지 않고 로그인이나 액세스 토큰을 안내합니다. 상점은 명령마다 고릅니다(--store· SAYREN_STORE_ID, 없으면 폴더 상태·토큰의 상점 범위·선택). .mcp.json에는 토큰을 쓰지 않고, 남아 있던 옛 토큰 헤더와 .env의 옛 SAYREN_TOKEN 줄은 지웁니다. CLI로 호스팅 사이트 고치기
  • 호환성 변경: 원격 MCP는 Sayren 계정 토큰만 받습니다. 옛 API 토큰·storeToken은 401 invalid_token입니다. 상점은 X-Store-Id로 정하고, 없으면 허락한 상점이 하나일 때만 연결합니다.
  • 추가: 원격 MCP 동적 클라이언트 등록. OAuth를 지원하는 AI 도구는 토큰 없이 주소만으로 연결하고, 처음 연결할 때 Sayren 계정 로그인과 권한 허용을 거칩니다. 이렇게 등록한 앱은 확인하지 않은 앱으로 표시되고 구성원 조회·관리, 상점 설정·웹훅 변경 권한은 받을 수 없습니다. 30일 동안 쓰지 않은 연결은 정리됩니다. 설치 없이 MCP 연결하기
  • 스토어프론트 API(X-Store-Code, 구매자 토큰)와 @sayren/storefront-sdk는 바뀌지 않았습니다.

옮기는 순서입니다.

  1. Sayren 계정 › 액세스 토큰에서 필요한 권한과 상점 범위로 액세스 토큰을 만듭니다. 구매자 위임에 쓸 토큰은 구매자 위임을 켭니다.
  2. 요청의 Authorization: Bearer를 새 토큰으로 바꾸고 X-Store-Id를 싣습니다.
  3. @sayren/store-sdk 0.27.0, @sayren/mcp 0.19.0, sayren 0.3.0으로 올립니다.

2026-09-30

주문 가능 상태·공개 조건·샘플 상품 (@sayren/storefront-sdk 0.18.0 · @sayren/store-sdk 0.25.0)

  • 추가: 스토어프론트 GET /store의 checkoutAvailable·checkoutMode·checkoutUnavailableReason. checkoutAvailable이 false면 결제 시작이 409 PAYMENT_NOT_CONFIGURED이니 주문 버튼을 숨기고 안내를 보이십시오. 옛 SDK는 이 필드를 모르고, 새 SDK는 필드가 없는 응답을 주문 가능으로 읽습니다. 결제를 받을 수 없는 상태
  • 추가: 결제 시작 409 PAYMENT_NOT_CONFIGURED의 error.details.reason(NO_PROVIDER·NOT_LIVE). 코드는 그대로입니다.
  • 변경: 샌드박스 PG만 있는 상점이 호스팅 쇼핑몰을 공개하면 결제(테스트 결제 포함)를 받지 않습니다(PAYMENT_NOT_LIVE).
  • 추가: 호스팅 공개 전환 409 STOREFRONT_NOT_READY — 판매 중인 상품(상점을 만들 때 넣은 샘플 제외)이 없으면 공개하지 않습니다. 공개하기
  • 추가: 관리 API 상품 응답의 sample(상점 플랫폼 빠른 시작이 넣은 샘플 상품이면 true). 스토어프론트 응답에는 없습니다.

유료 요금제 결제

상점 플랫폼에서 요금제를 고르고 카드로 결제합니다. 공개 API의 변경은 오류 코드 추가뿐입니다. 요금제와 한도

  • 추가: 402 STORE_PAYMENT_REQUIRED — 첫 결제를 기다리는 상점(계정당 무료 상점 1개를 넘어 유료로 시작한 상점)의 관리 API 요청.
  • 추가: 403 SEAT_SUSPENDED — 무료 요금제로 바뀌며 석이 멈춘 구성원의 요청.

계정 토큰 (@sayren/store-sdk · sayren CLI · @sayren/mcp)

Sayren 계정에 발급되는 토큰으로 관리 API를 부릅니다. 모든 변경은 가산이고 기존 API 토큰·storeToken은 그대로 동작합니다. 계정 토큰

  • 추가: 관리 API가 Sayren 계정 토큰(로그인 토큰·액세스 토큰)을 받습니다. 상점은 X-Store-Id로 고르고, 권한은 역할과 토큰 권한 중 좁은 쪽입니다. 오류 코드 STORE_NOT_GRANTED(403, 토큰 상점 범위 밖)·TOKEN_REVOKED(401)를 더했습니다. GET /v1/me·GET /v1/me/stores도 계정 토큰을 받습니다.
  • 추가: 구매자 위임 POST /v1/customers/delegations(권한 customer:rw + 토큰 권한 member:delegate). 계정 액세스 토큰과 Sayren 상점 플랫폼·CLI 로그인만 받고 다른 앱·AI 에이전트는 DELEGATION_NOT_ALLOWED(403)입니다. store-sdk customerDelegationRequestSchema· customerDelegationSchema, @sayren/store-sdk/server의 delegateMember.
  • CLI: sayren login이 브라우저·기기 코드로 Sayren 계정에 로그인하고 logout·whoami를 더했습니다. 로그인한 폴더의 .mcp.json에는 토큰을 쓰지 않습니다. CLI로 호스팅 사이트 고치기
  • 원격 MCP: Sayren 계정 로그인 창(OAuth)으로 연결합니다. stdio MCP는 SAYREN_STORE_ID를 읽어 X-Store-Id를 싣습니다. 원격 MCP

요금제·사용량 조회 (@sayren/store-sdk 0.24.0 · @sayren/mcp 0.17.0)

베타 기간의 요금제·한도·사용량을 공개 API로 봅니다. 모든 변경은 가산입니다. 요금제와 한도

  • 추가: GET /v1/store/subscription(스코프 store:r) — 요금제(plan.code·plan.name), 상태, 사용량 기간, 한도(limits), 이번 기간 사용량(usage), AI 작업 요약(aiActions), 판매 수수료(salesFeePercent, 항상 0). store-sdk subscription.get()· storeSubscriptionSchema·planErrorDetailsSchema.
  • 추가: 한도 오류 코드 PLAN_AI_ACTIONS_EXCEEDED(429)·PLAN_API_QUOTA_EXCEEDED(429, Retry-After)·PLAN_LIMIT_EXCEEDED(409)· PLAN_FEATURE_UNAVAILABLE(403)·SEAT_PAYMENT_REQUIRED(409). 베타 기간에는 구성원 석·AI 작업·관리 API 스로틀·상점 복제 기능· 소셜 로그인 공급자 수·웹훅 수 한도를 기록만 하고 막지 않습니다.
  • 변경: 편집 미리보기 시간·빌드 시간·초대·양도 메일 수에 요금제 월 한도가 있습니다. 넘으면 새 편집 환경·새 버전은 409 PLAN_LIMIT_EXCEEDED이고 메일은 보내지 않습니다(deliveryStatus: FAILED, deliveryErrorCode: MAIL_QUOTA_EXCEEDED, 초대·양도 요청은 만들어집니다).
  • 추가: 기존 한도 오류(STORE_LIMIT_EXCEEDED·STOREFRONT_DOMAIN_LIMIT·WEBHOOK_LIMIT_EXCEEDED)의 details에 feature·limit·used· plan·upgradeUrl, 상점 수 한도에는 reason(OWNED_STORES·FREE_STORES). 코드와 상태는 그대로입니다.
  • 추가: 초대 발송 실패 코드 deliveryErrorCode: MAIL_QUOTA_EXCEEDED.
  • MCP(@sayren/mcp 0.17.0): get_store_context에 요금제와 남은 AI 작업 수(plan.aiActions.remaining). AI 작업 한도·편집 한도 오류에 다음 할 일을 붙입니다.

스토어프론트 CLI·원격 MCP: 받은 폴더에 .mcp.json (sayren 0.2.0 · @sayren/mcp 0.16.0)

  • 변경: npx sayren pull이 받은 폴더에 sayren 원격 MCP 연결 설정 .mcp.json(Claude Code 형식, 토큰·상점 id 포함)을 씁니다. .sayrenrc는 없어지고 상태는 .sayren/state.json에 둡니다. 토큰은 .env가 아니라 .mcp.json에서 읽습니다(--token → SAYREN_TOKEN → .mcp.json). 옛 폴더는 다음 명령에서 한 번 옮깁니다. .mcp.json·.sayren/은 .gitignore에 더하고 배포에 올리지 않습니다. sayren-mcp create도 토큰을 주면 같은 .mcp.json을 씁니다. CLI로 호스팅 사이트 고치기
  • 추가: 원격 MCP의 선택 헤더 X-Store-Id. 토큰의 상점과 다르면 403입니다. 원격 MCP
  • 변경: 관리 API가 API 토큰(sy_pat_…) 요청의 X-Store-Id도 토큰의 상점과 대조합니다. 다르면 403 STORE_ID_MISMATCH입니다 (storeToken과 같은 규칙). 헤더를 보내지 않으면 전과 같습니다.

store-sdk: 세션 로그아웃이 서버 토큰을 폐기합니다 (@sayren/store-sdk 0.23.0)

  • 추가: createStoreSession의 logout(). refresh 토큰(갱신 체인 전체)과 access 토큰을 POST /v1/oauth2/revoke로 폐기한 뒤 세션을 지웁니다. 폐기 요청이 실패해도 세션은 지웁니다. 지금까지 clear()로 로그아웃했다면 logout()으로 바꾸십시오 — clear()는 저장한 값만 지워 서버의 refresh 토큰이 만료까지 유효하게 남습니다. store SDK

스토어프론트 호스팅: 배포 진행 조회·템플릿 변경 폐기

  • 호환성 변경: 템플릿 변경 POST /v1/storefront/site/template을 폐기했습니다. 경로는 남아 있고 본문과 관계없이 항상 409 TEMPLATE_CHANGE_NOT_SUPPORTED로 답하며 사이트·버전을 바꾸지 않습니다. 템플릿은 사이트를 만들 때(POST /v1/storefront/site, templateId 필수)만 고릅니다. store-sdk storefront.changeTemplate은 @deprecated로 남겨 두었습니다. 스토어프론트 호스팅
  • 변경: 상점을 만들어도 호스팅 사이트가 생기지 않습니다. GET /v1/storefront/site의 site는 사이트를 만들기 전까지 null이고, POST /v1/storefront/site가 사이트를 만드는 유일한 방법입니다. 이미 만들어진 사이트는 그대로입니다.
  • 추가: 배포 진행 GET /v1/storefront/site/deployment(단계 SITE·UPLOAD·PUBLISH, 기록, 실패 코드, 다음 자동 시도)와 다시 시도 POST /v1/storefront/site/deployment/retry(202). store-sdk storefront.getDeployment·retryDeployment.
  • 새 오류 코드: TEMPLATE_CHANGE_NOT_SUPPORTED.

상품 옵션: 추가 선택·직접 입력·저장 옵션 (@sayren/store-sdk 0.21.0 · @sayren/storefront-sdk 0.17.0 · @sayren/mcp 0.14.0)

옵션명마다 조합과 추가 선택 중 하나를 고르고, 구매자가 글자를 적는 직접 입력 항목을 둘 수 있습니다. 모든 변경은 가산이고 기존 필드의 형태는 그대로입니다. 상품 옵션 구성

  • 추가: 관리 API 상품·옵션 모델에 addonGroups(추가 선택, 재고 없이 값별 추가금)·customInputs(직접 입력)·optionSort(값 정렬 순서). PUT /v1/products/{productId}·PUT /v1/products/{productId}/options에서 생략하면 현재 값을 유지합니다. optionGroups·variants는 조합만 담습니다.
  • 추가: 조합 응답 variants[].hasOrders. 주문에 담긴 조합이면 true이고, 이 조합은 교체 목록에서 뺄 수 없습니다(409 OPTION_HAS_ORDERS).
  • 추가: 저장 옵션 /v1/saved-option-sets(목록·만들기·조회·수정·삭제, 상품에서 저장 POST /v1/saved-option-sets/from-product). SDK는 savedOptionSets입니다. 상품 등록·수정 본문의 sourceOptionSetIds는 불러온 저장 옵션을 기록합니다.
  • 추가: 스토어프론트 상품 상세의 addonGroups·customInputs. 장바구니 담기·수정과 바로 구매가 addons·customInputs를 받고, 장바구니·주문서·주문 상품 줄에 고른 값을 싣습니다. 추가 선택·직접 입력값이 다르면 다른 줄입니다.
  • 추가: 주문서 줄과 쿠폰 결과의 lineId. 같은 조합이 여러 줄에 있으면 상품 쿠폰을 걸 줄을 lineId로 지목합니다.
  • 추가: 관리 API 주문 상품의 optionSelections·customInputs(주문 시점 이름·값), 웹훅 ORDER.PAID의 itemOptions.
  • 추가: store-sdk CUSTOM_INPUT_FORBIDDEN_WORDS, storefront-sdk customInputLength·normalizeCustomInputValue.
  • 새 오류 코드: ADDON_OPTION_REQUIRED·ADDON_OPTION_NOT_FOUND·CUSTOM_INPUT_REQUIRED·CUSTOM_INPUT_TOO_LONG·CUSTOM_INPUT_NOT_FOUND· CUSTOM_INPUT_LABEL_FORBIDDEN·OPTION_SET_NAME_TAKEN·OPTION_SET_LIMIT_EXCEEDED·OPTION_ADDONS_UNAVAILABLE. OPTION_ADDONS_UNAVAILABLE는 추가 선택·직접 입력이 아직 열리지 않았을 때 비어 있지 않은 값을 보내면 나옵니다.
  • 스토어프론트 템플릿(@sayren/mcp 0.14.0): 상품 상세에 추가 선택·직접 입력을 두고 장바구니·주문서·주문 화면이 줄마다 옵션을 보여 줍니다. verify_storefront 규칙 custom-input-private를 더했습니다(직접 입력값을 URL·브라우저 저장소·로그에 두지 않음). 규칙은 20개입니다.

상품 유형 축소 (@sayren/store-sdk 0.20.0 · @sayren/storefront-sdk 0.16.0 · @sayren/mcp 0.13.0)

비실물 상품의 세부 유형을 없애고 상품 유형을 배송 상품(SHIPPING)과 배송 없는 상품(MANUAL) 둘로 줄였습니다. 배송 없는 상품은 판매자가 제공 처리(POST /v1/order-items/{orderItemId}/fulfill)로 제공 완료를 기록하는 한 가지 흐름입니다. 비실물 상품 판매하기

  • 호환성 변경: fulfillment.type의 DOWNLOAD·CODE·SERVICE·PICKUP을 없앴습니다. 요청에 보내면 400입니다. 배송 없는 상품은 { "type": "MANUAL" }이고 유형 속성이 없습니다. 주문 상품의 fulfillmentSnapshot도 SHIPPING·MANUAL 둘입니다.
  • 호환성 변경: 결제와 함께 자동으로 제공하는 방식(AUTO)을 없앴습니다. 모든 배송 없는 상품은 판매자가 제공 처리합니다. 제공 처리 본문은 note만 받습니다(codes 삭제).
  • 호환성 변경: 코드 발급을 없앴습니다. 코드 업로드·목록·폐기(/v1/products/{productId}/codes/**), 셀러 코드 열람(/v1/fulfillment-codes/{codeId}/reveal), 구매자 코드 열람(/storefront/v1/me/order-items/{orderItemId}/fulfillment/reveal, 비회원 …/guest/orders/{orderId}/order-items/{orderItemId}/fulfillment/reveal)이 없어졌습니다. SDK의 products.uploadCodes·listCodes·voidCode, fulfillmentCodes.reveal, myOrders.revealCode·revealGuestCode도 없앴습니다.
  • 호환성 변경: 파일 다운로드를 없앴습니다. 판매 파일(/v1/products/{productId}/digital-assets/**), 업로드 용도 digital-asset, 구매자 다운로드 (/storefront/v1/me/order-items/{orderItemId}/downloads, 비회원 …/guest/orders/{orderId}/order-items/{orderItemId}/downloads)가 없어졌습니다. 업로드 응답의 key·headers도 없앴습니다. SDK의 products.attachDigitalAsset·listDigitalAssets·removeDigitalAsset·previewDigitalAsset, myOrders.createDownload·createGuestDownload도 없앴습니다.
  • 호환성 변경: 방문 수령을 없앴습니다. 주소록 유형 PICKUP과 등록된 방문 수령지가 없어졌습니다(주소록 유형은 SHIPPING·RETURN).
  • 호환성 변경: 청약철회 제한 고지·동의를 없앴습니다. 결제 시작의 withdrawalAgreement, 주문의 withdrawalAgreedAt, 상점 설정 withdrawalNotice가 없어졌습니다. 주문서의 withdrawal은 항상 { "required": false, "notice": null }, 장바구니·주문서 줄의 fulfillment.withdrawalRestricted는 항상 false이고 Deprecated입니다(아래 「옛 SDK 호환 필드」).
  • 호환성 변경: 제공 안내 메일을 없앴습니다. 상점 설정 fulfillmentMailEnabled, 재발송 POST /v1/order-items/{orderItemId}/fulfillment/mail (orderItems.resendFulfillmentMail), 주문 상품 fulfillment.mail이 없어졌습니다. 구매자 알림은 ORDER.FULFILLED 웹훅으로 직접 보내십시오.
  • 호환성 변경: 주문 상품의 제공 기록 fulfillment는 status·fulfilledAt·note를 싣습니다(codes·download·mail 삭제). 관리 API의 fulfillment.mode와 웹훅 ORDER.FULFILLED의 fulfillment.mode는 항상 MANUAL이고 Deprecated입니다.
  • 옛 SDK 호환 필드: 이미 배포된 스토어프론트(@sayren/storefront-sdk 0.15 이하)와 연동(@sayren/store-sdk 0.19 이하)이 계속 동작하도록 위의 Deprecated 필드를 고정값으로 한 릴리스 더 싣습니다. 다음 릴리스에서 뺍니다. 그 전에 SDK를 이 버전 이상으로 올리십시오.
  • 삭제된 오류 코드: FULFILLMENT_TYPE_NOT_AVAILABLE·INVALID_FULFILLMENT_MODE·PICKUP_ADDRESS_REQUIRED·STOCK_MANAGED_BY_CODES·CODE_REQUIRED· CODE_COUNT_MISMATCH·CODES_NOT_ALLOWED·ASSET_REQUIRED·ASSET_NOT_PRIVATE·ASSET_CHECK_FAILED·ASSET_ALREADY_ATTACHED·DOWNLOAD_LIMIT_REACHED· DOWNLOAD_EXPIRED·WITHDRAWAL_AGREEMENT_REQUIRED·WITHDRAWAL_RESTRICTED·MAIL_SENDING.
  • SDK는 옛 서버나 옛 이벤트가 보낸 없앤 유형을 { type: "OTHER", rawType }로 읽습니다.
  • 스토어프론트 템플릿(@sayren/mcp 0.13.0)에서 다운로드·코드 보기 화면과 청약철회 동의 체크를 없앴습니다. 배송 없는 주문 상품은 판매자 이용 안내(fulfillment.note)를 보여 줍니다.

2026-09-29

원격 MCP (@sayren/mcp 0.11.0)

  • 추가: 설치 없이 https://mcp.sayren.app/mcp 주소와 API 토큰(Authorization: Bearer)으로 연결하는 원격 MCP. 상점 조회·관리 API·스토어프론트 호스팅 도구를 씁니다. 설치 없이 MCP 연결하기
  • 추가: 스토어프론트 호스팅 도구 get_site·list_versions·publish_version·rollback·set_visibility(로컬·원격 공통). AI 도구의 발행·되돌리기·공개 전환은 승인 뒤 실행됩니다.

스토어프론트 템플릿 쿠키·테마 (@sayren/mcp 0.10.0)

  • 호환성 변경: 템플릿의 서버 쿠키(구매자 세션·비회원 장바구니·소셜 로그인 왕복)는 배포 빌드에서 이름에 __Host- 접두사가 붙습니다 (src/lib/cookies.server.ts). 같은 상위 도메인의 다른 사이트가 심은 쿠키로 구매자 세션이 바뀌지 않게 하는 장치입니다. 접두사 없는 옛 이름은 읽지 않으므로 0.10.0 템플릿으로 배포하면 로그인해 있던 구매자는 한 번 다시 로그인합니다. 빌드 결과는 https(또는 localhost)에서만 로그인·장바구니가 유지됩니다.
  • 변경: 브랜드 색·서체·본문 폭 토큰이 src/styles.css에서 src/theme.css로 옮겨졌습니다. 페이지 바탕(--color-page)과 본문 최대 폭 (--container-page) 토큰이 더해졌습니다. 디자인 수정하기
  • 변경: verify_storefront의 buyer-token-cookie 규칙이 서버 쿠키에 domain을 주는 코드를 위반으로 봅니다. 규칙 수는 그대로 19개입니다.

스토어프론트 호스팅 API (@sayren/store-sdk 0.19.0)

  • 추가: 관리 API /v1/storefront/**(템플릿 목록, 사이트 조회·만들기, 템플릿 변경, 버전 목록·발행·되돌리기, 주소 변경, 공개 전환, 호스팅 끄기, 미리보기 링크)와 스코프 storefront:r·storefront:rw. 새 사이트 만들기는 순차적으로 엽니다. 열리기 전에는 GET /v1/storefront/site의 enabled가 false이고 만들기는 403 STOREFRONT_HOSTING_DISABLED입니다. 스토어프론트 호스팅

상품 모델 재구성 (@sayren/store-sdk 0.18.0 · @sayren/storefront-sdk 0.15.0 · @sayren/mcp 0.9.0)

상품의 유형과 유형별 속성을 fulfillment 하나로 모았습니다. 배송비·출고지 같은 배송 속성은 배송 상품에만, 다운로드 횟수·제공 방식 같은 비실물 속성은 그 유형에만 있습니다. 주문 상품은 주문 시점의 이행을 fulfillmentSnapshot으로 굳혀 둡니다. 상품 유형 참고.

  • 호환성 변경: 관리 API 상품 응답의 평면 필드(deliveryType·deliveryFee·conditionalFreeAmount·shippingAddressId·returnAddressId· returnFee·exchangeFee·fulfillmentType·fulfillmentMode·withdrawalRestricted·pickupAddressId·stockTracking·downloadLimit·downloadDays)를 없앴습니다. 유형과 속성은 fulfillment: { type, <유형>: { … } }입니다. 상품 목록의 유형은 fulfillment.type입니다.
  • 호환성 변경: 상품 등록·수정 요청은 정한 필드만 받습니다. 옛 평면 필드를 보내면 400입니다. 수정(PUT·PATCH)에서 fulfillment를 주면 유형 안의 필드를 모두 보내야 하고, 생략하면 이행을 바꾸지 않습니다.
  • 호환성 변경: 상품의 반품·교환 배송비는 refundPolicy.returnDeliveryFee·exchangeDeliveryFee만 씁니다. 배송이 없는 유형에 주면 400 REFUND_POLICY_NOT_APPLICABLE입니다. 기존 상품의 반품비·교환비는 상품 refundPolicy로 옮겼습니다.
  • 호환성 변경: 주문 상품의 fulfillmentType·withdrawalRestricted를 fulfillmentSnapshot으로 바꿨습니다(관리 API·스토어프론트 API). 스토어프론트 주문 상품의 pickupLocation은 fulfillmentSnapshot.pickup.location입니다.
  • 호환성 변경: 웹훅 ORDER.DELIVERED(비실물 제공)의 fulfillmentType은 fulfillmentSnapshot, ORDER.FULFILLED의 fulfillmentType·fulfillmentMode는 fulfillmentSnapshot·fulfillment.mode입니다. 전환 전에 만들어진 이벤트는 재시도·수동 재전송에서 옛 모양으로 올 수 있고, 0.18.0의 webhookEventSchema는 그 모양을 거부합니다. 웹훅
  • 호환성 변경: 스토어프론트 상품 카드·장바구니 줄·주문서 줄의 fulfillmentType·requiresShipping(·withdrawalRestricted)을 fulfillment로 모았습니다. 상품 상세의 delivery·returnPolicy는 fulfillment(배송 상품이면 fulfillment.shipping)와 returnPeriodDays로 바뀌었습니다. 장바구니·주문서·주문의 requiresShipping은 항상 실립니다.
  • 호환성 변경: 에이전트 승인의 상품 가격 필드가 점 경로입니다(fulfillment.shipping.deliveryFee 등).
  • 추가: 상품 응답 fulfillmentTypeLocked(판매 이력이 있어 유형을 바꿀 수 없음). 오류 400 PICKUP_ADDRESS_REQUIRED, 409 PRODUCT_CHANGED.
  • 변경: 주문 뒤의 다운로드 횟수·기간, 반품지, 방문 수령지, 환불 정책은 주문 시점 값을 씁니다. 상품을 고쳐도 이미 들어온 주문은 바뀌지 않습니다.
  • 변경: 수령 전 방문 수령 주문이 쓰는 방문 수령지는 삭제할 수 없습니다(409 ADDRESS_IN_USE).
  • 변경: 전환 시점에 결제하지 않은 주문서와 결제 대기 중인 결제는 만료됐습니다. 주문서를 다시 만드십시오.
  • SDK 응답의 유형은 열린 유니온입니다. 모르는 유형은 { type: "OTHER", rawType }(스토어프론트는 requiresShipping 포함)입니다.
표면이전이후
관리 API 상품deliveryType · deliveryFee · conditionalFreeAmountfulfillment.shipping.deliveryType · deliveryFee · conditionalFreeAmount
관리 API 상품shippingAddressId · returnAddressIdfulfillment.shipping.shippingAddressId · returnAddressId
관리 API 상품returnFee · exchangeFeerefundPolicy.returnDeliveryFee · exchangeDeliveryFee
관리 API 상품fulfillmentTypefulfillment.type
관리 API 상품fulfillmentModefulfillment.code.mode · fulfillment.service.mode
관리 API 상품downloadLimit · downloadDays · stockTrackingfulfillment.download.limit · days · stockTracking
관리 API 상품withdrawalRestrictedfulfillment.download.withdrawalRestricted · fulfillment.code.withdrawalRestricted
관리 API 상품pickupAddressIdfulfillment.pickup.addressId
주문 상품fulfillmentType · withdrawalRestrictedfulfillmentSnapshot.type · fulfillmentSnapshot.{download,code}.withdrawalRestricted
웹훅 ORDER.DELIVEREDfulfillmentTypefulfillmentSnapshot
웹훅 ORDER.FULFILLEDfulfillmentType · fulfillmentModefulfillmentSnapshot · fulfillment.mode
스토어프론트 상품 카드fulfillmentType · requiresShippingfulfillment.type · fulfillment.requiresShipping
스토어프론트 상품 상세delivery · returnPolicyfulfillment.shipping · returnPeriodDays
스토어프론트 장바구니·주문서 줄fulfillmentType · requiresShipping · withdrawalRestrictedfulfillment.type · requiresShipping · withdrawalRestricted
스토어프론트 주문 상품fulfillmentType · withdrawalRestricted · pickupLocationfulfillmentSnapshot(pickup.location)

2026-09-28

앱 토큰의 상점 범위와 구성원 변경 제한

  • 호환성 변경: 직접 등록한 앱(POST /v1/oauth2/register)으로 받은 토큰은 앱을 등록한 상점에서만 쓸 수 있습니다. 다른 상점을 X-Store-Id로 부르면 403 CLIENT_STORE_MISMATCH, 다른 상점을 audience로 교환하면 400 invalid_target입니다. GET /v1/me/stores는 앱 토큰에 등록한 상점만 돌려줍니다. 여러 상점에서 쓰는 앱은 상점마다 따로 등록합니다.
  • 호환성 변경: 구성원·초대 변경(POST /v1/store/invitations, POST /v1/store/invitations/{invitationId}/resend, DELETE /v1/store/invitations/{invitationId}, PATCH /v1/store/members/{userId}, DELETE /v1/store/members/{userId})은 상점 플랫폼에서 로그인한 소유자·관리자만 할 수 있습니다. API 토큰·M2M 토큰·직접 등록한 앱의 토큰은 403 INSUFFICIENT_ROLE입니다. 조회는 그대로입니다. 구성원 초대·관리 참고.

쿠폰 (@sayren/store-sdk 0.17.0 · @sayren/storefront-sdk 0.14.0 · @sayren/mcp 0.8.0)

  • 추가: 관리 API /v1/coupons/**(생성·수정·중지·재개·종료·지급·발급 내역·사용 내역·통계), POST /v1/coupon-issues/{issueId}/revoke, GET /v1/customers/{customerId}/coupons(customer:r). 쿠폰 API의 스코프는 promotion:r·promotion:rw입니다. 이미 발급한 토큰에는 이 스코프가 없으니 쿠폰 API를 쓰려면 토큰을 새로 발급합니다. 쿠폰 참고.
  • 추가: 스토어프론트 API POST /storefront/v1/checkout/{checkoutId}/pricing(금액 미리보기), GET /storefront/v1/checkout/{checkoutId}/coupons, GET /storefront/v1/me/coupons, 결제 시작 본문의 coupons. 쿠폰 적용이 열리기 전에는 쿠폰을 보낸 요청이 409 COUPONS_UNAVAILABLE이고 쿠폰 없는 결제는 그대로입니다.
  • 추가: 주문 payment.couponDiscountAmount·deliveryDiscountAmount·discounts, 주문 상품 couponDiscountAmount, 주문서·결제 시작 amounts의 같은 두 금액. 쿠폰이 없으면 0과 빈 배열입니다. 결제 금액은 productAmount + deliveryFee − couponDiscountAmount − deliveryDiscountAmount입니다.
  • 추가: 환불 정책 couponShortfallCharge(상점 단위, 기본 true), 클레임 승인·판매자 취소 응답과 웹훅의 couponChargeAmount· couponChargeRefundAmount. 쿠폰 없는 주문은 항상 0입니다.
  • 변경: 쿠폰 할인을 받은 주문 상품의 환불은 실제 결제 금액(totalPrice − couponDiscountAmount)을 수량 비율로 나눕니다.
  • 호환성 변경: SDK의 CheckoutSession·PaymentStart 타입에서 amounts.couponDiscountAmount·deliveryDiscountAmount가 필수 필드입니다. 응답은 옛 서버의 값도 0으로 읽습니다. 이 타입으로 목 객체를 직접 만드는 코드는 두 필드를 더해야 컴파일됩니다.
  • 추가: MCP 스토어프론트 템플릿 주문서의 쿠폰 코드 입력(미리보기 뒤 결제 시작에 같은 코드를 보냄, src/lib/coupon-code.ts).
  • 변경: 템플릿 주문서에서 팝업 결제를 취소하거나 실패한 뒤 입력을 바꾸지 않고 다시 결제하면 새 결제를 만들지 않고 payments.retry로 같은 결제를 이어 갑니다.

비실물 상품 (@sayren/store-sdk 0.17.0 · @sayren/storefront-sdk 0.14.0 · @sayren/mcp 0.8.0)

  • 추가: 상품 필드 fulfillmentType(SHIPPING·DOWNLOAD·CODE·SERVICE·PICKUP, 기본 SHIPPING)·fulfillmentMode(AUTO·MANUAL, 유형 기본값)· downloadLimit(기본 5)·downloadDays(기본 30)·stockTracking(파일 다운로드 기본 false, 그 밖 true)·withdrawalRestricted(기본 false, 파일 다운로드·코드 발급만)· pickupAddressId. 수정에서 fulfillmentType을 생략하면 바꾸지 않습니다. 판매 이력이 있는 상품의 유형 변경은 409 FULFILLMENT_TYPE_LOCKED, 아직 쓸 수 없는 유형은 409 FULFILLMENT_TYPE_NOT_AVAILABLE입니다. 비실물 상품 판매하기 참고.
  • 추가: 주소록 유형 PICKUP(방문 수령지). GET /v1/seller-addresses는 type=PICKUP을 줄 때만 방문 수령지를 돌려줍니다.
  • 추가: 제공 처리 POST /v1/order-items/{orderItemId}/fulfill(본문 note·codes)·POST /v1/order-items/fulfill(항목별 결과), 안내 메일 재발송 POST /v1/order-items/{orderItemId}/fulfillment/mail(주문 상품당 하루 3회). Store SDK orderItems.fulfill·fulfillBulk· resendFulfillmentMail. 주문 목록 필터 fulfillmentType.
  • 추가: 코드 발급 POST·GET /v1/products/{productId}/codes, POST /v1/products/{productId}/codes/{codeId}/void. Store SDK products.uploadCodes· listCodes·voidCode. 결제 즉시 배정 코드 상품의 재고는 판매 가능 코드 수이고 직접 바꾸면 409 STOCK_MANAGED_BY_CODES입니다.
  • 추가: 판매 파일 POST·GET /v1/products/{productId}/digital-assets, DELETE …/{assetId}. 업로드 용도 digital-asset과 업로드 응답의 key·headers(PUT에 반드시 싣는 헤더, 공개 업로드는 빈 객체). Store SDK products.attachDigitalAsset·listDigitalAssets·removeDigitalAsset. uploads.upload는 headers를 붙여 PUT합니다.
  • 추가: 주문 상품 fulfillmentType·fulfillment(status·mode·fulfilledAt·note·codes·download·mail)·withdrawalRestricted, 주문 requiresShipping·withdrawalAgreedAt. 배송 상품이나 제공 전이면 fulfillment는 null입니다. 비실물 주문 상품의 DELIVERED는 제공 완료입니다.
  • 추가: 상점 설정 fulfillmentMailEnabled(기본 true)·withdrawalNotice(null이면 기본 문구).
  • 추가: 웹훅 이벤트 ORDER.FULFILLED(orderId·orderItemId·status·fulfillmentType·fulfillmentMode·fulfilledAt·testPayment). 비실물 제공 때 ORDER.DELIVERED도 함께 오고 여기에 선택 필드 orderItemId·fulfillmentType이 실립니다. type을 닫힌 목록으로 검증한다면 허용하십시오. 웹훅 참고.
  • 추가: 스토어프론트 API 상품·장바구니·주문서 requiresShipping·fulfillmentType, 주문서 withdrawal(required·notice), 결제 시작 withdrawalAgreement. 결제 시작의 shippingAddress는 선택이고 배송이 필요한데 없으면 400 SHIPPING_ADDRESS_REQUIRED입니다. 주문서를 만든 뒤 상품 유형이 바뀌면 409 CHECKOUT_STALE입니다.
  • 추가: 코드 열람 POST /storefront/v1/me/order-items/{orderItemId}/fulfillment/reveal(비회원 /guest/orders/{orderId}/order-items/{orderItemId}/fulfillment/reveal), 다운로드 POST /storefront/v1/me/order-items/{orderItemId}/downloads(비회원 /guest/orders/{orderId}/order-items/{orderItemId}/downloads). Storefront SDK myOrders.revealCode·revealGuestCode·createDownload·createGuestDownload, payments.start의 withdrawalAgreement. 내 주문 상품 fulfillment·pickupLocation·withdrawalRestricted, 내 주문 withdrawalAgreedAt.
  • 변경: 배송이 없는 상품은 배송 묶음과 상점 전체 무료배송 기준 금액에서 빠집니다. 배송비·반품 배송비·교환 배송비가 0입니다.
  • 변경: 비실물 상품에 발송처리·배송완료를 하면 409 NOT_SHIPPABLE, 배송 조회는 404 NOT_SHIPPABLE입니다.
  • 호환성 변경: 청약철회 제한 상품이 든 주문서는 결제 시작에 withdrawalAgreement: true가 있어야 합니다. 이 필드를 보내지 않는 스토어프론트는 그 주문만 400 WITHDRAWAL_AGREEMENT_REQUIRED이고 다른 주문은 그대로입니다.
  • 호환성 변경: 비실물 주문 상품에 대한 구매자 클레임이 새 코드로 거절될 수 있습니다. 교환은 409 EXCHANGE_NOT_SUPPORTED, 청약철회 제한 상품(파일·코드)의 첫 다운로드·열람 뒤 단순 변심 취소·반품은 409 WITHDRAWAL_RESTRICTED입니다. 셀러의 반품 수거 등록은 409 COLLECTION_NOT_REQUIRED입니다(바로 환불 승인).
  • 추가: MCP 스토어프론트 템플릿 — 배송이 없는 주문서는 배송지를 묻지 않고, 청약철회 제한 고지와 동의를 받습니다. 주문 상세에서 코드 열람 (src/lib/code-reveal.ts)과 파일 다운로드(src/lib/download.ts)를 합니다(비회원 주문 조회 포함).

웹훅 sequence (@sayren/store-sdk 0.17.0)

  • 추가: 웹훅 본문의 sequence. 리소스(주문·클레임·문의·리뷰·상품)별 순번이고 같은 리소스의 이벤트마다 1씩 커집니다. 도착 순서는 보장하지 않으므로 같은 리소스의 이벤트는 이 값으로 정렬하십시오. 이 필드가 생기기 전의 이벤트와 테스트 전송에는 없습니다. 웹훅 참고.
  • 추가: Store SDK webhookEventSchema의 sequence(선택 필드).
  • 변경: 전송 기록의 attemptCount는 전송을 시작한 횟수입니다. 보내는 도중 장애로 결과를 기록하지 못한 시도도 셉니다.
  • 추가(0.17.0에 함께 실림): Store SDK productSchema의 discount(상품 상세 응답의 즉시할인, 없으면 null)와 discountSchema.

2026-09-27

MCP 스토어프론트 템플릿 기본 라이브러리 (@sayren/mcp 0.7.0)

  • 추가: 템플릿 기본 의존성 nuqs·@tanstack/react-query(+ @tanstack/react-router-ssr-query)·react-hook-form· @hookform/resolvers·query-string. zod는 이전부터 들어 있습니다.
  • 변경: 상품 목록(/products)의 검색어·카테고리·정렬·페이지를 nuqs 파서로 읽고 바꿉니다(src/lib/products-search.ts). 정렬 선택을 넣었습니다. 목록 데이터는 loader가 ensureQueryData로 서버에서 미리 받고 화면은 useSuspenseQuery로 읽습니다 (src/lib/catalog-queries.ts).
  • 변경: 회원가입·주문서·비회원 주문 조회 폼이 react-hook-form + zodResolver입니다. 입력 규칙은 SDK 스키마를 재사용하고 칸마다 오류를 보입니다(src/lib/form-schemas.ts).
  • 변경: verify_storefront 규칙 loader-first가 TanStack Query로 받는 화면에 loader의 ensureQueryData가 없으면 알립니다. 규칙 수는 그대로 19개입니다.

비회원 주문 조회 시도 한도

  • 호환성 변경: POST /storefront/v1/guest/orders/{orderId}에서 연락처·주문 조회 비밀번호가 틀리면 401 INVALID_CREDENTIALS가 아니라 404 ORDER_NOT_FOUND입니다. 주문이 없을 때와 같은 응답이라 주문이 있는지 드러나지 않습니다. 두 코드를 함께 처리하던 화면은 그대로 동작합니다.
  • 추가: 주문번호마다 10분에 5번까지 확인하고, 5번째가 틀리면 30분 동안 429 TOO_MANY_REQUESTS (error.details.retryAfterSeconds)입니다. 잠긴 동안은 맞는 비밀번호로도 조회되지 않고, 맞게 조회하면 횟수가 초기화됩니다.
  • 추가: 주문번호마다 하루 15번이 상한이고, 상점 전체의 비회원 주문 조회에도 분당 한도가 있습니다. 넘으면 같은 429입니다.
  • 변경: 조회 본문은 주문서의 비회원 정보와 같은 형식입니다. phone은 01로 시작하는 숫자 1011자, orderPassword는 6128자이고 어긋나면 400 VALIDATION_FAILED입니다(시도로 세지 않습니다).
  • 호환성 변경: 주문서·결제 시작의 guest.orderPassword는 128자까지입니다(400 VALIDATION_FAILED). 스토어프론트 SDK 0.13.0의 guestInfoSchema가 같은 상한을 검사하고, myOrders.getGuestOrder 문서 주석에 위 오류를 적었습니다.

판매자 취소 웹훅 (@sayren/store-sdk 0.16.0)

  • 추가: 웹훅 이벤트 ORDER.CANCELED. 셀러가 POST /v1/order-items/{orderItemId}/cancel로 판매자 취소를 하면(일부 수량 포함) 옵니다. data는 orderId·orderItemId·status(주문 대표 상태)·itemStatus·reason·quantity·canceledQuantity·refundAmount· deliveryFeeRefundAmount·deliveryFeeChargeAmount·testPayment입니다. 구독해야 옵니다. 웹훅 참고.
  • 추가: Store SDK eventTypeSchema의 ORDER.CANCELED, orderCanceledWebhookEventSchema·orderCanceledWebhookEventDataSchema. webhookEventSchema로 본문을 검증하던 수신 서버는 SDK를 올리면 이 이벤트를 받습니다.

MCP 스토어프론트 템플릿 (@sayren/mcp 0.6.1)

  • 변경: 화면 탭 제목이 {화면} | {상점 이름}입니다(예: 회원가입 | 나의첫번째몰). 홈은 상점 이름만 씁니다(src/lib/page-title.ts).
  • 변경: 비회원 주문 조회 화면이 429에서 남은 시간을 분 단위로 안내합니다(src/lib/guest-order.ts).

MCP 스토어프론트 템플릿 (@sayren/mcp 0.6.0)

  • 추가: 회원가입 화면(/signup). 이메일·비밀번호 로그인을 켠 상점에서 이용약관·개인정보 수집이용(필수)과 마케팅 수신(선택) 동의를 받아 가입하고, 비회원 장바구니를 회원 장바구니로 합칩니다.
  • 추가: 내 정보의 로그아웃(리프레시 토큰 폐기)과 회원 탈퇴, 비회원 주문 조회 화면(/guest-order).
  • 추가: 결제 복귀 화면의 다른 결제수단 재시도(payments.retry). 주문서가 결제창을 열기 전에 결제 옵션을 탭 저장소에 남깁니다.
  • 변경: 헤더와 브라우저 탭 제목이 상점 이름·로고(GET /store)를 씁니다. 파비콘(public/favicon.svg)을 넣었습니다.
  • 변경: 상품 이미지 대체 텍스트, 장바구니 수량 입력 이름 등 접근성 이름을 정리했습니다.
  • 추가: verify_storefront 규칙 payment-retry·buyer-signup·buyer-signout. 규칙은 19개입니다. 0.5.x 템플릿으로 만든 프로젝트는 이 세 규칙에 새로 걸릴 수 있습니다. 새 템플릿의 src/routes/signup.tsx·src/routes/account.tsx· src/routes/checkout.return.tsx·src/lib/payment-options.ts를 참고해 고칩니다.

구성원 초대

  • 추가: POST /v1/store/invitations가 초대 메일을 보냅니다. 받는 사람은 메일의 링크를 열어 로그인한 뒤 상점 플랫폼에서 수락합니다. 초대받은 이메일 주소의 계정으로만 수락할 수 있습니다. 구성원 초대·관리 참고.
  • 추가: POST /v1/store/invitations/{invitationId}/resend. 새 수락 링크로 다시 보내고 만료를 7일 뒤로 다시 정합니다. 이전 링크는 무효가 됩니다.
  • 추가: 초대 응답의 deliveryStatus(SENDING·SENT·FAILED·NOT_CONFIGURED, 발송 기록이 없는 옛 초대는 null)· deliveryErrorCode·lastSentAt·sendCount·acceptedAt. 만료 시각이 지난 대기 초대는 status가 EXPIRED로 보입니다.
  • 추가: 초대 메일 발송 한도(초대 + 재발송). 상점마다 한 시간에 20통, 초대한 사람마다 하루 50통, 받는 이메일 주소마다 하루 5통이고 넘으면 429 TOO_MANY_REQUESTS(details.limit: STORE·SENDER·RECIPIENT)입니다.
  • 추가: 초대 수락은 로그인한 지 10분 안에만 되고(403 REAUTH_REQUIRED), 이메일 주소 인증을 마친 계정만 할 수 있습니다 (403 EMAIL_NOT_VERIFIED).
  • 추가: 발송 결과를 확인하지 못한 초대는 deliveryStatus가 FAILED, deliveryErrorCode가 DELIVERY_UNKNOWN입니다.
  • 추가: Store SDK store.invite·store.resendInvitation·store.cancelInvitation·store.updateMemberRole·store.removeMember, store.listInvitations({ status }), 스키마 inviteMemberRequestSchema·updateMemberRoleRequestSchema·invitationDeliveryStatusSchema.
  • 변경: 관리자(ADMIN)는 다른 관리자의 역할을 바꾸거나 내보낼 수 없습니다(403 INSUFFICIENT_ROLE). 소유자만 합니다. 관리자 초대의 재발송·취소도 소유자만 합니다. 역할이 없는 API 토큰으로는 관리자로 올리거나 관리자를 바꾸거나 내보낼 수 없습니다.
  • 변경: 초대 이메일은 소문자로 저장하고, 같은 주소의 대기 초대는 대소문자와 관계없이 하나만 둡니다. 동시에 초대하거나 같은 주소에 대기 초대가 있는데 만료된 초대를 재발송하면 409 INVITATION_PENDING입니다.
  • 변경: 초대 메일 제목에 상점 이름을 싣지 않습니다. 본문의 상점·초대한 사람 이름은 「」 안에 싣고, 링크처럼 보이는 이름은 싣지 않습니다.
  • 변경: GET /v1/store/invitations의 status가 허용 값이 아니면 400입니다(전에는 빈 목록).
  • 추가: 초대·재발송·취소·수락·역할 변경·내보내기의 감사 로그(MEMBER_*).

AI 작업 승인과 감사 로그

  • 추가: 에이전트 쓰기 정책. AI 에이전트용 API 토큰이나 @sayren/mcp가 보낸 변경 중 환불·가격·할인처럼 되돌리기 어려운 작업은 실행하지 않고 202(meta.code = APPROVAL_REQUIRED)와 승인 요청을 돌려줍니다. 결제 설정·토큰 발급처럼 에이전트에게 맡기지 않는 작업은 403 AGENT_OPERATION_DENIED입니다. 판정은 관리 API 문서의 x-agent-policy에도 실립니다. 에이전트가 아닌 요청은 바뀌지 않습니다. AI 작업 승인과 감사 로그 참고.
  • 추가: POST /v1/store/api-tokens의 agent(AI 에이전트(MCP)용 토큰, 생략 = false)와 목록·발급 응답의 agent.
  • 추가: 요청 헤더 x-sayren-dry-run: 1. 변경 요청을 실행하지 않고 판정(decision)과 영향 미리보기(preview)를 200 (meta.code = DRY_RUN)으로 돌려줍니다. 영향 미리보기 참고.
  • 추가: GET /v1/approval-requests·GET /v1/approval-requests/{approvalId}·POST /v1/approval-requests/{approvalId}/approve· POST /v1/approval-requests/{approvalId}/reject. 승인 응답은 실행을 기다리지 않은 EXECUTING이고, 원 요청을 한 번 실행한 결과(SUCCEEDED·FAILED·UNKNOWN)는 단건 조회의 status·result로 봅니다. 단건 조회는 요청한 토큰도 자기 요청에 한해 할 수 있습니다. Store SDK는 approvalRequests입니다.
  • 추가: 같은 Idempotency-Key로 다시 보낸 에이전트 요청은 같은 승인 요청을 씁니다. 요청 내용이 다르면 409 IDEMPOTENCY_KEY_REUSED, 그 승인이 이미 끝났으면 200(meta.code = APPROVAL_ALREADY_DECIDED)과 승인 요청입니다.
  • 추가: Store SDK ApprovalPendingError. 승인 대기(202 APPROVAL_REQUIRED)와 끝난 승인(APPROVAL_ALREADY_DECIDED) 응답을 성공 값으로 돌려주지 않고 이 에러로 던집니다(approvalId·status·approval).
  • 추가: GET /v1/audit-logs 감사 로그. 관리 API의 모든 변경 요청을 행위자(USER·AGENT·APP·TOKEN·SYSTEM)·대상·결과· 멱등키·traceId와 함께 남기고 행위자·동작·대상·기간으로 거릅니다. 요청 기록은 1년 보관합니다. Store SDK는 auditLogs.list입니다.
  • 추가: 스코프 audit:r. 상점 플랫폼 로그인에서는 OWNER·ADMIN이 가집니다.
  • 추가: MCP call_api_write의 dryRun, 도구 get_approval_request. 판매자 취소·클레임 승인은 이제 막혀 있지 않고 승인 대기가 됩니다.

2026-09-26

MCP 검증 결과 집계

  • 추가: POST /v1/telemetry/mcp. @sayren/mcp가 verify_storefront 결과 요약(도구 이름·클라이언트 버전·임의의 세션 값· 호출 순번·실행 시간·통과 여부·검사한 파일 수·규칙별 통과/위반과 위반 개수)을 보냅니다. 파일 경로·소스 코드·위반 상세는 보내지 않습니다. 스코프가 필요 없고 상점은 토큰으로 정해지며 응답은 204입니다. 상점당 분당 60회·하루 2,000회이고, 하루 한도를 넘은 요청은 적재하지 않고 204로 받습니다.
  • 추가: MCP 환경변수 SAYREN_TELEMETRY. 0이면 이 집계를 보내지 않습니다. 검증 결과 집계 참고.

웹훅·API 실패 원인 찾기

  • 추가: 에러 응답 error.docs — 그 에러 코드를 설명하는 문서 주소입니다. 문서가 있는 코드에만 실리고 없으면 필드가 빠집니다. Store SDK는 ApiError의 docs로 읽습니다. 표준 응답 구조 참고.
  • 추가: GET /v1/webhooks/{webhookId}/deliveries 응답에 failureReason — 실패 원인 분류입니다. DNS·TLS·TIMEOUT·CONNECTION·HTTP_4XX·HTTP_5XX·HTTP_OTHER·SSRF_BLOCKED·SUSPENDED이고 성공·미시도면 null입니다. 기존 errorCode보다 잘게 나눈 값이고 둘은 함께 옵니다. 값이 추가될 수 있어 문자열로 읽습니다. 실패 원인 분류 참고.
  • 추가: 같은 응답에 resource — 이벤트가 가리키는 대표 리소스 { type, id }입니다. type은 현재 ORDER·CLAIM·PRODUCT·REVIEW·INQUIRY이고 특정할 수 없으면 null입니다. 값이 추가될 수 있어 문자열로 읽습니다. Store SDK는 두 값을 문자열로 파싱하고 현재 값을 WEBHOOK_FAILURE_REASONS·WEBHOOK_RESOURCE_TYPES로 제공합니다.
  • 변경: responseBodyExcerpt의 상한이 1,024자에서 4,096자로 늘었습니다.
  • 추가: 전송 기록 목록 쿼리 eventType·resourceId. resourceId는 대표 리소스뿐 아니라 이벤트 data에 실린 모든 ID를 보므로, 주문 ID로 그 주문의 클레임·품절 이벤트까지 찾습니다.
  • 추가: GET /v1/webhooks/{webhookId}/deliveries/{deliveryId} 전송 기록 상세. 목록 항목에 payload(보낸 요청 본문 문자열 그대로)·payloadTruncated(16KB 초과 시 true)·payloadMasked(민감 키 마스킹이 본문을 바꿨으면 true, 그때는 서명을 맞춰 볼 수 없음)·requestHeaders([{ name, value }], Sayren-Signature는 value가 null, Content-Length는 실제로 보낸 본문의 바이트 수)를 더합니다. 재시도가 같은 기록에 누적되므로 응답 상태·본문·소요 시간·failureReason은 마지막 시도의 값입니다. 없으면 404 WEBHOOK_DELIVERY_NOT_FOUND이고 권한은 전송 기록 조회와 같습니다. SDK는 webhooks.getDelivery(webhookId, deliveryId)입니다. 전송 기록 상세 참고.
  • 추가: POST /v1/webhooks/{webhookId}/test 응답에 failureReason·responseBodyExcerpt·requestHeaders·payload. 기존 responseStatusCode·responseTimeMs·errorCode는 그대로입니다.
  • 변경: 응답 본문과 발송 본문은 저장·노출 전에 가립니다. 키 이름에 authorization·token·secret·password·key· signature·credential·cookie가 들어 있으면(부분 일치, 대소문자 무시) 값이 ***가 되고, 값이 객체·배열이면 통째로 가립니다. 응답 본문은 평문 Authorization: … 줄·Bearer <토큰>·access_token=…도 가리고, 가린 뒤에 4,096자로 자릅니다. 전송 기록 상세 참고.
  • 추가: GET /v1/api-logs에 쿼리 traceId(정확히 일치). 에러 응답의 error.traceId를 그대로 넣어 그 요청 한 건을 찾습니다. SDK는 apiLogs.list({ traceId })입니다. API 로그 › 조회와 필터 참고.

배송비 규칙과 출고지·반품지

  • 추가: GET·PATCH /v1/store에 shippingPolicy — 상점 배송비 규칙 네 항목 (baseDeliveryFee 기본 3000 · freeThreshold 기본 null · jejuSurcharge·isolatedSurcharge 기본 0). PATCH는 준 항목만 바꾸고 모르는 항목 이름은 400입니다. GET은 정하지 않은 항목을 기본값으로 채워 전부 돌려줍니다. 바꾼 값은 새 주문서부터 적용됩니다. 배송비와 출고지·반품지 참고.
  • 호환성 변경 배송비가 배송 묶음(같은 출고지 + 같은 배송 정책인 상품)마다 한 번 붙습니다. 이전에는 같은 상품 단위였습니다. 출고지나 배송비가 다른 상품을 함께 주문하면 배송비가 묶음 수만큼 붙습니다.
  • 호환성 변경 상품별 조건부 무료배송(CONDITIONAL_FREE)의 기준액도 묶음 소계로 판정합니다. 출고지·배송비·기준액이 같은 상품은 서로 다른 상품이어도 금액을 합쳐 기준을 채웁니다. 이전에는 상품별 소계로만 판정했습니다.
  • 추가: 제주·도서산간 추가 배송비 — 배송지 우편번호가 해당 범위면 묶음마다 더합니다. 무료배송이어도 부과합니다. 우편번호 범위는 플랫폼이 고정하고 금액만 상점이 정합니다.
  • 추가: POST /storefront/v1/checkout/{checkoutId}/delivery-quote — 배송비 미리보기. 본문은 zipCode(숫자 5자리)이고 응답은 amounts·delivery입니다. 주문서를 바꾸지 않는 읽기 계산입니다. SDK는 checkout.quoteDelivery()입니다.
  • 추가: 주문서 조회·배송비 미리보기·결제 시작 응답에 delivery — baseFee·remoteSurcharge·remoteArea·remoteAreaLabel·bundleCount·freeByThreshold·zipCode입니다.
  • 추가: 결제 시작(POST /storefront/v1/checkout/{checkoutId}/payment) 응답에 amounts — 보낸 배송지로 배송비를 다시 계산한 확정 결제 금액입니다. 주문서 조회 시점의 amounts와 다를 수 있습니다(지역 추가 배송비).
  • 호환성 변경 상품 등록·수정에서 deliveryFee를 생략하면 상점 기본 배송비(shippingPolicy.baseDeliveryFee)로 저장합니다. 이전에는 0이었습니다. 배송비를 받지 않을 상품에는 deliveryType: "FREE" 또는 deliveryFee: 0을 보냅니다.
  • 추가: 출고지·반품지 주소록 /v1/seller-addresses — 목록·등록·상세·수정·삭제와 기본 주소 지정 (POST /v1/seller-addresses/{addressId}/default). 유형(SHIPPING·RETURN)별 최대 10개이고 첫 주소가 기본 주소입니다. 조회는 product:r, 나머지는 product:rw입니다. 409 ADDRESS_LIMIT_EXCEEDED·409 DEFAULT_ADDRESS_UNDELETABLE· 409 ADDRESS_IN_USE·404 ADDRESS_NOT_FOUND.
  • 추가: 상품 등록·수정·조회에 shippingAddressId·returnAddressId. 생략하면 상점 기본 주소가 들어갑니다.
  • 추가: 클레임 조회(관리 API GET /v1/claims·GET /v1/claims/{claimId}, 구매자 클레임 목록)에 returnAddress — 수거 안내에 쓸 반품지(name·contactName·phone·zipCode·address1·address2)입니다. 상품이 지정한 반품지, 없으면 상점 기본 반품지이고, 취소 클레임과 반품지를 등록하지 않은 상점은 null입니다.
  • 추가: @sayren/store-sdk의 sellerAddresses.list·create·update·remove·setDefault, @sayren/storefront-sdk의 checkout.quoteDelivery.
  • 변경: 배송비 환불이 묶음 단위입니다. 묶음의 남은 수량이 0이 될 때만 그 묶음의 배송비를 환불하고, 묶음에 붙은 지역 추가 배송비도 함께 환불합니다. 묶음 구성은 주문 시점에 고정됩니다.
  • 추가: 상점 플랫폼 설정에 배송(배송비 규칙·추가 배송비 지역)과 주소록(출고지·반품지) 화면이 생겼습니다.

현금영수증 발급

  • 추가: 결제 시작(POST /storefront/v1/checkout/{checkoutId}/payment)과 재시도(POST /storefront/v1/payments/{paymentId}/attempts) 본문에 cashReceipt — type(INCOME_DEDUCTION 소득공제 · EXPENSE_PROOF 지출증빙)과 identityNumber(식별 번호)입니다. 계좌이체·가상계좌 옵션에서만 보낼 수 있고(그 밖의 옵션이면 400 CASH_RECEIPT_NOT_AVAILABLE), 결제가 승인되면 결제한 결제사로 발급합니다. 구매자가 결제창에서 따로 입력하지 않습니다. 결제 연동 › 현금영수증 참고.
  • 식별 번호는 소득공제는 휴대폰 번호(01X + 78자리) 또는 현금영수증카드 번호(1319자리), 지출증빙은 사업자등록번호(10자리) 또는 현금영수증카드 번호입니다. 하이픈·공백은 빼고 읽습니다. 주민등록번호는 받지 않습니다 — 보내면 400 VALIDATION_FAILED이고 fieldErrors의 cashReceipt.identityNumber 코드가 RESIDENT_NUMBER_NOT_ALLOWED입니다. 식별 번호는 암호화해 저장하고 응답에는 가린 값만 나갑니다(원문은 조회할 수 없습니다).
  • 추가: 관리 API GET /v1/orders/{orderId} 응답 payment.cashReceipt, GET /v1/payments/{paymentId} 응답 cashReceipt — status·type·identityType·identityNumberMasked·amount·issuedAmount·provider·issueNumber·receiptUrl· issuedAt·canceledAt·failCode·failMessage·reissuable입니다. 신청이 없으면 null입니다. 상태는 REQUESTED 결제 전 · PENDING 발급 대기 · ISSUED 발급 · CANCELED 취소 · FAILED 실패 · SKIPPED 발급 대상 아님이고, 값이 추가될 수 있어 문자열로 읽습니다.
  • 추가: GET /v1/payments 목록 행에 cashReceiptStatus. 신청이 없으면 null입니다.
  • 추가: POST /v1/orders/{orderId}/cash-receipt/reissue 재발급. 실패(FAILED)했거나 재시도를 기다리는(PENDING) 현금영수증을 지금 다시 발급·취소합니다. 스코프는 order:rw이고 상점 플랫폼에 로그인한 OWNER·ADMIN만 호출할 수 있습니다. 404 CASH_RECEIPT_NOT_FOUND·409 CASH_RECEIPT_NOT_REISSUABLE·409 CASH_RECEIPT_IN_PROGRESS. 결제 내역 › 현금영수증 참고.
  • 추가: 환불하면 발급한 금액을 그만큼 취소합니다. 전액 환불이면 CANCELED, 부분 환불이면 남은 금액으로 맞춥니다. 발급해야 할 금액(amount)은 결제 금액에서 환불 완료 금액을 뺀 값입니다.
  • 결제사 응답 지연 같은 일시적인 오류는 자동으로 다시 시도합니다. 식별 번호 오류처럼 결제사가 거절한 발급은 FAILED로 남으니 failCode·failMessage를 보고 재발급하십시오. 발급이 늦거나 실패해도 결제와 주문은 그대로입니다.
  • 추가: @sayren/storefront-sdk의 checkout.startPayment·payments.start·payments.retry에 cashReceipt와 cashReceiptRequestSchema·cashReceiptAvailable·cashReceiptIdentityTypeOf, @sayren/store-sdk의 orders.reissueCashReceipt(orderId)와 상태 값 목록 CASH_RECEIPT_STATUS_VALUES.
  • 추가: 상점 플랫폼 주문 상세에 현금영수증 카드와 재발급 버튼이 생겼고, 결제 내역 상세에 현금영수증 상태가 실립니다.

택배 추적

  • 추가: 택배 추적 — 택배로 발송한 주문 상품의 송장을 택배사에서 조회해 추적 이력을 보여 줍니다. 택배사가 배송완료를 알려 오면 주문 상품이 자동으로 배송완료로 바뀌고 ORDER.DELIVERED 웹훅이 나갑니다. 자동 배송완료 대기일은 그대로 적용됩니다. 주문 처리하기 › 배송 추적 참고.
  • 추가: 관리 API GET /v1/order-items/{orderItemId}/delivery에 tracking(result·stage·checkedAt·carrierDeliveredAt), trackingEvents[]에 stage. 택배 발송이 아니거나 추적을 쓰지 않으면 tracking이 null입니다. result는 FOUND 조회됨 · NOT_FOUND 택배사 전산에 송장 없음 · UNSUPPORTED_CARRIER 추적을 지원하지 않는 택배사이고, 값이 추가될 수 있어 문자열로 읽습니다. carrierDeliveredAt은 택배사가 알려 준 배송완료 시각이라 주문 상품의 deliveredAt(추적을 반영한 시각)과 다를 수 있습니다.
  • 추가: 스토어프론트 API GET /storefront/v1/me/order-items/{orderItemId}/delivery에 trackingStage·trackingCheckedAt, trackingEvents[]에 stage. 단계 값(ACCEPTED·IN_TRANSIT·OUT_FOR_DELIVERY·DELIVERED)은 늘어날 수 있어 문자열입니다.
  • 변경: trackingEvents는 택배사에서 받은 이력입니다. 발송처리 때 넣던 예시 이벤트(집화처리)는 더 넣지 않습니다. 추적 전에는 빈 목록일 수 있습니다.
  • 추적은 주기적으로 갱신합니다. 발송처리 직후나 택배사에 이력이 올라간 직후 곧바로 반영되지는 않고, 배송완료 자동 전이도 그만큼 늦습니다. 마지막으로 받아 온 시각은 tracking.checkedAt(스토어프론트는 trackingCheckedAt)입니다.
  • 추가: 상점 플랫폼 주문 상세에 배송 추적 카드가 생겼습니다.

구매자 동의 이력

  • 추가: GET·POST /storefront/v1/me/consents — 약관·개인정보·마케팅 동의의 현재 상태 조회와 변경입니다. 필수 동의는 동의(true)만 보낼 수 있고 마케팅은 false로 철회합니다. 구매자 인증 › 동의와 재동의 참고.
  • 추가: GET /storefront/v1/me에 reconsentRequired — 약관이 개정돼 필수 동의를 다시 받아야 하면 true입니다. 로그인 응답에는 실리지 않습니다(약관 개정은 로그인한 채로도 일어납니다).
  • 추가: GET·PATCH /v1/store에 termsVersion·privacyVersion(기본 "1")·termsUrl·privacyUrl(https, null로 지웁니다)과 읽기 전용 termsVersionUpdatedAt·privacyVersionUpdatedAt. 버전을 올리면 이전 버전에 동의한 구매자에게 재동의를 요구합니다. 이 네 필드는 OWNER만 바꿀 수 있습니다(다른 상점 정보는 그대로입니다). 동의 이력에는 동의한 시점의 문서 주소가 documentUrl로 남습니다.
  • 추가: POST /storefront/v1/auth/idp/{provider}/authorize에 agreements — 소셜 가입 화면에서 받은 동의입니다. 이 로그인으로 가입이 되는 경우에만 이력에 남고, 기존 회원 로그인이면 무시합니다. 보내지 않아도 가입은 됩니다. 그때는 필수 동의 이력이 없어 reconsentRequired가 true이므로 첫 진입에서 POST /me/consents로 받으면 됩니다.
  • 추가: GET /v1/customers/{customerId}/consents — 구매자 동의 이력(최근 순, kind 필터, customer:r). 가입·소셜 가입·회원 연동·재동의·마케팅 변경이 한 줄씩 남습니다.
  • 추가: GET /v1/customers/{customerId} 응답에 consents — 종류별 현재 동의 상태와 재동의 필요 여부.
  • 추가: @sayren/storefront-sdk의 member.consents()·member.updateConsents(), @sayren/store-sdk의 customers.consents(customerId, params).
  • 변경: 가입·소셜 가입·자체 회원 연동으로 구매자를 만들 때 동의 이력을 함께 남깁니다. 기존 회원에게는 가입 시각과 버전 "1"로 필수 동의 이력을 채웠습니다(마케팅은 동의 상태인 회원만).
  • 변경: 구매자 탈퇴·셀러 탈퇴 처리에서 동의 이력도 함께 지웁니다.
  • 추가: POST /storefront/v1/me/consents에 구매자당 분당 10회 한도가 생겼습니다. 넘으면 429 TOO_MANY_REQUESTS이고 error.details.retryAfterSeconds에 남은 초가 실립니다.
  • 추가: 상점 플랫폼 설정 › 상점 정보에 약관 카드, 고객 상세에 동의 카드가 생겼습니다.

환불 정책과 수량 단위 부분 취소

  • 추가: GET·PATCH /v1/store에 refundPolicy — 취소·반품·교환의 배송비 부담을 정하는 다섯 항목 (preShipmentDeliveryFeeRefund·returnDeliveryFee·exchangeDeliveryFee·sellerFaultFreeShipping· conditionalFreeShortfallCharge). PATCH는 준 항목만 바꾸고 GET은 정하지 않은 항목을 기본값으로 채워 전부 돌려줍니다. 환불 정책과 배송비 참고.
  • 추가: 상품 등록·수정·조회에 refundPolicy — 상품별 덮어쓰기. 준 항목만 상점 기본을 덮어쓰고, null이나 생략은 상점 기본을 씁니다. returnDeliveryFee·exchangeDeliveryFee를 주면 returnFee·exchangeFee도 같은 값으로 맞춰집니다.
  • 추가: 수량 단위 부분 취소 — Storefront API POST /claims와 관리 API POST /v1/order-items/{orderItemId}/cancel에 quantity가 생겼습니다. 생략하면 남은 수량 전체입니다. 남은 수량보다 크면 400 INVALID_QUANTITY이고, 남은 수량이 0이면 관리 API의 판매자 취소는 409 ALREADY_CANCELED, 구매자 요청은 409 INVALID_STATUS입니다.
  • 추가: 주문 상품 응답(관리 API·스토어프론트 API)에 canceledQuantity·activeQuantity. 일부 수량만 취소·반품된 주문 상품은 상태를 유지한 채 activeQuantity가 줄고, 남은 수량이 0이 될 때 CANCELED가 됩니다.
  • 추가: 금액 응답 필드 — 판매자 취소에 quantity·canceledQuantity·deliveryFeeRefundAmount·deliveryFeeChargeAmount, 클레임 승인에 quantity·deliveryFeeRefundAmount·deliveryFeeChargeAmount, 클레임 접수에 quantity·deliveryFeeRefundAmount, 구매자 클레임 목록에 quantity.
  • 변경: 클레임 승인 응답의 deliveryFeeChargeAmount에는 판매자가 넣은 추가 차감(deductAmount)이 들어 있지 않습니다. 두 값을 더해 환불액에서 뺍니다.
  • 추가: 클레임 조회(관리 API GET /v1/claims·GET /v1/claims/{claimId}, 구매자 클레임 목록)에 expectedDeliveryFeeRefundAmount — 접수 시점의 예상 배송비 환불액입니다. 이 필드가 생기기 전에 접수된 클레임은 null입니다. expectedRefundAmount에는 배송비 환불이 들어 있지 않고, 조건부 무료배송 미달 차감은 이미 빠져 있습니다.
  • 추가: CLAIM.* 웹훅 data에 quantity·refundAmount·deliveryFeeRefundAmount·deliveryFeeChargeAmount·deductAmount. 이전 이벤트에는 없어 0(refundAmount는 null)으로 읽습니다. 모르는 필드를 거부하는 방식(strict)으로 검증한다면 다섯 필드를 허용하도록 바꿉니다. deliveryFeeChargeAmount는 배송비 차감(반품·교환 배송비 + 조건부 무료배송 기준 미달)만이고 판매자가 승인에 넣은 추가 차감은 deductAmount입니다. 환불액은 refundAmount = 상품 금액 + deliveryFeeRefundAmount − deliveryFeeChargeAmount − deductAmount로 읽습니다.
  • 변경: 취소·반품 환불액이 수량 비율이 됐습니다. 원 단위 반올림을 누적 금액 기준으로 하므로 나눠 처리해도 환불액의 합이 주문 상품 금액과 정확히 같습니다. 재고 복원과 PG 부분 취소도 같은 수량 비율입니다.
  • 변경: 배송비는 배송 묶음(같은 상품의 주문 상품)의 남은 수량이 0이 될 때만 환불합니다. 한 묶음의 배송비는 한 번만 정산되고, 주문이 낸 배송비를 넘겨 환불하지 않습니다.
  • 변경: 교환 재고 — 수거 완료에서 원래 옵션을 복원하고 재배송 등록에서 구매자가 고른 새 옵션을 차감합니다. 새 옵션의 재고가 부족하면 재배송 등록이 409 OUT_OF_STOCK입니다.

배송완료 처리와 자동 전이

  • 추가: POST /v1/order-items/{orderItemId}/delivered — 발송(DISPATCHED)·배송중(DELIVERING) 주문 상품을 배송완료(DELIVERED)로 바꿉니다. 여러 건은 POST /v1/order-items/delivered에 { "orderItemIds": [...] }로 보냅니다. 주문 처리하기 참고.
  • 추가: GET·PATCH /v1/store에 autoDeliverDays·autoConfirmDays(각 0~30, 기본 7)가 생겼습니다. 발송 후 N일이 지난 주문 상품을 배송완료로, 구매확정 예정일이 지난 주문 상품을 구매확정으로 자동 전이합니다. 0이면 그 자동 전이를 하지 않고, 진행 중인 취소·반품·교환이 있는 주문 상품은 빠집니다.
  • 변경: 배송완료 시점에 구매확정 예정일(autoDecisionDate)을 autoConfirmDays로 다시 잡습니다. 전에는 발송 시점에 7일로 고정해 넣었습니다.
  • 변경: ORDER.DELIVERED 웹훅이 실제로 발송됩니다. ORDER.PURCHASE_DECIDED는 자동 구매확정에서도 발송됩니다.
  • 변경: 반품·교환 가능 기간이 고정 7일에서 상점 설정 autoConfirmDays(기본 7일, 연장분 포함)로 바뀌었습니다. 자동 구매확정 기한과 같은 값이라, 구매확정된 뒤에 반품을 받을 수 없는 구간이 생기지 않습니다. 기간이 지난 요청은 그대로 409 PERIOD_EXPIRED입니다.
  • 변경: 구매확정 기한 연장(POST /v1/order-items/{id}/purchase-decision/extend)의 autoDecisionDate를 배송완료 시각 + (autoConfirmDays + 연장 횟수 × 7일)로 다시 계산합니다. 전에는 기존 예정일에 7일을 더했습니다.
  • 추가: @sayren/store-sdk의 orderItems.markDelivered(orderItemId)·orderItems.markDeliveredBulk(orderItemIds).
  • 추가: 상점 플랫폼 주문 상세에 배송완료 버튼과 선택 일괄 처리가 생겼고, 설정 › 상점 정보에 자동 전이 대기일 입력이 생겼습니다.

상품 이미지 업로드 (호환되지 않는 변경)

  • 제거: POST /v1/product-images. 상품 이미지는 POST /v1/uploads로 자리를 받아 uploadUrl에 파일을 직접 PUT하고, 받은 url을 상품의 images에 넣습니다. 상품 이미지 등록 참고.
  • 추가: POST /v1/uploads 요청에 purpose(product-image·description, 생략하면 description)와 size가 생겼습니다. 용도에 따라 허용 형식과 장당 최대 크기가 다릅니다 (product-image는 jpg·jpeg·png·webp·avif·gif, 10MB / description은 svg·동영상 포함, 50MB). 제한을 넘으면 400 UNSUPPORTED_FILE_TYPE 또는 413 FILE_TOO_LARGE입니다.
  • 변경: 상품 등록·수정의 images는 https 주소만 받고, 주소에 확장자가 있으면 이미지 형식이어야 합니다 (400 INVALID_IMAGE_URL·400 UNSUPPORTED_FILE_TYPE). 사내 CDN 밖의 주소도 그대로 넣을 수 있고, 이미 그 상품에 저장돼 있던 주소는 다시 검사하지 않습니다.
  • 변경: 스토어프론트 API의 thumbnailUrl이 nullable이 됐습니다(상품 목록·상세, 장바구니, 주문 상품, 작성 가능 리뷰). 전에는 이미지가 없으면 열리지 않는 자리표시 주소를 돌려줬습니다. 이제 null이고 자리표시 화면은 스토어프론트가 그립니다. @sayren/storefront-sdk 스키마도 nullable입니다.
  • 추가: 올린 이미지는 주소에 ?w=·?h=(1~4096)·?f=webp|avif를 붙여 줄이거나 포맷을 바꿔 받을 수 있습니다.
  • 추가: @sayren/store-sdk의 uploads.upload(file, { purpose }) — 자리 발급과 파일 PUT을 한 번에 합니다.
  • 추가: 상점 플랫폼 상품 등록·수정에 이미지 섹션이 생겼습니다. 올리기·순서 바꾸기·대표 지정·삭제를 합니다.

2026-09-25

결제 흐름 개편 (호환되지 않는 변경)

  • 변경: 결제창을 sayren이 그립니다. 결제 시작 응답의 결제 주소(payUrl)를 팝업이나 현재 탭으로 열면 결제 화면이 PG 결제창을 띄우고 승인까지 끝냅니다. 스토어프론트는 PG 스크립트도 승인 키도 다루지 않습니다. 결제 연동 참고.
  • 변경: 결제 시작 응답이 { paymentId, attemptId, option, payUrl, expiresAt, testPayment }로 바뀌었습니다 (pgProvider·pgParams·launch 삭제). SDK의 checkout.requestPayment는 checkout.startPayment로 바뀌었습니다.
  • 제거: 결제 승인 POST /payments/{paymentId}/confirm과 결제창 실패 보고 POST /payments/{paymentId}/attempts/{attemptId}/failure. 공개 결제 API는 결제 시작·재시도·상태 조회 셋뿐입니다.
  • 추가: 주문서 응답 paymentOptions — PG × 결제수단 × 간편결제사로 된 결제 옵션 목록. 결제 시작은 옵션 하나({ pg, method, provider? })와 복귀 주소(returnUrl)를 보냅니다. 옵션을 바꾸는 것만으로 다른 결제사로 결제합니다.
  • 추가: 간편결제 — 네이버페이·카카오페이·토스페이·페이코 결제창을 바로 엽니다(method: "EASY_PAY", provider). 상점 플랫폼 PG 상세에서 켭니다.
  • 추가: Storefront API POST /payments/{paymentId}/attempts — 같은 결제를 다른 옵션으로 다시 시도하고 새 payUrl을 받습니다. returnUrl을 생략하면 결제 시작 때의 복귀 주소를 씁니다.
  • 추가: GET /payments/{paymentId} 응답에 result(COMPLETED·PROCESSING·CANCELED·FAILED·EXPIRED·PENDING)와 lastFailure가 생겼습니다. 결제 결과 판정은 이 값으로 합니다.
  • 추가: 결과 전달 규약 — 팝업이면 결제 화면이 postMessage({ type: "sayren:payment", … })를 보내고, 리다이렉트면 복귀 주소에 sayrenPaymentId가 붙습니다. 둘 다 신호이고 판정의 근거는 상태 조회입니다. SDK 없이 쓰는 방법은 SDK 없이 결제 연동에 있습니다.
  • 추가: 결제 도메인 — 상점 플랫폼 설정 › 결제에 스토어프론트 주소를 등록하면 복귀 주소가 그 목록으로 제한됩니다. 선택 항목이라 비워 두면 모든 https 복귀 주소를 받습니다(테스트 결제는 localhost 허용). 목록 밖은 400 RETURN_URL_NOT_ALLOWED. 관리 API PUT /store/payment-settings의 checkoutOrigins.
  • 변경: 자동 전환·라우팅 방식을 없앴습니다. PG 우선순위는 결제 옵션 목록의 순서입니다. 관리 API 결제 설정에서 routingMode·effectiveRoutingMode가 빠졌습니다.
  • 변경: @sayren/storefront-sdk/payments가 start·retry·open·prepareWindow·result·getStatus로 바뀌었습니다 (complete·launch 삭제). 결제창 방식은 mode(auto·popup·redirect)로 고르고, 기본값은 모바일 리다이렉트·PC 팝업입니다. PG SDK 의존성(@tosspayments/*·@portone/*)이 빠졌습니다.
  • @sayren/storefront-sdk 0.8.0, @sayren/store-sdk 0.8.0, @sayren/mcp 0.2.0(템플릿 결제 화면·복귀 화면·검사 규칙).

상세설명 에디터

  • 수정: API로 등록한 상품처럼 상세설명이 HTML로만 있는 상품도 상점 플랫폼 상세설명 에디터에서 기존 본문(사진 포함)을 불러옵니다. 전에는 빈 에디터로 열려 저장하면 기존 본문이 바뀌었습니다.
  • 코드블록으로 끝난 글도 본문 아래 빈 곳을 누르면 이어 쓸 수 있습니다.
  • MCP 템플릿의 에디터 의존성을 최신으로 올렸습니다. @sayren/mcp 0.1.11.

구매자 계정 연결·자체 회원 연동

  • 추가: Storefront API GET /me/identities, POST /me/identities/{provider}/authorize, POST /me/identities/{provider}, DELETE /me/identities/{provider} — 로그인한 구매자의 소셜 계정 연결·해제. @sayren/storefront-sdk/auth의 identities()·linkIdp()·linkIdpCallback()·unlinkIdp().
  • 추가: 구매자 위임의 member-uid(자체 회원 시스템의 사용자 id로 지목). @sayren/store-sdk/server의 delegateMemberByUid()·delegateMemberByEmail().
  • 변경: 소유자가 상점 플랫폼 개발자 › 앱에서 서버 앱에 구매자 위임(member:delegate)을 줄 수 있습니다.
  • MCP 템플릿에 내 정보(/account) 화면: 로그인 수단 보기·소셜 계정 연결·해제. @sayren/mcp 0.1.10.
  • @sayren/storefront-sdk 0.7.0, @sayren/store-sdk 0.7.0.

방문 분석 2단계

  • 추가: 관리 API GET /analytics/realtime(최근 5분 방문자·세션·페이지), GET /analytics/devices(기기별 세션 퍼널), GET /analytics/pages(첫 페이지·이탈 페이지), GET /analytics/search(검색어별 검색·결과 없음·검색 후 구매, 결과 없는 검색어 zeroResultTerms), GET /analytics/checkout(결제 요청의 결과별 수, 진행 중은 IN_PROGRESS).
  • 추가: GET /analytics/overview의 excludedBotSessions(집계에서 뺀 봇 세션 수), GET /analytics/products 행의 clickThroughRate(목록 노출 → 상세 조회).
  • 상점 플랫폼 분석 › 애널리틱스: 개요의 이탈률을 반송률로 바꿨습니다(정의는 같습니다). 개요에 실시간 방문, 전환 퍼널에 결제 단계 결과·기기별 퍼널, 상품 분석에 노출 → 조회, 유입 경로에 첫 페이지·이탈 페이지, 새 검색 탭. 좌측 메뉴로 다시 들어와도 마지막으로 고른 기간을 유지합니다.
  • 원본 이벤트가 남아 있는 날(최근 89일)의 집계도 새 기준으로 다시 계산됩니다(10분마다 조금씩).
  • @sayren/store-sdk 0.7.0: analytics.realtime·devices·pages·search·checkout.

구매자 토큰 갱신

  • 변경: 같은 리프레시 토큰으로 동시에 갱신해도 로그아웃되지 않습니다. 쓴 토큰은 30초 안에 3번까지 더 받아 새 쌍을 주고, 30초가 지나거나 3번을 넘으면 401 INVALID_REFRESH_TOKEN입니다.

클레임 처리

  • 추가: GET /claims의 status가 쉼표로 여러 상태를 받습니다(예: REQUESTED,HELD,COLLECTED). 모르는 값은 400 VALIDATION_FAILED입니다.
  • 추가: 클레임 응답의 holdExpiresAt(보류 자동 해제 시각)·holdUsed(보류 사용 여부). 보류는 해제한 뒤에도 다시 할 수 없고, 7일이 지나면 자동으로 풀려 접수로 돌아옵니다. 끝난 클레임은 보류할 수 없습니다.
  • 추가: POST /claims/{claimId}/complete-exchange 교환 완료. 재배송 중인 교환을 끝내고 CLAIM.COMPLETED 웹훅을 보냅니다.
  • 변경: 같은 클레임을 동시에 처리하면 한 요청만 적용되고 나머지는 409 INVALID_STATUS입니다. 동시 승인으로 환불이 두 번 나가지 않습니다.
  • 변경: 보류는 접수(REQUESTED) 상태에서만 할 수 있습니다. 그 밖의 상태는 409 INVALID_STATUS입니다.
  • 변경: 재배송 중(REDELIVERING)인 교환은 거절할 수 없습니다(409 INVALID_STATUS). 교환 완료로 마칩니다.
  • 이미 재배송 중인 교환은 셀러가 교환 완료를 눌러야 끝납니다. 끝나기 전에는 진행 중인 클레임으로 남습니다.
  • 상점 플랫폼: 판매 › 클레임이 처리 필요·진행 중·전체 탭과 행마다 다음 처리 버튼(수거 등록·수거 완료·환불 승인·재배송 등록·교환 완료)으로 바뀌었습니다. 반품 환불 승인에 추가 차감, 보류·보류 해제, 상세 보기(단계·처리 이력)가 생겼습니다.
  • @sayren/store-sdk 0.6.0: claims.completeExchange, claims.list의 status 배열. 클레임 응답의 holdExpiresAt·holdUsed를 필수로 읽습니다.

2026-09-24

스토어프론트 템플릿 TanStack Router 전환

  • 변경: MCP가 내려주는 스토어프론트 템플릿이 React Router에서 TanStack Router(TanStack Start, SSR)로 바뀌었습니다. 화면과 구매 흐름은 같고 파일 구성이 app/에서 src/(파일 기반 라우트)로 바뀌었습니다. 이미 만든 쇼핑몰은 그대로 동작합니다. MCP로 만들기를 참고하십시오.
  • @sayren/mcp 0.1.9.

상점 플랫폼 시작하기

  • 상점 플랫폼: 로그인 화면을 새로 만들었고, 상점 만들기가 대화상자에서 전용 화면으로 바뀌었습니다. 상점이 없는 계정은 로그인 직후 이 화면으로 갑니다.
  • 상점 플랫폼: 상점 선택 화면을 최근 사용 순으로 바꿨습니다. 상점이 많으면 검색·역할 필터가 생기고, 이용정지·폐점 상점은 들어갈 수 없게 표시합니다.
  • 상점 플랫폼: 새 상점의 시작하기(스토어프론트 만들기·테스트 결제 연결·첫 상품 등록). 화면에서 MCP 토큰을 바로 발급하고 연결을 확인합니다. MCP로 만들기를 참고하십시오.

소셜 로그인

  • 추가: 구매자 카카오·네이버·구글 로그인. 스토어프론트 API POST /auth/idp/{provider}/authorize, POST /auth/idp/token(PKCE). @sayren/storefront-sdk/auth의 idp()·idpCallback().
  • 추가: 관리 API GET·PATCH /store/login-settings, PUT·PATCH·DELETE /store/auth-providers/{provider}, POST /store/auth-providers/{provider}/test(상점 플랫폼 OWNER 전용).
  • 추가: PATCH /store·GET /store의 storefrontOrigins(스토어프론트 도메인). 소셜 로그인 리디렉트와 방문 분석 수집이 함께 쓰는 목록입니다. analyticsAllowedOrigins는 같은 값의 옛 이름으로 남습니다.
  • 추가: 상점 공개 설정 loginMethods. 이메일·비밀번호를 끄면 비밀번호 가입·로그인이 403 PASSWORD_LOGIN_DISABLED입니다.
  • 변경: 구매자 email이 null일 수 있습니다(소셜 가입에서 이메일을 받지 못한 경우). 소셜 전용 구매자의 탈퇴는 비밀번호 대신 최근 로그인 토큰으로 확인합니다(403 REAUTH_REQUIRED).
  • 상점 플랫폼: 설정 › 로그인(로그인 방법 카드, 스토어프론트 도메인, 자체 회원 연동). 방문 분석 수집 도메인 카드는 여기로 옮겼습니다.
  • @sayren/store-sdk 0.5.0, @sayren/storefront-sdk 0.6.0.

구매자 관리와 비회원 주문 설정

  • 추가: 관리 API GET /customers(검색·가입일·마케팅 수신·정렬), GET /customers/{customerId}, GET /customers/{customerId}/orders(전체 기간), POST /customers/{customerId}/withdraw(탈퇴 처리). 새 스코프 customer:r(OWNER·ADMIN·STAFF)·customer:rw(OWNER·ADMIN).
  • 추가: PATCH /store·GET /store의 guestCheckoutEnabled. false면 비회원의 주문서 생성·결제 요청이 403 GUEST_CHECKOUT_DISABLED입니다.
  • 추가: 스토어프론트 API GET /store 상점 공개 설정(guestCheckout 포함).
  • 상점 플랫폼: 고객 › 구매자(목록·상세·탈퇴 처리), 설정 › 상점 정보 › 비회원 주문.
  • @sayren/store-sdk 0.4.0(customers.*), @sayren/storefront-sdk 0.5.0(store.get).

구매자 인증

  • 추가: POST /auth/withdraw 구매자 탈퇴. 비밀번호로 본인 확인을 하고 계정·배송지·위시리스트·장바구니·적립금을 지웁니다. 주문·클레임·리뷰·문의 기록은 남습니다. 진행 중인 주문·클레임·결제가 있으면 409 WITHDRAWAL_BLOCKED입니다. @sayren/storefront-sdk 0.4.0에 auth.withdraw가 추가됐습니다.
  • 추가: POST /auth/anonymous 비회원 세션 발급({ cartToken, expiresAt }).
  • 변경: 비회원 장바구니 토큰(X-Cart-Token)이 비회원 세션이 됐습니다. 마지막 활동 후 30일이 지나면 만료되고 장바구니도 지워집니다. 담은 적 없는 세션은 하루 뒤 지워집니다. 로그인으로 회원 장바구니에 합친 토큰은 더는 쓸 수 없고, 만료·종료된 토큰으로 장바구니를 부르면 새 토큰이 발급됩니다.
  • 추가: POST /auth/signup의 cartToken. 가입과 함께 비회원 장바구니를 합칩니다.
  • @sayren/storefront-sdk 0.3.0에 @sayren/storefront-sdk/auth(createStorefrontAuth: anonymous·session·signUp·refresh·signOut)와 auth.anonymous가 추가됐습니다. 사용법은 구매자 인증을 참고하십시오.

상점 플랫폼 메뉴

  • 메뉴 순서가 대시보드 → 상품 → 판매 → 고객 → 분석으로 바뀌었습니다. 매출 리포트·결제 내역은 분석 섹션으로 옮겼습니다.
  • 애널리틱스는 메뉴 한 항목(분석 › 애널리틱스)이 되고, 개요·전환 퍼널·상품 분석·유입 경로는 화면 안 탭으로 오갑니다. 주소(/analytics/funnel 등)는 그대로입니다.

방문 분석 수집 도메인 제한

  • 추가: PATCH /store의 analyticsAllowedOrigins, 응답 analyticsAllowedOrigins. 목록을 정하면 그 오리진에서 보낸 방문 분석 수집만 받고, 목록 밖은 403 ORIGIN_NOT_ALLOWED입니다. 비우면(기본) 이전처럼 모두 받습니다.
  • store-sdk에 store.update가 추가됐습니다.
  • MCP verify_storefront가 방문 분석 시작(analytics-start)과 서버 API 클라이언트의 방문 식별자(analytics-ids)를 검사합니다.

방문 분석 수집

  • 쿼리만 다른 페이지(페이지 넘기기·검색어·필터 변경)를 서로 다른 페이지뷰로 셉니다. 이전에는 같은 경로를 10초 안에 다시 보면 한 번으로 셌습니다.
  • 수집 한도가 방문자당 분당 120개로 늘었습니다. @sayren/storefront-sdk 0.2.1은 전체 분당 100개까지만 보내고, 상한으로 버린 페이지뷰의 상품 조회·체류는 보내지 않습니다.

2026-09-23

방문 분석 통계 조회

  • 추가: GET /analytics/overview·GET /analytics/products·GET /analytics/sources. 스토어프론트에서 수집한 방문을 날짜별로 집계해 개요·퍼널·상품·유입 통계를 돌려줍니다. 방문 분석 참고.
  • 새 스코프 analytics:r. 상점 플랫폼 로그인에서는 OWNER·ADMIN이 가집니다. API 토큰·앱에는 발급할 때 지정합니다.
  • store-sdk에 analytics.overview·analytics.products·analytics.sources가 추가됐습니다.

방문 분석

  • 스토어프론트 SDK에 방문 분석 모듈 @sayren/storefront-sdk/analytics가 추가됐습니다. 페이지뷰·상품 조회·목록 노출·검색·체류 시간을 수집합니다. 방문 분석 참고.
  • 추가: POST /storefront/v1/events(수집), SDK analyticsIdsFromCookie, createStorefrontClient의 visitorId·sessionId 옵션.
  • 스토어프론트 API 요청에 x-sayren-visitor·x-sayren-session 헤더를 실으면 장바구니 담기·빼기, 결제 시작, 구매가 서버에서 그 방문에 이어 기록됩니다.

문서 주소

  • 문서가 https://docs.sayren.app/(루트)로 옮겨졌습니다. 예를 들어 웹훅 가이드는 https://docs.sayren.app/guides/webhooks입니다. 옛 주소 https://docs.sayren.app/docs/…는 새 주소로 영구 리다이렉트(301)합니다.

OpenAPI 문서 주소

  • OpenAPI 문서 주소가 바뀌었습니다. 관리 API는 https://api.sayren.app/openapi/store.json, 스토어프론트 API는 https://api.sayren.app/openapi/storefront.json입니다(YAML은 확장자만 .yaml). 옛 주소 /docs/admin.json·/docs/storefront.json은 새 주소로 리다이렉트(308)합니다.
  • 모든 오퍼레이션에 요청 본문·응답 스키마가 실립니다. 오퍼레이션별 필요 스코프는 x-required-scopes, 상점 플랫폼 로그인이 필요한 오퍼레이션은 x-console-only: true로 표시됩니다. 멱등키를 받는 오퍼레이션에는 Idempotency-Key 헤더가 문서화됐습니다.
  • GET /products의 status 쿼리가 문서에 추가됐습니다.

MCP로 상점 운영

  • MCP에 관리 API 도구 4개(list_operations·describe_operation·call_api_read·call_api_write)가 추가됐습니다. 대화로 상점을 조회하고 바꿀 수 있습니다. AI로 상점 운영하기 참고.
  • get_store_context의 productCount는 등록된 상품 전체 수입니다. 구매자에게 보이는 수는 publicProductCount입니다.
  • npx @sayren/mcp create <폴더>로 스토어프론트 템플릿을 한 번에 받습니다.

2026-09-19

결제(PG) 연결

  • 셀러가 직접 계약한 토스페이먼츠·포트원으로 결제를 받습니다. 결제 대금은 PG가 셀러에게 바로 정산합니다. 결제 설정 참고.
  • PG마다 테스트 키와 라이브 키를 따로 등록하고, 샌드박스 모드(기본 켜짐)로 어느 키로 결제할지 정합니다. 샌드박스 모드를 끄려면 라이브 전환 조건 5개를 채워야 합니다. 샌드박스에서 라이브로 전환하기 참고.
  • 추가: GET·PUT /store/payment-settings, PUT /store/payment-providers/tosspayments, PUT /store/payment-providers/portone, PATCH·DELETE /store/payment-providers/{provider}, DELETE /store/payment-providers/{provider}/credentials/{environment}, POST /store/payment-providers/{provider}/verify. 상점 플랫폼에 로그인한 OWNER만 호출할 수 있습니다. 새 에러 409 LIVE_GATE_NOT_MET·LIVE_PAYMENTS_DISABLED(error.details.conditions에 조건별 충족 여부), 409 PAYMENT_PROVIDER_VERIFICATION_FAILED·PAYMENT_CREDENTIAL_ACTIVE·PAYMENT_CREDENTIAL_CHANGED· PAYMENT_PROVIDER_CHANGED, 404 PAYMENT_CREDENTIAL_NOT_FOUND. 토스페이먼츠 결제위젯 키는 400 VALIDATION_FAILED로 거부되고 fieldErrors에 credentials.clientKey·credentials.secretKey가 담깁니다.
  • GET /store/payment-settings 응답의 candidates·environment로 지금 결제에 쓰는 PG와 결제 환경을, providers[].routingExclusion으로 후보에서 빠진 사유를 확인합니다.
  • 가맹점을 바꾸거나 연결을 해제해도 이전 키는 보관돼 그 가맹점으로 받은 결제의 취소·환불에 계속 쓰입니다.
  • 두 PG를 모두 쓰면 라우팅 방식으로 AUTO_FAILOVER(기본) 또는 PRIORITY_DISPLAY를 고릅니다.
  • 샌드박스 모드에서 받은 결제는 테스트 결제입니다. 관리 API GET /orders·GET /orders/{orderId}·GET /order-items와 주문상품 처리 응답, 스토어프론트 API 내 주문·비회원 주문 조회 응답에 testPayment가 추가됐습니다. GET /orders·GET /order-items는 쿼리 testPayment=true|false로 거를 수 있습니다. 세 주문 조회 API가 API 레퍼런스에 문서화됐습니다.
  • 웹훅 ORDER.*·CLAIM.*의 data에 testPayment가 추가됐습니다. 수신 측에서 data를 모르는 필드를 거부하는 방식(strict)으로 검증한다면 이 필드를 허용하도록 바꿔야 합니다. 웹훅 참고.
  • 테스트 결제 주문에는 리뷰를 쓸 수 없습니다. 작성 가능 목록에서 빠지고, 작성하면 409 REVIEW_NOT_ALLOWED_FOR_TEST_ORDER입니다.
  • 결제 요청 POST /storefront/v1/checkout/{checkoutId}/payment에 새 에러 409 PAYMENT_NOT_CONFIGURED, 503 PAYMENT_PROVIDER_UNAVAILABLE이 추가됐습니다. pgProvider는 tosspayments 또는 portone입니다. 주문서 생성과 결제 요청 응답에 testPayment가 추가됐습니다.
  • 호환성 변경 pgParams에서 successUrl·failUrl을 제거하고 attemptId·provider·mock·environment를 추가했습니다. popupUrl만 쓰는 연동은 영향이 없습니다.
  • 추가: GET /storefront/v1/payments/{paymentId}. 결제 상태(status)·주문번호·테스트 결제 여부·만료 시각을 인증 없이 조회합니다. 팝업의 결과 메시지를 받지 못했을 때 쓰십시오. 결제 연동 참고.
  • 결제 승인 POST /storefront/v1/payments/{paymentId}/confirm 본문에 선택 필드 attemptId가 추가됐습니다. 새 에러 402 PAYMENT_NOT_APPROVED, 409 PAYMENT_RESULT_UNKNOWN·ATTEMPT_NOT_CURRENT·INVALID_ATTEMPT_STATE· PAYMENT_SESSION_OUTDATED·PG_AMOUNT_MISMATCH·PG_ENVIRONMENT_MISMATCH·LATE_APPROVAL_CANCELED, 502 PG_REQUEST_FAILED, 503 PG_CREDENTIALS_UNAVAILABLE·PG_MERCHANT_MISMATCH. (이 API는 2026-09-25에 제거됐습니다.)
  • 호환성 변경 카드 거절처럼 승인되지 않은 것이 확정되면 결제 세션이 failed가 아니라 pending으로 남습니다. 같은 세션에서 다른 결제사나 같은 결제사로 다시 결제할 수 있습니다.
  • 호환성 변경 결제수단의 뜻이 요청한 결제수단에서 실제 결제수단으로 바뀌었습니다. 실제 결제수단은 PG가 알려 준 수단이고, 알려 주지 않으면 요청한 수단입니다. 카드로 요청하고 결제창에서 간편결제로 결제하면 두 값이 다릅니다. 필드 형태와 값 목록은 그대로입니다.
    • GET /sales-report: method 쿼리 필터와 rows[].method. 카드였던 행 일부가 EASY_PAY 등으로 옮겨 갈 수 있습니다.
    • GET /orders/{orderId}의 payment.method. 관리 API 결제수단에는 간편결제가 없어 간편결제는 종전처럼 CARD로 나갑니다. 실제 결제수단을 그대로 보려면 새로 추가한 payment.approvedMethod를 쓰십시오(추가). 결제 기록이 없는 오래된 주문은 null입니다.
    • 스토어프론트 GET /me/orders, GET /me/orders/{orderId}, POST /guest/orders/{orderId}의 payment.method. EASY_PAY가 그대로 나갑니다.
    • 주문 응답은 새 주문부터 적용되고, 기존 주문은 요청한 결제수단 그대로입니다. 웹훅과 결제 요청·주문서의 paymentMethod는 바뀌지 않았습니다.
  • 호환성 변경 새로 만드는 결제 id(paymentId)가 길어졌습니다(pay_ 뒤 32자). 이미 만든 결제는 그대로입니다. id의 형식이나 길이를 가정해 저장·검증했다면 바꿔야 합니다.
  • 결제 시작과 결제 승인의 에러 응답이 API 레퍼런스에 문서화됐습니다.
  • 결제 관련 에러 메시지(error.message)의 문구를 바꿨습니다. 에러 코드는 그대로이니 분기는 error.code로 하십시오.
  • Storefront SDK: checkout.confirmPayment의 세 번째 인자 { attemptId }, payments.getStatus, 상태 값 목록 PAYMENT_STATUS_VALUES를 추가했습니다.
  • Store SDK: paymentSettings.*, liveGateErrorDetailsSchema, PAYMENT_ROUTING_EXCLUSION_CODES, 주문 목록 testPayment 인자를 추가했습니다. 토스페이먼츠 결제위젯 키는 요청 전 검증에서 ZodError로 거부됩니다.

매출 리포트

  • 추가: GET /sales-report. 기간·PG·결제수단별 결제·환불·순매출을 집계하며 settlement:r 스코프가 필요합니다. 테스트 결제는 기본으로 빠지고 includeTest=true로 포함합니다. 매출 리포트 참고.
  • 호환성 변경 구매확정 때 정산 내역을 쌓지 않습니다. 플랫폼 수수료를 공제하는 정산은 폐기됐고, 기존 정산 내역은 그대로 조회됩니다.
  • GET /settlements, GET /settlements/{settlementId}는 폐기 예정입니다. 응답에 Deprecation: true와 Link: </v1/sales-report>; rel="successor-version" 헤더가 붙습니다.
  • Store SDK: salesReport.get을 추가했고 settlements.*는 폐기 예정입니다.
  • 상점 플랫폼 정산 › 정산 내역은 매출 › 매출 리포트로 바뀌었습니다.

스토어프론트 만들기 (MCP)

  • 추가: sayren MCP 서버(npx @sayren/mcp). 코딩 도구에 붙이면 상점 컨텍스트·검증된 템플릿·생성 규칙· 결과 검증을 도구로 제공합니다. 스토어프론트 만들기 참고.
  • 템플릿은 React Router(SSR) 기반이고 구매 흐름 전체(홈·목록·상세·장바구니·주문서·결제·주문 내역)를 담습니다.
  • 상점 플랫폼 개발자 › 스토어프론트 만들기에서 연결 설정과 프롬프트를 복사할 수 있습니다.

결제 내역

  • 추가: GET /payments, GET /payments/{paymentId}. 결제 건 단위 조회이고 settlement:r 스코프가 필요합니다. 주문이 만들어지지 않은 결제(카드 거절·결제창 이탈·승인 결과 확인 중)도 나오고, 상세에는 시도 이력과 환불 이력이 함께 담깁니다. 실패한 환불 요청도 남습니다. 결제 내역 참고.
  • 기간 기준은 결제 시작 시각이고 최대 92일입니다. 생략하면 최근 30일이며, 테스트 결제는 includeTest=true일 때만 포함합니다.
  • Store SDK: payments.list·payments.get을 추가했습니다.
  • 상점 플랫폼 매출 › 결제 내역을 추가했습니다.

공통

  • 에러 엔벨로프 $.error에 선택 필드 details를 추가했습니다. 일부 에러 코드에만 보조 데이터가 담기며, 없으면 필드가 빠집니다.

웹훅 전송

  • 등록한 주소로 이벤트를 실제로 전송합니다. 요청 형식, 헤더, 이벤트별 data는 웹훅을 참고하십시오. ORDER.DELIVERED, ORDER.ADDRESS_CHANGE_REQUESTED, 교환 클레임의 CLAIM.COMPLETED는 아직 발송되지 않습니다.
  • Sayren-Signature 헤더로 서명합니다(t=<unix 초>,v1=<HMAC-SHA256 hex>). 시크릿 교체 후 24시간 동안은 새 시크릿과 기존 시크릿 서명을 함께 싣습니다.
  • 실패하면 1분·5분·30분·2시간 뒤에 재시도합니다. 첫 실패부터 24시간 이상 성공 없이 실패가 이어지면 구독이 SUSPENDED로 바뀝니다.
  • 추가: POST /webhooks/{webhookId}/resume, POST /webhooks/{webhookId}/deliveries/{deliveryId}/redeliver, POST /webhooks/{webhookId}/deliveries/redeliver-failed. 재전송은 원본 기록당 한 번입니다. 새 에러 코드 WEBHOOK_DELIVERY_NOT_FOUND(404), DELIVERY_NOT_FAILED·DELIVERY_ALREADY_REDELIVERED· WEBHOOK_SUSPENDED(409).
  • GET /webhooks/{webhookId}/deliveries가 실제 전송 기록을 반환합니다(최근 30일). status·limit 쿼리를 받습니다. 응답에 status: PENDING과 url·errorCode·attemptCount·attemptedAt·nextAttemptAt·responseBodyExcerpt· redeliveryOf·redeliveredAs 필드가 추가됐습니다.
  • POST /webhooks/{webhookId}/test가 등록한 주소로 실제 요청을 보내고 측정한 응답 코드와 응답 시간을 반환합니다. 응답에 errorCode가 추가됐습니다.
  • 웹훅 응답에 suspendedAt이 추가됐고, createdAt 등 일시 필드가 ISO 8601 형식으로 통일됐습니다.
  • PUT /webhooks/{webhookId}는 정지를 해제하면서 연속 실패 기록도 초기화합니다. DELETE /webhooks/{webhookId}는 전송 기록도 함께 삭제합니다.
  • 호환성 변경 등록·수정 때 구독 주소를 검사합니다. 내부 대역으로 해석되는 호스트나 사용자 정보(user:pass@)가 들어간 주소는 400 VALIDATION_FAILED로 거부됩니다.
  • Store SDK: webhooks.resume·webhooks.redeliver·webhooks.redeliverFailed, listDeliveries의 status·limit 인자, 요청 본문 스키마 webhookEventSchema, 서명 검증 verifyWebhookSignature(@sayren/store-sdk/server)를 추가했습니다.

API 로그

  • 상점으로 들어온 관리 API 요청을 7일간 기록하고 상점 플랫폼에서 조회할 수 있습니다. API 로그 참고.

앱 관리

  • 추가: GET /store/api-clients, GET /store/api-clients/{clientId}, POST /store/api-clients/{clientId}/rotate-secret, POST /store/api-clients/{clientId}/deactivate
  • 비활성화한 앱이 발급한 토큰과, 그 토큰을 교환해 받은 storeToken은 즉시 401로 거부됩니다.
  • 호환성 변경 앱 토큰의 권한은 사용자 역할, 토큰 scope, 앱 등록 scope가 모두 허용하는 범위로 제한됩니다. X-Store-Id 직접 호출과 token-exchange에 모두 적용됩니다.
  • 호환성 변경 API 토큰 발급, 클라이언트 등록(DCR), 앱 관리는 상점 플랫폼에서 로그인한 OWNER·ADMIN만 할 수 있습니다.
  • 웹훅 시크릿을 교체하면 기존 시크릿이 24시간 동안 함께 유효합니다. 유효한 시크릿은 최대 2개입니다.

2026-09-18

  • 호환성 변경 상품 옵션을 옵션 축(optionGroups) × 값 → 판매 단위(variants) 구조로 바꿨습니다. 재고는 판매 단위별로 관리하고, 상품의 stockQuantity는 판매 가능한 판매 단위의 재고 합계입니다.
  • GET·PUT /products/{productId}/options가 옵션 모델 전체를 다룹니다. PUT에서 판매 단위의 stockQuantity를 생략하면 기존 재고를 유지합니다.
  • 재고 변경 경로가 PATCH /products/{productId}/variants/{variantId}/stock으로 바뀌었습니다.
  • 판매 단위에 sku·barcode·costPrice·weightGram이 추가됐습니다.
  • 장바구니·주문·클레임·리뷰의 optionId는 판매 단위의 variantId와 같은 값입니다. ID 형식은 고정되어 있지 않으니 접두어로 판별하지 마십시오.
  • 스토어프론트 상품 상세가 options 대신 optionGroups·variants를 반환합니다.

2026-09-17

  • 셀러 로그인이 외부 IdP로 일원화됐습니다. GET /oauth2/authorize는 IdP 로그인 화면으로 이동하고, 인증이 끝나면 이전과 같이 redirect_uri로 code를 돌려줍니다. 토큰 발급·교환 방식은 바뀌지 않았습니다.

2026-07-19

  • 1:1 문의 API를 추가했습니다. 셀러용 /customer-inquiries, 구매자용 /storefront/v1/me/customer-inquiries.

2026-07-16

  • Store SDK·Storefront SDK: 로그인 세션 관리 createStoreSession·createStorefrontSession을 추가했습니다. 토큰 저장, refresh token 회전, 무음 갱신을 SDK가 처리합니다.

2026-07-14

  • userToken과 X-Store-Id 헤더로 token-exchange 없이 상점 리소스를 호출할 수 있습니다.
  • 카테고리에 표시 상태, PC·모바일 노출, 상품 진열, SEO 설정 필드가 추가됐습니다.

2026-07-11

  • 클레임 승인과 셀러 직권취소가 결제 취소(환불)까지 처리합니다.

2026-07-10

  • 카테고리 관리 API(/categories, 트리·검색·생성·수정·이동·삭제)와 category:r|rw 스코프를 추가했습니다.

2026-07-09

  • 호환성 변경 모든 응답이 표준 엔벨로프 { meta, data, error }를 따릅니다. OAuth 엔드포인트는 예외입니다.
  • 호환성 변경 관리 API 인증을 OAuth 2.1로 바꿨습니다. authorization_code + PKCE, refresh token 회전, client_credentials, token-exchange(audience={storeId})를 지원합니다. password grant는 제거했습니다.
  • API 토큰(/store/api-tokens), 클라이언트 등록(POST /oauth2/register), 구매자 위임 토큰 발급을 추가했습니다.
  • 토큰 서명을 RS256으로 바꾸고 /.well-known/jwks.json, /.well-known/oauth-authorization-server를 제공합니다.
  • 호환성 변경 상점 생성·폐쇄·양도와 초대 수락을 관리 API에서 제거했습니다. 상점 플랫폼에서 처리합니다.

이 페이지 목차

2026-10-13서비스 주소 이전 (sayren.io, sayren 0.9.0 · @sayren/mcp 0.24.0 · @sayren/app 0.9.0)2026-10-08앱 매니페스트 파일 이름 (sayren 0.8.1)개발자 서버에서 실행·에이전트 도구 (@sayren/store-sdk 0.30.13 · @sayren/app 0.8.0 · sayren 0.8.0)주문 흐름에서 앱 쓰기 (@sayren/store-sdk 0.30.13 · @sayren/app 0.8.0 · sayren 0.8.0)앱 공개 범위 — 비공개 앱·퍼블릭 앱앱 공개 HTTP·예약 작업·저장소 (@sayren/store-sdk 0.30.12 · @sayren/app 0.7.0 · sayren 0.7.0)앱 화면·HTTP 핸들러앱 권한 업데이트·실행 기록 (@sayren/store-sdk 0.30.11 · @sayren/app 0.6.1 · sayren 0.6.1)2026-10-05테스트 결제 주문 리뷰 설정·주문상품 리뷰 작성 가능 (@sayren/store-sdk 0.30.8 · @sayren/storefront-sdk 0.20.17)배송·주문 연락처 형식 (@sayren/storefront-sdk 0.20.16)흐름 기록 행동 이름·묶음에서 먼저 취소된 상품즉시 취소·묶음 클레임·판매자 취소 미리보기 (@sayren/store-sdk 0.30.7 · @sayren/storefront-sdk 0.20.15)기본 흐름 시드·흐름 만들기 순서 (@sayren/store-sdk 0.30.6)구매자 주문의 환불 금액·클레임 중 흐름 행동 (@sayren/storefront-sdk 0.20.14)권한 표 수수료·신청 안내·환불 대기 (@sayren/store-sdk 0.30.5 · @sayren/storefront-sdk 0.20.13)흐름은 빈 흐름을 편집한 그래프 하나승인 요청 작업 이름·미리보기 (@sayren/store-sdk 0.30.4)신청 전 미리보기·흐름 판정 (@sayren/storefront-sdk 0.20.12)프리셋 판매자 취소구매자 요청 철회 행동2026-10-04흐름 진행 단위 게시 반영플랫폼 행동 입력 칸·이전 자료 (@sayren/store-sdk 0.30.3 · @sayren/storefront-sdk 0.20.11)버튼 입력 칸·구매자 단계 요약 (@sayren/store-sdk 0.30.2 · @sayren/storefront-sdk 0.20.10)주문 흐름 확장 (@sayren/store-sdk 0.30.1 · @sayren/storefront-sdk 0.20.9)흐름 지정을 상품·카테고리로 (@sayren/store-sdk 0.30.0)주문 흐름 (#115) (@sayren/store-sdk 0.29.8 · @sayren/storefront-sdk 0.20.8)날짜별 재고·숙박 예약형 (#118) (@sayren/store-sdk 0.29.8 · @sayren/storefront-sdk 0.20.8)2026-10-02쿠폰 받기·회원가입 쿠폰 (@sayren/store-sdk 0.29.7 · @sayren/storefront-sdk 0.20.7)주문서 상품 대표 이미지 (@sayren/storefront-sdk 0.20.6)주문·상품 번호 (@sayren/store-sdk 0.29.6 · @sayren/storefront-sdk 0.20.5 · @sayren/mcp 0.23.7, 템플릿 0.2.15)MCP 대화에서 승인 요청 처리 (@sayren/mcp 0.23.6)버전의 기준 템플릿 버전 (@sayren/mcp 0.23.5, 템플릿 0.2.14)컬렉션 홈 섹션 (@sayren/mcp 0.23.4, 템플릿 0.2.13)적립금 화면 안내 (@sayren/storefront-sdk 0.20.4 · @sayren/mcp 0.23.4, 템플릿 0.2.13)적립금 전액 결제 (@sayren/store-sdk 0.29.5 · @sayren/storefront-sdk 0.20.3 · @sayren/mcp 0.23.3, 템플릿 0.2.12)판매량 상위 컬렉션 (@sayren/store-sdk 0.29.4 · @sayren/storefront-sdk 0.20.2)적립금 결제 사용·취소 환불 (@sayren/store-sdk 0.29.3 · @sayren/storefront-sdk 0.20.1 · @sayren/mcp 0.23.2, 템플릿 0.2.11)조건 자동 컬렉션 (@sayren/store-sdk 0.29.2)적립금 기존 잔액 이전적립금 — 리뷰 적립 기본값 끔·정책·구매 적립·소멸·조정 (@sayren/store-sdk 0.29.1)결제창 없는 결제 완료·응답 결제수단 확장 (@sayren/storefront-sdk 0.20.0)스토어프론트 템플릿 컬렉션 화면 (@sayren/mcp 0.23.1, 템플릿 0.2.10)컬렉션 (@sayren/store-sdk 0.29.0 · @sayren/storefront-sdk 0.19.0)원격 MCP 템플릿 원본 읽기 (@sayren/mcp 0.23.0)스토어프론트 CLI (sayren 0.4.0)결제수단 반복 실패 경고 (@sayren/store-sdk 0.28.3)출고지·반품지 주소록 조회 개방템플릿 링크 미리 불러오기·자산 캐시 (@sayren/mcp 0.22.2)결제 실패의 결제수단 사용 불가 안내 (@sayren/storefront-sdk 0.18.4)리뷰 랭킹순 정렬 기준2026-10-01상점 정보 수정 드라이런의 변경 미리보기리뷰 옵션별 필터·내가 쓴 리뷰·리뷰 사진 (@sayren/storefront-sdk 0.18.3)상품 이미지 일부 수정, 드라이런 본문 검증MCP 상품 이미지 업로드 (@sayren/mcp 0.22.0)사업자 정보·상품 정가·상품 문의 유형 (@sayren/store-sdk 0.28.2 · @sayren/storefront-sdk 0.18.2)원격 MCP에서 상점 정보 수정 요청요금제 개편 — Free·Pro·프리미엄·엔터프라이즈 (@sayren/store-sdk 0.28.0 · @sayren/mcp 0.20.0)파괴적 변경 — 셀러 토큰을 Sayren 계정 토큰으로 일원화 (@sayren/store-sdk 0.27.0 · @sayren/mcp 0.19.0 · sayren 0.3.0)2026-09-30주문 가능 상태·공개 조건·샘플 상품 (@sayren/storefront-sdk 0.18.0 · @sayren/store-sdk 0.25.0)유료 요금제 결제계정 토큰 (@sayren/store-sdk · sayren CLI · @sayren/mcp)요금제·사용량 조회 (@sayren/store-sdk 0.24.0 · @sayren/mcp 0.17.0)스토어프론트 CLI·원격 MCP: 받은 폴더에 .mcp.json (sayren 0.2.0 · @sayren/mcp 0.16.0)store-sdk: 세션 로그아웃이 서버 토큰을 폐기합니다 (@sayren/store-sdk 0.23.0)스토어프론트 호스팅: 배포 진행 조회·템플릿 변경 폐기상품 옵션: 추가 선택·직접 입력·저장 옵션 (@sayren/store-sdk 0.21.0 · @sayren/storefront-sdk 0.17.0 · @sayren/mcp 0.14.0)상품 유형 축소 (@sayren/store-sdk 0.20.0 · @sayren/storefront-sdk 0.16.0 · @sayren/mcp 0.13.0)2026-09-29원격 MCP (@sayren/mcp 0.11.0)스토어프론트 템플릿 쿠키·테마 (@sayren/mcp 0.10.0)스토어프론트 호스팅 API (@sayren/store-sdk 0.19.0)상품 모델 재구성 (@sayren/store-sdk 0.18.0 · @sayren/storefront-sdk 0.15.0 · @sayren/mcp 0.9.0)2026-09-28앱 토큰의 상점 범위와 구성원 변경 제한쿠폰 (@sayren/store-sdk 0.17.0 · @sayren/storefront-sdk 0.14.0 · @sayren/mcp 0.8.0)비실물 상품 (@sayren/store-sdk 0.17.0 · @sayren/storefront-sdk 0.14.0 · @sayren/mcp 0.8.0)웹훅 sequence (@sayren/store-sdk 0.17.0)2026-09-27MCP 스토어프론트 템플릿 기본 라이브러리 (@sayren/mcp 0.7.0)비회원 주문 조회 시도 한도판매자 취소 웹훅 (@sayren/store-sdk 0.16.0)MCP 스토어프론트 템플릿 (@sayren/mcp 0.6.1)MCP 스토어프론트 템플릿 (@sayren/mcp 0.6.0)구성원 초대AI 작업 승인과 감사 로그2026-09-26MCP 검증 결과 집계웹훅·API 실패 원인 찾기배송비 규칙과 출고지·반품지현금영수증 발급택배 추적구매자 동의 이력환불 정책과 수량 단위 부분 취소배송완료 처리와 자동 전이상품 이미지 업로드 (호환되지 않는 변경)2026-09-25결제 흐름 개편 (호환되지 않는 변경)상세설명 에디터구매자 계정 연결·자체 회원 연동방문 분석 2단계구매자 토큰 갱신클레임 처리2026-09-24스토어프론트 템플릿 TanStack Router 전환상점 플랫폼 시작하기소셜 로그인구매자 관리와 비회원 주문 설정구매자 인증상점 플랫폼 메뉴방문 분석 수집 도메인 제한방문 분석 수집2026-09-23방문 분석 통계 조회방문 분석문서 주소OpenAPI 문서 주소MCP로 상점 운영2026-09-19결제(PG) 연결매출 리포트스토어프론트 만들기 (MCP)결제 내역공통웹훅 전송API 로그앱 관리2026-09-182026-09-172026-07-192026-07-162026-07-142026-07-112026-07-102026-07-09