2025년 5월 03일·6분 읽기

Stripe 기반 사용량 과금: 실용적인 데이터 모델

Stripe 기반 사용량 과금은 이벤트 저장과 조정이 깔끔해야 합니다. 간단한 스키마, 웹후크 흐름, 백필 처리 및 이중 집계 방지 방법을 알아보세요.

Stripe 기반 사용량 과금: 실용적인 데이터 모델

실제로 무엇을 구축하는가(그리고 왜 깨지는가)

사용량 기반 과금은 간단해 보입니다: 고객의 사용량을 측정하고, 가격을 곱한 뒤 기간 말에 청구하면 됩니다. 실제로는 작은 회계 시스템을 만드는 것입니다. 데이터가 늦게 도착하거나, 두 번 도착하거나, 아예 도착하지 않을 때에도 정확성을 유지해야 합니다.

대부분의 실패는 결제나 대시보드에서 발생하지 않습니다. 메터링 데이터 모델에서 발생합니다. “이 인보이스에 어떤 사용 이벤트가 포함되었고 왜 포함되었는가?”를 자신 있게 설명할 수 없다면, 결국 과청구, 과소청구 또는 신뢰 상실이 발생합니다.

사용량 과금은 몇 가지 예측 가능한 방식으로 깨집니다: 장애 후 이벤트가 누락되거나, 재시도로 중복이 생기거나, 늦게 도착한 데이터가 총계 산출 뒤에 나타나거나, 서로 다른 시스템이 불일치해서 조정할 수 없게 되는 경우입니다.

Stripe는 가격 책정, 인보이스, 세금, 징수 측면에서 훌륭합니다. 그러나 Stripe는 원시 사용량을 알지 못합니다 — 당신이 보내야 합니다. 따라서 원장의 출처를 결정해야 합니다: Stripe가 원장인가요, 아니면 Stripe가 반영하는 당신의 데이터베이스가 원장인가요?

대부분의 팀에 안전한 분리는 다음과 같습니다:

  • 원시 사용 이벤트와 그 수명 주기에 대한 진실의 출처는 당신의 데이터베이스입니다.
  • 실제 인보이스와 결제 상태의 진실의 출처는 Stripe입니다.

예시: 당신이 "API 호출"을 추적한다고 가정합시다. 각 호출은 안정적인 고유 키를 가진 사용 이벤트를 생성합니다. 인보이스 시점에는 아직 청구되지 않은 적격 이벤트만 합산하여 Stripe 인보이스 항목을 생성하거나 업데이트합니다. 수집 재시도나 웹후크 중복이 있어도 멱등성 규칙이 중복을 무해하게 만듭니다.

테이블을 설계하기 전에 결정할 사항들

테이블을 만들기 전에 향후 과금이 설명 가능하게 유지될지를 결정하는 정의들을 확정하세요. 대부분의 “수수께끼 같은 인보이스 버그”는 잘못된 SQL이 아니라 불명확한 규칙에서 옵니다.

먼저 청구 단위를 정하세요. 측정하기 쉽고 논쟁하기 어려운 것을 선택하세요. "API 호출"은 재시도, 배치 요청, 실패로 복잡해질 수 있습니다. "분(minutes)"은 겹침 문제를 일으킬 수 있고, "GB"는 기본(GB vs GiB)과 측정 방법(평균 vs 피크)을 명확히 해야 합니다.

다음으로 경계를 정의하세요. 이벤트가 어떤 윈도우에 속하는지 정확히 알아야 합니다. 사용량은 시간 단위, 일 단위, 청구 기간 단위, 또는 고객 행동 기준으로 집계합니까? 고객이 월 중간에 업그레이드하면 윈도우를 나눌 것인지, 아니면 한 달 전체에 한 가격을 적용할 것인지 결정해야 합니다. 이러한 선택들이 이벤트 그룹화와 총계 설명 방식을 좌우합니다.

또 어떤 시스템이 어떤 사실을 소유하는지 결정하세요. Stripe와 함께 흔한 패턴은: 애플리케이션은 원시 이벤트와 파생 총계를 소유하고, Stripe는 인보이스와 결제 상태를 소유한다는 것입니다. 이 접근은 기록을 조용히 수정하지 않을 때 가장 잘 작동합니다. 수정은 새로운 항목으로 기록하고 원본은 보존하세요.

