支付与收款商户号分离:支付改读 pay_* 独立配置与证书目录,收款侧不动。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
22
dingdan/migrations/0018_dingdan_pay_mch_id.py
Normal file
22
dingdan/migrations/0018_dingdan_pay_mch_id.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0017_dingdan_guanshi_shangjia_fencheng'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='pay_mch_id',
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
default='',
|
||||
max_length=32,
|
||||
verbose_name='下单支付商户号',
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -64,6 +64,13 @@ class Dingdan(models.Model):
|
||||
db_index=True,
|
||||
verbose_name='微信支付订单号'
|
||||
)
|
||||
pay_mch_id = models.CharField(
|
||||
max_length=32,
|
||||
blank=True,
|
||||
default='',
|
||||
db_index=True,
|
||||
verbose_name='下单支付商户号',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan'
|
||||
|
||||
@@ -17,6 +17,7 @@ from decimal import Decimal
|
||||
from django.db import transaction
|
||||
from django.conf import settings
|
||||
from utils.xcx_sys_config import wx_cfg
|
||||
from utils.pay_mch_config import pay_cfg, get_current_pay_mch_id, get_pay_wx_cfg
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
@@ -202,6 +203,7 @@ class CreateOrderView(APIView):
|
||||
'tupian':shangpin_tupian,
|
||||
'user1_id':f"Boss{request.user.yonghuid}",
|
||||
'leixing_id': qiangdan_fields['leixing_id'],
|
||||
'pay_mch_id': get_current_pay_mch_id(),
|
||||
}
|
||||
|
||||
# 创建订单记录
|
||||
@@ -260,9 +262,9 @@ class CreateOrderView(APIView):
|
||||
try:
|
||||
# 微信支付配置
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
NOTIFY_URL = wx_cfg.WEIXIN_NOTIFY_URL
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
NOTIFY_URL = pay_cfg.WEIXIN_NOTIFY_URL
|
||||
|
||||
if not all([APPID, MCHID, KEY, NOTIFY_URL]):
|
||||
raise Exception('微信支付配置不完整')
|
||||
@@ -509,7 +511,7 @@ class WechatPayNotifyView(APIView):
|
||||
# 按key排序
|
||||
sorted_keys = sorted(data.keys())
|
||||
stringA = '&'.join([f"{k}={data[k]}" for k in sorted_keys])
|
||||
stringSignTemp = f"{stringA}&key={wx_cfg.WEIXIN_SHANGHUMIYAO}"
|
||||
stringSignTemp = f"{stringA}&key={pay_cfg.WEIXIN_SHANGHUMIYAO}"
|
||||
calc_sign = hashlib.md5(stringSignTemp.encode('utf-8')).hexdigest().upper()
|
||||
logger.debug(f"计算签名: {calc_sign}, 微信签名: {sign}")
|
||||
return calc_sign == sign
|
||||
@@ -833,8 +835,8 @@ class PaymentVerifyView(APIView):
|
||||
若查询失败,抛出异常
|
||||
"""
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
mch_id = wx_cfg.WEIXIN_MCHID
|
||||
key = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
mch_id = pay_cfg.WEIXIN_MCHID
|
||||
key = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
nonce_str = self._generate_nonce_str()
|
||||
|
||||
params = {
|
||||
@@ -1058,6 +1060,7 @@ class VirtualCreateOrderView(APIView):
|
||||
'tupian': shangpin_tupian,
|
||||
'user1_id': f"Boss{current_user.yonghuid}",
|
||||
'leixing_id': qiangdan_fields['leixing_id'],
|
||||
'pay_mch_id': get_current_pay_mch_id(),
|
||||
}
|
||||
dingdan = Dingdan.objects.create(**order_data)
|
||||
|
||||
@@ -7130,7 +7133,8 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
weixin_refund_result = self.call_weixin_refund(
|
||||
out_trade_no=dingdan_id,
|
||||
jine=jine,
|
||||
transaction_id=transaction_id
|
||||
transaction_id=transaction_id,
|
||||
pay_mch_id=getattr(dingdan_obj, 'pay_mch_id', '') or '',
|
||||
)
|
||||
|
||||
if not weixin_refund_result.get('success'):
|
||||
@@ -7217,20 +7221,24 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
def call_weixin_refund(self, out_trade_no, jine, transaction_id=None):
|
||||
def call_weixin_refund(self, out_trade_no, jine, transaction_id=None, pay_mch_id=None):
|
||||
"""
|
||||
调用微信退款接口
|
||||
:param out_trade_no: 商户订单号
|
||||
:param jine: 退款金额
|
||||
:param transaction_id: 微信支付订单号(优先使用)
|
||||
:param pay_mch_id: 下单时的支付商户号(有则按原商户退)
|
||||
:return: {'success': True/False, 'msg': '消息', 'refund_id': '退款单号'}
|
||||
"""
|
||||
try:
|
||||
# 检查配置
|
||||
required_settings = ['WEIXIN_APPID', 'WEIXIN_MCHID', 'WEIXIN_SHANGHUMIYAO', 'WEIXIN_CERT_PATH', 'WEIXIN_KEY_PATH']
|
||||
for setting in required_settings:
|
||||
if not hasattr(settings, setting) or not getattr(settings, setting):
|
||||
return {'success': False, 'msg': f'缺少微信支付配置: {setting}'}
|
||||
pcfg = get_pay_wx_cfg(pay_mch_id)
|
||||
mch_id = pcfg.get('MCHID') or ''
|
||||
key = pcfg.get('KEY') or ''
|
||||
cert_path = pcfg.get('CERT_PATH') or ''
|
||||
key_path = pcfg.get('KEY_PATH') or ''
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
if not all([appid, mch_id, key, cert_path, key_path]):
|
||||
return {'success': False, 'msg': '缺少微信支付配置'}
|
||||
|
||||
# 生成退款单号
|
||||
refund_id = f"TK{int(time.time())}{random.randint(1000, 9999)}"
|
||||
@@ -7238,13 +7246,13 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
|
||||
# 构造请求参数
|
||||
refund_data = {
|
||||
'appid': wx_cfg.WEIXIN_APPID,
|
||||
'mch_id': wx_cfg.WEIXIN_MCHID,
|
||||
'appid': appid,
|
||||
'mch_id': mch_id,
|
||||
'nonce_str': hashlib.md5(str(time.time()).encode()).hexdigest(),
|
||||
'out_refund_no': refund_id,
|
||||
'total_fee': total_fee_in_fen,
|
||||
'refund_fee': total_fee_in_fen,
|
||||
'op_user_id': wx_cfg.WEIXIN_MCHID,
|
||||
'op_user_id': mch_id,
|
||||
'refund_account': 'REFUND_SOURCE_UNSETTLED_FUNDS'
|
||||
}
|
||||
# 优先使用 transaction_id,否则用 out_trade_no
|
||||
@@ -7256,7 +7264,7 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
# 生成签名
|
||||
sorted_params = sorted(refund_data.items())
|
||||
sign_string = '&'.join([f"{k}={v}" for k, v in sorted_params if v])
|
||||
sign_string += f"&key={wx_cfg.WEIXIN_SHANGHUMIYAO}"
|
||||
sign_string += f"&key={key}"
|
||||
sign = hashlib.md5(sign_string.encode()).hexdigest().upper()
|
||||
refund_data['sign'] = sign
|
||||
|
||||
@@ -7266,7 +7274,7 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
url,
|
||||
data=xml_data.encode('utf-8'),
|
||||
headers={'Content-Type': 'application/xml'},
|
||||
cert=(wx_cfg.WEIXIN_CERT_PATH, wx_cfg.WEIXIN_KEY_PATH),
|
||||
cert=(cert_path, key_path),
|
||||
timeout=30
|
||||
)
|
||||
|
||||
@@ -7473,7 +7481,11 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
|
||||
# ========== 修改点开始 ==========
|
||||
# 13. 先调用微信退款接口,成功后才更新数据库
|
||||
weixin_refund_result = self.call_weixin_refund(dingdan_id, jine)
|
||||
weixin_refund_result = self.call_weixin_refund(
|
||||
dingdan_id,
|
||||
jine,
|
||||
pay_mch_id=getattr(dingdan_obj, 'pay_mch_id', '') or '',
|
||||
)
|
||||
|
||||
if not weixin_refund_result.get('success'):
|
||||
# 微信退款失败,直接返回错误,数据库不更新
|
||||
@@ -7588,27 +7600,20 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
|
||||
|
||||
|
||||
def call_weixin_refund(self, dingdan_id, jine):
|
||||
def call_weixin_refund(self, dingdan_id, jine, pay_mch_id=None):
|
||||
"""
|
||||
调用微信退款接口
|
||||
参考你提供的接口,但根据现有模型和配置调整
|
||||
|
||||
需要在settings.py中配置以下参数:
|
||||
WEIXIN_APPID = '你的小程序appid'
|
||||
WEIXIN_MCHID = '你的商户号'
|
||||
WEIXIN_SHANGHUMIYAO = '你的商户密钥'
|
||||
WEIXIN_CERT_PATH = '证书路径/cert/apiclient_cert.pem'
|
||||
WEIXIN_KEY_PATH = '证书路径/cert/apiclient_key.pem'
|
||||
|
||||
调用微信退款接口。pay_mch_id 有值时按原下单商户退款。
|
||||
返回:{'success': True/False, 'msg': '消息'}
|
||||
"""
|
||||
try:
|
||||
# 检查必要的配置
|
||||
required_settings = ['WEIXIN_APPID', 'WEIXIN_MCHID', 'WEIXIN_SHANGHUMIYAO', 'WEIXIN_CERT_PATH',
|
||||
'WEIXIN_KEY_PATH']
|
||||
for setting in required_settings:
|
||||
if not hasattr(settings, setting) or not getattr(settings, setting):
|
||||
return {'success': False, 'msg': f'缺少微信支付配置: {setting}'}
|
||||
pcfg = get_pay_wx_cfg(pay_mch_id)
|
||||
mch_id = pcfg.get('MCHID') or ''
|
||||
key = pcfg.get('KEY') or ''
|
||||
cert_path = pcfg.get('CERT_PATH') or ''
|
||||
key_path = pcfg.get('KEY_PATH') or ''
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
if not all([appid, mch_id, key, cert_path, key_path]):
|
||||
return {'success': False, 'msg': '缺少微信支付配置'}
|
||||
|
||||
# 生成退款单号
|
||||
refund_id = f"TK{int(time.time())}{random.randint(1000, 9999)}"
|
||||
@@ -7618,21 +7623,21 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
|
||||
# 准备微信退款参数
|
||||
refund_data = {
|
||||
'appid': wx_cfg.WEIXIN_APPID,
|
||||
'mch_id': wx_cfg.WEIXIN_MCHID,
|
||||
'appid': appid,
|
||||
'mch_id': mch_id,
|
||||
'nonce_str': hashlib.md5(str(time.time()).encode()).hexdigest(),
|
||||
'out_trade_no': dingdan_id, # 商户订单号(我们的订单ID)
|
||||
'out_refund_no': refund_id, # 商户退款单号
|
||||
'total_fee': total_fee_in_fen, # 订单总金额(分)
|
||||
'refund_fee': total_fee_in_fen, # 退款金额(分),全额退款
|
||||
'op_user_id': wx_cfg.WEIXIN_MCHID, # 操作员ID,默认为商户号
|
||||
'op_user_id': mch_id, # 操作员ID,默认为商户号
|
||||
'refund_account': 'REFUND_SOURCE_UNSETTLED_FUNDS' # 退款资金来源
|
||||
}
|
||||
|
||||
# 生成签名
|
||||
sorted_params = sorted(refund_data.items())
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f"&key={wx_cfg.WEIXIN_SHANGHUMIYAO}"
|
||||
sign_string = '&'.join([f"{key_}={value}" for key_, value in sorted_params if value])
|
||||
sign_string += f"&key={key}"
|
||||
sign = hashlib.md5(sign_string.encode()).hexdigest().upper()
|
||||
refund_data['sign'] = sign
|
||||
|
||||
@@ -7651,7 +7656,7 @@ class AdTongYiTuiKuanPingTai(APIView):
|
||||
weixin_refund_url,
|
||||
data=xml_data.encode('utf-8'),
|
||||
headers=headers,
|
||||
cert=(wx_cfg.WEIXIN_CERT_PATH, wx_cfg.WEIXIN_KEY_PATH),
|
||||
cert=(cert_path, key_path),
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user