微信APP支付实现记录

这个就直接贴代码了,反正暂时没法验证对错
错误工具类

import plugins.pay.wechat.domain.WechatPayError;

import java.util.HashMap;
import java.util.Map;

/**
 * 错误工具类,这里用枚举类会更好,不过我真的懒得去看文档改了,这个类也不是很重要,不搞了
 * @author zhang.shushan
 */
public class WeChatPayErrorUtil {

	private static Map<String, WechatPayError> errs = new HashMap<>();

	static {
		errs.put("NOAUTH", WechatPayError.builder().err_code_des("商户无此接口权限").err_cause("商户未开通此接口权限")
				.err_resolve("请商户前往申请此接口权限").build());
		errs.put("NOTENOUGH", WechatPayError.builder().err_code_des("余额不足").err_cause("用户帐号余额不足")
				.err_resolve("用户帐号余额不足,请用户充值或更换支付卡后再支付").build());
		errs.put("ORDERPAID", WechatPayError.builder().err_code_des("商户订单已支付").err_cause("商户订单已支付,无需重复操作")
				.err_resolve("商户订单已支付,无需更多操作").build());
		errs.put("ORDERCLOSED", WechatPayError.builder().err_code_des("订单已关闭").err_cause("当前订单已关闭,无法支付")
				.err_resolve("当前订单已关闭,请重新下单").build());
		errs.put("SYSTEMERROR",
				WechatPayError.builder().err_code_des("系统错误").err_cause("系统超时").err_resolve("系统异常,请用相同参数重新调用").build());
		errs.put("APPID_NOT_EXIST", WechatPayError.builder().err_code_des("APPID不存在").err_cause("参数中缺少APPID")
				.err_resolve("请检查APPID是否正确").build());
		errs.put("MCHID_NOT_EXIST", WechatPayError.builder().err_code_des("MCHID不存在").err_cause("参数中缺少MCHID")
				.err_resolve("请检查MCHID是否正确").build());
		errs.put("APPID_MCHID_NOT_MATCH", WechatPayError.builder().err_code_des("appid和mch_id不匹配")
				.err_cause("appid和mch_id不匹配").err_resolve("请确认appid和mch_id是否匹配").build());
		errs.put("LACK_PARAMS",
				WechatPayError.builder().err_code_des("缺少参数").err_cause("缺少必要的请求参数").err_resolve("请检查参数是否齐全").build());
		errs.put("OUT_TRADE_NO_USED", WechatPayError.builder().err_code_des("商户订单号重复 ").err_cause("同一笔交易不能多次提交")
				.err_resolve("请核实商户订单号是否重复提交").build());
		errs.put("SIGNERROR", WechatPayError.builder().err_code_des("签名错误").err_cause("参数签名结果不正确")
				.err_resolve("请核实商户订单号是否重复提交").build());
		errs.put("XML_FORMAT_ERROR", WechatPayError.builder().err_code_des("XML格式错误").err_cause("XML格式错误")
				.err_resolve("请检查XML参数格式是否正确").build());
		errs.put("REQUIRE_POST_METHOD", WechatPayError.builder().err_code_des("请使用post方法").err_cause("未使用post传递参数")
				.err_resolve("请检查请求参数是否通过post方法提交").build());
		errs.put("POST_DATA_EMPTY", WechatPayError.builder().err_code_des("post数据为空").err_cause("post数据不能为空")
				.err_resolve("请检查post数据是否为空").build());
		errs.put("NOT_UTF8", WechatPayError.builder().err_code_des("编码格式错误").err_cause("未使用指定编码格式")
				.err_resolve("请使用NOT_UTF8编码格式").build());

	}
	
	public static String getErrorMsg(String result_code) {
		// TODO Auto-generated method stub
		return errs.get(result_code).getErr_cause()+":"+errs.get(result_code).getErr_resolve();
	}
}

错误封装对象

public class WechatPayError {
	
	private String err_code_des;
	private String err_cause;
	private String err_resolve;
	
	public WechatPayError(String err_code_des,String err_cause,String err_resolve){
		this.err_code_des=err_code_des;
		this.err_cause=err_cause;
		this.err_resolve=err_resolve;
	}
	
	public static WechatPayErrorBuilder builder(){
		return new WechatPayErrorBuilder();
	}
	
	public static class WechatPayErrorBuilder{
		private String err_code_des;
		private String err_cause;
		private String err_resolve;
		