짧은 비협상 원칙들:

  • 추적 가능성(Traceability): 청구된 각 단위는 저장된 이벤트로 추적 가능해야 합니다.
  • 감사 가능성(Auditability): 몇 달 후에도 "왜 청구됐는가?"에 대답할 수 있어야 합니다.
  • 되돌림 가능성(Reversibility): 실수는 명시적인 조정으로 수정됩니다.
  • 멱등성(Idempotency): 동일한 입력이 두 번 계산되지 않아야 합니다.
  • 명확한 소유권: 각 사실(사용량, 가격, 청구)은 하나의 시스템이 소유합니다.

예시: 당신이 "전송된 메시지"로 청구한다면, 재시도가 포함되는지, 실패한 전달도 포함되는지, 어떤 타임스탬프가 우선하는지(클라이언트 시간 vs 서버 시간)를 결정하세요. 문서로 남기고 이벤트 필드와 검증 로직에 반영하세요.

사용 이벤트를 위한 단순한 데이터 모델

사용량 기반 과금은 사용을 회계처럼 다룰 때 가장 쉽습니다: 원시 사실은 추가만 가능하고(append-only), 총계는 파생됩니다. 이 단일 선택은 대부분의 분쟁을 방지합니다. 언제든지 수치의 출처를 설명할 수 있기 때문입니다.

실용적인 시작점은 다섯 개의 핵심 테이블입니다(이름은 달라질 수 있음):

  • customer: 내부 고객 id, Stripe 고객 id, 상태, 기본 메타데이터.
  • subscription: 내부 구독 id, Stripe 구독 id, 예상 플랜/가격, 시작/종료 타임스탬프.
  • meter: 측정 대상(API 호출, 좌석 수, 저장소 GB-시간 등). 안정적인 미터 키, 단위, 집계 방식(합계, 최대값, 고유 등)을 포함.
  • usage_event: 측정된 동작당 한 행. customer_id, subscription_id(알려진 경우), meter_id, quantity, occurred_at(발생 시각), received_at(수집 시각), source(app, 배치, 파트너), 그리고 중복 제거를 위한 안정적 외부 키를 저장.
  • usage_aggregate: 파생된 총계, 보통 고객+미터+시간 버킷(일 또는 시간) 및 청구 기간별. 합계 수량과 재계산을 지원하는 버전 또는 last_event_received_at을 저장.

usage_event는 불변으로 유지하세요. 나중에 오류를 발견하면 히스토리를 편집하지 말고 보정 이벤트(예: 취소를 위한 -3 좌석)를 기록하세요.

감사와 분쟁을 위해 원시 이벤트를 보관하세요. 영구 보관할 수 없다면 최소한 청구 소급 기간과 환불/분쟁 창 기간만큼 보관하세요.

파생 총계는 별도로 유지하세요. 집계는 인보이스와 대시보드를 빠르게 하기 위해 유용하지만 일회성입니다. usage_event로부터 언제든지 usage_aggregate를 재생성할 수 있어야 하며, 백필이 있더라도 재구성이 가능해야 합니다.

멱등성과 이벤트 라이프사이클 상태

사용 데이터는 노이즈가 많습니다. 클라이언트가 요청을 재시도하고, 큐가 중복을 전달하며, Stripe 웹후크는 순서가 바뀌어 도착할 수 있습니다. 데이터베이스가 "이 사용 이벤트가 이미 계산되었는가"를 증명할 수 없다면 결국 두 번 청구하게 됩니다.

각 사용 이벤트에 안정적이고 결정론적인 event_id를 부여하고 이에 대해 유니크 제약을 걸어두세요. 자동 증가 id만 식별자로 의존하지 마세요. 좋은 event_id는 비즈니스 동작에서 유도됩니다(예: customer_id + meter + source_record_id 또는 customer_id + meter + timestamp_bucket + sequence). 동일한 동작이 다시 전송되면 같은 event_id가 생성되어 삽입은 안전한 no-op가 됩니다.

멱등성은 공개 API 경로뿐 아니라 모든 수집 경로를 덮어야 합니다. SDK 호출, 배치 임포트, 워커 작업, 웹후크 처리기 모두 재시도됩니다. 규칙 하나: 입력이 재시도될 수 있다면, 데이터베이스에 저장되는 멱등성 키가 필요하고 총계 변경 전에 확인되어야 합니다.

