这个就直接贴代码了,反正暂时没法验证对错
错误工具类
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;
}
}
}
好了,我暂时没法验证是否有错。请谅解