支付宝支付流程
1、接收前端发过来的贝里数和结算金额
2、检查贝里数是否够用
3、获取结算中心的课程并应用优惠券
4、应用未绑定课程的优惠券
5、判断总价格减去优惠券价格是否等于实际支付金额
6、生成订单
7、生成去支付宝支付的链接
支付宝支付详细流程
表结构
1 class Order(models.Model): 2 """订单""" 3 payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里')) 4 payment_type = models.SmallIntegerField(choices=payment_type_choices) 5 payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True) 6 order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True) # 考虑到订单合并支付的问题 7 user = models.ForeignKey("User") 8 actual_amount = models.FloatField(verbose_name="实付金额") 9 10 status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消')) 11 status = models.SmallIntegerField(choices=status_choices, verbose_name="状态") 12 date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间") 13 pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间") 14 cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间") 15 16 class Meta: 17 verbose_name_plural = "订单表" 18 19 def __str__(self): 20 return "%s" % self.order_number 21 22 class OrderDetail(models.Model): 23 """订单详情""" 24 order = models.ForeignKey("Order") 25 26 content_type = models.ForeignKey(ContentType) # 可关联普通课程或学位 27 object_id = models.PositiveIntegerField() 28 content_object = GenericForeignKey('content_type', 'object_id') 29 30 original_price = models.FloatField("课程原价") 31 price = models.FloatField("折后价格") 32 content = models.CharField(max_length=255, blank=True, null=True) # ? 33 valid_period_display = models.CharField("有效期显示", max_length=32) # 在订单页显示 34 valid_period = models.PositiveIntegerField("有效期(days)") # 课程有效期 35 memo = models.CharField(max_length=255, blank=True, null=True) 36 37 def __str__(self): 38 return "%s - %s - %s" % (self.order, self.content_type, self.price) 39 40 class Meta: 41 verbose_name_plural = "订单详细" 42 unique_together = ("order", 'content_type', 'object_id')
逻辑
默认-会校验用户是否登录,使用UserAuth 数据格式: { course: {1:{choice_price_id:1,coupon_id:2}, 2:{choise_price_id:4,coupon_record_id:3}}, global_coupon_id:3, beli:2000, total_money:2000 } 计算价格公式:(课程1原价格*课程1优惠券+课程2原价格*课程2优惠券)*通用优惠券-贝里/10 1.接收前端来的数据 获取用户的贝里数,课程的头像、名称、价格等信息,全局优惠券,总价格 2.校验数据 2.1校验贝里数是否在用户拥有的范围之内 2.2校验课程信息 2.2.1校验课程是否存在 2.2.2校验价格策略 2.2.3校验课程优惠券 2.2.4计算优惠后的价格-按照三种优惠券分为三种计算方式, 2.3校验通用优惠券的合法性 2.4校验最终价格是否一致 #防止别人篡改支付宝的支付信息,让我们受害!! 3.生成订单 Order记录 OrderDetail 4.调支付宝接口 利用支付宝的沙箱环境,使用公钥私钥一些知识,使用Alipay这个类中的逻辑,完成支付宝支付
#payment.py
from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin,ModelViewSet from app01.utils.auth_class import UserAuth from app01.utils.response import BaseResponse from rest_framework.response import Response from app01.utils.exceptions import CommonException from app01.models import Course,CouponRecord,PricePolicy,CourseDetail import datetime class PaymentView(APIView,ViewSetMixin): """ 支付接口===订单接口: 1 接收数据 2 校验数据 3 生成订单(默认未支付状态) --- Order --- OrderDetail --- OrderDetail --- OrderDetail 4 调支付宝的支付接口: 返回的post:修改订单,修改优惠券,修改贝里 返回的get: 查看订单状态 """ authentication_classes = [UserAuth] def create(self, request, *args, **kwargs): ''' 请求发送的数据: { courses:{ 1:{ choose_price_id:1, coupon_id:2, }, 2:{ choose_price_id:4, coupon_record_id:3, }, }, global_coupon_id:3, beli:2000, total_money:2000 } 计算价格优先级 (课程1原价格*课程1优惠券+课程2原价格*课程2优惠券)*通用优惠券-贝里/10 ''' res=BaseResponse() try: # 1 获取数据 user = request.user beli = int(request.data.get("beli", 0)) courses_dict=request.data.get("courses") global_coupon_id=request.data.get("global_coupon_id") total_money=request.data.get("total_money") # 2 校验数据 # 2.1 校验内里数是否在登录用户实际拥有范围内 if user.beli < beli: raise CommonException("贝里数有问题!",1004) # 2.2 校验课程信息 now = datetime.datetime.now() course_price_list=[] for course_pk,course_info in courses_dict.items(): # 2.2.2 校验课程是否存在 course_obj=Course.objects.filter(pk=course_pk).first() if not course_obj: raise CommonException("课程不存在!",1002) if course_obj.status != 0: raise CommonException("课程未上线或者已下线!", 1005) # 2.2.3 校验价格策略 choose_price_id=course_info.get("choose_price_id") price_policy_all=course_obj.price_policy.all() if choose_price_id not in [obj.pk for obj in price_policy_all]: raise CommonException("价格策略错误!",1003) # 2.2.4 校验课程优惠券 coupon_record_id=course_info.get("coupon_record_id") coupon_record=CouponRecord.objects.filter(pk=coupon_record_id, user=user, status=0, coupon__valid_begin_date__lt=now, coupon__valid_end_date__gt=now, ).first() if not coupon_record: raise CommonException("优惠券有问题!", 1006) rel_course_obj=coupon_record.coupon.content_object if course_obj != rel_course_obj: raise CommonException("优惠券与课程不匹配!", 1007) # 计算优惠后的价格 price=PricePolicy.objects.filter(pk=choose_price_id).first().price rebate_price=self.cal_price(price,coupon_record) course_price_list.append(rebate_price) # 2.3 校验通用优惠券合法性 global_coupon_record = CouponRecord.objects.filter(pk=global_coupon_id, user=user, status=0, coupon__valid_begin_date__lt=now, coupon__valid_end_date__gt=now, ).first() if not global_coupon_record: raise CommonException("通用优惠券有问题!", 1009) # 2.4 校验最终价格是否一致 cal_price = self.cal_price(sum(course_price_list), global_coupon_record) final_price = cal_price - beli / 10 if final_price < 0: final_price = 0 if total_money != final_price : raise CommonException("支付价格有问题!", 1010) # 3 生成订单 # Order记录 # OrderDetail # OrderDetail # 4 调用支付宝接口 # alipay = ali() # 生成支付的url # query_params = alipay.direct_pay( # subject="Django课程", # 商品简单描述 # out_trade_no="x2" + str(time.time()), # 商户订单号 # total_amount=money, # 交易金额(单位: 元 保留俩位小数) # ) # # pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params) # # return redirect(pay_url) # 注意: # POST请求访问notify_url: # 更改订单 # 更改优惠券 # 更改贝里数 # GET请求return_url,用于页面的跳转展示 except CommonException as e: res.code=1004 res.error=e.error return Response(res.dict) def cal_price(self,price,coupon_record): """ price:原价格 coupon_record:优惠券对象 目的:计算优惠后的价格 :param price: :param coupon_record: :return: """ # 获取优惠券的类型 coupon_type=coupon_record.coupon.coupon_type if coupon_type == 0: # 立减券 money_equivalent_value=coupon_record.coupon.money_equivalent_value rebate_price=price - money_equivalent_value if rebate_price < 0 : rebate_price=0 elif coupon_type == 1: # 满减券 minimum_consume=coupon_record.coupon.minimum_consume if price > minimum_consume: money_equivalent_value = coupon_record.coupon.money_equivalent_value rebate_price=price-money_equivalent_value else: raise CommonException("优惠券不符合条件",1008) elif coupon_type == 2: off_percent=coupon_record.coupon.off_percent rebate_price=price*(off_percent/100) else: rebate_price=price return rebate_price
支付逻辑
from django.shortcuts import render, redirect, HttpResponse from utils.pay import AliPay import json import time def ali(): # 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info app_id = "2016091100486897" # POST请求,用于最后的检测 notify_url = "http://47.94.172.250:8804/page2/" # notify_url = "http://www.wupeiqi.com:8804/page2/" # GET请求,用于页面的跳转展示 return_url = "http://47.94.172.250:8804/page2/" # return_url = "http://www.wupeiqi.com:8804/page2/" merchant_private_key_path = "keys/app_private_2048.txt" alipay_public_key_path = "keys/alipay_public_2048.txt" alipay = AliPay( appid=app_id, app_notify_url=notify_url, return_url=return_url, app_private_key_path=merchant_private_key_path, alipay_public_key_path=alipay_public_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥 debug=True, # 默认False, ) return alipay def page1(request): if request.method == "GET": return render(request, 'page1.html') else: money = float(request.POST.get('money')) alipay = ali() # 生成支付的url query_params = alipay.direct_pay( subject="Django课程", # 商品简单描述 out_trade_no="x2" + str(time.time()), # 商户订单号 total_amount=money, # 交易金额(单位: 元 保留俩位小数) ) pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params) return redirect(pay_url) def page2(request): alipay = ali() if request.method == "POST": # 检测是否支付成功 # 去请求体中获取所有返回的数据:状态/订单号 from urllib.parse import parse_qs body_str = request.body.decode('utf-8') post_data = parse_qs(body_str) post_dict = {} for k, v in post_data.items(): post_dict[k] = v[0] print(post_dict) sign = post_dict.pop('sign', None) status = alipay.verify(post_dict, sign) print('POST验证', status) if status: # 修改订单状态 pass return HttpResponse('POST返回') else: params = request.GET.dict() sign = params.pop('sign', None) status = alipay.verify(params, sign) print('GET验证', status) if status: # 获取订单状态,显示给用户 return HttpResponse('支付成功')