django实现多种支付方式

  1. '''
  2. #思路
  3. 我们希望,通过插拔的方式来实现多方式登录,比如新增一种支付方式,那么只要在项目中新增一个py文件,导入里面的pay方法就可以了,这样在支付业务中支付语句是不发生变化的。
  4. 所以就可以使用python的鸭子类型及面向对象的反射方法来实现功能
  5. '''
  6. ##新建一个Pay文件夹,里面放支付方式.py文件
  7. #Alipay.py
  8. class Alipay:
  9. def pay(self):
  10. pass
  11. #Visapay.py
  12. class Visapay:
  13. def pay(self):
  14. pass
  15. #Wxpay.py(完全按照接口文档来得)
  16. import time
  17. #记得导入商户号和key哦!
  18. from app01.wx import settings
  19. class Wxpay:
  20. def pay(self,order_data):
  21. self.order_id = order_data["order_id"]
  22. self.open_id = order_data['open_id']
  23. self.ip = order_data['ip']
  24. data_body = self.get_body_data()
  25. import requests
  26. url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
  27. response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"})
  28. res_dict = self.xml_to_dic(response.content)
  29. timeStamp = str(int(time.time()))
  30. paySign = self.get_pay_sign(res_dict, timeStamp)
  31. data_dic = {
  32. 'timeStamp': timeStamp,
  33. 'nonceStr': res_dict['nonce_str'],
  34. 'package': f"prepay_id={res_dict['prepay_id']}",
  35. 'signType': 'MD5',
  36. "paySign": paySign,
  37. }
  38. return data_dic
  39. def get_pay_sign(self, res_dict, timeStamp):
  40. print("res_dict", res_dict)
  41. data_dic = {
  42. 'appId': res_dict['appid'],
  43. 'timeStamp': timeStamp,
  44. 'nonceStr': res_dict['nonce_str'],
  45. 'package': f"prepay_id={res_dict['prepay_id']}",
  46. "signType": "MD5"
  47. }
  48. sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
  49. sign_str = f"{sign_str}&key={settings.pay_apikey}"
  50. import hashlib
  51. md5 = hashlib.md5()
  52. md5.update(sign_str.encode("utf-8"))
  53. sign = md5.hexdigest()
  54. return sign.upper()
  55. def xml_to_dic(self, xml_data):
  56. import xml.etree.ElementTree as ET
  57. '''
  58. xml to dict
  59. :param xml_data:
  60. :return:
  61. '''
  62. xml_dict = {}
  63. root = ET.fromstring(xml_data)
  64. for child in root:
  65. xml_dict[child.tag] = child.text
  66. return xml_dict
  67. def get_random(self):
  68. import random
  69. data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP"
  70. nonce_str = "".join(random.sample(data, 30))
  71. return nonce_str
  72. def get_sign(self):
  73. data_dic = {
  74. "nonce_str": self.nonce_str,
  75. "out_trade_no": self.out_trade_no,
  76. "spbill_create_ip": self.spbill_create_ip,
  77. "notify_url": self.notify_url,
  78. "openid": self.open_id,
  79. "body": self.body,
  80. "trade_type": "JSAPI",
  81. "appid": self.appid,
  82. "total_fee": "1",
  83. "mch_id": self.mch_id
  84. }
  85. sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
  86. sign_str = f"{sign_str}&key={settings.pay_apikey}"
  87. import hashlib
  88. md5 = hashlib.md5()
  89. md5.update(sign_str.encode("utf-8"))
  90. sign = md5.hexdigest()
  91. return sign.upper()
  92. def get_body_data(self):
  93. self.appid = settings.AppId
  94. # openid=self.open_id
  95. self.mch_id = str(settings.pay_mchid)
  96. self.nonce_str = self.get_random()
  97. self.out_trade_no = self.order_id
  98. self.spbill_create_ip = self.ip
  99. self.notify_url = "https://www.test.com"
  100. self.body = "老男孩学费"
  101. self.sign = self.get_sign()
  102. body_data = f"""
  103. <xml>
  104. <appid>{self.appid}</appid>
  105. <mch_id>{self.mch_id}</mch_id>
  106. <nonce_str>{self.nonce_str}</nonce_str>
  107. <sign>{self.sign}</sign>
  108. <body>{self.body}</body>
  109. <out_trade_no>{self.out_trade_no}</out_trade_no>
  110. <total_fee>1</total_fee>
  111. <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip>
  112. <notify_url>{self.notify_url}</notify_url>
  113. <openid>{self.open_id}</openid>
  114. <trade_type>JSAPI</trade_type>
  115. </xml>"""
  116. return body_data
  117. ##调用支付方法的语句(一般支付都是发生在订单创建好之后)
  118. import importlib
  119. from rest_framework.response import Response
  120. pay_method = "Wxpay" #这里是举例子,所以把pay_method写死了,正常情况下,应该是外面传来的支付方式,然后用pay_method接收
  121. try:
  122. #用字符串导入支付方式的py文件,例如这里的app01.Pay.{pay_method}
  123. pay_field = importlib.import_module(f"app01.Pay.{pay_method}")
  124. #用反射拿到该文件下面的类
  125. pay_method_class = getattr(pay_field, pay_method)
  126. except:
  127. return Response({"code": 205, "msg": "错误的支付方式"})
  128. order_data['ip'] = host_ip
  129. order_data['open_id'] = open_id
  130. #完成支付,并把支付数据返回
  131. pay_data = pay_method_class().pay(order_data)
  132. '''
  133. 这里直接用反射拿到的支付类,然后使用它的pay方法,完成支付
  134. '''
  135. return Response({"code": 200, "msg": "ok", "data": pay_data})

