最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

Django 前后端分离实战项目 生鲜超市(十四)之支付宝接入

IT圈 admin 2浏览 0评论

Django 前后端分离实战项目 生鲜超市(十四)之支付宝接入

支付宝接入

前言

所有vue接口全部在src/api/api.js文件下

代码已上传至github:

此项目仅学习用途

要求

Package Version


certifi 2020.4.5.1
chardet 3.0.4
coreapi 2.3.1
coreschema 0.0.4
Django 1.11.3
django-cors-headers 2.1.0
django-crispy-forms 1.6.1
django-filter 1.0.4
django-formtools 2.0
django-guardian 1.4.9
django-reversion 2.0.9
djangorestframework 3.6.3
djangorestframework-jwt 1.11.0
future 0.16.0
httplib2 0.9.2
idna 2.9
itypes 1.2.0
Jinja2 2.11.2
Markdown 2.6.8
MarkupSafe 1.1.1
mysqlclient 1.3.10
olefile 0.46
Pillow 4.2.1
pip 20.0.2
pycryptodome 3.9.7
PyJWT 1.7.1
pytz 2019.3
requests 2.23.0
setuptools 46.1.3
six 1.10.0
uritemplate 3.0.1
urllib3 1.25.9
wheel 0.34.2
XlsxWriter 0.9.8
xlwt 1.2.0

注意:一定要填写下面代码中的APPID,否则页面将无法正常做出与支付宝相关操作

流程

trade文件夹下serializers.py代码如下:

serializers.py


import time
from rest_framework import serializersfrom goods.models import Goods
from .models import ShoppingCart, OrderInfo, OrderGoods
from goods.serializers import GoodsSerializer
from utils.alipay import AliPay
from Mxshop.settings import private_key_path, ali_pub_key_pathclass ShopCartDetailSerializer(serializers.ModelSerializer):goods = GoodsSerializer(many=False, read_only=True)class Meta:model = ShoppingCartfields = ("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": "请选择购买数量"})goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())def create(self, validated_data):user = self.context["request"].usernums = validated_data["nums"]goods = validated_data["goods"]existed = ShoppingCart.objects.filter(user=user, goods=goods)if existed:existed = existed[0]existed.nums += numsexisted.save()else:existed = ShoppingCart.objects.create(**validated_data)return existeddef update(self, instance, validated_data):#修改商品数量instance.nums = validated_data["nums"]instance.save()return instanceclass OrderGoodsSerialzier(serializers.ModelSerializer):goods = GoodsSerializer(many=False)class Meta:model = OrderGoodsfields = "__all__"class OrderDetailSerializer(serializers.ModelSerializer):goods = OrderGoodsSerialzier(many=True)alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1:8000/alipay/return/")url = alipay.direct_pay(subject=obj.order_sn,out_trade_no=obj.order_sn,total_amount=obj.order_mount,)re_url = ".do?{data}".format(data=url)return re_urlclass Meta:model = OrderInfofields = "__all__"class OrderSerializer(serializers.ModelSerializer):user = serializers.HiddenField(default=serializers.CurrentUserDefault())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)alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1:8000/alipay/return/")url = alipay.direct_pay(subject=obj.order_sn,out_trade_no=obj.order_sn,total_amount=obj.order_mount,)re_url = ".do?{data}".format(data=url)return re_urldef generate_order_sn(self):# 当前时间+userid+随机数from random import Randomrandom_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_sndef validate(self, attrs):attrs["order_sn"] = self.generate_order_sn()return attrsclass Meta:model = OrderInfofields = "__all__"

trade文件夹下views.py代码如下:

views.py

