Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions backend/alembic_migrations/versions/0051_drop_billing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""drop billing(credit_transactions / agent_usage_logs / users.credit_balance)

プリペイド課金・使用ログ課金を撤去(ADR-0023 / #522)。abuse 防止は日次レート制限
(#521 / agent_daily_usage)へ移行済み。

- credit_transactions テーブル削除(クレジット台帳)
- agent_usage_logs テーブル削除(使用ログ)
- users.credit_balance カラム削除(残高キャッシュ。libSQL/SQLite 3.35+ は素の
カラムの直接 DROP COLUMN をサポートするため batch を使わない)

Revision ID: 0051_drop_billing
Revises: 0050_create_agent_daily_usage
Create Date: 2026-07-22 00:00:00.000000
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0051_drop_billing"
down_revision: Union[str, None] = "0050_create_agent_daily_usage"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def _table_exists(name: str) -> bool:
conn = op.get_bind()
result = conn.execute(
sa.text("SELECT name FROM sqlite_master WHERE type='table' AND name=:name"),
{"name": name},
)
return result.fetchone() is not None


def upgrade() -> None:
if _table_exists("credit_transactions"):
op.drop_table("credit_transactions")
if _table_exists("agent_usage_logs"):
op.drop_table("agent_usage_logs")
op.drop_column("users", "credit_balance")


def downgrade() -> None:
# 課金は機能撤去のため復元しない(no-op / ADR-0023)。
# users.credit_balance のみ、スキーマ整合のため復元する。
op.add_column(
"users",
sa.Column("credit_balance", sa.Integer(), nullable=False, server_default="0"),
)
7 changes: 0 additions & 7 deletions backend/app/core/env_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,6 @@
# (services/agent/rate_limit.py の DEFAULT_AGENT_DAILY_LIMIT)を使う。
AGENT_DAILY_LIMIT = "AGENT_DAILY_LIMIT"

# --- 決済(Stripe Checkout / ADR-0012 Phase 2) ---

# 本番(Cloud Run)では Secret Manager から注入する。ログ出力禁止
STRIPE_SECRET_KEY = "STRIPE_SECRET_KEY"
# Webhook 署名検証用シークレット(whsec_...)。ログ出力禁止
STRIPE_WEBHOOK_SECRET = "STRIPE_WEBHOOK_SECRET"

# --- アプリ起動制御 ---

APP_BOOTSTRAPPED = "APP_BOOTSTRAPPED"
4 changes: 0 additions & 4 deletions backend/app/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ class ErrorCode(str, Enum):
AGENT_PARSE_ERROR = "AGENT_PARSE_ERROR"
# Agent の日次利用上限(#521 / ADR-0023)
AGENT_DAILY_LIMIT_EXCEEDED = "AGENT_DAILY_LIMIT_EXCEEDED"
# 課金(プリペイドクレジット / ADR-0012)
INSUFFICIENT_CREDITS = "INSUFFICIENT_CREDITS"
# 決済(Stripe Checkout / ADR-0012 Phase 2)
PAYMENT_ERROR = "PAYMENT_ERROR"
# アプリケーション全体
RATE_LIMITED = "RATE_LIMITED"
# サーバー
Expand Down
23 changes: 0 additions & 23 deletions backend/app/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,29 +234,6 @@ def get_ollama_timeout_seconds() -> float:
return value if value > 0 else 300.0


def get_stripe_secret_key() -> str:
"""Stripe シークレットキー(sk_...)を取得する。値はログや例外メッセージに含めないこと。"""
return os.getenv(env_keys.STRIPE_SECRET_KEY, "").strip()


def get_stripe_webhook_secret() -> str:
"""Stripe Webhook 署名シークレット(whsec_...)を取得する。値はログに含めないこと。"""
return os.getenv(env_keys.STRIPE_WEBHOOK_SECRET, "").strip()


def get_billing_return_base_url() -> str:
"""Stripe Checkout の success/cancel リダイレクト先のベース URL を返す。

フロントエンド(Cloudflare Pages)の URL を指す。OAuth と同じ ``CALLBACK_BASE_URL``
を優先し、未設定ならローカル開発向けに CORS の先頭オリジンへフォールバックする。
"""
base = get_callback_base_url()
if base:
return base
origins = get_cors_origins()
return origins[0].rstrip("/") if origins else ""


def get_log_format() -> str:
"""ログフォーマット指定(json / text / 空)を小文字で取得する。"""
return os.getenv(env_keys.LOG_FORMAT, "").strip().lower()
Expand Down
7 changes: 2 additions & 5 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from .routers import ( # noqa: E402
agent_router,
auth_router,
billing_router,
github_link_router,
health_router,
internal_router,
Expand Down Expand Up @@ -133,9 +132,8 @@ async def _unhandled_exception_handler(request: Request, exc: Exception):
)


# Stripe Webhook は Cloudflare Pages を経由せず Cloud Run へ直接届くため
# X-Internal-Secret を持たない。署名検証(Stripe-Signature)で別途防御する(ADR-0012)
_INTERNAL_SECRET_SKIP_PATHS = {"/health", "/api/billing/webhook"}
# INTERNAL_SECRET 検証をスキップするパス(ヘルスチェックのみ)
_INTERNAL_SECRET_SKIP_PATHS = {"/health"}
_INTERNAL_SECRET_HEADER = "x-internal-secret"


Expand Down Expand Up @@ -216,4 +214,3 @@ async def dispatch(self, request: Request, call_next) -> Response:
app.include_router(notifications_router)
app.include_router(internal_router)
app.include_router(agent_router)
app.include_router(billing_router)
8 changes: 0 additions & 8 deletions backend/app/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,6 @@
"draft_not_ready": "経歴書ドラフトの生成が完了していません。生成を実行してからダウンロードしてください。",
"skill_display_no_skills": "表示名を提案できるスキルがありません。先に GitHub 連携を実行してください。",
"skill_display_invalid_identity": "確定対象に連携結果に存在しないスキルが含まれています。"
},
"billing": {
"insufficient_credits": "クレジット残高が不足しています。Haiku(無料)に切り替えるか、クレジットを追加してください。",
"grant_user_not_found": "付与対象のユーザーが見つかりません。",
"checkout_unavailable": "クレジット購入機能は現在利用できません。時間をおいて再度お試しください。",
"checkout_failed": "決済ページの作成に失敗しました。時間をおいて再度お試しください。",
"webhook_unavailable": "決済通知を処理できません。",
"webhook_invalid": "決済通知の検証に失敗しました。"
}
},
"notification": {
Expand Down
3 changes: 0 additions & 3 deletions backend/app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""SQLAlchemy モデル。"""

from .agent_usage import AgentDailyUsage
from .billing import AgentUsageLog, CreditTransaction
from .cache import GitHubLinkCache, ResumeDraftCache
from .master_data import MQualification, MTechnologyStack
from .notification import Notification
Expand All @@ -26,8 +25,6 @@

__all__ = [
"AgentDailyUsage",
"AgentUsageLog",
"CreditTransaction",
"GitHubLinkCache",
"GitHubSkill",
"GitHubSkillDisplayDecision",
Expand Down
70 changes: 0 additions & 70 deletions backend/app/models/billing.py

This file was deleted.

7 changes: 1 addition & 6 deletions backend/app/models/user.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import uuid
from datetime import datetime

from sqlalchemy import DateTime, Integer, String, func
from sqlalchemy import DateTime, String, func
from sqlalchemy.orm import Mapped, mapped_column

from ..db import Base
Expand All @@ -16,11 +16,6 @@ class User(Base):
github_id: Mapped[int | None] = mapped_column(nullable=True, unique=True, default=None)
github_token: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
refresh_jti: Mapped[str | None] = mapped_column(String(36), nullable=True, default=None)
# プリペイドクレジット残高のキャッシュ(正本は credit_transactions 台帳 / ADR-0012)。
# 事前チェック通過後の事後減算により一時的に負になりうる(有界損失を許容)
credit_balance: Mapped[int] = mapped_column(
Integer, nullable=False, default=0, server_default="0"
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
Expand Down
2 changes: 0 additions & 2 deletions backend/app/repositories/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Repository 層。"""

from .base import BaseMasterRepository, SingleUserDocumentRepository
from .billing import BillingRepository
from .github_link import GitHubLinkCacheRepository
from .master_data import MQualificationRepository, MTechnologyStackRepository
from .resume import ResumeRepository
Expand All @@ -10,7 +9,6 @@

__all__ = [
"BaseMasterRepository",
"BillingRepository",
"GitHubLinkCacheRepository",
"GitHubSkillRepository",
"MQualificationRepository",
Expand Down
Loading