支付与收款商户号分离:支付改读 pay_* 独立配置与证书目录,收款侧不动。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
89fa08e5de
commit
db91626e7f
12 files changed
+531
-78
No files matched your search
@@ -667,5 +667,8 @@ WECHAT_PAY_V3_CONFIG = {
|
||||
# 多商户证书上传根目录(每个商户一个子目录:{BASE}/{mch_id}/)
|
||||
WECHAT_MCH_CERT_BASE_DIR = '/opt/1panel/apps/openresty/openresty/www/sites/119.28.221.247.1199/huizhifuzhengshu'
|
||||
|
||||
# 支付专用证书目录(与收款 huizhifuzhengshu、旧 tuikuanzhengshu 物理隔离)
|
||||
WECHAT_PAY_CERT_BASE_DIR = '/opt/1panel/apps/openresty/openresty/www/sites/119.28.221.247.1199/zhifuzhengshu'
|
||||
|
||||
|
||||
CROSS_PLATFORM_SYNC_TOKEN = 'xK9mP2vL8qW5rT7y'
|
||||
@@ -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'
|
||||
|
||||
+47
-42
@@ -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
|
||||
)
|
||||
|
||||
|
||||
+7
-5
@@ -28,6 +28,7 @@ import xmltodict
|
||||
|
||||
from django.conf import settings
|
||||
from utils.xcx_sys_config import wx_cfg
|
||||
from utils.pay_mch_config import pay_cfg, get_pay_wx_cfg
|
||||
|
||||
from dingdan.models import Dashoutupian, Chufajilu, PreSettlement, OrderDashouHistory
|
||||
# apps/houtai/views.py 继续添加
|
||||
@@ -1484,12 +1485,13 @@ class CrossPlatformRefundView(APIView):
|
||||
|
||||
def _call_wechat_refund(self, order):
|
||||
"""调用微信支付退款接口,返回 {'code':0, 'msg':'success'} 或错误信息"""
|
||||
# 获取微信支付配置
|
||||
# 获取微信支付配置(优先订单下单商户)
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
mch_id = wx_cfg.WEIXIN_MCHID
|
||||
key = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
cert_path = wx_cfg.WEIXIN_CERT_PATH
|
||||
key_path = wx_cfg.WEIXIN_KEY_PATH
|
||||
pcfg = get_pay_wx_cfg(getattr(order, 'pay_mch_id', None))
|
||||
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 ''
|
||||
|
||||
if not all([appid, mch_id, key, cert_path, key_path]):
|
||||
missing = [k for k, v in {'appid': appid, 'mch_id': mch_id, 'key': key, 'cert_path': cert_path, 'key_path': key_path}.items() if not v]
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 支付商户独立字段:从旧 weixin_* 回填,收款侧不动
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def copy_weixin_to_pay(apps, schema_editor):
|
||||
XcxSysPeizhi = apps.get_model('peizhi', 'XcxSysPeizhi')
|
||||
for row in XcxSysPeizhi.objects.all():
|
||||
updates = []
|
||||
if not (row.pay_mch_id or '').strip() and (row.weixin_mchid or '').strip():
|
||||
row.pay_mch_id = row.weixin_mchid
|
||||
updates.append('pay_mch_id')
|
||||
if not (row.pay_api_v2_key or '').strip() and (row.weixin_shanghu_miyao or '').strip():
|
||||
row.pay_api_v2_key = row.weixin_shanghu_miyao
|
||||
updates.append('pay_api_v2_key')
|
||||
if not (row.pay_notify_url or '').strip() and (row.weixin_notify_url or '').strip():
|
||||
row.pay_notify_url = row.weixin_notify_url
|
||||
updates.append('pay_notify_url')
|
||||
if not (row.pay_cert_path or '').strip() and (row.weixin_cert_path or '').strip():
|
||||
row.pay_cert_path = row.weixin_cert_path
|
||||
updates.append('pay_cert_path')
|
||||
if not (row.pay_key_path or '').strip() and (row.weixin_key_path or '').strip():
|
||||
row.pay_key_path = row.weixin_key_path
|
||||
updates.append('pay_key_path')
|
||||
if updates:
|
||||
row.save(update_fields=updates)
|
||||
|
||||
|
||||
def noop_reverse(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0024_xcx_sys_peizhi_shangjia_paifa_yajin'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_mch_id',
|
||||
field=models.CharField(blank=True, default='', max_length=32, verbose_name='支付商户号'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_api_v2_key',
|
||||
field=models.CharField(blank=True, default='', max_length=128, verbose_name='支付APIv2密钥'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_api_v3_key',
|
||||
field=models.CharField(blank=True, default='', max_length=128, verbose_name='支付APIv3密钥'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_notify_url',
|
||||
field=models.CharField(blank=True, default='', max_length=300, verbose_name='支付回调URL(支付商户)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_cert_path',
|
||||
field=models.CharField(blank=True, default='', max_length=500, verbose_name='支付退款证书路径'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_key_path',
|
||||
field=models.CharField(blank=True, default='', max_length=500, verbose_name='支付退款密钥路径'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='xcxsyspeizhi',
|
||||
name='pay_cert_serial_no',
|
||||
field=models.CharField(blank=True, default='', max_length=128, verbose_name='支付商户证书序列号'),
|
||||
),
|
||||
migrations.RunPython(copy_weixin_to_pay, noop_reverse),
|
||||
]
|
||||
@@ -694,6 +694,14 @@ class XcxSysPeizhi(models.Model):
|
||||
weixin_notify_url = models.CharField(max_length=300, blank=True, default='', verbose_name='支付回调URL')
|
||||
weixin_cert_path = models.CharField(max_length=500, blank=True, default='', verbose_name='退款证书路径')
|
||||
weixin_key_path = models.CharField(max_length=500, blank=True, default='', verbose_name='退款密钥路径')
|
||||
# 支付专用商户(与提现收款 WechatPayMchConfig / 旧 weixin_* 分离)
|
||||
pay_mch_id = models.CharField(max_length=32, blank=True, default='', verbose_name='支付商户号')
|
||||
pay_api_v2_key = models.CharField(max_length=128, blank=True, default='', verbose_name='支付APIv2密钥')
|
||||
pay_api_v3_key = models.CharField(max_length=128, blank=True, default='', verbose_name='支付APIv3密钥')
|
||||
pay_notify_url = models.CharField(max_length=300, blank=True, default='', verbose_name='支付回调URL(支付商户)')
|
||||
pay_cert_path = models.CharField(max_length=500, blank=True, default='', verbose_name='支付退款证书路径')
|
||||
pay_key_path = models.CharField(max_length=500, blank=True, default='', verbose_name='支付退款密钥路径')
|
||||
pay_cert_serial_no = models.CharField(max_length=128, blank=True, default='', verbose_name='支付商户证书序列号')
|
||||
notify_base_url = models.CharField(
|
||||
max_length=300, blank=True, default='https://wenhe.nmslb.com/hqhd', verbose_name='业务回调根URL'
|
||||
)
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
"""后台 — 支付专用商户配置(与转账收款 wxmch* 完全分离,权限 8080a)"""
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from django.db import transaction
|
||||
from rest_framework import permissions
|
||||
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from peizhi.models import XcxSysPeizhi
|
||||
from peizhi.xcx_config_views import _authorize_miniapp_config, _mask_secret
|
||||
from utils.pay_mch_config import pay_cert_base_dir, pay_cfg
|
||||
from utils.xcx_sys_config import clear_xcx_sys_peizhi_cache
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_SAFE_NAME = re.compile(r'[^A-Za-z0-9._-]+')
|
||||
|
||||
|
||||
def _get_or_create_row():
|
||||
row = XcxSysPeizhi.objects.first()
|
||||
if row is None:
|
||||
row = XcxSysPeizhi.objects.create()
|
||||
return row
|
||||
|
||||
|
||||
def _serialize_pay(row, hide_secret=True):
|
||||
v2 = (row.pay_api_v2_key or '') if row else ''
|
||||
v3 = (row.pay_api_v3_key or '') if row else ''
|
||||
cert = (row.pay_cert_path or '') if row else ''
|
||||
key = (row.pay_key_path or '') if row else ''
|
||||
return {
|
||||
'pay_mch_id': (row.pay_mch_id or '') if row else pay_cfg.PAY_MCH_ID,
|
||||
'pay_api_v2_key': _mask_secret(v2) if hide_secret else v2,
|
||||
'pay_api_v2_key_set': bool(v2),
|
||||
'pay_api_v3_key': _mask_secret(v3) if hide_secret else v3,
|
||||
'pay_api_v3_key_set': bool(v3),
|
||||
'pay_notify_url': (row.pay_notify_url or '') if row else '',
|
||||
'pay_cert_path': cert,
|
||||
'pay_key_path': key,
|
||||
'pay_cert_serial_no': (row.pay_cert_serial_no or '') if row else '',
|
||||
'pay_cert_exists': bool(cert and os.path.isfile(cert)),
|
||||
'pay_key_exists': bool(key and os.path.isfile(key)),
|
||||
'cert_base_dir': pay_cert_base_dir(),
|
||||
'effective_mch_id': pay_cfg.PAY_MCH_ID,
|
||||
}
|
||||
|
||||
|
||||
class PayMchQueryView(APIView):
|
||||
"""POST /peizhi/paymchhq — 查询支付商户配置"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
row = XcxSysPeizhi.objects.first()
|
||||
return Response({'code': 0, 'data': _serialize_pay(row)})
|
||||
|
||||
|
||||
class PayMchUpdateView(APIView):
|
||||
"""POST /peizhi/paymchgx — 更新支付商户配置"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
|
||||
data = request.data
|
||||
with transaction.atomic():
|
||||
row = _get_or_create_row()
|
||||
if 'pay_mch_id' in data:
|
||||
row.pay_mch_id = str(data.get('pay_mch_id') or '').strip()[:32]
|
||||
if 'pay_notify_url' in data:
|
||||
row.pay_notify_url = str(data.get('pay_notify_url') or '').strip()[:300]
|
||||
if 'pay_cert_serial_no' in data:
|
||||
row.pay_cert_serial_no = str(data.get('pay_cert_serial_no') or '').strip()[:128]
|
||||
if 'pay_cert_path' in data:
|
||||
row.pay_cert_path = str(data.get('pay_cert_path') or '').strip()[:500]
|
||||
if 'pay_key_path' in data:
|
||||
row.pay_key_path = str(data.get('pay_key_path') or '').strip()[:500]
|
||||
|
||||
v2 = data.get('pay_api_v2_key')
|
||||
if v2 is not None and str(v2).strip() and '****' not in str(v2):
|
||||
row.pay_api_v2_key = str(v2).strip()[:128]
|
||||
v3 = data.get('pay_api_v3_key')
|
||||
if v3 is not None and str(v3).strip() and '****' not in str(v3):
|
||||
row.pay_api_v3_key = str(v3).strip()[:128]
|
||||
|
||||
row.save()
|
||||
|
||||
clear_xcx_sys_peizhi_cache()
|
||||
return Response({'code': 0, 'msg': '保存成功', 'data': _serialize_pay(row)})
|
||||
|
||||
|
||||
class PayMchUploadView(APIView):
|
||||
"""
|
||||
POST /peizhi/paymchscwj — 上传支付退款证书/密钥
|
||||
form-data: username, file_type(cert|key), file
|
||||
可选 mch_id(默认用当前 pay_mch_id)
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
ALLOWED_TYPES = {
|
||||
'cert': 'apiclient_cert.pem',
|
||||
'key': 'apiclient_key.pem',
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
|
||||
file_type = str(request.data.get('file_type') or '').strip()
|
||||
upload = request.FILES.get('file')
|
||||
mch_id = str(request.data.get('mch_id') or request.data.get('pay_mch_id') or '').strip()
|
||||
|
||||
if file_type not in self.ALLOWED_TYPES:
|
||||
return Response({'code': 400, 'msg': 'file_type 仅支持 cert / key'})
|
||||
if not upload:
|
||||
return Response({'code': 400, 'msg': '请上传文件'})
|
||||
|
||||
row = _get_or_create_row()
|
||||
if not mch_id:
|
||||
mch_id = (row.pay_mch_id or pay_cfg.PAY_MCH_ID or '').strip()
|
||||
if not mch_id:
|
||||
return Response({'code': 400, 'msg': '请先填写支付商户号再上传证书'})
|
||||
|
||||
dest_name = self.ALLOWED_TYPES[file_type]
|
||||
folder = os.path.join(pay_cert_base_dir(), mch_id)
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
dest_path = os.path.join(folder, dest_name)
|
||||
|
||||
try:
|
||||
with open(dest_path, 'wb') as f:
|
||||
for chunk in upload.chunks():
|
||||
f.write(chunk)
|
||||
except OSError as e:
|
||||
logger.error('上传支付证书失败 mch=%s err=%s', mch_id, e, exc_info=True)
|
||||
return Response({'code': 500, 'msg': f'写入失败: {e}'})
|
||||
|
||||
if file_type == 'cert':
|
||||
row.pay_cert_path = dest_path
|
||||
row.save(update_fields=['pay_cert_path', 'update_time'])
|
||||
else:
|
||||
row.pay_key_path = dest_path
|
||||
row.save(update_fields=['pay_key_path', 'update_time'])
|
||||
|
||||
if not (row.pay_mch_id or '').strip():
|
||||
row.pay_mch_id = mch_id
|
||||
row.save(update_fields=['pay_mch_id', 'update_time'])
|
||||
|
||||
clear_xcx_sys_peizhi_cache()
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '上传成功',
|
||||
'data': {
|
||||
'mch_id': mch_id,
|
||||
'file_type': file_type,
|
||||
'path': dest_path,
|
||||
**_serialize_pay(row),
|
||||
},
|
||||
})
|
||||
@@ -15,6 +15,7 @@ from .wx_mch_views import (
|
||||
WechatMchListView, WechatMchSaveView, WechatMchUploadView,
|
||||
WechatMchUserBindListView, WechatMchUserBindSaveView, WechatMchUserBindDeleteView,
|
||||
)
|
||||
from .pay_mch_views import PayMchQueryView, PayMchUpdateView, PayMchUploadView
|
||||
from .xieyi_views import XieyiListView, XieyiDetailView, XieyiSignStatusView, XieyiSignView, XieyiCheckView
|
||||
|
||||
urlpatterns = [
|
||||
@@ -63,6 +64,9 @@ urlpatterns = [
|
||||
path('wxmchUserLb', WechatMchUserBindListView.as_view(), name='用户绑定转账商户列表'),
|
||||
path('wxmchUserBj', WechatMchUserBindSaveView.as_view(), name='保存用户绑定转账商户'),
|
||||
path('wxmchUserSc', WechatMchUserBindDeleteView.as_view(), name='删除用户绑定转账商户'),
|
||||
path('paymchhq', PayMchQueryView.as_view(), name='获取支付商户配置'),
|
||||
path('paymchgx', PayMchUpdateView.as_view(), name='更新支付商户配置'),
|
||||
path('paymchscwj', PayMchUploadView.as_view(), name='上传支付商户证书'),
|
||||
path('xieyihq', XieyiListView.as_view(), name='小程序获取协议列表'),
|
||||
path('xieyixq', XieyiDetailView.as_view(), name='小程序获取协议详情'),
|
||||
path('xieyijc', XieyiCheckView.as_view(), name='小程序检查协议签署'),
|
||||
|
||||
+14
-13
@@ -6,6 +6,7 @@ from rest_framework.throttling import AnonRateThrottle
|
||||
from django.db import models
|
||||
from shangpin.models import ShangpinLeixing, ShangpinZhuanqu, Shangpin
|
||||
from utils.xcx_sys_config import get_shangpin_shenhe_zhuangtai
|
||||
from utils.pay_mch_config import pay_cfg
|
||||
from utils.jwt_auth import OptionalYonghuidJWTAuthentication, YonghuidJWTAuthentication
|
||||
from yonghu.models import UserMain
|
||||
import logging
|
||||
@@ -549,8 +550,8 @@ class YajinGoumai(APIView):
|
||||
"""生成微信小程序支付参数"""
|
||||
# 获取配置
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
# 根据支付类型获取对应的配置
|
||||
if pay_type == 'yajin':
|
||||
@@ -876,7 +877,7 @@ class YajinHuitiao(View):
|
||||
|
||||
# 拼接字符串
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
|
||||
# 计算签名
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
@@ -1126,8 +1127,8 @@ class JifenBuchong(APIView):
|
||||
"""生成微信支付参数(完整版,无省略)"""
|
||||
# 微信支付配置 - 从settings中获取
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
# 根据支付类型获取对应的配置
|
||||
if pay_type == 'yajin':
|
||||
@@ -1429,7 +1430,7 @@ class JifenHuitiao(View):
|
||||
|
||||
# 拼接字符串
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
|
||||
# 计算签名
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
@@ -1724,8 +1725,8 @@ class HuiyuanGoumai(APIView):
|
||||
"""生成微信支付参数(完整版,无省略)"""
|
||||
# 微信支付配置 - 从settings中获取
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
# 根据支付类型获取对应的配置
|
||||
if pay_type == 'yajin':
|
||||
@@ -2269,7 +2270,7 @@ class HuiyuanHuitiao(View):
|
||||
|
||||
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
return sign == calculated_sign
|
||||
except Exception as e:
|
||||
@@ -2632,7 +2633,7 @@ class HuiyuanHuitiao(View):
|
||||
|
||||
# 拼接字符串
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
|
||||
# 计算签名
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
@@ -2963,8 +2964,8 @@ class ShangjiaChongzhi(APIView):
|
||||
"""生成微信小程序支付参数"""
|
||||
# 获取配置
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
# 根据支付类型获取对应的配置
|
||||
if pay_type == 'shangjia':
|
||||
@@ -3262,7 +3263,7 @@ class ShangjiaHuitiao(View):
|
||||
|
||||
# 拼接字符串
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
|
||||
# 计算签名
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
支付专用商户配置(与提现收款 WechatPayMchConfig / wechat_mch_service 完全分离)。
|
||||
|
||||
读取优先级:
|
||||
1) XcxSysPeizhi.pay_*
|
||||
2) 过渡回退:旧 weixin_* / settings.WEIXIN_*
|
||||
历史订单退款:若订单 pay_mch_id 与当前支付商户不一致,但等于旧 weixin_mchid,则用旧 weixin_* 退款。
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from utils.xcx_sys_config import wx_cfg, _val
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def pay_cert_base_dir():
|
||||
base = getattr(settings, 'WECHAT_PAY_CERT_BASE_DIR', '') or ''
|
||||
if base:
|
||||
return base
|
||||
return os.path.join(str(settings.BASE_DIR), 'zhifuzhengshu')
|
||||
|
||||
|
||||
def _pay_val(pay_attr, legacy_db_attr, settings_attr, default=''):
|
||||
"""pay_* 优先;空则回退旧 weixin_* / settings。"""
|
||||
v = _val(pay_attr, None, None)
|
||||
if v is not None and v != '':
|
||||
return v
|
||||
return _val(legacy_db_attr, settings_attr, default) or default
|
||||
|
||||
|
||||
class PayWxConfig:
|
||||
"""支付商户配置代理(属性名兼容旧 WEIXIN_*,便于替换调用点)。"""
|
||||
|
||||
@property
|
||||
def WEIXIN_MCHID(self):
|
||||
return _pay_val('pay_mch_id', 'weixin_mchid', 'WEIXIN_MCHID', '')
|
||||
|
||||
@property
|
||||
def WEIXIN_SHANGHUMIYAO(self):
|
||||
return _pay_val('pay_api_v2_key', 'weixin_shanghu_miyao', 'WEIXIN_SHANGHUMIYAO', '')
|
||||
|
||||
@property
|
||||
def WEIXIN_NOTIFY_URL(self):
|
||||
return _pay_val('pay_notify_url', 'weixin_notify_url', 'WEIXIN_NOTIFY_URL', '')
|
||||
|
||||
@property
|
||||
def WEIXIN_CERT_PATH(self):
|
||||
return _pay_val('pay_cert_path', 'weixin_cert_path', 'WEIXIN_CERT_PATH', '')
|
||||
|
||||
@property
|
||||
def WEIXIN_KEY_PATH(self):
|
||||
return _pay_val('pay_key_path', 'weixin_key_path', 'WEIXIN_KEY_PATH', '')
|
||||
|
||||
@property
|
||||
def PAY_API_V3_KEY(self):
|
||||
return _val('pay_api_v3_key', None, '') or ''
|
||||
|
||||
@property
|
||||
def PAY_CERT_SERIAL_NO(self):
|
||||
return _val('pay_cert_serial_no', None, '') or ''
|
||||
|
||||
# 别名,语义更清晰
|
||||
@property
|
||||
def PAY_MCH_ID(self):
|
||||
return self.WEIXIN_MCHID
|
||||
|
||||
@property
|
||||
def PAY_API_V2_KEY(self):
|
||||
return self.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
|
||||
pay_cfg = PayWxConfig()
|
||||
|
||||
|
||||
def get_current_pay_mch_id():
|
||||
return (pay_cfg.PAY_MCH_ID or '').strip()
|
||||
|
||||
|
||||
def pay_cfg_as_dict():
|
||||
return {
|
||||
'MCHID': pay_cfg.WEIXIN_MCHID,
|
||||
'KEY': pay_cfg.WEIXIN_SHANGHUMIYAO,
|
||||
'NOTIFY_URL': pay_cfg.WEIXIN_NOTIFY_URL,
|
||||
'CERT_PATH': pay_cfg.WEIXIN_CERT_PATH,
|
||||
'KEY_PATH': pay_cfg.WEIXIN_KEY_PATH,
|
||||
'API_V3_KEY': pay_cfg.PAY_API_V3_KEY,
|
||||
'CERT_SERIAL_NO': pay_cfg.PAY_CERT_SERIAL_NO,
|
||||
}
|
||||
|
||||
|
||||
def get_pay_wx_cfg(mch_id=None):
|
||||
"""
|
||||
按商户号取支付配置(用于退款反查)。
|
||||
- mch_id 空 / 等于当前支付商户 → 当前 pay_cfg
|
||||
- mch_id 等于旧 weixin_mchid 且与当前不同 → 用旧 weixin_*(换号后历史单退款)
|
||||
- 否则若 zhifuzhengshu/{mch_id}/ 下有证书,拼路径但仍需密钥;密钥仅当前/旧库有,故仍回退当前并打日志
|
||||
"""
|
||||
current = pay_cfg_as_dict()
|
||||
want = (mch_id or '').strip()
|
||||
current_mch = (current.get('MCHID') or '').strip()
|
||||
if not want or want == current_mch:
|
||||
return current
|
||||
|
||||
legacy_mch = (wx_cfg.WEIXIN_MCHID or '').strip()
|
||||
if want == legacy_mch and want != current_mch:
|
||||
return {
|
||||
'MCHID': wx_cfg.WEIXIN_MCHID,
|
||||
'KEY': wx_cfg.WEIXIN_SHANGHUMIYAO,
|
||||
'NOTIFY_URL': wx_cfg.WEIXIN_NOTIFY_URL,
|
||||
'CERT_PATH': wx_cfg.WEIXIN_CERT_PATH,
|
||||
'KEY_PATH': wx_cfg.WEIXIN_KEY_PATH,
|
||||
'API_V3_KEY': '',
|
||||
'CERT_SERIAL_NO': '',
|
||||
}
|
||||
|
||||
cert_path = os.path.join(pay_cert_base_dir(), want, 'apiclient_cert.pem')
|
||||
key_path = os.path.join(pay_cert_base_dir(), want, 'apiclient_key.pem')
|
||||
if os.path.isfile(cert_path) and os.path.isfile(key_path):
|
||||
logger.warning(
|
||||
'支付商户 %s 与当前配置 %s 不一致,仅找到证书文件,密钥仍用当前配置,退款可能失败',
|
||||
want, current_mch,
|
||||
)
|
||||
out = dict(current)
|
||||
out['MCHID'] = want
|
||||
out['CERT_PATH'] = cert_path
|
||||
out['KEY_PATH'] = key_path
|
||||
return out
|
||||
|
||||
logger.warning(
|
||||
'订单支付商户 %s 无可用历史配置,回退当前支付商户 %s',
|
||||
want, current_mch,
|
||||
)
|
||||
return current
|
||||
|
||||
|
||||
def apply_pay_cfg_to_proxy(cfg_dict):
|
||||
"""把 get_pay_wx_cfg 结果转成带 WEIXIN_* 属性的简单对象,兼容旧代码写法。"""
|
||||
class _Cfg:
|
||||
pass
|
||||
|
||||
o = _Cfg()
|
||||
o.WEIXIN_MCHID = cfg_dict.get('MCHID') or ''
|
||||
o.WEIXIN_SHANGHUMIYAO = cfg_dict.get('KEY') or ''
|
||||
o.WEIXIN_NOTIFY_URL = cfg_dict.get('NOTIFY_URL') or ''
|
||||
o.WEIXIN_CERT_PATH = cfg_dict.get('CERT_PATH') or ''
|
||||
o.WEIXIN_KEY_PATH = cfg_dict.get('KEY_PATH') or ''
|
||||
return o
|
||||
+24
-18
@@ -6,6 +6,7 @@ import requests
|
||||
from django.db import transaction, IntegrityError
|
||||
from django.conf import settings
|
||||
from utils.xcx_sys_config import wx_cfg
|
||||
from utils.pay_mch_config import pay_cfg, get_pay_wx_cfg
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
@@ -9211,7 +9212,8 @@ class KefuPlatformRefundView(APIView):
|
||||
refund_result = self.call_wechat_refund(
|
||||
dingdan_id=order.dingdan_id,
|
||||
jine=order.jine,
|
||||
transaction_id=wechat_transaction_id
|
||||
transaction_id=wechat_transaction_id,
|
||||
pay_mch_id=getattr(order, 'pay_mch_id', '') or '',
|
||||
)
|
||||
|
||||
if refund_result['code'] != 0:
|
||||
@@ -9337,16 +9339,17 @@ class KefuPlatformRefundView(APIView):
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
# ==================== 商户微信支付退款 ====================
|
||||
def call_wechat_refund(self, dingdan_id, jine, transaction_id=None):
|
||||
def call_wechat_refund(self, dingdan_id, jine, transaction_id=None, pay_mch_id=None):
|
||||
"""
|
||||
调用微信支付退款接口 secapi/pay/refund
|
||||
:return: {'code': 0, 'msg': '成功', 'refund_id': 'xxx'} 或 {'code': 非0, 'msg': '错误信息'}
|
||||
"""
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
mch_id = wx_cfg.WEIXIN_MCHID
|
||||
key = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
cert_path = wx_cfg.WEIXIN_CERT_PATH
|
||||
key_path = wx_cfg.WEIXIN_KEY_PATH
|
||||
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 ''
|
||||
|
||||
if not all([appid, mch_id, key, cert_path, key_path]):
|
||||
missing = [
|
||||
@@ -9596,7 +9599,8 @@ class KefuPlatformRefundView(APIView):
|
||||
refund_result = self.call_wechat_refund(
|
||||
dingdan_id=order.dingdan_id,
|
||||
jine=order.jine,
|
||||
transaction_id=wechat_transaction_id
|
||||
transaction_id=wechat_transaction_id,
|
||||
pay_mch_id=getattr(order, 'pay_mch_id', '') or '',
|
||||
)
|
||||
|
||||
if refund_result['code'] != 0:
|
||||
@@ -9672,20 +9676,22 @@ class KefuPlatformRefundView(APIView):
|
||||
logger.error(f"平台退款接口异常: {str(e)}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'})
|
||||
|
||||
def call_wechat_refund(self, dingdan_id, jine, transaction_id=None):
|
||||
def call_wechat_refund(self, dingdan_id, jine, transaction_id=None, pay_mch_id=None):
|
||||
"""
|
||||
调用微信支付退款接口
|
||||
:param dingdan_id: 商户订单号(我们的订单ID)
|
||||
:param jine: 退款金额
|
||||
:param transaction_id: 微信支付订单号(优先使用)
|
||||
:param pay_mch_id: 下单支付商户号
|
||||
:return: {'code': 0, 'msg': '成功', 'refund_id': 'xxx'} 或 {'code': 非0, 'msg': '错误信息'}
|
||||
"""
|
||||
# 获取配置
|
||||
appid = wx_cfg.WEIXIN_APPID
|
||||
mch_id = wx_cfg.WEIXIN_MCHID
|
||||
key = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
cert_path = wx_cfg.WEIXIN_CERT_PATH
|
||||
key_path = wx_cfg.WEIXIN_KEY_PATH
|
||||
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 ''
|
||||
|
||||
if not all([appid, mch_id, key, cert_path, key_path]):
|
||||
missing = [k for k, v in {'appid': appid, 'mch_id': mch_id, 'key': key, 'cert_path': cert_path, 'key_path': key_path}.items() if not v]
|
||||
@@ -14837,8 +14843,8 @@ class FaKuanPayView(APIView):
|
||||
|
||||
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
if pay_type == 'fakuan':
|
||||
PAY_DESCRIPTION = settings.FAKUAN_PAY_DESCRIPTION
|
||||
@@ -15098,7 +15104,7 @@ class FaKuanHuitiaoView(View):
|
||||
|
||||
# 拼接字符串
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
|
||||
# 计算签名
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
@@ -15360,8 +15366,8 @@ class KaohePayView(APIView):
|
||||
def generate_wechat_pay_params(self, dingdanid, jine, openid, attach, pay_type):
|
||||
"""生成微信JSAPI支付参数"""
|
||||
APPID = wx_cfg.WEIXIN_APPID
|
||||
MCHID = wx_cfg.WEIXIN_MCHID
|
||||
KEY = wx_cfg.WEIXIN_SHANGHUMIYAO
|
||||
MCHID = pay_cfg.WEIXIN_MCHID
|
||||
KEY = pay_cfg.WEIXIN_SHANGHUMIYAO
|
||||
|
||||
if pay_type == 'kaohe':
|
||||
PAY_DESCRIPTION = settings.KAOHE_PAY_DESCRIPTION
|
||||
@@ -15679,7 +15685,7 @@ class KaohePayCallbackView(View):
|
||||
|
||||
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
||||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||||
sign_string += f'&key={wx_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
sign_string += f'&key={pay_cfg.WEIXIN_SHANGHUMIYAO}'
|
||||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||||
|
||||
result = sign == calculated_sign
|
||||
|
||||
Reference in new issue
Block a user