import time
from datetime import datetime
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import SessionAuthentication
from rest_framework import mixins
from django.shortcuts import redirectfrom .serializers import ShopCartSerializer, OrderDetailSerializer, ShopCartDetailSerializer, OrderSerializer
from utils.permissions import IsOwnerOrReadOnly
from .models import ShoppingCart, OrderInfo, OrderGoodsclass ShoppingCartViewset(viewsets.ModelViewSet):"""购物车功能list:获取购物车详情create:加入购物车delete:删除购物记录"""permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)serializer_class = ShopCartSerializerlookup_field = "goods_id"def perform_create(self, serializer):shop_cart = serializer.save()goods = shop_cart.goodsgoods.goods_num -= shop_cart.numsgoods.save()def perform_destroy(self, instance):goods = instance.goodsgoods.goods_num += instance.numsgoods.save()instance.delete()def perform_update(self, serializer):existed_record = ShoppingCart.objects.get(id=serializer.instance.id)existed_nums = existed_record.numssaved_record = serializer.save()nums = saved_record.nums - existed_numsgoods = saved_record.goodsgoods.goods_num -= numsgoods.save()def get_serializer_class(self):if self.action == 'list':return ShopCartDetailSerializerelse:return ShopCartSerializerdef 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 = OrderSerializerdef get_queryset(self):return OrderInfo.objects.filter(user=self.request.user)def get_serializer_class(self):if self.action == "retrieve":return OrderDetailSerializerreturn OrderSerializerdef 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.goodsorder_goods.goods_num = shop_cart.numsorder_goods.order = orderorder_goods.save()shop_cart.delete()return orderfrom rest_framework.views import APIView
from utils.alipay import AliPay
from Mxshop.settings import ali_pub_key_path, private_key_path
from rest_framework.response import Responseclass AlipayView(APIView):def get(self, request):"""处理支付宝的return_url返回:param request::return:"""processed_dict = {}for key, value in request.GET.items():processed_dict[key] = valuesign = processed_dict.pop("sign", None)alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1: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:existed_order.pay_status = trade_statusexisted_order.trade_no = trade_noexisted_order.pay_time = datetime.now()existed_order.save()response = redirect("index")response.set_cookie("nextPath", "pay", max_age=3)return responseelse:response = redirect("index")return responsedef post(self, request):"""处理支付宝的notify_url:param request::return:"""processed_dict = {}for key, value in request.POST.items():processed_dict[key] = valuesign = processed_dict.pop("sign", None)alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1: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.goodsgoods.sold_num += order_good.goods_numgoods.save()existed_order.pay_status = trade_statusexisted_order.trade_no = trade_noexisted_order.pay_time = datetime.now()existed_order.save()return Response("success")

utils文件夹下alipay.py代码如下:

alipay.py


# pip install pycryptodomefrom 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, encodebytesimport jsonclass AliPay(object):"""支付宝支付接口"""def __init__(self, appid, app_notify_url, app_private_key_path,alipay_public_key_path, return_url, debug=False):self.appid = appidself.app_notify_url = app_notify_urlself.app_private_key_path = app_private_key_pathself.app_private_key = Noneself.return_url = return_urlwith open(self.app_private_key_path) as fp:self.app_private_key = RSA.importKey(fp.read())self.alipay_public_key_path = alipay_public_key_pathwith open(self.alipay_public_key_path) as fp:self.alipay_public_key = RSA.import_key(fp.read())if debug is True:self.__gateway = ".do"else:self.__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.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):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_urldata["return_url"] = self.return_urlreturn datadef 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"))# ordered_items = self.ordered_data(data)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_stringdef 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_keysigner = PKCS1_v1_5.new(key)signature = signer.sign(SHA256.new(unsigned_string))# base64 编码,转换为unicode表示并移除回车sign = encodebytes(signature).decode("utf8").replace("\n", "")return signdef _verify(self, raw_content, signature):# 开始计算签名key = self.alipay_public_keysigner = PKCS1_v1_5.new(key)digest = SHA256.new()digest.update(raw_content.encode("utf8"))if signer.verify(digest, decodebytes(signature.encode("utf8"))):return Truereturn Falsedef 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=2020-04-24+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="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path="../trade/keys/private_2048.txt",alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,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="测试订单2",out_trade_no="202004241888",total_amount=100,return_url="http://127.0.0.1:8000/alipay/return/")re_url = ".do?{data}".format(data=url)print(re_url)

支付宝windows密钥生成器,linux用户使用自带的openssl进行密钥生成即可。

.htm?clientType=assistant&platform=win&channelType=WEB

settings.py

STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),
)
#支付宝相关配置
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')

url.py

