前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信公众号支付失败问题-微信支付提示 调用支付JSAPI缺少参数:appId

微信公众号支付失败问题-微信支付提示 调用支付JSAPI缺少参数:appId

原创
作者头像
聚优云惠
修改2019-07-22 11:20:47
14.6K0
修改2019-07-22 11:20:47
举报
文章被收录于专栏:建站教程建站教程

场景概述

鉴于去年做过微信app支付,小程序支付,支付宝app支付,云闪付app支付等方面的功能和研究。最近要完成一个在微信公众号支付的场景。其中遇到了一个坑,坑了我足足一上午多的时间。所以我想写下来记录,以后遇到微信相关API调用的时候,就不会花这么久的时间了。

技术场景:微信公众号支付

本文不会详细讲解微信公众号支付的具体流程,因为官网文档已经说得很详细,至少比我写的详细。而且本文的目的是记录我自己遇到的坑,而不是写支付教程。放心,下次有时间多的话,我会手把手的写下相关教程步骤的。

技术概况

开发前的准备工作

  • 微信公众号服务号+微信支付商户号(都是需要企业资格)
  • 备案的域名一个
  • 后端支付程序部署测试: 1:本地服务+花生壳端口映射的方式(我采用的方案) 2:购买云服务器和购买域名并且通过备案

微信公众号需要是服务号认证,需要开通微信支付功能,这些都需要交钱 300大洋一年。这里就不多讲了,如果开通服务号,商户号,网上资料很多。

我选择花生壳的目的是:第一:本地调式程序速度快,虽然本人拥有很多台阿里云的服务器,而且我也熟悉后端程序发布,但是我还是选择在开发测试阶段,先用花生壳本地端口映射。第二:花生壳提供的域名是经过备案的,做做测试用还是可以的。

blob.jpg
blob.jpg

看见没,花生壳端口映射的域名,支持外网80端口(只能提供一个80端口)和外网随机端口。内网主机的端口,你自定义定义。是不是非常方便和适合测试微信,支付宝,云闪付之类的开发测试呢?

开发阶段

因为本人需要自己搞定移动前端网页和后端支付接口,所以前端我采用我熟悉的技术栈React来开发,后端采用的是SpringBoot框架来开发。

后端接口程序

核心代码:微信公众号后端支付接口

接口URL: http://qq784602719.imwork.net/school/wxpay/webPay

请求参数:需要传入openId(微信公众号身份标识)和total_fee(支付金额)

代码语言:txt
复制
/**
代码语言:txt
复制
 * 公众号支付
 */