		public WechatPayErrorBuilder err_code_des(String err_code_des){
			this.err_code_des=err_code_des;
			return this;
		}
		
		public WechatPayErrorBuilder err_cause(String err_cause){
			this.err_cause=err_cause;
			return this;
		}
		
		public WechatPayErrorBuilder err_resolve(String err_resolve){
			this.err_resolve = err_resolve;
			return this;
		}

		@Override
		public String toString(){
			return "WechatPayErrorBuilder[err_code_des="+this.err_code_des+",err_cause="+this.err_cause+",err_resolve="+err_resolve+"]";
		}
		
		public WechatPayError build(){
			return new WechatPayError(this.err_code_des, this.err_cause, this.err_resolve);
		}
	}
	
	public String getErr_code_des() {
		return err_code_des;
	}
	public void setErr_code_des(String err_code_des) {
		this.err_code_des = err_code_des;
	}
	public String getErr_cause() {
		return err_cause;
	}
	public void setErr_cause(String err_cause) {
		this.err_cause = err_cause;
	}
	public String getErr_resolve() {
		return err_resolve;
	}
	public void setErr_resolve(String err_resolve) {
		this.err_resolve = err_resolve;
	}

}

XML工具类

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * 2018/7/3
 */
public final class WXPayXmlUtil {
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
}

微信订单返回信息封装

/**
 * 微信预支付订单返回信息
 * @author zhang.shushan
 * @date 2018年1月19日
 */
public class WechatPreOrderInfo {

	//返回状态码
	private String return_code;
	//返回信息
	private String return_msg;
	//应用APPID
	private String appid;
	//商户号
	private String mch_id;
	//设备号
	private String device_info;
	//随机字符串
	private String nonce_str;
	//签名
	private String sign;
	//业务结果
	private String result_code;
	//错误代码
	private String err_code;
	//错误代码描述
	private String err_code_des;
	//交易类型
	private String trade_type;
	//预支付交易会话标识
	private String prepay_id;
	//扫码支付返回字段,用于生成二维码
	private String code_url;
	
	/**
	 * 连接是否成功
	 * @return
	 */
	public boolean isContact(){
		return "SUCCESS".equals(this.return_code);
	}
	
	/**
	 * 业务是否成功
	 * @return
	 */
	public boolean isSuccess(){
		if(isContact()){
			return "SUCCESS".equals(this.result_code);
		}
		return false;
	}

	/**
	 * 固定字段
	 * @return
	 */
	public String getPackage(){
		return "Sign=WXPay";
	}

	/**
	 * 时间戳
	 * @return
	 */
	public Long getTimestamp(){
		return System.currentTimeMillis()/1000;
	}

	public String getReturn_code() {
		return return_code;
	}

	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}

	public String getReturn_msg() {
		return return_msg;
	}

	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}

	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 getDevice_info() {
		return device_info;
	}

	public void setDevice_info(String device_info) {
		this.device_info = device_info;
	}

	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 getResult_code() {
		return result_code;
	}

	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}

	public String getErr_code() {
		return err_code;
	}

	public void setErr_code(String err_code) {
		this.err_code = err_code;
	}

	public String getErr_code_des() {
		return err_code_des;
	}

	public void setErr_code_des(String err_code_des) {
		this.err_code_des = err_code_des;
	}

	public String getTrade_type() {
		return trade_type;
	}

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

	public String getPrepay_id() {
		return prepay_id;
	}

	public void setPrepay_id(String prepay_id) {
		this.prepay_id = prepay_id;
	}

	public String getCode_url() {
		return code_url;
	}

	public void setCode_url(String code_url) {
		this.code_url = code_url;
	}

}

微信支付返回信息封装

/**
 * 微信支付返回信息类
 * @author zhang.shushan
 * @date 2018年1月24日
 */
public class WechatPayRet {

	//返回状态码
	private String return_code;
	//返回信息
	private String return_msg;
	//应用ID
	private String appid;
	//商户号
	private String mch_id;
	//设备号
	private String device_info;
	//随机字符串
	private String nonce_str;
	//签名
	private String sign;
	//业务结果
	private String result_code;
	//错误代码
	private String err_code;
	//错误代码描述
	private String err_des;
	//用户标识
	private String openid;
	//是否关注公众账号
	private String is_subscribe;
	//交易类型
	private String trade_type;
	//付款银行
	private String bank_type;
	//总金额
	private int total_fee;
	//货币种类
	private String fee_type;
	//现金支付金额
	private int cash_fee;
	//现金支付货币类型
	private String cash_fee_type;
	//代金券金额
	private int coupon_fee;
	//代金券使用数量
	private int coupon_count;
	//微信支付订单号
	private String transaction_id;
	//商户订单号
	private String out_trade_no;
	//商家数据包
	private String attach;
	//支付完成时间
	@JSONField(format="yyyy-MM-dd HH:mm:ss")
	private Date time_end;
	
