CJYen 3 rokov pred
rodič
commit
f71d2b3d6f

+ 3 - 2
app/api/api_v1/api.py

@@ -1,10 +1,11 @@
 from fastapi import APIRouter
 from fastapi import APIRouter
 
 
-from app.api.api_v1.endpoints import login, users, nft, line, joyso
+from app.api.api_v1.endpoints import login, users, nft, line, joyso, linepay
 
 
 api_router = APIRouter()
 api_router = APIRouter()
 api_router.include_router(login.router, tags=["login"])
 api_router.include_router(login.router, tags=["login"])
 api_router.include_router(users.router, prefix="/user", tags=["user"])
 api_router.include_router(users.router, prefix="/user", tags=["user"])
 api_router.include_router(nft.router, prefix="/nft", tags=["nft"])
 api_router.include_router(nft.router, prefix="/nft", tags=["nft"])
 api_router.include_router(line.router, prefix="/line", tags=["line"])
 api_router.include_router(line.router, prefix="/line", tags=["line"])
-api_router.include_router(joyso.router, prefix="/joyso", tags=["joyso"])
+api_router.include_router(joyso.router, prefix="/joyso", tags=["joyso"])
+api_router.include_router(linepay.router, prefix="/linepay", tags=["linepay"])

+ 0 - 5
app/api/api_v1/endpoints/config.py

@@ -1,5 +0,0 @@
-from linebot import LineBotApi, WebhookHandler
-
-# bot config
-line_bot_api = LineBotApi("SJT7VPT4RMQFLcS27jQBy3FcC24gtDrkcwJWZ5Xzqesr5T78LOKudHEJzt0k3b2S7n4KPwf27J7DVz2c8NQ4plSaaQylEeB1cYrfejaE/RPG/lCIQBYe4iBTzo26s4i2PcmT89837per/lTyvhVIKAdB04t89/1O/w1cDnyilFU=")
-handler = WebhookHandler("411ae3ef7e766739ed2c2c27b249d010")

+ 81 - 46
app/api/api_v1/endpoints/line.py

@@ -1,54 +1,48 @@
-import pymysql
-pymysql.install_as_MySQLdb()
-import uvicorn
 import fastapi
 import fastapi
-from fastapi.middleware.cors import CORSMiddleware
-from fastapi.responses import HTMLResponse
-from linebot import LineBotApi, WebhookHandler
 from linebot.models import (
 from linebot.models import (
-    MessageEvent, TextMessage, TextSendMessage, FollowEvent, TemplateSendMessage, ButtonsTemplate, URITemplateAction,
+    MessageEvent, TextMessage, TextSendMessage, FollowEvent,
+    TemplateSendMessage, ButtonsTemplate, URITemplateAction,
 )
 )
 import dataset
 import dataset
 import requests
 import requests
 import json
 import json
 import qrcode
 import qrcode
-# from PIL import Image
-# import base64, io
 from random import randrange
 from random import randrange
 from app.schemas import line
 from app.schemas import line
+from app.core.config import settings
 import datetime as dt
 import datetime as dt
 from fastapi import APIRouter
 from fastapi import APIRouter
 
 
-router = APIRouter()
 
 
+router = APIRouter()
 
 
-# bot config
-line_bot_api = LineBotApi("SJT7VPT4RMQFLcS27jQBy3FcC24gtDrkcwJWZ5Xzqesr5T78LOKudHEJzt0k3b2S7n4KPwf27J7DVz2c8NQ4plSaaQylEeB1cYrfejaE/RPG/lCIQBYe4iBTzo26s4i2PcmT89837per/lTyvhVIKAdB04t89/1O/w1cDnyilFU=")
-handler = WebhookHandler("411ae3ef7e766739ed2c2c27b249d010")
 
 
 # callback event
 # callback event
 @router.post("/callback")
 @router.post("/callback")
 async def callback(request: fastapi.Request):
 async def callback(request: fastapi.Request):
     signature = request.headers['X-Line-Signature']
     signature = request.headers['X-Line-Signature']
     body = await request.body()
     body = await request.body()
