django中使用事务及接入支付宝支付功能


Posted in Python onSeptember 15, 2019

之前一直想记录一下在项目中使用到的事务以及支付宝支付功能,自己一直犯懒没有完,趁今天有点兴致,在这记录一下。

商城项目必备的就是支付订单的功能,所以就会涉及到订单的保存以及支付接口的引入。先来看看订单的保存,在数据库模型涉及之初,将订单分成了两个表,一个为订单表,记录订单的基本信息,如订单号,用户信息,运费之类,一个为订单商品表,记录该订单中的商品信息。在保存订单时,肯定会涉及到两个表的新建和保存,其实还有一张表也需要进行一些修改,那就是商品表,当一个订单保存成功,意味着本次交易成功,商品售出,商品的库存应该进行修改。所以,在保存订单这一操作中,涉及到的表有三张。所以在保存订单时,多表数据的修改,要嘛同时成功,要嘛同时失败,这就跟数据库中的事务很像,因此,在这里引入事务,来完成订单保存的功能。

在Django中可以通过 django.db.transaction 模块提供的 atomic 来定义一个事务, atomic 提供两种用法,一种是装饰器,一种是with语句。

from django.db import transaction
@transaction.atomic
def viewfunc(request):
  # 这些代码会在一个事务中执行
  ...
from django.db import transaction
def viewfunc(request):
  # 这部分代码不在事务中,会被Django自动提交
  ...
  with transaction.atomic():
    # 这部分代码会在事务中执行
    ... 

 

在Django中,还提供了保存点的支持,可以在事务中创建保存点来记录数据的特定状态,数据库出现错误时,可以恢复到数据保存点的状态

from django.db import transaction
# 创建保存点
save_id = transaction.savepoint() 
# 回滚到保存点
transaction.savepoint_rollback(save_id)
# 提交从保存点到当前状态的所有数据库事务操作
transaction.savepoint_commit(save_id)

所以,可以在序列化器的create方法中,创建一个事务,还进行数据的修改保存还有新建,若有地方出错,则直接回滚,若没有问题则提交事务。代码如下

   

def create(self, validated_data): """ 保存订单 """ # 获取当前下单用户 user = self.context['request'].user # 组织订单编号 20170903153611+user.id # timezone.now() -> datetime order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id) address = validated_data['address'] pay_method = validated_data['pay_method'] # 生成订单 with transaction.atomic(): # 创建一个保存点 save_id = transaction.savepoint() try: # 创建订单信息 order = OrderInfo.objects.create( order_id=order_id, user=user, address=address, total_count=0, total_amount=Decimal(0), freight=Decimal(10), pay_method=pay_method, status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID'] ) # 获取购物车信息 redis_conn = get_redis_connection("cart") redis_cart = redis_conn.hgetall("cart_%s" % user.id) cart_selected = redis_conn.smembers('cart_selected_%s' % user.id) # 将bytes类型转换为int类型 cart = {} for sku_id in cart_selected: cart[int(sku_id)] = int(redis_cart[sku_id]) # # 一次查询出所有商品数据 # skus = SKU.objects.filter(id__in=cart.keys()) # 处理订单商品 sku_id_list = cart.keys() for sku_id in sku_id_list: while True: sku = SKU.objects.get(id=sku_id) sku_count = cart[sku.id] # 判断库存 origin_stock = sku.stock # 原始库存 origin_sales = sku.sales # 原始销量 if sku_count > origin_stock: transaction.savepoint_rollback(save_id) raise serializers.ValidationError('商品库存不足') # 用于演示并发下单 # import time # time.sleep(5) # 减少库存 # sku.stock -= sku_count # sku.sales += sku_count # sku.save() new_stock = origin_stock - sku_count new_sales = origin_sales + sku_count # 根据原始库存条件更新,返回更新的条目数,乐观锁 ret = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales) if ret == 0: continue # 累计商品的SPU 销量信息 sku.goods.sales += sku_count sku.goods.save() # 累计订单基本信息的数据 order.total_count += sku_count # 累计总金额 order.total_amount += (sku.price * sku_count) # 累计总额 # 保存订单商品 OrderGoods.objects.create( order=order, sku=sku, count=sku_count, price=sku.price, ) # 更新成功 break # 更新订单的金额数量信息 order.total_amount += order.freight order.save() except serializers.ValidationError: raise except Exception as e: logger.error(e) transaction.savepoint_rollback(save_id) raise # 提交事务 transaction.savepoint_commit(save_id) # 更新redis中保存的购物车数据 pl = redis_conn.pipeline() pl.hdel('cart_%s' % user.id, *cart_selected) pl.srem('cart_selected_%s' % user.id, *cart_selected) pl.execute() return order

