django实现多种支付方式

'''
#思路 我们希望,通过插拔的方式来实现多方式登录,比如新增一种支付方式,那么只要在项目中新增一个py文件,导入里面的pay方法就可以了,这样在支付业务中支付语句是不发生变化的。
所以就可以使用python的鸭子类型及面向对象的反射方法来实现功能 ''' ##新建一个Pay文件夹,里面放支付方式.py文件
#Alipay.py
class Alipay:
def pay(self):
pass
#Visapay.py
class Visapay:
def pay(self):
pass
#Wxpay.py(完全按照接口文档来得)
import time
#记得导入商户号和key哦!
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):
print("res_dict", res_dict)
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() 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
# openid=self.open_id
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
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 ##调用支付方法的语句(一般支付都是发生在订单创建好之后)
import importlib
from rest_framework.response import Response
pay_method = "Wxpay" #这里是举例子,所以把pay_method写死了,正常情况下,应该是外面传来的支付方式,然后用pay_method接收
try:
#用字符串导入支付方式的py文件,例如这里的app01.Pay.{pay_method}
pay_field = importlib.import_module(f"app01.Pay.{pay_method}") #用反射拿到该文件下面的类
pay_method_class = getattr(pay_field, pay_method)
except:
return Response({"code": 205, "msg": "错误的支付方式"}) order_data['ip'] = host_ip
order_data['open_id'] = open_id #完成支付,并把支付数据返回
pay_data = pay_method_class().pay(order_data)
'''
这里直接用反射拿到的支付类,然后使用它的pay方法,完成支付
'''
return Response({"code": 200, "msg": "ok", "data": pay_data})

django实现订单创建及支付

'''
几个注意点:
1.a,b两个用户同时买一个库存为1的商品,这样为了保证数据安全(即a买了,库存没更新,b又买了,这样就存在安全问题),需要在数据库操作时加锁,有两个选择:悲观锁和乐观锁。
悲观锁:冲突比较多的时候,使用悲观锁。悲观锁获取数据时对数据行了锁定,其他事务要想获取锁,必须等原事务结束。
乐观锁:冲突比较少的时候,使用乐观锁。查询时不锁数据,提交更改时进行判断.使用乐观锁前,要先 设置mysql事务的隔离级别transaction-isolation = READ-COMMITTED
2.a用户的订单有两种商品,这样提交订单后,假如第一种成功,第二种失败,很显然在订单一个函数里面写一个逻辑是行不通的,应该要么同时成功,要么同时失败。于是自然而然就用到了事务。
'''
#urls.py
from django.urls import path
from app01.view import order
urlpattern = [
path('order/create', order.Create.as_view()),
] #common文件夹下的func.py文件
import time,random
def get_order_id():
str_all="1242356796734534"
return time.strftime("%Y%m%d%H%M%S")+"".join(random.sample(str_all,5)) #view文件夹下的order.py文件
from rest_framework.views import APIView
from rest_framework.response import Response
from django.db import transaction
from django import forms
from django.core.cache import cache
from common import func class OrderForm():
phone = forms.CharField(
error_message = {
'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 Create(APIView):
@transaction.atomic
def post(self, requset):
param = request.data
#form表单检验订单数据是否符合规范
for_obj = OrderForm(param)
#校验成功,并且买东西了
if for_obj.is_valid() and param.get('buy_list'):
buy_list = param.get("buy_list")
#固定方法拿到付款用户的id
if request.META.get("HTTP_X_FORWARDED_FOR"):
host_ip = request.META["HTTP_X_FROWARDED_FOR"]
else:
host_ip = request.META["REMOTE_ADDR"]
#校验token,保证登入状态
cache_data = cache.get(param["token"])
if not cache_data:
return Response({"code": 202, "msg": "错误的token"})
openid = cache_data.split("&")[0]
#通过openid查找用户
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'],
} order_data['order_id']=func.get_order_id()
order_data["order_total"]=0 #获取用户购买商品的id
all_product_id=list(buy_list.keys())
#通过id来获取商品的信息
product_data=models.Product.objects.filter(product_id__in=all_product_id).all() #开启事务
sid = transaction.savepoint()
for product in product_data:
product.product_id=str(product.product_id)
# num=buy_list[product.id]
#获取商品的库存
for i in range(3):
product_stock = product.stock.quantity
new_product_stock = product_stock-buy_list[product.product_id]
if new_product_stock<0:
transaction.savepoint_rollback(sid)
return Response({"code": 204, "msg": f"{product.name}库存不足"})
#乐观锁
res=models.Stock.objects.filter(quantity=product_stock,stock_id=product.stock.stock_id).update(quantity=new_product_stock)
if not res :
#如果两次都不成功,就让用户重新下单
if i==2:
transaction.savepoint_rollback(sid)
return Response({"code": 205, "msg": "下单失败从新下单"})
else:
continue
else:
break #组织子订单数据
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) #获取订单总金额
order_data["order_total"] += buy_list[product.product_id]*product.price #创建总订单
models.Order.objects.create(**order_data) transaction.savepoint_commit(sid) #提交延时任务,判断订单再指定时间内,有没有支付,如果没有支付,回滚库存,取消订单
func.check_order(order_data['order_id']) #如果pay_methon是外面传的
pay_methon= "Wxpay"
try:
#导入app01.Pay.{pay_methon}
pay_filed=importlib.import_module(f"app01.Pay.{pay_methon}") #用反射获取,这个文件中的类
pay_methon_class=getattr(pay_filed,pay_methon)
except:
return Response({"code": 205, "msg": "错误的支付方式"}) order_data['ip'] = host_ip
order_data['open_id'] = openid #获取小程序所需的支付数据
pay_data= pay_methon_class().pay(order_data) # pay_methon ="Alipay"
# if pay_methon=="Wxpay":
# Wxpay().pay
# elif:...
# pay_methon
return Response({"code": 200, "msg": "ok","data":pay_data})
else:
return Response({"code": 203, "msg": "缺少参数"}) class Notify(APIView):
def post(self,request,paymethon):
pay_filed = importlib.import_module(f"app01.Pay.{paymethon}")
pay_methon_class = getattr(pay_filed, paymethon)
data=pay_methon_class().notify(request.data)
if data['stauts']=="suc":
models.Order.objects.filter(order_id=data['order_id']).update(pay_status="")

