yeonghoon.kim

  • 게시판
  • 갤러리

Tech Support 플랫폼

김영훈 2026.03.04 10:09 조회 수 : 80

CHT 토큰을 결제 수단으로 쓰는 기술 지원 플랫폼 작업.

프로젝트.

0030-tech-support

구성.

FastAPI
React
PostgreSQL
CHT / Sepolia
Docker Compose
Traefik
Caddy WAF

흐름.

회원가입
이메일 인증
지갑 생성
KRW 200,000 지급
KRW -> CHT 스왑
티켓 생성
CHT 결제
기술 지원 처리

구조.

0030-tech-support/
├── backend/
│   ├── app/
│   │   ├── main.py
│   │   ├── config.py
│   │   ├── database.py
│   │   ├── models.py
│   │   ├── schemas.py
│   │   ├── dependencies.py
│   │   ├── routers/
│   │   │   ├── auth.py
│   │   │   ├── tickets.py
│   │   │   ├── wallet.py
│   │   │   └── admin.py
│   │   └── utils/
│   │       └── jwt.py
│   ├── scripts/
│   │   ├── seed_categories.py
│   │   ├── seed_categories_hierarchy.py
│   │   └── seed_categories_v2.py
│   ├── tests/
│   ├── requirements.txt
│   └── Dockerfile
├── frontend/
│   ├── src/
│   │   ├── App.jsx
│   │   ├── pages/
│   │   ├── components/
│   │   ├── context/
│   │   └── api/
│   ├── nginx.conf
│   ├── package.json
│   └── Dockerfile
├── docker-compose.yml
└── scripts/send_eth.py

주요 API.

POST /api/v1/auth/register
POST /api/v1/auth/login
GET  /api/v1/auth/me

GET  /api/v1/categories
POST /api/v1/tickets
GET  /api/v1/tickets
GET  /api/v1/tickets/{id}

GET  /api/v1/wallet/balance
POST /api/v1/wallet/charge-krw
POST /api/v1/wallet/swap
POST /api/v1/tickets/{id}/payment

GET   /api/v1/admin/dashboard/stats
GET   /api/v1/admin/tickets
PATCH /api/v1/admin/tickets/{id}/status
GET   /api/v1/admin/transactions

이메일 인증 시 지갑 생성.

@router.get("/verify-email")
async def verify_email(token: str, db: Session = Depends(get_db)):
    payload = decode_email_token(token)
    user_id = payload.get("user_id")
    
    user = db.query(User).filter(User.id == user_id).first()
    
    if user.email_verified:
        raise HTTPException(400, "이미 인증됨")
    
    from eth_account import Account
    account = Account.create()
    
    from cryptography.fernet import Fernet
    fernet = Fernet(settings.WALLET_ENCRYPTION_KEY.encode())
    encrypted_key = fernet.encrypt(account.key.hex().encode())
    
    wallet_key = WalletKey(
        user_id=user.id,
        encrypted_private_key=encrypted_key
    )
    db.add(wallet_key)
    
    user.wallet_address = account.address
    user.email_verified = True
    
    wallet = Wallet(
        user_id=user.id,
        krw_balance=200000.0,
        cht_balance=0.0
    )
    db.add(wallet)
    db.commit()
    
    return {"message": "이메일 인증 완료! 지갑이 생성되었습니다."}

환율 테이블.

class ExchangeRate(Base):
    __tablename__ = "exchange_rates"
    
    id = Column(Integer, primary_key=True)
    base_currency = Column(String, default="USD")
    target_currency = Column(String, default="KRW")
    rate = Column(Numeric(precision=10, scale=4))
    updated_at = Column(DateTime, default=datetime.utcnow)

외부 환율 API.

async def fetch_exchange_rate() -> float:
    url = "https://api.exchangerate-api.com/v4/latest/USD"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        data = response.json()
        return data["rates"]["KRW"]

스왑 API.