from django.conf.urls import url,include
# from django.contrib import admin
import xadmin
from Mxshop.settings import MEDIA_ROOT
from django.views.static import serve
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views
from rest_framework_jwt.views import obtain_jwt_tokenfrom goods.views import GoodsListViewSet,CategoryViewset,HotSearchsViewset
from users.views import SmsCodeViewset,UserViewset
from user_operation.views import UserFavViewset, LeavingMessageViewset, AddressViewset
from trade.views import ShoppingCartViewset, OrderViewsetrouter = DefaultRouter()#配置goods的url
router.register(r'goods', GoodsListViewSet, base_name="goods")#配置category的url
router.register(r'categorys', CategoryViewset, base_name="categorys")router.register(r'codes', SmsCodeViewset, base_name="codes")router.register(r'hotsearchs', HotSearchsViewset, base_name="hotsearchs")router.register(r'users', UserViewset, base_name="users")#收藏
router.register(r'userfavs', UserFavViewset, base_name="userfavs")#留言
router.register(r'messages', LeavingMessageViewset, base_name="messages")#收货地址
router.register(r'address', AddressViewset, base_name="address")#购物车url
router.register(r'shopcarts', ShoppingCartViewset, base_name="shopcarts")#订单相关url
router.register(r'orders', OrderViewset, base_name="orders")goods_list = GoodsListViewSet.as_view({'get': 'list',
})from trade.views import AlipayView
from django.views.generic import TemplateView
urlpatterns = [url(r'^xadmin/', xadmin.site.urls),url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),url(r'^', include(router.urls)),url(r'^index/', TemplateView.as_view(template_name="index.html"), name="index"),url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),url(r'docs/',include_docs_urls(title='GOHB生鲜')),# drf自带的token认证模式url(r'^api-token-auth/', views.obtain_auth_token),# jwt的认证接口url(r'^login/', obtain_jwt_token),url(r'^alipay/return/', AlipayView.as_view(), name="alipay"),
]

如果在windows下,可以下载支付宝开放平台开发助手生成密钥,如果在linux下,采用本地的openssl即可生成(以下均为windows下操作)


将生成的公钥添加到支付宝沙箱环境的公钥中,并保存

复制APPID

将刚刚生成的密钥放在trade文件夹下keys文件夹下private_2048,pub_2048文件中(小编这里是在工具类Alipay.py中这样命名的)

复制支付宝公钥

将其放入alipay_key_2048中


测试是否正常返回沙箱

出现支付宝沙箱开发问题,请更换浏览器查看即可,或者关闭自己的浏览器代理。

正常访问页面,如下:

cnpm run build,生成静态文件放进django的template下

在页面点击支付宝支付

成功跳转支付页面

Django 前后端分离实战项目 生鲜超市(十四)之支付宝接入

支付宝接入

前言

所有vue接口全部在src/api/api.js文件下

代码已上传至github:

此项目仅学习用途

要求

Package Version


certifi 2020.4.5.1
chardet 3.0.4
coreapi 2.3.1
coreschema 0.0.4
Django 1.11.3
django-cors-headers 2.1.0
django-crispy-forms 1.6.1
django-filter 1.0.4
django-formtools 2.0
django-guardian 1.4.9
django-reversion 2.0.9
djangorestframework 3.6.3
djangorestframework-jwt 1.11.0
future 0.16.0
httplib2 0.9.2
idna 2.9
itypes 1.2.0
Jinja2 2.11.2
Markdown 2.6.8
MarkupSafe 1.1.1
mysqlclient 1.3.10
olefile 0.46
Pillow 4.2.1
pip 20.0.2
pycryptodome 3.9.7
PyJWT 1.7.1
pytz 2019.3
requests 2.23.0
setuptools 46.1.3
six 1.10.0
uritemplate 3.0.1
urllib3 1.25.9
wheel 0.34.2
XlsxWriter 0.9.8
xlwt 1.2.0

注意:一定要填写下面代码中的APPID,否则页面将无法正常做出与支付宝相关操作

流程

trade文件夹下serializers.py代码如下:

serializers.py


