Skip to content

BE handoff — 상품 계약·entitlement·provisioning

구현 도메인 (Supabase prototype)

  • product_catalog: 판매 가능한 모듈과 서버 가격
  • product_plans / product_plan_items: Goldnus 관리업무, 일반 ERP 프리셋
  • product_workspaces: 인증 사용자 소유 workspace
  • product_checkout_sessions / product_checkout_items: 견적 snapshot, 변경 action, idempotency key
  • product_subscriptions / product_subscription_items: 계약, 결제 주기, 가격 snapshot, 효력일
  • product_entitlements: workspace가 실제 사용할 수 있는 module key와 유효기간
  • product_provisioning_jobs: 결제 승인 이후 앱 초기화 상태

제품 표시명과 분류의 정본은 src/composables/productVocabulary.jsdocs/decisions/PRODUCT-NAMING-TAXONOMY-2026-07-26.md다. PRODUCT_CATEGORIES는 도메인, PRODUCT_GROUPS는 프로퍼티 도메인의 거래·분양과 운영 제품군을 표현한다. 실행 제품의 module_key는 route, 코드와 DB aggregate가 공유하는 bounded-context identifier다.

마이그레이션 정본은 supabase/migrations/20260712031000_product_subscriptions.sql이다. 2026-07-12 leyve.net 프로젝트(fhrkrcgihabltfeiemyz)에 적용했고 migration history에도 동일 version을 기록했다.

상태와 책임

draft → configured → checkout_pending → payment_authorized → provisioning → active

  • 견적 생성 시 서버가 가격·VAT·수량을 다시 계산한다.
  • 결제 webhook은 서명 검증 후 idempotent하게 승인 상태를 기록한다.
  • provisioning 성공 후에만 entitlement를 활성화한다.
  • 프론트가 보낸 합계나 active 상태를 신뢰하지 않는다.
  • 상품 제거는 계약 정책에 따라 effective_at을 갖는 예약 변경으로 저장한다.
  • 추가·제거가 함께 요청되면 동일 change request에 담되 entitlement 효력 시점은 item별로 기록한다.
  • payment_failed에서는 provisioning job을 만들지 않고, payment_authorized 이후에만 idempotent job을 생성한다.
  • provisioning 지연 중 기존 entitlement를 철회하지 않는다.

Supabase RPC

  • begin_product_checkout(...) → checkout uuid: 인증·workspace 소유권 확인, 카탈로그 유효성 검사, 가격/VAT 서버 재계산, snapshot 저장
  • confirm_product_checkout(checkout_id, outcome) → status: 결제 실패 또는 provisioning 시작
  • complete_product_provisioning(checkout_id) → active: subscription item과 entitlement를 idempotent하게 활성화

신규 Company FE는 저장된 결제수단 조회가 연결되기 전까지 payment_method=invoice만 보낸다. RPC의 registered-card 값은 향후 실제 카드 등록·조회 계약을 연결할 때 사용하며, 카드 별칭을 프론트에서 임의 생성하지 않는다.

product_workspaces.slug='goldnus'는 현재 사용자별 RLS 아래 유지되는 prototype checkout 호환 키일 뿐 고객 표시명이 아니다. UI의 Company 이름은 Company 계정 정본을 사용한다. 실제 결제수단·결제내역 API가 생기기 전에는 레거시 샘플 카드·계약·결제 행을 API 응답처럼 노출하지 않는다.

catalog는 anon/authenticated 읽기 전용이다. 사용자별 테이블은 RLS로 owner_user_id = auth.uid()만 조회할 수 있고 write는 security-definer RPC만 허용한다. RPC는 search_path = public, pg_temp로 고정하고 호출자의 workspace 소유권을 다시 확인한다.

원격 검증 결과

  • product table 10개, 전부 RLS 활성
  • catalog 11개, plan 3개
  • RPC 3개 존재 확인
  • Goldnus 인증 컨텍스트 transaction smoke: checkout 1 / active item 5 / entitlement 5 / succeeded job 1
  • smoke transaction rollback 후 persisted workspace 0 / checkout 0

Goldnus ERP와 Leyvecloud ERP는 독립 권한 키가 아니라 plan preset이다. 기능 접근 판단은 반드시 service-charge, accounting, facility 같은 안정적인 module key를 사용한다.

검수 계정 노출 프로필 (2026-07-19)

상품 판매 상태인 lifecycle과 기술 구현 준비도인 implementationStage를 분리한다. 프런트 내부 catalog가 허용하는 준비도는 server-core | partial-server | prototype | planned이며 공개 catalog projection에는 이 내부 필드를 내보내지 않는다.

  • goldnus@goldnus.com 또는 Goldnus Company 관리자: server-core 상품 전체를 메뉴·홈·상품 선택에 노출한다.
  • Goldnus 직원: server-core 필터를 먼저 적용하고 기존 Office entitlement와 교집합만 노출한다.
  • leysys@leysys.com 또는 Leysys Company: 미완성 상품과 catalog 밖 preview 앱까지 개발 검수 목적으로 노출한다.
  • 그 외 계정: 기존 Company 계약과 Office entitlement를 따른다.

