Skip to content

Commit 37e5367

Browse files
authored
add source code for 支付系统
1 parent 92751a2 commit 37e5367

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

Challenges/Web/支付系统/server.py

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import os
2+
import uuid
3+
from quart import Quart, render_template, redirect, jsonify, request, session
4+
from hashlib import pbkdf2_hmac
5+
from enum import IntEnum
6+
from tortoise import fields
7+
from tortoise.models import Model
8+
from tortoise.contrib.quart import register_tortoise
9+
from httpx import AsyncClient
10+
11+
app = Quart(__name__)
12+
app.secret_key = os.urandom(16)
13+
14+
15+
class TransactionStatus(IntEnum):
16+
SUCCESS = 0
17+
PENDING = 1
18+
FAILED = 2
19+
TIMEOUT = 3
20+
21+
22+
class Transaction(Model):
23+
id = fields.IntField(pk=True)
24+
user = fields.UUIDField()
25+
amount = fields.IntField()
26+
status = fields.IntEnumField(TransactionStatus)
27+
desc = fields.TextField()
28+
hash = fields.CharField(64, null=True)
29+
30+
def __init__(self, **kwargs):
31+
super().__init__()
32+
for k, v in kwargs.items():
33+
self.__setattr__(k, v)
34+
35+
36+
async def do_callback(transaction: Transaction):
37+
async with AsyncClient() as ses:
38+
transaction.status = int(TransactionStatus.FAILED)
39+
data = (
40+
f'{transaction.id}'
41+
f'{transaction.user}'
42+
f'{transaction.amount}'
43+
f'{transaction.status}'
44+
f'{transaction.desc}'
45+
).encode()
46+
await ses.post(f'http://localhost:8000/callback', data={
47+
'id': transaction.id,
48+
'user': transaction.user,
49+
'amount': transaction.amount,
50+
'desc': transaction.desc,
51+
'status': transaction.status,
52+
'hash': pbkdf2_hmac('sha256', data, app.secret_key, 2**20).hex()
53+
})
54+
55+
56+
@app.before_request
57+
async def create_session():
58+
if 'uid' not in session:
59+
session['uid'] = str(uuid.uuid4())
60+
session['balance'] = 0
61+
for tr in await Transaction.filter(user=session['uid']).all():
62+
if tr.status == TransactionStatus.SUCCESS:
63+
session['balance'] += tr.amount
64+
65+
66+
@app.route('/pay')
67+
async def pay():
68+
transaction = await Transaction.create(
69+
amount=request.args.get('amount'),
70+
desc=request.args.get('desc'),
71+
status=TransactionStatus.PENDING,
72+
user=uuid.UUID(session.get('uid'))
73+
)
74+
app.add_background_task(do_callback, transaction)
75+
return redirect(f'/transaction?id={transaction.id}')
76+
77+
78+
@app.route('/callback', methods=['POST'])
79+
async def callback():
80+
form = dict(await request.form)
81+
data = (
82+
f'{form.get("id")}'
83+
f'{form.get("user")}'
84+
f'{form.get("amount")}'
85+
f'{form.get("status")}'
86+
f'{form.get("desc")}'
87+
).encode()
88+
k = pbkdf2_hmac('sha256', data, app.secret_key, 2**20).hex()
89+
tr = await Transaction.get(id=int(form.pop('id')))
90+
if k != form.get("hash"):
91+
return '403'
92+
form['status'] = TransactionStatus(int(form.pop('status')))
93+
tr.update_from_dict(form)
94+
await tr.save()
95+
return 'ok'
96+
97+
98+
@app.route('/transaction')
99+
async def transaction():
100+
if 'id' not in request.args:
101+
return '404'
102+
transaction = await Transaction.get(id=request.args.get('id'))
103+
return await render_template('receipt.html', transaction=transaction)
104+
105+
106+
@app.route('/flag')
107+
async def flag():
108+
return await render_template(
109+
'flag.html',
110+
balance=session['balance'],
111+
flag=os.getenv('FLAG'),
112+
)
113+
114+
115+
@app.route('/')
116+
@app.route('/index.html')
117+
async def index():
118+
with open(__file__) as f:
119+
return await render_template('source-highlight.html', code=f.read())
120+
121+
122+
register_tortoise(
123+
app,
124+
db_url="sqlite://./data.db",
125+
modules={"models": [__name__]},
126+
generate_schemas=True,
127+
)
128+
129+
if __name__ == '__main__':
130+
app.run()

0 commit comments

Comments
 (0)