Skip to content

Commit

Permalink
Implement Payment Account management and resolve some bugs (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
eloravpn authored Jan 3, 2025
1 parent 6fa5136 commit 90c99ba
Show file tree
Hide file tree
Showing 13 changed files with 595 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
service_router,
payment_router,
transaction_router,
payment_account_router,
)
from src.config import DOCS, DEBUG, UVICORN_HOST, UVICORN_PORT
from src.database import Base, engine
Expand Down Expand Up @@ -95,6 +96,7 @@
app.include_router(monitoring_router, prefix="/api", tags=["Monitoring"])
app.include_router(club_user_router, prefix="/api", tags=["ClubUser"])
app.include_router(config_setting_router, prefix="/api", tags=["ConfigSettings"])
app.include_router(payment_account_router, prefix="/api", tags=["PaymentAccounts"])

app.include_router(system_router, prefix="/api", tags=["system"])

Expand Down
46 changes: 45 additions & 1 deletion src/commerce/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class Transaction(Base):
payment_id = Column(Integer, ForeignKey("payment.id"), nullable=True)
payment = relationship("Payment", back_populates="transactions")

payment_account_id = Column(
Integer, ForeignKey("payment_account.id"), nullable=True
)
payment_account = relationship("PaymentAccount", back_populates="transactions")

order_id = Column(Integer, ForeignKey("order.id"), nullable=True)
order = relationship("Order", back_populates="transactions")

Expand Down Expand Up @@ -64,6 +69,11 @@ class Payment(Base):
order_id = Column(Integer, ForeignKey("order.id"), nullable=True)
order = relationship("Order", back_populates="payments")

payment_account_id = Column(
Integer, ForeignKey("payment_account.id"), nullable=True
)
payment_account = relationship("PaymentAccount", back_populates="payments")

transactions = relationship("Transaction", back_populates="payment")

