網(wǎng)站首頁 編程語言 正文
多支付
原理
1.利用鴨子類型。規(guī)定前臺傳過來支付方式。pay_methon
2.再支付方式里面實現(xiàn)pay(名字統(tǒng)一)方法
3.回調(diào)函數(shù),在支付方式里面寫notify(名字統(tǒng)一)統(tǒng)一方法,返回的data統(tǒng)一格式。? ?
eg: data={"statu":'success',"order_id":notity_data['order_id'],"print":"0000"}
?這樣的的牛逼之處:我們在修改、添加支付方式的時候,只需要按照鴨子類型,命名一樣的函數(shù)名,寫好自己的支付方式即可。不需要改其他的代碼
多支付接口代碼
urls.py:
path("order/create",order.Creat.as_view()),
path("order/notify/<paymethod>",order.Notify.as_view())
# 這里所有的支付都是走的小程序微信支付:
import importlib
class Creat(APIView):
...偽代碼
pay_methon = "Wxpay" # 如果是PC端,可以前臺傳過來支付方式
try:
#pay_file是對象
pay_file = importlib.import_module(f"app01.Pay.{pay_methon}") # 調(diào)用對應的支付方式
pay_class = getattr(pay_file, pay_methon) # 反射機制
order_data['open_id'] = openid # 傳的參數(shù)
order_data['ip'] = host_ip # 傳的參數(shù)
data = pay_class().pay(order_data) # 調(diào)用支付
except:
return Response({"code":201,"msg":"未知支付方式"})
# 異步回調(diào)的
class Notify(APIView):
def post(self,request,paymethod):
pay_file = importlib.import_module(f"app01.Pay.{paymethod}")
pay_class = getattr(pay_file,paymethod)
data = pay_class().notify(request.data) # 調(diào)用異步回調(diào)
# 判斷data數(shù)據(jù)中屬性,然后修改訂單
if data["statu"] == "success":
models.Order.objects.filter(order_id =data['order_id']).update(pay_status =1)
return Response(data["print"])
支付方式代碼
Alipay支付
# Alipay支付
class Alipay:
def pay(self,order_data):
#統(tǒng)一下單方法
pass
def notify(self,notity_data):
if notity_data['success'] :
#notity_data['order_id']表示商城訂單號
data={"statu":'success',"order_id":notity_data['order_id'],"print":"0000"}
return data
YLpay支付方式
# YLpay支付方式
class YLpay:
def pay(self,order_data):
pass
def notify(self,request_data):
#驗簽
#數(shù)據(jù)處理
pass
Wxpay支付方式
import time
from app01.wx import settings
class Wxpay:
def pay(self,order_data):
self.order_id = order_data["order_id"]
self.open_id = order_data['open_id']
self.ip = order_data['ip']
data_body = self.get_body_data()
import requests
url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"})
res_dict = self.xml_to_dic(response.content)
timeStamp = str(int(time.time()))
paySign = self.get_pay_sign(res_dict, timeStamp)
data_dic = {
'timeStamp': timeStamp,
'nonceStr': res_dict['nonce_str'],
'package': f"prepay_id={res_dict['prepay_id']}",
'signType': 'MD5',
"paySign": paySign,
}
return data_dic
def get_pay_sign(self, res_dict, timeStamp):
data_dic = {
'appId': res_dict['appid'],
'timeStamp': timeStamp,
'nonceStr': res_dict['nonce_str'],
'package': f"prepay_id={res_dict['prepay_id']}",
"signType": "MD5"
}
sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
sign_str = f"{sign_str}&key={settings.pay_apikey}"
import hashlib
md5 = hashlib.md5()
md5.update(sign_str.encode("utf-8"))
sign = md5.hexdigest()
return sign.upper()
def xml_to_dic(self, xml_data):
import xml.etree.ElementTree as ET
'''
xml to dict
:param xml_data:
:return:
'''
xml_dict = {}
root = ET.fromstring(xml_data)
for child in root:
xml_dict[child.tag] = child.text
return xml_dict
def get_random(self):
import random
data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP"
nonce_str = "".join(random.sample(data, 30))
return nonce_str
def get_sign(self):
data_dic = {
"nonce_str": self.nonce_str,
"out_trade_no": self.out_trade_no,
"spbill_create_ip": self.spbill_create_ip,
"notify_url": self.notify_url,
"openid": self.open_id,
"body": self.body,
"trade_type": "JSAPI",
"appid": self.appid,
"total_fee": "1",
"mch_id": self.mch_id
}
sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
sign_str = f"{sign_str}&key={settings.pay_apikey}"
import hashlib
md5 = hashlib.md5()
md5.update(sign_str.encode("utf-8"))
sign = md5.hexdigest()
return sign.upper()
def get_body_data(self):
self.appid = settings.AppId
# openid=self.open_id
self.mch_id = str(settings.pay_mchid)
self.nonce_str = self.get_random()
self.out_trade_no = self.order_id
self.spbill_create_ip = self.ip
self.notify_url = "https://www.test.com"
self.body = "老男孩學費"
self.sign = self.get_sign()
body_data = f"""
<xml>
<appid>{self.appid}</appid>
<mch_id>{self.mch_id}</mch_id>
<nonce_str>{self.nonce_str}</nonce_str>
<sign>{self.sign}</sign>
<body>{self.body}</body>
<out_trade_no>{self.out_trade_no}</out_trade_no>
<total_fee>1</total_fee>
<spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip>
<notify_url>{self.notify_url}</notify_url>
<openid>{self.open_id}</openid>
<trade_type>JSAPI</trade_type>
</xml>"""
return body_data
Creat下訂單
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.wx import wx_login
import hashlib ,time
from app01 import models
from django.core.cache import cache
from django.db import transaction
from app01.func import function_tool
import importlib
class Creat(APIView):
@transaction.atomic
def post(self,request):
#小程序提交給我們的數(shù)據(jù)
'''
{'token': '0bb2aa1102ca9c8306133b2539c3508b',
'remark': '',
'province': '廣東省',
'city': '廣州市',
'county': '海珠區(qū)',
'address':
'新港中路397號',
'phone': '020-81167888',
'name': '張三',
'buy_list': {'2': 1}}
'''
param = request.data
if param.get("token") and param.get("buy_list"):
user_cache = cache.get(param["token"])
if user_cache:
# 獲取ip
if request.META.get("HTTP_X_FORWARDED_FOR"):
host_ip = request.META["HTTP_X_FROWARDED_FOR"]
else:
host_ip = request.META["REMOTE_ADDR"]
openid = user_cache.split("&")[0] #data['openid']+"&"+data["session_key"]
user_data = models.Wxuser.objects.filter(openid=openid).first()
order_data = {
"consignee_mobile": param['phone'],
'consignee_name': param['name'],
'wxuser_id': user_data.id,
"memo": param['remark'],
"consignee_area": f"{param['province']},{param['city']},{param['county']}",
"consignee_address": param['address'],
"order_id": function_tool.get_order_id(),
"order_total": 0
}
# 1 上面的order_data 出來上面的數(shù)據(jù),有些是需要通過購買上面列表做累加才能獲得到
# 2 order_item 是通過buy_list里面的商品列表,一個鍵值對就是一條記入'buy_list': {'2': 1,“1”:2}
# 3 再每一次增加一個order_item的時候,我們都需要校驗庫存。如果有一個商品的庫存不足,我們就應該不然用戶下單
# 4 由于第三步中進行多次增加,如果再后面的的商品庫存有問題,我們不讓他下單,但是前面的數(shù)據(jù)已經(jīng)插入。
# 所有我們要用數(shù)據(jù)庫的事務管理數(shù)據(jù)的統(tǒng)一。就算order_item沒有問題,order_data,插入的時候,也可能出錯,所以也要用事務
# 5 由于并發(fā)問題,所有的用戶都會對數(shù)據(jù)的庫存進行加減,所以我們這里再校驗庫存的時候要用鎖。
buy_list = param.get("buy_list")
# 獲取到buy_list是沒有商品信息只有有id,我們先把buy_list中的所有商品查出來
goods_key = list(buy_list.keys())
all_product = models.Product.objects.filter(product_id__in = goods_key)
#用for循環(huán)添加order_item
sid = transaction.savepoint()
for product in all_product:
# 將product.product_id 轉(zhuǎn)字符串,為了通過product.product_id在buy_list獲取商品的購買數(shù)量
product.product_id = str(product.product_id)
# 獲取訂單總金額
order_data['order_total'] += product.price* buy_list[product.product_id]
for i in range(3):
#先查庫存,重新查庫的
stock = product.stock.quantity
#當前的庫存的庫存數(shù)量,減去購買數(shù)量,是否大于0
new_stock = stock-buy_list[product.product_id]
if new_stock < 0 :
#庫存不足,回滾
transaction.savepoint_rollback(sid)
return Response({"code":201,"msg": f"{product.name}庫存不足"})
#樂觀鎖
res = models.Stock.objects.filter(quantity= stock,stock_id =product.stock.stock_id).update(quantity = new_stock)
if not res:
if i == 2:
transaction.savepoint_rollback(sid)
return Response({"code":201,"msg": "創(chuàng)建訂單失敗"})
else:
continue
else:
break
#獲取購買數(shù)量
new_buy_cout = product.buy_count + buy_list[product.product_id]
models.Product.objects.filter(product_id=product.product_id).update(buy_count =new_buy_cout)
#組織order_item的數(shù)據(jù)
order_item_data = {
'order_id': order_data['order_id'],
'product_id': product.product_id,
"name": product.name,
"image": product.image,
"price": product.price,
"nums": buy_list[product.product_id],
"brief": product.brief
}
models.Order_items.objects.create(**order_item_data)
models.Order.objects.create(**order_data)
transaction.savepoint_commit(sid)
#所有的支付都是走的小程序微信支付:
pay_methon = "Wxpay"
try:
#pay_file是對象
pay_file = importlib.import_module(f"app01.Pay.{pay_methon}")
pay_class = getattr(pay_file, pay_methon)
order_data['open_id'] = openid
order_data['ip'] = host_ip
data = pay_class().pay(order_data)
except:
return Response({"code":201,"msg":"未知支付方式"})
# 1對接小程序支付
# 2 我們要用celery去定時檢查,該訂單在指定時間內(nèi)用沒有支付,沒有支付,取消訂單,回滾庫存
function_tool.pay_status(order_data['order_id'])
return Response({"code":200,"msg":"ok","data":data})
else:
return Response({"code": 201, "msg": "無效的token"})
else:
return Response({"code":202,"msg":"缺少參數(shù)"})
class Notify(APIView):
def post(self,request,paymethod):
pay_file = importlib.import_module(f"app01.Pay.{paymethod}")
pay_class = getattr(pay_file,paymethod)
data = pay_class().notify(request.data)
# 判斷data數(shù)據(jù)中屬性,然后修改訂單
if data["statu"] == "success":
models.Order.objects.filter(order_id =data['order_id']).update(pay_status =1)
return Response(data["print"])
原文鏈接:https://www.cnblogs.com/guyouyin123/p/12663537.html
相關推薦
- 2022-10-06 Android開發(fā)Jetpack組件LiveData使用講解_Android
- 2022-07-22 使用@ControllerAdvice和@ExceptionHandler構(gòu)建全局異常處理器
- 2024-03-16 Spring獲取Bean的方式
- 2022-07-08 C#中的Dialog對話框_C#教程
- 2022-03-18 C#?利用Autofac批量接口注入依賴的問題小結(jié)_C#教程
- 2022-09-12 在CMD窗口中調(diào)用python函數(shù)的實現(xiàn)_python
- 2024-03-07 Mapper批量映射優(yōu)化
- 2022-09-09 C++中vector<vector<int>?>的基本使用方法_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支