fix: 罚款微信支付 openid 与 AppID 使用同一俱乐部
交罚款时不再用罚单申请人俱乐部取 openid、打手俱乐部取商户配置;统一按当前小程序俱乐部下单,避免部分人 appid/openid 不一致导致下单失败。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -142,6 +142,10 @@ class FaKuanPayView(APIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 生成支付参数(支持支付宝/微信)
|
# 生成支付参数(支持支付宝/微信)
|
||||||
|
# 支付必须用「当前小程序/打手写入俱乐部」的 AppID+openid;
|
||||||
|
# 与罚单 ClubID(申请人俱乐部)混用会导致部分人 openid/appid 不一致而下单失败。
|
||||||
|
from jituan.services.club_write import resolve_club_id_for_write
|
||||||
|
pay_club = resolve_club_id_for_write(request, request.user)
|
||||||
pay_method = (request.data.get('pay_method') or request.data.get('payMethod') or 'wechat').lower()
|
pay_method = (request.data.get('pay_method') or request.data.get('payMethod') or 'wechat').lower()
|
||||||
if pay_method == 'alipay':
|
if pay_method == 'alipay':
|
||||||
try:
|
try:
|
||||||
@@ -150,42 +154,46 @@ class FaKuanPayView(APIView):
|
|||||||
dingdanid=dingdanid,
|
dingdanid=dingdanid,
|
||||||
jine=jine,
|
jine=jine,
|
||||||
subject=settings.FAKUAN_PAY_DESCRIPTION,
|
subject=settings.FAKUAN_PAY_DESCRIPTION,
|
||||||
club_id=penalty_cz_club_id(fadan),
|
club_id=pay_club,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
chongzhi_jilu.delete()
|
chongzhi_jilu.delete()
|
||||||
logger.error(f"生成支付宝支付参数失败: {str(e)}")
|
logger.error(f"生成支付宝支付参数失败: {str(e)}")
|
||||||
return Response({
|
return Response({
|
||||||
'code': 500,
|
'code': 500,
|
||||||
'message': f'支付参数生成失败: {str(e)}'
|
'message': f'支付参数生成失败: {str(e)}',
|
||||||
|
'msg': f'支付参数生成失败: {str(e)}',
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
else:
|
else:
|
||||||
pay_club = penalty_cz_club_id(fadan)
|
pay_openid, pay_club = get_payment_openid(request, club_id=pay_club)
|
||||||
pay_openid, _ = get_payment_openid(request, club_id=pay_club)
|
|
||||||
if not pay_openid:
|
if not pay_openid:
|
||||||
chongzhi_jilu.delete()
|
chongzhi_jilu.delete()
|
||||||
return Response({
|
return Response({
|
||||||
'code': 400,
|
'code': 400,
|
||||||
'message': '当前小程序未绑定微信openid,请退出后重新登录再支付',
|
'message': '当前小程序未绑定微信openid,请退出后重新登录再支付',
|
||||||
|
'msg': '当前小程序未绑定微信openid,请退出后重新登录再支付',
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
try:
|
try:
|
||||||
pay_params = self.generate_wechat_pay_params(
|
pay_params = self.generate_wechat_pay_params(
|
||||||
dingdanid=dingdanid,
|
dingdanid=dingdanid,
|
||||||
jine=jine,
|
jine=jine,
|
||||||
openid=pay_openid,
|
openid=pay_openid,
|
||||||
pay_type='fakuan'
|
pay_type='fakuan',
|
||||||
|
club_id=pay_club,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
chongzhi_jilu.delete()
|
chongzhi_jilu.delete()
|
||||||
logger.error(f"生成微信支付参数失败: {str(e)}")
|
logger.error(f"生成微信支付参数失败: {str(e)}")
|
||||||
return Response({
|
return Response({
|
||||||
'code': 500,
|
'code': 500,
|
||||||
'message': f'支付参数生成失败: {str(e)}'
|
'message': f'支付参数生成失败: {str(e)}',
|
||||||
|
'msg': f'支付参数生成失败: {str(e)}',
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
return Response({
|
return Response({
|
||||||
'code': 200,
|
'code': 200,
|
||||||
'message': '支付参数生成成功',
|
'message': '支付参数生成成功',
|
||||||
|
'msg': '支付参数生成成功',
|
||||||
'payParams': pay_params,
|
'payParams': pay_params,
|
||||||
'dingdanid': dingdanid
|
'dingdanid': dingdanid
|
||||||
}, status=status.HTTP_200_OK)
|
}, status=status.HTTP_200_OK)
|
||||||
@@ -194,16 +202,30 @@ class FaKuanPayView(APIView):
|
|||||||
logger.error(f"罚款支付下单接口异常: {str(e)}", exc_info=True)
|
logger.error(f"罚款支付下单接口异常: {str(e)}", exc_info=True)
|
||||||
return Response({
|
return Response({
|
||||||
'code': 500,
|
'code': 500,
|
||||||
'message': '服务器内部错误,请稍后重试'
|
'message': '服务器内部错误,请稍后重试',
|
||||||
|
'msg': '服务器内部错误,请稍后重试',
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
|
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type, club_id=None):
|
||||||
from jituan.services.club_write import resolve_club_id_for_write
|
from jituan.services.club_write import resolve_club_id_for_write
|
||||||
from jituan.services.wechat_pay import get_wechat_v2_config
|
from jituan.services.wechat_pay import (
|
||||||
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, self.request.user))
|
get_wechat_v2_config,
|
||||||
|
log_wechat_v2_pay_diag,
|
||||||
|
parse_wechat_v2_unifiedorder_response,
|
||||||
|
)
|
||||||
|
pay_club = club_id or resolve_club_id_for_write(self.request, self.request.user)
|
||||||
|
pay_cfg = get_wechat_v2_config(pay_club)
|
||||||
|
uid = getattr(self.request.user, 'UserUID', '')
|
||||||
|
log_wechat_v2_pay_diag(
|
||||||
|
pay_cfg, scene='fakuan', openid=openid, order_hint=f'{dingdanid} uid={uid}',
|
||||||
|
)
|
||||||
APPID = pay_cfg['appid']
|
APPID = pay_cfg['appid']
|
||||||
MCHID = pay_cfg['mch_id']
|
MCHID = pay_cfg['mch_id']
|
||||||
KEY = pay_cfg['key']
|
KEY = pay_cfg['key']
|
||||||
|
if not APPID or not MCHID or not KEY:
|
||||||
|
raise Exception(
|
||||||
|
f'俱乐部{pay_club}微信支付配置不完整(appid/mch_id/密钥),请联系管理员'
|
||||||
|
)
|
||||||
|
|
||||||
if pay_type == 'fakuan':
|
if pay_type == 'fakuan':
|
||||||
PAY_DESCRIPTION = settings.FAKUAN_PAY_DESCRIPTION
|
PAY_DESCRIPTION = settings.FAKUAN_PAY_DESCRIPTION
|
||||||
@@ -259,63 +281,36 @@ class FaKuanPayView(APIView):
|
|||||||
|
|
||||||
logger.info(f"罚款微信支付响应: {response.text}")
|
logger.info(f"罚款微信支付响应: {response.text}")
|
||||||
|
|
||||||
root = ET.fromstring(response.content)
|
parsed = parse_wechat_v2_unifiedorder_response(
|
||||||
|
response.content, log_prefix=f'FAKUAN_UNIFIED club={pay_club}',
|
||||||
|
)
|
||||||
|
prepay_id = parsed['prepay_id']
|
||||||
|
timestamp = str(int(time.time()))
|
||||||
|
nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
|
||||||
|
package = f'prepay_id={prepay_id}'
|
||||||
|
sign_type = 'MD5'
|
||||||
|
|
||||||
return_code_node = root.find('return_code')
|
pay_sign_data = {
|
||||||
if return_code_node is None or return_code_node.text is None:
|
'appId': APPID,
|
||||||
logger.error(f"微信返回缺少return_code,原始: {response.text}")
|
'timeStamp': timestamp,
|
||||||
raise Exception('微信支付接口异常,缺少return_code')
|
'nonceStr': nonce_str,
|
||||||
return_code = return_code_node.text
|
'package': package,
|
||||||
|
'signType': sign_type
|
||||||
|
}
|
||||||
|
|
||||||
result_code_node = root.find('result_code')
|
sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
|
||||||
result_code = result_code_node.text if result_code_node is not None else None
|
pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
|
||||||
|
pay_sign_string += f'&key={KEY}'
|
||||||
|
pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()
|
||||||
|
|
||||||
if return_code == 'SUCCESS' and result_code == 'SUCCESS':
|
return {
|
||||||
prepay_id_node = root.find('prepay_id')
|
'appId': APPID,
|
||||||
if prepay_id_node is None or prepay_id_node.text is None:
|
'timeStamp': timestamp,
|
||||||
logger.error(f"统一下单成功但无prepay_id,原始: {response.text}")
|
'nonceStr': nonce_str,
|
||||||
raise Exception('微信支付接口异常,缺少prepay_id')
|
'package': package,
|
||||||
prepay_id = prepay_id_node.text
|
'signType': sign_type,
|
||||||
timestamp = str(int(time.time()))
|
'paySign': pay_sign
|
||||||
nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
|
}
|
||||||
package = f'prepay_id={prepay_id}'
|
|
||||||
sign_type = 'MD5'
|
|
||||||
|
|
||||||
pay_sign_data = {
|
|
||||||
'appId': APPID,
|
|
||||||
'timeStamp': timestamp,
|
|
||||||
'nonceStr': nonce_str,
|
|
||||||
'package': package,
|
|
||||||
'signType': sign_type
|
|
||||||
}
|
|
||||||
|
|
||||||
sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
|
|
||||||
pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
|
|
||||||
pay_sign_string += f'&key={KEY}'
|
|
||||||
pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()
|
|
||||||
|
|
||||||
return {
|
|
||||||
'appId': APPID,
|
|
||||||
'timeStamp': timestamp,
|
|
||||||
'nonceStr': nonce_str,
|
|
||||||
'package': package,
|
|
||||||
'signType': sign_type,
|
|
||||||
'paySign': pay_sign
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
err_code = root.find('err_code')
|
|
||||||
err_code_des = root.find('err_code_des')
|
|
||||||
return_msg = root.find('return_msg')
|
|
||||||
error_details = f"return_code: {return_code}"
|
|
||||||
if result_code:
|
|
||||||
error_details += f", result_code: {result_code}"
|
|
||||||
if return_msg is not None and return_msg.text:
|
|
||||||
error_details += f", return_msg: {return_msg.text}"
|
|
||||||
if err_code is not None and err_code.text:
|
|
||||||
error_details += f", err_code: {err_code.text}"
|
|
||||||
if err_code_des is not None and err_code_des.text:
|
|
||||||
error_details += f", err_code_des: {err_code_des.text}"
|
|
||||||
raise Exception(f"微信支付下单失败: {error_details}")
|
|
||||||
|
|
||||||
def get_client_ip(self):
|
def get_client_ip(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
|
|||||||
Reference in New Issue
Block a user