django实现订单创建及支付

  1. '''
  2. 几个注意点:
  3. 1.a,b两个用户同时买一个库存为1的商品,这样为了保证数据安全(即a买了,库存没更新,b又买了,这样就存在安全问题),需要在数据库操作时加锁,有两个选择:悲观锁和乐观锁。
  4. 悲观锁:冲突比较多的时候,使用悲观锁。悲观锁获取数据时对数据行了锁定,其他事务要想获取锁,必须等原事务结束。
  5. 乐观锁:冲突比较少的时候,使用乐观锁。查询时不锁数据,提交更改时进行判断.使用乐观锁前,要先 设置mysql事务的隔离级别transaction-isolation = READ-COMMITTED
  6. 2.a用户的订单有两种商品,这样提交订单后,假如第一种成功,第二种失败,很显然在订单一个函数里面写一个逻辑是行不通的,应该要么同时成功,要么同时失败。于是自然而然就用到了事务。
  7. '''
  8. #urls.py
  9. from django.urls import path
  10. from app01.view import order
  11. urlpattern = [
  12. path('order/create', order.Create.as_view()),
  13. ]
  14. #common文件夹下的func.py文件
  15. import time,random
  16. def get_order_id():
  17. str_all="1242356796734534"
  18. return time.strftime("%Y%m%d%H%M%S")+"".join(random.sample(str_all,5))
  19. #view文件夹下的order.py文件
  20. from rest_framework.views import APIView
  21. from rest_framework.response import Response
  22. from django.db import transaction
  23. from django import forms
  24. from django.core.cache import cache
  25. from common import func
  26. class OrderForm():
  27. phone = forms.CharField(
  28. error_message = {
  29. 'required': "手机号不能为空"
  30. },
  31. # 调用Form组件中的验证器来校验手机号
  32. # validators=[RegexValidator(r'1[1-9][0-9]{9}', '手机号格式不正确')],
  33. )
  34. token = forms.CharField( error_messages={
  35. "required": "token不能为空"
  36. })
  37. province=forms.CharField( error_messages={
  38. "required": "省份不能为空"
  39. })
  40. city = forms.CharField(error_messages={
  41. "required": "城市不能为空"
  42. })
  43. county = forms.CharField(error_messages={
  44. "required": "县/区不能为空"
  45. })
  46. address = forms.CharField(error_messages={
  47. "required": "详细地址不能为空"
  48. })
  49. name = forms.CharField(error_messages={
  50. "required": "姓名不能为空"
  51. })
  52. class Create(APIView):
  53. @transaction.atomic
  54. def post(self, requset):
  55. param = request.data
  56. #form表单检验订单数据是否符合规范
  57. for_obj = OrderForm(param)
  58. #校验成功,并且买东西了
  59. if for_obj.is_valid() and param.get('buy_list'):
  60. buy_list = param.get("buy_list")
  61. #固定方法拿到付款用户的id
  62. if request.META.get("HTTP_X_FORWARDED_FOR"):
  63. host_ip = request.META["HTTP_X_FROWARDED_FOR"]
  64. else:
  65. host_ip = request.META["REMOTE_ADDR"]
  66. #校验token,保证登入状态
  67. cache_data = cache.get(param["token"])
  68. if not cache_data:
  69. return Response({"code": 202, "msg": "错误的token"})
  70. openid = cache_data.split("&")[0]
  71. #通过openid查找用户
  72. user_data = models.Wxuser.objects.filter(openid=openid).first()
  73. #组织部分总订单数据
  74. order_data = {"consignee_mobile": param['phone'],
  75. 'consignee_name': param['name'],
  76. 'wxuser_id': user_data.id,
  77. "memo": param['remark'],
  78. "consignee_area": f"{param['province']},{param['city']},{param['county']}",
  79. "consignee_address": param['address'],
  80. }
  81. order_data['order_id']=func.get_order_id()
  82. order_data["order_total"]=0
  83. #获取用户购买商品的id
  84. all_product_id=list(buy_list.keys())
  85. #通过id来获取商品的信息
  86. product_data=models.Product.objects.filter(product_id__in=all_product_id).all()
  87. #开启事务
  88. sid = transaction.savepoint()
  89. for product in product_data:
  90. product.product_id=str(product.product_id)
  91. # num=buy_list[product.id]
  92. #获取商品的库存
  93. for i in range(3):
  94. product_stock = product.stock.quantity
  95. new_product_stock = product_stock-buy_list[product.product_id]
  96. if new_product_stock<0:
  97. transaction.savepoint_rollback(sid)
  98. return Response({"code": 204, "msg": f"{product.name}库存不足"})
  99. #乐观锁
  100. res=models.Stock.objects.filter(quantity=product_stock,stock_id=product.stock.stock_id).update(quantity=new_product_stock)
  101. if not res :
  102. #如果两次都不成功,就让用户重新下单
  103. if i==2:
  104. transaction.savepoint_rollback(sid)
  105. return Response({"code": 205, "msg": "下单失败从新下单"})
  106. else:
  107. continue
  108. else:
  109. break
  110. #组织子订单数据
  111. order_item_data = {'order_id': order_data['order_id'], 'product_id': product.product_id,"name": product.name, "image": product.image, "price": product.price,
  112. "nums": buy_list[product.product_id], "brief": product.brief}
  113. #创建数据
  114. models.Order_items.objects.create(**order_item_data)
  115. #获取订单总金额
  116. order_data["order_total"] += buy_list[product.product_id]*product.price
  117. #创建总订单
  118. models.Order.objects.create(**order_data)
  119. transaction.savepoint_commit(sid)
  120. #提交延时任务,判断订单再指定时间内,有没有支付,如果没有支付,回滚库存,取消订单
  121. func.check_order(order_data['order_id'])
  122. #如果pay_methon是外面传的
  123. pay_methon= "Wxpay"
  124. try:
  125. #导入app01.Pay.{pay_methon}
  126. pay_filed=importlib.import_module(f"app01.Pay.{pay_methon}")
  127. #用反射获取,这个文件中的类
  128. pay_methon_class=getattr(pay_filed,pay_methon)
  129. except:
  130. return Response({"code": 205, "msg": "错误的支付方式"})
  131. order_data['ip'] = host_ip
  132. order_data['open_id'] = openid
  133. #获取小程序所需的支付数据
  134. pay_data= pay_methon_class().pay(order_data)
  135. # pay_methon ="Alipay"
  136. # if pay_methon=="Wxpay":
  137. # Wxpay().pay
  138. # elif:...
  139. # pay_methon
  140. return Response({"code": 200, "msg": "ok","data":pay_data})
  141. else:
  142. return Response({"code": 203, "msg": "缺少参数"})
  143. class Notify(APIView):
  144. def post(self,request,paymethon):
  145. pay_filed = importlib.import_module(f"app01.Pay.{paymethon}")
  146. pay_methon_class = getattr(pay_filed, paymethon)
  147. data=pay_methon_class().notify(request.data)
  148. if data['stauts']=="suc":
  149. models.Order.objects.filter(order_id=data['order_id']).update(pay_status="")