@RequestMapping(value ="/webPay",method = {RequestMethod.POST,RequestMethod.GET})
@ResponseBody
public AjaxResult webPay(
		HttpServletRequest request,
		HttpServletResponse response,
		@RequestParam("total_fee") 
		String total_fee,@RequestParam("openId") 
		String openId) {
	// openId,采用 网页授权获取 access_token API:SnsAccessTokenApi获取
	//String openId = (String) request.getSession().getAttribute("openId");
	System.out.println("openId:"+openId);
	System.out.println("total_fee:"+total_fee);
	if (StrKit.isBlank(openId)) {
		result.addError("openId is null");
		return result;
	}
	if (StrKit.isBlank(total_fee)) {
		result.addError("请输入数字金额");
		return result;
	}
	
代码语言:txt
复制
	String ip = IpKit.getRealIp(request);
代码语言:txt
复制
	System.out.println("ip:"+ip);
代码语言:txt
复制
	if (StrKit.isBlank(ip)) {
代码语言:txt
复制
		ip = "127.0.0.1";
代码语言:txt
复制
	}
代码语言:txt
复制
	System.out.println("AppId:"+WxPayApiConfigKit.getWxPayApiConfig().getAppId());
代码语言:txt
复制
	System.out.println("MchId:"+WxPayApiConfigKit.getWxPayApiConfig().getMchId());
代码语言:txt
复制
	System.out.println("PaternerKey:"+WxPayApiConfigKit.getWxPayApiConfig().getPaternerKey());
代码语言:txt
复制
	System.out.println("notify_url:"+notify_url);
代码语言:txt
复制
	Map<String, String> params = WxPayApiConfigKit.getWxPayApiConfig()
代码语言:txt
复制
			.setAttach("微信公众号支付测试  -By arison")
代码语言:txt
复制
			.setBody("微信公众号支付测试  -By arison")
代码语言:txt
复制
			.setOpenId(openId)
代码语言:txt
复制
			.setSpbillCreateIp(ip)
代码语言:txt
复制
			.setTotalFee(total_fee)
代码语言:txt
复制
			.setTradeType(TradeType.JSAPI)
代码语言:txt
复制
			.setNotifyUrl(notify_url)
代码语言:txt
复制
			.setOutTradeNo(String.valueOf(System.currentTimeMillis()))
代码语言:txt
复制
			.build();
代码语言:txt
复制
	String xmlResult = WxPayApi.pushOrder(false,params);
代码语言:txt
复制
      log.info(xmlResult);
代码语言:txt
复制
	Map<String, String> resultMap = PaymentKit.xmlToMap(xmlResult);
代码语言:txt
复制
	String return_code = resultMap.get("return_code");
代码语言:txt
复制
	String return_msg = resultMap.get("return_msg");
代码语言:txt
复制
	if (!PaymentKit.codeIsOK(return_code)) {
代码语言:txt
复制
		result.addError(return_msg);
代码语言:txt
复制
		return result;
代码语言:txt
复制
	}
代码语言:txt
复制
	String result_code = resultMap.get("result_code");
代码语言:txt
复制
	if (!PaymentKit.codeIsOK(result_code)) {
代码语言:txt
复制
		result.addError(return_msg);
代码语言:txt
复制
		return result;
代码语言:txt
复制
	}
代码语言:txt
复制
	// 以下字段在return_code 和result_code都为SUCCESS的时候有返回
代码语言:txt
复制
	String prepay_id = resultMap.get("prepay_id");
代码语言:txt
复制
	Map<String, String> packageParams = PaymentKit.prepayIdCreateSign(prepay_id);
代码语言:txt
复制
	String jsonStr = JSON.toJSONString(packageParams);
代码语言:txt
复制
	result.success(jsonStr);
代码语言:txt
复制
	System.out.println("data:"+jsonStr);
代码语言:txt
复制
	return result;
代码语言:txt
复制
}

接口方法里面有些参数加密的方法用进行了封装(对微信公众号支付提供的demo中的工具类),在这里代码就不一一展示了。因为并不是很重要。

前端支付程序

这里用的是React技术,网络请求用的是fetchPost。请求的时候,后端需要配合处理下跨域问题。

这里的userOpenid是在前端项目绑定页面中绑定成功之后,把微信公众号给的openId通过Redux传给其它React界面。所以这里通过this.props.userOpenid就拿到了Redux传过来的openId。

