• 最近公司有个项目要求在微信公众平台开发模式下使用客服系统。网上的教程有不少,但没有一个清晰的说明,所以我们就自己研究了一下,最后用微信公众平台客服网页版达到了公司的要求。项目配置我们开发模式的后端...

    最近公司有个项目要求在微信公众平台开发模式下使用客服系统。网上的教程有不少,但没有一个清晰的说明,所以我们就自己研究了一下,最后用微信公众平台客服网页版达到了公司的要求。

    项目配置

    我们开发模式的后端服务器采用的是Node.JS + Express框架。

    引入的第三方依赖包: wechat

    配置微信公众号

    登录https://mp.weixin.qq.com,在开发/基本设置/URL(服务器地址) 中配上后端服务器负责处理微信公众号消息的路由 http://mycompay.com/wechat,

    配置后端服务器路由

    后端服务器相应路由代码

    ~~~
    /**
    * 微信
    */
    var wechatConfig = require(‘../../config/wechat’);
    var wechat = require(‘wechat’);

    router.use(‘/’, wechat(wechatConfig, function (req, res, next) {

    // 微信输入信息都在req.weixin上
    var message = req.weixin;
    
    // 开始订阅
    if(message.MsgType === 'event' && message.Event === 'subscribe'){
    
        var reply = '你好,这里是西葫芦网官方商城和服务号。在这里,你可以一键海淘,商品均由海外商家直接发货,代购不求人,再也不用担心买到假货啦![红心][酷]';
        return res.reply(reply);
    }
    else if(message.MsgType === 'event' &&  message.Event === 'CLICK'){ // 点击菜单事件
    
        var reply = 'xxx';
        return res.reply(reply);
    }
    else { //其他都进入微信客服模式
        return res.transfer2CustomerService();
    }
    

    ~~~

    到这里,我们公众号用户发送的消息就能自动转接到微信客服系统里了。但现在我们的公众号还没有开通客服系统,所以接下去就是去开通客服系统

    开通客服系统

    参考 http://kf.qq.com/faq/120911VrYVrA150319Yj6re6.html

    配置公众号的客服人员

    开通客服系统之后,就需要去配置公众号的客服人员。

    客服人员登录客服系统网页端

    最后绑定微信号的客服小哥登录 https://mpkf.weixin.qq.com/cgi-bin/kfloginpage

    这样用户就能实现和客服小哥在微信公众号上聊天了

    关于我们

    如果还有什么问题的话,请随时联系我们

    QQ号:1063815019

    展开全文
  • 通过本课程的学习,学员能够入门微信公众平台开发,能够胜任企业级的订阅号、服务号、企业号的应用开发工作。 通过本课程的学习,学员能够对微信公众平台有一个清晰的、系统性的认识。例如,公众号是什么,它有...
  • php 微信开发模式小结

    2019-06-19 12:47:20
    微信开发模式小结 一、前言概括 我们用php编程语言怎么来做微信功能开发?首先我们要明确三个角色,微信平台,微信接口,微信功能。所有的开发我们都是在微信公众号上进行,使用微信提供的各种接口,开发与微信相关...

    微信开发模式小结

    一、前言概括

    我们用php编程语言怎么来做微信功能开发?首先我们要明确三个角色,微信平台,微信接口,微信功能。所有的开发我们都是在微信公众号上进行,使用微信提供的各种接口,开发与微信相关的功能。

    二、微信对接(也就是开启开发者模式)

    一般微信会提供一个开发语言相关的demo(直接去微信公众平台上下载),然后进入开发者配置,配置相关的访问地址,密钥模式等选项。(一定要保证配置的链接可以外网能访问)

    微信开发

    图片解析:用户进入你开发的公众号中发生请求(关注,点击,回复),微信都会把对应的响应数据推送到在开发者模式中配置的链接上。我们就能拿到用户的数据(openid,姓名,地址)注册用户等等,我们也可以回应微信的

    响应,例如:你关注公众号,那么会提示欢迎你关注 “code学院”之类的信息。

    总结:只要是在微信公众号里发生的相关操作,微信都会转发到开发者模式对应的地址上。

    三.接口使用

    开发微信功能只能使用微信提供的相关接口,依赖于微信平台,用其他的是不行的。所以开发微信,明白了其开发模式,那么就是去熟悉微信的各种接口了。

    文章来至:http://www.codexueyuan.com/learn/learn-detail35-wxkfxj

    转载于:https://blog.51cto.com/8584311/2086100

    展开全文
  • 登录微信公众平台官网后,进入到公众平台后台管理页面。 选择 公众号基本设置-》基本配置 ,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey。   微信公众号配置界面:    以上...

    一、填写服务器配置信息的介绍

     

    登录微信公众平台官网后,进入到公众平台后台管理页面。

    选择 公众号基本设置-》基本配置 ,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey。

     

    微信公众号配置界面:

     

     以上配置需要认证后才会得到相应的权限。如果开发阶段,我们可以申请开发者测试账号。

     

    为什么要申请测试账号?

    主要是因为测试账号比我们没有认证的微信账号权限大一点。足够测试我们的接口了。

     

     以下是微信测试号的一些配置选项:

     

    服务器配置:

      URL:是开发者用来接收微信消息和事件 的接口URL。(必须以http://开头,目前支持80端口)

      Token:可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。注意必须为英文或数字,长度为3-32字符。

      EncodingAESKey:由开发者手动填写或随机生成,将用作消息体加解密密钥。(消息加密密钥由43位字符组成,可随机修改,字符范围为A-Z,a-z,0-9。)

    同时,开发者可选择消息加解密方式:明文模式、兼容模式和安全模式。

      明文模式:不使用消息体加解密功能,安全系数较低

          兼容模式:明文、密文将共存,方便开发者调试和维护

          安全模式下:消息包为纯密文,需要开发者加密和解密,安全系数高

     

     

    做个宣传:这个是我的微信公众号,欢迎大家关注!!!

     

     

    看完上面的,还是不知道该填些什么吧,不用急,先空着,后面再填。

     

    二、验证服务器地址的有效性

    开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:

    参数 描述
    signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
    timestamp 时间戳
    nonce 随机数
    echostr 随机字符串

     

    开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

    加密/校验流程如下:
    1. 将token、timestamp、nonce三个参数进行字典序排序
    2. 将三个参数字符串拼接成一个字符串进行sha1加密
    3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

     

    说明一下,我使用的是eclipse ,java语言, tomcat 7.0 环境。

    这些配置可以参照我的其他博客文章进行配置,这里就不多说了。

    以上介绍也可以参考  微信开发文档

     

     

    第二部分:微信服务器对接的实现代码部分

     

     那么我们现在开始撸码吧!!!

     

    一、 创建java项目,并实现校验。

     

    下面我们写程序进行校验:

    (1)新建一个名字叫 souvc 的 web项目。

     

    (2) 选择web.xml文件的生成。

     

    (3)新建一个servlet类,来接收微信服务器传来信息。

    复制代码
    package com.souvc.weixin.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.souvc.weixin.util.SignUtil;
    
    /**
     * 类名: CoreServlet </br> 
     * 描述: 来接收微信服务器传来信息 </br> 
     * 开发人员: souvc</br> 
     * 创建时间:2015-9-29 </br> 
     * 发布版本:V1.0 </br>
     */
    public class CoreServlet extends HttpServlet {
    
        private static final long serialVersionUID = 4323197796926899691L;
    
        /**
         * 确认请求来自微信服务器
         */
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            
            // 微信加密签名
            String signature = request.getParameter("signature");
            // 时间戳
            String timestamp = request.getParameter("timestamp");
            // 随机数
            String nonce = request.getParameter("nonce");
            // 随机字符串
            String echostr = request.getParameter("echostr");
    
            PrintWriter out = response.getWriter();
            
            // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
            if (SignUtil.checkSignature(signature, timestamp, nonce)) {
                out.print(echostr);
            }
            
            out.close();
            out = null;
        }
    
        /**
         * 处理微信服务器发来的消息
         */
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // TODO 消息的接收、处理、响应
        }
    
    }
    复制代码

     

    (4) 加密校验程序的工具类。这个校验的方法,可以通过分析官方文档的demo,然后通过java语言来写出。

    官方php校验代码一览 。

    复制代码
    package com.souvc.weixin.util;
    
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    
    /**
    * 类名: SignUtil </br>
    * 描述: 检验signature 工具类 </br>
    * 开发人员: souvc </br>
    * 创建时间:  2015-9-29 </br>
    * 发布版本:V1.0  </br>
     */
    public class SignUtil {
        
        // 与接口配置信息中的Token要一致
        private static String token = "souvcweixin";
    
        /**
        * 方法名:checkSignature</br>
        * 详述:验证签名</br>
        * 开发人员:souvc</br>
        * 创建时间:2015-9-29  </br>
        * @param signature
        * @param timestamp
        * @param nonce
        * @return
        * @throws
         */
        public static boolean checkSignature(String signature, String timestamp,String nonce) {
            // 1.将token、timestamp、nonce三个参数进行字典序排序
            String[] arr = new String[] { token, timestamp, nonce };
            Arrays.sort(arr);
            
            // 2. 将三个参数字符串拼接成一个字符串进行sha1加密
            StringBuilder content = new StringBuilder();
            for (int i = 0; i < arr.length; i++) {
                content.append(arr[i]);
            }
            MessageDigest md = null;
            String tmpStr = null;
            try {
                md = MessageDigest.getInstance("SHA-1");
                // 将三个参数字符串拼接成一个字符串进行sha1加密
                byte[] digest = md.digest(content.toString().getBytes());
                tmpStr = byteToStr(digest);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            
            content = null;
            // 3.将sha1加密后的字符串可与signature对比,标识该请求来源于微信
            return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
        }
    
        /**
        * 方法名:byteToStr</br>
        * 详述:将字节数组转换为十六进制字符串</br>
        * 开发人员:souvc </br>
        * 创建时间:2015-9-29  </br>
        * @param byteArray
        * @return
        * @throws
         */
        private static String byteToStr(byte[] byteArray) {
            String strDigest = "";
            for (int i = 0; i < byteArray.length; i++) {
                strDigest += byteToHexStr(byteArray[i]);
            }
            return strDigest;
        }
    
        /**
        * 方法名:byteToHexStr</br>
        * 详述:将字节转换为十六进制字符串</br>
        * 开发人员:souvc</br>
        * 创建时间:2015-9-29  </br>
        * @param mByte
        * @return
        * @throws
         */
        private static String byteToHexStr(byte mByte) {
            char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };
            char[] tempArr = new char[2];
            tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
            tempArr[1] = Digit[mByte & 0X0F];
            String s = new String(tempArr);
            return s;
        }
    }
    复制代码

     

    (5)配置一下访问路径的 web.xml。

    复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
      <display-name>souvc</display-name>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
      
      <servlet>
            <servlet-name>coreServlet</servlet-name>
            <servlet-class>
                com.souvc.weixin.servlet.CoreServlet
            </servlet-class>
        </servlet>
      
        <!-- url-pattern中配置的/coreServlet用于指定该Servlet的访问路径 -->
        <servlet-mapping>
            <servlet-name>coreServlet</servlet-name>
            <url-pattern>/coreServlet</url-pattern>
        </servlet-mapping>
           
    </web-app>
    复制代码

     

    在webroot下面, 添加一个主页index.jsp,代码如下

    复制代码
    <%@ page language="java" contentType="text/html; charset=utf-8"  pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>欢迎访问我的网站 www.souvc.com </title>
    </head>
    <body>
            欢迎访问我的网站 www.souvc.com 
    </body>
    </html>
    复制代码


    校验实现代码,基本完成。

     

     

    二 、把实现部署到服务器上。

     

    方式一:部署到BAE上面。

     

    (1)把程序从BAE上面导出来。

     

    复制svn地址

     

     

    在本地新建一个weixin的文件夹,然后从服务器上checkout代码:

     

     

     如下图,说明成功导出代码。

     

     

    (2)把我们写的程序从eclipse里面导出来,并打包成war包。

     

     

     

     

     

    (3)把程序提交到BAE服务器上面。

     

     

     

     

     

     

     

    预览并发布

     

     

     

    访问以下我们的链接: http://chiyan.duapp.com/

     

     

     

    方式二:把校验实现代码部署到SAE上。

     

    (1) 进入到sae控制台。

     

     

    (2 )然后选择上传代码包。

     

    通过eclipse打包成war包,然后传到sae上。

     

    (3)生成自己专属的域名。

     第三部分: 配置微信服务器。

     

     一、 进入到公众号测试账号设置页面

     

     

    二、填写配置

     url 填写项目的访问公网地址(这里是chiyan.duapp.com)加上验证微信服务器有效性的Servlet或控制器的请求地址,这里请求地址是:/CoreServlet

     Token 填写验证微信服务器有效性的Servlet或控制器中自定义的常量 token

    配置之后,会提示配置成功。

     

     

     

    第四部分:依据接口文档实现业务逻辑

     

     一、验证之后需要做的一些逻辑处理。

    复制代码
    /**
         * 处理微信服务器发来的消息
         */
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // TODO 消息的接收、处理、响应
        }
    复制代码

     

    这个后面再写业务,到此,我们就完成了与微信服务器的校验。

     

     二、配置温馨提醒:

    有朋友直接输入该请求:http://chiyan.duapp.com/coreServlet

    是会报500空指针的错误,因为我们这样访问并没有我们程序中要传的参数传过去。

     

     

    其他文章关联:

    第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备

    第二篇 :微信公众平台开发实战Java版之开启开发者模式,接入微信公众平台开发

    第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装

    第四篇 :微信公众平台开发实战Java版之完成消息接受与相应以及消息的处理

    第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token

    第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    第七篇 :微信公众平台开发实战Java版之如何获取微信用户基本信息

    第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容

    其他:Web开发须知:URL编码与解码

    展开全文
  • 企业微信如何收费? 如何申请企业微信,创建企业内部应用? 企业微信OAuth2接入流程 关于网页授权的可信域名 如何调用企业微信API? 企业微信如何收费? 微信微信是免费申请的,不收取任何费用。若企业人数超过200...
    目录
    1. 企业微信如何收费?
    2. 如何申请企业微信,创建企业内部应用?
    3. 企业微信OAuth2接入流程
    4. 关于网页授权的可信域名
    5. 如何调用企业微信API?

    企业微信如何收费?

    微信微信是免费申请的,不收取任何费用。若企业人数超过200人,需要进入企业微信后台的认证系统进行认证,操作如下:
    在这里插入图片描述
    认证费用是300元,认证成功后的企业需要年审,费用是300元/年。

    如何申请企业微信,创建企业内部应用?

    1.首先,到企业微信官网进行账号注册。在这里插入图片描述
    2.注册成功后,进入管理员后台。

    在这里插入图片描述

    3.需要导入通讯录,同步后台通讯录。

    在这里插入图片描述

    4.创建应用,点击应用管理->创建应用,填写相应的信息,方可创建。

    在这里插入图片描述

    5. 点击创建的应用,设置可信度域名。

    在这里插入图片描述
    例如:确认无误后点击确定。
    在这里插入图片描述

    6.设置工作应用台主页

    在这里插入图片描述
    说明:
    如果企业需要在打开的网页里面携带用户的身份信息,第一步需要构造如下的链接来获取code参数:
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect

    appid在我的企业>企业信息 企业ID 可以看到

    在这里插入图片描述
    员工点击后,页面将跳转至 redirect_uri?code=CODE&state=STATE,企业可根据code参数获得员工的userid。code长度最大为512字节。

    至此,创建一个应用的流程差不多结束。记得设置应用的可见范围。

    企业微信OAuth2接入流程

    详情可参考:企业微信开发之授权登录
    流程为:
    A) 用户访问第三方服务,第三方服务通过构造OAuth2链接(参数包括当前第三方服务的身份ID,以及重定向URI),将用户引导到认证服务器的授权页
    B) 用户选择是否同意授权
    C) 若用户同意授权,则认证服务器将用户重向到第一步指定的重定向URI,同时附上一个授权码。
    D) 第三方服务收到授权码,带上授权码来源的重定向URI,向认证服务器申请凭证。
    E) 认证服务器检查授权码和重定向URI的有效性,通过后颁发AccessToken(调用凭证)
    在这里插入图片描述

    关于网页授权的可信域名

    REDIRECT_URL中的域名,需要先配置至应用的“可信域名”,否则跳转时会提示“redirect_uri参数错误”。
    要求配置的可信域名,必须与访问链接的域名完全一致。举个例子:
    在这里插入图片描述

    如何调用企业微信API?

    1.引入js文件

    方法一:在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
    方法二:支持使用 AMD/CMD 标准模块加载方法加载(以vue为例)

    $ npm install weixin-js-sdk --save
    或
    $ yarn add weixin-js-sdk --save
    
    2.通过config接口注入权限验证配置

    所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用)。

    wx.config({
        beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '', // 必填,企业微信的corpID
        timestamp: , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,见附录1
        jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
    

    而signature的如何生成可参考官网API,出于安全考虑,开发者必须在服务器端实现签名的逻辑(即后端给出接口)。
    任意门

    3.通过ready接口处理成功验证
    wx.ready(function(){
        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
    });
    
    4.通过error接口处理失败验证
    wx.error(function(res){
        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
    });
    

    验证成功后,就可以调用微信API的其他接口了。
    任意门

    展开全文
  • 对接微信开发

    2019-11-06 23:22:26
    最近开始整理一下最近做到对接微信支付那点事吧. 前段时间做了微信支付H5的接口.相当坑爹.具体往下看吧.先把官方的网址贴出来.下面有什么变量不明白的地方自行看微信支付官网说明,我就不一一赘述了.当然也可以用我...

    最近很久没有开始写博客了,主要换了一个城市工作,然后来到新的城市很多东西要做,外加工作上的不顺心.最近开始整理一下最近做到对接微信支付那点事吧.

    前段时间做了微信支付H5的接口.相当坑爹.具体往下看吧.先把官方的网址贴出来.下面有什么变量不明白的地方自行看微信支付官网说明,我就不一一赘述了.当然也可以用我对接好的.直接拿来用就可以了.下面就让我开始吧.

    官网API地址:微信支付API文档(H5)地址.

    前言:由于微信这边需要会用到一个MD5加密.但是我对这方面不是很熟悉.这里MD5这方面我参考了该大佬的博客.大家可以去看看.(传送门)

    开始:这里需要引入Apache的相关工具类.

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>

    我根据官方文档做了一个基础的类.将必有的信息写进了一个基类去.后面的下单,退款,查询等的类直接继承该类即可.下面贴出该类代码.

    import org.apache.commons.lang3.RandomUtils;
    import sun.security.provider.MD5;
    
    import java.util.*;
    
    /**
     * @Author: hr222
     * @Date: 2019/5/27 10:07
     * @Description: 对微信支付的进行一个拆分,将每个接口都需要用到的字段信息分进这个类种
     */
    public class BaseWechatAO {
    
        /**
         * 因为生成签名的时候需要对参数按ASCII表顺序从大到小(字母序)排序对比,因此需要构建一个集合。
         */
        protected Map<String, String> map = new HashMap<String, String>();
    
        /**
         * 公众帐号Id
         */
        protected String appid;
    
        /**
         * 商户号Id
         */
        protected String mch_id;
    
        /**
         * 随机字符串
         */
        protected String nonce_str;
    
        /**
         * 签名
         */
        protected String sign;
    
        /**
         * 商户订单号
         */
        protected String out_trade_no;
    
        public String getAppid() {
            return appid;
        }
    
        /**
         * 微信分配的appid。企业号corpid为此appid
         *
         * @param appid
         */
        public void setAppid(String appid) {
            this.appid = appid;
            map.put("appid", appid);
        }
    
        public String getMch_id() {
            return mch_id;
        }
    
        /**
         * 微信支付分配的商户号
         *
         * @param mch_id
         */
        public void setMch_id(String mch_id) {
            this.mch_id = mch_id;
            map.put("mch_id", mch_id);
        }
    
        public String getNonce_str() {
            return nonce_str;
        }
    
        /**
         * 设置随机字符串,具体不能长于32位,参数设置请参考<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_3">官方文档</a>
         *
         * @param nonce_str
         */
        public void setNonce_str(String nonce_str) {
            this.nonce_str = nonce_str;
            map.put("nonce_str", nonce_str);
        }
    
        public String getSign() {
            return sign;
        }
    
        /**
         * 设置微信支付接口签名,由于签名需要按照ASCII码从小到大(字典序)进行。类中已经写好了。使用getSortSignByMD5()方法获取签名并赋值即可
         *
         * @param sign
         */
        public void setSign(String sign) {
            this.sign = sign;
            map = null;
        }
    
        public String getOut_trade_no() {
            return out_trade_no;
        }
    
        /**
         * 说明见<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2">官方文档</a>
         * 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
         *
         * @param out_trade_no
         */
        public void setOut_trade_no(String out_trade_no) {
            this.out_trade_no = out_trade_no;
            map.put("out_trade_no", out_trade_no);
        }
    
        /**
         * 依据微信支付的随机字符串
         *
         * @return
         */
        public String getTheOnceStr() {
            char[] elements = {
                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
            String theNonceStr = "";
    
            for (int i = 0; i < 32; i++) {
                //发现这个比Math.random()*62好用
                int index = RandomUtils.nextInt(0, 62);
                theNonceStr += elements[index];
            }
            return theNonceStr;
        }
    
        /**
         * 返回参数经过ASCII码排序后的签名
         * 类中有个map生成完签名后设置为空,因此请在最后才开始设置签名,因为这个会和后面的转换成为XML形式的String有冲突所以只能设置为空。
         *
         * @param apiKey key为商户平台设置的密钥key
         * @return
         */
        public String getSortSignByMD5(String apiKey) throws Exception {
            String sign = "";
    
            if (map.size() == 0) {
                throw new Exception("请先设置需要传输的参数在进行排序");
            }
    
            try {
                //获取类中的Map属性信息,并将其转换为list对象
                List<Map.Entry<String, String>> sortList = new ArrayList<>(map.entrySet());
                // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
                Collections.sort(sortList, new Comparator<Map.Entry<String, String>>() {
                    //这里需要说明一下ASCII排序,String中所实现的Comparable接口中的CompareTo方法对比正是采取他们两个字之间的ASCII码(字典序)
                    //对比则是比较两者中的字符的大小若是不同返回两者之间的差值若是一样则继续对比第二个,这里我们的做法也是一样的
                    @Override
                    public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                        return (o1.getKey()).compareTo(o2.getKey());
                    }
                });
    
                StringBuffer param = new StringBuffer();
                for (Map.Entry<String, String> entry : sortList) {
                    if (entry.getKey() != null || entry.getKey() != "") {
                        String k = entry.getKey();
                        String v = entry.getValue();
                        if (!(v == "" || v == null)) {
                            param.append(k + "=" + v + "&");
                        }
                    }
                }
                sign = param.toString() + "key=" + apiKey;
                map = null;
                return MD5Util.md5Encode(sign).toUpperCase();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return sign;
        }
    
    }
    

    这个类包含了获取签名这个最麻烦的地方.不过正好JAVA底层对比的默认采取的是ASCII码对比所以还算是比较简单的.

    这里由于支付的接口众多我这里就举例一个下单接口.剩余的可以看我上传的文件里面有很详细的讲解.下面我先贴出我的类对象,这里需要注意为了方便后面的发送和转换成为XML发送给微信那边我没有采用驼峰命名的方式.

    /**
     * @Author: hr222
     * @Date: 2019/5/23 14:18
     * @Description: 微信统一下单的参数对象, 具体可以参照<a href=" https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1">官网文档</a>,
     * 这里仅列出当前项目所用到的字段和必须字段,若后面有需要可以到该类依照文档内容进行添加。这里需要满足微信支付接口的定义所以参数不是驼峰命名。
     */
    public class NewOrderAO extends BaseWechatAO {
    
        /**
         * 商品描述
         */
        private String body;
    
        /**
         * 支付总金额
         */
        private int total_fee;
    
        /**
         * 终端Ip
         */
        private String spbill_create_ip;
    
        /**
         * 附加通知,用于给回调地址信息
         */
        private String attach;
    
        /**
         * 通知地址
         */
        private String notify_url;
    
        /**
         * 通知类型
         */
        private String trade_type;
    
        /**
         * 场景信息
         */
        private String scene_info;
    
        public String getAttach() {
            return attach;
        }
    
        public void setAttach(String attach) {
            this.attach = attach;
            map.put("attach", attach);
        }
    
        public String getBody() {
            return body;
        }
    
        /**
         * 设置商品的简单描述,该字段须严格按照规范传递。具体参考<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2">官方文档</a>
         *
         * @param body
         */
        public void setBody(String body) {
            this.body = body;
            map.put("body", body);
        }
    
        public int getTotal_fee() {
            return total_fee;
        }
    
        /**
         * 设置支付总金额,单位为分
         *
         * @param total_fee
         */
        public void setTotal_fee(int total_fee) {
            this.total_fee = total_fee;
            map.put("total_fee", total_fee + "");
        }
    
        public String getSpbill_create_ip() {
            return spbill_create_ip;
        }
    
        /**
         * 下单用户所在的终端的Ip地址,必须传正确的用户端IP,支持ipv4、ipv6格式。获取方式可以参考<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_5">官方文档</a>
         *
         * @param spbill_create_ip
         */
        public void setSpbill_create_ip(String spbill_create_ip) {
            this.spbill_create_ip = spbill_create_ip;
            map.put("spbill_create_ip", spbill_create_ip);
        }
    
        public String getNotify_url() {
            return notify_url;
        }
    
        /**
         * 通知地址,接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
         *
         * @param notify_url
         */
        public void setNotify_url(String notify_url) {
            this.notify_url = notify_url;
            map.put("notify_url", notify_url);
        }
    
        public String getTrade_type() {
            return trade_type;
        }
    
        /**
         * 交易类型,web端使用的是MWEB,其余具体参数请参考<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2">官方文档</a>
         *
         * @param trade_type
         */
        public void setTrade_type(String trade_type) {
            this.trade_type = trade_type;
            map.put("trade_type", trade_type);
        }
    
        public String getScene_info() {
            return scene_info;
        }
    
        /**
         * 该字段用于上报支付的场景信息,针对H5支付有以下三种场景,请根据对应场景上报,H5支付不建议在APP端使用,针对场景1,2请接入APP支付,不然可能会出现兼容性问题。
         * 该字符串必须是JSON数据类型的。例如:
         * {"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}
         * @param scene_info
         */
        public void setScene_info(String scene_info) {
            this.scene_info = scene_info;
            map.put("scene_info", scene_info);
        }
    
    }

    这里我们已经完成了数据的组装.下面我需要appid,商户号,微信证书等等.为了方便后期更换商户号等我写如了properties文件中.下面贴出来

    #############微信配置,相关信息可在官网中拿到###########
    
    #公众帐号Id
    wechat.appid=微信账号id
    #商户号
    wechat.mchId=商户号
    #apiKey
    wechat.apiKey=apikey
    #同意下单地址
    wechat.newOrderUrl=https://api.mch.weixin.qq.com/pay/unifiedorder
    #查询订单地址
    wechat.queryOrderUrl=https://api.mch.weixin.qq.com/pay/orderquery
    #关闭订单地址
    wechat.closeOrderUrl=https://api.mch.weixin.qq.com/pay/closeorder
    #退卡订单地址
    wechat.refundOrderUrl=https://api.mch.weixin.qq.com/secapi/pay/refund
    #查询退款订单地址
    wechat.queryRefundOrderUrl=https://api.mch.weixin.qq.com/pay/refundquery
    #微信新建订单的回调地址
    wechat.notifyURLByNewOrder=你的回调地址必须是不包含参数的回调(除了request这类的对象)
    #微信关闭订单的回调地址
    wechat.notifyURLByRefundOrder=你的回调地址必须是不包含参数的回调(除了request这类的对象)
    #微信证书
    wechat.certPath=微信支付证书地址

    下面完事具备,就差发送信息的httpclientUtil,和转换成为xml信息的工具类了.下面贴出我写好的工具类,这边需要引入的依赖有

    <!-- HTTP工具 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.8</version>
    </dependency>
    
    <!-- 微信支付需要发送到XML类型所使用的依赖 -->
    <dependency>
         <groupId>xpp3</groupId>
         <artifactId>xpp3_min</artifactId>
         <version>1.1.4c</version>
    </dependency>
    
    <!-- 微信支付发送XML类型进行转换 -->
    <dependency>
         <groupId>com.thoughtworks.xstream</groupId>
         <artifactId>xstream</artifactId>
         <version>1.4.9</version>
    </dependency>
    
    <!-- 用于将数据转换为Map -->
    <dependency>
         <groupId>org.jdom</groupId>
         <artifactId>jdom</artifactId>
         <version>1.1</version>
    </dependency>

    下面是我自己写的工具类,由于我这边做好了对象的转换所以我只打印了返回信息,有需要可以对该类进行改造.由于微信下单需要获取下单客户端的真实ip.这方面一开始用的request的后面发现会出问题,但是这种情况我第一次见到所以在获取IP方面我是搬运了一个大佬的博客.地址:大佬博客

    下面是我的代码:

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.thoughtworks.xstream.XStream;
    import org.apache.http.HttpEntity;
    import org.apache.http.NameValuePair;
    import org.apache.http.ParseException;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.ssl.SSLContexts;
    import org.apache.http.util.CharArrayBuffer;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.JDOMException;
    import org.jdom.input.SAXBuilder;
    
    import javax.net.ssl.SSLContext;
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    import java.net.URLEncoder;
    import java.security.*;
    import java.security.cert.CertificateException;
    import java.util.*;
    
    
    /**
     * @Author: hr222
     * @Date: 2019/5/23 16:19
     * @Description: 用于发送Http,Https请求,转换返回的信息,获取源IP的地址的工具类.已经转换成为相关的对象函数.具体可以参考Apache的http工具的API
     */
    public class HttpClientUtil {
    
        private static Logger log = LoggerFactory.getLogger(HttpClientUtil.class);
    
        /**
         * 采取单例模式
         */
        private final static XStream XSTREAM = new XStream();
    
        /**
         * 定义默认编码
         */
        private final static String CHARSET = "utf-8";
    
        /**
         * 用于获取IP地址中的未知值
         */
        private final static String UNKNOWN = "unknown";
    
        /**
         * socket读写超时时间,单位毫秒
         */
        private final static Integer SOCKET_TIME_OUT = 5000;
    
        /**
         * 设置接超时时间,单位毫秒
         */
        private final static Integer CONNECT_TIME_OUT = 5000;
    
        /**
         * 设置请求超时时间,单位毫秒
         */
        private final static Integer REQUEST_CONNECT_TIME = 5000;
    
        /**
         * 发送GET请求到目的URL,若有消息返回则打印消息并返回数据。没有返回null
         *
         * @param url     目标URL
         * @param charset 编码,默认为UTF-8.
         * @param data    需要发送到数据,若为空对象者则不发
         */
        public static String sendGet(String url, String charset, Map<String, String> data) {
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            if (data != null || data.size() != 0) {
                StringBuffer pram = new StringBuffer();
                //获取数据将MAP中的数据转换为String类型
                Set<String> key = data.keySet();
                try {
                    for (String k : key) {
                        pram.append(k + "=" + URLEncoder.encode(data.get(k), charset == null ? CHARSET : charset));
                        pram.append("&");
                    }
                    pram.deleteCharAt(pram.length() - 1);
                    url += "?" + pram.toString();
                } catch (UnsupportedEncodingException e1) {
                    log.error(e1.getMessage());
                }
    
                //建立连接
                HttpGet httpGet = new HttpGet(url);
                CloseableHttpResponse response = null;
                try {
                    // 配置信息
                    RequestConfig requestConfig = RequestConfig.custom()
                            // 设置连接超时时间(单位毫秒)
                            .setConnectTimeout(CONNECT_TIME_OUT)
                            // 设置请求超时时间(单位毫秒)
                            .setConnectionRequestTimeout(REQUEST_CONNECT_TIME)
                            // socket读写超时时间(单位毫秒)
                            .setSocketTimeout(SOCKET_TIME_OUT)
                            // 设置是否允许重定向(默认为true)
                            .setRedirectsEnabled(true).build();
    
                    // 将上面的配置信息 运用到这个Get请求里
                    httpGet.setConfig(requestConfig);
    
                    // 由客户端执行(发送)Get请求
                    response = httpClient.execute(httpGet);
    
                    // 从响应模型中获取响应实体
                    HttpEntity responseEntity = response.getEntity();
                    log.info("响应状态为:" + response.getStatusLine());
                    if (responseEntity != null) {
                        return printTheMsg(responseEntity, charset);
                    }
                } catch (ClientProtocolException e) {
                    log.error(e.getMessage());
                } catch (ParseException e) {
                    log.error(e.getMessage());
                } catch (IOException e) {
                    log.error(e.getMessage());
                } finally {
                    close(httpClient, response);
                }
            }
            return null;
        }
    
        /**
         * 发送POST请求到目的URL,假如有返回数据,则将数据打印并返回数据,没有则返回null
         *
         * @param url     发送目标地址
         * @param type    发送到数据类型,如text/xml,application/json等
         * @param charset 编码,默认UTF-8.
         * @param data    需要发送到数据,若没有则发送空数据请求
         */
        public static String sendPost(String url, String type, String charset, String data) throws Exception {
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            if (type == null || type.equals("")) {
                throw new Exception("请检查参数");
            }
    
            //建立链接
            HttpPost httpPost = new HttpPost(url);
            if (data != null) {
                //设置发送参数类型
                String contentType = charset == null || charset.equals("") ? type : type + ";charset=" + charset;
                //设置发送数据的信息和编码
                StringEntity entity = charset == null || charset.equals("") ? new StringEntity(data, CHARSET) : new StringEntity(data, charset);
                httpPost.setEntity(entity);
                httpPost.setHeader("Content-Type", contentType);
            }
            // 响应模型
            CloseableHttpResponse response = null;
            try {
                // 由客户端执行(发送)Post请求
                response = httpClient.execute(httpPost);
                // 从响应模型中获取响应实体
                HttpEntity responseEntity = response.getEntity();
    
                log.info("响应状态为:" + response.getStatusLine());
                if (responseEntity != null) {
                    return printTheMsg(responseEntity, charset);
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                close(httpClient, response);
            }
            return null;
        }
    
        /**
         * 发送POST请求到目的URL,假如有返回数据,则将数据打印并返回数据,没有则返回null
         * 注:这个是发送带证书的那种SSL,微信后面有用到.   
         *
         * @param url         发送目标地址
         * @param type        发送到数据类型,如text/xml,application/json等
         * @param charset     编码,默认UTF-8.
         * @param data        需要发送到数据,若没有则发送空数据请求
         * @param certPath    SSL证书的路径
         * @param certPassord SSL证书密码
         */
        public static String sendPost(String url, String type, String charset, String data, String certPath, String certPassord) throws Exception {
            if (type == null || type.equals("")) {
                throw new Exception("请检查参数");
            }
            //获取keystore实例
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
    
            //加载本地的证书进行https加密传输
            FileInputStream instream = new FileInputStream(new File(certPath));
            try {
                //加载证书密码,使用的是商户ID
                keyStore.load(instream, certPassord.toCharArray());
            } catch (CertificateException e) {
                log.error(e.getMessage());
            } catch (NoSuchAlgorithmException e) {
                log.error(e.getMessage());
            } finally {
                instream.close();
            }
    
            //配置SSL链接
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, certPassord.toCharArray()).build();
            // 只允许TLSv1
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
            CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
    
            //根据默认超时限制初始化requestConfig
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIME_OUT).setConnectTimeout(CONNECT_TIME_OUT).build();
            //建立链接
            HttpPost httpPost = new HttpPost(url);
            if (data != null) {
                //设置发送参数类型
                String contentType = charset == null || charset.equals("") ? type : type + ";charset=" + charset;
                //设置发送信息
                StringEntity entity = charset == null || charset.equals("") ? new StringEntity(data, CHARSET) : new StringEntity(data, charset);
                httpPost.setEntity(entity);
                //设置请求器的配置
                httpPost.setConfig(requestConfig);
                httpPost.setHeader("Content-Type", contentType);
            }
            // 响应模型
            CloseableHttpResponse response = null;
            try {
                // 由客户端执行(发送)Post请求
                response = httpClient.execute(httpPost);
                // 从响应模型中获取响应实体
                HttpEntity responseEntity = response.getEntity();
    
                log.info("响应状态为:" + response.getStatusLine());
                if (responseEntity != null) {
                    return printTheMsg(responseEntity, charset);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            } finally {
                close(httpClient, response);
            }
            return null;
        }
    
        /**
         * 发送以表单对象的POST请求到目的URL,假如有返回数据,则将数据打印并返回数据,没有则返回null
         *
         * @param url     发送目标地址
         * @param type    发送到数据类型,如text/xml,application/json等
         * @param charset 编码,默认UTF-8.
         * @param data    需要发送到数据,若没有则发送空数据请求
         */
        public static String sendPost(String url, String type, String charset, Map<String, String> data) throws Exception {
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            if (type == null || type.equals("")) {
                throw new Exception("请检查参数");
            }
    
            //建立链接
            HttpPost httpPost = new HttpPost(url);
            if (data != null) {
                //设置发送参数类型
                String contentType = charset == null || charset.equals("") ? type : type + ";charset=" + charset;
                //设置发送体的编码
                String chartSet = charset == null || charset.equals("") ? "UTF-8" : charset;
                //表单数据.这个是因为和同事对调接口的时候发现怎么弄他那边都接收不到数据map对象数据是空
                //后面查阅后知道需要这样, 类型是application/x-www-form-urlencoded
                List<NameValuePair> list = new ArrayList<>();
                for (Map.Entry<String, String> entry : data.entrySet()) {
                    list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, chartSet);
                httpPost.setEntity(urlEncodedFormEntity);
                httpPost.setHeader("Content-Type", contentType);
            }
            // 响应模型
            CloseableHttpResponse response = null;
            try {
                // 由客户端执行(发送)Post请求
                response = httpClient.execute(httpPost);
                // 从响应模型中获取响应实体
                HttpEntity responseEntity = response.getEntity();
    
                log.info("响应状态为:" + response.getStatusLine());
                if (responseEntity != null) {
                    return printTheMsg(responseEntity, charset);
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                close(httpClient, response);
            }
            return null;
        }
    
        /**
         * 打印请求发送了请求后的返回信息,并返回消息体
         *
         * @param responseEntity 请求返回体
         * @param charset        编码形式
         * @return
         * @throws IOException
         */
        private static String printTheMsg(HttpEntity responseEntity, String charset) throws IOException {
            log.info("响应内容长度为:" + responseEntity.getContentLength());
            Reader reader = new InputStreamReader(responseEntity.getContent(), charset == null || charset.equals("") ? CHARSET : charset);
            CharArrayBuffer buffer = new CharArrayBuffer((int) responseEntity.getContentLength());
            final char[] tmp = new char[1024];
            int line;
            while ((line = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, line);
            }
            log.info("响应内容为:" + buffer.toString());
            return buffer.toString();
        }
    
        /**
         * 释放资源
         *
         * @param response 返回的响应对象
         */
        private static void close(CloseableHttpClient httpClient, CloseableHttpResponse response) {
            try {
                httpClient.close();
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    
        /**
         * 将收到的对象类型转换成Json字符串
         *
         * @param tClass
         * @param <T>
         * @return
         */
        public static <T> String parseToJsonString(T tClass) {
            if (tClass instanceof org.json.JSONObject) {
                return tClass.toString();
            }
            return JSON.toJSONString(tClass);
        }
    
        /**
         * 将收到的Json字符串换成对象类型转
         *
         * @param tClass
         * @param <T>
         * @return
         */
        public static <T> T parseToObjectByJSON(Class<T> tClass, String jsonString) {
            JSON json = JSONObject.parseObject(jsonString);
            return JSON.toJavaObject(json, tClass);
        }
    
        /**
         * 将获取到的对象类,转换成XML形式字符串
         *
         * @param tClass
         * @param name   顶级标签的名称
         * @param <T>
         * @return
         */
        public static <T> String parseToXMLString(T tClass, String name) {
            XSTREAM.alias("xml", tClass.getClass());
            String xml = XSTREAM.toXML(tClass);
            return xml;
        }
    
        /**
         * 将XML字符串转换成为传入的对象
         *
         * @param clazz
         * @param xml
         * @param <T>
         * @return 若没有报错则返回转换后的对象,若报错返回null
         */
        public static <T> T parseToObjectByXML(Class<T> clazz, String xml) {
            //由于接受到的数据是以XML为父标签的,由于转换不知道要转换成为什么类,因此需要将父标签转换成为类的对应路径(绝对路径)
            xml = xml.replaceAll("xml", clazz.getCanonicalName());
            //由于接收到的数据可能带有CDATA标签这里把这些都去掉
            //xml = xml.replaceAll("<!\\u005BCDATA\\u005B","").replaceAll("]]>","");
            T object = (T) XSTREAM.fromXML(xml);
            return object;
        }
    
        /**
         * 通过HttpServetRequest中的信息流转换数据
         * 注:微信返回来需要通过request中的获取io对象读取里面的信息,转换为XML形式的字符串
         *   
         * @param inputStream
         * @return
         */
        public static StringBuffer parseDataByNetInputStream(InputStream inputStream) throws IOException {
            StringBuffer stringBuffer = new StringBuffer();
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String data;
            while ((data = in.readLine()) != null) {
                stringBuffer.append(data);
            }
            in.close();
            inputStream.close();
            return stringBuffer;
        }
    
        /**
         * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
         * 注:转换XML信息变换成MAP属性.当然也可以用class.不过涉及反射.我一开始做了后面发现不是很好用,故放弃
         *   
         * @param xmlString xml格式的字符串
         * @return
         * @throws JDOMException
         * @throws IOException
         */
        @SuppressWarnings({"rawtypes", "unchecked"})
        public static Map parseMapByXMLString(String xmlString) throws Exception {
            xmlString = xmlString.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
            if (null == xmlString || "".equals(xmlString)) {
                return null;
            }
            Map<String, String> map = new HashMap<>(16);
            InputStream in = new ByteArrayInputStream(xmlString.getBytes("UTF-8"));
            SAXBuilder builder = new SAXBuilder();
            Document doc = builder.build(in);
            Element root = doc.getRootElement();
            List list = root.getChildren();
            Iterator it = list.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String k = e.getName();
                String v;
                List children = e.getChildren();
                if (children.isEmpty()) {
                    v = e.getTextNormalize();
                } else {
                    v = getChildrenText(children);
                }
                map.put(k, v);
            }
            // 关闭流
            in.close();
            return map;
        }
    
        /**
         * 获取子结点的xml
         *
         * @param children
         * @return String
         */
        @SuppressWarnings({"rawtypes"})
        public static String getChildrenText(List children) {
            StringBuffer sb = new StringBuffer();
            if (!children.isEmpty()) {
                Iterator it = children.iterator();
                while (it.hasNext()) {
                    Element e = (Element) it.next();
                    String name = e.getName();
                    String value = e.getTextNormalize();
                    List list = e.getChildren();
                    sb.append("<" + name + ">");
                    if (!list.isEmpty()) {
                        sb.append(getChildrenText(list));
                    }
                    sb.append(value);
                    sb.append("</" + name + ">");
                }
            }
    
            return sb.toString();
        }
    
        /**
         * 获取请求源的Ip地址,获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址,如果没有代理,则获取真实ip
         * 注:该方法搬运自<a href="https://www.cnblogs.com/chinaifae/p/10189012.html">该博客</a>
         *
         * @param request
         * @return
         */
        public static String getSourceIpAddress(HttpServletRequest request) {
            //代理进来,则透过防火墙获取真实IP地址
            String ip = request.getHeader("X-Forwarded-For");
    
            //以下是判断用户是否开启了代理模式
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
    
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
    
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
    
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
    
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Real-IP");
            }
    
            //如果没有代理,则获取真实ip
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
        }
    
    }

    这边具体做好后实际上我们就能通过Main方法跑一趟了.这边我先弄个小样子吧.按照惯例我先将微信统一下单的之后返回的对象贴出来方便参考.同样的我将基本返回对象抽出来单独做了一个基础bean.下面就贴上来吧

    /**
     * @Author: hr222
     * @Date: 2019/5/27 10:29
     * @Description: 微信支付查询类接口的返回基础字段信息,在接口返回中的字段
     * return_code为SUCCESS的时候有返回。
     */
    public class BaseReturnWechatAO {
    
        /**
         * 返回状态码
         */
        protected String return_code;
    
        /**
         * 返回信息
         */
        protected String return_msg;
    
        /**
         * 公众账号ID
         */
        protected String appid;
    
        /**
         * 商户号
         */
        protected String mch_id;
    
        /**
         * 随机字符串
         */
        protected String nonce_str;
    
        /**
         * 签名
         */
        protected String sign;
    
        /**
         * 业务结果
         */
        protected String result_code;
    
        /**
         * 业务结果
         */
        protected String result_msg;
    
        /**
         * 错误代码
         */
        protected String err_code;
    
        /**
         * 错误代码描述
         */
        protected String err_code_des;
    
        /**
         * 坑爹的隐藏字段
         */ 
        private String sub_mch_id;
    
        /**
         * 当return_code为FAIL时返回信息为错误原因 ,例如
         * <p>
         * 签名失败
         * <p>
         * 参数格式校验错误
         *
         * @return
         */
        public String getAppid() {
            return appid;
        }
    
        public void setAppid(String appid) {
            this.appid = appid;
        }
    
        /**
         * 调用接口提交的公众账号ID
         *
         * @return
         */
        public String getMch_id() {
            return mch_id;
        }
    
        public void setMch_id(String mch_id) {
            this.mch_id = mch_id;
        }
    
        /**
         * 调用接口提交的终端设备号
         *
         * @return
         */
        public String getNonce_str() {
            return nonce_str;
        }
    
        public void setNonce_str(String nonce_str) {
            this.nonce_str = nonce_str;
        }
    
        /**
         * 微信返回的签名
         *
         * @return
         */
        public String getSign() {
            return sign;
        }
    
        public void setSign(String sign) {
            this.sign = sign;
        }
    
        /**
         * SUCCESS/FAIL
         *
         * @return
         */
        public String getResult_code() {
            return result_code;
        }
    
        public void setResult_code(String result_code) {
            this.result_code = result_code;
        }
    
        /**
         * 详细参见
         * <a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1">错误列表</a>
         *
         * @return
         */
        public String getErr_code() {
            return err_code;
        }
    
        public void setErr_code(String err_code) {
            this.err_code = err_code;
        }
    
        /**
         * 错误返回的
         * <a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1">信息描述</a>
         *
         * @return
         */
        public String getErr_code_des() {
            return err_code_des;
        }
    
        public void setErr_code_des(String err_code_des) {
            this.err_code_des = err_code_des;
        }
    
        public String getReturn_code() {
            return return_code;
        }
    
        public void setReturn_code(String return_code) {
            this.return_code = return_code;
        }
    
        /**
         * SUCCESS/FAIL
         * <p>
         * 此字段是通信标识,非交易标识,交易是否成功需要查看trade_state来判断
         *
         * @return
         */
        public String getReturn_msg() {
            return return_msg;
        }
    
        public void setReturn_msg(String return_msg) {
            this.return_msg = return_msg;
        }
    
        /**
         * 对业务结果的补充说明
         * @return
         */
        public String getResult_msg() {
            return result_msg;
        }
    
        public void setResult_msg(String result_msg) {
            this.result_msg = result_msg;
        }
    }
    

    这个是下单的返回字段信息:

    /**
     * @Author: hr222
     * @Date: 2019/5/25 16:54
     * @Description: 统一下单后返回的信息对象,详情参考<a href="https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1">官方文档</a>,
     * 由于需要满足微信支付接口参数的定义因此这里不能按照驼峰命名的规则,这里只添加必填类型,其余选填类型可以自行添加
     */
    public class ReturnNewOrderAO extends BaseReturnWechatAO {
    
        /**
         * 设备号
         */
        private String device_info;
    
        /**
         * 交易类型
         */
        private String trade_type;
    
        /**
         * 预支付交易会话标识
         */
        private String prepay_id;
    
        /**
         * 支付跳转链接
         */
        private String mweb_url;
    
        /**
         * 订单号
         */
        private String orderNum;
        ///////////////////////////以下字段在return_code为SUCCESS的时候有返回/////////////////////////////
    
        /**
         * 调用接口提交的商户号
         *
         * @return
         */
        public String getDevice_info() {
            return device_info;
        }
    
        public void setDevice_info(String device_info) {
            this.device_info = device_info;
        }
    
    
        ///////////////////////////////以下字段在return_code 和result_code都为SUCCESS的时候有返回///////////////////////
    
        /**
         * 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,,H5支付固定传MWEB
         *
         * @return
         */
        public String getTrade_type() {
            return trade_type;
        }
    
        public void setTrade_type(String trade_type) {
            this.trade_type = trade_type;
        }
    
        /**
         * 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时,针对H5支付此参数无特殊用途
         *
         * @return
         */
        public String getPrepay_id() {
            return prepay_id;
        }
    
        public void setPrepay_id(String prepay_id) {
            this.prepay_id = prepay_id;
        }
    
        /**
         * mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
         *
         * @return
         */
        public String getMweb_url() {
            return mweb_url;
        }
    
        public void setMweb_url(String mweb_url) {
            this.mweb_url = mweb_url;
        }
    
        public String getOrderNum() {
            return orderNum;
        }
    
        public void setOrderNum(String orderNum) {
            this.orderNum = orderNum;
        }
    }
    

    好了现在就可以开始了.这里我弄成main方法.没有上接口啥的.有什么需要可以进行改造.

     public static void main(String[] args) {
            //这里微信API内规定发送请求必须以XML形式发送,这个请求类型这里设置成为XML,若后期修改了的话可以将这个提取出来进行优化
            String contentType = "text/xml";
    
            //包装设备网站信息
            JSONObject json = new JSONObject();
            json.put("type", "Wap");
            json.put("wap_url", "https://pay.qq.com");
            json.put("wap_name", "微信支付");
            JSONObject scenceInfo = new JSONObject();
            scenceInfo.put("h5_info", json);
    
            //注:获取订单金额,若是金额是元,带小数点的那种,这里需要转换成为分单位,所以要乘100
            Integer money = 100;
    
            //以下是针对信息订单的相关信息设置,之后在统一转换成为XML形式,因为这里转换的时候如有下横杠会变成两个下横岗,垃圾的微信接口不认。
            // 所以这里用只能替代成为一个
            //注:参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析。
            String newOrderAO = initOrder("127.0.0.1", "1234567", money, "你的回调地址,没有不填", scenceInfo);
            newOrderAO = newOrderAO.replaceAll("__", "_");
            String returnNewOrderAO = HttpClientUtil.sendPost(newOrderUrl, contentType, null, newOrderAO);
            ReturnNewOrderAO returnAO = HttpClientUtil.parseToObjectByXML(ReturnNewOrderAO.class, returnNewOrderAO);     
            system.out.print(returnAO);
     }
    
    
        private String initOrder(String ip, String orderNum, int totalMoney, String notifyUrl, JSONObject scenceInfo) throws Exception {
            NewOrderAO newOrderAO = new NewOrderAO();
            newOrderAO.setSpbill_create_ip(ip);
            newOrderAO.setMch_id("你的商户号");
            newOrderAO.setAppid("你的公众账号id");
            newOrderAO.setNonce_str(newOrderAO.getTheOnceStr());
            newOrderAO.setAttach(orderNum);
            newOrderAO.setBody("微信支付-订单支付");
            newOrderAO.setOut_trade_no(orderNum);
            newOrderAO.setTotal_fee(totalMoney);
            newOrderAO.setNotify_url(notifyUrl);
            newOrderAO.setTrade_type("MWEB");
            newOrderAO.setScene_info(scenceInfo.toString());
            newOrderAO.setSign(newOrderAO.getSortSignByMD5("你的apikey"));
            return HttpClientUtil.parseToXMLString(newOrderAO, "xml");
        }

    这里就大共告成了下面我整理我弄好的.打包好上来.

    展开全文
  • 一、参考文档:微信开发文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 关于网页授权的两种scope的区别说明 1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户...
  • 模式一和模式二提供了两种不同的能力,适用于不同的场景,看商户具体的需求。1、两种模式,在支付的流程中,有一定的共同的流程:  1)生成订单。  2)用户支付。2、差别在于:  模式一,先扫码,再生成订单。  ...
  • 首先,开发微信公众号的相关功能一般是公司新申请的服务号,如果刚开始产品同学的需求比较简单,没有复杂的交互,可以先尝试在微信公众平台的功能编辑区完成功能,如果简单的情况下一般产品和运营同学就可以直接完成...
  • 今天的重点是如何将微信开发模式和SAE对接,希望朋友们已经完成了SAE注册,如果还没搞定的话请抓紧,有问题的话请及时提出,接下来的教程都在那上面进行。 在教程开始前建议朋友们可以先注册一个新的公众账号,...
  • 1.Action 层代码 package com.aiait.wechat.action; import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet....
  • 1、打开微信开发者文档,找到开始开发,接入指南,页面往下翻找到PHP示例代码下载,解压后文件为wx_sample.php: 2、复制wx_sample.php到开发目录下面,... ... define(“TOKEN”, “weixin”); //实例化微信对象
  • 手把手教你springboot企业微信开发(二)1、企业微信开发第一步2、weixin-java-cp-demo-master1)、引入thymeleaf改造项目2)增加Configuration3)回到企业号4)穿透内外网5)完善 从这一篇开始,开始从实际项目探索...
  • 本例子实现微信扫码支付模式二的支付功能,应用场景是,web网站微信扫码支付。实现从点击付费按钮、到弹出二维码、到用户用手机微信扫码支付、到手机上用户付费成功、web网页再自动调整到支付成功后的页面,这一个...
  • 让开发者快速学习微信支付功能,本课程基于Java开发工作,通过SpringBoot实现微信支 适用人群 针对互联网项目开发者,实现微信支付功能开发 课程简介 从零讲起微信支付,基于Spring Boot框架...
  • 快速对接payjs的个人支付接口(收银台模式) 近期在了解个人支付接口,希望能解决我在微信上支付的问题。...收银台模式对接其实非常简单,官方有开发包可以直接使用,或者自己开发也比较简单。 一、个人通过代码实现...
  • 公众平台开发模式的数据交互方式 1.2 公众平台开发模式的数据交互方式 公众平台消息接口为开发者提供了与用户进行消息交互的能力。对于成功接入消息接口的公众账号,当用户发消息给公众账号时,微信公众平台...
  • 微信支付SDK开发对接沙箱测试 前置配置: 我们先要去接入微信支付,这个网上有很多的教程。注意几个关键的参数即可: mch_id:商户id,也就是商户号。需要入驻成商户 appid:服务号的appid,需要做关联操作。 证书...
  • 开始没找到微信支付的sdk。自己根据官方给的接口文档纯手写,各种xml转JSON,JSON转xml,加密解密,签名、、、、整个人都是崩溃的 开发的第三天,发现有官方的sdk。心情一下子豁然开朗,整个人都轻松了一截。 话不...
  • 课程目录: 001_动力节点微信支付视频教程_支付流程图详解_公众号支付介绍 ...005_动力节点微信支付视频教程_支付流程图详解_微信买单支付介绍 006_动力节点微信支付视频教程_支付流程图详解_二维码...
1 2 3 4 5 ... 20
收藏数 8,362
精华内容 3,344