celery实现库存回滚

#proj_celery文件夹下的celery.py文件
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", "shop.settings")
django.setup()
from django.db import transaction @cel.task
@transaction.atomic
def del_order(order_id): from app01 import models # 查看订单数据
order_data = models.Order.objects.filter(order_id=order_id, pay_status=False).first() # 如果有数据表示没有支付,要进行库存回滚,和取消订单
if order_data: # 获取该订单下的所有子订单
order_items = models.Order_items.objects.filter(order_id=order_id).all() # 将子订单中的数据转变成 {商品id:购买数量,。。。}的格式
product_all_dic = {item.product_id: item.nums for item in order_items} # 获取所有商品的id,成为list格式
product_all_id = list(product_all_dic.keys()) # 获取所有的商品
all_product = models.Product.objects.filter(product_id__in=product_all_id).all() sid = transaction.savepoint() # 把对应的商品进行库存回滚
for product in all_product:
for i in range(3):
stock = product.stock.quantity
new_stock = stock + product_all_dic[product.product_id] #乐观锁
res = models.Stock.objects.filter(stock_id=product.stock.stock_id, quantity=stock).update(
quantity=new_stock) if not res:
if i == 2:
transaction.savepoint_rollback(sid) # 如果这个执行失败了,那我们要从新提交任务,不然库存无法回滚
from app01.common import func
func.check_order(order_id, 1)
return
else:
continue
else:
break
# 修改订单状态
res1 = models.Order.objects.filter(order_id=order_id, pay_status=False).update(status="dead")
if res1:
transaction.savepoint_commit(sid)
else:
transaction.savepoint_rollback(sid)

悲观锁和乐观锁

悲观锁和乐观锁与并发订单处理

