좌석 기반 청구는 계정의 사용자 수를 기준으로 고객에게 요금을 청구합니다. Dodo Payments는 add-on 시스템을 사용해 이를 구현합니다. 기본 구독 상품과 좌석 수를 나타내는 수량을 가진 좌석당 add-on으로 구성됩니다.
Implementation Tutorial
단계별 가이드 및 코드 예제.
Add-ons Documentation
좌석 기반 청구를 지원하는 애드온 시스템에 대해 알아보세요.
Subscription Management
좌석 기반 구독 및 요금제 변경을 관리하세요.
Webhooks
구독 웹후크를 통해 좌석 변경을 추적하세요.
좌석 기반 청구란?
Seat-based billing은 제품에 액세스하는 사용자 수를 기준으로 고객에게 요금을 청구합니다. 고정 요금 대신 팀 규모에 따라 가격이 조정됩니다.일반적인 사용 사례
좌석 기반 가격 책정의 이점
비즈니스의 경우:- 고객이 성장함에 따라 수익도 증가
- 고객이 비용을 예측하고 예산을 세울 수 있음
- 개인 사용자에서 팀, 엔터프라이즈로 이어지는 명확한 업그레이드 경로
- 팀이 확장될수록 고객 생애 가치 증가
- 보유한 사용자 수에 대해서만 비용 지불
- 비용을 쉽게 이해하고 예측 가능
- 필요에 따라 사용자 추가 또는 제거
- 팀 규모에 맞는 공정한 가격 책정
작동 방식
Dodo Payments는 Add-ons 시스템을 사용해 좌석 기반 청구를 구현합니다. 좌석 기반 구독은 다음 두 부분으로 구성됩니다.
고객의 월 총액은 다음과 같습니다.
가격 책정 전략
비즈니스에 맞는 좌석 기반 가격 책정 전략을 선택하세요:전략 1: 기본 + 좌석별 부가 기능
기본 요금제에 정해진 수의 좌석을 포함하고 추가 좌석에 대해 요금을 부과합니다.전략 2: 순수 좌석당 요금제
기본 요금 없이 좌석당 고정 요금을 청구합니다.전략 3: 단계별 좌석 요금제
기본 요금제와 좌석당 요금이 서로 다른 여러 상품을 제공합니다.전략 4: 좌석 번들
좌석을 개별 단위가 아니라 묶음으로 판매합니다.좌석 기반 청구 설정
1단계: 가격 정책 계획
구현하기 전에 가격 구조를 정의합니다.1
Define Base Plan
기본 구독에 포함할 항목을 결정합니다:
- 기본 가격 (순수한 seat 기반 요금의 경우 $0일 수 있음)
- 포함된 seat 수
- 이 등급에서 이용 가능한 기능
2
Set Seat Pricing
좌석당 add-on 비용을 결정합니다.
- 추가 좌석당 가격
- 수량 할인(여러 add-on을 통해 제공)
- 허용되는 최대 좌석 수(해당하는 경우)
3
Consider Billing Frequency
좌석 가격을 청구 주기에 맞춥니다.
- 월간 구독 → 월간 좌석 요금
- 연간 구독 → 연간 좌석 요금(대개 할인 적용)
2단계: 좌석 Add-on 생성
Dodo Payments 대시보드에서 다음을 수행합니다.- Products → Add-Ons로 이동합니다.
- Create Add-On을 클릭합니다.
- add-on을 구성합니다.
3단계: 기본 구독 생성
구독 상품을 생성합니다.- Products → Create Product로 이동합니다.
- Subscription을 선택합니다.
- 가격과 세부 정보를 구성합니다.
- Add-Ons 섹션에서 좌석 add-on을 연결합니다.
4단계: 상품에 Add-on 연결
좌석 add-on을 구독에 연결합니다.- 구독 상품을 편집합니다.
- Add-Ons 섹션으로 스크롤합니다.
- Add Add-Ons를 클릭합니다.
- 좌석 add-on을 선택합니다.
- 변경 사항을 저장합니다.
이제 구독 상품에서 좌석 기반 가격을 사용할 수 있습니다. 고객은 checkout 중에 원하는 수량의 추가 좌석을 구매할 수 있습니다.
좌석 관리
새 구독에 좌석 추가
checkout session을 생성할 때 좌석 수량을 지정합니다.기존 구독의 좌석 수 변경
Change Plan API를 사용해 좌석 수를 조정합니다.addons 배열은 변경량이 아니라 새로운 총 좌석 수를 설정합니다.
좌석 제거
좌석 수를 줄이려면 더 낮은 수량을 지정합니다.모든 추가 좌석 제거
빈addons 배열을 전달하면 모든 add-on이 제거됩니다.
좌석 변경에 대한 일할 계산
주기 중간에 좌석 변경이 적용되면 Dodo Payments는 다음 세 단계로 즉시 청구 금액을 계산합니다.각 모드의 크레딧 방식
difference_immediately에서는 고객이 기존 플랜 가격과 새 플랜 가격의 차액만 지불합니다. 이것이 이름의 유래이며, 주기의 어느 시점에 변경하더라도 금액이 동일한 이유입니다.
크레딧이 새 주기의 청구 금액보다 큰 경우 차액은 구독 범위의 크레딧으로 보관되며 이후 갱신에 자동으로 적용됩니다.
예시: 좌석 5개 추가
네 가지 모드로 하나의 시나리오를 실행하여 수치를 직접 비교합니다.
세 가지 즉시 적용 모드에서는 고객이 오늘 지불하는 금액에 대한 대가로 $130의 새로운 한 달 전체 기간을 받습니다.
prorated_immediately에서 시점이 중요한 이유
주기 후반에 변경할수록 돌려받을 수 있는 현재 주기의 크레딧이 줄어들기 때문에 같은 변경의 비용이 더 커집니다.
모든 행에서 고객은 새 월간 주기 전체를 받습니다. 달라지는 것은 “이미 지불한 금액”과 “지금 지불하는 금액”의 분배뿐입니다.
시점과 관계없이 seat 변경 비용을 동일하게 만들려면
difference_immediately를 사용하세요.
예시: “예상 밖의 청구”
판매자가 가장 자주 놀라는 사례입니다. 주기 후반에 소액의 좌석 add-on을 추가하면 add-on 가격보다 훨씬 큰 금액이 청구될 수 있습니다.
$10/월 seat 1개를 추가하면
prorated_immediately에서 비용은 $55.00입니다. 고객에게는 $60의 새로운 한 달 전체 기간에 대한 요금이 청구되고, 기존 월에 남아 있던 $5가 credit으로 적용되며, 갱신일이 재설정됩니다.
주기 중간에 seat을 소량 추가할 때 seat 가격만 부과하고 추가 비용은 없도록 하려면 difference_immediately를 사용하세요.
예시: 좌석 제거(다운그레이드)
새 요금제가 크레딧보다 저렴한 경우 초과분은 구독 크레딧으로 보관되며 이 구독의 이후 갱신에 자동으로 적용됩니다. 이 크레딧은 Customer Wallet에 추가되지 않으며 credit entitlement도 아닙니다.credit은 제거되는 seat뿐 아니라 기본 플랜과 모든 add-on을 포함한 구독 전체에 적용됩니다.
Preview 응답 읽기
previewChangePlan는 청구될 정확한 line item을 반환합니다. 각 line item에는 proration_factor가 있습니다.
일할 계산은 가장 가까운 일 단위로 반올림하지 않고 변경이 발생한 정확한 시점을 기준으로 초 단위까지 계산됩니다. 위의 예시는 이해를 돕기 위해 일 단위 경계의 수치를 사용했습니다.
변경 전 Preview
변경하기 전에 항상 일할 계산을 미리 확인합니다.Webhook으로 좌석 추적
구독 webhook을 수신해 좌석 변경을 모니터링합니다.관련 이벤트
Webhook handler 예시
좌석 한도 적용
애플리케이션에서 좌석 한도를 적용해야 합니다. Dodo Payments는 청구를 추적하지만 액세스 권한은 사용자가 제어합니다.- Hard Limit
- Soft Limit with Warning
- Auto-Upgrade
좌석 수를 초과해 사용자를 추가하지 못하도록 엄격하게 제한합니다.
고급 패턴
다양한 좌석 유형
가격이 서로 다른 여러 좌석 유형을 제공합니다.연간 좌석 할인
할인된 연간 좌석 가격을 제공합니다.최소 좌석 요구 사항
특정 요금제에 최소 좌석 수를 요구합니다.모범 사례
가격 정책 모범 사례
- 명확한 커뮤니케이션: 가격 페이지에 좌석당 가격을 눈에 잘 띄게 표시합니다.
- 포함 좌석: 진입 장벽을 낮추기 위해 기본 가격에 일부 좌석을 포함하는 방안을 고려합니다.
- 수량 할인: 엔터프라이즈 계약을 확보하기 위해 대규모 팀에 더 낮은 좌석당 요금을 제공합니다.
- 연간 인센티브: 현금 흐름과 고객 유지율을 개선하기 위해 연간 요금제를 할인합니다.
기술적 모범 사례
- 좌석 수 캐시: 모든 요청에서 API를 호출하지 않도록 구독 좌석 수를 로컬에 캐시합니다.
- 정기 동기화: API를 통해 로컬 좌석 수를 Dodo Payments와 주기적으로 동기화합니다.
- 실패 처리: 좌석 변경이 실패하면 명확한 오류 메시지와 재시도 옵션을 표시합니다.
- 감사 추적: 청구 분쟁과 규정 준수를 위해 모든 좌석 변경을 기록합니다.
사용자 경험 모범 사례
- 실시간 피드백: seat을 조정할 때 비용 영향을 즉시 표시
- 확인 단계: 청구 변경 전에 확인을 요청
- Proration 투명성: 적용 전에 일할 계산된 요금을 명확히 설명
- 간편한 다운그레이드: seat을 줄이는 과정을 어렵게 만들지 않기 (신뢰 구축에 도움이 됨)
문제 해결
Seat count mismatch between app and billing
Seat count mismatch between app and billing
증상: 앱에 표시되는 좌석 수가 구독의 좌석 수와 다릅니다.원인:
- Webhook을 수신하거나 처리하지 못함
- 좌석 변경 중 race condition 발생
- 캐시된 데이터가 업데이트되지 않음
subscription.plan_changed에 대한 webhook handler를 구현합니다.- 현재 구독을 가져오는 “Sync with billing” 버튼을 추가합니다.
- 정기적으로 새로 고침되도록 캐시 TTL을 설정합니다.
Unexpected mid-cycle charge amount
Unexpected mid-cycle charge amount
증상: 고객이 주기 중간의 청구 금액을 이해하지 못합니다.원인: 청구 주기 후반에
prorated_immediately를 사용함(위의 The Surprising Charge 예시 참고).해결 방법:- 변경하기 전에 항상
previewChangePlan를 사용합니다 - 명확한 내역을 표시합니다: “seat X개를 추가하면 오늘 $Y가 청구됩니다”
- 요금이 항상 가격 차액과 일치하도록 하려면
difference_immediately로 전환합니다
Add-on not appearing in checkout
Add-on not appearing in checkout
증상: checkout 중에 좌석 add-on을 사용할 수 없습니다.원인:
- 상품에 add-on이 연결되지 않음
- add-on이 보관 처리되었거나 삭제됨
- 상품과 add-on의 통화가 일치하지 않음
- 상품 설정에서 add-on이 연결되어 있는지 확인합니다.
- Add-Ons 대시보드에서 add-on 상태를 확인합니다.
- 통화가 정확히 일치하는지 확인합니다.
Cannot reduce seats below current usage
Cannot reduce seats below current usage
증상: 고객이 사용자를 할당한 상태에서 좌석을 줄이려고 합니다.해결 방법:
- 좌석을 줄이기 전에 제거해야 하는 사용자를 표시합니다.
- 사용자 제거 → 좌석 축소의 workflow를 구현합니다.
- 좌석 축소를 적용하기 전에 유예 기간을 고려합니다.
관련 문서
Seat-Based Pricing Tutorial
코드가 포함된 전체 구현 가이드.
Add-ons
add-on 시스템을 자세히 이해합니다.
Plan Changes & Proration
구독 수정 사항을 처리합니다.
Subscription Webhooks
구독 이벤트를 추적합니다.