단순한 라이프사이클 상태 모델은 재시도를 안전하게 만들고 지원 작업을 쉽게 합니다. 상태를 명시적으로 보관하고 실패 시 사유를 저장하세요:

  • received: 저장되었으나 아직 검증되지 않음
  • validated: 스키마, 고객, 미터, 시간 윈도우 규칙 통과
  • posted: 청구 기간 총계에 반영됨
  • rejected: 영구 무시(사유 코드 포함)

예시: 워커가 검증 후에 크래시하고 posted 이전에 멈췄다고 합시다. 재시도하면 동일한 event_id를 상태 validated로 찾아 계속 posted로 진행하고 두 번째 이벤트를 생성하지 않습니다.

Stripe 웹후크에는 동일한 패턴을 사용하세요: Stripe event.id를 저장하고 한 번만 처리되도록 표시하여 중복 전달이 무해하게 처리되도록 합니다.

단계별: 미터링 이벤트의 종단간 수집

Test billing before production
Prototype Stripe usage billing logic end to end using visual data models and processes.
Try It Now

모든 미터링 이벤트를 돈처럼 다루세요: 먼저 검증하고 원본을 저장한 다음 원본에서 총계를 파생하세요. 이렇게 하면 시스템이 재시도하거나 늦게 데이터를 보내도 청구가 예측 가능해집니다.

신뢰할 수 있는 수집 흐름

총계를 건드리기 전에 각 수신 이벤트를 검증하세요. 최소한 안정적인 고객 식별자, 미터 이름, 숫자 수량, 타임스탬프, 멱등성용 고유 이벤트 키를 요구하세요.

원시 이벤트를 먼저 기록하세요. 나중에 집계하더라도 그 원시 레코드는 재처리, 감사, 오류 수정에 사용됩니다.

신뢰할 수 있는 흐름은 다음과 같습니다:

  • 이벤트 수락, 필수 필드 검증, 단위 정규화(예: 초 vs 분).
  • 이벤트 키를 고유 제약으로 하여 원시 사용 이벤트 행 삽입.
  • 이벤트 수량을 적용해 버킷(일별 또는 청구 기간별)으로 집계.
  • Stripe에 사용량을 보고한다면 보낸 내용(미터, 수량, 기간, Stripe 응답 식별자)을 기록.
  • 이상(거부된 이벤트, 단위 변환, 늦은 도착)을 로그로 남겨 감사.

집계는 반복 가능하게 유지하세요. 일반적인 접근법은: 원시 이벤트를 하나의 트랜잭션에서 삽입한 뒤 버킷 업데이트 작업을 큐에 넣는 것입니다. 작업이 두 번 실행되면 원시 이벤트가 이미 적용되었음을 감지해야 합니다.

고객이 "왜 12,430개의 API 호출이 청구되었나요?"라고 물으면 그 청구 윈도우에 포함된 정확한 원시 이벤트 집합을 보여줄 수 있어야 합니다.

Stripe 웹후크와 데이터베이스 조정(리콘실리)**ing

Go live with confidence
Deploy your billing service to AppMaster Cloud or your own cloud when you are ready.
Deploy Now

웹후크는 Stripe가 실제로 수행한 일에 대한 영수증입니다. 애플리케이션이 드래프트를 만들고 사용량을 푸시하더라도 인보이스 상태는 Stripe가 알려줄 때 현실화됩니다.

대부분의 팀은 청구 결과에 영향을 주는 소수의 웹후크 타입에 집중합니다:

  • invoice.created, invoice.finalized, invoice.paid, invoice.payment_failed
  • customer.subscription.created, customer.subscription.updated, customer.subscription.deleted
  • checkout.session.completed (Checkout으로 구독을 시작하는 경우)

수신한 모든 웹후크를 저장하세요. 원시 페이로드와 도착 시 관찰한 것들(Stripe event.id, event.created, 서명 검증 결과, 서버 수신 타임스탬프)을 보관하세요. 이 기록은 불일치를 디버그하거나 "왜 청구됐나요?"에 답할 때 중요합니다.

