微信支付-超详细java开发-小程序对接

这篇具有很好参考价值的文章主要介绍了微信支付-超详细java开发-小程序对接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本文适用于有一定基础的开发者,简单易通。后台用的的是java,我用的是springBoot,其它框架基本同理,前端就是一个简单的demo。微信官方提供了V2和V3两种方式,本文基于V2版支付开发(后续更新V3)。V2和V3版本区别

微信支付接入前准备

1.思路介绍

本次以微信小程序开发为例,如果自己想要玩一下,需要在微信公众平台注册一个小程序账号,其次微信支付
平台,进行接入微信支付,然后就可以着手开发了。(公司开发的话,参数账号之类都会提供的,不用担心)

2.如何注册小程序账号?

可以直接进入微信公众平台直接注册,也可根据文档指引进行注册。申请账号官方详细文档
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
微信支付-超详细java开发-小程序对接

3.如何接入微信支付,需要准备哪些东西?

简单介绍一下自己的经验,重点是营业执照信息。关键这玩意怎么搞方面快捷呢。
我提供两种方案:
1.最方便的是自己注册个电商营业执照,拼多多商铺0元直接入驻即可。注册地址要和营业执照地址相同(要有房产证明等,写自己老家就行)。
2.如果自己租的房子,合同中必须体现可以作为商铺,要不然没法。 直接注册实体个体工商户,目前非常方便线下线上都可以办理-不同地方的软件不同 这里以杭州为例,直接下载浙里办-搜索个体工商户,在线办理即可。
最重要的是地址(可以借助朋友,或者租房的时候说好可以作为商铺,当然自己本地有房子咋样搞都行),其次网上那些挂靠地址等,如果你可以承担费用,自主决定。
请参考微信支付商户号申请,详细的一批。

4.小程序接入前的准备(APPID及mchid以上两步已拿到,只需配置一下API KEY即可)

官方文档指引-小程序支付
注意:API秘钥,一定要记住。最好记事本记录一下。
由于本文基于V2版支付开发,记得设置一下。
微信支付-超详细java开发-小程序对接

微信支付-完整代码

前端代码

index.wxml

<view class="container">
  <view class="container">
    <input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;"  />
    <button type="primary" bindtap="pay">微信支付</button>
</view>
</view>

index.js

const app = getApp()

Page({
  data: {
    txtOrderCode: '',
  },
  pay: function () {
    var ordercode = this.data.txtOrderCode;
    wx.login({
      success: function (res) {
        console.log("code值是多少"+res.code)
        if (res.code) {
          wx.request({
            url: 'http://localhost:8081/wechatPay/wxPay',
            data: {
              code: res.code,//要去换取openid的登录凭证
              money: ordercode,//支付金额(主要是code和money其他都是业务参数可去掉)
              name:"",  
              idCard:"",
              post:"",
              phone:"",
              receiverInfo:"",
              duesDate:"",
              partyOrg:"",
              agyCode:"",
              agyName:"",
              orderId:""
            },
            method: 'POST',
            success: function (res) {
              console.log("是否进来方法"+res.data.package)
              //通过接口返回的data,调用此方法唤起支付页面
              wx.requestPayment({
              	//这里的参数和值基本都是固定,不用更改
                timeStamp: res.data.timeStamp,
                nonceStr: res.data.nonceStr,
                package: res.data.package,
                signType: 'MD5',
                paySign: res.data.paySign,
                success: function (res) {
                  console.log("支付成功")
                  console.log(res);
                },
                fail: function (res) {
                  console.log("支付失败");
                  wx.showToast({
                    title: '支付失败',
                    icon:'none',
                    duration:2000
                  })
                },
                complete: function (res) {
                  console.log('支付完成');
                  if (res.errMsg == 'requestPayment:ok') {
                      wx.showModal({
                          title: '提示',
                          content: '支付成功'
                      });
                    }
                  }
              })
            },
            fail: function (res) {
              wx.showToast({
                title: '调用接口失败',
                icon:'none',
                duration:2000
              })
            },

          })
        } else {
          console.log('获取用户登录态失败!' + res.errMsg)
        }
      }
    });
},
getOrderCode: function (event) {
  console.log(event)
    this.setData({
      txtOrderCode: event.detail.value
    });
},
  onLoad: function () {
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },
})
效果图

微信支付-超详细java开发-小程序对接

后端代码

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hn.yuan</groupId>
    <artifactId>demo_wxpay</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo_wxpay</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <!--解析xml-->
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom2</artifactId>
            <version>2.0.6</version>
        </dependency>
        <!--Java对象和JSON数据之间进行映射的Java类库-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
        <!--处理输入输出流-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk16</artifactId>
            <version>1.46</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.21</version>
        </dependency>

        <!--小程序支付包-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.8</version>
        </dependency>

        <!--http commons client-->
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.1</version>
        </dependency>


        <!--springboot整合redisCluster-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--redis框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 8081

spring:
    datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/yuan_productlist?useUnicode=true&characterEncoding=UTF-8&useSSL=false
        username: root
        password: root
# 配置slq打印日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

log.path: F:/logs	#打印日志路径

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--注意resource属于.yml还是.properties-->
    <property resource="application.yml"/>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径     使用spring-boot的配置项LOG_PATH-->
    <springProperty scope="context" name="LOG_HOME" source="log.path"/>
    <!--下面用于application.properties-->
    <!--    <property name="LOG_HOME" value="${log.path}" />-->
    <property name="LOG_LEVEL" value="INFO" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/springbootjpa.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <logger name="com.wm.springbootjpa01" level="INFO"/>
    <logger name="org.apache.ibatis" level="${LOG_LEVEL}"/>
    <logger name="org.mybatis.spring" level="${LOG_LEVEL}"/>
    <logger name="org.springframework" level="${LOG_LEVEL}"/>
    <logger name="java.sql.Connection" level="${LOG_LEVEL}"/>
    <logger name="java.sql.Statement" level="${LOG_LEVEL}"/>
    <logger name="java.sql.PreparedStatement" level="${LOG_LEVEL}"/>

    <!--设置为OFF,即屏蔽; 留下sqltiming作为INFO级别输出-->
    <logger name="jdbc.connection" level="OFF"/>
    <logger name="jdbc.resultset" level="OFF"/>
    <logger name="jdbc.resultsettable" level="OFF"/>
    <logger name="jdbc.audit" level="OFF"/>
    <logger name="jdbc.sqltiming" level="INFO"/>
    <logger name="jdbc.sqlonly" level="OFF"/>

    <!-- 日志输出级别 -->
    <root level="${LOG_LEVEL}">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

