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遍历类中所有成员的方法
Mar 18 Python
Linux下将Python的Django项目部署到Apache服务器
Dec 24 Python
Python 包含汉字的文件读写之每行末尾加上特定字符
Dec 12 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
使用python将mysql数据库的数据转换为json数据的方法
Jul 01 Python
Pycharm远程调试原理及具体配置详解
Aug 08 Python
python 协程 gevent原理与用法分析
Nov 22 Python
Python对称的二叉树多种思路实现方法
Feb 28 Python
在Python中用GDAL实现矢量对栅格的切割实例
Mar 11 Python
python实现秒杀商品的微信自动提醒功能(代码详解)
Apr 27 Python
Python 使用dict实现switch的操作
Apr 07 Python
python 多态 协议 鸭子类型详解
Nov 27 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 动态多文件上传
2009/01/18 PHP
gd库图片下载类实现下载网页所有图片的php代码
2012/08/20 PHP
Laravel 4 初级教程之视图、命名空间、路由
2014/10/30 PHP
调试WordPress中定时任务的相关PHP脚本示例
2015/12/10 PHP
js设置组合快捷键/tabindex功能的方法
2013/11/21 Javascript
js实现键盘控制DIV移动的方法
2015/01/10 Javascript
JQuery中DOM事件合成用法实例分析
2015/06/13 Javascript
老司机带你解读jQuery插件开发流程
2016/05/16 Javascript
require.js配合插件text.js实现最简单的单页应用程序
2016/07/12 Javascript
jQuery使用Layer弹出层插件闪退问题
2016/12/22 Javascript
前端JS面试中常见的算法问题总结
2016/12/23 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
用React-Native+Mobx做一个迷你水果商城APP(附源码)
2017/12/25 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
vue的style绑定background-image的方式和其他变量数据的区别详解
2018/09/03 Javascript
vue-quill-editor富文本编辑器简单使用方法
2018/09/21 Javascript
python使用正则表达式检测密码强度源码分享
2014/06/11 Python
Python处理字符串之isspace()方法的使用
2015/05/19 Python
python自动化UI工具发送QQ消息的实例
2019/08/27 Python
Python 单例设计模式用法实例分析
2019/09/23 Python
pytorch掉坑记录:model.eval的作用说明
2020/06/23 Python
通过Canvas及File API缩放并上传图片完整示例
2013/08/08 HTML / CSS
美国网上购买眼镜:Eyeconic
2017/07/29 全球购物
达拉斯牛仔官方商店:Dallas Cowboys Pro Shop
2018/02/10 全球购物
FORZIERI福喜利中国官网:奢侈品购物梦工厂
2019/05/03 全球购物
DERMAdoctor官网:美国著名皮肤护理品牌
2019/07/06 全球购物
linux面试题参考答案(5)
2016/11/05 面试题
大学生职业生涯规划书范文
2014/01/14 职场文书
汽车装潢店创业计划书范文
2014/02/05 职场文书
大堂副理的岗位职责范文
2014/02/17 职场文书
安全生产实施方案
2014/02/23 职场文书
2014年党务工作总结
2014/11/25 职场文书
给老婆道歉的话
2015/01/20 职场文书
大队委员竞选演讲稿
2015/11/20 职场文书
Python实现8种常用抽样方法
2021/06/27 Python
Go 语言结构实例分析
2021/07/04 Golang