WeChat JSAPI 支付报错 appidget_brand_wcpay_request
结论
appidget_brand_wcpay_request 不是完整、标准的微信支付错误码。它很可能是下面这段代码把多个返回字段直接拼接后得到的字符串:
alert(res.err_code + res.err_desc + res.err_msg);
字段之间没有分隔符,真实的错误信息因此混在了一起。从字符串里的 appid 和 get_brand_wcpay_request 来看,问题通常与支付参数格式、appId 不匹配、支付授权目录配置有关,也可能是后端返回的内容并非 JavaScript 对象。
先输出完整的回调结果和 /RenewPay/GetData 的原始响应,再根据具体字段排查。
先打印真实错误
不要再直接拼接 err_code、err_desc 和 err_msg,改为:
WeixinJSBridge.invoke('getBrandWCPayRequest', payData, function (res) {
console.log("WeChat Pay result:", res);
alert(JSON.stringify(res));
});
重点查看 res.err_msg,常见结果有:
get_brand_wcpay_request:ok
get_brand_wcpay_request:cancel
get_brand_wcpay_request:fail
它们分别表示:
ok:用户完成了支付调用,但最终支付状态仍应以后端支付通知或主动查单结果为准。cancel:用户主动取消了支付。fail:参数、签名、商户配置或授权关系存在问题。
部分环境不会返回 err_code 和 err_desc,因此不能假定这两个字段一定有值。
检查后端返回的数据类型
WeixinJSBridge.invoke() 的第二个参数必须是对象。如果 ASP.NET MVC 返回的是 JSON 字符串,前端需要先解析。
可以明确指定 JSON 响应:
function jsApiCall() {
$.ajax({
url: "/RenewPay/GetData",
type: "GET",
dataType: "json",
success: function (data) {
var payData = data;
if (typeof payData === "string") {
payData = JSON.parse(payData);
}
console.log("Payment parameters:", payData);
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
payData,
function (res) {
console.log("WeChat Pay result:", res);
alert(JSON.stringify(res));
}
);
},
error: function (xhr) {
console.error("GetData failed:", xhr.status, xhr.responseText);
alert("获取支付参数失败");
}
});
}
后端不要对对象进行二次 JSON 序列化。例如,在 MVC 中可以直接返回:
return Json(new
{
appId = appId,
timeStamp = timeStamp,
nonceStr = nonceStr,
package = packageValue,
signType = "MD5",
paySign = paySign
}, JsonRequestBehavior.AllowGet);
不要写成:
return Json(JsonConvert.SerializeObject(payData));
后一种写法容易让前端拿到 JSON 字符串,而不是支付参数对象。
核对参数名称和格式
调用 getBrandWCPayRequest 时,参数名称区分大小写。典型结构如下:
{
appId: "wx1234567890abcdef",
timeStamp: "1700000000",
nonceStr: "random-string",
package: "prepay_id=wx201410272009395522657a690389285100",
signType: "MD5",
paySign: "SIGNATURE"
}
需要逐项检查:
- 字段名必须是
appId,不能写成appid或AppId。 - 字段名必须是
timeStamp,不能写成timestamp或TimeStamp。 - 字段名必须是
nonceStr,不能写成noncestr或NonceStr。 package必须采用prepay_id=...的形式。timeStamp通常应以字符串形式传递。signType必须与生成paySign时使用的签名方式一致。paySign必须使用商户支付密钥,按对应接口规则生成,不能使用公众号的 AppSecret。- 不能把统一下单请求的
sign直接用作前端调起支付所需的paySign。前端参数需要按规定重新签名。
还要特别留意 ASP.NET 的属性命名。直接序列化 C# 模型时,可能输出:
{
"AppId": "...",
"TimeStamp": "...",
"NonceStr": "..."
}
这些字段名不符合微信支付要求。应使用匿名对象、序列化属性标注或序列化命名策略,保证最终 JSON 中的字段大小写正确。
核对 appId、openid 和商户号的关系
支付参数中的 appId 必须是当前发起 JSAPI 支付的公众号 AppID,而且要在以下环节中保持一致:
- 用户通过该公众号对应的微信网页完成 OAuth 授权。
- 统一下单时使用该公众号的
appId。 - 统一下单中的
openid属于该appId。 - 商户号已与该公众号 AppID 建立正确的支付授权或绑定关系。
- 前端调起支付时仍然使用同一个
appId。
常见问题包括:
- 使用公众号 A 获取的
openid,却用公众号 B 的appId下单。 - 后端配置的是测试环境或其他公众号的 AppID。
- 用小程序 AppID 或开放平台应用 AppID 代替公众号 AppID。
- 商户号没有与当前公众号建立对应关系。
如果错误信息中明确出现 appid,应优先检查这些配置是否一致。
核对统一下单结果
前端调起支付之前,后端必须先成功创建 JSAPI 预支付订单。确认统一下单参数中包含:
trade_type = JSAPI
同时,统一下单响应中应返回有效的:
prepay_id
不能只看 HTTP 请求是否成功,还要检查微信返回的业务字段。不同版本的微信支付 API 使用的具体字段可能不同,但处理原则相同:只有下单成功并取得有效的预支付标识,才能生成前端支付参数。
同时确认以下事项:
- 订单号没有被其他订单重复使用。
- 金额和币种符合接口要求。
- 回调地址可以被微信支付服务器访问。
prepay_id没有过期。- 服务器时间正确,生成的时间戳没有明显偏差。
- 参与签名的字段、字段大小写、排序方式和密钥均符合当前 API 版本的规则。
不要混用不同版本的下单接口、签名算法和前端调起参数。
核对 JSAPI 支付授权目录
公众号后台配置的 JS 接口安全域名,与微信支付商户平台中的 JSAPI 支付授权目录不是同一项配置,两者解决的问题也不同。
假设支付页面地址是:
https://www.example.com/RenewPay/Index
需要在微信支付商户平台中配置与页面路径匹配的 JSAPI 支付授权目录,例如:
https://www.example.com/RenewPay/
检查时注意:
- 应使用实际发起支付页面的 URL,而不是
/RenewPay/GetData接口地址。 - 协议、域名、端口和路径必须与实际页面一致。
- 如果支付页面发生重定向,以微信中最终打开的页面地址为准。
- 测试域名、正式域名和不同子域名需要分别核对。
- 页面必须通过微信内置浏览器打开。
只配置公众号的 JSAPI 或 JS 接口安全域名,不能代替商户平台中的支付授权目录配置。
推荐的前端实现
function requestPayment() {
$.ajax({
url: "/RenewPay/GetData",
type: "GET",
dataType: "json",
cache: false,
success: function (data) {
var payData = typeof data === "string"
? JSON.parse(data)
: data;
console.log("Payment parameters:", payData);
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
payData,
function (res) {
console.log("Payment callback:", res);
switch (res.err_msg) {
case "get_brand_wcpay_request:ok":
// 这里不能作为最终到账依据,应向后端查询订单状态
window.location.href = "/RenewPay/Result";
break;
case "get_brand_wcpay_request:cancel":
alert("支付已取消");
break;
default:
alert("支付调用失败:" + JSON.stringify(res));
break;
}
}
);
},
error: function (xhr) {
console.error(xhr.status, xhr.responseText);
alert("无法获取支付参数");
}
});
}
function callpay() {
if (typeof WeixinJSBridge === "undefined") {
if (document.addEventListener) {
document.addEventListener(
"WeixinJSBridgeReady",
requestPayment,
false
);
} else if (document.attachEvent) {
document.attachEvent(
"WeixinJSBridgeReady",
requestPayment
);
document.attachEvent(
"onWeixinJSBridgeReady",
requestPayment
);
}
} else {
requestPayment();
}
}
按钮可以直接绑定调用函数:
<button type="button" onclick="callpay()">Pay Now</button>
建议的排查顺序
按以下顺序检查,通常能较快找到问题:
- 使用
JSON.stringify(res)获取完整的微信回调内容。 - 在浏览器调试工具中查看
/RenewPay/GetData的原始响应。 - 确认传给
invoke()的是对象,而不是字符串。 - 核对
appId、timeStamp、nonceStr、package、signType和paySign的名称及大小写。 - 确认
package的格式为prepay_id=...。 - 检查统一下单使用的
appId、openid、商户号和前端appId是否属于同一套配置。 - 在服务端使用最终返回给前端的字段重新验算支付签名。
- 检查微信支付商户平台中的 JSAPI 支付授权目录。
- 确认页面通过实际生产域名在微信内置浏览器中访问。
- 查看服务端的统一下单响应和支付通知日志。
前端回调中的 get_brand_wcpay_request:ok 只能说明客户端支付流程返回成功,不能直接作为发货或续费依据。最终业务状态必须以后端验签后的支付通知为准,也可以通过服务端主动查询订单获得支付结果。
备注:内容仅供参考。