현재 partial-server/prototype으로 분류한 상품은 purchasing, asset-management, accounting-source-documents, sales, long-term-repair-planning이다. 나머지 catalog 상품은 server-core다. 이 분류는 DB migration 존재 여부만으로 정하지 않고, 인증된 서버 저장·상태전이·권한 경계까지 고객이 검수 가능한지를 기준으로 갱신한다.

제품명과 안정 키

  • sales → 판매·매출채권 / Sales & Accounts Receivable
  • purchasing → 구매·매입채무 / Procurement & Accounts Payable
  • accounting-source-documents → 회계증빙관리 / Source Document Management (finance-assets)
  • asset-management → 고정자산관리 / Fixed Asset Management
  • tax-invoice → 전자문서 발행 / Electronic Document Issuance
  • human-capital → 인사관리 / Human Resources Management (HRM)
  • real-estate-brokerage → 매물·거래 / Property Listings & Transactions
  • property-sales → 분양관리 / Property Sales Management
  • electronic-records-management → 전자기록관리 / Electronic Records Management
  • supporting-documents → 업무증빙관리 / Supporting Document Management

후보 제품 crm, real-estate-brokerage, property-sales, supporting-documents, business-messaging, electronic-records-management은 네이밍 어휘에 존재해도 서버 catalog나 entitlement에 자동 등록하지 않는다. 실제 상품화는 owner 원장, route, 서버 capability, 주문 정책과 검증을 한 변경에서 추가한다.

회계증빙관리의 DB aggregate는 accounting_source_documents, 하위 객체 prefix는 accounting_source_document_*다. 업무증빙관리의 예정 aggregate는 supporting_documents다. 파일 저장 공통 capability는 document_blobs, document_versions, document_links를 사용하며 상품 entitlement로 만들지 않는다. 상세 계약은 docs/decisions/DOCUMENT-DOMAIN-BOUNDARIES-2026-07-26.md가 정본이다.

이 프로필은 프런트 출시 검수 정책이다. 서버의 Company·Office entitlement, RLS와 RPC 권한 검사를 넓히지 않는다. 클라이언트가 보낸 implementationStage, email 또는 노출 프로필을 서버 권한 근거로 신뢰하면 안 된다. 숨겨진 기존 entitlement는 상품 변경 checkout의 desired module set에서 보존해 UI 필터가 암묵적 해지를 만들지 않게 한다.

계약과 Billing Core 경계 (2026-07-26 개정)

  • contract-management는 독립 entitlement다.
  • contract-billing은 상품 키로 사용하지 않는다. 새 선택·계약·Office entitlement를 만들지 않으며 contract-to-cash preset은 contract-managementsales를 조합한다.
  • 후속 migration은 과거 product_catalog.contract-billing 행을 비활성화한다. 기존 계약·entitlement snapshot과 contract_billing_* 데이터는 감사·전환을 위해 보존하지만 capability 판정에는 사용할 수 없다.
  • 과거 /contract-billing/* 북마크 redirect는 FE 호환 정책이다. 서버는 이 URL을 entitlement나 Billing Core 권한 키로 해석하지 않는다.
  • Billing Core는 청구계획→회차별 채권→미수→수납 충당→반전의 상태·계산·이벤트 계약을 제공할 뿐 별도 entitlement를 요구하지 않는다.
  • 판매·매출채권, 관리비관리, 임대차관리는 공통 capability 골격을 재사용하되 product-owned 원장과 RPC를 합치지 않는다. 세부 경계는 docs/decisions/BILLING-FAMILY-BOUNDED-CONTEXTS-2026-07-19.md가 정본이다.

FE 라우팅 경계 (2026-07-13)

기존 고객의 /onboarding/products?mode=manage는 administration의 계정·계약 셸 안에서 렌더한다. URL·RPC·entitlement·provisioning 상태 계약은 변경하지 않았고, 부모 레이아웃만 보존한다. 신규 가입 흐름도 같은 URL을 사용할 수 있으므로 서버는 mode 쿼리를 권한 근거로 신뢰하지 않는다.

상품 선택 표면을 EDS radio-card·checkbox-card로 정정한 작업은 표시와 접근성만 바꾼다. bundleKey, module key 배열, 수량, 견적·결제 RPC 계약은 변경하지 않는다.

상품 구성 단계의 요약 카드는 클라이언트 lineItems로 계산한 VAT 포함 월 예상 이용료를 수량 입력과 함께 즉시 표시한다. 이는 의사결정용 예상치이며 결제 확정값이 아니다. begin_product_checkout은 기존과 동일하게 module key와 quantities.units·quantities.users를 받아 카탈로그 가격과 VAT를 서버에서 다시 계산하고, 이후 화면과 결제 처리는 서버 snapshot을 정본으로 사용해야 한다.