WechatConfig支付配置类

package com.hn.yuan.wxinterface;

/**
 * 支付配置类
 * @author XIAOCAO
 **/
public class WechatConfig {
    //小程序appid(开发者在微信公众平台查询)
    public static final String appid ="";
    //小程序appkey(开发者在微信公众平台查询)
    public static final String APP_SECRET="";
    //微信支付的商户id(开发者在微信商户平台查询)
    public static final String mch_id = "";
    //微信支付的商户密钥(开发者在微信商户平台查询)
    public static final String key = "";
    //支付成功后的服务器回调url(后台随便的一个接口能接收到就行,有效地址)
    public static final String notify_url = "";
    //签名方式,固定值
    public static final String SIGNTYPE = "MD5";
    //交易类型,小程序支付的固定值为JSAPI
    public static final String TRADETYPE = "JSAPI";
    //微信统一下单接口地址
    public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}

WxPayDto类

package com.hn.yuan.entity;

import lombok.Data;

import java.io.Serializable;


@Data
public class WxPayDto implements Serializable {

    private static final long serialVersionUID = 1L;
    private String partyOrg;
    private String duesDate;
    private String money;   //缴费金额
    private String name;
    private String idCard;
    private String post;
    private String phone;
    private String orderId;
    private String receiverInfo;
    private String code;//获取openid临时凭证
    private String agyCode;
    private String agyName;

}

业务代码WxpayController

package com.hn.yuan.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hn.yuan.entity.WxPayDto;
import com.hn.yuan.wxinterface.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import static com.hn.yuan.wxinterface.WXPayConstants.RETURN_CODE;
import static com.hn.yuan.wxinterface.WechatConfig.APP_SECRET;

@CrossOrigin
@RestController
@RequestMapping("/wechatPay")
public class WxpayController {
    private static Logger logger = LoggerFactory.getLogger(WxpayController.class);

    @Autowired
    private HttpServletRequest request;

    @Resource
    private HttpServletResponse response;


    @PostMapping("/wxPay")
    public String wxPay(@RequestBody WxPayDto dto) {
        System.out.println("进来方法了");
        Object result = new Object();
        try {
            //获取客户端的ip地址
            String spbill_create_ip = getIpAddr(request);
            System.out.println("客户端Ip地址" + spbill_create_ip);
            System.out.println("code是什么样式的" + dto.getCode());
            //获取openid
            String openid = getOpenId(dto.getCode());
            //订单号  uuid  随机生成
            String outTradeNo = WXPayUtil.generateUUID();
            //支付业务
            result = wxPay(spbill_create_ip, openid, outTradeNo, dto);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return PayUtil.toJson(result);
    }


    //获取IP
    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }

    /**
     * 缴纳费用
     **/
    public Map wxPay(String spbill_create_ip, String openId, String orderNumber, WxPayDto dto) {
        Map<String, String> payMap = new HashMap<String, String>();//返回给小程序端需要的参数
        try {
            logger.info("【小程序支付】 统一下单开始, 订单编号=" + orderNumber);
            //商品名称
            String body = dto.getAgyName() + "-" + dto.getName() + "-测试费用";
            //获取客户端的ip地址
            BigDecimal money = new BigDecimal(dto.getMoney());
            //组装参数,用户生成统一下单接口的签名
            logger.info("----------下单接口签名-------");
            Map<String, String> packageParams = new HashMap<>();
            //微信分配的小程序ID
            packageParams.put("appid", WechatConfig.appid);
            //微信支付分配的商户号
            packageParams.put("mch_id", WechatConfig.mch_id);
            //随机字符串
            packageParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
            //签名类型
            packageParams.put("sign_type", "MD5");
            //充值订单 商品描述
            packageParams.put("body", body);
            //商户订单号
            packageParams.put("out_trade_no", orderNumber);
            //订单总金额,单位为分
            packageParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
            //终端IP
            packageParams.put("spbill_create_ip", spbill_create_ip);
            //通知回调地址
            packageParams.put("notify_url", WechatConfig.notify_url);
            //交易类型
            packageParams.put("trade_type", WechatConfig.TRADETYPE);
            //用户标识
            packageParams.put("openid", openId);
            //第一次签名
            String sign = WXPayUtil.generateSignature(packageParams, WechatConfig.key);
            System.out.println("第一签名打印的是个啥" + sign);
            packageParams.put("sign", sign);
            System.out.println("字符串是啥" + PaymentKit.toXml(packageParams));
            //调用支付定义下单API,返回预付单信息 prepay_id
            String result = HttpKit.post(WechatConfig.pay_url, PaymentKit.toXml(packageParams));
            logger.info("调试模式_统一下单接口 返回XML数据:" + result);
            // 将解析结果存储在HashMap中
            Map<String, String> map = PaymentKit.xmlToMap(result);
            String return_code = map.get("return_code");//返回状态码
            String result_code = map.get("result_code");//返回状态码
            if (return_code.equals("SUCCESS") || return_code.equals(result_code)) {
                //返回的预付单信息
                String prepay_id = map.get("prepay_id");
                payMap.put("appId", WechatConfig.appid);
                payMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
                payMap.put("nonceStr", System.currentTimeMillis() + "");
                payMap.put("package", "prepay_id=" + prepay_id);
                payMap.put("signType", "MD5");

                //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = WXPayUtil.generateSignature(payMap, WechatConfig.key);
                System.out.println("第二签名打印的是个啥" + sign);
                logger.info("=======================第二次签名:", paySign + "============ ======");
                payMap.put("paySign", paySign);
                payMap.put("status", "success");
                //更新订单信息
            } else {
                logger.info("----------统一下单失败-------");
                payMap.put("status", "error");
                return payMap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return payMap;
    }

    /**
     * 小程序获取openid
     *
     * @return
     */
    public static String getOpenId(String code) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        RestTemplate restTemplate = new RestTemplate();
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + WechatConfig.appid + "&secret=" + APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
        ResponseEntity<String> responseEntity =
                restTemplate.getForEntity(url, String.class);
        String body = responseEntity.getBody();
        JSONObject object = JSON.parseObject(body);
        String openId = object.getString("openid");// 获取openId
        System.out.println("该用户的openid——>" + openId);
        return openId;
    }


    /**
     * 功能描述: <小程序回调>
     *
     * @return:
     **/
    @GetMapping("/wxProPayNotify")
    public void wxProPayNotify() throws Exception {
        logger.info("进入微信小程序支付回调");
        String xmlMsg = HttpKit.readData(request);
        logger.info("微信小程序通知信息" + xmlMsg);
        Map<String, String> resultMap = PaymentKit.xmlToMap(xmlMsg);
        if (resultMap.get(RETURN_CODE).equals("SUCCESS")) {
            String orderNo = resultMap.get("out_trade_no");
            logger.info("微信小程序支付成功,订单号{}", orderNo);
            //通过订单号 修改数据库中的记录,业务操作
        }
        String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        try {
            response.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

工具类

  • http,https-request类
package com.hn.yuan.wxinterface;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;

/**
 * http,https-request类
 **/
public class HttpKit {
    private static String CHARSET = "UTF-8";
    private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
    private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier();

    private HttpKit() {
    }

    private static SSLSocketFactory initSSLSocketFactory() {
        try {
            TrustManager[] e = new TrustManager[]{new HttpKit().new TrustAnyTrustManager()};
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init((KeyManager[])null, e, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception var2) {
            throw new RuntimeException(var2);
        }
    }

    public static void setCharSet(String charSet) {
        if(charSet!=null && !charSet.equals("")) {
            throw new IllegalArgumentException("charSet can not be blank.");
        } else {
            CHARSET = charSet;
        }
    }

    private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
        URL _url = new URL(url);
        HttpURLConnection conn = (HttpURLConnection)_url.openConnection();
        if(conn instanceof HttpsURLConnection) {
            ((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
            ((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
        }

        conn.setRequestMethod(method);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setConnectTimeout(19000);
        conn.setReadTimeout(19000);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("AuthUser-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
        if(headers != null && !headers.isEmpty()) {
            Iterator i$ = headers.entrySet().iterator();

            while(i$.hasNext()) {
                Map.Entry entry = (Map.Entry)i$.next();
                conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
            }
        }

        return conn;
    }

    public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
        HttpURLConnection conn = null;
        String e;
        try {
            conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "GET", headers);
            conn.connect();
            e = readResponseString(conn);
        } catch (Exception var8) {
            throw new RuntimeException(var8);
        } finally {
            if(conn != null) {
                conn.disconnect();
            }
        }
        return e;
    }

    public static String get(String url, Map<String, String> queryParas) {
        return get(url, queryParas, (Map)null);
    }

    public static String get(String url) {
        return get(url, (Map)null, (Map)null);
    }

    public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
        HttpURLConnection conn = null;

        String var6;
        try {
            conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "POST", headers);
            conn.connect();
            OutputStream e = conn.getOutputStream();
            e.write(data.getBytes(CHARSET));
            e.flush();
            e.close();
            var6 = readResponseString(conn);
        } catch (Exception var10) {
            throw new RuntimeException(var10);
        } finally {
            if(conn != null) {
                conn.disconnect();
            }
        }
        return var6;
    }

    public static String post(String url, Map<String, String> queryParas, String data) {
        return post(url, queryParas, data, (Map)null);
    }

    public static String post(String url, String data, Map<String, String> headers) {
        return post(url, (Map)null, data, headers);
    }

    public static String post(String url, String data) {
        return post(url, (Map)null, data, (Map)null);
    }

    private static String readResponseString(HttpURLConnection conn) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;

        try {
            inputStream = conn.getInputStream();
            BufferedReader e = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
            String line = null;

            while((line = e.readLine()) != null) {
                sb.append(line).append("\n");
            }

            String var5 = sb.toString();
            return var5;
        } catch (Exception var14) {
            throw new RuntimeException(var14);
        } finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException var13) {
                }
            }

        }
    }

    private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
        if(queryParas != null && !queryParas.isEmpty()) {
            StringBuilder sb = new StringBuilder(url);
            boolean isFirst;
            if(url.indexOf("?") == -1) {
                isFirst = true;
                sb.append("?");
            } else {
                isFirst = false;
            }

            String key;
            String value;
            for(Iterator i$ = queryParas.entrySet().iterator(); i$.hasNext(); sb.append(key).append("=").append(value)) {
                Map.Entry entry = (Map.Entry)i$.next();
                if(isFirst) {
                    isFirst = false;
                } else {
                    sb.append("&");
                }

                key = (String)entry.getKey();
                value = (String)entry.getValue();
                if(value!=null && !value.equals("")) {
                    try {
                        value = URLEncoder.encode(value, CHARSET);
                    } catch (UnsupportedEncodingException var9) {
                        throw new RuntimeException(var9);
                    }
                }
            }

            return sb.toString();
        } else {
            return url;
        }
    }

    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;

        try {
            StringBuilder e = new StringBuilder();
            br = request.getReader();
            String line = null;

            while((line = br.readLine()) != null) {
                e.append(line).append("\n");
            }

            line = e.toString();
            return line;
        } catch (IOException var12) {
            throw new RuntimeException(var12);
        } finally {
            if(br != null) {
                try {
                    br.close();
                } catch (IOException var11) {
                }
            }

        }
    }

    /** @deprecated */
    @Deprecated
    public static String readIncommingRequestData(HttpServletRequest request) {
        return readData(request);
    }


    private class TrustAnyTrustManager implements X509TrustManager {
        private TrustAnyTrustManager() {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    }

    private class TrustAnyHostnameVerifier implements HostnameVerifier {
        private TrustAnyHostnameVerifier() {
        }

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }


}
  • 微信支付api
package com.hn.yuan.wxinterface;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;


/**
 * 微信支付api
 * @author XIAOCAO
 * @version 2020/12/25
 **/
public class PaymentApi {

    private PaymentApi() {}

    // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
    private static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 交易类型枚举
     * WAP的文档:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1
     * @author L.cm
     * <pre>
     * email: 596392912@qq.com
     * site: http://www.dreamlu.net
     * date: 2015年10月27日 下午9:46:27
     * </pre>
     */
    public enum TradeType {
        JSAPI, NATIVE, APP, WAP, MWEB
    }