	/**
	 * 连接是否成功
	 * @return
	 */
	public boolean isContact(){
		return "SUCCESS".equals(this.return_code);
	}
	
	/**
	 * 业务是否成功
	 * @return
	 */
	public boolean isSuccess(){
		if(isContact()){
			return "SUCCESS".equals(this.result_code);
		}
		return false;
	}

	public String getReturn_code() {
		return return_code;
	}

	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}

	public String getReturn_msg() {
		return return_msg;
	}

	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}

	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 getDevice_info() {
		return device_info;
	}

	public void setDevice_info(String device_info) {
		this.device_info = device_info;
	}

	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 getResult_code() {
		return result_code;
	}

	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}

	public String getErr_code() {
		return err_code;
	}

	public void setErr_code(String err_code) {
		this.err_code = err_code;
	}

	public String getErr_des() {
		return err_des;
	}

	public void setErr_des(String err_des) {
		this.err_des = err_des;
	}

	public String getOpenid() {
		return openid;
	}

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

	public String getIs_subscribe() {
		return is_subscribe;
	}

	public void setIs_subscribe(String is_subscribe) {
		this.is_subscribe = is_subscribe;
	}

	public String getTrade_type() {
		return trade_type;
	}

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

	public String getBank_type() {
		return bank_type;
	}

	public void setBank_type(String bank_type) {
		this.bank_type = bank_type;
	}

	public int getTotal_fee() {
		return total_fee;
	}

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

	public String getFee_type() {
		return fee_type;
	}

	public void setFee_type(String fee_type) {
		this.fee_type = fee_type;
	}

	public int getCash_fee() {
		return cash_fee;
	}

	public void setCash_fee(int cash_fee) {
		this.cash_fee = cash_fee;
	}

	public String getCash_fee_type() {
		return cash_fee_type;
	}

	public void setCash_fee_type(String cash_fee_type) {
		this.cash_fee_type = cash_fee_type;
	}

	public int getCoupon_fee() {
		return coupon_fee;
	}

	public void setCoupon_fee(int coupon_fee) {
		this.coupon_fee = coupon_fee;
	}

	public int getCoupon_count() {
		return coupon_count;
	}

	public void setCoupon_count(int coupon_count) {
		this.coupon_count = coupon_count;
	}

	public String getTransaction_id() {
		return transaction_id;
	}

	public void setTransaction_id(String transaction_id) {
		this.transaction_id = transaction_id;
	}

	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 String getAttach() {
		return attach;
	}

	public void setAttach(String attach) {
		this.attach = attach;
	}

	public Date getTime_end() {
		return time_end;
	}

	public void setTime_end(Date time_end) {
		this.time_end = time_end;
	}
	
}

下单信息封装

import com.alibaba.fastjson.JSON;

import java.io.IOException;

/**
 * 微信支付信息实体类
 * @date 2018年1月19日
 */
public class WechatPayInfo {

    // 应用ID
    private String appid;
    // 商户号
    private String mch_id;
    // 终端设备号(门店号或收银设备ID),默认请传"WEB"
    private String device_info = "WEB";
    // 随机字符串
    private String nonce_str;
    // 签名,信息填充完整后使用工具类设置签名
    private String sign;
    // 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    private String sign_type = "MD5";
    /**
     * 商品描述交易字段格式根据不同的应用场景按照以下格式:
     * APP——需传入应用市场上的APP名字-实际商品名称,
     * 天天爱消除-游戏充值。
     */
    private String body;
    // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    private String attach;
    // 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一
    private String out_trade_no;
    // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见
    private String fee_type = "CNY";
    // 订单总金额,单位为分
    private int total_fee;
    //订单详情,用於单个商品的优惠,设置成对象的json
    private String detail;
    // 用户端实际ip
    private String spbill_create_ip;
    // 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    private String notify_url;
    // 交易类型
    private String trade_type;
    // 该字段用于统一下单时上报场景信息,目前支持上报实际门店信息,设置成对象的json
    private String scene_info;
    //微信公众号支付必填
    private String openid;
    //限制使用信用卡支付
    private String limit_pay;

