JAVA-小程序微信支付

版权声明:本文为KingYiFan原创文章,未经KingYiFan允许不得转载。 https://blog.csdn.net/weixin_39984161/article/details/81189721

- -微信支付之小程序- -

哈喽 我是你们的KingYiFan,一直说把微信支付给分享出来一直没有机会。终于闲下来了。听着音乐给你们分享一下。不懂可以随时联系我。。


-~~ 本文介绍小程序微信支付的java服务端~~。


首先给大家看一下微信支付的流程

废话不多说直接上微信官方流程图 哈哈哈哈哈 是不是看的一脸懵逼。

微信支付-小程序

–KingYiFan 给大家准备了一个自己画的流程图
微信支付流程

   /**
     * @Title: 小程序微信支付
     * @Description: 调用微信的支付接口 统一下单
     * @author: KingYiFan
     */
      @ResponseBody
      @RequestMapping(value = "wxpayTest", method = RequestMethod.GET, produces ="application/json; charset=utf-8")
      public void wxpayTest(HttpServletRequest request, HttpServletResponse response, Model model) {
        try {
        //前台传输一个openid--每个用户对应小程序都会生成一个独一无二的openid
        String openid = request.getParameter("openid");
        //创建一个实体类
        OrderInfo order = new OrderInfo();
        //appid 是小程序的appid(微信公众平台获取)
        order.setAppid(Configure.getAppID());
        //商户id(微信商户平台申请)
        order.setMch_id(Configure.getMch_id());
        //随机字符串,微信推荐长度要求在32位以内。
        order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        //商品描述-可以随便写
        order.setBody("KingYiFan-测试");
        //商户订单号可以根据这个订单查询账单
        order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));
        //支付金额 微信支付是按分为单位的 是int类型 1就是1分 10就是一角
        //这是一个坑
        order.setTotal_fee(1);
        //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
        //微信写的一团懵逼 到底是用户的ip还是我们服务器的ip
        //算了 不管了就直接用我们服务器ip把
        order.setSpbill_create_ip(Configure.getIp());
        //微信支付回调地址 也就是用户支付成功改状态的地方
        order.setNotify_url("https://wxxcx.jiajiao2o.com/v2/wxPayNotify");
        //如果是微信小小程序和公众号这是一个固定值
        order.setTrade_type("JSAPI");
        //openid 我上面介绍过,这里不解释了。。。
        order.setOpenid(openid);
        //封装签名的格式 md5 
        order.setSign_type("MD5");
        // 生成签名 一会我会把生成签名格式甩到下面
        String sign = Signature.getSign(order);
        //封装签名
        order.setSign(sign);
        //用httpclient 发送一个post请求微信官方Api
        String result = HttpRequest.sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", order);
        //返回一个预订单id
        System.out.println(result);
        //来回传输数据是xml格式的,然后返回结果也是xml格式的 解析xml
        XStream xStream = new XStream();
        xStream.alias("xml", OrderReturnInfo.class);
        //获得微信返回的结果
        OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);

        // 进行二次签名
        SignInfo signInfo = new SignInfo();
        //appid 是小程序appid 上面有介绍
        signInfo.setAppId(Configure.getAppID());
        //当前时间戳
        long time = System.currentTimeMillis() / 1000;
        signInfo.setTimeStamp(String.valueOf(time));
        //随机字符串 建议32位
        signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
        //预订单ip 上一次请求返回的id
        signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
        //签名加密方式
        signInfo.setSignType("MD5");
        // 封装参数返回小程序段
        String sign2 = Signature.getSign(signInfo);
        JSONObject json = new JSONObject();
        json.put("timeStamp", signInfo.getTimeStamp());
        json.put("nonceStr", signInfo.getNonceStr());
        json.put("package", signInfo.getRepay_id());
        json.put("signType", signInfo.getSignType());
        json.put("paySign", sign2);
        response.getWriter().append(json.toJSONString());
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
}

上面的代码只是调起来微信支付,下面我再说一下用户支付成功回调

废话不多说直接上代码:

