Browse Source

update backend

tomoya 1 year ago
parent
commit
6657e6e662

+ 2 - 2
backend/app/app/aianchor/utils2.py

@@ -5,9 +5,9 @@ import zipfile
 from io import BytesIO
 from chardet.universaldetector import UniversalDetector
 try:
-    from app.aianchor.config import *
+  from app.aianchor.config import *
 except ImportError:
-    from config import *
+  from config import *
 
 DEFAULT_ENCODING = "utf-8"
 

+ 4 - 2
backend/app/app/api/api_v1/api.py

@@ -1,7 +1,8 @@
 from fastapi import APIRouter
 
 from app.api.api_v1.endpoints import  login, users, utils, videos, images, reputations, ser_no
-from app.api.api_v1.endpoints import payment, ytviewspayment
+from app.api.api_v1.endpoints import ytviewspayment, payment
+from app.api.api_v1.endpoints import ecpay
 
 
 api_router = APIRouter()
@@ -13,4 +14,5 @@ api_router.include_router(images.router, prefix="/images", tags=["iamges"])
 api_router.include_router(reputations.router, prefix="/reputations", tags=["reputations"])
 api_router.include_router(ser_no.router, prefix="/ser_nos", tags=["serial numbers"])
 api_router.include_router(ytviewspayment.router, prefix="/payment", tags=["yt views payment"])
-api_router.include_router(payment.router, prefix="/payment", tags=["payment"])
+api_router.include_router(payment.router, prefix="/payment", tags=["payment"])
+api_router.include_router(ecpay.router, prefix="/ecpay", tags=["ecpay"])

+ 112 - 0
backend/app/app/api/api_v1/endpoints/ecpay.py

