目标问题
- 支持个人办理
- 支持个人网站扫码付费
- 本文章只是给出了通过网站扫码付款的一种实现方案,欢迎技术讨论交流,请勿用于非法用途.
实战
流程描述:
用户发起支付后,网站申请获取二维码,用户扫码后支付结果返回给回调链接,网站处理回调,完成支付流程.
运营准备
- 备案域名+https服务 target 正式提供服务+扫码付款的前提
- 外网可访问服务器 target 作为支付结果的回调链接.可获取用户的支付结果.
开发准备
本次实战使用的架构为:
Python.Tornado+MongoDB+Jquery
代码分3部分
- html页面
- 后台路由处理
- 网站自有业务处理-新增订单,确认付款结果,提供对应服务
步骤列下:
- 先写路由代码和业务处理代码
""" 常规 导包 """
import sys
import os
import codecs
import json
import io
import base64
import hashlib
import pymongo
bsdir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(bsdir)
# tornado
import tornado.web as tweb
import tornado.ioloop as tIO
db = pymongo.MongoClient(host='localhost', port=27017)
cdbname = '数据库名称'
collectionname = '集合名称'
# 默认移除未支付订单
db.get_database(cdbname).get_collection(collectionname).remove({'ispay': False})
class op(object):
# mongodb数据库实例,用于数据的增删改查接口
cdb = MongodbClient()
def create_order(**kwargs):
# 根据提供的参数处理业务
# 并返回处理结果
# 建议在此处添加out_trade_no 并保存到数据库,作为网站支付记录查询参数
# 添加total_fee 作为支付价格
out_trade_no = hashlib.md5(str(time.time()).encode('utf-8')).hexdigest().upper()
return {
"out_trade_no" : out_trade_no,
"total_fee": kwargs.get("total_fee"),
}
def process(self, **kwargs):
""" 网站业务处理部分
包含订单生成,服务处理,服务提供接口
"""
obj = cdb.insert(self.create_order(**kwargs)}
return obj.insert_id
def not_pay():
return "等待支付/支付失败"
class BusinessHandle(tweb.RequestHandle):
""" 处理支付相关业务
"""
# 生成订单所需参数
params = ["param1"]
def post(self):
res = op().process({name:self.get_argument(name) for name in self.params})
self.write(res)
class notify_url(tweb.RequestHandle):
""" 得到支付结果后 服务商回调链接,网站应当以此来更新订单是否完成支付"""
def post(self):
""" 支付结果判断,根据对接的服务商提供的文档添加判断"""
if self.get_argument("return_code") != 1:
self.write("支付失败")
# 并将此结果与out_trade_no对应的业务处理中ispay改为True
item = op().cdb.find_one({"out_trade_no":self.get_arguments("out_trade_no")})
item["ispay"] = True
op().cdb.update_one({"_id":item.get("_id")},{"$set":item})
class confirm(tweb.RequestHandle):
""" 网站页面轮询判断支付是否完成,并在完成后处理后续逻辑"""
def post(self):
out_trade_no = self.get_argument("out_trade_no")
if item and item.get('ispay', None):
self.write(op().result(item))
else:
self.write(op().not_pay())
class Pay(tweb.RequestHandle):
def post(self):
item = op().cdb.find_one({"out_trade_no":self.get_argument("out_trade_no")})
if item and item.get("ispay", None):
# 订单已支付/未查询到订单
return
total_fee = item.get("total_fee")
my_notify_url = "" # 此回调链接只能真机测试,即访问上述notify_url类
# 调用服务商扫码支付接口获取二维码和订单号
return your_qrcode(total_fee=total_fee)
路由部分
urls = [
(r'/businesshandle', BusinessHandle),
(r'/pay', Pay),
(r'/notify_url', notify_url),
(r'/confirm', confirm),
]
configuration = {
# 通用配置
'template_path' : '/'.join([bsdir, 'templates']),
'static_path' : '/'.join([bsdir, 'static']),
'autoreload' : True,
'static_hash_cache': False,
}
app = tweb.Application(urls, **configuration)
port = 8035
print('http://localhost:{port}'.format(port=port))
app.listen(port=port, address='0.0.0.0')
tIO.IOLoop.instance().start()
- HTML部分通过点击按钮发起调用
<button οnclick="pay()">发起支付</button>
function submitimage() {
$.ajax({
url: 'businesshandle',
method: 'post',
async: false,
data: {
// 业务参数,与params对应
},
success: function (res) {
// 发起支付后页面处理
...
// 开启轮询查询支付结果
confirm(res);
$.ajax({
url: '/pay',
method: 'post',
data: {
out_trade_no: res.out_trade_no,
},
success: resp => {
// 回显付款二维码,根据服务商参数指定
},
error: err => {
},
})
},
error: function (err) {
console.log("error");
}
})
}
function confirm(obj) {
$.ajax({
url: '/confirm',
method: 'post',
data: {
out_trade_no: obj.out_trade_no
},
success: resp => {
if (resp.ispay) {
// 隐藏付款二维码
// 提供服务
} else {
// 等待付款部分,每隔1.5秒,确认一次是否付款
setTimeout(() => {
confirm(obj);
}, 1500);
}
},
error: err => {
}
});
}
- 测试部分
本地可测试功能: 订单创建成功,假设支付成功的前提下完整提供服务
外网测试功能: 成功发起支付获得付款二维码,并在用户扫码付款/取消支付后,业务订单逻辑完整
后续
python代码部分只给出了框架,业务如何处理还需要自己添加.
代码纯手敲,未经测试.可访问Demo查看效果
需要完整代码或技术交流加博主微信