feat: init payments config backend & dash frontend

This commit is contained in:
swve 2024-09-26 19:07:30 +02:00
parent 96e453a4de
commit deba63cc15
15 changed files with 774 additions and 14 deletions

View file

@ -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):

View 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

View file

@ -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(

View 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"}

View 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()