    public String getOpenid() {
        return openid;
    }

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

    public String getLimit_pay() {
        return limit_pay;
    }

    public void setLimit_pay(String limit_pay) {
        this.limit_pay = limit_pay;
    }

    /**
     * 设置限制使用信用卡
     */
    public void configureLimitPay(){
        this.limit_pay="no_credit";
    }

    /**
     * 设置必填的自定义参数
     *
     * @param body
     * @param out_trade_no
     * @param total_fee
     * @param suffix
     * @param trade_type
     * @throws IOException
     */
    public WechatPayInfo(String body, String out_trade_no, String suffix,
                         int total_fee,
                         String trade_type, String spbill_create_ip) throws IOException {
        this.body = "开放平台APP name" + "-" + body;
        this.out_trade_no = out_trade_no;
        this.notify_url = "商户后台回调接口地址";
        this.trade_type = trade_type;
        this.spbill_create_ip = spbill_create_ip;
        this.total_fee = total_fee;
    }

    /**
     * 设置必填的自定义参数
     *
     * @param body
     * @param out_trade_no
     * @param total_fee
     * @param notify_url
     * @param trade_type
     * @throws IOException
     */
    public WechatPayInfo(String body, String out_trade_no,
                         int total_fee, String notify_url,
                         String trade_type, String spbill_create_ip) throws IOException {
        this.body = 开放平台APP name + "-" + body;
        this.out_trade_no = out_trade_no;
        this.notify_url = notify_url;
        this.trade_type = trade_type;
        this.spbill_create_ip = spbill_create_ip;
        this.total_fee = total_fee;
    }

    /**
     * 设置单品优惠信息
     *
     * @param orderInfos
     */
    public void configDetail(OrderInfos orderInfos) {
        this.detail = JSON.toJSONString(orderInfos);
    }

    /**
     * 设置实际门店信息
     *
     * @param sceneInfo
     */
    public void configScene_info(SceneInfo sceneInfo) {
        this.scene_info = JSON.toJSONString(sceneInfo);
    }

    /**
     * 设置必填的自定义参数
     *
     * @param body
     * @param out_trade_no
     * @param total_fee
     * @param notify_url
     * @param trade_type
     * @throws IOException
     */
    public void configMajorParameters(String body, String out_trade_no,
                                      int total_fee, String notify_url,
                                      String trade_type, String spbill_create_ip) throws IOException {
        this.body = "开放平台APP name" + "-" + body;
        this.out_trade_no = out_trade_no;
        this.notify_url = notify_url;
        this.trade_type = trade_type;
        this.spbill_create_ip = spbill_create_ip;
        this.total_fee = total_fee;
    }

    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 getDevice_info() {
        return device_info;
    }

    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }

    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 getSign_type() {
        return sign_type;
    }

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

    public String getBody() {
        return body;
    }

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

    public String getAttach() {
        return attach;
    }

    public void setAttach(String attach) {
        this.attach = attach;
    }

    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 String getFee_type() {
        return fee_type;
    }

    public void setFee_type(String fee_type) {
        this.fee_type = fee_type;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    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;
    }

    public String getScene_info() {
        return scene_info;
    }

    public void setScene_info(String scene_info) {
        this.scene_info = scene_info;
    }

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

}

下单和回调接口

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alipay.api.internal.util.StringUtils;
import plugins.pay.wechat.domain.WechatPayInfo;
import plugins.pay.wechat.domain.WechatPayRet;
import plugins.pay.wechat.domain.WechatPreOrderInfo;
import plugins.pay.wechat.sdk.WXPayXmlUtil;
import plugins.pay.wechat.utils.WeChatPayErrorUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URI;
import java.security.MessageDigest;
import java.util.*;

@RestController
@Api(tags="支付接口")
@RequestMapping("/Indent")
public class H5IndentController {