    /**
     * 统一下单
     * @param params 参数map
     * @return String
     */
    public static String pushOrder(Map<String, String> params) {
        return HttpKit.post(unifiedOrderUrl, PaymentKit.toXml(params));
    }

    private static Map<String, String> request(String url, Map<String, String> params, String paternerKey) {
        params.put("nonce_str", System.currentTimeMillis() + "");
        String sign = PaymentKit.createSign(params, paternerKey);
        params.put("sign", sign);
        String xmlStr = HttpKit.post(url, PaymentKit.toXml(params));
        return PaymentKit.xmlToMap(xmlStr);
    }

    /**
     * 文档说明:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_4
     * <pre>
     * @param appId 公众账号ID         是    String(32)    wx8888888888888888    微信分配的公众账号ID
     * 随机字符串         noncestr    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
     * 订单详情扩展字符串    package        是    String(32)    WAP    扩展字段,固定填写WAP
     * @param prepayId 预支付交易会话标识    是    String(64)    wx201410272009395522657a690389285100    微信统一下单接口返回的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
     * 签名                 sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
     * 时间戳            timestamp    是    String(32)    1414561699    当前的时间,其他详见时间戳规则
     * @param paternerKey 签名密匙
     * </pre>
     * @return {String}
     */
    public static String getDeepLink(String appId, String prepayId, String paternerKey) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appId);
        params.put("noncestr", System.currentTimeMillis() + "");
        params.put("package", "WAP");
        params.put("prepayid", prepayId);
        params.put("timestamp", System.currentTimeMillis() / 1000 + "");
        String sign = PaymentKit.createSign(params, paternerKey);
        params.put("sign", sign);

        String string1 = PaymentKit.packageSign(params, true);

        String string2 = "";
        try { string2 = PaymentKit.urlEncode(string1); } catch (UnsupportedEncodingException e) {}

        return "weixin://wap/pay?" + string2;
    }

    // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
    private static String orderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     * 根据商户订单号查询信息
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param transaction_id 微信订单号
     * @return 回调信息
     */
    public static Map<String, String> queryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("transaction_id", transaction_id);
        return request(orderQueryUrl, params, paternerKey);
    }

    /**
     * 根据商户订单号查询信息
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param out_trade_no 商户订单号
     * @return 回调信息
     */
    public static Map<String, String> queryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("out_trade_no", out_trade_no);
        return request(orderQueryUrl, params, paternerKey);
    }

    // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
    private static String closeOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";

    /**
     * 关闭订单
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param out_trade_no 商户订单号
     * @return 回调信息
     */
    public static Map<String, String> closeOrder(String appid, String mch_id, String paternerKey, String out_trade_no) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("out_trade_no", out_trade_no);
        return request(closeOrderUrl, params, paternerKey);
    }

    // 申请退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
    public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

//    /**
//     * 申请退款,内部添加了随机字符串nonce_str和签名sign
//     * @param params 参数map,内部添加了随机字符串nonce_str和签名sign
//     * @param paternerKey 商户密钥
//     * @param certPath 证书文件目录
//     * @return  map
//     */
//    public static Map<String, String> refund(Map<String, String> params, String paternerKey, String certPath) {
//        params.put("nonce_str", System.currentTimeMillis() + "");
//        String sign = PaymentKit.createSign(params, paternerKey);
//        params.put("sign", sign);
//        String partner = params.get("mch_id");
//        String xmlStr = HttpKit.delegate.postSSL(refundUrl, PaymentKit.toXml(params), certPath, partner);
//        return PaymentKit.xmlToMap(xmlStr);
//    }

    // 查询退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
    private static String refundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

    private static Map<String, String> baseRefundQuery(Map<String, String> params, String appid, String mch_id, String paternerKey) {
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        return request(refundQueryUrl, params, paternerKey);
    }

    /**
     * 根据微信订单号查询退款
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param transaction_id 微信订单号
     * @return map
     */
    public static Map<String, String> refundQueryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("transaction_id", transaction_id);
        return baseRefundQuery(params, appid, mch_id, paternerKey);
    }

    /**
     * 根据微信订单号查询退款
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param out_trade_no 商户订单号
     * @return map
     */
    public static Map<String, String> refundQueryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("out_trade_no", out_trade_no);
        return baseRefundQuery(params, appid, mch_id, paternerKey);
    }

    /**
     * 根据微信订单号查询退款
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param out_refund_no 商户退款单号
     * @return map
     */
    public static Map<String, String> refundQueryByOutRefundNo(String appid, String mch_id, String paternerKey, String out_refund_no) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("out_refund_no", out_refund_no);
        return baseRefundQuery(params, appid, mch_id, paternerKey);
    }

    /**
     * 根据微信订单号查询退款
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 商户密钥
     * @param refund_id 微信退款单号
     * @return map
     */
    public static Map<String, String> refundQueryByRefundId(String appid, String mch_id, String paternerKey, String refund_id) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("refund_id", refund_id);
        return baseRefundQuery(params, appid, mch_id, paternerKey);
    }

    private static String downloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

    /**
     * <pre>
     * ALL,返回当日所有订单信息,默认值
     * SUCCESS,返回当日成功支付的订单
     * REFUND,返回当日退款订单
     * REVOKED,已撤销的订单
     * </pre>
     */
    public static enum BillType {
        ALL, SUCCESS, REFUND, REVOKED
    }

    /**
     * 下载对账单
     * <pre>
     * 公众账号ID    appid        是    String(32)    wx8888888888888888    微信分配的公众账号ID(企业号corpid即为此appId)
     * 商户号        mch_id        是    String(32)    1900000109    微信支付分配的商户号
     * 设备号        device_info    否    String(32)    013467007045764    微信支付分配的终端设备号
     * 随机字符串    nonce_str    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
     * 签名        sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
     * 对账单日期    bill_date    是    String(8)    20140603    下载对账单的日期,格式:20140603
     * 账单类型        bill_type    否    String(8)
     * </pre>
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 签名密匙
     * @param billDate 对账单日期
     * @return String
     */
    public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate) {
        return downloadBill(appid, mch_id, paternerKey, billDate, null);
    }

    /**
     * 下载对账单
     * <pre>
     * 公众账号ID    appid        是    String(32)    wx8888888888888888    微信分配的公众账号ID(企业号corpid即为此appId)
     * 商户号        mch_id        是    String(32)    1900000109    微信支付分配的商户号
     * 设备号        device_info    否    String(32)    013467007045764    微信支付分配的终端设备号
     * 随机字符串    nonce_str    是    String(32)    5K8264ILTKCH16CQ2502SI8ZNMTM67VS    随机字符串,不长于32位。推荐随机数生成算法
     * 签名        sign        是    String(32)    C380BEC2BFD727A4B6845133519F3AD6    签名,详见签名生成算法
     * 对账单日期    bill_date    是    String(8)    20140603    下载对账单的日期,格式:20140603
     * 账单类型        bill_type    否    String(8)
     * </pre>
     * @param appid 公众账号ID
     * @param mch_id 商户号
     * @param paternerKey 签名密匙
     * @param billDate 对账单日期
     * @param billType 账单类型
     * @return String
     */
    public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate, BillType billType) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("nonce_str", System.currentTimeMillis() + "");
        params.put("bill_date", billDate);
        if (null != billType) {
            params.put("bill_type", billType.name());
        } else {
            params.put("bill_type", BillType.ALL.name());
        }
        String sign = PaymentKit.createSign(params, paternerKey);
        params.put("sign", sign);
        String str = HttpKit.post(downloadBillUrl, PaymentKit.toXml(params));
        return PaymentKit.xmlToMap(str);

    }

}
  • 微信支付的统一下单工具类
package com.hn.yuan.wxinterface;


import org.apache.commons.io.Charsets;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;



/**
 * 微信支付的统一下单工具类
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class PaymentKit {

    /**
     * 组装签名的字段
     * @param params 参数
     * @param urlEncoder 是否urlEncoder
     * @return String
     */
    public static String packageSign(Map<String, String> params, boolean urlEncoder) {
        // 先将参数以其参数名的字典序升序进行排序
        TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
        // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Entry<String, String> param : sortedParams.entrySet()) {
            String value = param.getValue();
            if (Tools.isEmpty(value)) {
                continue;
            }
            if (first) {
                first = false;
            } else {
                sb.append("&");
            }
            sb.append(param.getKey()).append("=");
            if (urlEncoder) {
                try { value = urlEncode(value); } catch (UnsupportedEncodingException e) {}
            }
            sb.append(value);
        }
        return sb.toString();
    }

    /**
     * urlEncode
     * @param src 微信参数
     * @return String
     * @throws UnsupportedEncodingException 编码错误
     */
    public static String urlEncode(String src) throws UnsupportedEncodingException {
        return URLEncoder.encode(src, Charsets.UTF_8.name()).replace("+", "%20");
    }

    /**
     * 生成签名
     * @param params 参数
     * @param paternerKey 支付密钥
     * @return sign
     */
    public static String createSign(Map<String, String> params, String paternerKey) {
        // 生成签名前先去除sign
        params.remove("sign");
        String stringA = packageSign(params, false);
        String stringSignTemp = stringA + "&key=" + paternerKey;
        return Tools.md5(stringSignTemp).toUpperCase();
    }

    /**
     * 支付异步通知时校验sign
     * @param params 参数
     * @param paternerKey 支付密钥
     * @return {boolean}
     */
    public static boolean verifyNotify(Map<String, String> params, String paternerKey){
        String sign = params.get("sign");
        String localSign = PaymentKit.createSign(params, paternerKey);
        return sign.equals(localSign);
    }

    /**
     * 微信下单,map to xml
     * @param params 参数
     * @return String
     */
    public static String toXml(Map<String, String> params) {
        StringBuilder xml = new StringBuilder();
        xml.append("<xml>");
        for (Entry<String, String> entry : params.entrySet()) {
            String key   = entry.getKey();
            String value = entry.getValue();
            // 略过空值
            if (Tools.isEmpty(value)) continue;
            xml.append("<").append(key).append(">");
            xml.append(entry.getValue());
            xml.append("</").append(key).append(">");
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 针对支付的xml,没有嵌套节点的简单处理
     * @param xmlStr xml字符串
     * @return map集合
     */
    public static Map<String, String> xmlToMap(String xmlStr) {
        XmlHelper xmlHelper = XmlHelper.of(xmlStr);
        return xmlHelper.toMap();
    }

}
  • 支付签名常用类
package com.hn.yuan.wxinterface;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.codec.digest.DigestUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * 支付签名常用类
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class PayUtil {
    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param key           密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param sign          签名结果
     * @param key           密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text +"&key=" + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws java.security.SignatureException
     * @throws UnsupportedEncodingException
     */
    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    private static boolean isValidChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
            return true;// 简体中文汉字编码
        return false;
    }

    /**
     * 除去数组中的空值和签名参数
     *
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * @param requestUrl    请求地址
     * @param requestMethod 请求方法
     * @param outputStr     参数
     */
    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
        // 创建SSLContext
        StringBuffer buffer = null;
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            //往服务器端写内容
            if (null != outputStr) {
                OutputStream os = conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // 读取服务器端返回的内容
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }

    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     *
     * @param strxml
     * @return
     * @throws org.jdom2.JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws Exception {
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = String2Inputstream(strxml);
        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);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     *
     * @param children
     * @return String
     */
    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();
    }

    public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

    /**
     * java对象转字符串
     * @param <T> 泛型占位符
     * @param obj T
     * @return String
     */
    public static <T> String toJson(T obj) {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").enableComplexMapKeySerialization().create();
        return gson.toJson(obj);
    }
}
  • 支付常量
package com.hn.yuan.wxinterface;


/**
 * 支付常量
 *
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class WXPayConstants {

    public enum SignType {
        MD5, HMACSHA256
    }

    public static final String DOMAIN_API = "api.mch.weixin.qq.com";
    public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
    public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
    public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";


    public static final String FAIL = "FAIL";
    /**
     * 成功的标识
     */
    public static final String SUCCESS = "SUCCESS";
    /**
     * 返回状态码的变量名
     */
    public static final String RETURN_CODE = "return_code";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";

    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";

    public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
    public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
    public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
    public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
    public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
    public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
    public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";

    // sandbox
    public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
    public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
    public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
    public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
    public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
    public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
    public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
    public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
    public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
    public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
    public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
  • xpath解析xml
package com.hn.yuan.wxinterface;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;


