commit 3c575870a1fb50b2d54bd22357b587071f2c5aee Author: RainSun Date: Mon Dec 9 09:48:07 2019 +0800 first diff --git a/README.md b/README.md new file mode 100644 index 0000000..9580b88 --- /dev/null +++ b/README.md @@ -0,0 +1,220 @@ +# 收款码合成后端文档 + +## 目录结构 +``` +|-- lib +| |-- allFunction.py +| |-- db.py +| |-- login.py +| |-- pay.py +| |-- signature.py +|-- templates +| |-- 400.html +| |-- 404.html +| |-- 410.html +| |-- 500.html +| |-- qrcode.html +|-- hello.py +|-- README.md +``` + +## 接口列表及说明 +```js +// $ 代表参数 +// <> 代表数据类型 + +// 用户小程序登录 +// 参数:code , sign +/login +|-- manageLogin() +| |-- checkContent($code,$sign) +| | |-- getOpenid($code) +| | | |-- {'errmsg': '$openid', 'errcode': 200} // 请求成功 +| | | |-- {'errmsg': '', 'errcode': ''} // 请求失败 +| | | +| | |-- False +| | +| |-- {'userInfo': findUser($openid), 'openid': 'str', 'errcode': 200} // 请求成功 +| |-- {'errmsg': '', 'errcode': ''} // 请求失败 +| |-- False +| +|-- {'userInfo': findUser($openid), 'openid': 'str', 'errcode': 200} // 请求成功 +|-- {'errmsg': '', 'errcode': ''} // 请求失败 +|-- 400 + +// 用户扫描二维码 +// 参数 id +/qr +|-- findQR() +| |-- findCode($id) +| | |-- arr ' Array>' //查询成功 +| | |-- False // id不合法或者查询无结果 +| | +| |-- arr ' Array>' // 查询成功 +| |-- False // id不合法或者查询无结果 +| +|-- template('qrcode.html') +|-- 410 + +// 小程序刷新用户信息 +// 参数 openId, sign +/reflash +|-- flash() +| |-- checkContent($openId, $sign) +| | |-- {'userInfo': findUser($openId) ' Array>', 'errcode': 200} // 返回查询到的所有用户信息,可能为空数组 +| | |-- False //openid不合法 +| | +| |-- {'userInfo': findUser($openId) ' Array>', 'errcode': 200} +| |-- False //openid不合法 +| +|-- {'userInfo': findUser($openId) ' Array>', 'errcode': 200} +|-- 400 + +// 小程序删除二维码 +// 参数 id, sign +/del +|-- delQR() +| |-- checkContent($id, $sign) +| | |-- delCode($id) +| | | |-- {'errcode': 200, 'errmsg': 'ok'} //删除成功 +| | | |-- False //id不合法 +| | | +| | |-- {'errcode': 200, 'errmsg': 'ok'} //删除成功 +| | |-- False +| | +| |-- {'errcode': 200, 'errmsg': 'ok'} //删除成功 +| |-- False +| +|-- {'errcode': 200, 'errmsg': 'ok'} //删除成功 +|-- 400 + +// 创建新的订单 +// 参数 data, totalFee +/newOrder +|-- createOrder() +| |-- checkData($data) +| | |-- $param = createOrderParams($totalFee) // 创建订单详情 +| | |-- $dataCache = createOrderCache($data, $params) // 把 out_trade_no 放进用户上传的data里边 +| | |-- $res = insertOrderCache($dataCache) // 把 data放进cache 表里边 +| | |-- {'params': params, 'errcode': 200} //上一步操作成功返回 +| | |-- False //以上操作出问题 +| | +| |-- {'params': params, 'errcode': 200} +| |-- False +| +|-- {'params': params, 'errcode': 200} +|-- 400 + +// 处理订单异步查询 +/notify +|-- manageNotify() +| |-- checkNotify($form) //校验数据 +| | |-- cache2Test($out_trade_no, payjs_order_id) +| | | |-- True //把缓存表中内容去出加上支付识别码放进正式表,在映射表建立 out_trade_no 到正式表 ObjectId 的映射 +| | | |-- False //以上操作出错 +| | | +| | |-- True +| | |-- False +| | +| |-- True +| |-- False +| +|-- True +|-- False + +// 小程序查询订单状态 +// 参数 out_trade_no +/checkOrder +|-- checkOrder() +| |-- findOrder($out_trade_no) +| | |-- order_id ' String>' //查询映射表 out_trade_no 对应的 id +| | |-- False //查询无结果或者删除映射表的数据失败 +| | +| |-- {'order_id': order_id, 'errcode': 200} +| |-- False +| +|-- {'order_id': order_id, 'errcode': 200} +|-- 500 +``` + +## TODO + +* MD5加解密 验证来源 + +## 配置环境 + +安装venv + +`python3 -m venv venv` + +启动venv + +`. venv/bin/activate` + +关闭venv + +`deactivate` + +查看现在已经安装的依赖 + +`pip3 list` + +更新pip + +`pip install --upgrade pip` + +安装Flask + +` pip install Flask` + +安装最新的flask + +`pip install -U https://github.com/pallets/flask/archive/master.tar.gz` + +安装pymongo + +` pip install pymongo` + +安装requests + +` pip install requests` + +安装payjs + +` pip install payjs` + +设置全局变量 + +`export FLASK_APP=qrcode.py` + +`export FLASK_ENV=development` + +启动 + +`flask run --host=0.0.0.0` + +在虚拟环境下安装gunicorn + +`pip install gunicorn` + +添加配置文件 gunicorn.conf + +``` +# 并行工作线程数 +workers = 4 +# 监听内网端口5000【按需要更改】 +bind = '127.0.0.1:5000' +# 设置守护进程【关闭连接时,程序仍在运行】 +daemon = True +# 设置超时时间120s,默认为30s。按自己的需求进行设置 +timeout = 120 +# 设置访问日志和错误信息日志路径 +# accesslog = './logs/acess.log' +# errorlog = './logs/error.log' +# 自动重启 +autostart = True +``` + +启动gunicorn + +`gunicorn qrcode:app -c gunicorn.conf` \ No newline at end of file diff --git a/data/5de3cc411e83c77332ded8a5.js b/data/5de3cc411e83c77332ded8a5.js new file mode 100644 index 0000000..b4c84f8 --- /dev/null +++ b/data/5de3cc411e83c77332ded8a5.js @@ -0,0 +1 @@ +var data = [{ "_id" : {"$oid": "5de3cc411e83c77332ded8a5"}, "username" : "小例子~欢迎测试~", "node" : "绝对不能删除这个东西", "wxcode" : "wxp://f2f0WvGwL7XfIB-3LU3S6bzSF9yZpiZCeMKP", "alcode" : "https://qr.alipay.com/fkx096505kpwu1n71l6pw4a", "qqcode" : "https://i.qianbao.qq.com/wallet/sqrcode.htm?m=tenpay&f=wallet&a=1&ac=CAEQkpzIoQQY14z17gU%3D_xxx_sign&u=1144131090&n=Rain+Sun", "openId" : "otxHM4icvFkfMcaqxTH1zSNKhKVs", "timeout" : "1606314047962", "create_time" : 1575210049 }] \ No newline at end of file diff --git a/data/5de7be0bb553c0e257aaed87.js b/data/5de7be0bb553c0e257aaed87.js new file mode 100644 index 0000000..4002003 --- /dev/null +++ b/data/5de7be0bb553c0e257aaed87.js @@ -0,0 +1 @@ +var data = [{ "_id" : {"$oid": "5de7be0bb553c0e257aaed87"}, "username" : "义卖", "node" : "", "wxcode" : "wxp://f2f0iCNCl9-RLi3FPcMTfJd1oDHAa6W9sQmL", "alcode" : "https://qr.alipay.com/fkx07547sutepd2xxujd61f", "qqcode" : "", "openId" : "otxHM4mV1Z6QGfMEfouhiEHId1x4", "timeout" : "1578060556451", "create_time" : 1575468555 }] \ No newline at end of file diff --git a/data/5dec451fa0e4c53d2e1b0c17.js b/data/5dec451fa0e4c53d2e1b0c17.js new file mode 100644 index 0000000..afa7113 --- /dev/null +++ b/data/5dec451fa0e4c53d2e1b0c17.js @@ -0,0 +1 @@ +var data = [{ "_id" : {"$oid": "5dec451fa0e4c53d2e1b0c17"}, "username" : "五金百货姚", "node" : "", "wxcode" : "wxp://f2f0lRtAjngRMk6kIBQoyox3g6mDiwnv1Vu3", "alcode" : "https://qr.alipay.com/fkx05073v5qnwtjbim8w17d", "qqcode" : "", "openId" : "otxHM4rQbf_KgadoZ4mclq2F6t2o", "timeout" : "1606869279009", "create_time" : 1575765279 }] \ No newline at end of file diff --git a/lib/allFunction.py b/lib/allFunction.py new file mode 100644 index 0000000..376884a --- /dev/null +++ b/lib/allFunction.py @@ -0,0 +1,302 @@ +from db import findUser, findCode, delCode, insertOrderCache, cache2Test, findOrder, insertCode +from login import getOpenid +from pay import checkNotify, createOrderParams +import re +import json +import time +import os + +# 主函数 + +# 处理登录操作 debug完成 +def manageLogin(request): + if checkContent(request.form['code'],request.form['sign']): # 校验 + res = getOpenid(request.form['code']) # 获取openid + if res['errcode'] == 200: # 获取成功返回用户信息 + return {'userInfo': findUser(res['errmsg']['openid']), 'openid': res['errmsg']['openid'], 'errcode': 200} + else: # 获取失败返回失败信息 + return res + else: + return False # 参数不全或者没通过校验 + +# 用户扫描二维码付款 debug完成 +def findQR(request): + # 这里做不了加密验证,防止攻击 + # 已知ObjectId只包含数字和字母且长度为24位,使用isalnum + codeId = str(request.args.get('id')) + if(len(codeId) == 24): # 长度校验 + if(codeId.isalnum()): # 内容校验 + return findCode(codeId) # 返回code信息 + else: + return False + else: + return False + +# 小程序刷新用户信息 debug完成 +def flash(request): + # 正常的加解密校验 + # 已知openId只包含-、_、数字和字母且长度为28位,使用正则 + if checkContent(request.form['openId'],request.form['sign']): # 校验 + openId = str(request.form['openId']) + if(len(openId) == 28): # 长度校验 + pattern = re.compile(r'[^\w-]') + if(pattern.search(openId)): # 内容校验 + return False + else: + return {'userInfo': findUser(openId), 'errcode': 200} # 返回用户信息 + else: + return False + else: + return False # 参数不全或者没通过校验 + +# 用户删除二维码 debug完成 +def delQR(request): + # 正常加解密校验 + # 和findQR一样,校验objectId + if checkContent(request.form['id'],request.form['sign']): # 校验 + codeId = str(request.form['id']) + if(len(codeId) == 24): # 长度校验 + if(codeId.isalnum()): # 内容校验 + res = delCode(codeId) # 返回code信息 + if res: + path = '{0}{1}.js'.format('/data/qrcode/data/', codeId) + print(path) + if os.path.exists(path): + os.remove(path) + return res + else: + return res + else: + return False + else: + return False + else: + return False + else: + return False + +# 用户上传二维码 +def addQR(request): + # 流程同createOrder一样,但是直接插入到正式表中,然后返回id + dataCache = json.loads(request.form['data']) # josn化,应该能当dist用 + checked = checkData(dataCache) # 校验所有用户上传的内容 + if checked: + res = insertCode(checked) + if res: + res = json.loads(res) # res是str 转成json + res = res['$oid'] # 取到里边的id + data = findCode(res) # 获取到所有的信息 + try: # 将信息写入文件 + f = open('{0}{1}.js'.format('/data/qrcode/data/', res),'w',encoding="utf-8") + f.write('{0}{1}'.format('var data = ',data)) + f.close() + except IOError: + return False + return {'errcode': 200, 'id': res, 'data':data} + else: + return False + else: + return False + + +# 微信小程序创建订单 debug完成 +def createOrder(request): + # 先就不加MD5校验了 + # 校验所有内容,目前策略:微信支付宝取网址后边的东西校验,qq转义 + # 需要接收的内容 : data : wxcode alcode qqcode username openId node; totalFee + # 判断过程:先看是否为空,不是空进行校验,报错返回False + dataCache = json.loads(request.form['data']) # josn化,应该能当dist用 + checked = checkData(dataCache) # 校验所有用户上传的内容 + if checked: + params = createOrderParams(request.form['totalFee']) # 获取到订单信息 + dataCache = createOrderCache(dataCache, params) # 处理要放进缓存表的数据 + res = insertOrderCache(dataCache) # 放进缓存表 + if res: + return {'params': params, 'errcode': 200} + else: + return False + else: + return False + +# 处理订单异步通知 debug完成 +def manageNotify(request): + check = checkNotify(request.form.to_dict()) # 回调验证 + if check: + res = cache2Test(request.form['out_trade_no'],request.form['payjs_order_id']) # 转移到Test + if res: # 转移成功返回id + return True + else: + return False + else: + return False + +# 小程序查询订单状况 debug完成 +def checkOrder(request): + order_id = findOrder(request.form['out_trade_no']) # 查询到的映射id + if order_id: # 后端还没收到反馈 + order_id = json.loads(order_id)['$oid'] + if order_id: # 查询删除都成功了 + return {'order_id': order_id, 'errcode': 200} + else: + return False + else: + return False + +# 工具函数--------------------------------------------------------------------------------- + +# 在这里进行解密对照 +def checkContent(row,rsa): + # 回头再写,先默认返回true + # 用那个MD5 + # 应该和MD5放在一个文件里 + return True + +# 判断str转换完是否为空 +def isKong(arg): + if arg == 'None' or arg == '' or arg == None: + return True + else: + return False + +# 上传参数校验 +def checkData(data): + hrefCount = 0 + usernameCount = 0 + openIdCount = 0 + timeoutCount = 0 + + # 可能的参数 + # wxp://f2f0e4PCkhToyNDT-zfA-Nn6zoAgPKvK9HUl + # https://qr.alipay.com/fkx03165mn5e2hx4gygpx04 + # HTTPS://QR.ALIPAY.COM/FKX01227ZSFRLWLKZSHL9C + # https://payapp.weixin.qq.com/qr/AQEGbDUlzvPBxYKSJst3hENW?t=GAAG#wechat_pay + # https://payapp.weixin.qq.com/qr/AQHoz2ywjCZbBKqDrvUuHDqG?t=GAAG#wechat_pay + # 微信验证 + # 微信为36位长度,只包含-、_、数字和字母,使用正则 + + wxp = re.compile(r'[^\w-]') + wxcode = str(data['wxcode']) + if isKong(wxcode): + wxcode = '' + else: + wxCache1 = re.findall(r"wxp://(.+)",wxcode) + wxCache2 = re.findall(r"https://payapp.weixin.qq.com/qr/(.+)\?t",wxcode) + if wxCache1: + # 第一种 + if(len(wxCache1[0]) == 36): + if not (wxp.search(wxCache1[0])): + hrefCount = hrefCount + 1 + else: + return False + else: + return False + elif wxCache2: + # 第二种 + if(len(wxCache2[0]) == 24): + if not (wxp.search(wxCache2[0])): + hrefCount = hrefCount + 1 + else: + return False + else: + return False + else: + # 都不是 + return False + + # openId验证 + # openId为28位长度,只包含-、_、数字和字母,使用正则 + openId = str(data['openId']) + if isKong(openId): + openId = '' + else: + if(len(openId) == 28): + if not (wxp.search(openId)): + openIdCount = 1 + else: + return False + else: + return False + + # 支付宝验证 + # 支付宝为23位或22位长度,只有数字和字母,使用isalnum + alcode = str(data['alcode']) + alCache1 = re.findall(r"https://qr.alipay.com/(.+)",alcode) + alCache2 = re.findall(r"HTTPS://QR.ALIPAY.COM/(.+)",alcode) + if isKong(alcode): + alcode = '' + else: + if alCache1: + # 第一种 + if len(alCache1[0]) == 23: + if alCache1[0].isalnum(): + hrefCount = hrefCount + 1 + else: + return False + else: + return False + elif alCache2: + # 第二种 + if len(alCache2[0]) == 22: + if alCache2[0].isalnum(): + hrefCount = hrefCount + 1 + else: + return False + else: + return False + else: + return False + + # QQ验证 + # QQ 一定包含 'qianbao.qq.com',使用正则 + # *!()_-.% 字母 数字 ,正则写不明白了,woc + qqp = re.compile(r'qianbao.qq.com') + qqcode = str(data['qqcode']) + if isKong(qqcode): + qqcode = '' + else: + if(qqp.search(qqcode)): + hrefCount = hrefCount + 1 + else: + return False + + # 用户名验证 + # 正则替换掉引号,$ + username = str(data['username']) + if(not isKong(username)): + username = username.replace('"','') + username = username.replace("'",'') + username = username.replace('$','') + usernameCount = 1 + else: + return False + + # 备注验证 + # 正则替换掉引号,$ + node = str(data['node']) + if(not isKong(node)): + node = node.replace('"','') + node = node.replace("'",'') + node = node.replace('$','') + else: + node = '' + + # 过期时间验证 + timeout = str(data['timeout']) + if(not isKong(timeout)): + if len(timeout) == 13: + timeoutCount = 1 + else: + return False + else: + return False + + if hrefCount >= 2 and usernameCount and openIdCount and timeoutCount: + return {'username': username, 'node': node, 'wxcode': wxcode, 'alcode': alcode, 'qqcode': qqcode, 'openId': openId, 'timeout': timeout, 'create_time':int(time.time())} + else: + return False + +# 创建订单详情 Cache +def createOrderCache(data,params): + data['out_trade_no'] = params['out_trade_no'] + data['create_time'] = int(time.time()) + return data diff --git a/lib/db.py b/lib/db.py new file mode 100644 index 0000000..af438cb --- /dev/null +++ b/lib/db.py @@ -0,0 +1,120 @@ +from pymongo import MongoClient +from bson import ObjectId, json_util + +# 主环境 +setting = 'test' + +# 获取数据集 +def col(arg: str = None): + conn = MongoClient('mongodb://yingbo:623910ert@localhost:12236/yingbo') + param = None + if arg: + param = arg + else: + param = setting + if param == 'test': + return conn.yingbo.test + elif param == 'order': + return conn.yingbo.order + elif param == 'cache': + return conn.yingbo.cache + elif param == 'code': + return conn.yingbo.code + +# 查找该用户所有信息 +def findUser(openid): + arr = [] + for i in col('code').find({'openId': openid}): + arr.append(i) + return json_util.dumps(arr) + +# 查找该二维码所有信息 +def findCode(id): + arr = [] + try: + for i in col('code').find({"_id" : ObjectId(id)}): + arr.append(i) + except Exception as e: + # id不合法 + return False + if(len(arr) == 0): + # 查询无结果 + return False + return json_util.dumps(arr) + +# 删除该二维码 +def delCode(id): + try: + col('code').remove({"_id" : ObjectId(id)}) + except Exception as e: + # id不合法 + return False + return {'errcode': 200, 'errmsg': 'ok'} + +# 用户上传进入缓存表 +def insertOrderCache(data): + try: + col('cache').insert_one(data) + except Exception as e: + # 失败 + return False + return True + +# 用户付款回调 从缓存写进正式表 +def cache2Test(out_trade_no,payjs_order_id): + try: + # 取出缓存表数据 + cache = col('cache').find_one({"out_trade_no" : out_trade_no}) + # 删除缓存表数据 + col('cache').remove({"out_trade_no" : out_trade_no},1) + except Exception as e: + # 失败 + return False + # 判断有没有,去重逻辑 + if cache: + # 向原始数据中添加支付识别码 + cache['payjs_order_id'] = payjs_order_id + # 插入到正式表 + res = col().insert_one(cache) + # 返回插入的正式表id + inserted_id = res.inserted_id + # 放进order表建立 out_trade_no 到 objectId的 映射 + try: + col('order').insert_one({'out_trade_no': out_trade_no, 'inserted_id': inserted_id}) + except Exception as e: + # 失败 + return False + return True + else: + return True + +# 小程序端查询支付结果 +def findOrder(out_trade_no): + arr = [] + try: + for i in col('order').find({"out_trade_no" : out_trade_no}): + arr.append(i) + except Exception as e: + # 失败 + return False + if(len(arr) == 0): + # 查询无结果 + return False + else: + # 删除order中的记录 + try: + col('order').remove({"out_trade_no" : out_trade_no}) + except Exception as e: + # 失败 + return False + return json_util.dumps(arr[0]['inserted_id']) + +# 用户上传进入主表 +def insertCode(data): + try: + res = col('code').insert_one(data) + inserted_id = res.inserted_id + except Exception as e: + # 失败 + return False + return json_util.dumps(inserted_id) \ No newline at end of file diff --git a/lib/login.py b/lib/login.py new file mode 100644 index 0000000..cfd3ef5 --- /dev/null +++ b/lib/login.py @@ -0,0 +1,27 @@ +from requests import request +import json + +def getOpenid(code): + url = 'https://api.weixin.qq.com/sns/jscode2session?appid=wx0df150c438e4c8f0&secret=e43b9f0356a29c8e5b39e79bb27376f1&js_code='+ code +'&grant_type=authorization_code' + response=request('GET',url) + if(response.status_code != 200): + return { + 'errcode': response.status_code, + 'errmsg': 'openid获取失败' + } + if('errcode' in response.json().keys()): + return { + 'errcode': response.json()['errcode'], + 'errmsg': response.json()['errmsg'] + } + return { + 'errcode': 200, + 'errmsg': response.json() + } + +#print(response.status_code) #返回状态码200 +#print(response.encoding) #返回编码 +#print(response.text) #返回响应的内容以unicode表示 +#print(response.headers) #返回头信息 +#print(response.cookies) #返回cookies CookieJar +#print(response.json()) #返回json数据 diff --git a/lib/pay.py b/lib/pay.py new file mode 100644 index 0000000..bce868a --- /dev/null +++ b/lib/pay.py @@ -0,0 +1,105 @@ +from payjs import PayJS # 也可根据个人习惯选择使用 Payjs/PAYJS/payjs +from payjs import PayJSNotify # 也可根据个人习惯选择使用 PayjsNotify/PAYJSNotify +from signature import get_signature +import random +import time + +MCHID = '1561399401' +KEY = 'C0vy3IlwgWn3TxJD' + +# 初始化 +p = PayJS(MCHID, KEY) + +# 订单查询 +def checkOrder(): + res = p.check_status(payjs_order_id=r.payjs_order_id) + if res: + return res.paid # 是否已支付 + else: + return {'errMsg': s.error_msg, 'err': s} # 错误信息 + +# 回调验证 +def checkNotify(str): + return PayJSNotify(KEY, str) + +# 创建微信小程序 Params +def createOrderParams(totalFee): + body = '收款码合并服务费' # 用户订单标题 + nonce = random.randint(100000,999999) # 随机数 + timestamp = int(time.time()) # 当前时间戳 + outTradeNo = 'QR-WXPay-' + str(timestamp) + '-' + str(nonce) # 商户端订单号 + notifyUrl = 'https://qr.powerrain.cn/notify' # 异步通知地址 + attach = 'from wx' # 异步通知附加数据 + paramsObject = { + 'mchid': MCHID, + 'total_fee': totalFee, + 'out_trade_no': outTradeNo, + 'body': body, + 'attach': attach, + 'notify_url': notifyUrl, + 'nonce': nonce + } + + sign = get_signature(KEY, paramsObject) # MD5签名算法签名 + + paramsObject['sign'] = sign + + return paramsObject + + +# 扫码支付 +# OUT_TRADE_NO = '2017TEST' # 外部订单号(自己的支付系统的订单号,请保证唯一) +# TOTAL_FEE = 1 # 支付金额,单位为分,金额最低 0.01 元最多 10000 元 +# BODY = '测试支付' # 订单标题 +# NOTIFY_URL = 'https://pay.singee.site/empty/' # Notify 网址 +# ATTACH = 'info' # Notify 内容 +# r = p.QRPay(out_trade_no=OUT_TRADE_NO, total_fee=TOTAL_FEE, body=BODY, notify_url=NOTIFY_URL, attach=ATTACH) +# if r: +# print(r.code_url) # 二维码地址(weixin:// 开头,请使用此地址构建二维码) +# print(r.qrcode) # 二维码地址(https:// 开头,为二维码图片的地址) +# print(r.payjs_order_id) # 订单号(PAYJS 的) +# else: +# print(r.STATUS_CODE) # HTTP 请求状态码 +# print(r.ERROR_NO) # 错误码 +# print(r.error_msg) # 错误信息 +# print(r) + +# 构造收银台支付网址(仅构造链接,请使用浏览器 302 到这个网址,无法预检查调用是否成功) +# c = p.get_cashier_url(out_trade_no=OUT_TRADE_NO, total_fee=TOTAL_FEE, body=BODY, callback_url=CALLBACK_URL, notify_url=NOTIFY_URL, attach=ATTACH) +# print(c) + +# JSApi 支付 +# OPENID = '这里填写支付用户的 OpenID' # 支付用户在 PayJS 端的 OpenID,可通过 get_openid 获取 +# j = p.JSApiPay(out_trade_no=OUT_TRADE_NO, total_fee=TOTAL_FEE, openid=OPENID, body=BODY, notify_url=NOTIFY_URL, attach=ATTACH) +# if j: +# print(j.jsapi) # 用于发起支付的支付参数 +# else: +# print(j.STATUS_CODE) # HTTP 请求状态码 +# print(j.ERROR_NO) # 错误码 +# print(j.error_msg) # 错误信息 +# print(j) + +# 刷卡支付 +# AUTH_CODE = '这里填写用户侧 18 位数字' # 用户的支付条码或二维码所对应的数字 +# m = p.MicroPay(out_trade_no=OUT_TRADE_NO, total_fee=TOTAL_FEE, auth_code=AUTH_CODE, body=BODY) +# print(m) + +# 订单关闭 +# def closeOrder(): +# res = p.close(r.payjs_order_id) +# if res: +# return {'errMsg': 'ok', 'errCode': 200} +# else: +# return {'errMsg': res.return_msg, 'errCode': 500} + +# # 订单退款 +# def refundOrder(): +# res = p.refund(r.payjs_order_id) +# if res: +# return {'errMsg': 'ok', 'errCode': 200} +# else: +# return {'errMsg': res.return_msg, 'errCode': 500} + +# # 获取用户 OpenId +# def getOpenId(): +# return p.get_openid(callback_url=CALLBACK_URL) diff --git a/lib/signature.py b/lib/signature.py new file mode 100644 index 0000000..801b8c9 --- /dev/null +++ b/lib/signature.py @@ -0,0 +1,37 @@ +from hashlib import md5 +from urllib.parse import urlencode, unquote_plus + +def get_signature(key: str, data: dict): + """ + 签名过程如下: + 0. 忽略已经存在的 sign + 1. 将所有参数名按照字典序排列,并以 "参数1=值1&参数2=值2&参数3=值3" 的形式排序 + 2. 将上面的内容加上 "&key=KEY"(KEY 为设置的商户密钥) + 3. 将组合后的字符串转换为大写 MD5 + + :param key: 商户密钥 + :param data: 要签名的参数字典 + :return: 签名后的字符串 + :rtype: str + """ + d = data.copy() + + # pop 掉 sign 字段 + try: + d.pop('sign') + except KeyError: + pass + + # pop 掉无效字段 + p = sorted([x for x in d.items() if (x[1] or x[1] == 0)], key=lambda x: x[0]) + + p.append(('key', key)) + + p = unquote_plus(urlencode(p)) + + h = md5() + h.update(p.encode()) + + r = h.hexdigest().upper() + + return r \ No newline at end of file diff --git a/qr/410.html b/qr/410.html new file mode 100644 index 0000000..24f3af5 --- /dev/null +++ b/qr/410.html @@ -0,0 +1,59 @@ + + + + + + + + 二维码已失效 + + + + + +
抱歉,
+
该二维码已失效
+ + diff --git a/qr/500.html b/qr/500.html new file mode 100644 index 0000000..2c8ce3a --- /dev/null +++ b/qr/500.html @@ -0,0 +1,59 @@ + + + + + + + + 服务器开小差了 + + + + + +
哎呀~
+
服务器开小差了呢
+ + diff --git a/qr/img/2934349b033b5bb5092b9c0d3bd3d539b700bc41.jpg.png b/qr/img/2934349b033b5bb5092b9c0d3bd3d539b700bc41.jpg.png new file mode 100644 index 0000000..0865634 Binary files /dev/null and b/qr/img/2934349b033b5bb5092b9c0d3bd3d539b700bc41.jpg.png differ diff --git a/qr/img/bg1.jpg b/qr/img/bg1.jpg new file mode 100644 index 0000000..71658d5 Binary files /dev/null and b/qr/img/bg1.jpg differ diff --git a/qr/img/bg2.jpg b/qr/img/bg2.jpg new file mode 100644 index 0000000..bcf60d0 Binary files /dev/null and b/qr/img/bg2.jpg differ diff --git a/qr/img/bg3.jpg b/qr/img/bg3.jpg new file mode 100644 index 0000000..071d25a Binary files /dev/null and b/qr/img/bg3.jpg differ diff --git a/qr/img/bg4.jpg b/qr/img/bg4.jpg new file mode 100644 index 0000000..cbf1f7d Binary files /dev/null and b/qr/img/bg4.jpg differ diff --git a/qr/img/bg5.jpg b/qr/img/bg5.jpg new file mode 100644 index 0000000..3583428 Binary files /dev/null and b/qr/img/bg5.jpg differ diff --git a/qr/img/bg6.jpg b/qr/img/bg6.jpg new file mode 100644 index 0000000..46e8bcd Binary files /dev/null and b/qr/img/bg6.jpg differ diff --git a/qr/img/sprite1.png b/qr/img/sprite1.png new file mode 100644 index 0000000..a5801b2 Binary files /dev/null and b/qr/img/sprite1.png differ diff --git a/qr/img/sprite2.png b/qr/img/sprite2.png new file mode 100644 index 0000000..dd5a224 Binary files /dev/null and b/qr/img/sprite2.png differ diff --git a/qr/img/sprite3.png b/qr/img/sprite3.png new file mode 100644 index 0000000..ca3d0ce Binary files /dev/null and b/qr/img/sprite3.png differ diff --git a/qr/qrcode.html b/qr/qrcode.html new file mode 100644 index 0000000..43ac58a --- /dev/null +++ b/qr/qrcode.html @@ -0,0 +1,444 @@ + + + + + + 收款码合成 + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
本二维码支持以下APP向我付款
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/qrcode.py b/qrcode.py new file mode 100644 index 0000000..a391af5 --- /dev/null +++ b/qrcode.py @@ -0,0 +1,113 @@ +import sys +sys.path.append('./lib') + +import os +from flask import Flask, escape, url_for, request, render_template, redirect, abort, send_from_directory +from allFunction import manageLogin, findQR, flash, delQR, manageNotify, createOrder, checkOrder, addQR + +app = Flask(__name__) + +if __name__ == '__main__': + app.run() + +# 用户小程序登录 +@app.route('/login',methods=['POST']) +def login(): + res = manageLogin(request) + if(res): + return res + else: + abort(400) + +# # 用户扫描二维码 +# @app.route('/qr',methods=['GET']) +# def qr(): +# res = findQR(request) +# if(res): +# return render_template('qrcode.html',data = res) +# else: +# abort(410) + +# 小程序刷新用户信息 +@app.route('/reflash',methods=['POST']) +def reflash(): + res = flash(request) + if(res): + return res + else: + abort(400) + +# 用户删除二维码 +@app.route('/del',methods=['POST']) +def delQRCode(): + res = delQR(request) + if(res): + return res + else: + abort(400) + +# 用户添加二维码 +@app.route('/add',methods=['POST']) +def addQRCode(): + res = addQR(request) + if(res): + return res + else: + abort(400) + +# 创建新的订单(暂时废弃) +# @app.route('/newOrder',methods=['POST']) +# def newOrder(): +# res = createOrder(request) +# if(res): +# return res +# else: +# abort(400) + +# 处理订单异步通知(暂时废弃) +# @app.route('/notify',methods=['POST']) +# def notify(): +# manageNotify(request) +# return 'ok' + +# 小程序查询订单状态(暂时废弃) +# @app.route('/checkOrder',methods=['POST']) +# def check(): +# res = checkOrder(request) +# print(res) +# if res: +# return res +# else: +# abort(500) + +# 控制小程序是否显示“最佳案例” +@app.route('/config', methods=['post']) +def config(): + return {'errcode': 200, 'showBest': 'true'} + + +# 图标 +@app.route('/favicon.ico') +def favicon(): + return send_from_directory(os.path.join(app.root_path, 'static'),'favicon.ico', mimetype='image/vnd.microsoft.icon') + + +# 二维码查询失败重定向, 404 +@app.errorhandler(404) +def page_not_found(e): + return render_template('404.html'), 404 + +@app.errorhandler(400) +def page_not_found(e): + return render_template('400.html'), 400 + +@app.errorhandler(500) +def page_not_found(e): + return render_template('500.html'), 500 + +@app.errorhandler(410) +def page_not_found(e): + return render_template('410.html'), 500 + +# 目前的安全模式依赖于限制post访问,非对称加密校验,对所有内容进行规则审查, +# 主要还是防止有人恶意访问,等到写密码本的时候尝试模拟ssl加密模式 diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..aea4304 Binary files /dev/null and b/static/favicon.ico differ diff --git a/templates/400.html b/templates/400.html new file mode 100644 index 0000000..128dbae --- /dev/null +++ b/templates/400.html @@ -0,0 +1,59 @@ + + + + + + + + 参数错误 + + + + + +
主人~
+
参数有点奇怪呢
+ + diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..9ee08ed --- /dev/null +++ b/templates/404.html @@ -0,0 +1,59 @@ + + + + + + + + 页面走丢了 + + + + + +
抱歉,
+
您访问的页面不存在
+ + diff --git a/templates/410.html b/templates/410.html new file mode 100644 index 0000000..24f3af5 --- /dev/null +++ b/templates/410.html @@ -0,0 +1,59 @@ + + + + + + + + 二维码已失效 + + + + + +
抱歉,
+
该二维码已失效
+ + diff --git a/templates/500.html b/templates/500.html new file mode 100644 index 0000000..2c8ce3a --- /dev/null +++ b/templates/500.html @@ -0,0 +1,59 @@ + + + + + + + + 服务器开小差了 + + + + + +
哎呀~
+
服务器开小差了呢
+ +