견고하고 멱등적인 조정 패턴은 다음과 같습니다:

  1. stripe_webhook_events 테이블에 event_id에 대한 유니크 제약으로 웹후크 삽입.
  2. 삽입이 실패하면 재시도이므로 중단.
  3. 서명 검증을 수행하고 통과/실패를 기록.
  4. Stripe ID(고객, 구독, 인보이스)로 내부 레코드를 조회하여 이벤트 처리.
  5. 상태 변경은 오직 전진하는 경우에만 적용.

순서가 뒤바뀌어 도착하는 것은 정상입니다. "최대 상태가 승리"하는 규칙과 타임스탬프를 사용하세요: 레코드를 뒤로 이동시키지 마세요.

예시: 인보이스 in_123에 대해 invoice.paid를 받았는데 내부 인보이스 행이 아직 없다면, "Stripe에서 본 것으로 표시된" 행을 생성하고 나중에 Stripe 고객 ID로 해당 계정에 연결하세요. 이렇게 하면 중복 처리 없이 원장을 일관성 있게 유지할 수 있습니다.

사용 총계에서 인보이스 라인 아이템으로

원시 사용을 인보이스 라인으로 바꾸는 것은 주로 타이밍과 경계의 문제입니다. 실시간 총계(대시보드, 사용 알림)가 필요한지 아니면 청구 시점에만 필요한지 결정하세요. 많은 팀은 두 가지를 모두 수행합니다: 이벤트는 계속 쓰고, 인보이스 준비용 총계는 예약 작업으로 계산합니다.

사용 윈도우를 Stripe의 청구 기간과 정렬하세요. 달력을 추측하지 마세요. 구독 아이템의 현재 청구 기간 시작과 종료를 사용하고, 그 윈도우 안에 속한 이벤트만 합산하세요. 타임스탬프는 UTC로 저장하고 청구 윈도우도 UTC로 맞추세요.

히스토리는 불변으로 유지하세요. 나중에 오류를 발견하면 오래된 이벤트를 편집하지 말고 해당 윈도우를 가리키는 조정 레코드를 만들어 더하거나 빼세요. 이렇게 하면 감사가 쉽고 설명하기도 편합니다.

요금제 변경과 요금 비례(proration)는 추적 가능성이 흔히 사라지는 부분입니다. 고객이 주기 중간에 요금제를 바꾸면 활성 가격 범위에 맞춰 사용량을 하위 윈도우로 분리하세요. 인보이스에는 두 개의 사용 라인 아이템(또는 하나의 라인과 하나의 조정)이 포함될 수 있으며, 각각 특정 가격과 시간 범위에 연결됩니다.

실용적 흐름:

  • Stripe에서 period start와 end를 가져옴.
  • 해당 윈도우와 가격에 적격한 사용 이벤트를 집계.
  • 사용 총계와 조정을 기반으로 인보이스 라인 아이템 생성.
  • 계산 실행 id를 저장해 나중에 수치를 재현할 수 있게 함.

백필과 늦은 데이터가 신뢰를 깨지 않게 처리하기

Harden webhook processing
Store Stripe webhook events once and process them safely even when they arrive twice.
Set Up Webhooks

늦게 도착하는 사용 데이터는 정상입니다. 디바이스가 오프라인이 되거나, 배치 작업이 밀리거나, 파트너가 파일을 재전송하거나, 로그를 복구해 재생할 수 있습니다. 핵심은 백필을 ‘숫자를 맞추기 위한 수단’이 아니라 수정 작업으로 취급하는 것입니다.

백필의 출처(애플리케이션 로그, 데이터 웨어하우스 익스포트, 파트너 시스템)를 명시하세요. 각 이벤트에 소스를 기록하면 늦게 온 이유를 설명할 수 있습니다.

백필할 때는 최소 두 개의 타임스탬프를 유지하세요: 사용이 발생한 시점(청구하고 싶은 시간)과 수집된 시점. 이벤트에 backfilled 태그를 붙이되 히스토리를 덮어쓰지 마세요.

합계를 델타로 오늘의 집계에 바로 적용하는 것보다 원시 이벤트에서 집계를 다시 만드는 것이 더 안전합니다. 재생(replay)은 버그 없이 복구하는 방법입니다. 파이프라인이 멱등적이라면 하루, 일주일, 또는 전체 청구 기간을 다시 실행해도 같은 총계를 얻을 수 있습니다.