@@ -0,0 +1,112 @@
+from typing import Any, List, Optional
+from datetime import datetime
+
+from fastapi import APIRouter, Body, Depends, HTTPException, Form, status, Response
+from fastapi.encoders import jsonable_encoder
+from pydantic.networks import EmailStr
+from sqlalchemy.orm import Session
+
+from app import crud, models, schemas
+from app.api import deps
+from app.core.config import settings
+from app.core.ecpay_payment_sdk import ECPayPaymentSdk
+from app.utils import send_new_account_email
+
+import requests
+
+router = APIRouter()
+
+@router.post("/ecpay-result-return")
+def ecpay_return(
+    *,
+    MerchantID: Optional[str]=Form(None),
+    MerchantTradeNo: Optional[str]=Form(None),
+    StoreID: Optional[str]=Form(None),
+    RtnCode: Optional[int]=Form(None),
+    RtnMsg: Optional[str]=Form(None),
+    TradeNo: Optional[str]=Form(None),
+    TradeAmt: Optional[int]=Form(None),
+    PaymentDate: Optional[str]=Form(None),
+    PaymentType: Optional[str]=Form(None),
+    PaymentTypeChargeFee: Optional[int]=Form(None),
+    TradeDate: Optional[str]=Form(None),
+    SimulatePaid: Optional[int]=Form(None),
+    CustomField1: Optional[str]=Form(None),
+    CustomField2: Optional[str]=Form(None),
+    CustomField3: Optional[str]=Form(None),
+    CustomField4: Optional[str]=Form(None),
+    CheckMacValue: Optional[str]=Form(None),
+) -> Any:
+  #送email
+  print(f"\
+MerchantID: {MerchantID} \n\
+MerchantTradeNo: {MerchantTradeNo}\n\
+StoreID: {StoreID}\n\
+RtnCode: {RtnCode}\n\
+RtnMsg: {RtnMsg}\n\
+TradeNo: {TradeNo}\n\
+TradeAmt: {TradeAmt}\n\
+PaymentDate: {PaymentDate}\n\
+PaymentType: {PaymentType}\n\
+PaymentTypeChargeFee: {PaymentTypeChargeFee}\n\
+TradeDate: {TradeDate}\n\
+SimulatePaid: {SimulatePaid}\n\
+CustomField1: {CustomField1}\n\
+CustomField2: {CustomField2}\n\
+CustomField3: {CustomField3}\n\
+CustomField4: {CustomField4}\n\
+CheckMacValue: {CheckMacValue}\
+  ")
+  return Response(content='1', status_code=status.HTTP_200_OK)
+
+@router.post("/ecpay-test-result-return")
+def ecpay_return(
+    *,
+    MerchantID: Optional[str]=Form(None),
+    MerchantTradeNo: Optional[str]=Form(None),
+    StoreID: Optional[str]=Form(None),
+    RtnCode: Optional[int]=Form(None),
+    RtnMsg: Optional[str]=Form(None),
+    TradeNo: Optional[str]=Form(None),
+    TradeAmt: Optional[int]=Form(None),
+    PaymentDate: Optional[str]=Form(None),
+    PaymentType: Optional[str]=Form(None),
+    PaymentTypeChargeFee: Optional[int]=Form(None),
+    TradeDate: Optional[str]=Form(None),
+    SimulatePaid: Optional[int]=Form(None),
+    CustomField1: Optional[str]=Form(None),
+    CustomField2: Optional[str]=Form(None),
+    CustomField3: Optional[str]=Form(None),
+    CustomField4: Optional[str]=Form(None),
+    CheckMacValue: Optional[str]=Form(None),
+) -> Any:
+  #送email
+  print(f"\
+MerchantID: {MerchantID} \n\
+MerchantTradeNo: {MerchantTradeNo}\n\
+StoreID: {StoreID}\n\
+RtnCode: {RtnCode}\n\
+RtnMsg: {RtnMsg}\n\
+TradeNo: {TradeNo}\n\
+TradeAmt: {TradeAmt}\n\
+PaymentDate: {PaymentDate}\n\
+PaymentType: {PaymentType}\n\
+PaymentTypeChargeFee: {PaymentTypeChargeFee}\n\
+TradeDate: {TradeDate}\n\
+SimulatePaid: {SimulatePaid}\n\
+CustomField1: {CustomField1}\n\
+CustomField2: {CustomField2}\n\
+CustomField3: {CustomField3}\n\
+CustomField4: {CustomField4}\n\
+CheckMacValue: {CheckMacValue}\
+  ")
+  return Response(content='1', status_code=status.HTTP_200_OK)
+
+
+@router.get('/check-payment-list')
+def get_payment_list(
+  *,
+  db: Session = Depends(deps.get_db),
+  current_user: models.User = Depends(deps.get_current_active_user)
+):
+  pass

+ 276 - 56
backend/app/app/api/api_v1/endpoints/payment.py

@@ -3,65 +3,287 @@ from datetime import datetime
 
 from fastapi import APIRouter, Body, Depends, HTTPException, Form, status, Response
 from fastapi.encoders import jsonable_encoder
-from pydantic.networks import EmailStr
+from pydantic.networks import EmailStr, HttpUrl
 from sqlalchemy.orm import Session
 
-from app import crud, models, schemas
+import app.crud as crud
+import app.models as models
+import app.schemas as schemas 
 from app.api import deps
 from app.core.config import settings
 from app.core.ecpay_payment_sdk import ECPayPaymentSdk
 from app.utils import send_new_account_email
-
+from pydantic import BaseModel
 import requests
+from random import choice
+import string
+import json
 
 router = APIRouter()
 