代码语言:txt
复制
 wxPayRequest = () => {
代码语言:txt
复制
    Toast.loading("")
代码语言:txt
复制
    // const data="wxpay/webPay"; this.props.userOpenid
代码语言:txt
复制
    fetchPost("http://qq784602719.imwork.net/school/wxpay/webPay", {
代码语言:txt
复制
        openId: this.props.userOpenid,
代码语言:txt
复制
        total_fee: '1'
代码语言:txt
复制
    }).then((response) => {
代码语言:txt
复制
        Toast.hide();
代码语言:txt
复制
        console.log("response:" + JSON.stringify(response));
代码语言:txt
复制
        const data = response.data;
代码语言:txt
复制
        if (typeof WeixinJSBridge == "undefined") {
代码语言:txt
复制
            if (document.addEventListener) {
代码语言:txt
复制
                document.addEventListener('WeixinJSBridgeReady', this.onBridgeReady(data), false);
代码语言:txt
复制
            } else if (document.attachEvent) {
代码语言:txt
复制
                document.attachEvent('WeixinJSBridgeReady', this.onBridgeReady(data));
代码语言:txt
复制
                document.attachEvent('onWeixinJSBridgeReady', this.onBridgeReady(data));
代码语言:txt
复制
            }
代码语言:txt
复制
        } else {
代码语言:txt
复制
            this.onBridgeReady(data);
代码语言:txt
复制
        }
代码语言:txt
复制
    }).catch((error) => {
代码语言:txt
复制
        console.log("error:" + JSON.stringify(error));
代码语言:txt
复制
    })
代码语言:txt
复制
}
代码语言:txt
复制
onBridgeReady = (json) => {
代码语言:txt
复制
    const that = this;
代码语言:txt
复制
    WeixinJSBridge.invoke(
代码语言:txt
复制
        'getBrandWCPayRequest', JSON.parse(json),
代码语言:txt
复制
        function (res) {
代码语言:txt
复制
            if (res.err_msg == "get_brand_wcpay_request:ok") {
代码语言:txt
复制
                // 使用以上方式判断前端返回,微信团队郑重提示:
代码语言:txt
复制
                //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
代码语言:txt
复制
                Toast.success("支付成功!")
代码语言:txt
复制
            } else {
代码语言:txt
复制
                message.info("支付失败:" + res.err_msg)
代码语言:txt
复制
                that.setState({
代码语言:txt
复制
                    payInfo: json,
代码语言:txt
复制
                    payResult: "支付失败:" + JSON.stringify(res)
代码语言:txt
复制
                });
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
}

当我在前端写的支付界面中调用上面的 wxPayRequest 方法后,遇到了错误 get_brand_wcpay_request:fail

我马上通过JSON.stringify(res)把错误对象解析成字符串放入界面展示,为了看到更全的报错信息。如图:

blob.jpg
blob.jpg

错误信息:

代码语言:txt
复制
{"err_desc":"调用支付JSAP缺少参数appId","err_msg":"get_brand_wcpay_request:fail","err_code":"-1"}

我当时就在想,我openId在本地后端程序日志窗口打印发现,我参数传到后端接口方法是对的啊,而且我微信公众号和商户号也设置正确了。尤其是设置了商户平台的 支付授权目录。

支付授权目录是:你的支付界面访问路径

http://qq784602719.imwork.net/school/rechargeHome 去掉最后一个斜杠后面的字符串

支付授权目录 http://qq784602719.imwork.net/school/

但是我设置了正确的支付授权目录之后,还是提示上面那个问题,缺少参数appId。很是纳闷啊。不过我感觉是前端JS调用方面确实出现了问题,于是开始排查JS端写的代码。

后面经过多次排查,问题果然还是被我找到了,找到之后,发现居然是一个很简单的问题。不过还是最终解决了问题。

blob.jpg
blob.jpg

问题的关键之处是我出问题的代码写成了如下:

代码语言:txt
复制
onBridgeReady = (json) => {
代码语言:txt
复制
    const that = this;
代码语言:txt
复制
    WeixinJSBridge.invoke(
代码语言:txt
复制
        'getBrandWCPayRequest', json,
代码语言:txt
复制
        function (res) {
代码语言:txt
复制
            if (res.err_msg == "get_brand_wcpay_request:ok") {
代码语言:txt
复制
                // 使用以上方式判断前端返回,微信团队郑重提示:
代码语言:txt
复制
                //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
代码语言:txt
复制
                Toast.success("支付成功!")
代码语言:txt
复制
            } else {
代码语言:txt
复制
                message.info("支付失败:" + res.err_msg)
代码语言:txt
复制
                that.setState({
代码语言:txt
复制
                    payInfo: json,
代码语言:txt
复制
                    payResult: "支付失败:" + JSON.stringify(res)
代码语言:txt
复制
                });
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
}

微信JSAP 接口 WeixinJSBridge.invoke中第二个参数是javascript对象,而不是json字符串!!!后面我用JSON.parse(json)把服务器接口给我的json字符串转化为javascript对象,就能顺利支付成功了。

附属参考文档:

微信公众号支付文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

微信支付提示 调用支付JSAPI缺少参数:appId :https://bbs.csdn.net/topics/391028145

微信支付JSAPI支付授权目录陷阱:https://blog.csdn.net/a7442358/article/details/85766204

解决我错误的启发文章:微信公众号支付JSAPI,提示:2支付缺少参数:appId

原文:http://cloud.yundashi168.com/archives/732#i-6

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景概述
  • 技术概况
    • 开发前的准备工作
      • 开发阶段
        • 后端接口程序
        • 前端支付程序
    • 附属参考文档:
    相关产品与服务
    访问管理
    访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档