인보이스가 이미 존재할 때 수정 정책:

  • 인보이스가 확정되지 않았다면 확정 전에 다시 계산하고 업데이트.
  • 확정되었고 과소청구된 경우, 명확한 설명이 달린 추가 인보이스(또는 새 인보이스 항목)를 발행.
  • 확정되었고 과청구된 경우, 원본 인보이스를 참조하여 크레딧 노트를 발행.
  • 교정을 피하기 위해 사용량을 다른 기간으로 옮기지 말 것.
  • 교정 사유(파트너 재전송, 로그 지연, 버그 수정 등)를 간단히 저장.

예시: 파트너가 1월 28-29의 누락 이벤트를 2월 3일에 전송했다면, occurred_at은 1월, ingested_at은 2월로 삽입하고 소스는 "partner"로 태깅하세요. 1월 인보이스가 이미 결제되었다면 누락 단위에 대해 작은 추가 인보이스를 생성하고 사유를 조정 레코드에 저장합니다.

이중 집계를 일으키는 흔한 실수들

이중 집계는 시스템이 "메시지가 도착했다"를 "행동이 발생했다"로 취급할 때 발생합니다. 재시도, 늦은 웹후크, 백필을 분리해 고객의 행동과 당신의 처리 과정을 구분해야 합니다.

주된 원인들:

  • 재시도가 새로운 사용으로 처리됨: 모든 이벤트가 안정적인 액션 id(request_id, message_id)를 포함하지 않거나 데이터베이스가 유니크성을 강제하지 않으면 두 번 계산됩니다.
  • 이벤트 시간과 처리 시간이 섞임: 수집 시간으로 리포팅하면 늦은 이벤트가 잘못된 기간에 들어가고 재실행 시 다시 계산될 수 있습니다.
  • 원시 이벤트 삭제 또는 덮어쓰기: 실행 중인 총계만 보관하면 무슨 일이 있었는지 증명할 수 없고 재처리로 총계가 불어나게 됩니다.
  • 웹후크 순서를 가정: 웹후크는 중복되거나 순서가 바뀌거나 부분 상태를 나타낼 수 있습니다. Stripe 객체 ID로 조정하고 "이미 처리됨" 가드를 유지하세요.
  • 취소, 환불, 크레딧을 명시적으로 모델링하지 않음: 항상 사용량만 더하고 음수 조정을 기록하지 않으면, 수정을 위해 임포트하면 또 계산되어 잘못된 총계가 됩니다.

예시: 하루의 사용량으로 "10 API 호출"을 기록한 뒤 장애로 인해 2건을 크레딧으로 돌려주면, 같은 날 사용량을 다시 전송하면서 크레딧까지 적용하면 고객은 18(10 + 10 - 2) 호출로 보게 되어야 할 8 대신 잘못된 수치를 보게 됩니다.

실전 전 체크리스트

Avoid long-term lock-in
Build no-code, then keep control by exporting real source code for self-hosting.
Generate Code

실제로 사용 고객에게 과금하기 전에 비싼 청구 버그를 막을 기본 사항을 최종 점검하세요. 대부분의 실패는 "Stripe 문제"가 아니라 데이터 문제(중복, 누락된 날, 조용한 재시도)입니다.

단락은 짧고 강제 가능한 항목들로 유지하세요:

  • 사용 이벤트에 대해 유일성 강제(예: event_id에 유니크 제약)를 적용하고 하나의 id 전략을 채택.
  • 모든 웹후크를 저장, 서명 검증, 멱등적 처리.
  • 원시 사용을 불변으로 취급. 수정은 조정으로(양수 또는 음수).
  • 일일 재조정 작업을 실행해서 내부 총계(고객별, 미터별, 일별)를 Stripe 청구 상태와 비교.
  • 누락일, 음수 총계, 급격한 스파이크, "수집된 이벤트"와 "청구된 이벤트" 간 큰 차이 등 이상에 대한 알림 추가.

간단한 테스트: 한 고객을 골라 지난 7일간 수집을 다시 실행하고 총계가 바뀌지 않는지 확인하세요. 바뀐다면 아직 멱등성 또는 백필 문제가 있는 것입니다.

예시 시나리오: 현실적인 한 달의 사용과 인보이스

