• 支付宝第三方支付


    支付宝支付第三方接口

    # 沙箱环境:https://openhome.alipay.com/platform/appDaily.htm
    
    # https://opendocs.alipay.com/open/291/106097/ 下载软件,生成秘钥(公钥私钥)
    # 取出公钥,配置再支付宝平台,它会生成一个支付宝公钥
    
    
    
    
    pip install python-alipay-sdk
    
    
    from alipay import AliPay
    
    
    # app_private_key_string = open("/path/to/your/private/key.pem").read()
    # alipay_public_key_string = open("/path/to/alipay/public/key.pem").read()
    
    app_private_key_string = """-----BEGIN RSA PRIVATE KEY-----
    私钥
    -----END RSA PRIVATE KEY-----
    """
    
    alipay_public_key_string = """-----BEGIN PUBLIC KEY-----
    支付宝公钥
    -----END PUBLIC KEY-----
    """
    
    alipay = AliPay(
        appid="2016092000554611",
        app_notify_url='http://127.0.0.1:8000/home/',  # the default notify path
        app_private_key_string=app_private_key_string,
        # alipay public key, do not use your own public key!
        alipay_public_key_string=alipay_public_key_string,
        sign_type="RSA2", # RSA or RSA2
        debug=True  # False by default
    )
    
    
    alipay_url='https://openapi.alipaydev.com/gateway.do?'
    order_string = alipay.api_alipay_trade_page_pay    (
        out_trade_no="20161112www4334",
        total_amount=9999,
        subject='娃娃',
        return_url="https://www.luffycity.com/free-course",
        notify_url="https://www.luffycity.com/free-course"
    )
    print(alipay_url+order_string)
    

    支付宝支付流程

    支付宝二次封装

    把变量值从配置文件中获取,只要修改配置文件就可以改变参数

    目录结构 libs中,比如,支付宝付款,发送验证码
    al_pay
        -pem
        -__init__.py
        -pay.py
        -setting.py
        
    #__init__.py
    from .pay import alipay,gateway
    
    #pay.py
    
    from alipay import AliPay
    from . import setting
    alipay = AliPay(
        appid=setting.APPID,
        app_notify_url=None,  # the default notify path
        app_private_key_string=setting.APP_PRIVATE_KEY_STRING,
        # alipay public key, do not use your own public key!
        alipay_public_key_string=setting.ALIPAY_PUBLIC_KEY_STRING,
        sign_type=setting.SIGN_TYPE, # RSA or RSA2
        debug=setting.DEBUG  # False by default
    )
    gateway=setting.GATEWAY
    
    # setting.py
    import os
    APPID="2016092000554611"
    APP_PRIVATE_KEY_STRING = open(os.path.join(os.path.dirname(__file__),'pem','private_key.pem')).read()
    ALIPAY_PUBLIC_KEY_STRING = open(os.path.join(os.path.dirname(__file__),'pem','al_public_key.pem')).read()
    SIGN_TYPE='RSA2'
    DEBUG=True
    GATEWAY='https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
    

    订单模块与表分析

    # 订单表分析
    	-订单表
        -订单详情
        
    from django.db import models
    from django.db import models
    from user.models import User
    from course.models import Course
    # 订单表
    class Order(models.Model):
        """订单模型"""
        status_choices = (
            (0, '未支付'),
            (1, '已支付'),
            (2, '已取消'),
            (3, '超时取消'),
        )
        pay_choices = (
            (1, '支付宝'),
            (2, '微信支付'),
        )
        subject = models.CharField(max_length=150, verbose_name="订单标题")
        total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="订单总价", default=0)
        out_trade_no = models.CharField(max_length=64, verbose_name="订单号", unique=True)
        trade_no = models.CharField(max_length=64, null=True, verbose_name="流水号")  # 支付宝生成回来的
        order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")
        pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="支付方式")
        pay_time = models.DateTimeField(null=True, verbose_name="支付时间")
        # 一个用户可以下多个订单,一个订单只属于一个用户,一对多的关系,关联字段写在多个一方,写在order方
        user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name="下单用户")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
        updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
        class Meta:
            db_table = "luffy_order"
            verbose_name = "订单记录"
            verbose_name_plural = "订单记录"
    
        def __str__(self):
            return "%s - ¥%s" % (self.subject, self.total_amount)
    
        @property
        def courses(self):
            data_list = []
            for item in self.order_courses.all():
                data_list.append({
                    "id": item.id,
                    "course_name": item.course.name,
                    "real_price": item.real_price,
                })
            return data_list
    
    # 订单详情表
    # 订单和详情是一对多,关联字段写在多个的一方,写在订单详情表中
    class OrderDetail(models.Model):
        """订单详情"""
        order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, db_constraint=False, verbose_name="订单")
        course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.SET_NULL, db_constraint=False, verbose_name="课程",null=True)
        price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
        real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")
    
        class Meta:
            db_table = "luffy_order_detail"
            verbose_name = "订单详情"
            verbose_name_plural = "订单详情"
    
        def __str__(self):
            try:
                return "%s的订单:%s" % (self.course.name, self.order.out_trade_no)
            except:
                return super().__str__()
    

    支付宝支付逻辑

    订单模块接口分析

    # 1 支付接口(生成订单,,生成支付连接,返回支付连接)
    	-order表和orderdetail表插入数据,重写create方法
        -生成订单号(uuid)
        -登录后才能做(jwt认证)
        -当前登录用户就是下单用户,存到order表中
        -下了三个课程,总价格100,前端提交的价格是99,异常处理
         '''
            # 1)订单总价校验
            # 2)生成订单号
            # 3)支付用户:request.user
            # 4)支付链接生成
            # 5)入库(两个表)的信息准备
        '''
    # 2 支付宝异步回调的post接口(验证签名,修改订单状态)
    # 3 当支付宝get回调前端,vue组件一创建,立马向后端发一个请求(get)
    
    序列化器
    更新订单表时,也将订单信息表添加数据。
    登录状态,价格核对,生成订单号,返回支付链接,context的使用,request传入,支付链接传出。
    
    class OrderSerializer(serializers.ModelSerializer):
        # 前端传什么数据过来{course:[1,2,3],total_amount:100,subject:xx商品,pay_type:1,}
        # user字段需要,但是不是传的,使用了jwt
    
    
        # 需要把course:[1,2,3] 处理成 course:[obj1,obj2,obj3]
    
        # 课时:[1,4,6,]===>课时:[obj1,obj4,obj6,]
        # course=serializers.CharField()
        course=serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)
    
        class Meta:
            model = models.Order
            fields = ['total_amount','subject','pay_type','course']
            extra_kwargs={
                'total_amount':{'required':True},
                'pay_type': {'required': True},
            }
    
    
        def _check_price(self,attrs):
            total_amount=attrs.get('total_amount')
            course_list=attrs.get('course')
            total_price=0
            for course in course_list:
                total_price+=course.price
            if total_price!=total_amount:
                raise ValidationError('价格不合法')
            return total_amount
    
        def _gen_out_trade_no(self):
            import uuid
            return str(uuid.uuid4()).replace('-','')
    
        def _get_user(self):
            # 需要request对象(需要视图通过context把reuqest对象传入。重写create方法)
            request=self.context.get('request')
            return request.user
    
        def _gen_pay_url(self,out_trade_no,total_amout,subject):
            # total_amout是Decimal类型,识别不了,需要转换成float类型
            from luffyapi.libs.al_pay import alipay,gateway
            order_string = alipay.api_alipay_trade_page_pay    (
                out_trade_no=out_trade_no,
                total_amount=float(total_amout),
                subject=subject,
                return_url=settings.RETURN_URL,  # get回调,前台地址
                notify_url=settings.NOTIFY_URL   # post回调,后台地址
            )
            return gateway+order_string
    
        def _before_create(self,attrs,user,pay_url,out_trade_no):
            attrs['user']=user
            attrs['out_trade_no']=out_trade_no
    
            self.context['pay_url']=pay_url
        def validate(self, attrs):
            '''
            # 1)订单总价校验
            # 2)生成订单号
            # 3)支付用户:request.user
            # 4)支付链接生成
            # 5)入库(两个表)的信息准备
            '''
            # 1)订单总价校验
            total_amout = self._check_price(attrs)
            # 2)生成订单号
            out_trade_no=self._gen_out_trade_no()
            # 3)支付用户:request.user
            user=self._get_user()
            # 4)支付链接生成
            pay_url=self._gen_pay_url(out_trade_no,total_amout,attrs.get('subject'))
            # 5)入库(两个表)的信息准备
            self._before_create(attrs,user,pay_url,out_trade_no)
            return attrs
    
        def create(self, validated_data):
            course_list=validated_data.pop('course')
            order=models.Order.objects.create(**validated_data)
            for course in course_list:
                models.OrderDetail.objects.create(order=order,course=course,price=course.price,real_price=course.price)
    
            return order
        
    
    视图类
    前端get请求查看后台,获得想新的订单状态。
    异步post请求,验证签名,验证登录状态,验证订单支付状态,然后,修改库中订单的状态。
    class PayView(GenericViewSet,CreateModelMixin):
        authentication_classes = [JSONWebTokenAuthentication,]
        permission_classes = [IsAuthenticated,]
        queryset = models.Order.objects.all()
        serializer_class = serializer.OrderSerializer
    
        # 重写create方法
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data,context={'request':request})
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            return Response(serializer.context.get('pay_url'))
    
    
    class SuccessView(APIView):
        def get(self,request,*args,**kwargs):
            out_trade_no=request.query_params.get('out_trade_no')
            order=models.Order.objects.filter(out_trade_no=out_trade_no).first()
            if order.order_status==1:
                return Response(True)
            else:
                return Response(False)
    
        def post(self,request,*args,**kwargs):
            '''
            支付宝回调接口
            '''
            from luffyapi.libs.al_pay import alipay
            from luffyapi.utils.logger import log
            data = request.data
            out_trade_no=data.get('out_trade_no',None)
            gmt_payment=data.get('gmt_payment',None)
            signature = data.pop("sign")
            # 验证签名
            success = alipay.verify(data, signature)
            if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
                models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1,pay_time=gmt_payment)
                log.info('%s订单支付成功'%out_trade_no)
                return Response('success')
            else:
                log.info('%s订单有问题' % out_trade_no)
                return Response('error')
    

    支付接口

    # 1 前端传什么格式数据
    	-{course:[1,2,3],total_amount:100,subject:xx商品,pay_type:1,}
        
    # 2 后端接到数据要校验
    	-course:[1,2,3]===》course:[obj1,obj2,obj3]
        -在序列化类中: 					                  course=serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)
        
    # 3 在序列化类的validate中处理一堆逻辑
            '''
            # 1)订单总价校验:
            	-一个个课程价格取出来累加看是否等于传入的总价格
            # 2)生成订单号
            	-通过uuid生成
            # 3)支付用户:request.user
            	-通过视图和序列化类之间的桥梁context对象传递
            	-重新视图类中的create方法,把request对象放入context
            	-self.context.get('request').user
            # 4)支付链接生成
            	-导入封装的支付宝支付,生成
            	order_string = alipay.api_alipay_trade_page_pay    (
                out_trade_no=out_trade_no,
                total_amount=total_amout,
                subject=subject,
                return_url=settings.RETURN_URL,  # get回调,前台地址
                notify_url=settings.NOTIFY_URL   # post回调,后台地址
            )
            # 5)入库(两个表)的信息准备
            	-把user放入attrs中
            	-把订单号,放入attrs中
            	attrs['user']=user
            	attrs['out_trade_no']=out_trade_no
            	self.context['pay_url']=pay_url
            '''
        
     # 4 重写序列化类的create方法
    	-把course_list弹出来
        -order=models.Order.objects.create(**validated_data)
    
                        let pay_url=response.data
                        //前端发送get请求
                        open(pay_url,'_self')
    

    前台支付成功页面

    <script>
        import Header from "@/components/Head"
    
        export default {
            name: "Success",
            data() {
                return {
                    result: {},
                };
            },
            created() {
                // url后拼接的参数:?及后面的所有参数 => ?a=1&b=2
                // console.log(location.search);
                // 解析支付宝回调的url参数
                let params = location.search.substring(1);  // 去除? => a=1&b=2
                let items = params.length ? params.split('&') : [];  // ['a=1', 'b=2']
                //逐个将每一项添加到args对象中
                for (let i = 0; i < items.length; i++) {  // 第一次循环a=1,第二次b=2
                    let k_v = items[i].split('=');  // ['a', '1']
                    //解码操作,因为查询字符串经过编码的
                    if (k_v.length >= 2) {
                        // url编码反解
                        let k = decodeURIComponent(k_v[0]);
                        this.result[k] = decodeURIComponent(k_v[1]);
                        // 没有url编码反解
                        // this.result[k_v[0]] = k_v[1];
                    }
    
                }
                // 解析后的结果
                // console.log(this.result);
    
    
                // 把地址栏上面的支付结果,再get请求转发给后端
                this.$axios({
                    url: this.$settings.base_url + '/order/success/' + location.search,
                    method: 'get',
                }).then(response => {
                    console.log(response.data);
                }).catch(() => {
                    console.log('支付结果同步失败');
                })
            },
            components: {
                Header,
            }
        }
    </script>
    

    支付宝get回调参数

    charset=utf-8&
    out_trade_no=17fcf8cac17c442bbbc114df6004afff&
    method=alipay.trade.page.pay.return&
    total_amount=99.00&
    sign=WfK2beWFKvVaTHXpREi8HqZtFRH3JbeIvkliReYvfuhAsqaxHguARKtW6jUqUdZinm7ZSaYE1NrBRQa3%2BLquMk6uMnxE0i%2FTXIu4%2FmNTCEqSUlG8fTRPwC2%2BuU4nN1Ym0eM4puzAc2TUnEJnXCGKP9UxMifN3cjqR5BP%2B3RRngZSS4IQeogjurpfdiIolLzed%2FHTWbc4HqvWlWn9JuLmFGTtKHvRRKFr1hqq8Pj%2Fe3Al8kieDN9Q7JhEdC6F5ROo9rLlmUJtevkjI22oRScrfJl5hb%2BeYosxNg3WktmYKlF5vsKeZKKnLayAvKGoySLvaWk90x0LijHzzf2%2F8a9s3w%3D%3D&
    trade_no=2020072922001480160500851368&
    auth_app_id=2016092000554611&
    version=1.0&
    app_id=2016092000554611&
    sign_type=RSA2&
    seller_id=2088102176466324&
    timestamp=2020-07-29%2015%3A06%3A44
    
  • 相关阅读:
    CSS 仿 iOS 系统通知数字样式
    Asp.Net Grieview Eval 绑定数据 调用JS事件
    C#:decimal的去0显示
    WebService 错误:无法加载协定为xxx的终结点配置部分,因为找到了该协定的多个终结点配置
    C#:调用webservice时提示对操作的回复消息正文进行反序列化时出错
    C#:生成短网址
    C#:优惠券代码
    C# 调用webservice 几种办法(转载)
    ASP.NET: 正在中止线程 错误原及解决方法
    IOS:Safari不兼容Javascript中的Date问题
  • 原文地址:https://www.cnblogs.com/pythonwl/p/13394150.html
Copyright © 2020-2023  润新知