微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
了解一些注意事项后,具体实现
import com.weein.quickpass.core.config.WeChatConfig;
import com.weein.wcommon.utils.DateUtils;
import java.util.*;
/**
* @Des下载微信对账单
* @Author hzt
*/
public class DownloadBillUtils {
public static void main(String[] args) throws Exception {
String list = downloadBill();
System.out.println(list);
}
public static String downloadBill() throws Exception {
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
//这里面的value值,大家自己根据自己的微信商户参数配置一下
parameters.put("appid", WeChatConfig.APPID);//appid
parameters.put("mch_id", WeChatConfig.MCH_ID);//商户号
//parameters.put("device_info", "");//微信支付分配的终端设备号,填写此字段,只下载该设备号 的对账单
parameters.put("nonce_str", CreateNoncestr());//随机字符串
//下载对账单的日期,格式:20140603,当前日期前一天。
//parameters.put("bill_date", "20190329");
String billDate = DateUtils.format(DateUtils.addDay(new Date(), -1), DateUtils.FORMAT_DATE_02);
parameters.put("bill_date", billDate);
//bill_type:ALL返回当日所有订单信息,默认值SUCCESS返回当日成功支付的订单。REFUND,返回当日退款订单
parameters.put("bill_type", "ALL");//对账单类型
//这里调用方法生成微信需要的签名(这里是一个坑点,当时在做微信退款的时候因为签名不符合规范被卡一天时间)
String sign = createSign("utf-8", parameters);//签名
parameters.put("sign", sign);
//getRequestXml():是转xml的工具方法
String requestXml = getRequestXml(parameters);
//从微信下载对账单
String result = HttpUtil.httpsRequest(WeChatConfig.DOWNLOAD_BILL_URL, "POST", requestXml);
return result;
}
/**
* 随机字符串
*
* @return
*/
public static String CreateNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
}
/**
* 编写签名
*
* @param charSet
* @param parameters
* @return
* @throws Exception
*/
public static String createSign(String charSet, SortedMap<Object, Object> parameters) throws Exception {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + WeChatConfig.KEY);
String sign = MD5Util.MD5Encode(sb.toString(), charSet).toUpperCase();
return sign;
}
/**
* 转为xml格式
*
* @param parameters
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
}
Do
public class WeChatBillDO implements Serializable {
private Integer id;
/**
* 交易时间
*/
private String tradeTime;
/**
* 公众账号ID
*/
private String appId;
/**
* 商户号
*/
private String mchId;
/**
* 子商户号
*/
private String mchAppId;
/**
* 设备号
*/
private String deviceInfo;
/**
* 微信订单号
*/
private String transactionId;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 用户标识
*/
private String openId;
/**
* 交易类型
*/
private String tradeType;
/**
* 交易状态
*/
private String tradeStatus;
/**
* 付款银行
*/
private String payBank;
/**
* 货币种类
*/
private String moneyType;
/**
* 应结订单金额
*/
private String orderPay;
/**
* 代金劵金额
*/
private String voucherAmount;
/**
* 微信退款单号
*/
private String refundNumber;
/**
* 商户退款单号
*/
private String outRefundNo;
/**
* 退款金额
*/
private String refundAmount;
/**
* 充值劵退款金额
*/
private String refundAmountVoucher;
/**
* 退款类型
*/
private String refundsType;
/**
* 退款状态
*/
private String refundsStatus;
/**
* 商品名称
*/
private String commodityName;
/**
* 商户数据包
*/
private String dataPacket;
/**
* 手续费
*/
private String serviceCharge;
/**
* 费率
*/
private String rate;
/**
* 订单金额
*/
private String orderAmount;
/**
* 申请退款金额
*/
private String applicationRefundAmount;
/**
* 费率备注
*/
private String rateNotes;
HttpUtil
import org.apache.log4j.Logger;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
public class HttpUtil {
private static Logger log = Logger.getLogger(HttpUtil.class);
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr){
try {
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:{}");
} catch (Exception e) {
System.out.println("https请求异常:{}");
}
return null;
}
}
MD5Util
import java.security.MessageDigest;
/**
*
* @Title: MD5Util.java
* @Description: MD5加密 工具类
*
* @author liufei
* @date 2019年3月18日
*/
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
定时下载微信对账单
/**
* 生成下载微信对账单并存入数据库任务
*/
@Scheduled(cron = "30 * * * * ?")
public void downloadWeChatBill() {
weChatBillService.saveWeChatBill();
}
将对账单存入数据库实现
public interface IWeChatBillService {
int saveWeChatBill();
}
public class WeChatBillServiceImpl implements IWeChatBillService {
private final static Logger log = LoggerFactory.getLogger(WeChatBillServiceImpl.class);
@Resource
IWeChatBillMapper weChatBillMapper;
@Override
public int saveWeChatBill() {
String result = null;
try {
result = DownloadBillUtils.downloadBill();
if (result.startsWith("<xml>")) {//查询日期为当天时,错误信息提示日期无效
log.info(result);
log.info("无订单");
return 0;
}
} catch (Exception e) {
e.printStackTrace();
}
int x = 0;
int i = result.indexOf("`");
int j = result.indexOf("总");
String substring = result.substring(i, j - 2);
String[] temp = substring.split(",``");
for (int k = 0; k < temp.length; k++) {
String[] payment = temp[k].replace("`", "").split(",");
WeChatBillDO bean = new WeChatBillDO();
bean.setTradeTime(payment[0]);
bean.setAppId(payment[1]);
bean.setMchId(payment[2]);
bean.setMchAppId(payment[3]);
bean.setDeviceInfo(payment[4]);
bean.setTransactionId(payment[5]);
bean.setOutTradeNo(payment[6]);
bean.setOpenId(payment[7]);
bean.setTradeType(payment[8]);
bean.setTradeStatus(payment[9]);
bean.setPayBank(payment[10]);
bean.setMoneyType(payment[11]);
bean.setOrderPay(payment[12]);
bean.setVoucherAmount(payment[13]);
bean.setRefundNumber(payment[14]);
bean.setOutRefundNo(payment[15]);
bean.setRefundAmount(payment[16]);
bean.setRefundAmountVoucher(payment[17]);
bean.setRefundsType(payment[18]);
bean.setRefundsStatus(payment[19]);
bean.setCommodityName(payment[20]);
bean.setDataPacket(payment[21]);
bean.setServiceCharge(payment[22]);
bean.setRate(payment[23]);
bean.setOrderAmount(payment[24]);
bean.setApplicationRefundAmount(payment[25]);
x = weChatBillMapper.insertSelective(bean);
}
return x;
}
}