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. 利用zabbix API 统计一段时间内监控项的均值和峰值

    #coding:utf-8 #给定主机IP获取一段时间内cpu的均值和峰值并写入文件(txt,可以跑完后直接把后缀名改为csv以表格形式打开):需要指定IP文件 import requests imp ...

  2. Android 监听手机锁屏的工具类

    自定义 ScreenListener package com.example.teagardenhd.Listener; import android.content.BroadcastReceive ...

  3. ORACLE 12.2RAC之问题 ora.chad OFFLINE

    问题描述: 早上巡检是发现一套RAC的ora.chad一个节点的状态是offline,其他的均正常. crsctl stat res -t ora.chad               ONLINE  ...

  4. centeros系统之上传下载文件

    安装lrzszlrzsz这个软件,可以让我们直接从linux上,下载和上传文件的操作 yum install -y lrzsz11.上传文件通过输入 rz命令,可以弹出上传文件的对话框,然后就可以上传 ...

  5. chunked

    简介 chunked是一种HTTP协议的分块传输编码的机制,即一个HTTP消息可以分成多个部分进行传输,它对于HTTP请求和HTTP响应都适用.对于非持续连接,浏览器通过连接是否关闭来界定请求和响应实 ...

  6. redis 开源客户端下载

    redis 开源客户端下载地址: https://github.com/qishibo/AnotherRedisDesktopManager/releases

  7. 桶排序(C语言)

    #include <stdio.h> int main(void) { int arr[5]={2,5,1,3,3}; //定义需要排序的数组 int res[6]={0}; //初始化& ...

  8. java中使用lambda表达式

    使用lambda表达式能够使复杂的编写方式变的简单 lambda表达式的语法 (parameters) -> expression 或 (parameters) ->{ statement ...

  9. pymysql用法,Python连接MySQL数据库

    Pymysql模块是专门用来操作mysql数据库的模块,使用前需要安装,安装指令:pip install pymysql 操作流程: 第一步:import pymysql 第二步:获取数据库的连接 , ...

  10. IT兄弟连 HTML5教程 CSS3揭秘 CSS选择器1

    要使用CSS对HTML页面中的元素实现一对一.一对多或者多对一的控制,就需要用到CSS选择器.选择器是CSS3中一个重要的内容,使用它可以大幅度地提高开发人员书写或修改样式表的效率.在大型网站中,样式 ...