-    handler.handle(body.decode('utf-8'), signature)
+    settings.handler.handle(body.decode('utf-8'), signature)
     return 'OK'
     return 'OK'
 
 
+
 # follow event
 # follow event
-@handler.add(FollowEvent)
+@settings.handler.add(FollowEvent)
 def handle_follow(event):
 def handle_follow(event):
     # get user id when follow
     # get user id when follow
     real_user_id = event.source.user_id
     real_user_id = event.source.user_id
 
 
     # db connect and search
     # db connect and search
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table = db['users']
     table = db['users']
     result1 = table.find_one(userid=real_user_id)
     result1 = table.find_one(userid=real_user_id)
 
 
     # 都存在db的話
     # 都存在db的話
     if result1:
     if result1:
         db.close()
         db.close()
-        line_bot_api.reply_message(
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TextSendMessage(text='很高興再見到您!'))
             TextSendMessage(text='很高興再見到您!'))
 
 
@@ -56,7 +50,10 @@ def handle_follow(event):
     else:
     else:
         # create user account api
         # create user account api
         url = 'https://nft-api-staging.joyso.io/api/v1/accounts'
         url = 'https://nft-api-staging.joyso.io/api/v1/accounts'
-        headers = {'Authorization': 'Basic bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'}
+        headers = {
+            'Authorization':
+            'Basic bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'
+        }
 
 
         # setup for temp use (unique id)
         # setup for temp use (unique id)
         rand_num = str(randrange(99999))
         rand_num = str(randrange(99999))
@@ -77,8 +74,9 @@ def handle_follow(event):
         qr.add_data(user_address)
         qr.add_data(user_address)
         qr.make(fit=True)
         qr.make(fit=True)
         img_qr = qr.make_image(fill='black', back_color='white')
         img_qr = qr.make_image(fill='black', back_color='white')
-        filename = "/var/www/ArkCard-Linebot/ArkCard-web/qrcode/" + real_user_id + '.png'
-        img_save = img_qr.save(filename)
+        filename = "/var/www/ArkCard-Linebot/ArkCard-web/qrcode/" + \
+                   real_user_id + '.png'
+        img_qr.save(filename)
 
 
         # add to db
         # add to db
         data = dict(userid=real_user_id, useraddress=user_address)
         data = dict(userid=real_user_id, useraddress=user_address)
@@ -86,12 +84,13 @@ def handle_follow(event):
 
 
         db.close()
         db.close()
 
 
-        line_bot_api.reply_message(
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TextSendMessage(text='歡迎加入好友'))
             TextSendMessage(text='歡迎加入好友'))
 
 
+
 # message handler
 # message handler
-@handler.add(MessageEvent, message=TextMessage)
+@settings.handler.add(MessageEvent, message=TextMessage)
 def message(event):
 def message(event):
     if '我要發送' in event.message.text:
     if '我要發送' in event.message.text:
         button_template_message = ButtonsTemplate(
         button_template_message = ButtonsTemplate(
@@ -100,22 +99,24 @@ def message(event):
             actions=[
             actions=[
                 URITemplateAction(
                 URITemplateAction(
                     label='打開發送頁',
                     label='打開發送頁',
-                    uri='https://ark.cards/collect.html?' + event.source.user_id),])
-        line_bot_api.reply_message(
+                    uri='https://ark.cards/collect.html?'
+                        + event.source.user_id)])
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TemplateSendMessage(
             TemplateSendMessage(
                 alt_text="Receive",
                 alt_text="Receive",
                 template=button_template_message))
                 template=button_template_message))
 
 
