TL;DR

  • 기존 가격을 새 상품의 정확한 미러 가격으로 대응시키고 고객 결제 조건을 바꾸지 않은 채 실시간 구독을 이전하는 Stripe 카탈로그 마이그레이션임.
  • 이전 대상 구독은 금액, 통화, 결제 주기, 수량, 갱신일, 결제 수단과 할인 조건을 유지하며, 접근 권한 판정 기준은 상품명에서 가격 메타데이터로 이동함.
  • 배치 작업은 라이브 API를 기준으로 이전 계획을 만들고 구독을 이전 가능 여부별로 분류하며, 예시에서는 124건 중 117건 이전·7건 제외임.
  • 저장소는 가격 확인·메타데이터 설정·구독 항목 교체·권한 부여 SQL을 제공하지만, 새 상품과 미러 가격은 생성하지 않음.
  • --apply와 확인 입력을 요구하고, 변경 전후 검증·멱등성 키·별도 롤백 키를 사용하며 민감한 내보내기 파일의 제한된 보관을 요구함.

Stripe 카탈로그 마이그레이션

  • 고객이 지불하는 금액을 바꾸지 않고 실시간 구독을 새 Stripe 상품 카탈로그로 옮기는 저장소임.
  • 기존의 모든 가격에 새 상품 아래 정확히 일치하는 미러 가격을 두는 마이그레이션을 위한 Python 스크립트와 SQL을 포함함.
  • 구독의 금액, 통화, 결제 주기, 수량, 갱신일, 결제 수단과 할인 조건을 유지하고, 권한 부여 기준을 상품명에서 가격 메타데이터로 옮기는 방식임.
  • 구독, 평생 라이선스, 업데이트 기간이 있는 영구 라이선스, 임대 후 소유(rent-to-own) 요금제가 포함된 완료된 마이그레이션에서 추출한 프로젝트임. 계정 ID와 고객 데이터는 설정값과 자리표시자로 대체됨.

쓰기 전에 마이그레이션 확인

  • 배치 작업은 드라이런(dry run)으로 시작함. 라이브 API에서 출발지와 목적지의 대응표를 다시 만들고 out/plan.csv를 기록하며, 이전할 구독과 제외 사유별 구독을 모두 나열함.
  • 예시 수치는 총 124건이며, 117건 이전 대상과 7건 제외 대상으로 구성됨.
  • 제외 사유는 no_mirror[studio/web/std] 3건, renews_imminently 2건, has_schedule 1건, multi_item[2] 1건임.
  • 위 수치는 예시이므로 --apply를 추가하기 전에 자체 계정의 계획을 검토해야 함.

마이그레이션 모델

  • Stripe 가격은 상품 연결 관계를 유지하므로 카탈로그 재구성은 다음 네 단계로 진행함.
  • 새 상품 생성.
  • 실시간 구독자가 있는 각 기존 가격에 대해 금액, 통화, 결제 주기, 결제 주기 횟수와 tax_behavior가 일치하는 미러 가격 생성.
  • 새 가격에 구조화된 메타데이터를 추가한 뒤 각 구독 항목을 정확한 미러 가격으로 교체하고 proration_behavior: none 설정 적용.
  • 상품명이 아니라 가격 메타데이터를 기준으로 접근 권한 판정.
  • 저장소는 가격에 태그를 지정하고 검증하며, 구독 교체 계획을 세우고 적용하는 기능과 권한 부여 SQL을 제공함. 상품이나 미러 가격은 생성하지 않음.

시작하기

  • Python 3.9 이상, stripe-python 10 이상이 필요하며 SQL은 Supabase를 대상으로 함.
  • 가상 환경을 만들고 활성화한 뒤 의존성을 설치하고, config.example.py를 config.py로 복사하는 순서로 준비함.
  • config.py에 상품 ID, 티어 규칙과 고유한 멱등성 접두사를 입력해야 함. STRIPE_API_KEY는 로컬 비밀 저장소에서 불러와야 하며, 라이브 키를 스크립트에 붙여 넣거나 커밋하거나 보고서에 포함해서는 안 됨.
  • 먼저 읽기 전용 검사와 드라이런을 실행함: scripts 디렉터리에서 audit_tax_behavior.py, write_price_metadata.py, migrate_one.py, migrate_all.py를 차례로 실행함.
  • 배치 작업 전에 sql/entitlements.sql을 배포해야 함. 기존 가격과 새 가격이 올바르게 권한으로 해석되는지 확인한 뒤, 리허설·첫 배치·전체 실행·검증 순서로 마이그레이션 당일 실행 지침을 따라야 함.

안전 모델

  • 먼저 드라이런을 실행함. Stripe를 변경하는 스크립트에는 --apply가 필요하며, 실시간 구독 변경에는 yes 입력도 필요함.
  • 정확히 일치하는 미러 가격만 사용함. 라우팅은 티어, 채널, 임대 후 소유 여부, 통화, 결제 주기, 결제 주기 횟수와 금액을 기준으로 하며, 일치 항목이 없거나 여러 개면 건너뜀.
  • 계약 조건의 무단 변경을 방지함. 배치 작업은 세금 설정 불일치, 여러 항목이 있는 구독, 스케줄이 있는 구독, 연결된 애플리케이션과 설정된 안전 기간 안에 갱신되는 구독을 거부함.
  • 모든 쓰기 작업을 검증함. 각 구독을 변경 전후에 읽고, 달라질 수 있는 값은 price_id와 상품뿐임. 예상하지 않은 변경이 처음 발견되면 배치 작업을 중단함.
  • 안전한 재시도와 롤백을 지원함. 모든 쓰기에 멱등성 키를 사용하며, Stripe는 키를 재사용하면 저장된 응답을 반환하므로 롤백에는 별도의 키 네임스페이스를 사용함.
  • 생성된 계획, 결과와 내보내기 파일에는 구독 ID, 고객 ID와 가격 ID가 포함됨. out/, *.csv와 config.py는 버전 관리에서 제외되지만 제한된 접근이 가능한 곳에 보관해야 함. export_migration_origins.py는 --no-email을 전달하지 않으면 고객 이메일을 포함함.

포함 항목

  • 설정: config.example.py — 계정별 ID, 라우팅 규칙과 메타데이터 규칙.
  • 마이그레이션: scripts/ — 감사, 메타데이터 계획, 리허설, 배치, 검증과 롤백.
  • 권한 부여: sql/ — 참조 스키마, 판정기, 프로젝션과 커뮤니케이션 쿼리.
  • 운영: docs/RUNBOOK.md — 마이그레이션 당일의 순서별 점검 목록.
  • 규칙: docs/CONVENTIONS.md — 가격 별칭 문법과 메타데이터 키.
  • 교훈: docs/LESSONS.md — 원래 마이그레이션에서 발견한 실패와 예외 사례.

한계

  • 즉시 적용 가능한 마이그레이션이 아니라 참고 구현임. 대상 카탈로그 생성, Stripe 데이터베이스 동기화, 임대 후 소유 요금제 완료 처리는 제공하지 않음.
  • Supabase 역할과 사용자 조회를 조정하지 않은 일반 PostgreSQL 지원도 제공하지 않음.
  • 라이브 키를 사용하기 전에 자체 스키마와 API 버전에서 라우팅 및 SQL을 테스트해야 함.

라이선스

  • MIT 라이선스, © 2026 Loris Comba.