Make retries safe
Create an idempotent ingest pipeline with Business Processes and unique event keys.
Start Building

소규모 지원 팀이 대화 하나당 $0.10을 청구하는 고객 포털을 운영한다고 합시다. 사용량 기반 과금을 Stripe로 처리하지만, 데이터가 엉클어졌을 때 신뢰를 주는 것은 어떻게 처리하느냐에 달려 있습니다.

3월 1일 고객의 청구 기간이 시작됩니다. 상담원이 대화를 종료할 때마다 앱은 사용 이벤트를 방출합니다:

  • event_id: 앱에서 생성한 안정적 UUID
  • customer_idsubscription_item_id
  • quantity: 1 대화
  • occurred_at: 종료 시간
  • ingested_at: 처음 수신된 시간

3월 3일, 배경 워커가 타임아웃 후 재시도하여 같은 대화를 다시 보냅니다. event_id가 유니크하므로 두 번째 삽입은 no-op가 되어 총계는 변하지 않습니다.

중순에 Stripe가 인보이스 미리보기와 최종 인보이스에 대한 웹후크를 보냅니다. 웹후크 핸들러는 stripe_event_id, type, received_at을 저장하고 데이터베이스 트랜잭션이 커밋된 뒤에만 처리된 것으로 표시합니다. 웹후크가 두 번 전달되면 stripe_event_id가 이미 존재하므로 두 번째 전달은 무시됩니다.

3월 18일, 오프라인이었던 모바일 클라이언트로부터 늦은 배치가 도착합니다. 3월 17일의 대화 35건이 포함되어 있고 occurred_at은 3월 17일로 더 오래된 값이지만 유효합니다. 시스템은 이를 삽입하고 3월 17일의 일별 총계를 재계산하며, 해당 청구 기간이 아직 열려 있으므로 다음 인보이스에 반영됩니다.

3월 22일, 동일한 대화가 버그로 서로 다른 event_id 두 개로 기록된 것을 발견합니다. 기록을 삭제하는 대신 quantity = -1인 조정 이벤트를 작성하고 사유에 "duplicate detected"를 적습니다. 이렇게 하면 감사 추적이 보존되고 인보이스 변경이 설명 가능합니다.

다음 단계: 안전하게 구현하고 모니터링하며 반복하기

작게 시작하세요: 하나의 미터, 하나의 플랜, 잘 이해하는 고객 세그먼트 하나부터 시작합니다. 목표는 간단한 일관성입니다 — 당신의 숫자가 매달 Stripe와 일치하고 놀라움이 없어야 합니다.

작게 만들고 단단히 다지기

실용적 첫 롤아웃:

  • 하나의 이벤트 형태를 정의(무엇을, 어떤 단위로, 어떤 시점에 집계할지).
  • 모든 이벤트를 고유 멱등성 키와 명확한 상태로 저장.
  • 인보이스 설명을 위해 일간(또는 시간별) 집계 생성.
  • 정기적으로 Stripe 웹후크와 조정(리콘실리)을 실행.
  • 인보이스 후 기간을 닫고 늦은 이벤트는 조정 경로로 처리.

코드 없이도 강한 데이터 무결성을 유지하려면 잘못된 상태가 발생하지 않게 하세요: 멱등성 키에 대한 유니크 제약 적용, 고객과 구독에 대한 외래 키 요구, 수락된 원시 이벤트 업데이트 금지 등을 적용합니다.

나중에 당신을 구해줄 모니터링

초기에 간단한 감사 화면을 추가하세요. 한 번이라도 "이번 달 청구가 왜 더 높죠?"라는 질문을 받으면 이 화면이 본전을 뽑습니다. 유용한 뷰는 고객과 기간으로 이벤트 검색, 기간별 일일 총계 보기, 웹후크 처리 상태 추적, 누가/언제/왜 백필 및 조정을 했는지 검토하는 것입니다.

AppMaster(appmaster.io)로 구현한다면 모델은 자연스럽게 맞아떨어집니다: Data Designer에서 원시 이벤트, 집계, 조정을 정의하고 Business Processes로 멱등성 수집, 예약 집계, 웹후크 조정을 구현하세요. 수작업으로 모든 파이프라인을 작성하지 않아도 실제 원장과 감사 추적을 얻을 수 있습니다.

