自己没有公网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 或 线上appid app_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 或者 RSA2 debug=True # 默认False 沙箱环境把这里设置为True ) url = alipay.api_alipay_trade_page_pay( out_trade_no=obj.order_sn, # 订单id total_amount=str(obj.order_mount), # 支付总金额(这里用str 它内部自动将它转换成json) subject=obj.order_sn, # 订单标题 ) return 'https://openapi.alipaydev.com/gateway.do?' + url def generate_order_sn(self): """ 生成订单号 :return: """ from random import Random random_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_sn def validate(self, attrs): print("attrs", attrs) attrs["order_sn"] = self.generate_order_sn() return attrs class Meta: model = OrderInfo fields = "__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 或 线上appid app_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 或者 RSA2 debug=True # 默认False 沙箱环境把这里设置为True ) url = alipay.api_alipay_trade_page_pay( out_trade_no=obj.order_sn, # 订单id total_amount=str(obj.order_mount), # 支付总金额(这里用str 它内部自动将它转换成json) subject=obj.order_sn, # 订单标题 ) return 'https://openapi.alipaydev.com/gateway.do?' + url class Meta: model = OrderInfo fields = "__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 OrderDetailSerializer return 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="2016092500596768", # 使用支付宝沙箱的id(你有实际的id就用实际的) app_notify_url=None, # 默认回调url 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 或者 RSA2 debug=True # 默认False(这里用的沙箱所以是True 实际开发是False) ) # 调用支付宝的交易查询接口 while True: response = alipay.api_alipay_trade_query(order.order_sn) # 返回的response就是这样的一个字典 # response = { # "trade_no": "2017032121001004070200176844", # 支付宝交易号 # "code": "10000", # 接口调用是否成功 # "invoice_amount": "20.00", # "open_id": "20880072506750308812798160715407", # "fund_bill_list": [ # { # "amount": "20.00", # "fund_channel": "ALIPAYACCOUNT" # } # ], # "buyer_logon_id": "csq***@sandbox.com", # "send_pay_date": "2017-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_no order.pay_time = datetime.now() order.pay_status = "success" order.save() # 返回结果 from django.shortcuts import redirect response = 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 time time.sleep(5) continue else: return Response("failure")
-
url
from rest_framework.routers import DefaultRouter from trade.views import OrderViewSet, CheckPayView # 生成一个注册器实例对象 router = DefaultRouter() # 订单 router.register(r"orders", OrderViewSet, base_name="orders") urlpatterns = [ # 自动生成url url(r"^", include(router.urls)), url(r"^check_pay/$", CheckPayView.as_view(), name="check_pay"), ]
-
效果图