chore: initialize tuhui repository

This commit is contained in:
Codex
2026-03-08 19:28:32 +08:00
commit ee10c46aae
189 changed files with 17754 additions and 0 deletions

View File

@@ -0,0 +1,444 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
易收米支付SDK Web服务演示
这个演示展示了如何在Web应用中集成易收米支付
1. 创建支付页面
2. 处理支付回调
3. 查询订单状态
运行前请安装依赖:
pip install fastapi uvicorn jinja2 python-multipart
运行方式:
python web_demo.py
然后访问: http://localhost:8000
"""
import json
import time
import asyncio
from fastapi import FastAPI, Request, Form, Response
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from pay import create_payment
from query import query_order
from notify import PaymentNotify
# 创建FastAPI应用
app = FastAPI(title="易收米支付演示", description="易收米支付SDK Web演示")
# 配置信息 - 请替换为你的真实配置
APPID = 'YSMcd16b45d'
APPSECRET = '899850e778e8d2b53e4c4a4e88695688'
# 回调地址 - 实际使用时应该是你的域名
NOTIFY_URL = "http://localhost:8000/notify"
NOPAY_URL = "http://localhost:8000/cancel"
CALLBACK_URL = "http://localhost:8000/success"
# 模板引擎
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
"""首页 - 支付表单"""
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>易收米支付演示</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input, select, textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #0056b3; }
.result { margin: 20px 0; padding: 15px; background: #f8f9fa; border-radius: 4px; }
.success { border-left: 4px solid #28a745; }
.error { border-left: 4px solid #dc3545; }
</style>
</head>
<body>
<h1>🚀 易收米支付演示</h1>
<h2>创建支付订单</h2>
<form action="/create_payment" method="post">
<div class="form-group">
<label>商品名称:</label>
<input type="text" name="description" value="演示商品 - 快充数据线" required>
</div>
<div class="form-group">
<label>支付金额(元):</label>
<input type="number" name="amount" value="1.00" step="0.01" min="0.01" required>
</div>
<div class="form-group">
<label>支付类型:</label>
<select name="pay_type">
<option value="1">微信内支付</option>
<option value="2">微信扫码支付</option>
<option value="3">微信H5支付</option>
<option value="11">支付宝H5支付</option>
</select>
</div>
<button type="submit">💳 创建支付</button>
</form>
<h2>查询订单状态</h2>
<form action="/query_order" method="post">
<div class="form-group">
<label>订单号:</label>
<input type="text" name="order_id" placeholder="输入订单号" required>
</div>
<button type="submit">🔍 查询订单</button>
</form>
<h2>📚 接口说明</h2>
<ul>
<li><strong>支付回调:</strong> POST /notify</li>
<li><strong>支付成功:</strong> GET /success</li>
<li><strong>支付取消:</strong> GET /cancel</li>
<li><strong>订单列表:</strong> GET /orders</li>
</ul>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.post("/create_payment")
async def create_payment_endpoint(
description: str = Form(...),
amount: float = Form(...),
pay_type: int = Form(...)
):
"""创建支付订单"""
try:
# 生成订单号
order_id = f"demo_{int(time.time())}"
# 转换金额为分
amount_cents = int(amount * 100)
# 创建支付
pay_url = await create_payment(
appid=APPID,
appsecret=APPSECRET,
order_id=order_id,
description=description,
amount=amount_cents,
notify_url=NOTIFY_URL,
nopay_url=NOPAY_URL,
callback_url=CALLBACK_URL,
pay_type=pay_type
)
if pay_url:
# 保存订单信息(实际应用中应该保存到数据库)
order_info = {
'order_id': order_id,
'description': description,
'amount': amount,
'pay_type': pay_type,
'pay_url': pay_url,
'status': 'created',
'created_at': time.time()
}
result_html = f"""
<!DOCTYPE html>
<html>
<head>
<title>支付订单创建成功</title>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}
.success {{ background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 15px; border-radius: 4px; }}
.info {{ background: #f8f9fa; border: 1px solid #dee2e6; padding: 15px; border-radius: 4px; margin: 15px 0; }}
.button {{ display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px; }}
.button:hover {{ background: #0056b3; }}
</style>
</head>
<body>
<h1>✅ 支付订单创建成功</h1>
<div class="success">
支付订单已成功创建!请点击下方链接进行支付。
</div>
<div class="info">
<strong>订单信息:</strong><br>
订单号: {order_id}<br>
商品: {description}<br>
金额: ¥{amount}<br>
支付类型: {pay_type}
</div>
<a href="{pay_url}" class="button" target="_blank">🚀 立即支付</a>
<a href="/" class="button">🏠 返回首页</a>
<a href="/query_order_page?order_id={order_id}" class="button">🔍 查询订单</a>
</body>
</html>
"""
return HTMLResponse(content=result_html)
else:
return JSONResponse(
content={"error": "创建支付失败"},
status_code=400
)
except Exception as e:
return JSONResponse(
content={"error": f"创建支付时出错: {str(e)}"},
status_code=500
)
@app.post("/query_order")
async def query_order_endpoint(order_id: str = Form(...)):
"""查询订单状态"""
try:
# 查询订单
order_info = await query_order(
appid=APPID,
mch_orderid=order_id
)
if order_info:
status_map = {
'SUCCESS': '✅ 支付成功',
'REFUND': '🔄 转入退款',
'NOTPAY': '⏳ 未支付',
'CLOSED': '❌ 已关闭',
}
state = order_info.get('state', 'UNKNOWN')
status_text = status_map.get(state, f'未知状态({state})')
result_html = f"""
<!DOCTYPE html>
<html>
<head>
<title>订单查询结果</title>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}
.info {{ background: #f8f9fa; border: 1px solid #dee2e6; padding: 15px; border-radius: 4px; margin: 15px 0; }}
.success {{ color: #155724; }}
.pending {{ color: #856404; }}
.failed {{ color: #721c24; }}
pre {{ background: #f8f9fa; padding: 10px; border-radius: 4px; overflow-x: auto; }}
.button {{ display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px; }}
</style>
</head>
<body>
<h1>🔍 订单查询结果</h1>
<div class="info">
<strong>订单号:</strong> {order_id}<br>
<strong>状态:</strong> <span class="{'success' if state == 'SUCCESS' else 'pending' if state == 'NOTPAY' else 'failed'}">{status_text}</span>
</div>
<h3>📋 详细信息:</h3>
<pre>{json.dumps(order_info, ensure_ascii=False, indent=2)}</pre>
<a href="/" class="button">🏠 返回首页</a>
</body>
</html>
"""
return HTMLResponse(content=result_html)
else:
return JSONResponse(
content={"error": "订单不存在或查询失败"},
status_code=404
)
except Exception as e:
return JSONResponse(
content={"error": f"查询订单时出错: {str(e)}"},
status_code=500
)
@app.post("/notify")
async def payment_notify(request: Request):
"""支付成功回调处理"""
try:
# 获取请求数据
request_data = await request.body()
# 创建通知处理器
notify_handler = PaymentNotify(APPID, APPSECRET)
# 处理通知
success, message = await notify_handler.process(request_data)
if success:
# 这里应该更新数据库中的订单状态
print(f"💰 支付成功回调处理: {message}")
return Response(content=message)
else:
print(f"❌ 支付回调处理失败: {message}")
return JSONResponse(content={"error": message}, status_code=400)
except Exception as e:
print(f"❌ 处理支付回调时出错: {str(e)}")
return JSONResponse(content={"error": str(e)}, status_code=500)
@app.get("/success")
async def payment_success(request: Request):
"""支付成功页面"""
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>支付成功</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; text-align: center; }
.success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 30px; border-radius: 4px; }
.button { display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 10px; }
</style>
</head>
<body>
<div class="success">
<h1>🎉 支付成功!</h1>
<p>您的订单已支付成功,感谢您的购买!</p>
</div>
<a href="/" class="button">🏠 返回首页</a>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.get("/cancel")
async def payment_cancel(request: Request):
"""支付取消页面"""
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>支付取消</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; text-align: center; }
.warning { background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; padding: 30px; border-radius: 4px; }
.button { display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 10px; }
</style>
</head>
<body>
<div class="warning">
<h1>⚠️ 支付已取消</h1>
<p>您取消了支付,如有需要可以重新发起支付。</p>
</div>
<a href="/" class="button">🏠 返回首页</a>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.get("/query_order_page")
async def query_order_page(request: Request, order_id: str = None):
"""订单查询页面"""
if order_id:
# 直接查询订单
try:
order_info = await query_order(appid=APPID, mch_orderid=order_id)
if order_info:
status_map = {
'SUCCESS': '✅ 支付成功',
'REFUND': '🔄 转入退款',
'NOTPAY': '⏳ 未支付',
'CLOSED': '❌ 已关闭',
}
state = order_info.get('state', 'UNKNOWN')
status_text = status_map.get(state, f'未知状态({state})')
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>订单详情</title>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}
.info {{ background: #f8f9fa; border: 1px solid #dee2e6; padding: 15px; border-radius: 4px; margin: 15px 0; }}
pre {{ background: #f8f9fa; padding: 10px; border-radius: 4px; }}
.button {{ display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px; }}
</style>
</head>
<body>
<h1>📋 订单详情</h1>
<div class="info">
<strong>订单号:</strong> {order_id}<br>
<strong>状态:</strong> {status_text}
</div>
<pre>{json.dumps(order_info, ensure_ascii=False, indent=2)}</pre>
<a href="/" class="button">🏠 返回首页</a>
</body>
</html>
"""
return HTMLResponse(content=html_content)
except:
pass
# 显示查询表单
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>订单查询</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<h1>🔍 订单查询</h1>
<form action="/query_order" method="post">
<div class="form-group">
<label>订单号:</label>
<input type="text" name="order_id" required>
</div>
<button type="submit">查询订单</button>
</form>
<a href="/">🏠 返回首页</a>
</body>
</html>
"""
return HTMLResponse(content=html_content)
if __name__ == "__main__":
import uvicorn
print("🚀 启动易收米支付演示服务...")
print("📱 访问地址: http://localhost:8000")
print("📋 配置信息:")
print(f" AppID: {APPID}")
print(f" AppSecret: {APPSECRET[:8]}***{APPSECRET[-8:]}")
print("=" * 50)
uvicorn.run(app, host="0.0.0.0", port=8000)