• Django REST framework+Vue 打造生鲜电商项目(笔记九)


    (from:http://www.cnblogs.com/derek1184405959/p/8859309.html)

    十二、支付宝沙箱环境配置

    12.1.创建应用

    进入蚂蚁金服开放平台(https://open.alipay.com/platform/home.htm),登录后进入管理中心-->>应用列表

     创建应用

     

     创建应用后会有一个appid。还需要提交信息进行审核。微信支付和支付宝支付都是要求企业认证才可以完成的,个人开发不可以,所以我们需要用

    沙箱环境,它可以让我们不具备这些应用或者说应用审核还没通过的时候先开发调试

    12.2.沙箱环境

    沙箱应用地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

    (1)公钥和私钥的生成方法

    地址:https://docs.open.alipay.com/291/105971/

    选winwods,如果linux就选linux

    下载好工具照着生成就可以了

     (2) 把生成的公钥和私钥拷贝到trade/keys下面--->>>重命名--->>首位各添加下面的内容

    -----BEGIN PRIVATE KEY-----
    -----END PRIVATE KEY-----

     

    (3)把支付宝公钥也拷贝到这路径下面,同样首尾添加

     

    12.3.文档说明

    我们主要用到电脑网站支付,文档地址:https://docs.open.alipay.com/270

    用到的API接口:统一收单下单并支付页面接口

    里面有文档说明

     

    几个比较重要的参数

     sign签名

     应该看未使用开放平台的SDK里面的说明:https://docs.open.alipay.com/291/106118

     请求参数

    必填的几个:

     

    12.4.编写代码

    把环境改成本地的

    首先的安装一个模块

    pip install pycryptodome

    utils中新建alipay.py,代码如下:

    # apps/utils.py
    
    import json
    from datetime import datetime
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from base64 import b64encode, b64decode
    from urllib.parse import quote_plus
    from urllib.parse import urlparse, parse_qs
    from urllib.request import urlopen
    from base64 import decodebytes, encodebytes
    
    
    class AliPay(object):
        """
        支付宝支付接口
        """
        def __init__(self, appid, app_notify_url, app_private_key_path,
                     alipay_public_key_path, return_url, debug=False):
            self.appid = appid
            self.app_notify_url = app_notify_url
            #私钥
            self.app_private_key_path = app_private_key_path
            self.app_private_key = None
            self.return_url = return_url
            with open(self.app_private_key_path) as fp:
                self.app_private_key = RSA.importKey(fp.read())
            #公钥
            self.alipay_public_key_path = alipay_public_key_path
            with open(self.alipay_public_key_path) as fp:
                self.alipay_public_key = RSA.import_key(fp.read())
    
    
            if debug is True:
                self.__gateway = "https://openapi.alipaydev.com/gateway.do"
            else:
                self.__gateway = "https://openapi.alipay.com/gateway.do"
    
        def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
            #请求参数
            biz_content = {
                "subject": subject,
                "out_trade_no": out_trade_no,
                "total_amount": total_amount,
                "product_code": "FAST_INSTANT_TRADE_PAY",
                # "qr_pay_mode":4
            }
            #允许传递更多参数,放到biz_content
            biz_content.update(kwargs)
            data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
            return self.sign_data(data)
    
        def build_body(self, method, biz_content, return_url=None):
            #build_body主要生产消息的格式
            #公共请求参数
            data = {
                "app_id": self.appid,
                "method": method,
                "charset": "utf-8",
                "sign_type": "RSA2",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0",
                "biz_content": biz_content
            }
    
            if return_url is not None:
                data["notify_url"] = self.app_notify_url
                data["return_url"] = self.return_url
    
            return data
    
        def sign_data(self, data):
            #签名
            data.pop("sign", None)
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            #排完序后拼接起来
            unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
            #这里得到签名的字符串
            sign = self.sign(unsigned_string.encode("utf-8"))
            #对url进行处理
            quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
    
            # 获得最终的订单信息字符串
            signed_string = quoted_string + "&sign=" + quote_plus(sign)
            return signed_string
    
        #参数传进来一定要排序
        def ordered_data(self, data):
            complex_keys = []
            for key, value in data.items():
                if isinstance(value, dict):
                    complex_keys.append(key)
    
            # 将字典类型的数据dump出来
            for key in complex_keys:
                data[key] = json.dumps(data[key], separators=(',', ':'))
    
            return sorted([(k, v) for k, v in data.items()])
    
        def sign(self, unsigned_string):
            # 开始计算签名
            key = self.app_private_key
            #签名的对象
            signer = PKCS1_v1_5.new(key)
            #生成签名
            signature = signer.sign(SHA256.new(unsigned_string))
            # base64 编码,转换为unicode表示并移除回车
            sign = encodebytes(signature).decode("utf8").replace("
    ", "")
            return sign
    
        def _verify(self, raw_content, signature):
            # 开始计算签名
            key = self.alipay_public_key
            signer = PKCS1_v1_5.new(key)
            digest = SHA256.new()
            digest.update(raw_content.encode("utf8"))
            if signer.verify(digest, decodebytes(signature.encode("utf8"))):
                return True
            return False
    
        def verify(self, data, signature):
            if "sign_type" in data:
                sign_type = data.pop("sign_type")
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
            return self._verify(message, signature)
    
    
    if __name__ == "__main__":
        return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
        o = urlparse(return_url)
        query = parse_qs(o.query)
        processed_query = {}
        ali_sign = query.pop("sign")[0]
    
    
    # 测试用例
        alipay = AliPay(
            # 沙箱里面的appid值
            appid="2016091500517456",
            #notify_url是异步的url
            app_notify_url="http://127.0.0.1:8000/",
            # 我们自己商户的密钥
            app_private_key_path="../trade/keys/private_2048.txt",
            # 支付宝的公钥
            alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            # debug为true时使用沙箱的url。如果不是用正式环境的url
            debug=True,  # 默认False,
            return_url="http://127.0.0.1:8000/alipay/return/"
        )
    
        for key, value in query.items():
            processed_query[key] = value[0]
        # print (alipay.verify(processed_query, ali_sign))
    
        # 直接支付:生成请求的字符串。
        url = alipay.direct_pay(
            # 订单标题
            subject="测试订单derek",
            # 我们商户自行生成的订单号
            out_trade_no="20180417derek",
            # 订单金额
            total_amount=100,
            #成功付款后跳转到的页面,return_url同步的url
            # return_url="http://127.0.0.1:8000/"
        )
        # 将生成的请求字符串拿到我们的url中进行拼接
        re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
        print(re_url)

    运行alipay.py,会生成一个支付链接url,点进去跳到支付页面

     点“登录账户付款”,用沙箱账号付款

     输入账号密码以及支付密码,成功付款

     12.5.django集成支付宝notify_url和return_url

    (1)配置url

    # 配置支付宝支付相关接口的url
    path('alipay/return/', AlipayView.as_view())

    (2)alipay.py

    把return_url和notify_url都改成远程服务器的地址

    return_url="http://47.93.198.159:8000/alipay/return/"
    
    app_notify_url="http://47.93.198.159:8000/alipay/return/"
    # apps/utils.py
    
    import json
    from datetime import datetime
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from base64 import b64encode, b64decode
    from urllib.parse import quote_plus
    from urllib.parse import urlparse, parse_qs
    from urllib.request import urlopen
    from base64 import decodebytes, encodebytes
    
    
    class AliPay(object):
        """
        支付宝支付接口
        """
        def __init__(self, appid, app_notify_url, app_private_key_path,
                     alipay_public_key_path, return_url, debug=False):
            self.appid = appid
            self.app_notify_url = app_notify_url
            #私钥
            self.app_private_key_path = app_private_key_path
            self.app_private_key = None
            self.return_url = return_url
            with open(self.app_private_key_path) as fp:
                self.app_private_key = RSA.importKey(fp.read())
            #公钥
            self.alipay_public_key_path = alipay_public_key_path
            with open(self.alipay_public_key_path) as fp:
                self.alipay_public_key = RSA.import_key(fp.read())
    
    
            if debug is True:
                self.__gateway = "https://openapi.alipaydev.com/gateway.do"
            else:
                self.__gateway = "https://openapi.alipay.com/gateway.do"
    
        def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
            #请求参数
            biz_content = {
                "subject": subject,
                "out_trade_no": out_trade_no,
                "total_amount": total_amount,
                "product_code": "FAST_INSTANT_TRADE_PAY",
                # "qr_pay_mode":4
            }
            #允许传递更多参数,放到biz_content
            biz_content.update(kwargs)
            data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
            return self.sign_data(data)
    
        def build_body(self, method, biz_content, return_url=None):
            #build_body主要生产消息的格式
            #公共请求参数
            data = {
                "app_id": self.appid,
                "method": method,
                "charset": "utf-8",
                "sign_type": "RSA2",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0",
                "biz_content": biz_content
            }
    
            if return_url is not None:
                data["notify_url"] = self.app_notify_url
                data["return_url"] = self.return_url
    
            return data
    
        def sign_data(self, data):
            #签名
            data.pop("sign", None)
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            #排完序后拼接起来
            unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
            #这里得到签名的字符串
            sign = self.sign(unsigned_string.encode("utf-8"))
            #对url进行处理
            quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
    
            # 获得最终的订单信息字符串
            signed_string = quoted_string + "&sign=" + quote_plus(sign)
            return signed_string
    
        #参数传进来一定要排序
        def ordered_data(self, data):
            complex_keys = []
            for key, value in data.items():
                if isinstance(value, dict):
                    complex_keys.append(key)
    
            # 将字典类型的数据dump出来
            for key in complex_keys:
                data[key] = json.dumps(data[key], separators=(',', ':'))
    
            return sorted([(k, v) for k, v in data.items()])
    
        def sign(self, unsigned_string):
            # 开始计算签名
            key = self.app_private_key
            #签名的对象
            signer = PKCS1_v1_5.new(key)
            #生成签名
            signature = signer.sign(SHA256.new(unsigned_string))
            # base64 编码,转换为unicode表示并移除回车
            sign = encodebytes(signature).decode("utf8").replace("
    ", "")
            return sign
    
        def _verify(self, raw_content, signature):
            # 开始计算签名
            key = self.alipay_public_key
            signer = PKCS1_v1_5.new(key)
            digest = SHA256.new()
            digest.update(raw_content.encode("utf8"))
            if signer.verify(digest, decodebytes(signature.encode("utf8"))):
                return True
            return False
    
        def verify(self, data, signature):
            if "sign_type" in data:
                sign_type = data.pop("sign_type")
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
            return self._verify(message, signature)
    
    
    if __name__ == "__main__":
        return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
        o = urlparse(return_url)
        query = parse_qs(o.query)
        processed_query = {}
        ali_sign = query.pop("sign")[0]
    
    
    # 测试用例
        alipay = AliPay(
            # 沙箱里面的appid值
            appid="2016091500517456",
            #notify_url是异步的url
            app_notify_url="http://47.93.198.159:8000/alipay/return/",
            # 我们自己商户的密钥
            app_private_key_path="../trade/keys/private_2048.txt",
            # 支付宝的公钥
            alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
            # debug为true时使用沙箱的url。如果不是用正式环境的url
            debug=True,  # 默认False,
            return_url="http://47.93.198.159:8000/alipay/return/"
        )
    
        for key, value in query.items():
            processed_query[key] = value[0]
        # print (alipay.verify(processed_query, ali_sign))
    
        # 直接支付:生成请求的字符串。
        url = alipay.direct_pay(
            # 订单标题
            subject="测试订单derek",
            # 我们商户自行生成的订单号
            out_trade_no="20180417derek2",
            # 订单金额
            total_amount=100,
            #成功付款后跳转到的页面,return_url同步的url
            return_url="http://47.93.198.159:8000/alipay/return/"
        )
        # 将生成的请求字符串拿到我们的url中进行拼接
        re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
        print(re_url)
    
    alipay.py
    alipay.py

    (3)settings.py

     settings中配置公钥私钥路径

    # 支付宝相关的key
    private_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/private_2048.txt')
    ali_pub_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/alipay_key_2048.txt')

    (4)trade/views.py

    from datetime import datetime
    from utils.alipay import AliPay
    from rest_framework.views import APIView
    from MxShop.settings import ali_pub_key_path, private_key_path
    from rest_framework.response import Response
    
    class AlipayView(APIView):
        def get(self, request):
            """
            处理支付宝的return_url返回
            """
            processed_dict = {}
            # 1. 获取GET中参数
            for key, value in request.GET.items():
                processed_dict[key] = value
            # 2. 取出sign
            sign = processed_dict.pop("sign", None)
    
            # 3. 生成ALipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            verify_re = alipay.verify(processed_dict, sign)
    
            # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
            if verify_re is True:
                order_sn = processed_dict.get('out_trade_no', None)
                trade_no = processed_dict.get('trade_no', None)
                trade_status = processed_dict.get('trade_status', None)
    
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
    
    
        def post(self, request):
            """
            处理支付宝的notify_url
            """
            #存放post里面所有的数据
            processed_dict = {}
            #取出post里面的数据
            for key, value in request.POST.items():
                processed_dict[key] = value
            #把signpop掉,文档有说明
            sign = processed_dict.pop("sign", None)
    
            #生成一个Alipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            #进行验证
            verify_re = alipay.verify(processed_dict, sign)
    
            # 如果验签成功
            if verify_re is True:
                #商户网站唯一订单号
                order_sn = processed_dict.get('out_trade_no', None)
                #支付宝系统交易流水号
                trade_no = processed_dict.get('trade_no', None)
                #交易状态
                trade_status = processed_dict.get('trade_status', None)
    
                # 查询数据库中订单记录
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    # 订单商品项
                    order_goods = existed_order.goods.all()
                    # 商品销量增加订单中数值
                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
    
                    # 更新订单状态
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
                #需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
                return Response("success")

    (5)trade/serializers.py

    创建订单的时候生成一个支付的url,这个逻辑OderSerializer和OrderDetailSerializer中都添加

     #支付订单的url
        alipay_url = serializers.SerializerMethodField(read_only=True)
    
        def get_alipay_url(self, obj):
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            url = alipay.direct_pay(
                subject=obj.order_sn,
                out_trade_no=obj.order_sn,
                total_amount=obj.order_mount,
            )
            re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
            return re_url
    # trade/serializer.py
    __author__ = 'derek'
    
    import time
    
    from .models import ShoppingCart
    from rest_framework import serializers
    from goods.models import Goods
    from goods.serializers import GoodsSerializer
    from .models import OrderInfo,OrderGoods
    from MxShop.settings import ali_pub_key_path, private_key_path
    from utils.alipay import AliPay
    
    class ShopCartDetailSerializer(serializers.ModelSerializer):
        '''
        购物车商品详情信息
        '''
        # 一个购物车对应一个商品
        goods = GoodsSerializer(many=False, read_only=True)
        class Meta:
            model = ShoppingCart
            fields = ("goods", "nums")
    
    
    class ShopCartSerializer(serializers.Serializer):
        #获取当前登录的用户
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )
        nums = serializers.IntegerField(required=True, label="数量",min_value=1,
                                        error_messages={
                                            "min_value":"商品数量不能小于一",
                                            "required": "请选择购买数量"
                                        })
        #这里是继承Serializer,必须指定queryset对象,如果继承ModelSerializer则不需要指定
        #goods是一个外键,可以通过这方法获取goods object中所有的值
        goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())
    
        #继承的Serializer没有save功能,必须写一个create方法
        def create(self, validated_data):
            # validated_data是已经处理过的数据
            #获取当前用户
            # view中:self.request.user;serizlizer中:self.context["request"].user
            user = self.context["request"].user
            nums = validated_data["nums"]
            goods = validated_data["goods"]
    
            existed = ShoppingCart.objects.filter(user=user, goods=goods)
            #如果购物车中有记录,数量+1
            #如果购物车车没有记录,就创建
            if existed:
                existed = existed[0]
                existed.nums += nums
                existed.save()
            else:
                #添加到购物车
                existed = ShoppingCart.objects.create(**validated_data)
    
            return existed
    
        def update(self, instance, validated_data):
            # 修改商品数量
            instance.nums = validated_data["nums"]
            instance.save()
            return instance
    
    
    #订单中的商品
    class OrderGoodsSerialzier(serializers.ModelSerializer):
        goods = GoodsSerializer(many=False)
        class Meta:
            model = OrderGoods
            fields = "__all__"
    
    #订单商品信息
    # goods字段需要嵌套一个OrderGoodsSerializer
    class OrderDetailSerializer(serializers.ModelSerializer):
        goods = OrderGoodsSerialzier(many=True)
        # 支付订单的url
        alipay_url = serializers.SerializerMethodField(read_only=True)
    
        def get_alipay_url(self, obj):
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            url = alipay.direct_pay(
                subject=obj.order_sn,
                out_trade_no=obj.order_sn,
                total_amount=obj.order_mount,
            )
            re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
            return re_url
        class Meta:
            model = OrderInfo
            fields = "__all__"
    
    class OrderSerializer(serializers.ModelSerializer):
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )
        #生成订单的时候这些不用post
        pay_status = serializers.CharField(read_only=True)
        trade_no = serializers.CharField(read_only=True)
        order_sn = serializers.CharField(read_only=True)
        pay_time = serializers.DateTimeField(read_only=True)
        nonce_str = serializers.CharField(read_only=True)
        pay_type = serializers.CharField(read_only=True)
        #支付订单的url
        alipay_url = serializers.SerializerMethodField(read_only=True)
    
        def get_alipay_url(self, obj):
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            url = alipay.direct_pay(
                subject=obj.order_sn,
                out_trade_no=obj.order_sn,
                total_amount=obj.order_mount,
            )
            re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    
            return re_url
    
    
        def generate_order_sn(self):
            #生成订单号
            # 当前时间+userid+随机数
            from random import Random
            random_ins = Random()
            order_sn = "{time_str}{userid}{ranstr}".format(time_str=time.strftime("%Y%m%d%H%M%S"),
                                                           userid=self.context["request"].user.id,
                                                           ranstr=random_ins.randint(10, 99))
            return order_sn
    
        def validate(self, attrs):
            #validate中添加order_sn,然后在view中就可以save
            attrs["order_sn"] = self.generate_order_sn()
            return attrs
    
        class Meta:
            model = OrderInfo
            fields = "__all__"
    
    trade/serializer.py
    trade/serializer.py

    (6)测试代码

    把本地修改的地方一定要上传到服务器,因为我们需要在服务器上调试代码

    vue项目中api.js里面local_host改为服务器ip

    let local_host = 'http://47.93.198.159:8000';

    在phcharm中开始运行项目

    浏览器访问地址:http://47.93.198.159:8000/orders/

    创建一个订单

    生成订单的详情

    12.6.vue静态文件放到django中

    vue有两种开发模式

    • build    用来生成静态文件 
    • dev

    (1)运行

    cnpm run build

    生成的静态文件在dist目录下面

    (2)把index.html拷贝到templates目录下

    (3)django中创建static目录

    • 把index.entry.js考到django的static目录下面
    • 把dist/static下的两个文件夹拷贝到django static目录下

     

    (4)settings设置静态文件路径

    STATIC_URL = '/static/'
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, "static"),
    )

     (5)修改index.html中静态文件路径

    <script type="text/javascript" src="/static/index.entry.js"></script></body>

    (6)配置index的url

    from django.views.generic import TemplateView
    
    urlpatterns = [
        # 首页
        path('index/', TemplateView.as_view(template_name='index.html'),name='index')
    ]

     (7)配置支付成功return的地址

    trade/views.py

    response = redirect("/index/#/app/home/member/order")
    return response
    else:
    response = redirect("index")
    return response
    # trade/views.py
    
    from rest_framework import viewsets
    from rest_framework.permissions import IsAuthenticated
    from utils.permissions import IsOwnerOrReadOnly
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.authentication import SessionAuthentication
    from .serializers import ShopCartSerializer,ShopCartDetailSerializer,OrderDetailSerializer,OrderGoodsSerialzier,OrderSerializer
    from .models import ShoppingCart,OrderGoods,OrderInfo
    from rest_framework import mixins
    from django.shortcuts import render, redirect
    
    
    class ShoppingCartViewset(viewsets.ModelViewSet):
        """
        购物车功能
        list:
            获取购物车详情
        create:
            加入购物车
        delete:
            删除购物记录
        """
        permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
        serializer_class = ShopCartSerializer
        #商品的id
        lookup_field = "goods_id"
    
        def get_serializer_class(self):
            if self.action == 'list':
                return ShopCartDetailSerializer
            else:
                return ShopCartSerializer
    
        #获取购物车列表
        def get_queryset(self):
            return ShoppingCart.objects.filter(user=self.request.user)
    
    
    class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
                       viewsets.GenericViewSet):
        """
        订单管理
        list:
            获取个人订单
        delete:
            删除订单
        create:
            新增订单
        """
        permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
        serializer_class = OrderSerializer
        #动态配置serializer
        def get_serializer_class(self):
            if self.action == "retrieve":
                return OrderDetailSerializer
            return OrderSerializer
        #获取订单列表
        def get_queryset(self):
            return OrderInfo.objects.filter(user=self.request.user)
    
        #在订单提交保存之前还需要多两步步骤,所以这里自定义perform_create方法
        #1.将购物车中的商品保存到OrderGoods中
        #2.情况购物车
        def perform_create(self, serializer):
            order = serializer.save()
            # 获取购物车所有商品
            shop_carts = ShoppingCart.objects.filter(user=self.request.user)
            for shop_cart in shop_carts:
                order_goods = OrderGoods()
                order_goods.goods = shop_cart.goods
                order_goods.goods_num = shop_cart.nums
                order_goods.order = order
                order_goods.save()
                #清空购物车
                shop_cart.delete()
            return order
    
    from datetime import datetime
    from utils.alipay import AliPay
    from rest_framework.views import APIView
    from MxShop.settings import ali_pub_key_path, private_key_path
    from rest_framework.response import Response
    
    class AlipayView(APIView):
        def get(self, request):
            """
            处理支付宝的return_url返回
            """
            processed_dict = {}
            # 1. 获取GET中参数
            for key, value in request.GET.items():
                processed_dict[key] = value
            # 2. 取出sign
            sign = processed_dict.pop("sign", None)
    
            # 3. 生成ALipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            verify_re = alipay.verify(processed_dict, sign)
    
            # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
            if verify_re is True:
                order_sn = processed_dict.get('out_trade_no', None)
                trade_no = processed_dict.get('trade_no', None)
                trade_status = processed_dict.get('trade_status', None)
    
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
    
                response = redirect("/index/#/app/home/member/order")
                return response
    
            else:
                response = redirect("index")
                return response
    
        def post(self, request):
            """
            处理支付宝的notify_url
            """
            #存放post里面所有的数据
            processed_dict = {}
            #取出post里面的数据
            for key, value in request.POST.items():
                processed_dict[key] = value
            #把signpop掉,文档有说明
            sign = processed_dict.pop("sign", None)
    
            #生成一个Alipay对象
            alipay = AliPay(
                appid="2016091500517456",
                app_notify_url="http://47.93.198.159:8000/alipay/return/",
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.93.198.159:8000/alipay/return/"
            )
    
            #进行验证
            verify_re = alipay.verify(processed_dict, sign)
    
            # 如果验签成功
            if verify_re is True:
                #商户网站唯一订单号
                order_sn = processed_dict.get('out_trade_no', None)
                #支付宝系统交易流水号
                trade_no = processed_dict.get('trade_no', None)
                #交易状态
                trade_status = processed_dict.get('trade_status', None)
    
                # 查询数据库中订单记录
                existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
                for existed_order in existed_orders:
                    # 订单商品项
                    order_goods = existed_order.goods.all()
                    # 商品销量增加订单中数值
                    for order_good in order_goods:
                        goods = order_good.goods
                        goods.sold_num += order_good.goods_num
                        goods.save()
    
                    # 更新订单状态
                    existed_order.pay_status = trade_status
                    existed_order.trade_no = trade_no
                    existed_order.pay_time = datetime.now()
                    existed_order.save()
                #需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
                return Response("success")
    
    # trade/views.py
    trade/views.py

    现在可以通过index直接访问了:http://47.93.198.159:8000/index/#/app/home/index

    添加商品到购物车-->>去结算-->>生成订单-->>跳到支付页面了

  • 相关阅读:
    Android中Chronometer 计时器和震动服务控件
    Android中几种常用的话框
    Android通过长按图片设置为壁纸
    Android把图片保存到SQLite中
    激活Win10
    SQL 语句解决实际问题
    在Foxmail邮件客户端登录263企业邮箱
    Visual Studio 安装VS10x CodeMAP
    开发常见问题汇总
    Java笔记:编写第一个Java程序
  • 原文地址:https://www.cnblogs.com/linyuhong/p/9948418.html
Copyright © 2020-2023  润新知