import time
from rest_framework import serializersfrom goods.models import Goods
from .models import ShoppingCart, OrderInfo, OrderGoods
from goods.serializers import GoodsSerializer
from utils.alipay import AliPay
from Mxshop.settings import private_key_path, ali_pub_key_pathclass ShopCartDetailSerializer(serializers.ModelSerializer):goods = GoodsSerializer(many=False, read_only=True)class Meta:model = ShoppingCartfields = ("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": "请选择购买数量"})goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())def create(self, validated_data):user = self.context["request"].usernums = validated_data["nums"]goods = validated_data["goods"]existed = ShoppingCart.objects.filter(user=user, goods=goods)if existed:existed = existed[0]existed.nums += numsexisted.save()else:existed = ShoppingCart.objects.create(**validated_data)return existeddef update(self, instance, validated_data):#修改商品数量instance.nums = validated_data["nums"]instance.save()return instanceclass OrderGoodsSerialzier(serializers.ModelSerializer):goods = GoodsSerializer(many=False)class Meta:model = OrderGoodsfields = "__all__"class OrderDetailSerializer(serializers.ModelSerializer):goods = OrderGoodsSerialzier(many=True)alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1:8000/alipay/return/")url = alipay.direct_pay(subject=obj.order_sn,out_trade_no=obj.order_sn,total_amount=obj.order_mount,)re_url = ".do?{data}".format(data=url)return re_urlclass Meta:model = OrderInfofields = "__all__"class OrderSerializer(serializers.ModelSerializer):user = serializers.HiddenField(default=serializers.CurrentUserDefault())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)alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1:8000/alipay/return/")url = alipay.direct_pay(subject=obj.order_sn,out_trade_no=obj.order_sn,total_amount=obj.order_mount,)re_url = ".do?{data}".format(data=url)return re_urldef generate_order_sn(self):# 当前时间+userid+随机数from random import Randomrandom_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_sndef validate(self, attrs):attrs["order_sn"] = self.generate_order_sn()return attrsclass Meta:model = OrderInfofields = "__all__"

trade文件夹下views.py代码如下:

views.py

import time
from datetime import datetime
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import SessionAuthentication
from rest_framework import mixins
from django.shortcuts import redirectfrom .serializers import ShopCartSerializer, OrderDetailSerializer, ShopCartDetailSerializer, OrderSerializer
from utils.permissions import IsOwnerOrReadOnly
from .models import ShoppingCart, OrderInfo, OrderGoodsclass ShoppingCartViewset(viewsets.ModelViewSet):"""购物车功能list:获取购物车详情create:加入购物车delete:删除购物记录"""permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)serializer_class = ShopCartSerializerlookup_field = "goods_id"def perform_create(self, serializer):shop_cart = serializer.save()goods = shop_cart.goodsgoods.goods_num -= shop_cart.numsgoods.save()def perform_destroy(self, instance):goods = instance.goodsgoods.goods_num += instance.numsgoods.save()instance.delete()def perform_update(self, serializer):existed_record = ShoppingCart.objects.get(id=serializer.instance.id)existed_nums = existed_record.numssaved_record = serializer.save()nums = saved_record.nums - existed_numsgoods = saved_record.goodsgoods.goods_num -= numsgoods.save()def get_serializer_class(self):if self.action == 'list':return ShopCartDetailSerializerelse:return ShopCartSerializerdef 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 = OrderSerializerdef get_queryset(self):return OrderInfo.objects.filter(user=self.request.user)def get_serializer_class(self):if self.action == "retrieve":return OrderDetailSerializerreturn OrderSerializerdef 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.goodsorder_goods.goods_num = shop_cart.numsorder_goods.order = orderorder_goods.save()shop_cart.delete()return orderfrom rest_framework.views import APIView
from utils.alipay import AliPay
from Mxshop.settings import ali_pub_key_path, private_key_path
from rest_framework.response import Responseclass AlipayView(APIView):def get(self, request):"""处理支付宝的return_url返回:param request::return:"""processed_dict = {}for key, value in request.GET.items():processed_dict[key] = valuesign = processed_dict.pop("sign", None)alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1: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:existed_order.pay_status = trade_statusexisted_order.trade_no = trade_noexisted_order.pay_time = datetime.now()existed_order.save()response = redirect("index")response.set_cookie("nextPath", "pay", max_age=3)return responseelse:response = redirect("index")return responsedef post(self, request):"""处理支付宝的notify_url:param request::return:"""processed_dict = {}for key, value in request.POST.items():processed_dict[key] = valuesign = processed_dict.pop("sign", None)alipay = AliPay(# 填写自己的appid(必填)appid="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path=private_key_path,alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,debug=True,  # 默认False,return_url="http://127.0.0.1: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.goodsgoods.sold_num += order_good.goods_numgoods.save()existed_order.pay_status = trade_statusexisted_order.trade_no = trade_noexisted_order.pay_time = datetime.now()existed_order.save()return Response("success")

utils文件夹下alipay.py代码如下:

alipay.py


# pip install pycryptodomefrom 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, encodebytesimport jsonclass AliPay(object):"""支付宝支付接口"""def __init__(self, appid, app_notify_url, app_private_key_path,alipay_public_key_path, return_url, debug=False):self.appid = appidself.app_notify_url = app_notify_urlself.app_private_key_path = app_private_key_pathself.app_private_key = Noneself.return_url = return_urlwith open(self.app_private_key_path) as fp:self.app_private_key = RSA.importKey(fp.read())self.alipay_public_key_path = alipay_public_key_pathwith open(self.alipay_public_key_path) as fp:self.alipay_public_key = RSA.import_key(fp.read())if debug is True:self.__gateway = ".do"else:self.__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.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):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_urldata["return_url"] = self.return_urlreturn datadef 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"))# ordered_items = self.ordered_data(data)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_stringdef 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_keysigner = PKCS1_v1_5.new(key)signature = signer.sign(SHA256.new(unsigned_string))# base64 编码,转换为unicode表示并移除回车sign = encodebytes(signature).decode("utf8").replace("\n", "")return signdef _verify(self, raw_content, signature):# 开始计算签名key = self.alipay_public_keysigner = PKCS1_v1_5.new(key)digest = SHA256.new()digest.update(raw_content.encode("utf8"))if signer.verify(digest, decodebytes(signature.encode("utf8"))):return Truereturn Falsedef 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=2020-04-24+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="",app_notify_url="http://127.0.0.1:8000/alipay/return/",app_private_key_path="../trade/keys/private_2048.txt",alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,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="测试订单2",out_trade_no="202004241888",total_amount=100,return_url="http://127.0.0.1:8000/alipay/return/")re_url = ".do?{data}".format(data=url)print(re_url)

支付宝windows密钥生成器,linux用户使用自带的openssl进行密钥生成即可。

.htm?clientType=assistant&platform=win&channelType=WEB

settings.py

STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),
)
#支付宝相关配置
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')

