springBoot 微信支付 PC网站微信扫码支付-Native支付

  • 一、采坑大合集
    • 1.当前商户号暂不支持关联该类型的appid
    • 2.签名错误,请检查后再试
  • 二、springboot集成微信支付Demo(老版本XML)
    • 1.官方SDK下载:[https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1)

最近项目需求集成支付功能,支付宝支付就不用多说了,官方文档Demo都很详细,仔细搞一下就可以。今天这篇文章主要讲集成PC网站集成Native支付,中间遇到了几个坑,这里给大家讲解下解决方法,相信一定会帮到你。
下面的支付Demo是老版本使用xml传输的格式,新版本使用的是json
api文档: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1

一、采坑大合集

1.当前商户号暂不支持关联该类型的appid

微信支付需要的三个重要参数 :1.mch_id 2.mch_api_key 3.app_id

  1. mch_id:商户id,这个去微信商户平台注册认证即可
  2. mch_api_key:注册成功后,进入商户平台->账户中心->API安全->设置API密钥
  3. app_id:appId,才是接下来的重点,当你根据官方文档说明,去微信公众平台注册账号,并交完认证费300元后,获得了appId,然后再进行appId与mch_id绑定时,提示"当前商户号暂不支持关联该类型的appid"。去社区客服回答:“开放平台网站应用和第三方应用均不支持自助绑定,请悉知”
    解决办法: 在微信公众平台注册时,有四种选择:订阅号,服务号,小程序,企业微信。默认创建的应该是订阅号,但是Native支付只能绑定公众平台类型为服务号的appId,时间:2021/09/03,以后是否支持不一定。所以小伙伴们麻溜的去申请一个服务号类型的appId就可以了

2.签名错误,请检查后再试

  1. 签名错误问题,首先需要检查的是对照官方的api,看看哪些字段是必须的,是否有漏掉
  2. 字段无误后,发现还是错误,然后网上搜了一圈,说是什么必须按照某个顺序罗列字段才行,然后你就试了各种顺序还是错误。其实并不是你的字段错误,而真的是签名错误。
  3. 签名校验原理:将所有参数用api_key进行md5加密后生成sign,然后将sign随参数一起发送微信,微信端根据参数也用api_key进行md5加密后生成的sign与你参数的sign进行比较,相同的话就认证成功,不同就认证失败


    看上面两段代码有何不同,在生成sign的时候一定要将全部参数都加进去,不能在生成了sign后,还有别的参数添加,这样就会导致签名错误。所以只要保证sign在最后一个就好了

二、springboot集成微信支付Demo(老版本XML)

1.官方SDK下载:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1


好多工具类从sdk中copy即可,下面是我的集成demo,

统一下单以及回调接口
WXpay.java

