1.前端数据
2.后端接口
1.
#订单号 import time ,random def get_order_id(): st="012345679qwertyui" order_id=str(time.strftime("%Y%m%d%h%M%S"))+"".join(random.sample(st,5)) return order_id from datetime import datetime from pro_celery.celery import del_order def add_task(order_id,seconds=5): ctime = datetime.now() #获取当前时间 utc_ctime = datetime.utcfromtimestamp(ctime.timestamp()) from datetime import timedelta time_delay = timedelta(seconds=seconds) #延迟执行任务时间 task_time = utc_ctime + time_delay #执行延迟任务 result = del_order.apply_async(args=[order_id, ], eta=task_time)
2.
import time from app01.wx import settings class Wxpay: def pay(self,order_data): self.order_id = order_data["order_id"] self.open_id = order_data['open_id'] self.ip = order_data['ip'] #自定义函数获取支付信息字符串 data_body = self.get_body_data() #向微信服务器发送支付信息 import requests url = "https://api.mch.weixin.qq.com/pay/unifiedorder" response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"}) #自定义函处理返回数据 res_dict = self.xml_to_dic(response.content) timeStamp = str(int(time.time())) #二次签名 paySign = self.get_pay_sign(res_dict, timeStamp) data_dic = { 'timeStamp': timeStamp, 'nonceStr': res_dict['nonce_str'], 'package': f"prepay_id={res_dict['prepay_id']}", 'signType': 'MD5', "paySign": paySign, } return data_dic #二次签名 def get_pay_sign(self, res_dict, timeStamp): data_dic = { 'appId': res_dict['appid'], 'timeStamp': timeStamp, #时间戳 'nonceStr': res_dict['nonce_str'], #随机字符串 'package': f"prepay_id={res_dict['prepay_id']}", #数据包 "signType": "MD5" #签名类型 } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() #处理微信返回xml数据 def xml_to_dic(self, xml_data): import xml.etree.ElementTree as ET ''' xml to dict :param xml_data: :return: ''' xml_dict = {} root = ET.fromstring(xml_data) for child in root: xml_dict[child.tag] = child.text return xml_dict #随机字符串 def get_random(self): import random data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP" nonce_str = "".join(random.sample(data, 30)) return nonce_str #签名 def get_sign(self): data_dic = { "nonce_str": self.nonce_str, "out_trade_no": self.out_trade_no, "spbill_create_ip": self.spbill_create_ip, "notify_url": self.notify_url, "openid": self.open_id, "body": self.body, "trade_type": "JSAPI", "appid": self.appid, "total_fee": "1", "mch_id": self.mch_id } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() #支付信息字符串 def get_body_data(self): self.appid = settings.AppId self.mch_id = str(settings.pay_mchid) #商户号 self.nonce_str = self.get_random() #随机字符串 self.out_trade_no = self.order_id #订单号 self.spbill_create_ip = self.ip #终端ip self.notify_url = "https://www.test.com" #异步回调地址 self.body = "老男孩学费" #商品描述 self.sign = self.get_sign() #签名(自定义函数) #格式替换 body_data = f""" <xml> <appid>{self.appid}</appid> <mch_id>{self.mch_id}</mch_id> <nonce_str>{self.nonce_str}</nonce_str> <sign>{self.sign}</sign> <body>{self.body}</body> <out_trade_no>{self.out_trade_no}</out_trade_no> <total_fee>1</total_fee> <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip> <notify_url>{self.notify_url}</notify_url> <openid>{self.open_id}</openid> <trade_type>JSAPI</trade_type> </xml>""" return body_data
3.
from rest_framework.views import APIView from rest_framework.response import Response from django.core.cache import cache from app01 import models import hashlib,time from django.db import transaction from app01.comment import func from django import forms import importlib #form组件(校验前端订单数据数据) class OrderForm(forms.Form): phone = forms.CharField( error_messages={ "required": "手机号不能为空" }, # 调用Form组件中的验证器来校验手机号 # validators=[RegexValidator(r'1[1-9][0-9]{9}', '手机号格式不正确')], ) token = forms.CharField( error_messages={ "required": "token不能为空" }) province=forms.CharField( error_messages={ "required": "省份不能为空" }) city = forms.CharField(error_messages={ "required": "城市不能为空" }) county = forms.CharField(error_messages={ "required": "县/区不能为空" }) address = forms.CharField(error_messages={ "required": "详细地址不能为空" }) name = forms.CharField(error_messages={ "required": "姓名不能为空" }) #创建订单接口 class Creat(APIView): @transaction.atomic def post(self,request): #获取前端数据form组件校验 param=request.data form_obj=OrderForm(param) if form_obj.is_valid() and param['buy_list']: if request.META.get("HTTP_X_FORWARDED_FOR"): host_ip = request.META["HTTP_X_FROWARDED_FOR"] else: host_ip = request.META["REMOTE_ADDR"] #获取redis获取openID的拼接数据 user_cache=cache.get(param['token']) if user_cache: #获取openid openid=user_cache.split("&")[0] user_data=models.Wxuser.objects.filter(openid=openid).first() #创建订单字典 order_data = {"consignee_mobile": param['phone'], 'consignee_name': param['name'], 'wxuser_id': user_data.id, "memo": param['remark'], "consignee_area":f"{param['province']},{param['city']},{param['county']}", "consignee_address":param['address'] , } #获取前端商品字典,去数据库查出所有商品 buy_list=param['buy_list'] goods_key=list(buy_list.keys()) all_product=models.Product.objects.filter(product_id__in=goods_key) #调用自定义函数产生订单号,同时也是主键 order_data['order_id']=func.get_order_id() order_data['order_total']=0 #总价 order_data['quantity']=0 #商品数 #开启库存 sid=transaction.savepoint() #循环每个商品对象 for product in all_product: product.product_id=str(product.product_id) #主键 order_data['order_total']+=product.price*buy_list[product.product_id] #总价 order_data['quantity']+=buy_list[product.product_id] #商品数 #创建子订单 for i in range(3): #跨库存表,查询当前库存 stock=product.stock.quantity #当前减轻购买的数量,等于剩余的库存 new_stock=stock-buy_list[product.product_id] #判断库存是否足够 if new_stock<0: #回滚 transaction.savepoint_rollback(sid) #如果库存不住够,我们直接返回 return Response({"code":203,"msg":f"{product.name}库存不足"}) #乐观锁更新库存表库存 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock.stock_id).update(quantity=new_stock) #判断影响行数 if not res: if i==2: transaction.savepoint_rollback(sid) return Response({"code": 203, "msg": f"创建订单失败"}) else: continue else: break #更新商品表商品销量 new_buy_count = product.buy_count + buy_list[product.product_id] models.Product.objects.filter(product_id=product.product_id).update(buy_count=new_buy_count) #子订单创建 order_item_data={'order_id': order_data['order_id'], 'product_id': product.product_id, \ "name": product.name, "image": product.image, "price": product.price, \ "nums": buy_list[product.product_id], "brief": product.brief} models.Order_items.objects.create(**order_item_data) #创建订单 models.Order.objects.create(**order_data) #多种方式支付 pay_methon="Wxpay" try: pay_file=importlib.import_module(f"app01.Pay.{pay_methon}") #动态导入,路径名拿到相关py文件模块 pay_class=getattr(pay_file,pay_methon) #反射取出支付类 order_data['open_id'] = openid order_data['ip']=host_ip data=pay_class().pay(order_data) #支付类实例化传入订单数据。调用支付方法 except: transaction.savepoint_rollback(sid) return Response({"code": 200, "msg": "未知的支付方式" }) transaction.savepoint_commit(sid) #celer延迟任务订单回退 func.add_task(order_data['order_id']) #返回支付链接 return Response({"code": 200, "msg": "ok" ,"data":data}) else: return Response({"code":202,"msg":"token已过期"}) else: return Response({"code":201,"msg":"缺少参数"}) #多种方式支付回调 class Notity(APIView): def post(self,request,paymethon): ##动态导入,路径名拿到相关py文件模块 pay_file = importlib.import_module(f"app01.Pay.{paymethon}") # 反射取出支付类 pay_class = getattr(pay_file, paymethon) # 支付类实例化。调用回调方法 data=pay_class().notity(request.data) #更新订单状态 if data['status']=="success": models.Order.objects.filter(order_id=data['order']).updata(pay_status=1)
4.
1.settings.py AppId="" AppSecret="" code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code" pay_mchid ='' pay_apikey = '' 2.wx_login.py import requests from app01.wx import settings def login(code): code_url = settings.code2Session.format(settings.AppId, settings.AppSecret, code) response = requests.get(code_url) json_response = response.json() if json_response.get('session_key'): return json_response else: return False
5.
import celery import time # broker='redis://127.0.0.1:6379/2' 不加密码 backend='redis://127.0.0.1:6379/1' broker='redis://127.0.0.1:6379/2' cel=celery.Celery('test',backend=backend,broker=broker) import os, sys import django BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 定位到你的django根目录 # sys.path.append(os.path.join(BASE_DIR, "app01")) sys.path.append(os.path.abspath(BASE_DIR)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wxshop.settings") django.setup() from django.db import transaction @cel.task @transaction.atomic def del_order(order_id): ''' 1 拿订单查询,订单号,是否支付,活跃 2 判断data是否有 :param order_id: :return: ''' from app01 import models data=models.Order.objects.filter(order_id=order_id,pay_status=0,status="active").first() #主订单 if data: item_data=models.Order_items.objects.filter(order_id=order_id).values("product","nums") #子订单们 # [{product:1,nums:3}] all_product_dict = { k['product']:k['nums'] for k in item_data} #子订单的商品id和数量{1:3,2:1} all_product_id =list(all_product_dict.keys()) #所有商品id products_all=models.Product.objects.filter(product_id__in=all_product_id) #所有商品们 sid=transaction.savepoint() #循环出每个商品 for product in products_all: for i in range(3): stock=product.stock.quantity #数据库商品库存 new_stock=stock+all_product_dict[product.product_id] #数据库商品库存+订单每个商品数量 new_buy_count=product.buy_count-all_product_dict[product.product_id] #商品销量——订单每个商品数量 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock).update(quantity=new_stock) #跟新库存 if not res: if i==2: from app01.comment import func transaction.savepoint_rollback(sid) func.add_task(order_id,1) return else: continue else: break models.Product.objects.filter(product_id=product.product_id).update(buy_count=new_buy_count) #更新商品销量 row=models.Order.objects.filter(order_id=order_id,pay_status=0).update(status="dead") #更新订单状态为作废 if row: transaction.savepoint_commit(sid) else: transaction.savepoint_rollback(sid)