提示:
- 如果用户选择的支付方式是 "支付宝" ,在点击《去支付》时对接支付宝的支付系统。
支付宝开放平台入口
1.创建应用
2.沙箱环境
支付宝提供给开发者的模拟支付的环境。跟真实环境是分开的。
沙箱应用:https://openhome.alipay.com/platform/appDaily.htm?tab=info
沙箱账号:https://openhome.alipay.com/platform/appDaily.htm?tab=account
pip install python-alipay-sdk --upgrade
提示:
- 美多商城私钥加密数据,美多商城公钥解密数据。
- 支付宝私钥加密数据,支付宝公钥解密数据。
1.生成美多商城公私钥
$ openssl
$ OpenSSL> genrsa -out app_private_key.pem 2048 # 制作私钥RSA2
$ OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
$ OpenSSL> exit
2.配置美多商城公私钥
配置美多商城私钥
payment
,在该子应用下新建文件夹keys
用于存储公私钥。app_private_key.pem
拷贝到keys
文件夹中。配置美多商城公钥
将payment.keys.app_public_key.pem
文件中内容上传到支付宝。
3.配置支付宝公钥
将支付宝公钥内容拷贝到payment.keys.alipay_public_key.pem
文件中。
-----BEGIN PUBLIC KEY-----
支付宝公钥内容
-----END PUBLIC KEY-----
配置公私钥结束后
========================================================
提示:
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /payment/(?P |
2.请求参数:路径参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
alipay_url | 支付宝登录链接 |
4.后端接口定义和实现
# 测试账号:[email protected]
class PaymentView(LoginRequiredJSONMixin, View):
"""订单支付功能"""
def get(self,request, order_id):
# 查询要支付的订单
user = request.user
try:
order = OrderInfo.objects.get(order_id=order_id, user=user, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'])
except OrderInfo.DoesNotExist:
return http.HttpResponseForbidden('订单信息错误')
# 创建支付宝支付对象
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
sign_type="RSA2",
debug=settings.ALIPAY_DEBUG
)
# 生成登录支付宝连接
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=settings.ALIPAY_RETURN_URL,
)
# 响应登录支付宝连接
# 真实环境电脑网站支付网关:https://openapi.alipay.com/gateway.do? + order_string
# 沙箱环境电脑网站支付网关:https://openapi.alipaydev.com/gateway.do? + order_string
alipay_url = settings.ALIPAY_URL + "?" + order_string
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'alipay_url': alipay_url})
5.支付宝SDK配置参数
ALIPAY_APPID = '2016082100308405'
ALIPAY_DEBUG = True
ALIPAY_URL = 'https://openapi.alipaydev.com/gateway.do'
ALIPAY_RETURN_URL = 'http://www.meiduo.site:8000/payment/status/'
=================================
用户订单支付成功后,支付宝会将用户重定向到 http://www.meiduo.site:8000/payment/status/
,并携带支付结果数据。
参考统一收单下单并支付页面接口:https://docs.open.alipay.com/270/alipay.trade.page.pay
提示:
我们需要将
订单编号
和交易流水号
进行关联存储,方便用户和商家后续使用。
class Payment(BaseModel):
"""支付信息"""
order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE, verbose_name='订单')
trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付编号")
class Meta:
db_table = 'tb_payment'
verbose_name = '支付信息'
verbose_name_plural = verbose_name
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /payment/status/ |
2.请求参数:路径参数
参考统一收单下单并支付页面接口中的《页面回跳参数》
3.响应结果:HTML
pay_success.html
4.后端接口定义和实现
注意:保存订单支付结果的同时,还需要修改订单的状态为
待评价
# 测试账号:[email protected]
class PaymentStatusView(View):
"""保存订单支付结果"""
def get(self, request):
# 获取前端传入的请求参数
query_dict = request.GET
data = query_dict.dict()
# 获取并从请求参数中剔除signature
signature = data.pop('sign')
# 创建支付宝支付对象
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None,
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"),
sign_type="RSA2",
debug=settings.ALIPAY_DEBUG
)
# 校验这个重定向是否是alipay重定向过来的
success = alipay.verify(data, signature)
if success:
# 读取order_id
order_id = data.get('out_trade_no')
# 读取支付宝流水号
trade_id = data.get('trade_no')
# 保存Payment模型类数据
Payment.objects.create(
order_id=order_id,
trade_id=trade_id
)
# 修改订单状态为待评价
OrderInfo.objects.filter(order_id=order_id, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']).update(
status=OrderInfo.ORDER_STATUS_ENUM["UNCOMMENT"])
# 响应trade_id
context = {
'trade_id':trade_id
}
return render(request, 'pay_success.html', context)
else:
# 订单支付失败,重定向到我的订单
return http.HttpResponseForbidden('非法请求')
5.渲染支付成功页面信息
=============================================
提示:
点击《我的订单》页面中的《待评价》按钮,进入到订单商品评价页面。
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /orders/comment/ |
2.请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
3.响应结果:HTML
goods_judge.html
4.后端接口定义和实现
class OrderCommentView(LoginRequiredMixin, View):
"""订单商品评价"""
def get(self, request):
"""展示商品评价页面"""
# 接收参数
order_id = request.GET.get('order_id')
# 校验参数
try:
OrderInfo.objects.get(order_id=order_id, user=request.user)
except OrderInfo.DoesNotExist:
return http.HttpResponseNotFound('订单不存在')
# 查询订单中未被评价的商品信息
try:
uncomment_goods = OrderGoods.objects.filter(order_id=order_id, is_commented=False)
except Exception:
return http.HttpResponseServerError('订单商品信息出错')
# 构造待评价商品数据
uncomment_goods_list = []
for goods in uncomment_goods:
uncomment_goods_list.append({
'order_id':goods.order.order_id,
'sku_id':goods.sku.id,
'name':goods.sku.name,
'price':str(goods.price),
'default_image_url':goods.sku.default_image.url,
'comment':goods.comment,
'score':goods.score,
'is_anonymous':str(goods.is_anonymous),
})
# 渲染模板
context = {
'uncomment_goods_list': uncomment_goods_list
}
return render(request, 'goods_judge.html', context)
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /orders/comment/ |
2.请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
order_id | int | 是 | 订单编号 |
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
4.后端接口定义和实现
class OrderCommentView(LoginRequiredMixin, View):
"""订单商品评价"""
def get(self, request):
"""展示商品评价页面"""
......
def post(self, request):
"""评价订单商品"""
# 接收参数
json_dict = json.loads(request.body.decode())
order_id = json_dict.get('order_id')
sku_id = json_dict.get('sku_id')
score = json_dict.get('score')
comment = json_dict.get('comment')
is_anonymous = json_dict.get('is_anonymous')
# 校验参数
if not all([order_id, sku_id, score, comment]):
return http.HttpResponseForbidden('缺少必传参数')
try:
OrderInfo.objects.filter(order_id=order_id, user=request.user, status=OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT'])
except OrderInfo.DoesNotExist:
return http.HttpResponseForbidden('参数order_id错误')
try:
sku = SKU.objects.get(id=sku_id)
except SKU.DoesNotExist:
return http.HttpResponseForbidden('参数sku_id错误')
if is_anonymous:
if not isinstance(is_anonymous, bool):
return http.HttpResponseForbidden('参数is_anonymous错误')
# 保存订单商品评价数据
OrderGoods.objects.filter(order_id=order_id, sku_id=sku_id, is_commented=False).update(
comment=comment,
score=score,
is_anonymous=is_anonymous,
is_commented=True
)
# 累计评论数据
sku.comments += 1
sku.save()
sku.spu.comments += 1
sku.spu.save()
# 如果所有订单商品都已评价,则修改订单状态为已完成
if OrderGoods.objects.filter(order_id=order_id, is_commented=False).count() == 0:
OrderInfo.objects.filter(order_id=order_id).update(status=OrderInfo.ORDER_STATUS_ENUM['FINISHED'])
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '评价成功'})
===========================================
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /comments/(?P |
2.请求参数:查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
sku_id | int | 是 | 商品SKU编号 |
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
comment_list[ ] | 评价列表 |
username | 发表评价的用户 |
comment | 评价内容 |
score | 分数 |
{
"code":"0",
"errmsg":"OK",
"comment_list":[
{
"username":"itcast",
"comment":"这是一个好手机!",
"score":4
}
]
}
4.后端接口定义和实现
class GoodsCommentView(View):
"""订单商品评价信息"""
def get(self, request, sku_id):
# 获取被评价的订单商品信息
order_goods_list = OrderGoods.objects.filter(sku_id=sku_id, is_commented=True).order_by('-create_time')[:30]
# 序列化
comment_list = []
for order_goods in order_goods_list:
username = order_goods.order.user.username
comment_list.append({
'username': username[0] + '***' + username[-1] if order_goods.is_anonymous else username,
'comment':order_goods.comment,
'score':order_goods.score,
})
return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'OK', 'comment_list': comment_list})
5.渲染商品评价信息
-
[[comment.username]]
[[comment.comment]]
商品评价([[ comments.length ]])
提示:订单商品评价完成后,一个订单的流程就结束了,订单状态修改为
已完成
。