django实现多种支付、并发订单处理的更多相关文章

  1. 递归与树的写法-多种支付的设计-支付的接通-celery订单的回退实现

    递归与树的写法 data: data=[ {"cat_id":1,"name":"北京","parent_id":0}, ...

  2. 微信支付 统一订单 $order = WxPayApi::unifiedOrder($input); 断点调试

    定位至 CODE /** * 将xml转为array * @param string $xml * @throws WxPayException */ public static function I ...

  3. 如何在Django模型中管理并发性 orm select_for_update

    如何在Django模型中管理并发性 为单用户服务的桌面系统的日子已经过去了 - 网络应用程序现在正在为数百万用户提供服务,许多用户出现了广泛的新问题 - 并发问题. 在本文中,我将介绍在Django模 ...

  4. Django实现支付宝支付

    一 去支付宝申请 - 正式:营业执照 - 测试: 沙箱测试环境    APPID:2016092000554391    买家: esnrce2727@sandbox.com    登录和支付密码: ...

  5. django 实现电子支付功能

    思路:调用第三方支付 API 接口实现支付功能.本来想用支付宝来实现第三方网站的支付功能的,但是在实际操作中发现支付宝没有 Python 接口,网上虽然有他人二次封装的的 Python 接口,但是对我 ...

  6. Django 对接 支付宝支付, 回调

    平台 点击这里进入 蚂蚁金服开放平台 沙箱 点击这里进入 沙箱环境 初始界面 设置公钥 下载创建秘钥工具 1.  进入文档中心 这里 2. 选中 电脑网站支付 3. 进入后选中 API 列表 中的 统 ...

  7. 利用微信支付的订单查询接口可以在APP 中提高支付的可靠性

    最近公司有一个应用,用户可以在微信公众号上面下单,也可以在APP 中下单. 当用户在公共号上面下单时,微信支付成功可以返回微信支付单号,但是在APP 中用户微信支付时,个别时候会出现用户已经付款成功, ...

  8. python -django 之第三方支付

    神魔是第三方支付: 第三方支付是指具有一定实力和信誉保障的第三方独立机构.通过与各大银行签订合同,建立连接用户和银行支付结算系统的平台,从而实现电子支付模式.从另一个角度来看,第三方支付就是非金融机构 ...

  9. java 高并发 订单编号递增(解决方案)

    业务描述: 首先从数据中查找最近的一条订单数据,然后将之前的订单号码+1作为新的订单号码,插入到数据库当中.(需求不能改变) 当出现并发操作时,A从数据库中获取最近一条订单的订单号为N,这是A还没有完 ...

随机推荐

  1. C lang:character input and output (I/O)

    Xx_Introduction Character input and output is by more line character conpose of the text flow  Defin ...

  2. Python升级后ssl模块不可用问题解决和浅析

    在Cent0S 7.5下将Python 2.7.5升级到Python 3.6.6后,发现ssl模块不可用,具体详细信息如下所示: [root@db-server ~]# pip list Packag ...

  3. Cocos2d-x.3.0开发环境搭建之—— 极简式环境搭建

    配置:win7 + VS2012 + Cocos2d-x.3.0 + Cocos Studio v1.4.0.1 使用此法可以方便的创建Cocos2d-x项目.如果需要运行Cocos2d-x引擎自带的 ...

  4. 09-Node.js学习笔记-异步编程

    同步API,异步API 同步API:只有当前API执行完成后,才能继续执行下一个API console.log('before'); console.log('after'); 异步API:当前API ...

  5. Git入门基础教程

    目录 一.Git的安装 1.1 图形化界面 1.2 命令行界面 二.本地仓库的创建与提交 2.1 图形化界面 2.1.1 首先在电脑上有一个空白目录 2.1.2 打开SourceTree 2.1.3 ...

  6. acwing 851. spfa求最短路 模板

    地址 https://www.acwing.com/problem/content/description/853/ 给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你求出 ...

  7. WSGI与uWSGI的应用场景与使用方法

    WSGI  /与/  uWSGI 在阿里云上部署项目时,在通信中我们都会用到wsgi与uWSGI,这此我就带大家来了解一下wsgi与uWSGI. 对了,上次有个朋友问我Django的生命周期是什么?我 ...

  8. Java面试题和解答(五)

    1.在Java中Executor和Executors的区别? Executor是线程池的顶层接口,它的实现类如下图所示: Executors是一个类,提供了多个静态方法,用于生成不同类型的线程池,如下 ...

  9. 基于V7的新版RL-USB V6.X + RL-FlashFS V6.X模板,操作CLASS10的SD卡速度12-15MB/S,含RTX5和FreeRTOS两版

    说明: 1.如果需要RL-USB源码的话,将DAPLink(CMSIS-DAP)里面的USB代码导出来即可,DAPLink开源了RL-USB的Device代码.      也可以反过来,在工程模板的基 ...

  10. numpy-np.ceil,np.floor,np.expand_dims方法

    np.ceil(多维数组):对多维数组的各个数向上取整 np.floor(多维数组):对多维数组的各个数向下取整 np.expand_dims(x,axis = 0):在x的第一维度上插入一个维度,a ...