mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init payments config backend & dash frontend
This commit is contained in:
parent
96e453a4de
commit
deba63cc15
15 changed files with 774 additions and 14 deletions
|
|
@ -30,7 +30,7 @@ class CollectionUpdate(CollectionBase):
|
|||
courses: Optional[list]
|
||||
name: Optional[str]
|
||||
public: Optional[bool]
|
||||
description: Optional[str]
|
||||
description: Optional[str] = ""
|
||||
|
||||
|
||||
class CollectionRead(CollectionBase):
|
||||
|
|
|
|||
49
apps/api/src/db/payments/payments.py
Normal file
49
apps/api/src/db/payments/payments.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Literal, Optional
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import JSON
|
||||
from sqlmodel import Field, SQLModel, Column, BigInteger, ForeignKey
|
||||
|
||||
|
||||
class StripeProviderConfig(BaseModel):
|
||||
stripe_key: str = ""
|
||||
stripe_secret_key: str = ""
|
||||
stripe_webhook_secret: str = ""
|
||||
|
||||
class PaymentProviderEnum(str, Enum):
|
||||
STRIPE = "stripe"
|
||||
|
||||
class PaymentsConfigBase(SQLModel):
|
||||
enabled: bool = False
|
||||
provider: PaymentProviderEnum = PaymentProviderEnum.STRIPE
|
||||
provider_config: dict = Field(default={}, sa_column=Column(JSON))
|
||||
|
||||
|
||||
class PaymentsConfig(PaymentsConfigBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
org_id: int = Field(
|
||||
sa_column=Column(BigInteger, ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
creation_date: datetime = Field(default=datetime.now())
|
||||
update_date: datetime = Field(default=datetime.now())
|
||||
|
||||
|
||||
class PaymentsConfigCreate(PaymentsConfigBase):
|
||||
pass
|
||||
|
||||
|
||||
class PaymentsConfigUpdate(PaymentsConfigBase):
|
||||
enabled: Optional[bool] = False
|
||||
provider_config: Optional[dict] = None
|
||||
|
||||
|
||||
class PaymentsConfigRead(PaymentsConfigBase):
|
||||
id: int
|
||||
org_id: int
|
||||
creation_date: datetime
|
||||
update_date: datetime
|
||||
|
||||
|
||||
class PaymentsConfigDelete(SQLModel):
|
||||
id: int
|
||||
|
|
@ -5,7 +5,7 @@ from src.routers import dev, trail, users, auth, orgs, roles
|
|||
from src.routers.ai import ai
|
||||
from src.routers.courses import chapters, collections, courses, assignments
|
||||
from src.routers.courses.activities import activities, blocks
|
||||
from src.routers.ee import cloud_internal
|
||||
from src.routers.ee import cloud_internal, payments
|
||||
from src.routers.install import install
|
||||
from src.services.dev.dev import isDevModeEnabledOrRaise
|
||||
from src.services.install.install import isInstallModeEnabled
|
||||
|
|
@ -32,6 +32,7 @@ v1_router.include_router(
|
|||
)
|
||||
v1_router.include_router(trail.router, prefix="/trail", tags=["trail"])
|
||||
v1_router.include_router(ai.router, prefix="/ai", tags=["ai"])
|
||||
v1_router.include_router(payments.router, prefix="/payments", tags=["payments"])
|
||||
|
||||
if os.environ.get("CLOUD_INTERNAL_KEY"):
|
||||
v1_router.include_router(
|
||||
|
|
|
|||
53
apps/api/src/routers/ee/payments.py
Normal file
53
apps/api/src/routers/ee/payments.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlmodel import Session
|
||||
from src.core.events.database import get_db_session
|
||||
from src.db.payments.payments import PaymentsConfig, PaymentsConfigBase, PaymentsConfigCreate, PaymentsConfigRead, PaymentsConfigUpdate
|
||||
from src.db.users import PublicUser
|
||||
from src.security.auth import get_current_user
|
||||
from src.services.payments.payments import (
|
||||
create_payments_config,
|
||||
get_payments_config,
|
||||
update_payments_config,
|
||||
delete_payments_config,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/{org_id}/config")
|
||||
async def api_create_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
payments_config: PaymentsConfigCreate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
) -> PaymentsConfig:
|
||||
return await create_payments_config(request, org_id, payments_config, current_user, db_session)
|
||||
|
||||
@router.get("/{org_id}/config")
|
||||
async def api_get_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
) -> list[PaymentsConfigRead]:
|
||||
return await get_payments_config(request, org_id, current_user, db_session)
|
||||
|
||||
@router.put("/{org_id}/config")
|
||||
async def api_update_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
payments_config: PaymentsConfigUpdate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
) -> PaymentsConfig:
|
||||
return await update_payments_config(request, org_id, payments_config, current_user, db_session)
|
||||
|
||||
@router.delete("/{org_id}/config")
|
||||
async def api_delete_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session: Session = Depends(get_db_session),
|
||||
):
|
||||
await delete_payments_config(request, org_id, current_user, db_session)
|
||||
return {"message": "Payments config deleted successfully"}
|
||||
127
apps/api/src/services/payments/payments.py
Normal file
127
apps/api/src/services/payments/payments.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
from typing import Optional
|
||||
from fastapi import HTTPException, Request
|
||||
from sqlmodel import Session, select
|
||||
from src.db.payments.payments import (
|
||||
PaymentsConfig,
|
||||
PaymentsConfigCreate,
|
||||
PaymentsConfigUpdate,
|
||||
PaymentsConfigRead,
|
||||
)
|
||||
from src.db.users import PublicUser, AnonymousUser
|
||||
from src.db.organizations import Organization
|
||||
from src.services.orgs.orgs import rbac_check
|
||||
|
||||
|
||||
async def create_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
payments_config: PaymentsConfigCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
) -> PaymentsConfig:
|
||||
# Check if organization exists
|
||||
statement = select(Organization).where(Organization.id == org_id)
|
||||
org = db_session.exec(statement).first()
|
||||
if not org:
|
||||
raise HTTPException(status_code=404, detail="Organization not found")
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "create", db_session)
|
||||
|
||||
# Check if payments config already exists for this organization
|
||||
statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
|
||||
existing_config = db_session.exec(statement).first()
|
||||
if existing_config:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Payments config already exists for this organization",
|
||||
)
|
||||
|
||||
# Create new payments config
|
||||
new_config = PaymentsConfig(**payments_config.model_dump(), org_id=org_id)
|
||||
db_session.add(new_config)
|
||||
db_session.commit()
|
||||
db_session.refresh(new_config)
|
||||
|
||||
return new_config
|
||||
|
||||
|
||||
async def get_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
) -> list[PaymentsConfigRead]:
|
||||
# Check if organization exists
|
||||
statement = select(Organization).where(Organization.id == org_id)
|
||||
org = db_session.exec(statement).first()
|
||||
if not org:
|
||||
raise HTTPException(status_code=404, detail="Organization not found")
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "read", db_session)
|
||||
|
||||
# Get payments config
|
||||
statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
|
||||
configs = db_session.exec(statement).all()
|
||||
|
||||
return [PaymentsConfigRead.model_validate(config) for config in configs]
|
||||
|
||||
|
||||
async def update_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
payments_config: PaymentsConfigUpdate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
) -> PaymentsConfig:
|
||||
# Check if organization exists
|
||||
statement = select(Organization).where(Organization.id == org_id)
|
||||
org = db_session.exec(statement).first()
|
||||
if not org:
|
||||
raise HTTPException(status_code=404, detail="Organization not found")
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "update", db_session)
|
||||
|
||||
# Get existing payments config
|
||||
statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
|
||||
config = db_session.exec(statement).first()
|
||||
if not config:
|
||||
raise HTTPException(status_code=404, detail="Payments config not found")
|
||||
|
||||
# Update config
|
||||
for key, value in payments_config.model_dump().items():
|
||||
setattr(config, key, value)
|
||||
|
||||
db_session.add(config)
|
||||
db_session.commit()
|
||||
db_session.refresh(config)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
async def delete_payments_config(
|
||||
request: Request,
|
||||
org_id: int,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
) -> None:
|
||||
# Check if organization exists
|
||||
statement = select(Organization).where(Organization.id == org_id)
|
||||
org = db_session.exec(statement).first()
|
||||
if not org:
|
||||
raise HTTPException(status_code=404, detail="Organization not found")
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, org.org_uuid, current_user, "delete", db_session)
|
||||
|
||||
# Get existing payments config
|
||||
statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
|
||||
config = db_session.exec(statement).first()
|
||||
if not config:
|
||||
raise HTTPException(status_code=404, detail="Payments config not found")
|
||||
|
||||
# Delete config
|
||||
db_session.delete(config)
|
||||
db_session.commit()
|
||||
Loading…
Add table
Add a link
Reference in a new issue