/**
  * @Description: 提交支付后的微信异步返回接口
  * @author: KingYiFan
  */
 @ResponseBody
 @RequestMapping(value = "wxPayNotify", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public void wxPayNotify(HttpServletRequest request, HttpServletResponse response) {

    String resXml = "";
    Map<String, String> backxml = new HashMap<String, String>();

    InputStream inStream;
    try {
        inStream = request.getInputStream();

        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        logger.error("微信支付----付款成功----");
        outSteam.close();
        inStream.close();
        String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
        logger.error("微信支付----result----=" + result);
        Map<String, String> map = XCXUtils.readStringXmlOut(result);
        if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
            logger.error("微信支付----返回成功");
            //必须验证微信官方回调过来的签名加密方式防止恶意请求
            if (Signature.verifyWeixinNotify(map)) {
               //书写自己的业务逻辑
            }
            // ------------------------------
            // 处理业务完毕
            // ------------------------------
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        logger.error("支付回调发布异常:" + e);
        e.printStackTrace();
    }
}

是不是炒鸡简单哈哈哈哈哈 被我雷到了么?哈哈哈哈~~

–下面给大家分享一下我自己封装的实体类


/**
  * 预订单 Bean
  * @author KingYiFan
  */
public class OrderInfo {
private String appid;// 小程序ID
private String mch_id;// 商户号
private String nonce_str;// 随机字符串
private String sign_type;//签名类型
private String sign;// 签名
private String body;// 商品描述
private String out_trade_no;// 商户订单号
private int total_fee;// 标价金额 ,单位为分
private String spbill_create_ip;// 终端IP
private String notify_url;// 通知地址
private String trade_type;// 交易类型
private String openid;//用户标识    

public String getSign_type() {
    return sign_type;
}

public void setSign_type(String sign_type) {
    this.sign_type = sign_type;
}

public String getOpenid() {
    return openid;
}

public void setOpenid(String openid) {
    this.openid = openid;
}

public String getAppid() {
    return appid;
}

public void setAppid(String appid) {
    this.appid = appid;
}

public String getMch_id() {
    return mch_id;
}

public void setMch_id(String mch_id) {
    this.mch_id = mch_id;
}

public String getNonce_str() {
    return nonce_str;
}

public void setNonce_str(String nonce_str) {
    this.nonce_str = nonce_str;
}

public String getSign() {
    return sign;
}

public void setSign(String sign) {
    this.sign = sign;
}

public String getBody() {
    return body;
}

public void setBody(String body) {
    this.body = body;
}

public String getOut_trade_no() {
    return out_trade_no;
}

public void setOut_trade_no(String out_trade_no) {
    this.out_trade_no = out_trade_no;
}

public int getTotal_fee() {
    return total_fee;
}

public void setTotal_fee(int total_fee) {
    this.total_fee = total_fee;
}

public String getSpbill_create_ip() {
    return spbill_create_ip;
}

public void setSpbill_create_ip(String spbill_create_ip) {
    this.spbill_create_ip = spbill_create_ip;
}

public String getNotify_url() {
    return notify_url;
}

public void setNotify_url(String notify_url) {
    this.notify_url = notify_url;
}

public String getTrade_type() {
    return trade_type;
}

public void setTrade_type(String trade_type) {
    this.trade_type = trade_type;
 }
}

下面是封装签名的工具类。。。

/**
 * 签名算
 * @param o
 * 要参与签名的数据对象
 * @return 签名
 * @throws IllegalAccessException
 */
public static String getSign(Object o) throws IllegalAccessException {
    ArrayList<String> list = new ArrayList<String>();
    Class cls = o.getClass();
    Field[] fields = cls.getDeclaredFields();
    for (Field f : fields) {
        f.setAccessible(true);
        if (f.get(o) != null && f.get(o) != "") {
            String name = f.getName();
            XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
            if (anno != null)
                name = anno.value();
            list.add(name + "=" + f.get(o) + "&");
        }
    }
    int size = list.size();
    String[] arrayToSort = list.toArray(new String[size]);
    Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < size; i++) {
        sb.append(arrayToSort[i]);
    }
    String result = sb.toString();
    result += "key=" + Configure.getKey();
    System.out.println("签名数据:" + result);
    result = MD5.MD5Encode(result).toUpperCase();
    return result;
}

签名实体类—-

/**
 * 签名信息
 * @author KingYiFan
 *
 */