@router.post("/swap")
async def swap_currency(
    request: SwapRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    wallet = db.query(Wallet).filter(Wallet.user_id == current_user.id).first()
    
    rate = get_latest_exchange_rate(db)
    
    if request.direction == "krw_to_cht":
        krw_amount = request.amount
        cht_amount = krw_amount / rate.rate
        
        if wallet.krw_balance < krw_amount:
            raise HTTPException(400, "KRW 잔액 부족")
        
        wallet.krw_balance -= krw_amount
        wallet.cht_balance += cht_amount
    
    elif request.direction == "cht_to_krw":
        cht_amount = request.amount
        krw_amount = cht_amount * rate.rate
        
        if wallet.cht_balance < cht_amount:
            raise HTTPException(400, "CHT 잔액 부족")
        
        wallet.cht_balance -= cht_amount
        wallet.krw_balance += krw_amount
    
    db.commit()
    return {"new_krw_balance": wallet.krw_balance, "new_cht_balance": wallet.cht_balance}

티켓 카테고리.

categories = [
    {"name": "DevOps", "rate_per_hour": 50000},
    {"name": "인프라/보안", "rate_per_hour": 60000},
    {"name": "개발 컨설팅", "rate_per_hour": 70000},
    {"name": "모니터링/로깅", "rate_per_hour": 50000},
    {"name": "기타", "rate_per_hour": 0}
]

티켓 생성.

@router.post("/tickets")
async def create_ticket(
    request: TicketCreateRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    category = db.query(Category).filter(Category.id == request.category_id).first()
    
    estimated_cost = category.rate_per_hour * request.estimated_hours
    
    ticket = Ticket(
        user_id=current_user.id,
        category_id=request.category_id,
        title=request.title,
        description=request.description,
        estimated_hours=request.estimated_hours,
        estimated_cost_krw=estimated_cost,
        status="requested"
    )
    db.add(ticket)
    db.commit()
    
    return TicketResponse.from_orm(ticket)

상태.

requested
approved
processing
completed
paid

Docker 실행.

docker compose up --build -d

상태 확인.

docker compose ps

로그.

docker compose logs -f backend

테스트.

pytest backend/tests/ -v --cov=backend/app --cov-report=term-missing

결과.

112 tests passed
Coverage 81%

커밋 기록.

2026-03-05
CHT 기반 가격 체계 전환

2026-03-05
API 응답에 fixed_price_cht 필드 추가

2026-03-05
지갑에 ETH 잔액 추가

2026-03-05
에스크로 방식 복구: 티켓 생성 시 CHT 즉시 차감

2026-03-05
티켓 비용 계산 수정

2026-03-06
Header 컴포넌트 추가

2026-03-06
파스텔톤 배경 제거 및 이모지 삭제

2026-03-06
모든 파일에서 이모지 제거

처음엔 회원가입 시 지갑을 만들었는데 이메일 인증 완료 시점으로 변경.

자원 할당은 인증 이후.

Traefik 라우팅 문제는 restart로 안 잡혀서 force recreate 사용.

docker compose up -d --force-recreate

  • 추천 0

  • 비추천 0
이 게시물을
목록

댓글 0

번호 제목 글쓴이 날짜 조회 수
공지 2025 일본 여행 계획 김영훈 2024.10.10 4119
공지 현금, 저축, 투자, 지출, 예산, 보험 내역(2024-05-30) 김영훈 2024.03.10 3752
17 ARM Kubernetes 구축 1 - VNC & KVM 설치 김영훈 2026.02.14 70
16 ARM Kubernetes 구축 3 - 모니터링 스택 Prometheus Grafana 김영훈 2026.02.16 179
15 ARM Kubernetes 구축 5 - 실전 업그레이드 경험기 v1.29→v1.35 김영훈 2026.02.18 238
14 ARM Kubernetes 구축 4 - 외부 접근 환경 VPN MetalLB Ingress 김영훈 2026.02.18 246
13 ARM Kubernetes 구축 7 - 클러스터 무중단 업그레이드 v1.29→v1.35 김영훈 2026.02.20 252
12 ARM Kubernetes 구축 8 - 실전 보안 RBAC 네트워크 정책 Pod 보안 김영훈 2026.02.20 194
11 ARM Kubernetes 구축 6 - 무중단 업그레이드 김영훈 2026.02.20 208
10 비트코인 자동매매 봇 대시보드 김영훈 2026.02.21 144
9 Apache RewriteRule 쿼리스트링 처리 김영훈 2026.02.28 258
8 Redis 대기열 시스템 김영훈 2026.03.01 212
7 ChloeToken 테스트넷 배포 김영훈 2026.03.02 232
» Tech Support 플랫폼 김영훈 2026.03.04 80
5 WAF 운영 6주 분석 김영훈 2026.03.13 199
4 JSON-LD 학습 - 메일과 Calendar 자동 인식 김영훈 2026.03.28 151
3 7일간의 일본 여행 일정 짜기: 신칸센 + JR 패스 + Google Calendar 활용 프로젝트 김영훈 2026.03.28 128
2 교토 4박 5일 여행 일정: Google Calendar로 관리하는 일정짜기 김영훈 2026.04.05 125
1 OpenClaw 복원과 GLM-5.1 전환 김영훈 2026.04.07 145
쓰기 태그
 첫 페이지 7 8 9 10 11 12 13 14 15 16 끝 페이지