main.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import uuid
  2. import os
  3. from datetime import datetime, timedelta
  4. from typing import Optional
  5. from fastapi import FastAPI, Depends, HTTPException, status
  6. from fastapi.templating import Jinja2Templates
  7. # from fastapi_jwt_auth import AuthJWT
  8. from dotenv import load_dotenv
  9. from os.path import join, dirname
  10. from linepay import LinePayApi
  11. from starlette.responses import HTMLResponse
  12. from sqlalchemy.orm import Session
  13. from fastapi.encoders import jsonable_encoder
  14. from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
  15. from sql.database import get_db_session
  16. from sql.crud import create_payment, get_user
  17. from sql.models import Payment, User
  18. from sql.schemas import PaymentSchema
  19. from sql.schemas import UserSchema
  20. from jose import JWTError, jwt
  21. from passlib.context import CryptContext
  22. # TBD load_env
  23. SECRET_KEY = "df2f77bd544240801a048bd4293afd8eeb7fff3cb7050e42c791db4b83ebadcd"
  24. ALGORITHM = "HS256"
  25. ACCESS_TOKEN_EXPIRE_DAYS = 5
  26. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  27. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  28. def verify_password(plain_password, hashed_password):
  29. return pwd_context.verify(plain_password, hashed_password)
  30. def get_password_hash(password):
  31. return pwd_context.hash(password)
  32. def authenticate_user(db_sesion: Session, username: str, password: str):
  33. user = get_user(db_sesion, username)
  34. if not user:
  35. return False
  36. if not verify_password(password, user.password):
  37. return False
  38. return user
  39. def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
  40. to_encode = data.copy()
  41. if expires_delta:
  42. expire = datetime.utcnow() + expires_delta
  43. else:
  44. expire = datetime.utcnow() + timedelta(minutes=15)
  45. to_encode.update({"exp": expire})
  46. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  47. return encoded_jwt
  48. def get_current_user(db_sesion: Session, token: str = Depends(oauth2_scheme)):
  49. credentials_exception = HTTPException(
  50. status_code=status.HTTP_401_UNAUTHORIZED,
  51. detail="Could not validate credentials",
  52. headers={"WWW-Authenticate": "Bearer"},
  53. )
  54. try:
  55. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  56. username: str = payload.get("sub")
  57. if username is None:
  58. raise credentials_exception
  59. except JWTError:
  60. raise credentials_exception
  61. user = get_user(db_sesion, username=username)
  62. if user is None:
  63. raise credentials_exception
  64. return user
  65. # dotenv
  66. # dotenv_path = join(dirname(__file__),'./env/.env')
  67. dotenv_path = join(dirname(__file__),'./env/test.env') ## sandbox
  68. load_dotenv(dotenv_path)
  69. # logger (TBD)
  70. # template
  71. templates = Jinja2Templates(directory="templates")
  72. # Line Pay Config
  73. LINE_PAY_CHANNEL_ID = os.environ.get("LINE_PAY_CHANNEL_ID")
  74. LINE_PAY_CHANNEL_SECRET = os.environ.get("LINE_PAY_CHANNEL_SECRET")
  75. LINE_PAY_REQEST_BASE_URL = "https://{}".format(os.environ.get("HOST_NAME"))
  76. # line = LinePayApi(LINE_PAY_CHANNEL_ID, LINE_PAY_CHANNEL_SECRET, is_sandbox=False)
  77. line = LinePayApi(LINE_PAY_CHANNEL_ID, LINE_PAY_CHANNEL_SECRET, is_sandbox=True)
  78. # CACHE
  79. CACHE = {}
  80. # Fastapi
  81. app = FastAPI()
  82. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  83. @app.get('/')
  84. def hello():
  85. return templates.TemplateResponse("login.html",{"request": {}})
  86. @app.post('/')
  87. def hello(form_data: OAuth2PasswordRequestForm = Depends(), db_sesion: Session = Depends(get_db_session)):
  88. user = authenticate_user(db_sesion, form_data.username, form_data.password)
  89. if not user:
  90. raise HTTPException(
  91. status_code=status.HTTP_401_UNAUTHORIZED,
  92. detail="Incorrect username or password",
  93. headers={"WWW-Authenticate": "Bearer"},
  94. )
  95. access_token_expires = timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
  96. access_token = create_access_token(
  97. data={"sub": user.username}, expires_delta=access_token_expires
  98. )
  99. index = {}
  100. index["product"] = "早鳥方案"
  101. index["amount"] = 1200
  102. index["url"] = "/request"
  103. index["name"] = user.username
  104. # return {"product" : ,"amount" : 1200}
  105. # return templates.TemplateResponse("index.html", {"request":index, "access_token": access_token, "token_type": "bearer"})
  106. return {"access_token": access_token, "token_type": "bearer"}
  107. ## Request
  108. @app.post('/request', response_class=HTMLResponse)
  109. async def pay_request(token: str = Depends(oauth2_scheme), db_sesion: Session = Depends(get_db_session)):
  110. user = get_current_user(db_sesion, token)
  111. order_id = str(uuid.uuid4())
  112. amount = 1200
  113. currency = "TWD"
  114. CACHE["orderid"] = order_id
  115. CACHE["amount"] = amount
  116. CACHE["currency"] = currency
  117. CACHE["userid"] = user.id
  118. request_options ={
  119. "amount" : amount,
  120. "currency" : currency,
  121. "orderId" : order_id,
  122. "packages" : [
  123. {
  124. "id" : "早鳥方案",
  125. "amount" : 1200,
  126. "products" :[
  127. {
  128. # "id" : "Id_早鳥方案",
  129. "name" : "早鳥方案",
  130. "quantity" : 1,
  131. "price" : 1200,
  132. "imageUrl" : "https://kb.rspca.org.au/wp-content/uploads/2018/11/golder-retriever-puppy.jpeg"
  133. }
  134. ]
  135. }
  136. ],
  137. "redirectUrls" : {
  138. "confirmUrl" : LINE_PAY_REQEST_BASE_URL + "/confirm/",
  139. "cancelUrl" : LINE_PAY_REQEST_BASE_URL + "/cancel/"
  140. }
  141. }
  142. response = line.request(request_options)
  143. transaction_id = int(response.get("info",{}).get("transactionId",0))
  144. check_result = line.check_payment_status(transaction_id)
  145. response["transaction_id"] = transaction_id
  146. response["paymentStatusCheckReturnCode"] = check_result.get("returnCode", None)
  147. response["paymentStatusCheckReturnMessage"] = check_result.get("returnMessage", None)
  148. response["full_name"] = user.username
  149. return templates.TemplateResponse("request.html", {"request":response})
  150. # return response
  151. ## Confirm
  152. @app.get('/confirm/')
  153. async def pay_confirm(transactionId: int, orderId: Optional[str] = None, db_sesion: Session = Depends(get_db_session)):
  154. CACHE["transaction_id"] = transactionId
  155. response = line.confirm(transactionId,float(CACHE.get("amount",0)),CACHE.get("currency","TWD"))
  156. check_result = line.check_payment_status(transactionId)
  157. payment_details = line.payment_details(transaction_id=transactionId)
  158. response["transaction_id"] = transactionId
  159. response["paymentStatusCheckReturnCode"] = check_result.get("returnCode", None)
  160. response["paymentStatusCheckReturnMessage"] = check_result.get("returnMessage",None)
  161. response["payment_details"] = payment_details
  162. if(response["paymentStatusCheckReturnCode"] == '0123'):
  163. orderin = {}
  164. orderin["OrderId"] = CACHE["orderid"]
  165. orderin["UserId"] = CACHE["userid"]
  166. orderin["TransactionId"] = CACHE["transaction_id"]
  167. orderin["Source"] = "linepay"
  168. orderin["UpdateTime"] = datetime.now()
  169. create_payment(db_sesion, order_in= orderin)
  170. return templates.TemplateResponse("confirm.html", {"request":response})
  171. # return {"transactionId" : str(transactionId), "orderId" : orderId}
  172. ## Capture
  173. ## Refund
  174. ## Payment Details API
  175. @app.get('/payments')
  176. async def pay_payments(orderId : str):
  177. payment_details = line.payment_details(order_id=orderId)
  178. return payment_details
  179. ## Pay Preapproved API