还有一点需要注意的是,当订单提交,购物车中相应的商品应该进行删除。好了,以上就是django中的事务。

再来说说支付宝支付功能的引入,现在基本上所有的项目涉及到支付功能时都会引入第三方支付,其中使用最广泛的应该就是支付宝和微信了,这里我使用的是支付宝支付。当订单创建完成,接下来就是支付了。

先来缕一下流程,用户点击按钮请求支付宝支付界面,先进行登录,登录成功后进行支付操作,支付成功会进行回调。

首先第一步,用户点击按钮,后端会进行url的拼接,将拼接好的url返给前端,前端进行跳转,跳转到支付宝相关界面,用户进行登录和支付等操作。

查看pythonsdk,首先我们可以通过openssl命令生成一个密钥(公钥和私钥),私钥自己留存,公钥用户校验。命令如下:

openssl

OpenSSL> genrsa -out app_private_key.pem  2048 # 私钥 OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥 OpenSSL> exit

同时,你要从支付宝获得一个公钥字符串,格式可参考:https://github.com/fzlee/alipay/blob/master/tests/certs/ali/ali_public_key.pem

由于上面我们用的是RSA生成的密钥,所以在支付宝中我们也需要RSA的公钥

django中使用事务及接入支付宝支付功能

设置好了密钥,我们就可以开始写视图,代码如下:

class PaymentView(APIView):
  """
  支付
  """
  permission_classes = (IsAuthenticated,)
  def get(self, request, order_id):
    """
    获取支付链接
    """
    # 判断订单信息是否正确
    try:
      order = OrderInfo.objects.get(order_id=order_id, user=request.user,
                     pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"],
                     status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"])
    except OrderInfo.DoesNotExist:
      return Response({'message': '订单信息有误'}, status=status.HTTP_400_BAD_REQUEST)
    # 构造支付宝支付链接地址
    alipay = AliPay(
      appid=settings.ALIPAY_APPID,
      app_notify_url=None, # 默认回调url
      app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"),
      alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
      sign_type="RSA2", # RSA 或者 RSA2
      debug=settings.ALIPAY_DEBUG # 默认False
    )
    order_string = alipay.api_alipay_trade_page_pay(
      out_trade_no=order_id,
      total_amount=str(order.total_amount),
      subject="美多商城%s" % order_id,
      return_url="http://www.meiduo.site:8080/pay_success.html", 
    )
    # 需要跳转到https://openapi.alipay.com/gateway.do? + order_string
    # 拼接链接返回前端
    alipay_url = settings.ALIPAY_URL + "?" + order_string
    return Response({'alipay_url': alipay_url})

相关的参数可以提前在配置文件中配置好(ALPAY_APPID,ALPAY_URL,ALPAY_DEBUG)注意ALPAY为True时才启用沙箱环境。当用户支付成功,会对你填写的回调网址进行回调,返回的参数如下图。

django中使用事务及接入支付宝支付功能 

前端页面将此数据发送给后端,后端检验并保存支付结果。以上就是全部过程。具体的过程可以参考pythonsdk。

总结

以上所述是小编给大家介绍的django中使用事务及接入支付宝支付功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Python 相关文章推荐
python 输出一个两行字符的变量
Feb 05 Python
Python实现将DOC文档转换为PDF的方法
Jul 25 Python
Python判断文本中消息重复次数的方法
Apr 27 Python
一个基于flask的web应用诞生(1)
Apr 11 Python
Python使用django框架实现多人在线匿名聊天的小程序
Nov 29 Python
python爬取酷狗音乐排行榜
Feb 20 Python
python使用requests模块实现爬取电影天堂最新电影信息
Apr 03 Python
新手入门Python编程的8个实用建议
Jul 12 Python
pyenv与virtualenv安装实现python多版本多项目管理
Aug 17 Python
python 普通克里金(Kriging)法的实现
Dec 19 Python
pycharm中选中一个单词替换所有重复单词的实现方法
Nov 17 Python
Python机器学习工具scikit-learn的使用笔记
Jan 28 Python
Django中的FBV和CBV用法详解
Sep 15 #Python
Python爬虫实现使用beautifulSoup4爬取名言网功能案例
Sep 15 #Python
Python爬虫实现的根据分类爬取豆瓣电影信息功能示例
Sep 15 #Python
Python实现的爬取豆瓣电影信息功能案例
Sep 15 #Python
Python数据库小程序源代码
Sep 15 #Python
python中树与树的表示知识点总结
Sep 14 #Python
python树的同构学习笔记
Sep 14 #Python
You might like
php中异常处理方法小结
2015/01/09 PHP
Yii框架实现邮箱激活的方法【数字签名】
2016/10/18 PHP
javascript 常用关键字列表集合
2007/12/04 Javascript
JavaScript 对话框和状态栏使用说明
2009/10/25 Javascript
工作中常用到的JS表单验证代码(包括例子)
2010/11/11 Javascript
页面使用密码保护代码
2013/04/10 Javascript
固定网页背景图同时保持图片比例的思路代码
2013/08/15 Javascript
jquery 无限级下拉菜单的简单实现代码
2014/02/21 Javascript
详解Javascript中的Object对象
2016/02/28 Javascript
javascript获取wx.config内部字段解决微信分享
2016/03/09 Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
2016/12/27 Javascript
VUE实现日历组件功能
2017/03/13 Javascript
原生JS实现 MUI导航栏透明渐变效果
2017/11/07 Javascript
Vue 中 a标签上href无法跳转的解决方式
2019/11/12 Javascript
详解vue中在循环中使用@mouseenter 和 @mouseleave事件闪烁问题解决方法
2020/04/07 Javascript
Vue 组件的挂载与父子组件的传值实例
2020/09/02 Javascript
Javascript中window.name属性详解
2020/11/19 Javascript
Python批量修改文本文件内容的方法
2016/04/29 Python
Python实现基于PIL和tesseract的验证码识别功能示例
2018/07/11 Python
对Python 3.5拼接列表的新语法详解
2018/11/08 Python
对Python3 解析html的几种操作方式小结
2019/02/16 Python
python cumsum函数的具体使用
2019/07/29 Python
详解PyTorch中Tensor的高阶操作
2019/08/18 Python
python列表返回重复数据的下标
2020/02/10 Python
python两种注释用法的示例
2020/10/09 Python
Brasty波兰:香水、化妆品、手表网上商店
2019/04/15 全球购物
英国奢华护肤、美容和Spa品牌:Temple Spa
2019/11/02 全球购物
什么是SCM(软件配置管理)
2014/08/16 面试题
经典团队口号大全
2014/06/21 职场文书
2015年银行客户经理工作总结
2015/04/01 职场文书
教师节校长致辞
2015/07/31 职场文书
护士心得体会范文
2016/01/25 职场文书
如何使用Python提取Chrome浏览器保存的密码
2021/06/09 Python
5道关于python基础 while循环练习题
2021/11/27 Python
Windows server 2012搭建FTP服务器
2022/04/29 Servers
SQL中的连接查询详解
2022/06/21 SQL Server