自己没有公网ip接入支付宝
技术是vue+drf
vue–主要看创建订单和修改订单支付状态
balanceCount () {// 结算if(this.addrInfo.length==0){alert("请选择收货地址")}else{createOrder({post_script:this.post_script,address:this.address,signer_name:this.signer_name,signer_mobile:this.signer_mobile,order_mount:this.totalPrice}).then((response)=> {alert('订单创建成功');window.location.href=response.data.alipay_url;updateOrder({"order_id": response.data.id}).then((response)=>{alert("订单修改成功");}).catch(function (error) {console.log(error);});}).catch(function (error) {console.log(error);});
api–只看创建订单和修改订单支付状态
//添加订单export const createOrder = params => {return axios.post(`${local_host}/orders/`, params)}//订单更新export const updateOrder = params => {return axios.post(`${local_host}/check_pay/`, params)}
订单序列化–创建订单时去支付宝支付, 所以在序列化类里要创建个alipay_url
class OrderSerializer(serializers.ModelSerializer):"""订单信息序列化类"""user = serializers.HiddenField(default=serializers.CurrentUserDefault())add_time = serializers.DateTimeField(read_only=True, format="%Y-%m-%d %H:%M:%S")# read_only只返回不提交# write_only只提交不反回pay_status = serializers.CharField(read_only=True)order_sn = serializers.CharField(read_only=True)trade_no = serializers.CharField(read_only=True)# order_mount = serializers.FloatField(read_only=True)pay_time = serializers.DateTimeField(read_only=True)# SerializerMethodField 这是一个只读字段。它通过在附加的序列化器类上调用一个方法来获取其值。它可以用于将任何类型的数据添加到对象的序列化表示中。# read_only 只返回不提交alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(appid="", # 沙箱appid 或 线上appidapp_notify_url=None, # 默认回调url 不写的话这里用None# os.path.dirname(__file__): 获取当前文件所在目录的路径app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 私钥alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"),# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,sign_type="RSA2", # RSA 或者 RSA2debug=True # 默认False 沙箱环境把这里设置为True)url = alipay.api_alipay_trade_page_pay(out_trade_no=obj.order_sn, # 订单idtotal_amount=str(obj.order_mount), # 支付总金额(这里用str 它内部自动将它转换成json)subject=obj.order_sn, # 订单标题)return '/gateway.do?' + urldef generate_order_sn(self):"""生成订单号:return:"""from random import Randomrandom_ins = Random()# 当前时间+userid+随机数# 当前时间并且格式化time.strftime("%Y%m%d%H%M%S")# self.request.user.id当前用户的id# random_ins.randint(10, 99)生成两位的随机数order_sn = "%s%s%s" % (time.strftime("%Y%m%d%H%M%S"), self.context["request"].user.id, random_ins.randint(10, 99))return order_sndef validate(self, attrs):print("attrs", attrs)attrs["order_sn"] = self.generate_order_sn()return attrsclass Meta:model = OrderInfofields = "__all__"
订单详情序列化–订单的详情页面点击去支付, 所以在序列化类里要创建个alipay_url
class OrderDetailSerializer(serializers.ModelSerializer):"""订单详情序列化类"""goods = OrderGoodsSerializer(many=True)# SerializerMethodField 这是一个只读字段。它通过在附加的序列化器类上调用一个方法来获取其值。它可以用于将任何类型的数据添加到对象的序列化表示中。# read_only 只返回不提交alipay_url = serializers.SerializerMethodField(read_only=True)def get_alipay_url(self, obj):alipay = AliPay(appid="", # 沙箱appid 或 线上appidapp_notify_url=None, # 默认回调url 不写的话这里用None# os.path.dirname(__file__): 获取当前文件所在目录的路径app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 私钥alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"),# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,sign_type="RSA2", # RSA 或者 RSA2debug=True # 默认False 沙箱环境把这里设置为True)url = alipay.api_alipay_trade_page_pay(out_trade_no=obj.order_sn, # 订单idtotal_amount=str(obj.order_mount), # 支付总金额(这里用str 它内部自动将它转换成json)subject=obj.order_sn, # 订单标题)return '/gateway.do?' + urlclass Meta:model = OrderInfofields = "__all__"
view–订单
class OrderViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):"""订单管理create:创建订单list:获取订单信息delete:取消订单retrieve:查看订单详细信息"""# 动态指定序列化类def get_serializer_class(self):if self.action == "retrieve":return OrderDetailSerializerreturn OrderSerializer# IsAuthenticated指定权限, 用户登入才能访问# IsOwnerOrReadOnly总是允许GET、HEAD或OPTIONS请求, 其他请求判断当前操作的数据它的user是否为当前用户permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)# json token的验证 局部大于全局, 就算你在全局设置过, 但是你只要在这里加上authentication_classes 就会替换全局设置authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)def get_queryset(self):return OrderInfo.objects.filter(user=self.request.user)def perform_create(self, serializer):"""保存序列化信息:param serializer: 序列化类:return: 序列化关联的model的实例"""# 想在保存之前做点操作, 就重写perform_create方法# 想在创建时做点操作, 就重写create方法order = serializer.save()# 1. 把购物车信息保存到OrderGoods模型类里面shop_carts = ShoppingCart.objects.filter(user=self.request.user)for shop_cart in shop_carts:order_goods = OrderGoods.objects.create(goods=shop_cart.goods, goods_num=shop_cart.nums, order=order)order_goods.save()# 2. 清空购物车shop_carts.delete()# 3. 返回订单信息return order
view-checkpay
class CheckPayView(APIView):def post(self, request):# 业务处理:使用python SDK调用支付宝的支付接口# 初始化order_id = request.data.get("order_id", None)try:order = OrderInfo.objects.get(id=order_id)except OrderInfo.DoesNotExist:return Response({"order": "无效id"}, status=status.HTTP_404_NOT_FOUND)alipay = AliPay(appid="092500596768", # 使用支付宝沙箱的id(你有实际的id就用实际的)app_notify_url=None, # 默认回调urlapp_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 应用私钥路径# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"), # 支付宝公钥路径sign_type="RSA2", # RSA 或者 RSA2debug=True # 默认False(这里用的沙箱所以是True 实际开发是False))# 调用支付宝的交易查询接口while True:response = alipay.api_alipay_trade_query(order.order_sn)# 返回的response就是这样的一个字典# response = {#"trade_no": "032121001004070200176844", # 支付宝交易号#"code": "10000", # 接口调用是否成功#"invoice_amount": "20.00",#"open_id": "20880072506750308812798160715407",#"fund_bill_list": [# {# "amount": "20.00",# "fund_channel": "ALIPAYACCOUNT"# }#],#"buyer_logon_id": "csq***@",#"send_pay_date": "-03-21 13:29:17",#"receipt_amount": "20.00",#"out_trade_no": "out_trade_no15",#"buyer_pay_amount": "20.00",#"buyer_user_id": "2088102169481075",#"msg": "Success",#"point_amount": "0.00",#"trade_status": "TRADE_SUCCESS", # 支付结果#"total_amount": "20.00"# }# response里的 code是10000(表示调用成功) 20000(表示不成功) 20001(授权权限不足) 40001(缺少必要的参数) 40002(非法参数)# 40004(业务处理失败) 40006(权限不足)# response里的 trade_status值为TRADE_SUCCESS(交易支付成功) WAIT_BUYER_PAY(交易创建,等待买家付款)、# TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_FINISHED(交易结束,不可退款)code = response.get('code')trade_status = response.get('trade_status')if code == '10000' and trade_status == 'TRADE_SUCCESS':# 支付成功# 获取支付宝交易号trade_no = response.get('trade_no')# 更新订单的状态order.trade_no = trade_noorder.pay_time = datetime.now()order.pay_status = "success"order.save()# 返回结果from django.shortcuts import redirectresponse = redirect("index")# 设置cookie 最长时间为2秒response.set_cookie("nextPath", "pay", max_age=2)return Response("success")elif code == '40004' or (code == '10000' and trade_status == 'WAIT_BUYER_PAY'):# 等待买家付款# 业务处理失败, 可能一会就会成功import timetime.sleep(5)continueelse:return Response("failure")
url
from rest_framework.routers import DefaultRouterfrom trade.views import OrderViewSet, CheckPayView# 生成一个注册器实例对象router = DefaultRouter()# 订单router.register(r"orders", OrderViewSet, base_name="orders")urlpatterns = [# 自动生成urlurl(r"^", include(router.urls)),url(r"^check_pay/$", CheckPayView.as_view(), name="check_pay"),]
效果图