's'
This commit is contained in:
Binary file not shown.
Binary file not shown.
162
app/main.py
162
app/main.py
@@ -1,122 +1,84 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import requests
|
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from urllib.parse import urlencode, unquote
|
|
||||||
import time
|
import time
|
||||||
"""
|
import urllib.parse
|
||||||
JSAPI 支付
|
import random
|
||||||
"""
|
import string
|
||||||
key = "zA2hBU6pv6CIBzkW" # 填写通信密钥
|
|
||||||
mchid = "1561724891" # 特写商户号
|
|
||||||
|
|
||||||
# memos 配置
|
|
||||||
# MEMOS_API = "https://memos.hackrobot.org/api/v1/users"
|
|
||||||
# MEMOS_TOKEN = "eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiaGFja3JvYm90IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc1NzQwNTE0OX0.Idn7kBlxE-CoSOXwWuZ1tHGIRKHAIeDyXSafGS5OHsg"
|
|
||||||
MEMOS_API = "https://qun.hackrobot.cn/api/v1/users"
|
|
||||||
MEMOS_TOKEN = "eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiaGFja3JvYm90IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc1NzQwNTE0OX0.Idn7kBlxE-CoSOXwWuZ1tHGIRKHAIeDyXSafGS5OHsg"
|
|
||||||
|
|
||||||
# 构造签名函数
|
|
||||||
def sign(attributes):
|
|
||||||
attributes_new = {k: attributes[k] for k in sorted(attributes.keys())}
|
|
||||||
return (hashlib.md5((unquote(urlencode(attributes_new)) + "&key=" +
|
|
||||||
key).encode(encoding="utf-8")).hexdigest().upper())
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
|
||||||
event: str
|
|
||||||
EventGroupMsg: None = None
|
|
||||||
prirobot_wxidce: None = None
|
|
||||||
type: None = None
|
|
||||||
|
|
||||||
|
|
||||||
# event
|
|
||||||
# "":"EventGroupMsg",//事件标示(当前值为群消息事件)
|
|
||||||
# "robot_wxid":"wxid_5hxa04j4z6pg22",//机器人wxid
|
|
||||||
# "robot_name":"",//机器人昵称,一般为空
|
|
||||||
# "type":1,//1/文本消息 3/图片消息 34/语音消息 42/名片消息 43/视频 47/动态表情 48/地理位置 49/分享链接 2000/转账 2001/红包 2002/小程序 2003/群邀请
|
|
||||||
# "from_wxid":"18900134932@chatroom",//群id,群消息事件才有
|
|
||||||
# "from_name":"微群测",//群名字
|
|
||||||
# "final_from_wxid":"sundreamer",//发该消息的用户微信id
|
|
||||||
# "final_from_name":"遗忘悠剑o",//微信昵称
|
|
||||||
# "to_wxid":"wxid_5hxa04j4z6pg22",//接收消息的人id,(一般是机器人收到了,也有可能是机器人发出的消息,别人收到了,那就是别人)
|
|
||||||
# "msg":"图片https://b3logfile.com/bing/20201024.jpg",//消息内容(string/array) 使用时候根据不同的事件标示来定义这个值,字符串类型或者数据类型
|
|
||||||
# "money":0.01 //金额,只有"EventReceivedTransfer"事件才有该参数
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 允许所有来源跨域访问(不推荐用于生产环境)
|
|
||||||
origins = ["*"]
|
|
||||||
|
|
||||||
# 设置CORS(跨域资源共享)策略
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"], # 允许所有来源
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"], # 允许所有方法
|
allow_methods=["*"],
|
||||||
allow_headers=["*"], # 允许所有请求头
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ==================== XorPay 配置 ====================
|
||||||
|
AID = "8220" # 你的 aid
|
||||||
|
SECRET = "afcacd99570945f88de62624aaa3578e" # 你的 app secret
|
||||||
|
CASHIER_URL = f"https://xorpay.com/api/cashier/{AID}" # 收银台接口
|
||||||
|
# ====================================================
|
||||||
|
|
||||||
@app.get("/")
|
# 官方推荐签名方式(严格无分隔符拼接)
|
||||||
async def root():
|
def xorpay_sign(name: str, pay_type: str, price: str, order_id: str, notify_url: str) -> str:
|
||||||
return JSONResponse(content={"message": "Hello World"})
|
raw = name + pay_type + price + order_id + notify_url + SECRET
|
||||||
|
return hashlib.md5(raw.encode('utf-8')).hexdigest().lower()
|
||||||
|
|
||||||
@app.post("/payh5")
|
@app.post("/payh5")
|
||||||
async def payh5(item: dict):
|
async def payh5(item: dict):
|
||||||
print('item', item)
|
print('payh5 item:', item)
|
||||||
# 时间戳
|
|
||||||
timenew = str(int(time.time()))
|
total_fee_yuan = f"{item['total_fee'] / 100:.2f}"
|
||||||
order = {
|
|
||||||
"mchid": mchid,
|
random_suffix = ''.join(random.choices(string.hexdigits.lower(), k=8))
|
||||||
"body": timenew, # 订单标题
|
order_id = f"order_{int(time.time())}_{random_suffix}"
|
||||||
"out_trade_no": timenew, # 订单号
|
|
||||||
"total_fee": item['total_fee'], # 金额,单位:分
|
type_map = {
|
||||||
# "openid": "o7LFAwWu3OhSrs49-1x5cSlFm0D8", # 通过openid接口获取到的openid
|
"book": "购买书籍",
|
||||||
# "notify_url": ""
|
"meetup": "活动报名",
|
||||||
# "callback_url":'https://aiimg.hackrobot.cn/api'
|
"video": "视频解锁",
|
||||||
|
"vip": "VIP会员充值",
|
||||||
|
}
|
||||||
|
name = type_map.get(item.get('type'), "商品支付")
|
||||||
|
|
||||||
|
notify_url = "https://www.hackrobot.cn".rstrip("/")
|
||||||
|
|
||||||
|
# 临时不传 return_url,避免 ngrok 干扰
|
||||||
|
# return_url = item.get('callback_url')
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"name": name,
|
||||||
|
"pay_type": "jsapi",
|
||||||
|
"price": total_fee_yuan,
|
||||||
|
"order_id": order_id,
|
||||||
|
"notify_url": notify_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
order["callback_url"] = item['callback_url']
|
# if return_url:
|
||||||
order["openid"] = item['openid']
|
# params["return_url"] = return_url
|
||||||
order["sign"] = sign(order)
|
|
||||||
|
|
||||||
request_url = "https://payjs.cn/api/jsapi"
|
params["sign"] = xorpay_sign(
|
||||||
# 返回结果中包含`jsapi`字段,该字段的值即是前端发起时所需的6个支付参数
|
params["name"],
|
||||||
headers = {"content-type": "application/x-www-form-urlencoded"}
|
params["pay_type"],
|
||||||
response = requests.post(request_url, data=order, headers=headers)
|
params["price"],
|
||||||
print('response.json()----',response.json())
|
params["order_id"],
|
||||||
if response:
|
params["notify_url"]
|
||||||
print(response.json())
|
)
|
||||||
return response.json()
|
|
||||||
|
|
||||||
|
print("生成的参数:", params)
|
||||||
|
|
||||||
|
# 返回参数字典,前端用 form POST
|
||||||
class CreateUserRequest(BaseModel):
|
return {
|
||||||
username: str = None
|
"status": "ok",
|
||||||
password: str = None
|
"xorpay_params": params,
|
||||||
|
"xorpay_url": f"https://xorpay.com/api/cashier/{AID}"
|
||||||
@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",
|
@app.post("/xorpay_notify")
|
||||||
"Authorization": f"Bearer {MEMOS_TOKEN}"
|
async def xorpay_notify(request: dict):
|
||||||
}
|
print("XorPay 异步通知:", request)
|
||||||
memos_resp = requests.post(MEMOS_API, json=memos_data, headers=memos_headers)
|
# TODO: 正式上线后加验签和订单处理
|
||||||
memos_result = memos_resp.json()
|
return {"status": "ok"}
|
||||||
print('memos_result-',memos_result)
|
|
||||||
return memos_result
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user