url.py

from django.conf.urls import url,include
# from django.contrib import admin
import xadmin
from Mxshop.settings import MEDIA_ROOT
from django.views.static import serve
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views
from rest_framework_jwt.views import obtain_jwt_tokenfrom goods.views import GoodsListViewSet,CategoryViewset,HotSearchsViewset
from users.views import SmsCodeViewset,UserViewset
from user_operation.views import UserFavViewset, LeavingMessageViewset, AddressViewset
from trade.views import ShoppingCartViewset, OrderViewsetrouter = DefaultRouter()#配置goods的url
router.register(r'goods', GoodsListViewSet, base_name="goods")#配置category的url
router.register(r'categorys', CategoryViewset, base_name="categorys")router.register(r'codes', SmsCodeViewset, base_name="codes")router.register(r'hotsearchs', HotSearchsViewset, base_name="hotsearchs")router.register(r'users', UserViewset, base_name="users")#收藏
router.register(r'userfavs', UserFavViewset, base_name="userfavs")#留言
router.register(r'messages', LeavingMessageViewset, base_name="messages")#收货地址
router.register(r'address', AddressViewset, base_name="address")#购物车url
router.register(r'shopcarts', ShoppingCartViewset, base_name="shopcarts")#订单相关url
router.register(r'orders', OrderViewset, base_name="orders")goods_list = GoodsListViewSet.as_view({'get': 'list',
})from trade.views import AlipayView
from django.views.generic import TemplateView
urlpatterns = [url(r'^xadmin/', xadmin.site.urls),url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),url(r'^', include(router.urls)),url(r'^index/', TemplateView.as_view(template_name="index.html"), name="index"),url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),url(r'docs/',include_docs_urls(title='GOHB生鲜')),# drf自带的token认证模式url(r'^api-token-auth/', views.obtain_auth_token),# jwt的认证接口url(r'^login/', obtain_jwt_token),url(r'^alipay/return/', AlipayView.as_view(), name="alipay"),
]

如果在windows下,可以下载支付宝开放平台开发助手生成密钥,如果在linux下,采用本地的openssl即可生成(以下均为windows下操作)


将生成的公钥添加到支付宝沙箱环境的公钥中,并保存

复制APPID

将刚刚生成的密钥放在trade文件夹下keys文件夹下private_2048,pub_2048文件中(小编这里是在工具类Alipay.py中这样命名的)

复制支付宝公钥

将其放入alipay_key_2048中


测试是否正常返回沙箱

出现支付宝沙箱开发问题,请更换浏览器查看即可,或者关闭自己的浏览器代理。

正常访问页面,如下:

cnpm run build,生成静态文件放进django的template下

在页面点击支付宝支付

成功跳转支付页面

发布评论

评论列表 (0)

  1. 暂无评论