    @GetMapping("/addWechat")
    @ApiOperation(value = "微信新增订单", notes = "微信新增订单")
    public Map<String,Object> addWechat(HttpServletRequest request, HttpServletResponse response){

        // 返回结果
        Map<String,Object> map = null;

        try {
            //创建微信预支付返回对象,主要是为了返回给APP时数据结构不变
            WechatPreOrderInfo wechatPreOrderInfo = new WechatPreOrderInfo();

            //创建预支付请求信息,18是价格
            WechatPayInfo payInfo = new WechatPayInfo("会员充值", "商户订单id",
                    18, "商户回调接口地址",
                    "APP", request.getRemoteAddr());
            // 向微信发起请求,这里需要调起各种配置信息
            wechatPreOrderInfo = preOrder(payInfo);
            // 加签
            map = sign4App(wechatPreOrderInfo);

            // TODO 保存数据

            // 给返回结果添加订单id
            map.put("indentId", "商户订单id");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    // 微信APP收款回调
    @PostMapping("/wechatNotify")
    public void wechatNotify(HttpServletRequest request, HttpServletResponse response)throws Exception{

        //解析数据
        WechatPayRet ret = parseRequest(request);

        String return_code = ret.getReturn_code();

        if(return_code.equals("SUCCESS")){
            // 开始进行订单处理,ret.getOut_trade_no()就是商户订单号,delIndent()是业务处理逻辑自己完善
            String result = delIndent(ret.getOut_trade_no());

            System.out.println("----------------------------------------------------------------------交易完成!");

            if(result.equals("success")){
                //成功之后要应答,让微信别调了。但是还是会有重入的可能,所以必须做好数据锁
                echo(response);
            }else{
                return;
            }
        }else{
            return;
        }
    }

    // APP支付二次加签
    public static Map<String,Object> sign4App(WechatPreOrderInfo preOrderInfo)throws Exception{
        TreeMap<String, Object> map = new TreeMap<>();
        map.put("appid", preOrderInfo.getAppid());
        map.put("partnerid", preOrderInfo.getMch_id());
        map.put("prepayid", preOrderInfo.getPrepay_id());
        map.put("package", preOrderInfo.getPackage());
        map.put("noncestr", generator(32, "QWERTYUIPASDFGHJKLZXCVBNMqwertyuipasdfghjklzxcvbnm01234567890".toCharArray()));
        map.put("timestamp", preOrderInfo.getTimestamp());
        map.put("sign", signMap(map));
        return map;
    }

    // 加签
    private static String signMap(TreeMap<String,Object> map) throws Exception{
        Set<String> keys = map.keySet();
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            sb.append(key).append("=").append(map.get(key)).append("&");
        }
        sb.append("key").append("=").append("商户号密钥");
        return DigestUtils.md5Hex(sb.toString()).toUpperCase();
    }

    // 生成数据和字母组成的随机字符串
    private static String generator(int count, char[] arr) {
        if (count <= 0) {
            count = 6;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            double d = Math.random();
            int index = (int) Math.floor(d * arr.length);
            sb.append(arr[index]);
        }

        return sb.toString();
    }

    public static WechatPreOrderInfo preOrder(WechatPayInfo payInfo) throws Exception {

        if(payInfo.getTrade_type().equals("JSAPI")&& StringUtils.isEmpty(payInfo.getOpenid())){
            throw new RuntimeException("公众号支付openid不能为空,请填入正确的openid");
        }

        payInfo.setAppid("开放平台APPID");
        payInfo.setMch_id("商户平台商户号");

        payInfo.setNonce_str(generator(32, "QWERTYUIPASDFGHJKLZXCVBNMqwertyuipasdfghjklzxcvbnm01234567890".toCharArray()).toUpperCase());
        payInfo.setSign(generateSign(payInfo));
        Document doc = convertMap2Xml(getSignMap(payInfo, WechatPayInfo.class));
        URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com").setPath("/pay/unifiedorder")
                .build();
        String xml = connectWithXMLByPost(uri, doc);
        WechatPreOrderInfo info = (WechatPreOrderInfo) convertXml2Bean(xml, WechatPreOrderInfo.class);
        if (!info.isContact()) {
            String msg = info.getReturn_code() + ":" + info.getReturn_msg();
            msg = new String(msg.getBytes("iso-8859-1"), "utf-8");
            throw new RuntimeException(msg);
        }
        if (!info.isSuccess()) {
            String msg = info.getResult_code() + ":" + WeChatPayErrorUtil.getErrorMsg(info.getErr_code());
            throw new RuntimeException(msg);
        }
        return info;
    }

    // xml字符串转对象
    public static Object convertXml2Bean(String xmlString, Class<?> clazz) {
        Document document = null;
        try {
            document = DocumentHelper.parseText(xmlString);
        } catch (DocumentException e) {
            throw new RuntimeException("获取Document异常" + xmlString);
        } // 获取根节点
        return convertXml2Bean2(document, clazz);
    }

    // xml文档Document转对象
    @SuppressWarnings("unchecked")
    public static Object convertXml2Bean2(Document document, Class<?> clazz) {
        Map<String,String> map = new HashMap<>();
        // 获取根节点
        org.dom4j.Element root = document.getRootElement();
        try {
            List<org.dom4j.Element> properties = root.elements();
            for (org.dom4j.Element pro : properties) {
                String propName = pro.getName();
                String propValue = pro.getText();
                map.put(propName, propValue);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        //处理map里的JSON字符串字段,防止解析错误
        Map<String,Object> objMap = new TreeMap<>();
        Set<String> keys = map.keySet();
        for (String key : keys) {
            String str = map.get(key);

            try {
                //如果是JSON字符串,则转换成对象,再添加到objMap中
                objMap.put(key, JSON.parse(str));
            } catch (JSONException e) {
                //如果不是JSON字符串,则直接添加到objMap中
                objMap.put(key, str);
            } catch (Exception e){
                //其余错误抛出
                e.printStackTrace();
            }

        }

        return JSON.parseObject(JSON.toJSONString(map),clazz);
    }

    // 使用post方法发送xml
    public static String connectWithXMLByPost(URI uri, Document doc) throws Exception {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(uri);
            post.addHeader("Content-Type", "text/xml;charset=UTF-8");
            StringEntity xml = new StringEntity(doc.asXML(), "UTF-8");
            post.setEntity(xml);
            try (CloseableHttpResponse response = httpClient.execute(post)) {
                HttpEntity entity = response.getEntity();
                return EntityUtils.toString(entity);
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    // 将获取的Map转换成xml
    public static Document convertMap2Xml(Map<String,Object> map){
        Document doc = DocumentHelper.createDocument();
        try {
            org.dom4j.Element root = doc.addElement("xml");
            Set<String> keys = map.keySet();
            for (String key : keys) {
                org.dom4j.Element ele = root.addElement(key);
                ele.addCDATA(map.get(key).toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return doc;
    }

    // 设置签名
    private static String generateSign(Object obj) throws Exception {
        TreeMap<String, Object> map = getSignMap(obj, obj.getClass());
        return signMap(map);
    }

    // 获取按顺序整理好的非空值字段
    @SuppressWarnings("unchecked")
    private static TreeMap<String, Object> getSignMap(Object obj, Class<?> clz) throws Exception {
        if (obj == null) {
            throw new RuntimeException("支付对象不能为空");
        }
        Field[] fields = clz.getDeclaredFields();

        // 使用treeMap将字段按要求排序
        TreeMap<String, Object> map = new TreeMap<>();
        for (Field field : fields) {
            map.put(field.getName(), clz.getMethod("get" + uperFirst(field.getName())).invoke(obj));
        }

        // 使用fastjson过滤掉null的字段,爱死fastjson了
        String json = JSON.toJSONString(map);
        map = JSON.parseObject(json, TreeMap.class);
        return map;
    }

    // 首字母大写
    private static String uperFirst(String name) {
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }

    // 应答微信回调
    public static void echo(HttpServletResponse response) throws Exception {
        response.setContentType("application/xml");
        ServletOutputStream os = response.getOutputStream();
        os.print("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
    }

    // 解析微信后台返回的数据
    public static WechatPayRet parseRequest(HttpServletRequest request) throws Exception {
        String xml = readXmlFromRequest(request);
        Map map = xmlToMap(xml);
        WechatPayRet ret = new WechatPayRet();
        ret.setReturn_code((String) map.get("result_code"));
        ret.setOut_trade_no((String) map.get("out_trade_no"));
        return ret;
    }

    // XML格式字符串转换为Map
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.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) {
            getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

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

    // 从request读取xml
    public static String readXmlFromRequest(HttpServletRequest request) {
        StringBuilder xmlSb = new StringBuilder();
        try(
                ServletInputStream in = request.getInputStream();
                InputStreamReader inputStream = new InputStreamReader(in);
                BufferedReader buffer = new BufferedReader(inputStream);
        ){
            String line = null;
            while((line=buffer.readLine())!=null){
                xmlSb.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return xmlSb.toString();
    }

    // MD5加密
    public static String md5(String key) {
        System.out.println(key);
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        try {
            byte[] btInput = key.getBytes("UTF-8");
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            String s = new String(str);
            s = s.toLowerCase();
            return s;
        } catch (Exception e) {
            return null;
        }
    }
}

好了,我暂时没法验证是否有错。请谅解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章