-@router.post("/ecpay-result-return")
-def ecpay_return(
-    *,
-    MerchantID: Optional[str]=Form(None),
-    MerchantTradeNo: Optional[str]=Form(None),
-    StoreID: Optional[str]=Form(None),
-    RtnCode: Optional[int]=Form(None),
-    RtnMsg: Optional[str]=Form(None),
-    TradeNo: Optional[str]=Form(None),
-    TradeAmt: Optional[int]=Form(None),
-    PaymentDate: Optional[str]=Form(None),
-    PaymentType: Optional[str]=Form(None),
-    PaymentTypeChargeFee: Optional[int]=Form(None),
-    TradeDate: Optional[str]=Form(None),
-    SimulatePaid: Optional[int]=Form(None),
-    CustomField1: Optional[str]=Form(None),
-    CustomField2: Optional[str]=Form(None),
-    CustomField3: Optional[str]=Form(None),
-    CustomField4: Optional[str]=Form(None),
-    CheckMacValue: Optional[str]=Form(None),
-) -> Any:
-  #送email
-  print(f"\
-MerchantID: {MerchantID} \n\
-MerchantTradeNo: {MerchantTradeNo}\n\
-StoreID: {StoreID}\n\
-RtnCode: {RtnCode}\n\
-RtnMsg: {RtnMsg}\n\
-TradeNo: {TradeNo}\n\
-TradeAmt: {TradeAmt}\n\
-PaymentDate: {PaymentDate}\n\
-PaymentType: {PaymentType}\n\
-PaymentTypeChargeFee: {PaymentTypeChargeFee}\n\
-TradeDate: {TradeDate}\n\
-SimulatePaid: {SimulatePaid}\n\
-CustomField1: {CustomField1}\n\
-CustomField2: {CustomField2}\n\
-CustomField3: {CustomField3}\n\
-CustomField4: {CustomField4}\n\
-CheckMacValue: {CheckMacValue}\
-  ")
-  return Response(content='1', status_code=status.HTTP_200_OK)
+@router.post('/ecpay-test-payment')
+def ecpay_payment(
+  *,
+  db: Session = Depends(deps.get_db),
+  current_user: models.User = Depends(deps.get_current_active_user),
+  payment_data: schemas.PaymentCreate,
+  lang: str=''
+):  
+    print(payment_data)
+    MerchantTradeNo = 'test'+datetime.now().strftime("NO%Y%m%d%H%M%S")
+    remark = {'MerchantTradeNo':MerchantTradeNo}
+    remark_string = json.dumps(remark, ensure_ascii=False)
+    payment = crud.payment.create_with_payment_data(db, 
+                                                    obj_in=payment_data,
+                                                    owner_id=current_user.id, 
+                                                    epayment='ecpay',
+                                                    remark=remark_string)
+    order_params = {
+        'MerchantTradeNo': MerchantTradeNo,
+        'StoreID': 'SaaS',
+        'MerchantTradeDate': datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
+        'PaymentType': 'aio',
+        'TotalAmount': payment.amount,
+        'TradeDesc': 'SaaS訂單',
+        'ItemName': f'credit: {payment.item}',
+        'ReturnURL': 'https://cloud.choozmo.com/api/v1/payment/ecpay-result-return',
+        'ChoosePayment': 'ALL',
+        'ClientBackURL': 'https://cloud.choozmo.com/payment',
+        'ItemURL': 'https://cloud.choozmo.com/payment',
+        'Remark': '',
+        'ChooseSubPayment': '',
+        'OrderResultURL': '',
+        'NeedExtraPaidInfo': 'Y',
+        'DeviceSource': '',
+        'IgnorePayment': 'ATM#CVS#BARCODE',
+        'PlatformID': '',
+        'InvoiceMark': 'N',
+        'CustomField1': f'user id: {str(current_user.id)}',
+        'CustomField2': f'pyment id: {str(payment.id)}',
+        'CustomField3': '',
+        'CustomField4': '',
+        'EncryptType': 1,
+        'Language': lang,
+    }
+
+    extend_params_1 = {
+        'ExpireDate': 7,
+        'PaymentInfoURL': 'https://www.ecpay.com.tw/payment_info_url.php',
+        'ClientRedirectURL': '',
+    }
+
+    extend_params_2 = {
+        'StoreExpireDate': 15,
+        'Desc_1': '',
+        'Desc_2': '',
+        'Desc_3': '',
+        'Desc_4': '',
+        'PaymentInfoURL': 'https://www.ecpay.com.tw/payment_info_url.php',
+        'ClientRedirectURL': '',
+    }
+
+    extend_params_3 = {
+        'BindingCard': 0,
+        'MerchantMemberID': '',
+    }
+
+    extend_params_4 = {
+        'Redeem': 'N',
+        'UnionPay': 0,
+    }
+
+    inv_params = {
+        # 'RelateNumber': 'Tea0001', # 特店自訂編號
+        # 'CustomerID': 'TEA_0000001', # 客戶編號
+        # 'CustomerIdentifier': '53348111', # 統一編號
+        # 'CustomerName': '客戶名稱',
+        # 'CustomerAddr': '客戶地址',
+        # 'CustomerPhone': '0912345678', # 客戶手機號碼
+        # 'CustomerEmail': 'abc@ecpay.com.tw',
+        # 'ClearanceMark': '2', # 通關方式
+        # 'TaxType': '1', # 課稅類別
+        # 'CarruerType': '', # 載具類別
+        # 'CarruerNum': '', # 載具編號
+        # 'Donation': '1', # 捐贈註記
+        # 'LoveCode': '168001', # 捐贈碼
+        # 'Print': '1',
+        # 'InvoiceItemName': '測試商品1|測試商品2',
+        # 'InvoiceItemCount': '2|3',
+        # 'InvoiceItemWord': '個|包',
+        # 'InvoiceItemPrice': '35|10',
+        # 'InvoiceItemTaxType': '1|1',
+        # 'InvoiceRemark': '測試商品1的說明|測試商品2的說明',
+        # 'DelayDay': '0', # 延遲天數
+        # 'InvType': '07', # 字軌類別
+    }
+
+    # 建立實體
+    ecpay_payment_sdk = ECPayPaymentSdk(
+        MerchantID='3002607',
+        HashKey='pwFHCqoQZGmho4w6',
+        HashIV='EkRm7iFT261dpevs'
+    )
+
+    # 合併延伸參數
+    order_params.update(extend_params_1)
+    order_params.update(extend_params_2)
+    order_params.update(extend_params_3)
+    order_params.update(extend_params_4)
+
+    # 合併發票參數
+    order_params.update(inv_params)
+    try:
+        # 產生綠界訂單所需參數
+        final_order_params = ecpay_payment_sdk.create_order(order_params)
+
+        # 產生 html 的 form 格式
+        action_url = 'https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5'  # 測試環境
+        # action_url = 'https://payment.ecpay.com.tw/Cashier/AioCheckOut/V5' # 正式環境
+        html = ecpay_payment_sdk.gen_html_post_form(action_url, final_order_params)
+        
+        return html
+    except Exception as error:
+        print('An exception happened: ' + str(error))
+
+@router.post('/ecpay-payment')
+def ecpay_payment(
+  *,
+  db: Session = Depends(deps.get_db),
+  current_user: models.User = Depends(deps.get_current_active_user),
+  payment_data: schemas.PaymentCreate,
+  lang: str=''
+):
+    print(payment_data)
+    numbers = string.digits
+    randomNumber = ''.join(choice(numbers) for _ in range(4))
+    MerchantTradeNo = datetime.now().strftime("%Y%m%d%H%M%SNO")+randomNumber
+    remark = {'MerchantTradeNo':MerchantTradeNo}
+    remark_string = json.dumps(remark, ensure_ascii=False)
+    payment = crud.payment.create_with_payment_data(db, 
+                                                    obj_in=payment_data,
+                                                    owner_id=current_user.id, 
+                                                    epayment='ecpay',
+                                                    remark=remark_string)
+    order_params = {
+        'MerchantTradeNo': MerchantTradeNo,
+        'StoreID': 'SaaS',
+        'MerchantTradeDate': datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
+        'PaymentType': 'aio',
+        'TotalAmount': payment.amount,
+        'TradeDesc': 'SaaS訂單',
+        'ItemName': f'credit: {payment.item}',
+        'ReturnURL': 'https://cloud.choozmo.com/api/v1/payment/ecpay-result-return',
+        'ChoosePayment': 'ALL',
+        'ClientBackURL': 'https://cloud.choozmo.com/payment',
+        'ItemURL': 'https://cloud.choozmo.com/payment',
+        'Remark': '',
+        'ChooseSubPayment': '',
+        'OrderResultURL': '',
+        'NeedExtraPaidInfo': 'Y',
+        'DeviceSource': '',
+        'IgnorePayment': 'ATM#CVS#BARCODE',
+        'PlatformID': '',
+        'InvoiceMark': 'N',
+        'CustomField1': f'user id: {str(current_user.id)}',
+        'CustomField2': f'pyment id: {str(payment.id)}',
+        'CustomField3': '',
+        'CustomField4': '',
+        'EncryptType': 1,
+        'Language': lang,
+    }
+
+    extend_params_1 = {
+        'ExpireDate': 7,
+        'PaymentInfoURL': 'https://www.ecpay.com.tw/payment_info_url.php',
+        'ClientRedirectURL': '',
+    }
+
+    extend_params_2 = {
+        'StoreExpireDate': 15,
+        'Desc_1': '',
+        'Desc_2': '',
+        'Desc_3': '',
+        'Desc_4': '',
+        'PaymentInfoURL': 'https://www.ecpay.com.tw/payment_info_url.php',
+        'ClientRedirectURL': '',
+    }
 
