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

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

在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中的事务。

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

支付宝开发平台网址https://open.alipay.com/platform/home.htm。由于开发者账户需要经过审核,支付宝有提供沙箱环境,来进行测试。沙箱应用:https://docs.open.alipay.com/200/105311。沙箱账号:https://openhome.alipay.com/platform/appDaily.htm?tab=account。python对接支付宝SDK:https://github.com/fzlee/alipay/blob/master/README.zh-hans.md。SDK:https://docs.open.alipay.com/270/106291/

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

首先第一步,用户点击按钮,后端会进行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的公钥

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

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时才启用沙箱环境。当用户支付成功,会对你填写的回调网址进行回调,返回的参数如下图。

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

本人QQ:595395786,欢迎交流!!


 

django中使用事务以及接入支付宝支付功能的更多相关文章

  1. ThinkPHP接入支付宝支付功能

    最近做系统,需要实现在线支付功能,毫不犹豫,选择的是支付宝的接口支付功能.这里我用的是即时到帐的接口,具体实现的步骤如下: 一.下载支付宝接口包 下载地址:https://b.alipay.com/o ...

  2. 支付宝支付功能(使用支付宝sdk)

    1.准备参数        新建一个公共参数配置类NewAlipayconfig (可将参数存放到config配置文件中读取)          public class NewAlipayconfi ...

  3. Android 接入支付宝支付实现

    接上篇android接入微信支付文章,这篇我们带你来接入支付宝支付服务 简介 首先要说明的是个人感觉接入支付宝比微信简单多了,很轻松的,所以同学们不要紧张~ 当然还是老规矩啦,上来肯定的贴上官网地址, ...

  4. Android接入支付宝支付实现

    接上篇android接入微信支付文章,这篇我们带你来接入支付宝支付服务 简介 首先要说明的是个人感觉接入支付宝比微信简单多了,很轻松的,所以同学们不要紧张~ 当然还是老规矩啦,上来肯定的贴上官网地址, ...

  5. DJango中开启事务的两种方式

    目录 Django中开启事务的两种方式 第一种 第二种 Django中开启事务的两种方式 第一种 from django.db import transaction with transaction. ...

  6. 事务的隔离级别,mysql中开启事务、django中开启事务

    目录 一.事务的特性 二.数据库中开启事务 三.Django中开启事务的两种方式 第一种 第二种 四.事务的隔离级别 隔离级别 如何查看mysql隔离级别? 修改事务的隔离级别 隔离级别解释 read ...

  7. ThinkPHP5.0 实现 app支付宝支付功能

    前几天做项目,要求要用到支付宝接口,第一次做,弄了好几天 各种坑啊,简单写一下我做支付宝支付的过程,希望对也是第一次做支付宝支付的童鞋有帮助, 不懂的可以先去支付平台看一下支付宝支付的文档,我是下的d ...

  8. laravel实现支付宝支付功能

    起因 前段时间因为项目中需要实现支付宝手机网站支付功能,所以写下这篇文章以作记录,不足之处,欢迎指教. 后端框架:Laravel 5.5 业务功能 适用于商家在移动端网页应用中集成支付宝支付功能.商家 ...

  9. SSM 实现支付宝支付功能(图文详解+完整代码)

    阅读本文大概需要 4 分钟. 前言 本教程详细介绍了如何使用ssm框架实现支付宝支付功能.本文章分为两大部分,分别是「支付宝测试环境代码测试」和「将支付宝支付整合到ssm框架」,详细的代码和图文解释, ...

随机推荐

  1. 自定义SWT控件七之自定义Shell(可伸缩窗口)

    7.可伸缩窗口 该自定义窗口可以通过鼠标随意更改窗口大小 package com.hikvision.encapsulate.view.control.shell; import org.eclips ...

  2. 洛谷P2125 题解

    吐槽: 只能说这道题很数学,本数学蒟蒻推了半天没推出来,只知道要用绝对值,幸亏教练提醒,才勉强想出正解(似乎不是这样的),真的是很无语. 以上皆为吐槽本题,可直接 跳过 分析: 既然题目是要使书架上的 ...

  3. JVM总结(一)

    JVM总结(1) 1.JVM组成: JVM由类加载器子系统.运行时数据区.执行引擎以及本地方法接口组成. 2.JVM运行原理: Java源文件经编译器,编译成字节码程序,通过JVM将每一条指令翻译成不 ...

  4. Element UI系列:Upload图片自定义上传

    HTML部分代码 Javascript部分代码 CSS代码 样式部分可以自由调整 主要实现的原理是利用 http-request 的属性对上传进行自定义

  5. Sublime Text3激活及个性化配置

    [TOC] 在我们的开发过程中,选择正确的开发工具会让我们事半功倍.作为后端开发我们熟悉的myeclipse和itellij idea这些工具我也介绍曾介绍过关于他们的安装及破解.但是我们并不能仅仅使 ...

  6. 利用DoHome APP和音箱控制小车的实验参考步骤

    准备材料: Arduino Uno 一块 Arduino 扩展板        购买链接 DT-06模块一个       购买链接 安卓手机一个 小度音箱一个 小车一个 杜邦线若干 1.DT-06固件 ...

  7. String与new String()的区别

    JVM为了提升性能和减少内存开销,避免字符串的重复创建,维护了一块特殊的内存空间——字符串实例池. String赋值的两种方式. 1.String str = "test"; 以这 ...

  8. 谈谈你对java平台的理解?

    问题:谈谈你对java平台的理解?java是解释执行,这句话对吗? 典型回答:java本身是一种面向对象的语言,具有很好的跨平台的能力,能够做到“write once ,run anywhere”.另 ...

  9. SQLServer数据库处于恢复挂起状态的解决办法

    一.总结 如果数据库处于一个恢复挂起的状态,并且对数据库做脱机和分离的操作,报出数据库文件不可访问的错误,可能是因为数据库的数据文件和日志文件在数据库正常连接的情况下,文件所在的磁盘脱机了,导致数据库 ...

  10. (三)c#Winform自定义控件-有图标的按钮

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...