private static String wxpayString="<form name=\"punchout_form\" method=\"post\" action=\"%s\">\n" +"<input type=\"hidden\" name=\"wxpayUrl\" value=\"%s\">\n" +"<input type=\"hidden\" name=\"id\" value=\"%s\">\n" +"<input type=\"submit\"  style=\"display:none\" >\n" +"</form>\n" +"<script>document.forms[0].submit();</script>\n";private static String  qrCodeString="/wxpay/qrCode";/*** 腾讯支付* @param product* @param order must 1.order_code 2.money 3.spbill_create_ip* @return* @throws Exception*/public static String goWXpay(BillingPackageVo product, BillingPackageOrderVo order) {String result="";try {//1.配置请求参数HashMap<String,String> reqData=new HashMap<>();reqData.put("appid", app_id);reqData.put("mch_id", mch_id);reqData.put("device_info", WEB);reqData.put("nonce_str", WXpayUtil.generateNonceStr());reqData.put("sign_type", SignType.MD5+"");//商品描述reqData.put("body", product.getBillingPackageName()+",服务期限"+product.getBillingPeriod()+"天,支持设备"+product.getMaxAccessClientNumber()+"个");//商品详情
//        reqData.put("detail", "微信支付测试");//订单号reqData.put("out_trade_no",order.getBillingPackageOrderCode());//订单总金额,单位为分reqData.put("total_fee",(long)(order.getOrderAmount()*100)+"");//终端IP,客户端IPreqData.put("spbill_create_ip",order.getSpbillCreateIp());//通知地址reqData.put("notify_url",notify_url);//交易类型reqData.put("trade_type", TradeType.NATIVE+"");//商品Id,trade_type=NATIVE时,此参数必传。reqData.put("product_id",order.getBillingPackageOrderCode());// 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用reqData.put("attach",String.valueOf(null==order.getAccessClientId()?0:order.getAccessClientId()));reqData.put("sign", WXpayUtil.generateSignature(reqData, api_key, SignType.MD5));// 2.map转换成xmlString reqBody = WXpayUtil.mapToXml(reqData);log.info("订单号:"+order.getBillingPackageOrderCode()+",微信支付请求xml:"+reqBody);//3.发送支付请求String resXml= HttpClient.sendPostDataByXml(gatewayUrl, reqBody);log.info("订单号:"+order.getBillingPackageOrderCode()+",微信支付返回xml:"+resXml);//4.xml转换成mapMap<String, String> resMap = WXpayUtil.xmlToMap(resXml);String codeUrl=(SUCCESS.equals(resMap.get("return_code")) && SUCCESS.equals(resMap.get("result_code")))?resMap.get("code_url"):"";//5.组装返回值result=String.format(wxpayString,qrCodeString,codeUrl,order.getId());log.info("微信支付跳转信息:"+result);return result;}catch (Exception e){e.printStackTrace();log.info("微信支付请求失败,订单号:"+order.getBillingPackageOrderCode());return  result;}}/*** 微信支付回调* @param request* @return*/@ResponseBody@RequestMapping("/wechatNotify")public String weChatNotify(HttpServletRequest request) throws Exception {log.info("微信支付成功, 进入异步通知接口...");Map<String,String> returnMap=new HashMap<>();returnMap.put("return_code",SUCCESS);returnMap.put("return_msg","");Map<String, String> notifyMap = WXpayUtil.getNotifyParameter(request);  // 转换成mapif (!this.isPayResultNotifySignatureValid(notifyMap)) {// 签名错误,如果数据里没有sign字段,也认为是签名错误log.info("签名错误!!!!");returnMap.put("return_msg","sign not valid");return WXpayUtil.mapToXml(returnMap);}//订单号String orderCode = notifyMap.get("out_trade_no");String resultCode = notifyMap.get("result_code");//格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。Long paymentTime =DataUtil.stringIntegerSpecialDate(notifyMap.get("time_end"));String paymentCode= notifyMap.get("transaction_id");// 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用String attach= notifyMap.get("attach");//这里做其他业务操作returnMap.put("return_msg","");return WXpayUtil.mapToXml(returnMap);}/*** 判断支付结果通知中的sign是否有效** @param reqData 向wxpay post的请求数据* @return 签名是否有效* @throws Exception*/private boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception {String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE);SignType signType;if (signTypeInData == null) {signType = SignType.MD5;} else {signTypeInData = signTypeInData.trim();if (signTypeInData.length() == 0) {signType = SignType.MD5;}else if (WXPayConstants.MD5.equals(signTypeInData)) {signType = SignType.MD5;}else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) {signType = SignType.HMACSHA256;}else {throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData));}}return this.isSignatureValid(reqData,api_key, signType);}/*** 判断签名是否正确,必须包含sign字段,否则返回false。** @param data Map类型数据* @param key API密钥* @param signType 签名方式* @return 签名是否正确* @throws Exception*/public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {return false;}String sign = data.get(WXPayConstants.FIELD_SIGN);return WXpayUtil.generateSignature(data, key, signType).equals(sign);}

WXpayUtil.java

private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final Random RANDOM = new SecureRandom();/*** XML格式字符串转换为Map** @param strXML XML字符串* @return XML数据转换后的Map* @throws Exception*/public static Map<String, String> xmlToMap(String strXML) throws Exception {try {Map<String, String> data = new HashMap<String, String>();DocumentBuilder documentBuilder = WXpayUtil.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}try {stream.close();} catch (Exception ex) {ex.printStackTrace();}return data;} catch (Exception ex) {throw ex;}}/*** 将Map转换为XML格式的字符串** @param data Map类型数据* @return XML格式的字符串* @throws Exception*/public static String mapToXml(Map<String, String> data) throws Exception {org.w3c.dom.Document document = WXpayUtil.newDocument();org.w3c.dom.Element root = document.createElement("xml");document.appendChild(root);for (String key: data.keySet()) {String value = data.get(key);if (value == null) {value = "";}value = value.trim();org.w3c.dom.Element filed = document.createElement(key);filed.appendChild(document.createTextNode(value));root.appendChild(filed);}TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();DOMSource source = new DOMSource(document);transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");transformer.setOutputProperty(OutputKeys.INDENT, "yes");StringWriter writer = new StringWriter();StreamResult result = new StreamResult(writer);transformer.transform(source, result);String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");try {writer.close();}catch (Exception ex) {ex.printStackTrace();}return output;}/*** 获取随机字符串 Nonce Str** @return String 随机字符串*/public static String generateNonceStr() {char[] nonceChars = new char[32];for (int index = 0; index < nonceChars.length; ++index) {nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));}return new String(nonceChars);}/*** 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。** @param data 待签名数据* @param key API密钥* @param signType 签名方式* @return 签名*/public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if (k.equals(WXPayConstants.FIELD_SIGN)) {continue;}if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名sb.append(k).append("=").append(data.get(k).trim()).append("&");}sb.append("key=").append(key);if (SignType.MD5.equals(signType)) {return MD5(sb.toString()).toUpperCase();}else if (SignType.HMACSHA256.equals(signType)) {return HMACSHA256(sb.toString(), key);}else {throw new Exception(String.format("Invalid sign_type: %s", signType));}}/*** 生成 MD5** @param data 待处理数据* @return MD5结果*/public static String MD5(String data) throws Exception {java.security.MessageDigest md = MessageDigest.getInstance("MD5");byte[] array = md.digest(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 生成 HMACSHA256* @param data 待处理数据* @param key 密钥* @return 加密结果* @throws Exception*/public static String HMACSHA256(String data, String key) throws Exception {Mac sha256_HMAC = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");sha256_HMAC.init(secret_key);byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 从request的inputStream中获取参数* @param request* @return* @throws Exception*/public  static Map<String, String> getNotifyParameter(HttpServletRequest request) throws Exception {InputStream inputStream = request.getInputStream();ByteArrayOutputStream outSteam = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length = 0;while ((length = inputStream.read(buffer)) != -1) {outSteam.write(buffer, 0, length);}outSteam.close();inputStream.close();// 获取微信调用我们notify_url的返回信息String resultXml = new String(outSteam.toByteArray(), "utf-8");log.info("微信异步通知返回xml:"+resultXml);Map<String, String> notifyMap = xmlToMap(resultXml);log.info("********************** 微信支付返回参数**********************");notifyMap.forEach((key, value) -> {log.info((key+":"+value));});log.info("********************************************");return notifyMap;}/*** 组装xml* @return* @throws ParserConfigurationException*/public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);documentBuilderFactory.setXIncludeAware(false);documentBuilderFactory.setExpandEntityReferences(false);return documentBuilderFactory.newDocumentBuilder();}public static Document newDocument() throws ParserConfigurationException {return newDocumentBuilder().newDocument();}

WXPayConfig.java

@Configuration
public class WXPayConfig implements InitializingBean {// 公众账号IDpublic static String app_id;// 商户号public static String mch_id;// 商户的key【API密匙】public static String api_key;// 微信网关public static String gatewayUrl;// 服务器异步通知页面路径public static String notify_url;@Value("${wxpay.app_id}")private String getApp_id;@Value("${wxpay.mch_id}")private String getMch_id;@Value("${wxpay.api_key}")private String getApi_key;@Value("${wxpay.gatewayUrl}")private String getGatewayUrl;@Value("${project_address}")private String getProject_address;private String getNotify_url="/wxpay/wechatNotify";@Overridepublic void afterPropertiesSet() throws Exception {WXPayConfig.app_id=this.getApp_id;WXPayConfig.mch_id=this.getMch_id;WXPayConfig.api_key=this.getApi_key;WXPayConfig.gatewayUrl=this.getGatewayUrl;WXPayConfig.notify_url=this.getProject_address+this.getNotify_url;}
}

httpClient.java

 /*** post请求传输xml数据* @param url* @param xml* @return* @throws ClientProtocolException* @throws IOException*/public static String sendPostDataByXml(String url, String xml){log.info("url:"+url+",body:"+xml);String result = "";// 创建httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建post方式请求对象HttpPost httpPost = new HttpPost(url);// 设置参数到请求对象中StringEntity stringEntity = new StringEntity(xml, "UTF-8");httpPost.addHeader("Content-Type", "text/xml");httpPost.setEntity(stringEntity);// 执行请求操作,并拿到结果(同步阻塞)CloseableHttpResponse response = null;try {response = httpClient.execute(httpPost);// 获取结果实体// 判断网络连接状态码是否正常(0--200都数正常)if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {result = EntityUtils.toString(response.getEntity(), "UTF-8");log.info("xml请求返回结果:"+result);}} catch (Exception e) {e.printStackTrace();log.info("请求失败:"+url);}finally {// 释放链接try {response.close();httpClient.close();} catch (IOException e) {e.printStackTrace();}}return result;}

PC网站微信扫码支付,Native支付,“当前商户号暂不支持关联该类型的appid“,“签名错误,请检查后再试““springBoot 微信支付“相关推荐

  1. 微信支付,“签名错误,请检查后再试” 的坑爹经历

    微信提交预订单**请求**XML报文: <xml>   <appid><![CDATA[wx45485fdfd5fd5fdfd]]></appid>   ...

  2. 微信小程序——解决上传并部署云函数时报错ResourceNotFound.Function, 未找到函数版本,请创建后再试。 (7f2d9d2d-5eac-4575-9n57-acd66cfa587g

    1. 上传部署我们的云函数 2. 报错 错误信息为:Error: ResourceNotFound.Function, 未找到函数版本,请创建后再试. (7f2d9d2d-5eac-4575-9b57 ...

  3. JAVA 接入微信扫码(Native)支付

    一:web网站接入微信扫码支付功能(NATIVE) 二:准备工作 微信支付配置参数 1:appId 商家平台ID 2:mchID 商户平台ID 3:machSecret 商户平台密钥 以上三个参数找老 ...

  4. pc端实现微信扫码登录

    pc端实现微信扫码登录 流程:使用vue-wxlogin组件当我们打开微信扫一扫,此时二维码组件会有变化,显示扫描成功 我们的手机就会弹出一个授权页面.记住让后端绑定一个微信公众,通过授权该公众就可以 ...

  5. Spring Boot + OAuth2.0 实现微信扫码登录,这才叫优雅

    点击"终码一生",关注,置顶公众号 每日技术干货,第一时间送达! 微信开放平台:微信扫码登录功能 官方文档:https://developers.weixin.qq.com/doc ...

  6. TIM新版支持微信扫码登录:自动生成新QQ

    之前没注意,同事提醒新版TIM竟然可以用微信登录,还会自动生成新的QQ,这是微信QQ要互相打通的节奏吗? 深入了解发现,升级后的TIM新版,界面也变的像第三方微信辅助软件,微信微信扫码TIM登陆后,真 ...

  7. 微信扫码赚钱是什么意思

    最近流行微信扫码赚钱,那么,微信扫码是什么意思呢?其实,微信扫码,只要用微信扫二维码就可以赚钱的意思了.下面,具体与大家谈下微信扫描二维码赚钱. 微信扫码赚钱是今年新流行的一个词,具体的微信扫码赚钱是 ...

  8. PC网站微信扫码支付之Native支付(模式二)

    简介 Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信"扫一扫"完成支付的模式.该模式适用于PC网站.实体店单品或订单.媒体广告支付等场景. Native支付 ...

  9. 微信:微信扫码支付、调用统一下单接口、网站支付 + springmvc

    一.场景:公司需要在网站上进行微信支付. 二.API:使用微信开放平台的接入微信支付 -扫码支付.微信支付开发者平台链接 三.分析: 接入扫码支付(包含PC网站支付)包含三个阶段,问这里只讲使用,也就 ...

最新文章

  1. pandas dataframe 字符映射为数字
  2. C# 获取图片的EXIF 信息
  3. 人脸关键点 PFLD
  4. Pytorch学习 - Task6 PyTorch常见的损失函数和优化器使用
  5. sysbench 一些选项参数记录
  6. Android之ndk中JNIENV env->NewStringUTF (*env)->NewStringUTF
  7. MySQL 存储过程的变量
  8. ubuntu18.04窗口截图和选区截图快捷键
  9. 华为笔试题大全(史上最齐全)
  10. 【转】linux下tcp测试工具
  11. 织梦php 文章采集规则,织梦输入网址采集单个网页功能发布 不需要写采集规则一键采集...
  12. CSP-2022 游寄
  13. 关于JSP文件的运行方法
  14. 大学生面试着装要求(男生篇)
  15. 深度学习常用python库学习笔记
  16. 现实世界的模型与理论
  17. 2019年安徽省大学生网络攻防赛赛前培训——Day2
  18. windows下CUDA的卸载,怒推!!!亲测可以!!!
  19. 2017年12月全球数据库排名:SQL Server 跌势明显,刚获阿里投资的MariaDB表现良好
  20. c++:dll缺少依赖文件的解决方法

热门文章

  1. Java 后端开发常用的第三方服务 TOP10
  2. 【​观察】中国民生银行与华为组成新CP 共同释放金融科技创新红利
  3. 【智能商务】学习财务分析,看这10张思维导图就够啦
  4. 注册表与盘符(转victor888文章 )
  5. C源码:通过IP地址查找主机名 findip.c
  6. 2.跟我走吧,现在就出发
  7. 华为ensp防火墙web登陆配置
  8. 在阿里云上搭建私有GIT仓库
  9. 最新版codeblocks安装与汉化视频教程-自带编译器版
  10. 音频特征提取——常用音频特征