-    elif '我要接收' in  event.message.text:
+    elif '我要接收' in event.message.text:
         button_template_message = ButtonsTemplate(
         button_template_message = ButtonsTemplate(
             title=' ',
             title=' ',
             text='點擊並打開接收頁面,即可分享接收地址給對方!',
             text='點擊並打開接收頁面,即可分享接收地址給對方!',
             actions=[
             actions=[
                 URITemplateAction(
                 URITemplateAction(
                     label='打開接收頁',
                     label='打開接收頁',
-                    uri='https://ark.cards/qr-code.html?' + event.source.user_id),])
-        line_bot_api.reply_message(
+                    uri='https://ark.cards/qr-code.html?' +
+                        event.source.user_id)])
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TemplateSendMessage(
             TemplateSendMessage(
                 alt_text="Receive",
                 alt_text="Receive",
@@ -128,8 +129,9 @@ def message(event):
             actions=[
             actions=[
                 URITemplateAction(
                 URITemplateAction(
                     label='打開NFT商品頁',
                     label='打開NFT商品頁',
-                    uri='https://ark.cards/shop.html?' + event.source.user_id),])
-        line_bot_api.reply_message(
+                    uri='https://ark.cards/shop.html?' +
+                        event.source.user_id)])
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TemplateSendMessage(
             TemplateSendMessage(
                 alt_text="Receive",
                 alt_text="Receive",
@@ -142,8 +144,9 @@ def message(event):
             actions=[
             actions=[
                 URITemplateAction(
                 URITemplateAction(
                     label='打開收藏頁',
                     label='打開收藏頁',
-                    uri='https://ark.cards/collect.html?' + event.source.user_id), ])
-        line_bot_api.reply_message(
+                    uri='https://ark.cards/collect.html?' +
+                        event.source.user_id), ])
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TemplateSendMessage(
             TemplateSendMessage(
                 alt_text="Receive",
                 alt_text="Receive",
@@ -155,22 +158,28 @@ def message(event):
             actions=[
             actions=[
                 URITemplateAction(
                 URITemplateAction(
                     label='ArkCard的官網',
                     label='ArkCard的官網',
-                    uri='https://ark.cards'),])
-        line_bot_api.reply_message(
+                    uri='https://ark.cards')])
+        settings.line_bot_api.reply_message(
             event.reply_token,
             event.reply_token,
             TemplateSendMessage(
             TemplateSendMessage(
                 alt_text="Receive",
                 alt_text="Receive",
                 template=button_template_message))
                 template=button_template_message))
 
 
+
 @router.post("/push/")
 @router.post("/push/")
 def push_text(user, message):
 def push_text(user, message):
-    line_bot_api.push_message(user, TextSendMessage(text=message))
+    settings.line_bot_api.push_message(
+        user, TextSendMessage(text=message)
+    )
+
 
 
 # nft collection api
 # nft collection api
 @router.get("/collection/{userid}")
 @router.get("/collection/{userid}")
 def collection(userid):
 def collection(userid):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table3 = db['nftdrops']
     table3 = db['nftdrops']
     table2 = db['nft']
     table2 = db['nft']
     nftdrops = {}
     nftdrops = {}
@@ -199,11 +208,14 @@ def collection(userid):
         return nfts_all
         return nfts_all
         db.close()
         db.close()
 
 
+
 # receive handler
 # receive handler
 @router.get("/receive/{userid}")
 @router.get("/receive/{userid}")
 def receive(userid):
 def receive(userid):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table = db['users']
     table = db['users']
 
 
     table.find_one(userid=userid)
     table.find_one(userid=userid)
@@ -212,14 +224,19 @@ def receive(userid):
         return "ERROR: User Not Found"
         return "ERROR: User Not Found"
     else:
     else:
         result = table.find_one(userid=userid)
         result = table.find_one(userid=userid)
-        return {"userid": result['userid'], "useraddress": result['useraddress']}
+        return {
+            "userid": result['userid'], "useraddress": result['useraddress']
+        }
     db.close()
     db.close()
 
 
+
 # send handler
 # send handler
 @router.post("/send")
 @router.post("/send")
-async def receive(userModel : line.TransactionNft):
+async def send(userModel: line.TransactionNft):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table = db['users']
     table = db['users']
     table2 = db['nft']
     table2 = db['nft']
     table3 = db['nftdrops']
     table3 = db['nftdrops']
@@ -274,13 +291,17 @@ async def receive(userModel : line.TransactionNft):
         return {'msg': 'nft not found'}
         return {'msg': 'nft not found'}
     return {'msg': 'OK'}
     return {'msg': 'OK'}
 
 
+
 # shop handler
 # shop handler
 @router.get("/shop/{userid}")
 @router.get("/shop/{userid}")
 def shop(userid):
 def shop(userid):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
 
 
-    sql = 'SELECT DISTINCT(title), id, imgurl, userid FROM arkcard.nft  WHERE id<1001 and userid IS NULL GROUP BY title LIMIT 5'
+    sql = 'SELECT DISTINCT(title), id, imgurl, userid FROM arkcard.nft  ' \
+          'WHERE id<1001 and userid IS NULL GROUP BY title LIMIT 5'
     result = db.query(sql)
     result = db.query(sql)
     rows = {}
     rows = {}
     i = 0
     i = 0
@@ -290,10 +311,13 @@ def shop(userid):
     return rows
     return rows
     db.close()
     db.close()
 
 
+
 @router.post("/buy")
 @router.post("/buy")
 async def buy(userModel: line.BuyNft):
 async def buy(userModel: line.BuyNft):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table2 = db['nft']
     table2 = db['nft']
 
 
     # input
     # input
@@ -320,10 +344,13 @@ async def buy(userModel: line.BuyNft):
         db.close()
         db.close()
     return "您已購買成功!"
     return "您已購買成功!"
 
 
+
 @router.post("/event")
 @router.post("/event")
-async def nftdrops(userModel : line.NftDrops):
+async def nftdrops(userModel: line.NftDrops):
     # db connect
     # db connect
-    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
+    db = dataset.connect(
+        'mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4'
+    )
     table3 = db['nftdrops']
     table3 = db['nftdrops']
 
 
     # input對應
     # input對應
@@ -337,8 +364,16 @@ async def nftdrops(userModel : line.NftDrops):
     # 如果userid不在db, 寫入到一個空值nft
     # 如果userid不在db, 寫入到一個空值nft
     if not table3.find_one(userid=userid):
     if not table3.find_one(userid=userid):
         # 新增資料
         # 新增資料
-        table3.insert(dict(eventid=eventid, nftid=nftid, userid=userid, email=email, time=now))
-        table3.insert(dict(eventid=eventid, nftid=nftid2, userid=userid, email=email, time=now))
+        table3.insert(
+            dict(
+                eventid=eventid, nftid=nftid,
+                userid=userid, email=email, time=now
+            ))
+        table3.insert(
+            dict(
+                eventid=eventid, nftid=nftid2,
+                userid=userid, email=email, time=now
+            ))
         db.close()
         db.close()
         # push訊息
         # push訊息
         message = "已為您登記活動!"
         message = "已為您登記活動!"

+ 96 - 0
app/api/api_v1/endpoints/linepay.py

@@ -0,0 +1,96 @@
+from fastapi import APIRouter
+from linepay import LinePayApi
+from fastapi.templating import Jinja2Templates
+
+# template
+templates = Jinja2Templates(directory="templates")
+
+router = APIRouter()
+
+LINE_PAY_CHANNEL_ID = "1656387996"
+SECRET_KEY = \
+    "df2f77bd544240801a048bd4293afd8eeb7fff3cb7050e42c791db4b83ebadcd"
+
+HOST_NAME = "https://f516-42-74-70-95.ngrok.io"
+# Line Pay Config
+LINE_PAY_CHANNEL_ID = LINE_PAY_CHANNEL_ID
+LINE_PAY_CHANNEL_SECRET = SECRET_KEY
+# LINE_PAY_REQEST_BASE_URL = "https://{}".format(HOST_NAME)
+LINE_PAY_REQEST_BASE_URL = "https://f516-42-74-70-95.ngrok.io"
+line = LinePayApi(
+    LINE_PAY_CHANNEL_ID, LINE_PAY_CHANNEL_SECRET, is_sandbox=True
+)
+
+# CACHE
+CACHE = {}
+
+
+# Request
+@router.post('/request')
+async def pay_request():
+    order_id = "20211227011"
+    amount = "1"
+    currency = "TWD"
+
+    request_options = {
+        "amount": amount,
+        "currency": currency,
+        "orderId": order_id,
+        "packages": [
+            {
+                "id": "NFT",
+                "amount": 1,
+                "products": [
+                    {
+                        "name": "NFT Box 1",
+                        "quantity": 1,
+                        "price": 1,
+                        "imageUrl": "https://i.imgur.com/3rFHRVz.jpg"
+                    }
+                ]
+            }
+
+        ],
+        "redirectUrls": {
+            "confirmUrl": LINE_PAY_REQEST_BASE_URL + "/confirm/"
+            # "cancelUrl": LINE_PAY_REQEST_BASE_URL + "/cancel/"
+        }
+    }
+    response = line.request(request_options)
+    transaction_id = int(response.get("info", {}).get("transactionId", 0))
+    check_result = line.check_payment_status(transaction_id)
+    response["transaction_id"] = transaction_id
+    response["paymentStatusCheckReturnCode"] = check_result.get(
+        "returnCode", None
+    )
+    response["paymentStatusCheckReturnMessage"] = check_result.get(
+        "returnMessage", None
+    )
+    # return response
+    return response
+
+
+# Confirm
+@router.get('/confirm/')
+async def pay_confirm(
+    transactionId: int = "transactionId",
+):
+    CACHE["transaction_id"] = transactionId
+    response = line.confirm(
+        transactionId, float(CACHE.get("amount", 0)),
+        CACHE.get("currency", "TWD"))
+    check_result = line.check_payment_status(transactionId)
+    payment_details = line.payment_details(transaction_id=transactionId)
+    response["transaction_id"] = transactionId
+    response["paymentStatusCheckReturnCode"] = check_result.get(
+        "returnCode", None
+    )
+    response["paymentStatusCheckReturnMessage"] = check_result.get(
+        "returnMessage", None
+    )
+    response["payment_details"] = payment_details
+    if(response["paymentStatusCheckReturnCode"] == '0123'):
+        # return response
+        return "confirm.html", {"request": response}
+    else:
+        return "Not found"

+ 9 - 1
app/core/config.py

@@ -2,7 +2,7 @@ import secrets
 from typing import Any, Dict, List, Optional, Union
 from typing import Any, Dict, List, Optional, Union
 
 
 from pydantic import AnyHttpUrl, BaseSettings, EmailStr, HttpUrl, PostgresDsn, validator
 from pydantic import AnyHttpUrl, BaseSettings, EmailStr, HttpUrl, PostgresDsn, validator
-
+from linebot import LineBotApi, WebhookHandler
 
 
 class Settings(BaseSettings):
 class Settings(BaseSettings):
     API_V1_STR: str = "/api/v1"
     API_V1_STR: str = "/api/v1"
@@ -29,6 +29,14 @@ class Settings(BaseSettings):
     IMG_PATH: str = "/var/www/ArkCard-Linebot/ArkCard-web/img/nft/"
     IMG_PATH: str = "/var/www/ArkCard-Linebot/ArkCard-web/img/nft/"
     IMG_HOST: AnyHttpUrl = "https://ark.cards/img/nft/"
     IMG_HOST: AnyHttpUrl = "https://ark.cards/img/nft/"
 
 
+    # # bot config
+    line_bot_api = LineBotApi(
+        "SJT7VPT4RMQFLcS27jQBy3FcC24gtDrkcwJWZ5Xzqesr5T78LOKudHEJzt0k3b2S7"
+        "n4KPwf27J7DVz2c8NQ4plSaaQylEeB1cYrfejaE/RPG/lCIQBYe4iBTzo26s4i2Pcm"
+        "T89837per/lTyvhVIKAdB04t89/1O/w1cDnyilFU=")
+    handler = WebhookHandler("411ae3ef7e766739ed2c2c27b249d010")
+
+
     @validator("SENTRY_DSN", pre=True)
     @validator("SENTRY_DSN", pre=True)
     def sentry_dsn_can_be_blank(cls, v: str) -> Optional[str]:
     def sentry_dsn_can_be_blank(cls, v: str) -> Optional[str]:
         if len(v) == 0:
         if len(v) == 0: