微信浏览器内用apiv3支付,总是不能调起微信支付框,一直提示支付验证签名失败?但是后台输出也找不到
settings.py里面配置
# 微信支付相关配置
WECHAT_APP_ID = 'wxbxxx' #
WECHAT_MCH_ID = '16xxxx' #
WECHAT_V3_KEY = 'b5B0xxxxx' # 改为V3密钥
WECHAT_CERT_SERIAL_NO = '53CECxxx' # 新增,从商户平台获取
WECHAT_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'pay/wechat/apiclient_key.pem') # 私钥路径
WECHAT_CERT_PATH = os.path.join(BASE_DIR, 'pay/wechat/apiclient_cert.pem') # 证书路径
WECHAT_NOTIFY_URL = 'https://www.xxx.cn/payments/wechat/notify/' # 保持不变
views.py里面的代码:
def handle_wechat(request, payment_record):
try:
# 初始化微信支付
wechat_pay = WeChatPay(
wechatpay_type=WeChatPayType.NATIVE,
mchid=settings.WECHAT_MCH_ID,
private_key=open(settings.WECHAT_PRIVATE_KEY_PATH).read(),
cert_serial_no=settings.WECHAT_CERT_SERIAL_NO,
apiv3_key=settings.WECHAT_V3_KEY,
appid=settings.WECHAT_APP_ID,
notify_url=settings.WECHAT_NOTIFY_URL,
cert_dir=settings.WECHAT_CERT_DIR,
logger=logger,
partner_mode=False
)
# PC端微信支付 电脑端支付是正常的
if payment_record.payment_method == 'wechat_pc':
code, message = wechat_pay.pay(
description='账户充值',
out_trade_no=payment_record.order_no,
amount={'total': payment_record.amount},
)
if code == 200:
try:
if isinstance(message, str):
message = json.loads(message)
if isinstance(message, dict) and 'code_url' in message:
return JsonResponse({
'status': 'success',
'code_url': message['code_url'],
'order_no': payment_record.order_no
})
else:
return JsonResponse({
'status': 'error',
'message': '支付接口返回格式异常'
}, status=400)
except json.JSONDecodeError as e:
return JsonResponse({
'status': 'error',
'message': '支付接口返回数据解析失败'
}, status=400)
else:
return JsonResponse({
'status': 'error',
'message': f'创建支付订单失败: {message}'
}, status=400)
elif payment_record.payment_method == 'wechat_mp':
# 使用微信公众号的appid
appid = settings.WECHAT_MP_APP_ID
if not hasattr(request.user, 'openid_mp') or not request.user.openid_mp:
logger.error(f"用户未绑定微信公众号账号,用户名: {request.user.username}")
return JsonResponse({
'status': 'error',
'message': '未绑定微信公众号账号,请先退出当前账号用微信登录后再用微信充值!'
}, status=400)
logger.info(f"用户公众号OpenID: {request.user.openid_mp}")
wechat_pay = WeChatPay(
wechatpay_type=WeChatPayType.JSAPI,
mchid=settings.WECHAT_MCH_ID,
private_key=open(settings.WECHAT_PRIVATE_KEY_PATH).read(),
cert_serial_no=settings.WECHAT_CERT_SERIAL_NO,
apiv3_key=settings.WECHAT_V3_KEY,
appid=appid, # 使用微信公众号的appid
notify_url=settings.WECHAT_NOTIFY_URL,
cert_dir=settings.WECHAT_CERT_DIR,
logger=logger,
partner_mode=False
)
code, message = wechat_pay.pay(
description='账户充值',
out_trade_no=payment_record.order_no,
amount={'total': payment_record.amount},
payer={'openid': request.user.openid_mp}, # 使用公众号openid
pay_type=WeChatPayType.JSAPI
)
if code == 200:
try:
if isinstance(message, str):
message = json.loads(message)
prepay_id = message['prepay_id']
timestamp = str(int(time.time()))
noncestr = str(uuid.uuid4()).replace('-', '')
package = f"prepay_id={prepay_id}"
# 生成签名
sign_str = f"{appid}\n{timestamp}\n{noncestr}\n{package}\n"
signature = wechat_pay.sign(sign_str)
return JsonResponse({
'status': 'success',
'pay_params': {
'appId': appid,
'timeStamp': timestamp,
'nonceStr': noncestr,
'package': package,
'signType': 'RSA',
'paySign': signature,
'order_no': payment_record.order_no
}
})
except (KeyError, json.JSONDecodeError) as e:
return JsonResponse({
'status': 'error',
'message': '支付接口返回数据解析失败'
}, status=400)
else:
return JsonResponse({
'status': 'error',
'message': f'微信支付创建订单失败,状态码: {code}, 错误信息: {message}'
}, status=400)
# 未知支付方式
else:
logger.error(f"未知的微信支付方式: {payment_record.payment_method}")
return JsonResponse({
'status': 'error',
'message': '不支持的支付方式'
}, status=400)
except Exception as e:
logger.error(f"处理微信支付时发生未预期错误: {str(e)}", exc_info=True)
return JsonResponse({
'status': 'error',
'message': '支付处理发生意外错误'
}, status=500)
这是前端html的支付页面:
<form id="rechargeForm">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<div class="form-group">
<label for="{{ form.amount.id_for_label }}">{{ form.amount.label }}:</label>
<div class="amount-input-container">
<input type="number"
name="amount"
id="id_amount"
min="1"
max="1000"
required
placeholder="请输入1-1000之间的整数金额">
<span class="bonus-tip" id="bonusTip"></span>
</div>
{% if form.amount.errors %}
<div class="invalid-feedback">
{{ form.amount.errors }}
</div>
{% endif %}
<div class="package-radios">
<label>
<input type="radio" name="package" value="10">
10元
</label>
<label>
<input type="radio" name="package" value="50">
50元<span class="package-bonus">送3积分</span>
</label>
<label>
<input type="radio" name="package" value="100">
100元<span class="package-bonus">送8积分</span>
</label>
<label>
<input type="radio" name="package" value="200">
200元<span class="package-bonus">送20积分</span>
</label>
<label>
<input type="radio" name="package" value="500">
500元<span class="package-bonus">送75积分</span>
</label>
<label>
<input type="radio" name="package" value="1000">
1000元<span class="package-bonus">送200积分</span>
</label>
</div>
</div>
<div class="form-group">
<label>{{ form.payment_method.label }}:</label>
<div id="paymentMethods">
<!-- 支付方式会根据环境动态显示 -->
</div>
{% if form.payment_method.errors %}
<div class="invalid-feedback">
{{ form.payment_method.errors }}
</div>
{% endif %}
</div>
<button type="submit" class="btn-primary" id="submitBtn">确认支付</button>
</form> <script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<script>
// 套餐赠送金额映射
const bonusMap = {
'50': 3,
'100': 8,
'200': 20,
'500': 75,
'1000': 200
};
//按钮重置函数
function resetSubmitButton() {
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
}
// 处理手动输入金额
document.getElementById('id_amount').addEventListener('input', function() {
// 取消选中的套餐
document.querySelectorAll('.package-radios input[type="radio"]').forEach(radio => {
radio.checked = false;
radio.parentElement.classList.remove('selected');
});
// 验证金额范围
let value = parseInt(this.value);
if (value > 1000) this.value = 1000;
if (value < 1 && this.value !== '') this.value = 1;
// 隐藏赠送提示
document.getElementById('bonusTip').style.display = 'none';
});
// 处理充值套餐选择
document.querySelectorAll('.package-radios input[type="radio"]').forEach(radio => {
radio.addEventListener('change', function() {
document.getElementById('id_amount').value = this.value;
// 更新选中状态
document.querySelectorAll('.package-radios label').forEach(label => {
label.classList.remove('selected');
});
this.parentElement.classList.add('selected');
// 显示赠送积分提示
const bonusAmount = bonusMap[this.value];
const bonusTip = document.getElementById('bonusTip');
if (bonusAmount) {
bonusTip.textContent = `(送${bonusAmount}积分)`;
bonusTip.style.display = 'block';
} else {
bonusTip.style.display = 'none';
}
});
});
// 检测支付环境并显示合适的支付方式
function detectEnvironment() {
const ua = navigator.userAgent.toLowerCase();
const isWechat = ua.includes('micromessenger');
const isMobile = /mobile|android|iphone|ipod|ipad|phone/i.test(ua);
const paymentMethods = document.getElementById('paymentMethods');
paymentMethods.innerHTML = '';
if (isWechat) {
// 微信内环境 - 只显示微信内支付
addPaymentMethod('wechat_mp', '微信支付', '/static/payments/img/wx.gif');
} else if (isMobile) {
// 移动端非微信环境 - 显示支付宝移动支付
addPaymentMethod('alipay_mobile', '支付宝支付', '/static/payments/img/zfb.gif');
} else {
// PC端环境 - 显示支付宝和微信扫码支付
addPaymentMethod('alipay_pc', '支付宝支付', '/static/payments/img/zfb.gif');
addPaymentMethod('wechat_pc', '微信扫码支付', '/static/payments/img/wx.gif');
}
}
// 添加支付方式选项
function addPaymentMethod(value, label, iconUrl) {
const div = document.createElement('div');
div.className = 'payment-method-item';
const input = document.createElement('input');
input.type = 'radio';
input.name = 'payment_method';
input.value = value;
input.id = 'pay_' + value;
input.required = true;
const icon = document.createElement('img');
icon.className = 'payment-icon';
icon.src = iconUrl;
icon.alt = label;
const labelElement = document.createElement('label');
labelElement.htmlFor = 'pay_' + value;
labelElement.textContent = label;
div.appendChild(input);
div.appendChild(icon);
div.appendChild(labelElement);
// 点击选中效果
div.addEventListener('click', function() {
document.querySelectorAll('.payment-method-item').forEach(item => {
item.classList.remove('selected');
});
this.classList.add('selected');
input.checked = true;
});
paymentMethods.appendChild(div);
}
// 检查支付状态
function checkPaymentStatus(orderNo) {
const statusElement = document.getElementById('paymentStatus');
const qrcodeDiv = document.getElementById('qrcode');
const closeBtn = document.querySelector('.qrcode-content button');
let checkCount = 0;
const maxChecks = 180;
const check = () => {
checkCount++;
statusElement.textContent = `正在检查支付状态(${checkCount}/${maxChecks})...`;
if (checkCount > maxChecks) {
qrcodeDiv.style.display = 'none';
statusElement.innerHTML = `
<div style="color: #ff4d4f; margin-bottom: 10px;">
支付超时,请刷新本页面后重新发起支付
</div>
`;
closeBtn.textContent = '刷新本页发布重新支付';
closeBtn.onclick = function() {
window.location.reload();
};
resetSubmitButton();
return;
}
fetch(`/payments/check_status/?order_no=${orderNo}&t=${Date.now()}`)
.then(response => {
if (!response.ok) throw new Error('网络错误');
return response.json();
})
.then(data => {
if (data.status === 'success') {
statusElement.textContent = '支付成功,正在跳转...';
setTimeout(() => {
window.location.href = data.redirect_url || '/payments/success/';
}, 1000);
} else if (data.status === 'pending') {
setTimeout(check, 2000);
} else {
statusElement.textContent = '支付失败: ' + (data.message || '未知错误');
}
})
.catch(error => {
console.error('检查支付状态出错:', error);
statusElement.textContent = '检查支付状态出错,请稍后重试';
setTimeout(check, 2000);
});
};
check();
}
// 显示微信支付二维码
function showWechatQRCode(codeUrl, orderNo) {
const modal = document.getElementById('qrcodeModal');
const qrcodeDiv = document.getElementById('qrcode');
qrcodeDiv.innerHTML = '';
new QRCode(qrcodeDiv, {
text: codeUrl,
width: 256,
height: 256,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
modal.style.display = 'block';
checkPaymentStatus(orderNo);
}
// 关闭二维码弹窗
function closeQRCode() {
document.getElementById('qrcodeModal').style.display = 'none';
document.getElementById('submitBtn').disabled = false;
document.getElementById('submitBtn').textContent = '确认支付';
}
// 调用微信内支付
function callWechatPay(params) {
const submitBtn = document.getElementById('submitBtn');
if (typeof WeixinJSBridge === 'undefined') {
alert('请在微信内打开页面进行支付');
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
return false;
}
const requiredParams = ['appId', 'timeStamp', 'nonceStr', 'package', 'paySign'];
for (const param of requiredParams) {
if (!params[param]) {
alert('支付参数不完整,无法发起支付');
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
return false;
}
}
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
"appId": params.appId,
"timeStamp": params.timeStamp,
"nonceStr": params.nonceStr,
"package": params.package,
"signType": params.signType || 'RSA',
"paySign": params.paySign
},
function(res) {
// 无论成功或失败都重置按钮状态
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
if (res.err_msg == "get_brand_wcpay_request:ok") {
window.location.href = '/payments/success/';
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
alert('您已取消支付');
} else {
alert('支付失败: ' + res.err_msg);
}
}
);
return true;
}
// 处理表单提交
document.getElementById('rechargeForm').addEventListener('submit', async function(e) {
e.preventDefault();
// 验证支付方式是否已选择
const selectedPayment = document.querySelector('input[name="payment_method"]:checked');
if (!selectedPayment) {
alert('请选择支付方式');
return;
}
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.textContent = '处理中...';
try {
const formData = new FormData(this);
const response = await fetch('/payments/recharge/', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('网络响应不正常');
const data = await response.json();
if (data.status === 'success') {
if (data.pay_url) {
window.location.href = data.pay_url;
} else if (data.code_url) {
showWechatQRCode(data.code_url, data.order_no);
} else if (data.pay_params) {
if (!callWechatPay(data.pay_params)) {
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
}
}
} else {
alert(data.message || '支付请求失败');
submitBtn.disabled = false;
submitBtn.textContent = '确认支付';
}
} catch (error) {
console.error('支付请求出错:', error);
alert('网络错误,请重试');
resetSubmitButton();
}
});
// 页面加载时初始化支付方式
document.addEventListener('DOMContentLoaded', detectEnvironment);
</script>
一提交支付就弹出:支付验证签名失败 ,查看后端的输出的日志:
INFO 2025-04-02 22:58:49,277 middleware 2077 140355370846336 Payment Request: GET /payments/recharge/
INFO 2025-04-02 22:58:51,758 middleware 2077 140355370846336 Payment Request: POST /payments/recharge/
INFO 2025-04-02 22:58:51,759 middleware 2077 140355370846336 POST Data: {'csrfmiddlewaretoken': 'HuDgXYafdKsufP7WfaDl3CdqnTEom1lB6f6xF7dWeiCwCwE4thXNxIkQSWsDjLZ0', 'amount': '10', 'package': '10', 'payment_method': 'wechat_mp'}
INFO 2025-04-02 22:58:51,787 views 2077 140355370846336 开始处理微信支付,订单号: 20250402225851922732
INFO 2025-04-02 22:58:51,787 views 2077 140355370846336 支付方式: wechat_mp
INFO 2025-04-02 22:58:51,788 views 2077 140355370846336 支付金额(分): 1000
INFO 2025-04-02 22:58:51,832 views 2077 140355370846336 微信支付实例初始化成功
INFO 2025-04-02 22:58:51,832 views 2077 140355370846336 处理微信内支付(JSAPI)
INFO 2025-04-02 22:58:51,832 views 2077 140355370846336 用户公众号OpenID: o5gkx55DJ-zSSMPaQ.....
DEBUG 2025-04-02 22:58:51,877 core 2077 140355370846336 Request url: https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
DEBUG 2025-04-02 22:58:51,878 core 2077 140355370846336 Request type: POST
DEBUG 2025-04-02 22:58:51,878 core 2077 140355370846336 Request headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'wechatpay v3 api python sdk(https://github.com/minibear2021/wechatpayv3)', 'Authorization': 'WECHATPAY2-SHA256-RSA2048 mchid="1606......",nonce_str="2033AD92E72948C794F41F7E933E73DD",signature="TApvtpBkVJyvzscEbL/I0PSUL+dZLfEdP/Pi97j9xf28BnTzjoBv5eOCyVvtdfM6SSQR96FMOYZZPpCD6VSfZj4fshZZweD3UFmnx5lBhb7X2dmWK0c1Gz5D+OJ3um4jV9TIKEd/o/NCWv2F7kAh4HITHvqB9fQlzgchV1v5dD0v7W2IID3gfczgJB6jcWe5zrxyhrCBxSv3N/Mj+SzNEqJP3toYFNNhgDQd/wuO+VopLPLE7YKW1lLMGMSmc93bUQYP4fvx4O7TCjfNH3Na7L2nWW3YRsHLQmwMgHtnCOxLPCpmceQwgV1HbqYaW/Rzk+1uxO5hIWiE20q54qnKkw==",timestamp="1743605931",serial_no="2ACCDC2342F6254DD34E79........."'}
DEBUG 2025-04-02 22:58:51,878 core 2077 140355370846336 Request params: {'notify_url': 'https://www.xxxx.cn/payments/wechat/notify/', 'description': '账户充值', 'out_trade_no': '20250402225851922732', 'amount': {'total': 1000}, 'payer': {'openid': 'o5gkx55DJ-zSSMPaQB3..........'}, 'appid': 'wxbfcf8cd.......', 'mchid': '16062..........'}
DEBUG 2025-04-02 22:58:52,290 core 2077 140355370846336 Response status code: 200
DEBUG 2025-04-02 22:58:52,291 core 2077 140355370846336 Response headers: {'Server': 'nginx', 'Date': 'Wed, 02 Apr 2025 14:58:52 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': '52', 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=8', 'Cache-Control': 'no-cache, must-revalidate', 'X-Content-Type-Options': 'nosniff', 'Request-ID': '08ABA1B5BF0610890718CBDFEEA3062082CC0428BEAC02-0', 'Content-Language': 'zh-CN', 'Wechatpay-Nonce': '6c6d3338bc6892c84f37692657db6d05', 'Wechatpay-Signature': 'OUiJPyjv7wMQF54YpgiUx+JTVo2g75j9X9M4phUzeL9kWjEEMH+0hH2/WiM8OseL9z4WrQaG2jqwmDYfhOFRORzgu3hQW56FL4b3l6ntjw2NlrJw/05bgrgkc+wtMzgq4zxbo5v/OhwdjxjJL/TENsGSm+5FDyYZajHJjyHIjJk+TRVnTDFMfKiTy2hOxtbZ0sZ4jzMbid0nX7h2WyA+hOfS/d+SeVWzBbnkSjYRILj/LH9QS+LKq8/2x4jbJCPcbj4AE3HmwVkfNCrj6vfcbPmbrOPyI5QEleG8ufujsgYLziCPwDcZfKpF6S8UxGlYoUFi/XG1/Awx8mtbNT/CAg==', 'Wechatpay-Timestamp': '1743605932', 'Wechatpay-Serial': '7A8944B09297B8D38586337081A95ED40139D254', 'Wechatpay-Signature-Type': 'WECHATPAY2-SHA256-RSA2048'}
DEBUG 2025-04-02 22:58:52,291 core 2077 140355370846336 Response content: {"prepay_id":"wx0222585222892369d1490734869d460000"}
INFO 2025-04-02 22:58:52,291 views 2077 140355370846336 微信支付API返回 - 状态码: 200, 消息: {"prepay_id":"wx0222585222892369d1490734869d460000"}
INFO 2025-04-02 22:58:52,292 views 2077 140355370846336 成功生成JSAPI支付参数
DEBUG 2025-04-02 22:58:52,292 views 2077 140355370846336 签名原始字符串: wxbfcf8c...............
1743605932
feb3847e3b964ba5add659ca8126af35
prepay_id=wx0222585222892369d1490734869d460000
DEBUG 2025-04-02 22:58:52,292 views 2077 140355370846336 生成签名: VlthV0QaJBldmBtGkn4wDek+UEUxwb4ZDIprLEEMu/xMfk63lYHEJmTTEETA4I4c...
INFO 2025-04-02 22:58:52,292 middleware 2077 140355370846336 Payment Response: {'status': 'success', 'pay_params': {'appId': 'wxbfcf8c...........', 'timeStamp': '1743605932', 'nonceStr': 'feb3847e3b964ba5add659ca8126af35', 'package': 'prepay_id=wx0222585222892369d1490734869d460000', 'signType': 'RSA', 'paySign': 'VlthV0QaJBldmBtGkn4wDek+UEUxwb4ZDIprLEEMu/xMfk63lYHEJmTTEETA4I4cApcCP4wbGTYNUJ8a+acgWHGPbJD2Z4x4MEUi9vk+w6N4R8YSO/vJPEfp6hQ5wcyp1Wknb3t29AOsjwsiKmw+Sa13CdXNJ+RITn3iK45emS6VpCkepBQzxuHryBFSNQc/2axHzGoON2qs6ZoyL395W+WsZce8oxfMSpQGXLAU337E4FMLjD3j4VqFjgkXBapTZb/ECOm92t5TVtsUOmKNRb12LEUIMJdHH4sbrkoKvL0BrKa30o5sVxUueQrtr2r1cSt7PXeT0p3g66zbAxhlVA==', 'order_no': '20250402225851922732'}}
似乎到了最后一步就可以弹出支付框了,但是就是提示:支付验证签名失败 或别的不,我的商户是特约商户不是普通商户跟这个有关吗? 在电脑端用微信扫码支付就很正常。 微信服务号的ip白名单,权限域名,以及受权域名都加好了。我是直接在服务器上测试的,不知道再从哪果查找问题了?麻烦各位有知道原因的吗? 谢谢
谢谢各位。已经在微信开放社区被高手解决了
我是用的 wechatpayv3 这个库。按这个的示例写的签名方法传进去就正常调起微信了。
我上面的代码里的签名算法里有换行,而他们的示例里没有换行。 如下: