欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA-小程序微信支付

程序员文章站 2022-06-14 23:27:51
...

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

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


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


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

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

JAVA-小程序微信支付

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

   /**
     * @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;
}

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


鼓励作者写出更好的技术文档,就请我喝一瓶哇哈哈哈哈哈哈哈。。
微信:
JAVA-小程序微信支付
支付宝:
JAVA-小程序微信支付
感谢一路支持我的人。。。。。
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