method = Column(
Expand All @@ -73,7 +83,8 @@ class Payment(Base):
total = Column(BigInteger, default=0)

status = Column(Enum(PaymentStatus), nullable=False, default=PaymentStatus.pending)

verify = Column(Boolean, default=True, nullable=False)
paid_at = Column(DateTime, default=datetime.utcnow, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
modified_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

Expand Down Expand Up @@ -172,3 +183,36 @@ def discount_percent(self):
return percent
else:
return 0


class PaymentAccount(Base):
__tablename__ = "payment_account"
id = Column(Integer, primary_key=True, index=True)

# Relations
user_id = Column(Integer, ForeignKey("user.id"))
user = relationship("User", back_populates="payment_accounts")

# Account info
card_number = Column(String(16), unique=True, index=True)
account_number = Column(String(128), unique=True, index=True)
bank_name = Column(String(128), unique=True, index=True)
shaba = Column(String(128), unique=True, index=True)
owner_name = Column(String(128))
payment_notice = Column(String(300))
owner_family = Column(String(128))

# Settings
enable = Column(Boolean, default=True)
min_payment_for_bot = Column(BigInteger, default=0)
min_payment_amount = Column(BigInteger, default=0)
max_daily_transactions = Column(Integer, default=100)
max_daily_amount = Column(BigInteger, default=1000000000) # 1B default limit

# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
modified_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

# Relationships
payments = relationship("Payment", back_populates="payment_account")
transactions = relationship("Transaction", back_populates="payment_account")
111 changes: 107 additions & 4 deletions src/commerce/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import src.commerce.service as commerce_service
import src.users.service as user_service
from src.admins.schemas import Admin
from src.commerce.exc import MaxPendingOrderError
from src.commerce.models import Order, Payment
from src.commerce.models import Order, Payment, PaymentAccount
from src.commerce.schemas import (
OrderResponse,
OrderCreate,
Expand All @@ -28,18 +27,22 @@
PaymentMethod,
TransactionResponse,
TransactionCreate,
TransactionModify,
TransactionsResponse,
TransactionType,
PaymentAccountResponse,
PaymentAccountModify,
PaymentAccountsResponse,
PaymentAccountCreate,
)
from src.database import get_db
from src.exc import EloraApplicationError
from src.hosts.service import get_host_zone

order_router = APIRouter()
service_router = APIRouter()
payment_router = APIRouter()
transaction_router = APIRouter()
payment_account_router = APIRouter()


logger = logging.getLogger("uvicorn.error")

Expand Down Expand Up @@ -281,17 +284,27 @@ def add_payment(
raise HTTPException(status_code=404, detail="User not found")

db_order: Order = None
db_payment_account: PaymentAccount = None

if payment.order_id > 0:
db_order = commerce_service.get_order(db=db, order_id=payment.order_id)

if not db_order:
raise HTTPException(status_code=404, detail="Order not found")

if payment.payment_account_id > 0:
db_payment_account = commerce_service.get_payment_account(
db, payment.payment_account_id
)

if not db_payment_account:
raise HTTPException(status_code=404, detail="Payment account not found")

try:
db_payment = commerce_service.create_payment(
db=db,
db_user=db_user,
db_payment_account=db_payment_account,
db_order=db_order if db_order else None,
payment=payment,
)
Expand Down Expand Up @@ -531,3 +544,93 @@ def get_transactions(
)

return {"transactions": transactions, "total": count}


@payment_account_router.post(
"/payment-accounts/", tags=["PaymentAccount"], response_model=PaymentAccountResponse
)
def add_payment_account(
account: PaymentAccountCreate,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
try:
db_account = commerce_service.create_payment_account(db=db, account=account)
except IntegrityError:
raise HTTPException(status_code=409, detail="Payment account already exists")

return db_account


@payment_account_router.put(
"/payment-accounts/{account_id}",
tags=["PaymentAccount"],
response_model=PaymentAccountResponse,
)
def modify_payment_account(
account_id: int,
account: PaymentAccountModify,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
db_account = commerce_service.get_payment_account(db, account_id=account_id)
if not db_account:
raise HTTPException(status_code=404, detail="Payment account not found")

return commerce_service.update_payment_account(
db=db, db_account=db_account, modify=account
)


@payment_account_router.get(
"/payment-accounts/{account_id}",
tags=["PaymentAccount"],
response_model=PaymentAccountResponse,
)
def get_payment_account(
account_id: int,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
db_account = commerce_service.get_payment_account(db, account_id)
if not db_account:
raise HTTPException(status_code=404, detail="Payment account not found")

return db_account


@payment_account_router.delete(
"/payment-accounts/{account_id}", tags=["PaymentAccount"]
)
def delete_payment_account(
account_id: int,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
db_account = commerce_service.get_payment_account(db, account_id)
if not db_account:
raise HTTPException(status_code=404, detail="Payment account not found")

commerce_service.remove_payment_account(db=db, db_account=db_account)
return {}


@payment_account_router.get(
"/payment-accounts/",
tags=["PaymentAccount"],
response_model=PaymentAccountsResponse,
)
def get_payment_accounts(
offset: int = None,
limit: int = None,
user_id: int = 0,
enable: bool = None,
q: str = None,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
accounts, count = commerce_service.get_payment_accounts(
db=db, offset=offset, limit=limit, user_id=user_id, enable=enable, q=q
)

return {"payment_accounts": accounts, "total": count}
44 changes: 43 additions & 1 deletion src/commerce/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from enum import Enum
from typing import List, Optional

from pydantic import BaseModel
from pydantic import BaseModel, constr

from src.accounts.schemas import AccountResponse
from src.hosts.schemas import HostZoneResponse
Expand Down Expand Up @@ -40,6 +40,7 @@ class TransactionBase(BaseModel):
user_id: Optional[int] = None
payment_id: Optional[int] = None
order_id: Optional[int] = None
payment_account_id: Optional[int] = None
amount: int
type: TransactionType = TransactionType.payment

Expand All @@ -62,6 +63,9 @@ class TransactionModify(TransactionBase):
class PaymentBase(BaseModel):
user_id: Optional[int] = None
order_id: Optional[int] = None
payment_account_id: Optional[int] = None
paid_at: Optional[datetime] = datetime.utcnow()
verify: Optional[bool] = True
total: int

method: PaymentMethod = PaymentMethod.money_order
Expand Down Expand Up @@ -201,6 +205,44 @@ class PaymentsResponse(BaseModel):
total: int


class PaymentAccountBase(BaseModel):
card_number: constr(min_length=16, max_length=16)
account_number: constr(max_length=128)
bank_name: Optional[str]
shaba: Optional[str]
owner_name: str
owner_family: str
enable: bool = True
min_payment_for_bot: int = 0
max_daily_transactions: int = 100
max_daily_amount: int = 1000000000
min_payment_amount: int = 0
user_id: Optional[int] = None
payment_notice: Optional[str] = None


class PaymentAccountCreate(PaymentAccountBase):
pass


class PaymentAccountModify(PaymentAccountBase):
pass


class PaymentAccountResponse(PaymentAccountBase):
id: int
created_at: datetime
modified_at: datetime

class Config:
orm_mode = True


class PaymentAccountsResponse(BaseModel):
payment_accounts: List[PaymentAccountResponse]
total: int


TransactionResponse.update_forward_refs()
PaymentResponse.update_forward_refs()
OrdersResponse.update_forward_refs()
Loading

0 comments on commit 90c99ba

Please sign in to comment.