첫 미터가 안정되면 다음 미터를 추가하세요. 같은 라이프사이클 규칙, 같은 감사 도구, 같은 습관을 유지하세요: 한 번에 한 가지씩 변경하고 끝까지 검증합니다.

자주 묻는 질문

Why does usage-based billing with Stripe feel harder than it sounds?

사용량 기반 과금은 작은 원장(ledger)을 다루는 것처럼 생각하세요. 어려운 부분은 카드 결제가 아니라, 이벤트가 늦게 오거나 중복으로 도착하거나 수정이 필요할 때도 무엇이 계산되었는지 정확히 설명할 수 있도록 기록을 유지하는 것입니다.

Should Stripe or my database be the source of truth for usage?

안전한 기본 원칙은: 데이터베이스는 원시 사용 이벤트와 그 상태에 대한 진실의 출처(source of truth)이고, Stripe는 실제 인보이스와 결제 결과의 진실의 출처입니다. 이런 분리는 추적 가능성을 유지하면서 Stripe가 가격, 세금, 징수를 처리하도록 합니다.

What makes a good idempotency key (event_id) for a usage event?

재시도(retry) 시 동일한 식별자가 나오도록 안정적이고 결정론적인 키를 만드세요. 보통은 고객 ID + 미터 키 + 소스 레코드 ID 같은 실제 비즈니스 동작에서 유도합니다. 이렇게 하면 중복 전송이 무해한 no-op가 됩니다.

How do I fix incorrect usage without rewriting history?

수락된 사용 이벤트를 편집하거나 삭제하지 마세요. 대신 보정 조정 이벤트(필요하면 음수 수량)를 기록하고 원본은 그대로 두세요. 이렇게 하면 나중에 기록을 설명하기 쉽습니다.

Do I really need both raw events and aggregated totals?

원시 사용 이벤트는 추가만 가능한(append-only) 형태로 유지하고, 집계는 별도의 파생 데이터로 보관하세요. 집계는 속도와 리포팅을 위해 필요하지만, 문제 발생 시 원시 이벤트로부터 집계를 다시 만들 수 있어야 합니다.

How should I handle late-arriving usage data or backfills?

최소 두 개의 타임스탬프를 저장하세요: 실제 발생 시점(occurred_at)과 수집 시점(ingested_at), 그리고 소스를 기록하세요. 인보이스가 아직 확정되지 않았다면 확정 전 재계산하고, 이미 확정됐다면 추가 청구나 크레딧 같은 명확한 수정 절차로 처리하세요.

What’s the safest way to process Stripe webhooks without duplicates?

수신한 모든 웹후크 페이로드를 저장하고 Stripe의 이벤트 id를 고유 키로 사용해 중복 없이 처리하세요. 웹후크는 중복이나 순서 변경이 자주 발생하므로, 처리 시 idempotent 하게 작동해야 합니다.

How do I handle upgrades, downgrades, and proration with usage meters?

청구 기간의 시작과 끝(period start/end)을 사용해 윈도우를 정하고, 가격이 변경된 경우에는 활성화 기간에 맞춰 사용량을 분할하세요. 각 인보이스 라인이 특정 시간 범위와 가격에 연결되도록 하는 것이 목표입니다.

How can I tell if my metering pipeline is safe before going live?

집계 로직이 어떤 원시 이벤트가 포함되었는지 증명할 수 있도록 만들고, 계산 실행 식별자(calculation run id) 같은 메타데이터를 저장해 나중에 동일한 결과를 재현할 수 있게 하세요. 같은 윈도우로 재처리했을 때 합계가 바뀌면 idempotency 또는 라이프사이클 상태에 문제가 있는 것입니다.

Can I build this data model and workflow in AppMaster without custom code?

Data Designer에서 원시 사용 이벤트, 집계, 조정, 웹후크 인박스 테이블을 모델링하고 Business Processes에서 유일성 제약으로 idempotent한 수집과 조정 로직을 구현하세요. 이렇게 하면 수작업으로 모든 코드를 쓰지 않고도 감사 가능한 원장을 만들 수 있습니다.

쉬운 시작
멋진만들기

무료 요금제로 AppMaster를 사용해 보세요.
준비가 되면 적절한 구독을 선택할 수 있습니다.

시작하다