celery实现库存回滚

  1. #proj_celery文件夹下的celery.py文件
  2. import celery
  3. import time
  4. # broker='redis://127.0.0.1:6379/2' 不加密码
  5. backend = 'redis://127.0.0.1:6379/1'
  6. broker = 'redis://127.0.0.1:6379/2'
  7. cel = celery.Celery('test', backend=backend, broker=broker)
  8. import os, sys
  9. import django
  10. BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 定位到你的django根目录
  11. # sys.path.append(os.path.join(BASE_DIR, "app01"))
  12. sys.path.append(os.path.abspath(BASE_DIR))
  13. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "shop.settings")
  14. django.setup()
  15. from django.db import transaction
  16. @cel.task
  17. @transaction.atomic
  18. def del_order(order_id):
  19. from app01 import models
  20. # 查看订单数据
  21. order_data = models.Order.objects.filter(order_id=order_id, pay_status=False).first()
  22. # 如果有数据表示没有支付,要进行库存回滚,和取消订单
  23. if order_data:
  24. # 获取该订单下的所有子订单
  25. order_items = models.Order_items.objects.filter(order_id=order_id).all()
  26. # 将子订单中的数据转变成 {商品id:购买数量,。。。}的格式
  27. product_all_dic = {item.product_id: item.nums for item in order_items}
  28. # 获取所有商品的id,成为list格式
  29. product_all_id = list(product_all_dic.keys())
  30. # 获取所有的商品
  31. all_product = models.Product.objects.filter(product_id__in=product_all_id).all()
  32. sid = transaction.savepoint()
  33. # 把对应的商品进行库存回滚
  34. for product in all_product:
  35. for i in range(3):
  36. stock = product.stock.quantity
  37. new_stock = stock + product_all_dic[product.product_id]
  38. #乐观锁
  39. res = models.Stock.objects.filter(stock_id=product.stock.stock_id, quantity=stock).update(
  40. quantity=new_stock)
  41. if not res:
  42. if i == 2:
  43. transaction.savepoint_rollback(sid)
  44. # 如果这个执行失败了,那我们要从新提交任务,不然库存无法回滚
  45. from app01.common import func
  46. func.check_order(order_id, 1)
  47. return
  48. else:
  49. continue
  50. else:
  51. break
  52. # 修改订单状态
  53. res1 = models.Order.objects.filter(order_id=order_id, pay_status=False).update(status="dead")
  54. if res1:
  55. transaction.savepoint_commit(sid)
  56. else:
  57. 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. My97DatePicker-WdatePicker日历日期插件详细示例

    <!DOCTYPE html> <html> <head> <title>排行</title> <meta charset=" ...

  2. SAP用户角色分配函数权限

    事务码 PFCG修改角色 选择[权限缺省] 选择 RFC 输入的函数只能是允许远程连接的函数,否则不能调用和添加到角色. 完成添加.

  3. 数据库 tcp协程实现并发 回调函数

    数据库 tcp协程实现并发 回顾 一.回顾 进程池,线程池,回调函数 # from gevent import monkey;monkey.patch_all() #补丁 from gevent im ...

  4. DBUtils的使用之查询的操作

    1.1 查询的代码实现1.1.1.1 查询一条记录 l 创建一个对象:Account l 查询代码实现 1.1.1.2 查询多条记录

  5. OpenCV-3.4.3图像通道处理

    图像通道处理 图像读取和处理都是按BGR通道顺序进行的 #include <iostream> #include <opencv2/opencv.hpp> #include & ...

  6. sshd服务安装-ssh命令使用方法

    SSHD服务概述 介绍:SSH协议:安全外壳协议.为 Secure Shell的缩写.SSH 为建立在应用层和传输层基础上的安全协议. 作用:SSHD服务使用SSH协议可以用来进行远程控制,或在计算机 ...

  7. CSS权重的进制问题

    这是复习篇的第一个知识点,(CSS权重进制在IE6为256,后来扩大到了65536.而现代浏览器则采用更大的数量)在说这个知识点之前我们先来看一个例子 <!DOCTYPE html> &l ...

  8. Mac PyCharm2019激活码

    此教程支持最新2019.2版本Pycharm及其他软件 此教程实时更新,请放心使用:如果有新版本出现猪哥都会第一时间尝试激活: pycharm官网下载地址:http://www.jetbrains.c ...

  9. netty源码解析(4.0)-21 ByteBuf的设计原理

        io.netty.buffer包中是netty ByteBuf的实现.ByteBuf是一个二进制缓冲区的抽象接口,它的功能有: 可以随机访问.顺序访问. 支持基本数据类型(byte, shor ...

  10. C++ 深入浅出工厂模式(进阶篇)

    介绍 前文初始篇C++ 深入浅出工厂模式(初始篇),主要阐述了简单工厂模式.工厂方法模式和抽象工厂模式的结构.特点和缺陷等.以上三种方式,在新增产品时,要么修改工厂类,要么需新增具体的工厂类,说明工厂 ...