安装
pip install python-alipay-sdk
生成密钥文件
openssl
生成私钥
genrsa -out app_private_key.pem 2048
ctr + d退出
ls 查看生成的私钥文件
cat app_private_key.pem
生成公钥
rsa -in app_private_key.pem -pubout -out app_public_key.pem
可以在另一个终端ls查看
私钥保存在程序中,公钥放到支付宝中
cat app_public_key.pem
把上面的公钥放到支付宝的沙箱应用的查看公钥中,除去上下的修饰
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtL8j5quexGUn5dGTdO76 vx+yfkpOQFkTymk1FALj0FSWucrM7u8+8O5DJtbRI+Skt9tGRNU/ZjG6IlQUBzmM xIB3b0I3I5GCg2ZaFWmqblzcqo3RZ9aC+kOX9h3o/xeaq5aemwRsPxezJoFmF38f 6YwR8YIWWnqsFw93MWahbeSt02qnZPwKnq41zUSV/iPogUubLud2D7Dg+cgREfm8 pflbTL4utt41PU7O+tbGUet9fQKpliTESs7Gda/IMZf9KtbBKQCjxiVKiLHcMQje 0FcaaYWtyEebE02E4qIqnHRUklKExj1/mQfXQsum0wO6+EQPuN9VSQUaAMSfqQiq
把家目录中的私钥放到程序中的orders应用中
把支付宝的公钥复制到程序中
在orders应用中新建一个文件alipay_public_key.pem
将公钥的内容复制保存到一个文本文件中(alipay_pubilc_key.pem),注意需要在文本的首尾添加标记位(-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----) ,形如:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzkXJrjPbdu2DdCygEChuLzuBq0Hhvp2SOoe4LzPR0LyKcQF3TM/5O/K5YlNuspeZgMqm+mNLmpp6ahfo6RrMSrnZ9f5jN81mz7ZAIe7PAG0Fj1lTzkBNLu2Ab2NVgkHT9wf/Qgug+Vef4bSVyVdED9cCxsZq76BdSKHKoSufts1YK8QzEg7oX4f/FcRyo1afuqXl2HV+LSTstw0nLq9VkaOawP5bewTg4L7yIIjsb+RLDO7mwTOe3HoGxmWOTU+EIJk2AWqaQWAIGpRQrQZ54T/B8K0wcuGsTt6Ru5Z2XcGvZ6Mk1drQsZ6u1AuOPIvlR7FM8+azGbQmADLevseYOwIDAQAB -----END PUBLIC KEY-----
用户通过支付宝完成提交订单
支付宝接口文档 https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
前端的页面如下所示:
因为后端要向支付宝发送请求,需要向支付宝传递的参数有
订单的编号
订单的金额
所以前端需要向后端传送这两个参数,因为后端可以根据订单的编号找到订单的信息表,所以订单的金额可以不传,只需传送订单的标号即可,
后端会给前端返回一个支付宝的页面地址,可以引导用户在这个页面付款
前端的js代码:
必须是待支付的状态,才会发送请求
<script type="text/javascript"> $(‘.oper_btn‘).click(function() { var order_id = $(this).attr("order_id"); var order_status = $(this).attr("order_status"); order_status = parseInt(order_status); if (1 == order_status) { // 表示待支付 var req_data ={ order_id:order_id, csrfmiddlewaretoken: "{{ csrf_token }}" } $.post(‘/orders/pay‘, req_data, function(data){ if ( 1 == data.code ) { // 用户未登录 location.href = "/users/login"; } else if (0 == data.code ) { // 发起支付请求成功,跳转的地址 window.open(data.url); } else { alert(data.message); } }); } });
后端视图业务逻辑
用户必须是登陆的状态,通过继承自定义的装饰器
1 接受前端传送的订单编号
2 判断编号是否为空
为空返回缺少订单的编号
3 通过(订单的编号,用户,支付的方式,支付的状态),获取订单的信息表
没查到返回订单有误
下面睡固定的写法
4 构建alipay支付工具对象(固定)
alipay = AliPay( appid=settings.ALIPAY_APPID, # 沙箱模式中的appid app_notify_url=None, # 默认回调url app_private_key_path=os.path.join(settings.BASE_DIR, "apps/orders/app_private_key.pem"), alipay_public_key_path=os.path.join(settings.BASE_DIR, "apps/orders/alipay_public_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False, 沙箱模式配置为true )
5 借助alipay对象向支付宝发起支付请求 电脑网站支付,需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string (固定)
order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_id, # 订单编号 total_amount=str(order.total_amount), # 订单金额 subject="天天生鲜%s" % order_id, # 订单描述信息 return_url=None, # 订单成功返回的信息 notify_url=None # 可选, 不填则使用默认notify url )
6返回 电脑网站支付,需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string
在orders.models中的OrderInfo类中添加以下的属性,表示订单的状态
ORDER_STATUS_ENUM = { "UNPAID": 1, "UNSEND": 2, "UNRECEIVED": 3, "UNCOMMENT": 4, "FINISHED": 5 }
在orders.models中的OrderInfo类中添加以下的属性,表示支付方式的序号
PAY_METHODS_ENUM = { "CASH": 1, "ALIPAY": 2 }
在settiing中添加访问支付宝的网址和支付宝的沙箱模式中的id
# 支付宝的网址 ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do" ALIPAY_APPID = "2016081600258081"
代码如下:
class PayView(LoginRequiredJsonMixin, View): """支付宝支付视图""" def post(self, request): # 订单编号 order_id order_id = request.POST.get("order_id") if not order_id: return JsonResponse({"code": 2, "message": "缺失订单编号"}) # 获取订单信息 try: order = OrderInfo.objects.get(order_id=order_id, user=request.user, status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"], pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"]) except OrderInfo.DoesNotExist: return JsonResponse({"code": 3, "message": "订单信息错误"}) # 构建alipay支付工具对象 alipay = AliPay( appid=settings.ALIPAY_APPID, # 沙箱模式中的appid app_notify_url=None, # 默认回调url app_private_key_path=os.path.join(settings.BASE_DIR, "apps/orders/app_private_key.pem"), alipay_public_key_path=os.path.join(settings.BASE_DIR, "apps/orders/alipay_public_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False, 沙箱模式配置为true ) # 借助alipay对象,向支付宝发起支付请求 # 电脑网站支付,需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_id, # 订单编号 total_amount=str(order.total_amount), # 订单金额 subject="天天生鲜%s" % order_id, # 订单描述信息 return_url=None, notify_url=None # 可选, 不填则使用默认notify url ) # 返回json数据 alipay_url = settings.ALIPAY_URL + "?" + order_string return JsonResponse({"code": 0, "message": "发起支付成功", "url": alipay_url})
配置url
url(‘^pay$‘, views.PayView.as_view(), name="pay"),
当用户点击待支付按钮,就可以完成支付了
检查支付的结果
这个是没有公网,需要自己去向支付宝发送请求询问支付成功还是失败
前端js的代码如下:
红色区域表示新增的代码 :支付成功重新加载页面,失败显示失败的信息
<script type="text/javascript">
$(‘.oper_btn‘).click(function() {
var order_id = $(this).attr("order_id");
var order_status = $(this).attr("order_status");
order_status = parseInt(order_status);
// 只有待支付的状态才可以点击
if (1 == order_status) {
// 表示待支付
var req_data ={
order_id:order_id,
csrfmiddlewaretoken: "{{ csrf_token }}"
}
$.post(‘/orders/pay‘, req_data, function(data){
if ( 1 == data.code ) {
// 用户未登录
location.href = "/users/login";
} else if (0 == data.code ) {
// 发起支付请求成功
window.open(data.url);
// 向后端发起查询支付状态的请求
$.get("/orders/check_pay?order_id="+order_id, function (resp_data) {
if (0 == resp_data) {
// 支付成功
location.reload();
} else {
alert(resp_data.message);
}
});
} else {
alert(data.message);
}
});
} else if (4 == order_status) {
location.href = ("/orders/comment/" + order_id);
}
});
</script>
后端视图业务逻辑
支付宝开发者文档 https://openhome.alipay.com/developmentDocument.htm
后端视图的业务逻辑
1 用户必须是登陆的状态,
2 通过get请求的方式接受用户传送过来的订单编号
如果订单的编号不存在,返回订单信心缺少
3 通过订单的(编号,用户,交易的状态是否为待支付和支付的方式是否为支付宝),查询对应的订单信息表
如果不存在,返回订单信息错误
4 通过whileTrue循环遍历查询用户交易的状态
如果交易的状态码为10000和交易的状态为TRADE_SUCCESS说明交易成功,把支付宝的交易号保存在订单信息表的trade_id,订单的状态设为待评价,返回给前端
如果交易的状态码为40004(表示支付宝还没生成订单)或者交易的状态码为10000并且交易的状态为用户待支付(WAIT_BUYER_PAY),延迟10scontinue
else 返回支付失败
完整的代码如下:
class CheckPayStatusView(LoginRequiredJsonMixin, View): """检查支付结果""" def get(self, request): order_id = request.GET.get("order_id") if not order_id: return JsonResponse({"code": 2, "message": "缺少订单号"}) # 获取订单信息 try: order = OrderInfo.objects.get(order_id=order_id, user=request.user, status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"], pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"]) except OrderInfo.DoesNotExist: return JsonResponse({"code": 3, "message": "订单信息错误"}) # 构建alipay支付工具对象 alipay = AliPay( appid=settings.ALIPAY_APPID, # 沙箱模式中的appid app_notify_url=None, # 默认回调url app_private_key_path=os.path.join(settings.BASE_DIR, "apps/orders/app_private_key.pem"), alipay_public_key_path=os.path.join(settings.BASE_DIR, "apps/orders/alipay_public_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False, 沙箱模式配置为true ) # 借助alipay工具查询支付结果 while True: response = alipay.api_alipay_trade_query(order_id) code = response.get("code") trade_status = response.get("trade_status") if code == "10000" and trade_status == "TRADE_SUCCESS": # 表示用户支付成功 order.trade_id = response.get("trade_no") # 支付宝的交易标号 order.status = OrderInfo.ORDER_STATUS_ENUM["UNCOMMENT"] # 设置订单状态为待评价 order.save() return JsonResponse({"code": 0, "message": "支付成功"}) elif code == "40004" or (code == "10000" and trade_status == "WAIT_BUYER_PAY"): # 表示支付宝订单还没创建好, 或者用户还未支付 time.sleep(10) continue else: return JsonResponse({"code": 4, "message": "支付失败"})
配置url路径
url(‘^check_pay$‘, views.CheckPayStatusView.as_view(), name="check_pay"),