/**
 * xpath解析xml
 * <pre>
 *     文档地址:
 *     http://www.w3school.com.cn/xpath/index.asp
 * </pre>
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class XmlHelper {
    private final XPath path;
    private final Document doc;

    private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory dbf = getDocumentBuilderFactory();
        DocumentBuilder db = dbf.newDocumentBuilder();
        doc = db.parse(inputSource);
        path = getXPathFactory().newXPath();
    }

    private static XmlHelper create(InputSource inputSource) {
        try {
            return new XmlHelper(inputSource);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static XmlHelper of(InputStream is) {
        InputSource inputSource = new InputSource(is);
        return create(inputSource);
    }

    public static XmlHelper of(String xmlStr) {
        StringReader sr = new StringReader(xmlStr.trim());
        InputSource inputSource = new InputSource(sr);
        XmlHelper xmlHelper = create(inputSource);
        IOUtils.closeQuietly(sr);
        return xmlHelper;
    }

    private Object evalXPath(String expression, Object item, QName returnType) {
        item = null == item ? doc : item;
        try {
            return path.evaluate(expression, item, returnType);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取String
     * @param expression 路径
     * @return String
     */
    public String getString(String expression) {
        return (String) evalXPath(expression, null, XPathConstants.STRING);
    }

    /**
     * 获取Boolean
     * @param expression 路径
     * @return String
     */
    public Boolean getBoolean(String expression) {
        return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN);
    }

    /**
     * 获取Number
     * @param expression 路径
     * @return {Number}
     */
    public Number getNumber(String expression) {
        return (Number) evalXPath(expression, null, XPathConstants.NUMBER);
    }

    /**
     * 获取某个节点
     * @param expression 路径
     * @return {Node}
     */
    public Node getNode(String expression) {
        return (Node) evalXPath(expression, null, XPathConstants.NODE);
    }

    /**
     * 获取子节点
     * @param expression 路径
     * @return NodeList
     */
    public NodeList getNodeList(String expression) {
        return (NodeList) evalXPath(expression, null, XPathConstants.NODESET);
    }


    /**
     * 获取String
     * @param node 节点
     * @param expression 相对于node的路径
     * @return String
     */
    public String getString(Object node, String expression) {
        return (String) evalXPath(expression, node, XPathConstants.STRING);
    }

    /**
     * 获取
     * @param node 节点
     * @param expression 相对于node的路径
     * @return String
     */
    public Boolean getBoolean(Object node, String expression) {
        return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN);
    }

    /**
     * 获取
     * @param node 节点
     * @param expression 相对于node的路径
     * @return {Number}
     */
    public Number getNumber(Object node, String expression) {
        return (Number) evalXPath(expression, node, XPathConstants.NUMBER);
    }

    /**
     * 获取某个节点
     * @param node 节点
     * @param expression 路径
     * @return {Node}
     */
    public Node getNode(Object node, String expression) {
        return (Node) evalXPath(expression, node, XPathConstants.NODE);
    }

    /**
     * 获取子节点
     * @param node 节点
     * @param expression 相对于node的路径
     * @return NodeList
     */
    public NodeList getNodeList(Object node, String expression) {
        return (NodeList) evalXPath(expression, node, XPathConstants.NODESET);
    }

    /**
     * 针对没有嵌套节点的简单处理
     * @return map集合
     */
    public Map<String, String> toMap() {
        Element root = doc.getDocumentElement();
        Map<String, String> params = new HashMap<String, String>();

        // 将节点封装成map形式
        NodeList list = root.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);
            if (node instanceof Element) {
                params.put(node.getNodeName(), node.getTextContent());
            }
        }
        return params;
    }

    private static DocumentBuilderFactory getDocumentBuilderFactory(){
        return XmlHelperHolder.documentBuilderFactory;
    }

    private static XPathFactory getXPathFactory() {
        return  XmlHelperHolder.xPathFactory;
    }

    /**
     * 内部类单例
     */
    private static class XmlHelperHolder {
        private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        private static XPathFactory xPathFactory = XPathFactory.newInstance();
    }

}

  • 常用工具类
package com.hn.yuan.wxinterface;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.*;


/**
 * 常用工具类
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class WXPayUtil {

    /**
     * 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>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.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) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.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) {
        }
        return output;
    }


    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名类型
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        String sign = generateSignature(data, key, signType);
        data.put(WXPayConstants.FIELD_SIGN, sign);
        return mapToXml(data);
    }


    /**
     * 判断签名是否正确
     *
     * @param xmlStr XML格式数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = xmlToMap(xmlStr);
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /**
     * 生成签名
     *
     * @param data 待签名数据
     * @param key API密钥
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.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 (WXPayConstants.SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }


    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        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();
    }

    /**
     * 日志
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }

    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * 获取当前时间戳,单位毫秒
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

    /**
     * 生成 uuid, 即用来标识一笔单,也用做 nonce_str
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

}
  • Tools工具类
package com.hn.yuan.wxinterface;

import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 常用工具
 * @author XIAOCAO
 * @version 2020/12/24
 **/
public class Tools {

    /**
     * 随机生成六位数验证码
     * @return
     */
    public static int getRandomNum(){
        Random r = new Random();
        return r.nextInt(900000)+100000;//(Math.random()*(999999-100000)+100000)
    }

    /**
     * 检测字符串是否不为空(null,"","null")
     * @param s
     * @return 不为空则返回true,否则返回false
     */
    public static boolean notEmpty(String s){
        return s!=null && !"".equals(s) && !"null".equals(s);
    }

    /**
     * 检测字符串是否为空(null,"","null")
     * @param s
     * @return 为空则返回true,不否则返回false
     */
    public static boolean isEmpty(String s){
        return s==null || "".equals(s) || "null".equals(s);
    }

    /**
     * 字符串转换为字符串数组
     * @param str 字符串
     * @param splitRegex 分隔符
     * @return
     */
    public static String[] str2StrArray(String str,String splitRegex){
        if(isEmpty(str)){
            return null;
        }
        return str.split(splitRegex);
    }

    /**
     * 用默认的分隔符(,)将字符串转换为字符串数组
     * @param str	字符串
     * @return
     */
    public static String[] str2StrArray(String str){
        return str2StrArray(str,",\\s*");
    }

