FIM One은 관리자가 제어하는 기능 플래그 뒤에 완전한 Stripe 결제 파이프라인을 제공합니다. 결제가 필요 없는 프라이빗 배포는 이를 비활성화하고 UI를 볼 수 없습니다. SaaS 운영자는 한 번의 토글로 호스팅된 Checkout, Customer Portal, 웹훅 기반 구독 생명주기 및 할당량 적용을 즉시 사용할 수 있습니다.Documentation Index
Fetch the complete documentation index at: https://docs.fim.ai/llms.txt
Use this file to discover all available pages before exploring further.
결제는 기본적으로 비활성화되어 있습니다. 새로운 설치 및 기존 셀프호스트 모두
system_settings.billing_enabled = FALSE로 시작합니다. 관리자가 명시적으로 활성화할 때까지 결제 UI는 표시되지 않습니다.무엇을 얻을 수 있는가
- Free + Pro 티어 — 월간 토큰 할당량 포함 (기본값: Free 1M / Pro 5M; 활성화 후 조정 가능)
- Stripe 호스팅 Checkout — 사용자가 업그레이드할 때 카드 데이터가 코드를 거치지 않음
- Customer Portal — 사용자가 결제 방법 업데이트, 청구서 다운로드, 구독 취소 — 모두 Stripe UI에서 처리
- Webhook 기반 라이프사이클 — 구독 자동 프로비저닝 및 갱신; 취소된 구독은 기간 종료 시 Free로 강등
- 할당량 적용 — 기간당 토큰 사용량 추적; 스트림 중 차단 및 구조화된 업그레이드 프롬프트
- 관리자 페이지 — 요금제 CRUD 및 구독 모니터링
전제 조건
- Stripe 계정 (라이브 모드 활성화됨). 싱가포르 법인은 KYC 완료 필요(사업 UEN, 이사 신분증, 은행 계좌). 승인은 일반적으로 1-3일 소요됩니다.
- Stripe 라이브 API 키 (제한된 유형 권장 — 표준
sk_live_***보다 해지하기 쉽고 권한 범위 지정 가능). - 웹훅 엔드포인트 (
<your-domain>/api/webhooks/stripe에서 공개적으로 접근 가능). - 은행 계좌 (지급용). 다중 통화 결제(예: USD 계좌로 USD 지급)는 USD 기본값이 아닌 Stripe 계정의 경우 거래당 1.5-2% FX 손실을 피하기 위해 권장됩니다.
설정
1. Stripe 대시보드
Pro 상품 만들기
- Catalog → Products → + Add product
- Name:
Pro, description:5M tokens / month, priority support - Pricing: Recurring, monthly, $20.00 USD (adjust to your pricing strategy)
- Save → copy the resulting
price_***ID (you will UPDATE the localbilling_planstable with this value after activation)
제한된 API 키 생성
- Developers → API keys → + Create restricted key
- Name:
fim-one production - Permissions (minimum):
- Customers: Write
- Subscriptions: Write
- Checkout Sessions: Write
- Customer portal: Write
- Prices: Read
- Products: Read
- Save → copy
rk_live_***
웹훅 엔드포인트 등록
- Developers → Webhooks → + Add endpoint
- URL:
https://<your-domain>/api/webhooks/stripe - 수신할 이벤트:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
- 저장 후 “Reveal signing secret” 클릭 →
whsec_***복사
다중 통화 결제 구성(권장)
Stripe 계정의 기본 통화가 청구하는 가격 통화와 다른 경우(일반적인 경우: SGD 계정에서 USD 청구):- Settings → Bank accounts and currencies → Add a settlement currency
- 가격 통화 선택(예: USD)
- 일치하는 은행 계좌 연결(예: Aspire USD 가상 계좌)
- 저장 — Stripe가 USD 청구를 USD 지급으로 직접 라우팅하므로 환율 변환 없음
2. Backend .env
프로덕션 .env에서 다음 세 개의 키를 설정하세요:
.env를 편집한 후 백엔드를 다시 시작하여 키가 적용되도록 하세요:
3. Admin에서 활성화
- 관리자로 로그인
- Admin → System Settings → Billing
- Enable Stripe Billing 토글을 ON으로 설정
- 백엔드가
STRIPE_SECRET_KEY와STRIPE_WEBHOOK_SECRET이 모두 존재하는지 검증합니다 — 둘 중 하나라도 누락되면 400을 반환하고 토글은 OFF 상태로 유지됩니다 - 처음 활성화할 때만 백엔드가 멱등성 설정을 실행합니다:
- Free + Pro 플랜을 시드합니다 (이미 존재하면 건너뜀)
system_settings.default_plan_id를 Free 플랜 id로 설정합니다- 플랜이 없는 모든 사용자에 대해
users.plan_id = free.id를 역채우기합니다 default_token_quota→ Free 플랜monthly_token_quota를 동기화하여 기존 관리자 제어 전역 할당량이 유지되도록 합니다
- 이후 토글 끄기/켜기는 순수 플래그 전환이며 데이터 부작용이 없습니다
4. Pro 플랜을 라이브 가격으로 업데이트
활성화 후, 시드된 Pro 플랜을 라이브 Stripe 가격으로 업데이트하세요:- Admin → Billing → Plans → Pro → Edit
- 1단계에서 얻은
price_1***을Stripe Price ID에 붙여넣기 - 저장
5. Smoke test
/settings?tab=billing을 일반 사용자로 열기- Switch to Pro 클릭
- Stripe Checkout이 열림; 소액 실제 카드로 완료 (이후 환불)
- Webhook이 실행되어야 함 — Stripe Dashboard → Webhooks → 최근 이벤트에서 2xx 응답 확인
subscriptions테이블에 구독 행이 나타남;users.plan_id가pro로 변경됨- UI에 Pro 플랜 + “Manage subscription” 버튼 표시
청구 비활성화
Admin → System Settings → Billing에서 Enable Stripe Billing 스위치를 OFF로 전환합니다. 비활성화되면:- 모든
/api/billing/*엔드포인트가 503을 반환합니다 - 웹훅 엔드포인트가 503을 반환합니다 (Stripe가 재시도한 후 Dashboard에 실패로 표시됩니다 — 청구가 영구적으로 비활성화된 경우 Stripe Dashboard에서 웹훅을 비활성화할 수 있습니다)
Plan & Billing사용자 대면 탭이 사라집니다- Admin → Billing 네비게이션 그룹이 숨겨집니다
- 할당량 체인이 요금제 계층을 건너뛰고
default_token_quota로 직접 폴백합니다
subscriptions, billing_plans, users.plan_id 행은 변경되지 않습니다. 다시 활성화하면 마이그레이션 없이 동일한 상태에서 재개됩니다.
계산 참고 — 할당량 및 토큰 수학
이것은 사용자가 소비할 수 있는 것, 카운터가 언제 재설정되는지, 그리고 해결 체인이 어떻게 구성되는지를 결정하는 모든 숫자 규칙의 권위 있는 참고 자료입니다. 가격 책정을 변경하거나, 할당량을 조정하거나, 사용량 대시보드를 구축하거나, v2/v3 작업을 계획하기 전에 이것을 읽으세요. 아직 배포되지 않은 향후 규칙은 예약된 슬롯에 문서화되어 있으므로 기여자들이 새로운 로직이 어디에 연결되는지 알 수 있습니다.용어집
| 변수 | 저장소 | 의미 | 범위 |
|---|---|---|---|
users.token_quota | per-user (override) | 3가지 상태 오버라이드; 아래 의미 참조 | NULL, 0, 또는 양의 정수 |
users.tokens_used_this_period | per-user (counter) | 마지막 리셋 이후 누적 토큰 | 음이 아닌 정수 |
users.quota_reset_at | per-user (anchor) | 유료 사용자의 Subscription.current_period_end 반영 | timestamp |
users.plan_id | per-user (FK) | 활성 플랜 | FK billing_plans.id |
billing_plans.monthly_token_quota | per-plan | 이 플랜의 사용자에 대한 하드 캡 | 음이 아닌 정수 |
system_settings.default_token_quota | singleton | 적용되는 플랜이 없을 때의 방어적 폴백 | 음이 아닌 정수 |
system_settings.default_plan_id | singleton | 신규/미할당 사용자를 위한 무료 플랜 포인터 | FK 또는 NULL |
system_settings.billing_enabled | singleton | 마스터 스위치 — 체인의 2단계를 제어 | boolean |
토큰으로 계산되는 항목
토큰 소비는 LLM 호출 레이어에서 계산되며, 모든 완료 시 LiteLLM의usage 객체에서 소싱됩니다.
- 계산됨: 모든 모델 호출에서 프롬프트 토큰 + 완료 토큰
- 계산됨: 다단계 / 도구 사용 에이전트 흐름의 모든 왕복 (각 모델 호출은 자체 차감)
- 계산됨: 임베딩 요청 (KB 수집, 검색 점수 매김)
- 계산 안 됨: 모델로 전송되지 않은 입력 (예: 사용자가 버린 업로드된 파일)
- 계산 안 됨: 제공자에 도달하기 전에 실패한 요청 (인증 오류, 속도 제한 사전 확인)
- 캐시된 입력: v1에서는 정가로 계산됨 (제공자 캐시 할인이 표시되지 않음). v2는 캐시된 프롬프트 토큰을 별도로 크레딧할 수 있습니다.
3단계 오버라이드 의미론
users.token_quota는 사용자별 관리 오버라이드입니다. 하나의 열에 세 가지 의미를 담고 있습니다:
| 값 | 의미 | 사용 사례 |
|---|---|---|
NULL | 설정되지 않음 — 플랜 / 기본값으로 위임 | 모든 일반 사용자의 기본 상태 |
0 | 무제한 | 관리자 / 내부 계정; “VIP 선물” |
N > 0 | N에서 하드 캡 | 유료 구독을 취소하지 않고 악용자 차단; 선결제 엔터프라이즈 할당량 |
할당량 해결 체인 — v1 (현재)
인증된 모든 요청에 대해 상한은 하향식으로 계산됩니다 — 첫 번째 일치가 우선입니다:users.plan_id를 역채우기하기 때문입니다. 잘못 구성된 플랜이 사용자를 자동으로 무제한으로 설정하지 않도록 하는 심층 방어 메커니즘으로 존재합니다.
기간 재설정
- 유료 사용자의 경우,
quota_reset_at은Subscription.current_period_end를 반영합니다.invoice.payment_succeeded웹훅 핸들러는 각 갱신 성공 시tokens_used_this_period = 0으로 설정하고quota_reset_at을 새로운 기간 종료로 진행합니다. - Free 사용자(Stripe 구독 없음)의 경우, 시간별 크론이 계획 할당 날짜를 기준으로 한 달력 월 경계에서
tokens_used_this_period를 0으로 롤링합니다. - 기간 중 계획 변경은 카운터를 재설정하지 않습니다 — 갱신만 재설정합니다. 이는 할당량 순환 악용(“구독 → Pro 할당량 사용 → 취소 → 다시 구독”)을 방지합니다.
스트림 중 적용
- 채팅 호출 진입 시 사전 점검: 가장 저렴한 경로로, 사용자가 감당할 수 없는 요청을 차단합니다.
- 스트리밍 중에는 모든 청크에서 실행 중인 토큰 수를 다시 평가합니다. 한도를 초과하면 네트워크 오류가 아닌 구조화된 종료 프레임으로 스트림을 닫습니다.
- 프론트엔드는 종료자를 해석하고
/settings?tab=billing으로의 딥 링크와 함께<QuotaExceededDialog>를 표시합니다. - 비스트리밍 응답은 HTTP
402를 반환하며, 본문은{ code: "QUOTA_EXCEEDED", reset_at, upgrade_url }입니다.
청구 비활성화 폴백
system_settings.billing_enabled = FALSE일 때:
- 체인의 2단계가 건너뛰어집니다 — 체인이
override → default → unlimited로 축소됩니다. /api/billing/*및/api/webhooks/stripe는503을 반환합니다.Plan & Billing사용자 탭 및 Admin → Billing 네비게이션 그룹이 숨겨집니다.- 모든 청구 데이터(구독, 플랜,
users.plan_id)는 보존됩니다 — 다시 활성화하면 마이그레이션 없이 동일한 상태에서 재개됩니다.
Reserved: quota chain v2 — Team seats
아직 출시되지 않았습니다. v2 작업이 알려진 착륙 지점을 가지도록 여기에 문서화되어 있습니다.
Subscription.quantity는 좌석 수를 전달합니다(Stripe 기본).- 사용자의 유효한 플랜은 Team 멤버십을 통해 해결되며, 그 후 개인 플랜으로 폴백됩니다:
- 할당량은 좌석당(각 Team 멤버는 전체
monthly_token_quota를 받음)이며, 풀링된 버킷이 아닙니다. 풀링된 버킷은 선착순 소진을 만들고 고객 친화적이지 않습니다. - 재정의 의미는 변경되지 않습니다 — Team 관리자는 여전히
users.token_quota = N을 통해 개별 멤버를 하드 캡할 수 있으며, 이는 team 플랜 체인 위에 있습니다.
Reserved: quota chain v3 — native Org allocation (no Stripe)
아직 출시되지 않음. Stripe를 통해 사용자당 비용을 지불하지 않고 내부적으로 할당량을 배분하는 온프레미스/엔터프라이즈 배포용으로 예약됨.
- 새로운 테이블
org_quota_allocations(user_id, monthly_token_quota, org_id)는 상위 예산을 멤버 전체에 배분합니다. - 할당량은 공유 풀이 아닌 사용자별 — 모든 멤버가 명확한 개별 SLA를 가집니다.
- 업데이트된 체인:
max(),sum()아님. 유료 Pro 사용자는 조직 관리자가 낮은 할당량을 설정하더라도 결코 지불한 것보다 적게 받지 않습니다. Stripe 지불 할당량은 절대 불변입니다.
예약됨: 종량제 크레딧 잔액 (v3 별도 차원)
아직 출시되지 않음. 위의 체인과 별개의 축——크레딧은 구독 티어가 아닌 일회성 충전입니다.
- 새로운 테이블
user_credits(user_id, balance_cents, currency)— Stripe Checkoutmode='payment'를 통해 자금 조달됨. - 소비 순서: 구독 할당량 우선, 그 다음 크레딧 잔액 (구독이 소진된 후에만 크레딧 차감 시작).
- 크레딧 잔액은 환불 불가능 (선불식 업계 표준).
- UI는 두 개의 바를 노출:
Subscription quota: 4.2M / 5M used+Credits: $7.40 remaining.
기본값
배포 시 기본값 — 명시된 경우를 제외하고 모두 설치 후 조정 가능합니다.| 변수 | 기본값 | 조정 방법 |
|---|---|---|
billing_plans.monthly_token_quota (Free) | 1,000,000 | Admin → Billing → Plans → Free → Edit |
billing_plans.monthly_token_quota (Pro) | 5,000,000 | Admin → Billing → Plans → Pro → Edit |
system_settings.default_token_quota | 1,000,000 (활성화 시 Free와 동기화) | Admin → System Settings → Quotas |
system_settings.billing_enabled | FALSE | Admin → System Settings → Billing |
| Pro 정가 | $20.00 USD / month | Stripe Dashboard (price object) |
| 구독한 Stripe 웹훅 이벤트 | 6 | Stripe Dashboard → Webhooks |
| Stripe 가격 캐시 TTL | 5 minutes | stripe_client.py에 하드코딩됨 |
| 구독 수명 주기 cron | 매시간 | web/main.py의 APScheduler |
| Free 티어 리셋 cron | 매시간 (달력 월 경계) | web/main.py의 APScheduler |
가격 책정 모델
V1은 정액 구독입니다. Free + Pro, 월간 청구, USD만 지원합니다. V1 범위 외 (의도적으로 로드맵으로 연기됨):- Team 플랜 (Stripe seats /
subscription.quantity) - 연간 청구
- 다중 통화 표시
- 쿠폰 / 프로모션 코드
- 세금 처리 (Stripe Tax 통합 — 별도 규정 검토 필요)
- 사용량 기반 계량 / 초과 요금
- 선불 크레딧 잔액 (일회성 충전)
문제 해결
토글이 활성화되지 않음 — 400 오류
토글이 활성화되지 않음 — 400 오류
활성화 엔드포인트에는
STRIPE_SECRET_KEY와 STRIPE_WEBHOOK_SECRET이 모두 설정되어야 합니다. .env에 존재하는지 확인하고 편집 후 백엔드가 다시 시작되었는지 확인하세요.웹훅이 503을 반환함
웹훅이 503을 반환함
청구가 비활성화되어 있거나(토글이 OFF) 요청 서명 검증에 실패했습니다(
STRIPE_WEBHOOK_SECRET 불일치). Stripe 대시보드 → 웹훅 → 최근 이벤트에서 실제 오류 본문을 확인하세요.사용자가 구독했지만 여전히 무료 요금제를 봄
사용자가 구독했지만 여전히 무료 요금제를 봄
checkout.session.completed 웹훅이 백엔드에 도달하지 않았습니다. Stripe 대시보드의 엔드포인트 URL이 <your-domain>/api/webhooks/stripe와 정확히 일치하는지(후행 경로 포함) 확인하세요. 웹훅 최근 전달 기록에서 실패를 확인하세요.Pro 사용자가 테스트 모드 가격 ID를 봄
Pro 사용자가 테스트 모드 가격 ID를 봄
시드 마이그레이션이 테스트 모드 가격 ID를 작성합니다. 프로덕션 청구를 활성화한 후 관리자 → 청구 → 요금제 → Pro → 편집을 통해 Pro 요금제를 라이브
price_1***을 사용하도록 업데이트하거나 직접 SQL UPDATE를 실행하세요.영수증이 Stripe 기본값으로 브랜딩됨
영수증이 Stripe 기본값으로 브랜딩됨
Stripe 대시보드 → 설정 → 브랜딩에서 비즈니스 브랜딩을 구성하세요. 로고, 비즈니스 이름(예: “FIM Labs Pte. Ltd.”), 주소를 추가하세요. Stripe는 이를 모든 자동 생성 영수증 및 청구서에 적용합니다.
지급 시 환율 손실
지급 시 환율 손실
Stripe 계정 기본 통화가 청구 통화와 다르면 Stripe는 매번 지급할 때 환전합니다(1.5-2% 스프레드). 설정 → 은행 계좌 및 통화에서 일치하는 결제 통화를 추가하고 동일 통화 은행 계좌를 연결하면 Stripe가 환전 없이 동일 통화 결제를 라우팅합니다.