zoukankan      html  css  js  c++  java
  • 支付宝支付功能, 创建订单并生成支付链接的接口, 后端支付宝异步回调接口

    支付宝支付功能

    1. 阅读支付宝开放平台的电脑网站支付文档

      1. 支付宝服务器同步回调一次结果给前端
      2. 支付宝服务器最多异步回调8次结果给后端, 如果后端返回success, 则支付宝服务器结束异步回调
    2. 在github上搜索alipay, 选择星最多的sdk, 然后安装: pip install python-alipay-sdk --upgrade

    3. 下载支付宝官方提供的一键生成 RSA 密钥工具, 生成应用公钥和应用私钥, 将应用公钥添加到支付宝开放平台, 然后获取支付宝公钥

    4. 二次封装网页支付sdk

      '''
      # ...luffyapiluffyapilibsalipayweb_pay.py
      from alipay import AliPay
      from .settings import *
      
      alipay = AliPay(
          # 真实appid则debug为False, 沙箱appid则debug为True
          appid=APP_ID,
          debug=DEBUG,
      
          app_notify_url=None,
          app_private_key_string=APP_PRIVATE_KEY_STRING,
          alipay_public_key_string=ALIPAY_PUBLIC_KEY_STRING,
          sign_type=SIGN,
      )
      
      
      # ...luffyapiluffyapilibsalipay\__init__.py
      from .web_pay import alipay
      from .settings import GATEWAY as alipay_gateway  # 支付宝网关接口
      '''
      

    创建订单并生成支付链接的接口

    '''
    # ...luffyapiluffyapiappsorderviews.py
    ...
    from rest_framework.generics import CreateAPIView
    from rest_framework.permissions import IsAuthenticated
    
    
    class OrderCreateAPIView(CreateAPIView):
        permission_classes = [IsAuthenticated]  # 设置登录后才能购买课程
        serializer_class = order_serializers.OrderModelSerializer
    
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data, context={'request': request})  # 将request对象传入OrderModelSerializer类中
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            return Response(serializer.pay_url)
            
    
    # ...luffyapiluffyapiappsorderorder_serializers.py
    from rest_framework import serializers
    from . import models
    from ..course.models import Course
    
    
    class OrderModelSerializer(serializers.ModelSerializer):
        courses = serializers.PrimaryKeyRelatedField(required=True, queryset=Course.objects.all(), many=True)  # 自定义反序列化字段
    
        class Meta:
            model = models.Order
            fields = ['subject', 'total_amount', 'pay_type', 'courses']
            ...
    
        def _check_total_amount(self, attrs):
            total_amount = attrs.get('total_amount')  # 获取前端传过来的订单总价
    
            # 根据订单中的课程信息统计出实际的订单总价
            total_amount_temp = 0
            courses = attrs.get('courses')
            for course in courses:
                total_amount_temp += course.price
    
            # 将前端传过来的订单总价与实际的订单总价进行比对
            if total_amount != total_amount_temp:
                raise serializers.ValidationError({'total_amount': '价格异常'})
            return total_amount
    
        def _get_out_trade_no(self):
            import time
            temp_no = '%.7f' % time.time()
            out_trade_no = temp_no.replace('.', '')
            return out_trade_no[-13: -1]
    
        # 从传入的request对象中获取用户对象
        def _get_request_user(self):
            return self.context.get('request').user
    
        def _get_pay_url(self, out_trade_no, total_amount, subject):
            from luffyapi.libs.alipay import alipay, alipay_gateway
            from django.conf import settings
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=out_trade_no,
                total_amount=str(total_amount),
                subject=subject,
                return_url=settings.RETURN_URL,  # 同步回调的前端接口
                notify_url=settings.NOTIFY_URL  # 异步回调的后端接口
            )
            return alipay_gateway + order_string
    
    	def validate(self, attrs):
            total_amount = self._check_total_amount(attrs)  # 校验订单总价
            out_trade_no = self._get_out_trade_no()  # 生成订单号
            user = self._get_request_user()  # 获取下单用户
    
            pay_url = self._get_pay_url(out_trade_no, total_amount, attrs.get('subject'))  # 生成支付链接
            self.pay_url = pay_url  # 将支付链接绑定给OrderModelSerializer类的pay_url属性
    
            # 在订单表中创建新的订单记录时所需要的额外字段数据
            attrs['out_trade_no'] = out_trade_no
            attrs['user'] = user
            return attrs
            
    	重写create方法: 1. 在订单表中创建新的订单记录, 2. 在订单详情表中创建新的订单详情记录
        def create(self, validated_data):
            courses = validated_data.pop('courses')  # 将订单中的课程信息取出额外记录到订单详情表中
            order_obj = models.Order.objects.create(**validated_data)  # 在订单表中创建新的订单记录
    
            # 在订单详情表中创建新订单详情记录
            for course in courses:
                models.OrderDetail.objects.create(order=order_obj, course=course, price=course.price, real_price=course.price)
            return order_obj
    '''
    

    后端支付宝异步回调接口

    '''
    # ...luffyapiluffyapiappsorderviews.py
    ...
    from luffyapi.libs.alipay import alipay
    from luffyapi.utils.my_logging import logger
    
    
    ...
    class PayResAPIView(APIView):
        # 对前端转发的支付宝同步回调数据作出响应
        def get(self, request, *args, **kwargs):
            return Response(data='received')
    
        # 处理支付宝异步回调传过来的数据
        def post(self, request, *args, **kwargs):
            data = request.data.dict()  # QueryDict类的对象没有pop方法, 可以通过".dict()"转化为dict类的对象
    
            # 验签
            sign = data.pop('sign')
            result = alipay.verify(data, sign)
    
            out_trade_no = data.get('out_trade_no')  # 获取订单号
            trade_status = data.get("trade_status")  # 获取交易状态
    
            # 根据验签结果和交易状态修改数据库中的订单状态
            if result and trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
                models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)
                logger.critical('订单号:%s, 交易状态: %s' % (out_trade_no, trade_status))  # 项目上线后, 没有控制台输出结果, 需要通过日志记录订单支付信息
                return Response('success')
            return Response('failed')
    '''
    

    其他需要注意的点

    1. drf-jwt的JWT_AUTH配置需要写在drf的REST_FRAMEWORK配置之前

    2. 前端携带token

      '''
                      this.$axios({
                          ...,
                          headers: {
                              authorization: `jwt ${token}`,
                          }
                      }).then(response => {
                          ...;
                      }).catch(error => {
                          ...;
                      })
      '''
      
    3. 前端非同站点页面跳转: window.open(url, '_self') , _self表示跳转时不新开标签页, 前端同站点页面跳转: this.$router.push(url)

    4. 前端使用 location.search 获取url中?以及?后的字符串

    5. 前端字符串裁剪: "cql".substring(1, 2) # q

    6. 前端异常处理语句: try {} catch (e) {}

    7. 前端对url编码数据进行解码: decodeURLComponet(...)

    8. 码云上配置的公钥私钥与电脑进行绑定, 支付宝开放平台配置的公钥私钥与项目应用进行绑定

  • 相关阅读:
    【BZOJ4826】【HNOI2017】影魔(扫描线,单调栈)
    【BZOJ4540】【HNOI2016】序列(莫队)
    【NOIP2017】列队(Splay)
    ZJOI2018酱油记
    【BZOJ4828】【HNOI2017】大佬(动态规划)
    【NOIP2017】宝藏(状态压缩,动态规划)
    【HDU4336】Card Collector (动态规划,数学期望)
    【HDU4652】Dice(数学期望,动态规划)
    【BZOJ4945】【NOI2017】游戏(搜索,2-sat)
    【BZOJ3714】Kuglarz(最小生成树)
  • 原文地址:https://www.cnblogs.com/-406454833/p/12709354.html
Copyright © 2011-2022 走看看