    /**
     * 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
     * @param date
     * @return yyyy-MM-dd HH:mm:ss
     */
    public static String date2Str(Date date){
        return date2Str(date,"yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
     * @param date
     * @return
     */
    public static Date str2Date(String date){
        if(notEmpty(date)){
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try {
                return sdf.parse(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return new Date();
        }else{
            return null;
        }
    }

    /**
     * 按照参数format的格式,日期转字符串
     * @param date
     * @param format
     * @return
     */
    public static String date2Str(Date date,String format){
        if(date!=null){
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            return sdf.format(date);
        }else{
            return "";
        }
    }

    /**
     * 把时间根据时、分、秒转换为时间段
     * @param StrDate
     */
    public static String getTimes(String StrDate){
        String resultTimes = "";

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date now;

        try {
            now = new Date();
            Date date=df.parse(StrDate);
            long times = now.getTime()-date.getTime();
            long day  =  times/(24*60*60*1000);
            long hour = (times/(60*60*1000)-day*24);
            long min  = ((times/(60*1000))-day*24*60-hour*60);
            long sec  = (times/1000-day*24*60*60-hour*60*60-min*60);

            StringBuffer sb = new StringBuffer();
            //sb.append("发表于:");
            if(hour>0 ){
                sb.append(hour+"小时前");
            } else if(min>0){
                sb.append(min+"分钟前");
            } else{
                sb.append(sec+"秒前");
            }

            resultTimes = sb.toString();
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return resultTimes;
    }

    /**
     * 验证邮箱
     * @param email
     * @return
     */
    public static boolean checkEmail(String email){
        boolean flag = false;
        try{
            String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
            Pattern regex = Pattern.compile(check);
            Matcher matcher = regex.matcher(email);
            flag = matcher.matches();
        }catch(Exception e){
            flag = false;
        }
        return flag;
    }

    /**
     * 验证手机号码
     * @return
     */
    public static boolean checkMobileNumber(String mobileNumber){
        Pattern p = null;
        Matcher m = null;
        boolean b = false;
        p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$"); // 验证手机号
        m = p.matcher(mobileNumber);
        b = m.matches();
        return b;
    }

    /**
     * 将驼峰转下划线
     * @param param
     * @return
     */
    public static String camelToUnderline(String param){
        if (param==null||"".equals(param.trim())){
            return "";
        }
        int len=param.length();
        StringBuilder sb=new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c=param.charAt(i);
            if (Character.isUpperCase(c)){
                sb.append("_");
                sb.append(Character.toLowerCase(c));
            }else{
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 去掉下划线并将下划线后的首字母转为大写
     * @param str
     * @return
     */
    public static String transformStr(String str){
        //去掉数据库字段的下划线
        if(str.contains("_")) {
            String[] names = str.split("_");
            String firstPart = names[0];
            String otherPart = "";
            for (int i = 1; i < names.length; i++) {
                String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
                otherPart += word;
            }
            str = firstPart + otherPart;
        }
        return str;
    }

    /**
     * 转换为map
     * @param list
     * @return
     */
    public static List<Map<String,Object>> transformMap(List<Map<String,Object>> list){
        List<Map<String,Object>> resultMapList = new ArrayList<>();

        for (Map<String, Object> map : list) {
            Map<String,Object> tempMap = new HashMap<>();
            for (String s : map.keySet()) {
                tempMap.put(transformStr(s),map.get(s));
            }
            resultMapList.add(tempMap);
        }
        return resultMapList;
    }

    public static String clearHtml(String content,int p) {
        if(null==content) return "";
        if(0==p) return "";

        Pattern p_script;
        Matcher m_script;
        Pattern p_style;
        Matcher m_style;
        Pattern p_html;
        Matcher m_html;

        try {
            String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
            //定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
            String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
            //定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
            String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式

            p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
            m_script = p_script.matcher(content);
            content = m_script.replaceAll(""); //过滤script标签
            p_style = Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
            m_style = p_style.matcher(content);
            content = m_style.replaceAll(""); //过滤style标签

            p_html = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
            m_html = p_html.matcher(content);

            content = m_html.replaceAll(""); //过滤html标签
        }catch(Exception e) {
            return "";
        }

        if(content.length()>p){
            content = content.substring(0, p)+"...";
        }else{
            content = content + "...";
        }

        return content;
    }

    public static String md5(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte b[] = md.digest();

            int i;

            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            str = buf.toString();
        } catch (Exception e) {
            e.printStackTrace();

        }
        return str;
    }


}

微信小程序支付功能完整流程-总结(可忽略)

微信支付-超详细java开发-小程序对接

获取令牌token

  • 获取令牌token
// 点击支付
async handleOrderPay() {
  // 从缓存中获取token
  const token = wx.getStorageSync('token')
  // 如果token不存在,跳转到授权页面获取token
  if (!token) {
    wx.navigateTo({
      url: '/pages/auth/auth',
    })
 }
<!-- 点击按钮获取用户信息 -->
<button type="primary" plain open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">授权</button>
  • 缓存中不存在token,授权!
// 授权操作
// pages/auth/auth.js
import {request} from '../../request/index.js' //网络请求
import regeneratorRuntime from '../../lib/runtime/runtime' //使用es7语法
import {login} from '../../utils/asyncWx.js' //内部封装了wx.login(),通过该方法获取一个必须参数code

Page({
  // 获取用户信息
  async handleGetUserInfo(e) {
    try {
      // 获取小程序登录成功后的五个必须参数
      const {
        encryptedData,
        rawData,
        iv,
        signature
      } = e.detail
      const {
        code
      } = await login()
      // 将这五个参数存入loginParams自定义对象中
      const loginParams = {
        encryptedData,
        rawData,
        iv,
        signature,
        code
      }
      // 获取token
      const {
        token
      } = await request({
        url: '/users/wxlogin',
        // 传入这五个必须参数
        data: loginParams,
        method: 'POST'
      })
      // 将token存入缓存中,同时跳转到上一个页面
      wx.setStorageSync('token', token)
      wx.navigateBack({
        // delta表示返回的层级,1表示返回上一层,2表示返回上两层
        delta: 1,
      })
    } catch (error) {
      console.log(error)
    }
  }
})

创建订单

  • 获取到5个必须参数
// 创建订单
    // 请求头参数,这个参数会贯穿整个支付过程中的网络请求
    const header = {
      // 授权参数即为token
      Authorization: token
    }
    // 请求体参数
    const order_price = this.data.totalPrice //总价格
    const consignee_addr = this.data.address.all //详细收获地址
    const cart = this.data.cart 
    let goods = [] //订单数组,从购物车中提取出几个所需要的属性组成的数组
    goods = cart.forEach(v => {
      goods_id: v.goods_id
      goods_number: v.num
      goods_price: v.goods_price
    })
	// 将所需要的订单参数存入orderParams
    const orderParams = {
      order_price,
      consignee_addr,
      goods
    }
  • 获取订单编号
 // 获取订单编号
    const {order_number} = await request({
      url: '/my/orders/create',
      method: 'POST',
      // 将之前获得的订单参数传入
      data: orderParams,
      header
    })
    console.log(order_number)
  }

预支付

  • 根据订单编号获取到支付参数对象pay
const {pay} = await request({
      url: '/my/orders/req_unifiedorder',
      method: "POST",
      header,
      // 传入订单编号
      data: {
        order_number
      }
    })
    console.log('pay')

发起微信支付

  • 将支付参数pay传入到requestPayment中即可
	await requestPayment(pay)

收尾工作

  • 跳转到订单页面,同时删除购物车中以购买的商品
await showToast('支付成功')
      // 删除缓存中已经被选中的商品
      let newCart = wx.getStorageSync('cart')
      newCart = newCart.filter(v => !v.checked)
      wx.setStorageSync('cart', newCart)
      // 此时跳转到订单页面
      wx.navigateTo({
        url: '/pages/order/order',
      })

后续V3版本-待编辑
微信支付-超详细java开发-小程序对接

各位看官》创作不易,点个赞!!!
诸君共勉:万事开头难,只愿肯放弃。

免责声明:本文章仅用于学习参考文章来源地址https://www.toymoban.com/news/detail-491803.html

到了这里,关于微信支付-超详细java开发-小程序对接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot对接微信小程序支付功能开发(一,下单功能)

    1,接入前准备: 接入模式选择直连模式; 申请小程序,得到APPID,并开通微信支付; 申请微信商户号,得到mchid,并绑定APPID; 配置商户API key,下载并配置商户证书,根据微信官方文档操作:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml 上面都配置完之后会得到:小

    2024年02月10日
    浏览(63)
  • 【超详细,全流程】java对接支付宝支付

    本文对接使用的是支付宝的电脑网站支付,其它对接方式可做参考 接入分两种 第一种是常规接入,主要是针对 企业用户 ,需要有 营业执照;经过备案能正常访问的网站 第二种是 沙箱接入 ,主要针对 个人用户 ,只需要有能正常使用的支付宝就可以完成接入,真实企业用户

    2024年02月02日
    浏览(48)
  • java对接微信支付:JSAPI支付(微信公众号支付)

    本文是【微信JSAPI支付】文章,主要讲解商户对接微信支付,简洁版测试 文章目录 一、JSAPI支付接入前准备 二、代码片段 1.引入Maven依赖 2.后端业务请求接口 3.前端调起支付请求方法 总结 1、JSAPI支付首先需要注册、认证一个公众号(大概300块一年) 微信公众号注册 2、申请成为

    2024年02月08日
    浏览(62)
  • java对接微信支付:JSAPI支付成功之“微信回调”

    承接上一篇微信支付,现在简单说一下 微信支付回调 目录 一、支付回调 二、微信回调地址问题 1.本地/上线测试 2.控制器调用接口(代码) 总结 当用户支付成功之后,支付平台会向我们指定的服务器接口发送请求传递订单支付状态数据 如果你是再本地进行测试,那就需要使用

    2024年02月12日
    浏览(41)
  • SpringBoot对接小程序微信支付

    目录 前言 一、准备工作 2.1、企业微信小程序开通 2.1.1、获取开发者ID 2.1.2、开通支付功能 2.1.3、关联商户号 2.2、企业商户号的开通 2.2.1、获取商户号mch_id 2.2.2、获取商户API密钥mch_key 二、整体流程 三、后端项目搭建 3.1、统一下单 3.2、支付支付回调 3.3、问题排查 3.4、统一下

    2024年02月04日
    浏览(53)
  • 微信小程序对接微信支付所遇问题合集

    1. 后端使用微信官方sdk会简单很多,可以看看在第五个大标题上。 2. 相关文档汇总:         1、WxJava - MiniApp Java SDK 4.6.0 API         2、wechatpay-java/README.md at main · wechatpay-apiv3/wechatpay-java · GitHub         3、网络 | 微信开放文档         4、开发指引 - JSAPI支付 | 微信支付商

    2024年04月25日
    浏览(56)
  • java对接微信支付api3心得(小白易懂)

            公司做的项目中,需要支付功能,因为做的微信小程序项目,所以直接就用微信支付了,以前我也对接过微信支付,但以前没有java的sdk,并且还是用的xml报文,我们还得解析xml才行,所以麻烦的很,这次对接突然发现,微信已经提供了好多中语言的sdk,其中也包

    2024年02月03日
    浏览(50)
  • 微信支付-商家给小程序用户转账教程(全网最详细JAVA版)

    一.首页要开通商家转账到零钱功能,点产品设置,开启验密批量API,设置接口IP,这里的接口ip不是指用户的,而是你本机的 ip或服务器的ip 二.获取微信平台证书,注意微信平台证书和商户证书是有区别的,别弄混淆了 1.根据链接把获取平台证书的代码下载下来 https://github

    2024年02月11日
    浏览(48)
  • weixin-java-pay对接微信V3支付记录

    https://github.com/binarywang/weixin-java-pay-demo 这个demo里, 没有v3版本的配置, 这里记录一下 v3支付, 相对之前的版本来说, 更为安全, 也相对繁琐一些, 而且请求和响应都使用了json格式的数据 1. 配置 发起支付所需的配置有三个证书文件, 在商户后台申请 apiclient_cert.p12 apiclient_key.pem ap

    2024年02月11日
    浏览(55)
  • 校园二手书商城源代码基于微信小程序云开发,可在线支付提现,含详细配置教程

    校园二手书商城源代码基于微信小程序云开发,可在线支付提现,含详细配置教程 完整代码下载地址:校园二手书商城源代码基于微信小程序云开发 长话短说,下面直接说配置流程 1、下载导入 直接下载到本地,然后导入开发者工具 最近墙的厉害,也可使用蓝奏云下载 ht

    2023年04月24日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包