一. 用户中心-订单页
1. 编辑用户中心-订单页类视图
# user / views.py
import re
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.core.mail import send_mail
from django.core.paginator import Paginator
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse
# 显示异常,但不会报错
from django.views.generic import View
from user.models import User, Address
from goods.models import GoodsSKU
from order.models import OrderInfo, OrderGoods
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired
from apps.celery_tasks.tasks import send_register_activer_email
from util.mixin import LoginRequiredMixin
# /user/order
class UserOrderView(LoginRequiredMixin, View):
"""用户中心 - 订单页"""
def get(self, request, page):
"""显示"""
# 获取用户的订单信息
user = request.user
orders = OrderInfo.objects.filter(user=user).order_by('-create_time')
# 遍历获取订单商品的信息
for order in orders:
# 根据order_id查询订单商品信息
order_skus = OrderGoods.objects.filter(order_id=order.order_id)
# 遍历order_skus计算商品的小计
for order_sku in order_skus:
# 计算小计
amount = order_sku.count * order_sku.price
# 动态给order_sku增加属性amount,保存订单商品的小计
order_sku.amount = amount
# 动态给order增加属性,保存订单状态标题
order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
# 动态给order增加属性,保存订单商品的信息
order.order_skus = order_skus
# 分页
paginator = Paginator(orders, 1)
# 获取page页的内容
try:
page = int(page)
except Exception as e:
page = 1
if page > paginator.num_pages: # 判断是否大于总结书
page = 1
# 获取第 page页 的Page实例对象
order_page = paginator.page(page)
# todo: 进行页码的控制,页面上最多显示5个页码
# 1. 总页数小于5页,页面上显示所有页码
# 2. 如果当前页是前3页,显示1-5页
# 3. 如果当前页是后3页,显示后5页
# 4. 其他情况, 显示当前页的前2页,当前页,当前页后2页
num_pages = paginator.num_pages
if num_pages < 5:
pages = range(1, num_pages + 1)
elif page <= 3:
pages = range(1, 6)
elif num_pages - page <= 2:
pages = range(num_pages - 4, num_pages + 1)
else:
pages = range(page - 2, page + 3)
# 组织上下文
context = {
'order_page': order_page,
'pages': pages,
# page:'order': 用户中心显示的那个页面
'page': 'order'
}
# 使用模板
return render(request, 'user_center_order.html', context)
2. 编辑用户中心-订单页路由
# user / urls.py
from django.conf.urls import url
from apps.user.views import RegisterView, ActiveView, LoginView, UserInfoView, UserOrderView, UserSiteView, LogoutView
app_name = "user" # 指定命名空间
urlpatterns = [
...
url(r'^order/(?P<page>\d+)$', UserOrderView.as_view(), name='order'), # 用户中心-订单页
]
3. 编辑用户中心-订单页模型
(user_center_order.html)准备中...
二. 支付宝简介
1. 登录支付宝开发平台
支付宝开放平台https://openhome.alipay.com/
2. 使用沙箱应用模拟支付
3. API-> 支付->电脑网站支付->开发文档
三. 订单支付-网站对接支付宝流程图
四. 订单支付-订单支付代码配置
python封装的sdk文档: https://github.com/fzlee/alipay/blob/master/README.zh-hans.md
1. 卸载pycrypt包
pip uninstall pycrypt
2. 安装python-alipay-sdk --upgrade
pip install python-alipay-sdk --upgrade
3. 电脑上安装openssl(生成秘钥)
(1). 进入官网
Shining Light Productions - Home
(2). 安装
(3). 配置环境变量
4. 黑窗口输入指令生成公钥和私钥
# 1. 进入 openssl
openssl
# 2. 输入指令,在哪执行就会在哪生成文件
genrsa -out app_private_key.pem 2048 # 私钥
rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
# 3. 退出 openssl
exit
5. 更新沙箱环境自定义公钥
复制电脑生成的公钥替换沙箱原有的自定义公钥
6. 查看支付宝公钥保存到项目中
(1). 新建文件 order子应用 / alipay_public_key.pem
(2). 复制沙箱环境的自定义公钥到项目中
(3). 增加首尾两行代码
7. 将电脑生成的私钥拷贝到项目中
order子应用下 / app_private_key.pem
五. 创建订单-支付后台
1. 定义支付类
# order /views.py
from datetime import datetime
from django.http import JsonResponse
from django.shortcuts import render, redirect
from goods.models import GoodsSKU
from user.models import Address
from order.models import OrderInfo, OrderGoods
# Create your views here.
# /order/place
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection
from util.mixin import LoginRequiredMixin
from django.db import transaction
from django.conf import settings
import os
from alipay import AliPay
# ajax post
# 前端传递的参数: 订单id(order_id)
# order /pay
class OrderPayView(View):
"""订单支付"""
def post(self, request):
"""订单支付"""
# 用户是否登录
user = request.user
if not user.is_authenticated:
return JsonResponse({"res": 0, "errmsg": "用户未登录"})
# 接收参数
order_id = request.POST.get("order_id")
# 校验参数
if not order_id:
return JsonResponse({"res": 1, "errmsg": "无效的订单id"})
try:
order = OrderInfo.objects.get(order_id=order_id,
user=user,
pay_method=3,
order_status=1)
except OrderInfo.DoesNotExist:
return JsonResponse({"res": 2, "errmsg": "订单错误"})
# 业务处理: 使用python skd调用支付宝的支付接口
# 初始化
alipay = AliPay(
appid="xxxxxxxx",# 沙箱应用里的APPID
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem'), # 私钥
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
alipay_public_key_path=os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem'),
sign_type='RSA2', # RSA 或者 RSA2
debug=True # 默认False,沙箱改为True
)
# 调用支付接口
# 电脑网站支付,需要跳转到:https://openapi.alipaydev.com/gateway.do? + order_string
total_pay = order.total_price + order.transit_price
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order_id, # 订单id
total_amount=str(total_pay), # 支付总金额
subject="天天生鲜%s" % order_id, # 标题
return_url=None,
notify_url=None # 可选,不填则使用默认 notify url
)
# 返回应答
pay_url = "https://openapi-sandbox.dl.alipaydev.com/gateway.do?" + order_string
return JsonResponse({"res": 3, "pay_url":pay_url})
2. 定义支付路由
from django.conf.urls import url
from order.views import OrderPlaceView,OrderCommitView,OrderPayView
app_name="order"
urlpatterns = [
...
url('^pay$',OrderPayView.as_view(),name='pay'), # 订单支付
]
3. 定义支付模板
准备中...
六. 创建订单-前端js
<script>
$('.oper_btn').click(function () {
// 获取status
status = $(this).attr('status')
if (status == 1) {
// 进行支付
// 获取订单id
order_id = $(this).attr('order_id')
csrf = $('input[name="csrfmiddlewaretoken"]').val() // 获取csrf的value
// 组织参数
params = {"order_id": order_id, "csrfmiddlewaretoken": csrf}
// 发起ajax post请求,访问/order/pay,传递参数:order_id
$.post('/order/pay', params, function (data) {
if (data.res == 3) {
// 引导用户到支付页面
window.open(data.pay_url)
} else {
alert(data.errmsg)
}
})
} else {
// 其他情况
}
})
</script>
七. 完成支付操作
1. 点击去付款
2. 登录沙箱账号中买家账号
3. 完成支付
八. 订单支付-获取支付结果
1. 定义查看结果视图类
# order /views.py
# ajax post
# 前端传递的参数: 订单id(order_id)
# /order/check
class CheckPayView(View):
"""查看订单支付的结果"""
def post(self, request):
"""查询支付结果"""
# 用户是否登录
user = request.user
if not user.is_authenticated:
return JsonResponse({"res": 0, "errmsg": "用户未登录"})
# 接收参数
order_id = request.POST.get("order_id")
# 校验参数
if not order_id:
return JsonResponse({"res": 1, "errmsg": "无效的订单id"})
try:
order = OrderInfo.objects.get(order_id=order_id,
user=user,
pay_method=3,
order_status=1)
except OrderInfo.DoesNotExist:
return JsonResponse({"res": 2, "errmsg": "订单错误"})
# 业务处理: 使用python skd调用支付宝的支付接口
# 初始化
alipay = AliPay(
appid="xxxxxxxx", # 沙箱应用里的APPID
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(settings.BASE_DIR, 'apps/order/app_private_key.pem'), # 私钥
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
alipay_public_key_path=os.path.join(settings.BASE_DIR, 'apps/order/alipay_public_key.pem'),
sign_type='RSA2', # RSA 或者 RSA2
debug=True # 默认False,沙箱改为True
)
# 调用支付宝的交易查询接口
while True:
# 一定要将 alipay.api_alipay_trade_query 中的沙箱 url 改成新版的支付宝沙箱url,否则会报错
response = alipay.api_alipay_trade_query(order_id)
# 返回的字典格式
# response = {
# "trade_no": "2017032121001004070200176844", # 支付宝交易号
# "code": "10000", # 接口调用是否成功
# "invoice_amount": "20.00",
# "open_id": "20880072506750308812798160715407",
# "fund_bill_list": [
# {
# "amount": "20.00",
# "fund_channel": "ALIPAYACCOUNT"
# }
# ],
# "buyer_logon_id": "csq***@sandbox.com",
# "send_pay_date": "2017-03-21 13:29:17",
# "receipt_amount": "20.00",
# "out_trade_no": "out_trade_no15",
# "buyer_pay_amount": "20.00",
# "buyer_user_id": "2088102169481075",
# "msg": "Success",
# "point_amount": "0.00",
# "trade_status": "TRADE_SUCCESS", # 支付结果
# "total_amount": "20.00"
# }
code = response.get('code')
if code == '10000' and response.get('trade_status') == 'TRADE_SUCCESS':
# 支付成功
# 获取支付宝交易号
trade_no = response.get('trade_no')
# 更新订单状态
order.trade_no = trade_no
order.order_status = 4 # 待评价
order.save()
# 返回结果
return JsonResponse({"res": 3, "message": "支付成功"})
elif code == '40004' or (code == '10000' and response.get('trade_status') == 'WAIT_BUYER_PAY'):
# 等待买家付款
# 40004: 业务处理失败,可能一会就会成功
import time
time.sleep(5)
continue
else:
# 支付错误
return JsonResponse({"res": 4, "message": "支付失败"})
2. 定义路由
from django.conf.urls import url
from order.views import OrderPlaceView,OrderCommitView,OrderPayView,CheckPayView
app_name="order"
urlpatterns = [
...
url('^check$',CheckPayView.as_view(),name='check'), # 查询支付交易结果
]
3. 前端js显示支付成功
user_center_order.html
<script>
// 更改 状态
$('.oper_btn').each(function () {
// 获取支付状态
status = $(this).attr('status')
if (status == 1) {
$(this).text('去支付')
} else if (status == 4) {
$(this).text('去评价')
} else if (status == 5) {
$(this).text('已完成')
}
})
$('.oper_btn').click(function () {
// 获取status
status = $(this).attr('status')
// 获取订单id
order_id = $(this).attr('order_id')
if (status == 1) {
// 进行支付
csrf = $('input[name="csrfmiddlewaretoken"]').val() // 获取csrf的value
// 组织参数
params = {"order_id": order_id, "csrfmiddlewaretoken": csrf}
// 发起ajax post请求,访问/order/pay,传递参数:order_id
$.post('/order/pay', params, function (data) {
if (data.res == 3) {
// 引导用户到支付页面
window.open(data.pay_url)
// 浏览器访问/order/check, 获取支付交易的结果
// ajax post 传递参数: order_id
$.post('/order/check', params, function (data) {
if (data.res == 3) {
alert("支付成功")
// 刷新页面
location.reload()
} else {
alert(data.errmsg)
}
})
} else {
alert(data.errmsg)
}
})
} else if (status == 4) {
// 其他情况
// 跳转到评价页面
location.href = '/order/comment/' + order_id
}
})
</script>
九. 订单评论
1. 定义视图类
# order / views.py
class CommentView(View):
"""订单评论"""
def get(self, request, order_id):
"""提供订单评论页面"""
user = request.user
# 校验数据
if not order_id:
return redirect(reverse('user:order'))
try:
order = OrderInfo.objects.get(order_id=order_id, user=user)
except OrderInfo.DoesNotExist:
return redirect(reverse('user:order'))
# 根据订单的状态获取订单的状态标题
order.status_name = OrderInfo.ORDER_STATUS[order.order_status]
# 获取订单商品的信息
order_skus = OrderGoods.objects.filter(order_id=order_id)
for order_sku in order_skus:
# 计算商品小计
amount = order_sku.count * order_sku.price
# 动态给order_sku增加属性amount,保存商品小计
order_sku.amount = amount
# 动态给order增加属性order_skus,保存订单商品信息
order.order_skus = order_skus
# 使用模板
return render(request, 'order_comment.html', {"order": order})
def post(self, request, order_id):
"""处理评论内容"""
user = request.user
# 校验数据
if not order_id:
return redirect(reverse('user:order'))
try:
order = OrderInfo.objects.get(order_id=order_id, user=user)
except OrderInfo.DoesNotExist:
return redirect(reverse('user:order'))
# 获取评论条数
total_count = request.POST.get('total_count')
total_count = int(total_count)
# 循环获取订单中商品的评论内容
for i in range(1, total_count + 1):
# 获取评论的商品id
sku_id = request.POST.get('sku_%d' % i) # sku_1 sku_2 ...
# 获取评论的商品的内容
content = request.POST.get('content_%d' % i, '') # cotent_1 cotent_2
try:
order_goods = OrderGoods.objects.get(order=order, sku_id=sku_id)
except OrderGoods.DoesNotExist:
continue
order_goods.comment = content
order_goods.save()
order.order_status = 5 # 已完成
order.save()
return redirect(reverse('user:order', kwargs={"page": 1}))
2. 定义路由
from django.conf.urls import url
from order.views import OrderPlaceView, OrderCommitView, OrderPayView, CheckPayView,CommentView
app_name = "order"
urlpatterns = [
...
url('^comment/(?P<order_id>.+)$', CommentView.as_view(), name='comment'), # 订单评论
]
3. 定义模板
准备中...