public class SignInfo {

private String appId;//小程序ID    
private String timeStamp;//时间戳    
private String nonceStr;//随机串    
@XStreamAlias("package")
private String repay_id;
private String signType;//签名方式

public String getAppId() {
    return appId;
}
public void setAppId(String appId) {
    this.appId = appId;
}
public String getTimeStamp() {
    return timeStamp;
}
public void setTimeStamp(String timeStamp) {
    this.timeStamp = timeStamp;
}
public String getNonceStr() {
    return nonceStr;
}
public void setNonceStr(String nonceStr) {
    this.nonceStr = nonceStr;
}
public String getRepay_id() {
    return repay_id;
}
public void setRepay_id(String repay_id) {
    this.repay_id = repay_id;
}
public String getSignType() {
    return signType;
}
public void setSignType(String signType) {
    this.signType = signType;
}
}

生成随机数的工具类

/**
 * 获取一定长度的随机字符串
 * @param length 指定字符串长度
 * @return 一定长度的字符串
 * @author KingYiFan
 */
public static String getRandomStringByLength(int length) {
    String base = "abcdefghijklmnopqrstuvwxyz0123456789";
    Random random = new Random();
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < length; i++) {
        int number = random.nextInt(base.length());
        sb.append(base.charAt(number));
    }
    return sb.toString();
}
}

httpclient 发送post工具类

//连接超时时间,默认10秒
private static final int socketTimeout = 10000;

//传输超时时间,默认30秒
private static final int connectTimeout = 30000;
/**
 * post请求
 * @throws IOException 
 * @throws ClientProtocolException 
 * @throws NoSuchAlgorithmException 
 * @throws KeyStoreException 
 * @throws KeyManagementException 
 * @throws UnrecoverableKeyException 
 */
public static String sendPost(String url, Object xmlObj) throws Exception {

    HttpPost httpPost = new HttpPost(url);
    //解决XStream对出现双下划线的bug
    XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
    xStreamForRequestPostData.alias("xml", xmlObj.getClass());
    //将要提交给API的数据对象转换成XML格式数据Post给API
    String postDataXML = xStreamForRequestPostData.toXML(xmlObj);

    //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
    StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
    httpPost.addHeader("Content-Type", "text/xml");
    httpPost.setEntity(postEntity);

    //设置请求器的配置
    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
    httpPost.setConfig(requestConfig);
    HttpClient httpClient = HttpClients.createDefault();
    HttpResponse response = httpClient.execute(httpPost);
    HttpEntity entity = response.getEntity();
    String result = EntityUtils.toString(entity, "UTF-8");
    return result;
}

微信支付回调认证是否是微信官方请求工具类

/**
 * 验证回调是否是微信官方
 * @param map
 * @return
 * KingYiFan
 */
public static boolean verifyWeixinNotify(Map<String, String> map) {
    SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
    String sign = (String) map.get("sign");
    for (Object keyValue : map.keySet()) {
        if (!keyValue.toString().equals("sign")) {
            parameterMap.put(keyValue.toString(), map.get(keyValue).toString());
        }
    }
    String createSign = getSign(parameterMap);
    if (createSign.equals(sign)) {
        return true;
    } else {
        return false;
    }
}

xml转换map工具类

/**
 * @description 将xml字符串转换成map
 * @param xml
 * @return Map
 */
public static Map<String, String> readStringXmlOut(String xml) {
    Map<String, String> map = new HashMap<String, String>();
    Document doc = null;
    try {
        doc = DocumentHelper.parseText(xml); // 将字符串转为XML
        Element rootElt = doc.getRootElement(); // 获取根节点
        List<Element> list = rootElt.elements();// 获取根节点下所有节点
        for (Element element : list) { // 遍历节点
            map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
        }
    } catch (DocumentException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return map;
}

上面就是一套完整的微信支付服务器端流程,有什么不懂就联系我。


鼓励作者写出更好的技术文档,就请我喝一瓶哇哈哈哈哈哈哈哈。。
微信:
微信支付
支付宝:
支付宝
感谢一路支持我的人。。。。。
Love me and hold me
QQ:69673804(16年老号)
EMAIL:[email protected]
友链交换
如果有兴趣和本博客交换友链的话,请按照下面的格式在评论区进行评论,我会尽快添加上你的链接。

网站名称:KingYiFan’S Blog
网站地址:http://blog.cnbuilder.cn
网站描述:年少是你未醒的梦话,风华是燃烬的彼岸花。
网站Logo/头像:http://blog.cnbuilder.cn/upload/2018/7/avatar20180720144536200.jpg

猜你喜欢

转载自blog.csdn.net/weixin_39984161/article/details/81189721