-@router.post("/ecpay-test-result-return")
+    extend_params_3 = {
+        'BindingCard': 0,
+        'MerchantMemberID': '',
+    }
+
+    extend_params_4 = {
+        'Redeem': 'N',
+        'UnionPay': 0,
+    }
+
+    inv_params = {
+        # 'RelateNumber': 'Tea0001', # 特店自訂編號
+        # 'CustomerID': 'TEA_0000001', # 客戶編號
+        # 'CustomerIdentifier': '53348111', # 統一編號
+        # 'CustomerName': '客戶名稱',
+        # 'CustomerAddr': '客戶地址',
+        # 'CustomerPhone': '0912345678', # 客戶手機號碼
+        # 'CustomerEmail': 'abc@ecpay.com.tw',
+        # 'ClearanceMark': '2', # 通關方式
+        # 'TaxType': '1', # 課稅類別
+        # 'CarruerType': '', # 載具類別
+        # 'CarruerNum': '', # 載具編號
+        # 'Donation': '1', # 捐贈註記
+        # 'LoveCode': '168001', # 捐贈碼
+        # 'Print': '1',
+        # 'InvoiceItemName': '測試商品1|測試商品2',
+        # 'InvoiceItemCount': '2|3',
+        # 'InvoiceItemWord': '個|包',
+        # 'InvoiceItemPrice': '35|10',
+        # 'InvoiceItemTaxType': '1|1',
+        # 'InvoiceRemark': '測試商品1的說明|測試商品2的說明',
+        # 'DelayDay': '0', # 延遲天數
+        # 'InvType': '07', # 字軌類別
+    }
+
+    # 建立實體
+    ecpay_payment_sdk = ECPayPaymentSdk(
+        MerchantID='3226141',
+        HashKey='OhcjDTeXK9PKW9vb',
+        HashIV='AfOmUM06S0bt8KPE'
+    )
+
+    # 合併延伸參數
+    order_params.update(extend_params_1)
+    order_params.update(extend_params_2)
+    order_params.update(extend_params_3)
+    order_params.update(extend_params_4)
+
+    # 合併發票參數
+    order_params.update(inv_params)
+    try:
+        # 產生綠界訂單所需參數
+        final_order_params = ecpay_payment_sdk.create_order(order_params)
+
+        # 產生 html 的 form 格式
+        # action_url = 'https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5'  # 測試環境
+        action_url = 'https://payment.ecpay.com.tw/Cashier/AioCheckOut/V5' # 正式環境
+        html = ecpay_payment_sdk.gen_html_post_form(action_url, final_order_params)
+
+        return html
+    except Exception as error:
+        print('An exception happened: ' + str(error))
+
+
+@router.get('/list-all', response_model=List[schemas.YTViews])
+def get_list(
+  *,
+  db: Session = Depends(deps.get_db),
+):
+  ytviews_list = crud.ytviews.get_multi(db)
+  return ytviews_list
+
+@router.post("/ecpay-result-return")
 def ecpay_return(
     *,
+    db: Session = Depends(deps.get_db),
     MerchantID: Optional[str]=Form(None),
     MerchantTradeNo: Optional[str]=Form(None),
     StoreID: Optional[str]=Form(None),
@@ -100,13 +322,11 @@ CustomField3: {CustomField3}\n\
 CustomField4: {CustomField4}\n\
 CheckMacValue: {CheckMacValue}\
   ")
-  return Response(content='1', status_code=status.HTTP_200_OK)
-
-
-@router.get('/check-payment-list')
-def get_payment_list(
-  *,
-  db: Session = Depends(deps.get_db),
-  current_user: models.User = Depends(deps.get_current_active_user)
-):
-  pass
+  
+  if RtnCode==1:
+    ytviews_id = int(CustomField1)
+    ytviews = crud.ytviews.get(db=db, id=ytviews_id)
+    remark = json.loads(ytviews.remark)
+    remark['TradeNo'] = TradeNo
+    crud.ytviews.update(db, db_obj=ytviews, obj_in={"payment_state":"succeeded", "remark":json.dumps(remark, ensure_ascii=False)})
+  return Response(content='1', status_code=status.HTTP_200_OK)

+ 3 - 0
backend/app/app/api/api_v1/endpoints/videos.py

@@ -86,6 +86,9 @@ def upload_plot(
     Create new video.
     """
     print(title)
+    print(anchor)
+    print(style)
+    print(lang)
     print(upload_file.filename)
     filename = crud.video.generate_file_name(db=db, n=20)
     filepath = str(Path(BACKEND_ZIP_STORAGE).joinpath(filename+".zip"))

+ 58 - 0
backend/app/app/core/video_utils.py

@@ -0,0 +1,58 @@
+import pandas as pd
+from pathlib import Path
+import subprocess
+import shutil
+import os
+import chardet
+import zipfile
+from test_chardet import guess_codec
+from io import BytesIO
+  
+def check_zip(zip_filepath:str):
+  path = Path(zip_filepath)
+  with zipfile.ZipFile(str(path)) as zf:
+    filenames = [x for x in zf.namelist() if not x.endswith('/')]
+    result = guess_codec(filenames)
+    true_filenames = [x.encode('cp437').decode(result) for x in zf.namelist() if not x.endswith('/')]
+    # print(true_filenames)
+    scenarios_files = [(x, i) for i, x in enumerate(true_filenames) if Path(x).suffix in [".xlsx", ".csv"] and not Path(x).name.startswith("._") and Path(x).stem != "style"]
+    # print(scenarios_files)
+    
+    if len(scenarios_files) == 0:
+      raise ValueError("no excel or csv file in zip.")
+    if len(scenarios_files) > 1:
+      raise ValueError("too many excel or csv file in zip.")
+    f = zf.read(filenames[scenarios_files[0][1]])
+    if Path(scenarios_files[0][0]).suffix == ".xlsx":
+      table = pd.read_excel(BytesIO(f), dtype=object)
+    elif Path(scenarios_files[0][0]).suffix == ".csv":
+      table = pd.read_csv(BytesIO(f), dtype=object)
+    table.reset_index(inplace=True)
+    # print(table)
+    
+    stems = [Path(x).stem for x in true_filenames]
+    for i in range(len(table)):
+      # excel 裡的圖檔跟zip裡的檔案要一致
+      if table.loc[i, ['素材']].isna().item():
+        img =  table.loc[i, ['素材']].item()
+        print(img)
+
+        img_files = [x.strip() for x in img.split(',')]
+        for img in img_files:
+          print(img)
+          n = stems.count(img)
+          if n == 0:
+            raise ValueError(f"{img}: no such media file in zip.")
+          elif n > 1:
+            raise ValueError(f'too many same name media files as {img} in zip')
+      
+      # 需要tts文字或音檔
+      if table.loc[i, ['字幕']].isna().item():
+        if table.loc[i, ['音檔']].isna().item():
+          raise ValueError(f'text or voice file is needed at scene {i+1}.')
+        voice_file = table.loc[i, ['音檔']].item()
+        n = stems.count(voice_file)
+        if n != 1:
+          raise ValueError(f"voice file is can't find is zip at scene {i+1}.")
+        
+    

+ 1 - 0
backend/app/app/crud/__init__.py

@@ -3,6 +3,7 @@ from .crud_video import video
 from .crud_article import article
 from .crud_ser_no import serial_number
 from .crud_ytviews import ytviews
+from .crud_payment import payment
 # For a new basic set of CRUD operations 
 # you could just do
 

+ 38 - 0
backend/app/app/crud/crud_payment.py

@@ -0,0 +1,38 @@
+from typing import List
+
+from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
+from sqlalchemy import desc
+from app.crud.base import CRUDBase
+from app.models.payment import Payment
+from app.schemas.payment import PaymentCreate, PaymentUpdate
+
+from app.utils import random_name
+
+class CRUDPayment(CRUDBase[Payment, PaymentCreate, PaymentUpdate]):
+    def create_with_payment_data(
+        self, db: Session, *, obj_in: PaymentCreate, 
+        owner_id:int,
+        epayment:str,
+        remark: str
+    ) -> Payment:
+        
+        obj_in_data = jsonable_encoder(obj_in)
+        db_obj = self.model(**obj_in_data, 
+                            owner_id=owner_id,
+                            epayment=epayment,
+                            remark=remark)
+        print(db_obj)
+        db.add(db_obj)
+        db.commit()
+        db.refresh(db_obj)
+        return db_obj
+      
+    def get_all_desc(self, db: Session) -> List[Payment]:
+        return (
+            db.query(self.model)
+            .order_by(desc(Payment.id))
+            .all()
+        )
+        
+payment = CRUDPayment(Payment)

+ 4 - 3
backend/app/app/models/payment.py

@@ -12,12 +12,13 @@ if TYPE_CHECKING:
 
 class Payment(Base):
   id = Column(Integer, primary_key=True, index=True)
-  order_number = Column(String(30), index=True, nullable=False)
-  datetime = Column(DateTime)
+  created_datetime = Column(DateTime(timezone=True), default=func.now())
+  item = Column(String(20), nullable=False)
   amount = Column(Integer, nullable=False)
-  payment_state = Column(DateTime(timezone=True), default=func.now())
+  payment_state = Column(String(10), default='waiting', nullable=False)
   epayment = Column(String(10), 
                     ForeignKey("epayment.name", ondelete="RESTRICT", onupdate="CASCADE"),
                     default="ecpay")
+  remark = Column(Text)
   owner_id = Column(Integer, ForeignKey("user.id"))
   owner = relationship("User", back_populates="payments")

+ 2 - 1
backend/app/app/schemas/__init__.py

@@ -4,4 +4,5 @@ from .msg import Msg
 from .video import Video, VideoCreate, VideoInDB, VideoUpdate
 from .article import ArticleBase, ArticleCreate, ArticleUpdate
 from .serial_number import SerialNumberBase, SerialNumberCreate, SerialNumberUpdate
-from .ytviews import YTViewsBase, YTViewsCreate, YTViews
+from .ytviews import YTViewsBase, YTViewsCreate, YTViews
+from .payment import PaymentBase, PaymentCreate, PaymentInDB, PaymentUpdate

+ 37 - 0
backend/app/app/schemas/payment.py

@@ -0,0 +1,37 @@
+from typing import Optional
+
+from pydantic import BaseModel, EmailStr, HttpUrl
+
+# Shared properties
+class PaymentBase(BaseModel):
+    item: Optional[str] = None
+    amount: Optional[str] = None
+    
+# Properties to receive on video creation
+class PaymentCreate(PaymentBase):
+    item: str 
+    amount: str 
+    
+# Properties to receive on video update
+class PaymentUpdate(PaymentBase):
+    pass
+
+# Properties shared by models stored in DB
+class PaymentBaseInDBBase(PaymentBase):
+    id: int
+    item: str
+    amount: str
+    payment_state: str
+    
+    class Config:
+        orm_mode = True
+
+
+# Properties to return to client
+class Payment(PaymentBaseInDBBase):
+    pass
+
+
+# Properties properties stored in DB
+class PaymentInDB(PaymentBaseInDBBase):
+    pass