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针对给定列表中元素进行翻转操作的方法分析
Apr 27 Python
浅谈python中对于json写入txt文件的编码问题
Jun 07 Python
Flask之flask-session的具体使用
Jul 26 Python
python实现朴素贝叶斯算法
Nov 19 Python
分享PyCharm的几个使用技巧
Nov 10 Python
解决pycharm 安装numpy失败的问题
Dec 05 Python
django实现将后台model对象转换成json对象并传递给前端jquery
Mar 16 Python
keras 解决加载lstm+crf模型出错的问题
Jun 10 Python
完美解决TensorFlow和Keras大数据量内存溢出的问题
Jul 03 Python
如何以Winsows Service方式运行JupyterLab
Aug 30 Python
Python 必须了解的5种高级特征
Sep 10 Python
利用Python发送邮件或发带附件的邮件
Nov 12 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
德劲1103的维修打理经验
2021/03/02 无线电
php 缩略图实现函数代码
2011/06/23 PHP
PHP管理内存函数 memory_get_usage()使用介绍
2012/09/23 PHP
使用session判断用户登录用户权限(超简单)
2013/06/08 PHP
PHP+Ajax实现的无刷新分页功能详解【附demo源码下载】
2017/07/03 PHP
php学习笔记之mb_strstr的基本使用
2018/02/03 PHP
解决laravel5中auth用户登录其他页面获取不到登录信息的问题
2019/10/08 PHP
Span元素的width属性无效果原因及解决方案
2010/01/15 Javascript
简单实用的反馈表单无刷新提交带验证
2013/11/15 Javascript
jQuery判断数组是否包含了指定的元素
2015/03/10 Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
2015/06/19 Javascript
使用jQuery实现WordPress中的Ctrl+Enter和@评论回复
2016/05/21 Javascript
老生常谈 关于JavaScript的类的继承
2016/06/24 Javascript
Bootstrap modal使用及点击外部不消失的解决方法
2016/12/13 Javascript
基于vue2的table分页组件实现方法
2017/03/20 Javascript
Vue input控件通过value绑定动态属性及修饰符的方法
2017/05/03 Javascript
JavaScript中正则表达式判断匹配规则及常用方法
2017/08/03 Javascript
AngularJS实现注册表单验证功能
2017/10/16 Javascript
详解ES6中的代理模式——Proxy
2018/01/08 Javascript
vue2 全局变量的设置方法
2018/03/09 Javascript
vue-swiper的使用教程
2018/08/30 Javascript
Node.js 路由的实现方法
2019/06/05 Javascript
浅谈vue单页面中有多个echarts图表时的公用代码写法
2020/07/19 Javascript
python实现mysql的读写分离及负载均衡
2018/02/04 Python
Python求一批字符串的最长公共前缀算法示例
2019/03/02 Python
Django 过滤器汇总及自定义过滤器使用详解
2019/07/19 Python
pycharm中导入模块错误时提示Try to run this command from the system terminal
2020/03/26 Python
python如何进行矩阵运算
2020/06/05 Python
python实现在列表中查找某个元素的下标示例
2020/11/16 Python
Footshop法国:购买运动鞋
2020/01/19 全球购物
什么是封装
2013/03/26 面试题
民族团结先进个人材料
2014/02/05 职场文书
党员反对四风思想汇报范文
2014/10/25 职场文书
店铺转让协议书
2014/12/02 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
2019教师的学习计划
2019/06/25 职场文书