From dcc0d775712aa6614d709295d0782a54a825ff02 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 17 Dec 2025 06:56:04 -0600 Subject: [PATCH] 's' --- app/__pycache__/main.cpython-312.pyc | Bin 3039 -> 5587 bytes app/main.py | 96 +++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc index e951125e238a7b3b40ec6a36b6ed83241a481a70..5ff28820d184bf82eb82ab683025fa137138a1a0 100644 GIT binary patch literal 5587 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 U1O!ue%;$gsk4in`55aW*0Y~=(OaK4? diff --git a/app/main.py b/app/main.py index e6b3ba4..fe8ccbc 100644 --- a/app/main.py +++ b/app/main.py @@ -1,10 +1,11 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel import hashlib import time -import urllib.parse import random import string +import requests app = FastAPI() @@ -19,14 +20,28 @@ app.add_middleware( # ==================== XorPay 配置 ==================== AID = "8220" # 你的 aid SECRET = "afcacd99570945f88de62624aaa3578e" # 你的 app secret -CASHIER_URL = f"https://xorpay.com/api/cashier/{AID}" # 收银台接口 # ==================================================== +# memos 配置(从原代码恢复) +MEMOS_API = "https://qun.hackrobot.cn/api/v1/users" +MEMOS_TOKEN = "eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiaGFja3JvYm90IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc1NzQwNTE0OX0.Idn7kBlxE-CoSOXwWuZ1tHGIRKHAIeDyXSafGS5OHsg" + # 官方推荐签名方式(严格无分隔符拼接) def xorpay_sign(name: str, pay_type: str, price: str, order_id: str, notify_url: str) -> str: raw = name + pay_type + price + order_id + notify_url + SECRET return hashlib.md5(raw.encode('utf-8')).hexdigest().lower() +# 回调验签函数 +def verify_xorpay_notify(data: dict) -> bool: + received_sign = data.get("sign", "") + aoid = data.get("aoid", "") + order_id = data.get("order_id", "") + pay_price = data.get("pay_price", "") + pay_time = data.get("pay_time", "") + raw = aoid + order_id + pay_price + pay_time + SECRET + calculated_sign = hashlib.md5(raw.encode('utf-8')).hexdigest().lower() + return calculated_sign == received_sign + @app.post("/payh5") async def payh5(item: dict): print('payh5 item:', item) @@ -44,10 +59,7 @@ async def payh5(item: dict): } name = type_map.get(item.get('type'), "商品支付") - notify_url = "https://www.hackrobot.cn".rstrip("/") - - # 临时不传 return_url,避免 ngrok 干扰 - # return_url = item.get('callback_url') + notify_url = "https://www.hackrobot.cn/xorpay_notify" # 建议使用完整路径,避免冲突 params = { "name": name, @@ -57,9 +69,6 @@ async def payh5(item: dict): "notify_url": notify_url, } - # if return_url: - # params["return_url"] = return_url - params["sign"] = xorpay_sign( params["name"], params["pay_type"], @@ -70,15 +79,76 @@ async def payh5(item: dict): print("生成的参数:", params) - # 返回参数字典,前端用 form POST + # 返回参数字典,前端用 form POST 到 xorpay 收银台 return { "status": "ok", "xorpay_params": params, "xorpay_url": f"https://xorpay.com/api/cashier/{AID}" } -# 异步通知回调 + +# 异步通知回调:在支付成功后自动创建 memos 用户 @app.post("/xorpay_notify") async def xorpay_notify(request: dict): print("XorPay 异步通知:", request) - # TODO: 正式上线后加验签和订单处理 - return {"status": "ok"} \ No newline at end of file + + # 1. 验签(强烈推荐正式环境开启) + if not verify_xorpay_notify(request): + print("验签失败,可能为伪造通知") + return {"status": "fail", "msg": "sign error"} + + # 2. 检查支付状态(XorPay 成功通知时 status=1) + if request.get("status") != "1": + print("支付未成功") + return {"status": "ok"} # 仍返回 ok,避免重复通知 + + # # 3. 这里可以根据你的业务逻辑,从 order_id 或其他参数提取用户名/密码等信息 + # # 假设前端在支付时传了自定义参数(如 order_id 中编码了用户名),或使用 return_url 携带信息 + # # 示例:简单生成一个用户(类似原 /create_user) + # timenew = str(int(time.time())) + # username = f"user_{timenew}" + # password = "123456" # 可以随机生成或从业务中获取 + + # memos_data = { + # "username": username, + # "password": password, + # "role": "USER", + # "state": "NORMAL" + # } + # memos_headers = { + # "Content-Type": "application/json", + # "Authorization": f"Bearer {MEMOS_TOKEN}" + # } + + # try: + # memos_resp = requests.post(MEMOS_API, json=memos_data, headers=memos_headers) + # memos_result = memos_resp.json() + # print('memos 创建结果:', memos_result) + # # 这里可以记录订单与 memos 用户的关联(数据库等) + # except Exception as e: + # print("创建 memos 用户失败:", e) + + # 返回 ok 表示通知成功接收 + return {"status": "ok"} + +# 保留原来的手动创建用户接口(如果还需要) +class CreateUserRequest(BaseModel): + username: str = None + password: str = None + +@app.post("/create_user") +async def create_user(item: CreateUserRequest): + timenew = str(int(time.time())) + memos_data = { + "username": item.username or f"user{timenew}", + "password": item.password or "123456", + "role": "USER", + "state": "NORMAL" + } + memos_headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {MEMOS_TOKEN}" + } + memos_resp = requests.post(MEMOS_API, json=memos_data, headers=memos_headers) + memos_result = memos_resp.json() + print('memos_result-', memos_result) + return memos_result \ No newline at end of file