企业微信开放平台 https://developer.work.weixin.qq.com/document/path/91201
微信开放平台 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
1, 企业微信消息推送接口对接
#企业微信相关配置
#wecom:
#企业ID
corpid=ww97923728bc80d3d5
#应用的凭证密钥
corpsecret=7lrL-8JsIGA6reMQe8dN2x4z5PjTB5Ya6CHMVvWdReA
#获取access_token的URL
accessTokenUrl=https://qyapi.weixin.qq.com/cgi-bin/gettoken
#发送应用信息URL
sendMessageUrl=https://qyapi.weixin.qq.com/cgi-bin/message/send
#企业应用的id
agentid=1000005
#获取userid的URL
useridUrl=https://qyapi.weixin.qq.com/cgi-bin/user/getuserid
@Service
public class AddVisitCouponListener implements ApplicationListener<AddVisitCouponModel> {
@Autowired
public RedisClient redisClient;
@Autowired
public UserInfoDAO userInfoDAO;
public static String accToken = "CACHE_ACCESSTOKEN";
@Async
@Override
public void onApplicationEvent(AddVisitCouponModel event) {
String corpid = RequestRule_ParamUtils.getValue("corpid");//企业ID
String corpsecret = RequestRule_ParamUtils.getValue("corpsecret");//应用的凭证密钥
String accessTokenUrl = RequestRule_ParamUtils.getValue("accessTokenUrl");
String sendMessageUrl = RequestRule_ParamUtils.getValue("sendMessageUrl");
String agentid = RequestRule_ParamUtils.getValue("agentid");
String useridUrl = RequestRule_ParamUtils.getValue("useridUrl");
String msg = RequestRule_ParamUtils.getValue("msg");
WriteLog.writeLog("YNHX", "onApplicationEvent", "corpid=" + corpid + "||corpsecret=" + corpsecret + "||agentid=" + agentid);
WriteLog.writeLog("YNHX", "onApplicationEvent", "msg=" +msg);
//推送消息到企业微信
String accessToken = "";
String expires = "";//accessToken有效期
//1, 获取access_token
// String corpid = "企业ID";
// String corpsecret = "应用的凭证密钥";
/**
入参:
corpid 是 企业ID,获取方式参考:术语说明-corpid
corpsecret 是 应用的凭证密钥,注意应用需要是启用状态,获取方式参考:术语说明-secret
出参:
{
"errcode": 0,
"errmsg": "ok",
"access_token": "accesstoken000001",
"expires_in": 7200
}
*/
//String getAccessToken = " https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET";
String getAccessToken = " https://qyapi.weixin.qq.com/cgi-bin/gettoken";
Map<String, Object> map = new HashMap<>();
map.put("corpid", corpid);
map.put("corpsecret", corpsecret);
HttpResponse execute = HttpUtil.createGet(accessTokenUrl).form(map).execute();
Map<String, Object> respMap = JSONUtil.toBean(execute.body(), Map.class);
if (StrUtil.equals(MapUtil.getStr(respMap, "errcode"), "0")) {
accessToken = MapUtil.getStr(respMap, "access_token");
expires = MapUtil.getStr(respMap, "expires_in");//access_token有效期
} else {
throw new BusinessException("获取access_token错误,返回信息: " + MapUtil.getStr(respMap, "errmsg"));
}
if (redisClient.exists(accToken)) {
accessToken = redisClient.get(accToken);
WriteLog.writeLog("YNHX", "onApplicationEvent", "获取缓存accessToken=" + accessToken);
} else {
redisClient.set(accToken, accessToken, Convert.toInt(expires));
WriteLog.writeLog("YNHX", "onApplicationEvent", "缓存accessToken=" + accessToken);
}
//2, 手机号获取userid
//2.1 根据人员信息获取手机号
String userPhone = "";
String userid = "";
UserInfo byId = userInfoDAO.findById(Convert.toLong(event.getUserid()));
userPhone = byId.getTel();
if (StrUtil.isBlank(userPhone)) {
WriteLog.writeLog("YNHX", "onApplicationEvent", "获取手机号失败!userid=" + event.getUserid());
throw new BusinessException("获取手机号失败!userid=" + event.getUserid());
}
//2.2 根据手机号获取企业微信useid
/**
{
"mobile": "15877397212"
}
*/
/* //String getUseridUrl = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=ACCESS_TOKEN";
String getUseridUrl = useridUrl + "?access_token=" + accessToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("mobile", userPhone);
HttpResponse execute1 = HttpUtil.createPost(getUseridUrl).body(jsonObject).execute();
Map<String, Object> getUseridUrlRespMap = JSONUtil.toBean(execute1.body(), Map.class);
if (StrUtil.equals(MapUtil.getStr(getUseridUrlRespMap, "errcode"), "0")) {
userid = MapUtil.getStr(getUseridUrlRespMap, "userid");
} else {
throw new BusinessException("根据手机号获取userid失败");
}*/
JSONObject jsonObject = new JSONObject();
jsonObject.put("mobile", userPhone);
ResponseEntity<String> getHttpResponseEntity = getHttpResponseEntity(accessToken, useridUrl, jsonObject);
Map<String, Object> getUseridUrlRespMap = JSONUtil.toBean(getHttpResponseEntity.getBody(), Map.class);
if (StrUtil.equals(MapUtil.getStr(getUseridUrlRespMap, "errcode"), "0")) {
userid = MapUtil.getStr(getUseridUrlRespMap, "userid");
WriteLog.writeLog("YNHX", "onApplicationEvent", "获取企业微信userid成功, userid=" + userid);
} else {
throw new BusinessException("根据手机号获取userid失败");
}
//3, 发送应用消息
//3.1 消息模板
/** 消息示例
{
"touser" : "UserID1|UserID2|UserID3", //可以输入多个userid
"toparty" : "PartyID1|PartyID2", // 部门ID列表
"totag" : "TagID1 | TagID2", //标签ID列表 touser/toparty/totag 不可同时为空
"msgtype" : "text",
"agentid" : 1, // 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值
"text" : {
"content" : "你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看<a href=\"http://work.weixin.qq.com\">邮件中心视频实况</a>,聪明避开排队。"
},
"safe":0,
"enable_id_trans": 0,
"enable_duplicate_check": 0,
"duplicate_check_interval": 1800
}
*/
/* JSONObject jsonObject1 = new JSONObject();// 消息模板:以下四个字段必填
jsonObject1.put("touser", userid);
jsonObject1.put("msgtype", "text");
jsonObject1.put("agentid", agentid);
jsonObject1.put("text", new JSONObject("content", "测试: 有您的访客申请。\n请前去一卡通访客券进行审批!"));
//String postContentUrl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN";
String postContentUrl = sendMessageUrl + "?access_token=" + accessToken;
HttpResponse execute2 = HttpUtil.createPost(postContentUrl).body(jsonObject1).execute();
Map<String, Object> postContentMap = JSONUtil.toBean(execute2.body(), Map.class);
if (StrUtil.equals(MapUtil.getStr(postContentMap, "errcode"), "0")) {
// { "errcode": 0, "errmsg": "ok", "msgid": "fcLc6UhB2absSaoEDgOVFHFJna2x-M_EI9XtgcXRzXLEWWyDf0xSlq8pytd6r4FbJzcfTo2eWn8fVaR5ovd2WA" }
String msgid = MapUtil.getStr(postContentMap, "msgid");
WriteLog.writeLog("YNHX", "onApplicationEvent", "消息推送成功, 消息msgid=" + msgid);
} else {
throw new BusinessException("消息推送失败");
}*/
JSONObject sendJsonObject = new JSONObject();// 消息模板:以下四个字段必填
sendJsonObject.put("touser", userid);
sendJsonObject.put("msgtype", "text");
sendJsonObject.put("agentid", agentid);
JSONObject jsonObject2 = new JSONObject();
WriteLog.writeLog("YNHX", "onApplicationEvent", "msg=" +StrUtil.str(msg, "UTF-8"));
jsonObject2.put("content", "温馨提示:有您的福利劵审批代办,请打开工作台进入智慧后勤应用,点击福利券审批进行处理");//
sendJsonObject.put("text", jsonObject2);
ResponseEntity<String> httpResponseEntitySend = getHttpResponseEntity(accessToken, sendMessageUrl, sendJsonObject);
Map<String, Object> postContentMap = JSONUtil.toBean(httpResponseEntitySend.getBody(), Map.class);
if (StrUtil.equals(MapUtil.getStr(postContentMap, "errcode"), "0")) {
// { "errcode": 0, "errmsg": "ok", "msgid": "fcLc6UhB2absSaoEDgOVFHFJna2x-M_EI9XtgcXRzXLEWWyDf0xSlq8pytd6r4FbJzcfTo2eWn8fVaR5ovd2WA" }
String msgid = MapUtil.getStr(postContentMap, "msgid");
WriteLog.writeLog("YNHX", "onApplicationEvent", "消息推送成功, 消息msgid=" + msgid);
} else {
throw new BusinessException("消息推送失败");
}
}
public ResponseEntity<String> getHttpResponseEntity(String accToken, String url, JSONObject json) {
RestTemplate restTemplate = new RestTemplate();
// 1.设置请求头
HttpHeaders httpHeaders = new HttpHeaders();
//传递请求体时必须设置传递参数的格式,为Content-Type : application/json
httpHeaders.add("Content-Type", "application/json");
HttpEntity<String> httpEntity = new HttpEntity(json, httpHeaders);
// 拼接地址
StringBuilder sb = new StringBuilder(url).append("?").append("access_token=" + accToken);
WriteLog.writeLog("YNHX", "onApplicationEvent", "tokenUrl=" + sb);
url = sb.toString();
System.out.println("请求URL: " + url);
//返回类型,如果不确定返回类型,使用String接受,用fastjson处理Json字符串
return restTemplate.exchange(
url,
HttpMethod.POST,
httpEntity,
String.class);
}
2, 微信native支付接口
package com.datalook.nxykd.util.http.v3;
/**
* 微信支付常量配置
*/
public interface PayConstants {
String NATIVE_ORDER_KEY = "native_order_key_";
String NATIVE_ORDER_ID_KEY = "native_order_id_key_";
String NATIVE_ORDER_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";//微信支付地址
String RECONCILIATION_URL= "https://api.mch.weixin.qq.com/v3/bill/tradebill";//微信对账接口(获取下载账单接口)
String TRANSACTIONS_URL= "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/";//根据商户订单号查询支付结果接口地址
String MCH_ID = "1604562573";//商户号
String APP_ID = "wxd77863786f14c269";//appid
String MCH_SERIAL_NO = "294A37239D1AE510A1B3A4BEADA2119F6C3FBF1D";//商户证书序列号
String NOTIFY_URL = "https://www.baidu.com/";//支付成功回调地址
String API_V3KEY = "nxykdxzyysfc67432581234567890000";//api密钥
String PACKAGE = "Sign=WXPay";//签名固定字符串(微信要求的)
String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4zVdO+P6T1MWI\n" +
"auRDQUi1ZqUakqub+TCsqCMe3nfhPH7B4ikHOiDNtnxZEoJNedZgga+ZrFI8hPJi\n" +
"k8Eik/2hKbR85VvG641wtmW2PXYrAOMcPIpEWgZ4WzRd0nDWA0jouG+VWLuFAI3m\n" +
"DhfbQIKeaBlkDLMuj/Ss19Ep0Y1+YAHTHeQyV4Tzw1zY9D+hE6oWTBQeCB4MAkgp\n" +
"RJvxzTScQ6gO47MYQXSztuFk1j0bO+8peNKavmNgmwYN9HAoMt8hjKJicZg3LS6H\n" +
"xUw4w3lD+tbrw5P1oJ3aJeFfQkmdhhZbdk8B8OizFG20en/qVaR45bapeEM6zB/Y\n" +
"mMdATIaLAgMBAAECggEBAK3zk6BhnniWyo07WLu54u/lu7nEn8aEjH2pEs4GyL76\n" +
"C7MC6G3GGISqpmfNlVvKOLHEwP8dBpey6NlDjYO33ADs2djKqFYY+Z1EA3/EYMvf\n" +
"3OeCUQA/4WE/MI6z/MRmeSWeHmJtEK4ENI19bJVUJt9my6eykKNenAhqaZc8ijyO\n" +
"FZgyWDU6m+iRMP3slX49DwxEm8boII9oCR9IjM4VrciYtgbaEhPcjUOeXwlkSJ4n\n" +
"/xqG6DWa79hXrSSRHtPmttaZSpkK0hvts1DWNx443yA6hx1xXhSkTpK4EkkuiofV\n" +
"u1XyygpfGEz9tV3yOokVx6w0eoRMSKVGWeW0MCNMepkCgYEA7LJObPScsvDKpkg9\n" +
"SHNJjAmRo1AKObllAgQwKIa9EQMn6VluwaVQ4quEiyVRzvVmc2QZ08kdpLThj+cj\n" +
"3CuNURw7SAWDaFbArCGXlxJx035Rpm/K42jWPlRuscq+1GszT8RdMUQr329w9Ajx\n" +
"cUh6G2ywmKaTjh4LpqTTE+23VZUCgYEAx9+YhjYepPbagys/n1oqa/Y2bwwQyuHF\n" +
"+kjHOug3YFz4ixlVv8E8iGuWRmWMoV0HTBQo+Ufk0jpWAzZWl3XR7RA+aYmhf0fo\n" +
"0BtiNSES1YDVPAVd5NvX8TcQobVNW3/Ih/hoSorZuGuwUDw5xbW2mTpeqZrWNlg1\n" +
"/Pa+hqsPI58CgYEAuwhiZEIeGF+hIifvFPLUaO2ewVeb3OUcZa19iGh/uZOu0/sx\n" +
"Q58n/RTfFTyWGl1baU7uAaXn4KB1pEpW0cEwV6lhRpXksmvqr5mTuXJ/s0kbp9Kq\n" +
"lWvf7zy0g5Tbmepjj2Jw6V8e892ReaQfdQ56PnVyTg1Ch4hCWJnhmNu8hMkCgYAc\n" +
"rVUZ9IvjCVQCu+LN/0bU5KecuTNzmH8qUOTc69J+LfivJHxsHdKMlDskcI+D23Af\n" +
"l0jls03+Z8ZNga9c3de31rvNsqSMOYoKydMGYm9sWKa/jNmS64QJJOhq+zNVLHle\n" +
"IOL9xvxD/Jd4e5uiZVcElx5b4U5Yz8E3QUPG2KY8awKBgQC/G2kmcVkUo7iaKvTC\n" +
"Q7FwdkCp5xUYpxoqrvYs/fT/OEK5rU9MLdshD9jyEMjZgoZC+Soe2LnuNpkTMXlY\n" +
"YxXoqADUaFW2orbKJRLDSmSyFOl8O6STKUYviQeqzwhYBHmxVEmilnGecQt+Pqpz\n" +
"qtlvGvHcRHSdXQo7kDdInDZuMg=="
+ "-----END PRIVATE KEY-----\n";//商户私钥
String FAIL = "FAIL";
String SUCCESS= "SUCCESS";
String ALL= "ALL";
}
2.1, 微信native下单接口
package com.datalook.nxykd.test;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import com.datalook.nxykd.util.http.v3.PayConstants;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
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 java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* 微信POST请求测试类
*/
public class Test{
//商户私钥(拷贝apiclient_key.pem文件里-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----之间的内容)
public static String rsaPrivateKey ="MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4zVdO+P6T1MWI\n" +
"auRDQUi1ZqUakqub+TCsqCMe3nfhPH7B4ikHOiDNtnxZEoJNedZgga+ZrFI8hPJi\n" +
"k8Eik/2hKbR85VvG641wtmW2PXYrAOMcPIpEWgZ4WzRd0nDWA0jouG+VWLuFAI3m\n" +
"DhfbQIKeaBlkDLMuj/Ss19Ep0Y1+YAHTHeQyV4Tzw1zY9D+hE6oWTBQeCB4MAkgp\n" +
"RJvxzTScQ6gO47MYQXSztuFk1j0bO+8peNKavmNgmwYN9HAoMt8hjKJicZg3LS6H\n" +
"xUw4w3lD+tbrw5P1oJ3aJeFfQkmdhhZbdk8B8OizFG20en/qVaR45bapeEM6zB/Y\n" +
"mMdATIaLAgMBAAECggEBAK3zk6BhnniWyo07WLu54u/lu7nEn8aEjH2pEs4GyL76\n" +
"C7MC6G3GGISqpmfNlVvKOLHEwP8dBpey6NlDjYO33ADs2djKqFYY+Z1EA3/EYMvf\n" +
"3OeCUQA/4WE/MI6z/MRmeSWeHmJtEK4ENI19bJVUJt9my6eykKNenAhqaZc8ijyO\n" +
"FZgyWDU6m+iRMP3slX49DwxEm8boII9oCR9IjM4VrciYtgbaEhPcjUOeXwlkSJ4n\n" +
"/xqG6DWa79hXrSSRHtPmttaZSpkK0hvts1DWNx443yA6hx1xXhSkTpK4EkkuiofV\n" +
"u1XyygpfGEz9tV3yOokVx6w0eoRMSKVGWeW0MCNMepkCgYEA7LJObPScsvDKpkg9\n" +
"SHNJjAmRo1AKObllAgQwKIa9EQMn6VluwaVQ4quEiyVRzvVmc2QZ08kdpLThj+cj\n" +
"3CuNURw7SAWDaFbArCGXlxJx035Rpm/K42jWPlRuscq+1GszT8RdMUQr329w9Ajx\n" +
"cUh6G2ywmKaTjh4LpqTTE+23VZUCgYEAx9+YhjYepPbagys/n1oqa/Y2bwwQyuHF\n" +
"+kjHOug3YFz4ixlVv8E8iGuWRmWMoV0HTBQo+Ufk0jpWAzZWl3XR7RA+aYmhf0fo\n" +
"0BtiNSES1YDVPAVd5NvX8TcQobVNW3/Ih/hoSorZuGuwUDw5xbW2mTpeqZrWNlg1\n" +
"/Pa+hqsPI58CgYEAuwhiZEIeGF+hIifvFPLUaO2ewVeb3OUcZa19iGh/uZOu0/sx\n" +
"Q58n/RTfFTyWGl1baU7uAaXn4KB1pEpW0cEwV6lhRpXksmvqr5mTuXJ/s0kbp9Kq\n" +
"lWvf7zy0g5Tbmepjj2Jw6V8e892ReaQfdQ56PnVyTg1Ch4hCWJnhmNu8hMkCgYAc\n" +
"rVUZ9IvjCVQCu+LN/0bU5KecuTNzmH8qUOTc69J+LfivJHxsHdKMlDskcI+D23Af\n" +
"l0jls03+Z8ZNga9c3de31rvNsqSMOYoKydMGYm9sWKa/jNmS64QJJOhq+zNVLHle\n" +
"IOL9xvxD/Jd4e5uiZVcElx5b4U5Yz8E3QUPG2KY8awKBgQC/G2kmcVkUo7iaKvTC\n" +
"Q7FwdkCp5xUYpxoqrvYs/fT/OEK5rU9MLdshD9jyEMjZgoZC+Soe2LnuNpkTMXlY\n" +
"YxXoqADUaFW2orbKJRLDSmSyFOl8O6STKUYviQeqzwhYBHmxVEmilnGecQt+Pqpz\n" +
"qtlvGvHcRHSdXQo7kDdInDZuMg==";
public static void main(String[] args) throws Exception{
//2, 微信native下单接口
String url = PayConstants.NATIVE_ORDER_URL;//微信下单接口
//时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
//2, 微信下单
JSONObject jsonObject = new JSONObject();
jsonObject.put("mchid", PayConstants.MCH_ID);
jsonObject.put("appid", PayConstants.APP_ID);
jsonObject.put("notify_url", PayConstants.NOTIFY_URL);
jsonObject.put("description", "测试商户:一卡通充值");
jsonObject.put("out_trade_no", "ykt-"+timestamp);//随机订单号
JSONObject json = new JSONObject();
json.put("total", 1);//订单金额, 单位:分
json.put("currency", "CNY");//币种, 境内商户默认都是人民币
jsonObject.put("amount", json);//随机订单号
//拼签名串
StringBuffer sb =new StringBuffer();
sb.append("POST").append("\n");
sb.append("/v3/pay/transactions/native").append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append(jsonObject).append("\n");
System.out.println("签名原串:"+sb.toString());
//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey)));
System.out.println("签名sign值:"+sign);
//拼装http头的Authorization内容
String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+PayConstants.MCH_ID+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+PayConstants.MCH_SERIAL_NO+"\"";
System.out.println("authorization值:"+authorization);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost =new HttpPost(url);
//设置头部
httpPost.addHeader("Accept","application/json");
httpPost.addHeader("Content-Type","application/json ");
httpPost.addHeader("Authorization", authorization);
HttpEntity Entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON);
httpPost.setEntity(Entity);
//获取返回内容
CloseableHttpResponse response = httpclient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
String rescontent =new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
// //获取返回的http header
// Header[] headers = response.getAllHeaders();
// int i =0;
// while (i < headers.length) {
// System.out.println(headers[i].getName() +": " + headers[i].getValue());
// i++;
// }
//
// //验证微信支付返回签名
// String Wtimestamp = response.getHeaders("Wechatpay-Timestamp")[0].getValue();
// String Wnonce = response.getHeaders("Wechatpay-Nonce")[0].getValue();
// String Wsign = response.getHeaders("Wechatpay-Signature")[0].getValue();
// String serial = response.getHeaders("Wechatpay-Serial")[0].getValue();
// //拼装待签名串
// StringBuffer ss =new StringBuffer();
// ss.append(Wtimestamp).append("\n");
// ss.append(Wnonce).append("\n");
// ss.append(rescontent).append("\n");
//
// //java.security.Signature.getInstance()
// //验证签名
// if(verifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), rsaPublicKeyFile)) {
// System.out.println("签名验证成功");
// }else {
// System.out.println("签名验证失败");
// }
//
// EntityUtils.consume(httpEntity);
// response.close();
/* try {
//商户号
String mchid ="1604562573";
String appid ="wxd77863786f14c269";
//证书序列号
String serial_no ="294A37239D1AE510A1B3A4BEADA2119F6C3FBF1D";//2023/07/26至2028/07/24
//商户私钥(拷贝apiclient_key.pem文件里-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----之间的内容)
//String rsaPrivateKey ="";
//微信支付平台公钥
String rsaPublicKeyFile = "D:\\n8_dingzhi\\n8-0303-sp3\\00-new\\n8-escp\\escp-manage\\src\\main\\resources\\apiclient_cert.p12";
//时间戳
String timestamp = Long.toString(System.currentTimeMillis()/1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
*//* //图片文件
String filePath ="";//文件路径
File file =new File(filePath);
String filename = file.getName();//文件名
String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256*//*
JSONObject jsonObject = new JSONObject();
jsonObject.put("mchid", mchid);
jsonObject.put("appid", appid);
jsonObject.put("notify_url","https://www.baidu.com/");
jsonObject.put("description", "商品描述:一卡通微信充值");
jsonObject.put("out_trade_no", "YKT"+System.currentTimeMillis()+"");//随机订单号
JSONObject json = new JSONObject();
json.put("total", 1);
json.put("currency", "CNY");//币种, 境内商户默认都是人民币
jsonObject.put("amount", json);//随机订单号
//拼签名串
StringBuffer sb =new StringBuffer();
sb.append("POST").append("\n");
sb.append("/v3/pay/transactions/native").append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
// sb.append("{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}").append("\n");//接口入参
sb.append(jsonObject).append("\n");
System.out.println("签名原串:"+sb.toString());
//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey)));
System.out.println("签名sign值:"+sign);
//拼装http头的Authorization内容
String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchid+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+serial_no+"\"";
System.out.println("authorization值:"+authorization);
//接口URL
String url ="https://api.mch.weixin.qq.com/v3/pay/transactions/native";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost =new HttpPost(url);
//设置头部
httpPost.addHeader("Accept","application/json");
httpPost.addHeader("Content-Type","application/json ");
httpPost.addHeader("Authorization", authorization);
// //创建MultipartEntityBuilder
// MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
// //设置boundary
// multipartEntityBuilder.setBoundary("");
// multipartEntityBuilder.setCharset(Charset.forName("UTF-8"));
// //设置meta内容
// //multipartEntityBuilder.addTextBody("meta","{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}", ContentType.APPLICATION_JSON);
// multipartEntityBuilder.addTextBody("meta","", ContentType.APPLICATION_JSON);
// //设置图片内容
// multipartEntityBuilder.addBinaryBody("file", file, ContentType.create("image/jpg"), filename);
// //放入内容
// httpPost.setEntity(multipartEntityBuilder.build());
HttpEntity Entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON);
httpPost.setEntity(Entity);
//获取返回内容
CloseableHttpResponse response = httpclient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
String rescontent =new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
// //获取返回的http header
// Header[] headers = response.getAllHeaders();
// int i =0;
// while (i < headers.length) {
// System.out.println(headers[i].getName() +": " + headers[i].getValue());
// i++;
// }
//
// //验证微信支付返回签名
// String Wtimestamp = response.getHeaders("Wechatpay-Timestamp")[0].getValue();
// String Wnonce = response.getHeaders("Wechatpay-Nonce")[0].getValue();
// String Wsign = response.getHeaders("Wechatpay-Signature")[0].getValue();
// String serial = response.getHeaders("Wechatpay-Serial")[0].getValue();
// //拼装待签名串
// StringBuffer ss =new StringBuffer();
// ss.append(Wtimestamp).append("\n");
// ss.append(Wnonce).append("\n");
// ss.append(rescontent).append("\n");
//
// //java.security.Signature.getInstance()
// //验证签名
// if(verifyRSA(ss.toString(), Base64.decodeBase64(Wsign.getBytes()), rsaPublicKeyFile)) {
// System.out.println("签名验证成功");
// }else {
// System.out.println("签名验证失败");
// }
//
// EntityUtils.consume(httpEntity);
response.close();
}catch (Exception e) {
System.out.println("发送POST请求异常!" + e);
e.printStackTrace();
}*/
}
public static byte[] InputStreamTOByte(InputStream in)throws IOException {
int BUFFER_SIZE =4096;
ByteArrayOutputStream outStream =new ByteArrayOutputStream();
byte[] data =new byte[BUFFER_SIZE];
int count = -1;
while((count = in.read(data,0,BUFFER_SIZE)) != -1)
outStream.write(data,0, count);
data =null;
byte[] outByte = outStream.toByteArray();
outStream.close();
return outByte;
}
public static byte[] signRSA(String data, String priKey) throws Exception {
//签名的类型
Signature sign = Signature.getInstance("SHA256withRSA");
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes("UTF-8"));
return sign.sign();
}
public static boolean verifyRSA(String data, byte[] sign, String pubKey) throws Exception{
if(data == null || sign == null || pubKey == null){
return false;
}
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream(pubKey);
Certificate c = cf.generateCertificate(in);
in.close();
PublicKey publicKey = c.getPublicKey();
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
return signature.verify(sign);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
2.2, 微信支付结果查询接口
package com.datalook.nxykd.test;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.RandomUtil;
import com.datalook.nxykd.util.http.v3.PayConstants;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.After;
import org.junit.Before;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* 微信支付结果查询接口
*/
public class FindPayStatusByOuttradenoTest {
private static CloseableHttpClient httpClient = HttpClients.createDefault();
private static AutoUpdateCertificatesVerifier verifier;
public static String rsaPrivateKey ="MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4zVdO+P6T1MWI\n" +
"auRDQUi1ZqUakqub+TCsqCMe3nfhPH7B4ikHOiDNtnxZEoJNedZgga+ZrFI8hPJi\n" +
"k8Eik/2hKbR85VvG641wtmW2PXYrAOMcPIpEWgZ4WzRd0nDWA0jouG+VWLuFAI3m\n" +
"DhfbQIKeaBlkDLMuj/Ss19Ep0Y1+YAHTHeQyV4Tzw1zY9D+hE6oWTBQeCB4MAkgp\n" +
"RJvxzTScQ6gO47MYQXSztuFk1j0bO+8peNKavmNgmwYN9HAoMt8hjKJicZg3LS6H\n" +
"xUw4w3lD+tbrw5P1oJ3aJeFfQkmdhhZbdk8B8OizFG20en/qVaR45bapeEM6zB/Y\n" +
"mMdATIaLAgMBAAECggEBAK3zk6BhnniWyo07WLu54u/lu7nEn8aEjH2pEs4GyL76\n" +
"C7MC6G3GGISqpmfNlVvKOLHEwP8dBpey6NlDjYO33ADs2djKqFYY+Z1EA3/EYMvf\n" +
"3OeCUQA/4WE/MI6z/MRmeSWeHmJtEK4ENI19bJVUJt9my6eykKNenAhqaZc8ijyO\n" +
"FZgyWDU6m+iRMP3slX49DwxEm8boII9oCR9IjM4VrciYtgbaEhPcjUOeXwlkSJ4n\n" +
"/xqG6DWa79hXrSSRHtPmttaZSpkK0hvts1DWNx443yA6hx1xXhSkTpK4EkkuiofV\n" +
"u1XyygpfGEz9tV3yOokVx6w0eoRMSKVGWeW0MCNMepkCgYEA7LJObPScsvDKpkg9\n" +
"SHNJjAmRo1AKObllAgQwKIa9EQMn6VluwaVQ4quEiyVRzvVmc2QZ08kdpLThj+cj\n" +
"3CuNURw7SAWDaFbArCGXlxJx035Rpm/K42jWPlRuscq+1GszT8RdMUQr329w9Ajx\n" +
"cUh6G2ywmKaTjh4LpqTTE+23VZUCgYEAx9+YhjYepPbagys/n1oqa/Y2bwwQyuHF\n" +
"+kjHOug3YFz4ixlVv8E8iGuWRmWMoV0HTBQo+Ufk0jpWAzZWl3XR7RA+aYmhf0fo\n" +
"0BtiNSES1YDVPAVd5NvX8TcQobVNW3/Ih/hoSorZuGuwUDw5xbW2mTpeqZrWNlg1\n" +
"/Pa+hqsPI58CgYEAuwhiZEIeGF+hIifvFPLUaO2ewVeb3OUcZa19iGh/uZOu0/sx\n" +
"Q58n/RTfFTyWGl1baU7uAaXn4KB1pEpW0cEwV6lhRpXksmvqr5mTuXJ/s0kbp9Kq\n" +
"lWvf7zy0g5Tbmepjj2Jw6V8e892ReaQfdQ56PnVyTg1Ch4hCWJnhmNu8hMkCgYAc\n" +
"rVUZ9IvjCVQCu+LN/0bU5KecuTNzmH8qUOTc69J+LfivJHxsHdKMlDskcI+D23Af\n" +
"l0jls03+Z8ZNga9c3de31rvNsqSMOYoKydMGYm9sWKa/jNmS64QJJOhq+zNVLHle\n" +
"IOL9xvxD/Jd4e5uiZVcElx5b4U5Yz8E3QUPG2KY8awKBgQC/G2kmcVkUo7iaKvTC\n" +
"Q7FwdkCp5xUYpxoqrvYs/fT/OEK5rU9MLdshD9jyEMjZgoZC+Soe2LnuNpkTMXlY\n" +
"YxXoqADUaFW2orbKJRLDSmSyFOl8O6STKUYviQeqzwhYBHmxVEmilnGecQt+Pqpz\n" +
"qtlvGvHcRHSdXQo7kDdInDZuMg==";
@Before
public void setup() {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(PayConstants.PRIVATE_KEY);
//使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
@After
public void after() throws IOException {
httpClient.close();
}
public static void main(String[] args) throws Exception{
String reconciliationUrl = PayConstants.TRANSACTIONS_URL;//查询订单接口
String out_trade_no = "YKT-967wcp1691147868";
String url = "?mchid=" + PayConstants.MCH_ID;
reconciliationUrl = reconciliationUrl + out_trade_no + url;
System.out.println(reconciliationUrl);
//拼签名串
Map<String, Object> str = getsign("GET", "/v3/pay/transactions/out-trade-no/"+ out_trade_no + url, "");
System.out.println("签名原串:" + str.get("signStr"));
System.out.println("签名sign值:" + str.get("sign"));
System.out.println("authorization值:" + str.get("authorization"));
HttpGet httpGet = new HttpGet(reconciliationUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(str.get("authorization")));
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
//返回内容:{"amount":{"payer_currency":"CNY","total":1},"appid":"wxd77863786f14c269","mchid":"1604562573","out_trade_no":"YKT-967wcp1691147868","promotion_detail":[],"scene_info":{"device_id":""},"trade_state":"NOTPAY","trade_state_desc":"订单未支付"}
}
/**
* 获取签名串
*
* @param type GET/POST
* @param url 接口地址:GET请求要带query参数(/v3/bill/tradebill?bill_date=2023-07-31&bill_type=ALL),POST参数只接口参数(/v3/pay/transactions/native)
* timestamp 时间戳(System.currentTimeMillis() / 1000)
* nonce_str 32位随机字符串
* @param bodystr 接口入参:GET请求的这个参数为空, POST请求的这个字段为接口入参
* @return
*/
public static Map getsign(String type, String url, String bodystr) throws Exception {
Map<String, Object> map = new HashMap<>();
//时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
//拼签名串
StringBuffer sb = new StringBuffer();
sb.append(type).append("\n");
sb.append(url).append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append(bodystr).append("\n");
System.out.println("签名原串:" + sb.toString());
String sign = new String(Base64.encodeBase64(signRSA(sb.toString(), rsaPrivateKey)));
String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
map.put("signStr", sb);
map.put("sign", sign);
map.put("authorization", authorization);
return map;
}
public static byte[] InputStreamTOByte(InputStream in) throws IOException {
int BUFFER_SIZE = 4096;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
byte[] outByte = outStream.toByteArray();
outStream.close();
return outByte;
}
public static byte[] signRSA(String data, String priKey) throws Exception {
//签名的类型
Signature sign = Signature.getInstance("SHA256withRSA");
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes("UTF-8"));
return sign.sign();
}
public static boolean verifyRSA(String data, byte[] sign, String pubKey) throws Exception {
if (data == null || sign == null || pubKey == null) {
return false;
}
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream(pubKey);
Certificate c = cf.generateCertificate(in);
in.close();
PublicKey publicKey = c.getPublicKey();
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
return signature.verify(sign);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
2.3, 微信支付对账接口
package com.datalook.nxykd.test;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.datalook.event.IEventManager;
import com.datalook.event.model.handle.BilldownloadModel;
import com.datalook.nxykd.model.BilldownloadData;
import com.datalook.nxykd.util.http.v3.PayConstants;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.After;
import org.junit.Before;
import javax.annotation.Resource;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* api3接口 微信对账 微信GET请求测试类
*/
public class ReconciliationTest {
private static CloseableHttpClient httpClient = HttpClients.createDefault();
private static AutoUpdateCertificatesVerifier verifier;
public static String rsaPrivateKey ="MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4zVdO+P6T1MWI\n" +
"auRDQUi1ZqUakqub+TCsqCMe3nfhPH7B4ikHOiDNtnxZEoJNedZgga+ZrFI8hPJi\n" +
"k8Eik/2hKbR85VvG641wtmW2PXYrAOMcPIpEWgZ4WzRd0nDWA0jouG+VWLuFAI3m\n" +
"DhfbQIKeaBlkDLMuj/Ss19Ep0Y1+YAHTHeQyV4Tzw1zY9D+hE6oWTBQeCB4MAkgp\n" +
"RJvxzTScQ6gO47MYQXSztuFk1j0bO+8peNKavmNgmwYN9HAoMt8hjKJicZg3LS6H\n" +
"xUw4w3lD+tbrw5P1oJ3aJeFfQkmdhhZbdk8B8OizFG20en/qVaR45bapeEM6zB/Y\n" +
"mMdATIaLAgMBAAECggEBAK3zk6BhnniWyo07WLu54u/lu7nEn8aEjH2pEs4GyL76\n" +
"C7MC6G3GGISqpmfNlVvKOLHEwP8dBpey6NlDjYO33ADs2djKqFYY+Z1EA3/EYMvf\n" +
"3OeCUQA/4WE/MI6z/MRmeSWeHmJtEK4ENI19bJVUJt9my6eykKNenAhqaZc8ijyO\n" +
"FZgyWDU6m+iRMP3slX49DwxEm8boII9oCR9IjM4VrciYtgbaEhPcjUOeXwlkSJ4n\n" +
"/xqG6DWa79hXrSSRHtPmttaZSpkK0hvts1DWNx443yA6hx1xXhSkTpK4EkkuiofV\n" +
"u1XyygpfGEz9tV3yOokVx6w0eoRMSKVGWeW0MCNMepkCgYEA7LJObPScsvDKpkg9\n" +
"SHNJjAmRo1AKObllAgQwKIa9EQMn6VluwaVQ4quEiyVRzvVmc2QZ08kdpLThj+cj\n" +
"3CuNURw7SAWDaFbArCGXlxJx035Rpm/K42jWPlRuscq+1GszT8RdMUQr329w9Ajx\n" +
"cUh6G2ywmKaTjh4LpqTTE+23VZUCgYEAx9+YhjYepPbagys/n1oqa/Y2bwwQyuHF\n" +
"+kjHOug3YFz4ixlVv8E8iGuWRmWMoV0HTBQo+Ufk0jpWAzZWl3XR7RA+aYmhf0fo\n" +
"0BtiNSES1YDVPAVd5NvX8TcQobVNW3/Ih/hoSorZuGuwUDw5xbW2mTpeqZrWNlg1\n" +
"/Pa+hqsPI58CgYEAuwhiZEIeGF+hIifvFPLUaO2ewVeb3OUcZa19iGh/uZOu0/sx\n" +
"Q58n/RTfFTyWGl1baU7uAaXn4KB1pEpW0cEwV6lhRpXksmvqr5mTuXJ/s0kbp9Kq\n" +
"lWvf7zy0g5Tbmepjj2Jw6V8e892ReaQfdQ56PnVyTg1Ch4hCWJnhmNu8hMkCgYAc\n" +
"rVUZ9IvjCVQCu+LN/0bU5KecuTNzmH8qUOTc69J+LfivJHxsHdKMlDskcI+D23Af\n" +
"l0jls03+Z8ZNga9c3de31rvNsqSMOYoKydMGYm9sWKa/jNmS64QJJOhq+zNVLHle\n" +
"IOL9xvxD/Jd4e5uiZVcElx5b4U5Yz8E3QUPG2KY8awKBgQC/G2kmcVkUo7iaKvTC\n" +
"Q7FwdkCp5xUYpxoqrvYs/fT/OEK5rU9MLdshD9jyEMjZgoZC+Soe2LnuNpkTMXlY\n" +
"YxXoqADUaFW2orbKJRLDSmSyFOl8O6STKUYviQeqzwhYBHmxVEmilnGecQt+Pqpz\n" +
"qtlvGvHcRHSdXQo7kDdInDZuMg==";
// @Resource
// private IEventManager eventManager;
@Before
public void setup() {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(PayConstants.PRIVATE_KEY);
//使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
@After
public void after() throws IOException {
httpClient.close();
}
public static void main(String[] args) throws Exception {
String reconciliationUrl = PayConstants.RECONCILIATION_URL;//微信对账接口(获取下载账单接口)
/* //时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);*/
String yesterday = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd");//获取前一天的账单日期
//reconciliationUrl += "?bill_date=" + yesterday + "&bill_type=" + PayConstants.ALL;
String url = "?bill_date=" + yesterday + "&bill_type=" + PayConstants.SUCCESS;
reconciliationUrl += url;
System.out.println(reconciliationUrl);
//拼签名串
/* StringBuffer sb = new StringBuffer();
sb.append("GET").append("\n");
sb.append("/v3/bill/tradebill?bill_date=2023-07-31&bill_type=ALL").append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append("").append("\n");
System.out.println("签名原串:" + sb.toString());*/
Map<String, Object> str = getsign("GET", "/v3/bill/tradebill"+ url, "");
System.out.println("签名原串:" + str.get("signStr"));
//计算签名
//String sign = new String(Base64.encodeBase64(signRSA(Convert.toStr(str.get("sign")), rsaPrivateKey)));
System.out.println("签名sign值:" + str.get("sign"));
//拼装http头的Authorization内容
//String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
System.out.println("authorization值:" + str.get("authorization"));
//CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(reconciliationUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(str.get("authorization")));
//HttpEntity Entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON);
// HttpParams httpParams = new BasicHttpParams();
// httpParams.setParameter("bill_date", yesterday);
// httpParams.setParameter("bill_type", PayConstants.SUCCESS);
//
// httpGet.setParams(httpParams);
CloseableHttpResponse response = httpClient.execute(httpGet);
//获取返回内容
//CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
/**
* //获取下载账单地址
* {
* "download_url": "https://api.mch.weixin.qq.com/v3/billdownload/file?token=6XIv5TUPto7pByrTQKhd6kwvyKLG2uY2wMMR8cNXqaA_Cv_isgaUtBzp4QtiozLO",
* "hash_type": "SHA1",
* "hash_value": "8823044c286bea726f149bfcfce0b0318122d755"
* }
*/
JSONObject jsonRescontent = JSONUtil.parseObj(rescontent);
if (jsonRescontent.containsKey("download_url")) {
String downloadUrl = jsonRescontent.getStr("download_url");
Map<String, Object> downloadStr = getsign("GET", downloadUrl.replace("https://api.mch.weixin.qq.com", ""), "");
//下载账单
System.out.println("签名原串:" + downloadStr.get("signStr"));
System.out.println("签名sign值:" + downloadStr.get("sign"));
System.out.println("authorization值:" + downloadStr.get("authorization"));
httpGet = new HttpGet(downloadUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(downloadStr.get("authorization")));
response = httpClient.execute(httpGet);
//获取返回内容
//CloseableHttpResponse response = httpClient.execute(httpGet);
httpEntity = response.getEntity();
rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
List<BilldownloadData> dataList = new ArrayList<>();
String[] split = StrUtil.split(rescontent, "\n");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
if (i == 0){
continue;
}
String[] splitBilldownload = StrUtil.split(split[i], ",");
if (splitBilldownload.length > 19){
BilldownloadData billdownload = new BilldownloadData();
// 交易时间 格式为yyyy-MM-dd HH:MM:SS
billdownload.setBillDateTime(StrUtil.replace(splitBilldownload[0], "`",""));
// 公众账号ID
billdownload.setAppid(StrUtil.replace(splitBilldownload[1], "`",""));
// 商户号
billdownload.setMchid(StrUtil.replace(splitBilldownload[2], "`",""));
// 特约商户号
billdownload.setTyMchid(StrUtil.replace(splitBilldownload[3], "`",""));
// 设备号
billdownload.setDeviceInfoCode(StrUtil.replace(splitBilldownload[4], "`",""));
// 微信订单号
billdownload.setWxPayOrder(StrUtil.replace(splitBilldownload[5], "`",""));
// 商户订单号
billdownload.setOrderno(StrUtil.replace(splitBilldownload[6], "`",""));
// 用户标识
billdownload.setUserIdentify(StrUtil.replace(splitBilldownload[7], "`",""));
// 交易类型
billdownload.setPayType(StrUtil.replace(splitBilldownload[8], "`",""));
// 交易状态
billdownload.setPayStatus(StrUtil.replace(splitBilldownload[9], "`",""));
// 付款银行
billdownload.setPayBank(StrUtil.replace(splitBilldownload[10], "`",""));
// 货币种类
billdownload.setCurrencyType(StrUtil.replace(splitBilldownload[11], "`",""));
// 应结订单金额
billdownload.setPayTotal(StrUtil.replace(splitBilldownload[12], "`",""));//(订单金额)
// 代金券金额
billdownload.setVoucherTotal(StrUtil.replace(splitBilldownload[13], "`",""));
// 商品名称
billdownload.setDescription(StrUtil.replace(splitBilldownload[14], "`",""));
// 商户数据包(商户传入的该笔订单)
billdownload.setPayertotal(StrUtil.replace(splitBilldownload[15], "`",""));
// 手续费
billdownload.setServiceCharge(StrUtil.replace(splitBilldownload[16], "`",""));
// 费率
billdownload.setRate(StrUtil.replace(splitBilldownload[17], "`",""));
// 订单金额
billdownload.setOrdernoTotal(StrUtil.replace(splitBilldownload[18], "`",""));
// 费率备注
billdownload.setRateRemark(StrUtil.replace(splitBilldownload[19], "`",""));
dataList.add(billdownload);
}
}
System.out.println(dataList);
// /*List<BilldownloadData> list = new ArrayList<>();
// //读取文件流内容
// BufferedReader reader = null;
// try {
// System.out.println("以行为单位读取文件内容,一次读一整行:");
// reader = new BufferedReader(new InputStreamReader((httpEntity.getContent())));
// String tempString = null;
// String[] strings = null;
//
// int line = 1;
// //一次读入一行,直到读入null为文件结束
// while ((tempString = reader.readLine()) != null){
// //显示行号
// System.out.println("line " + line + ": " + tempString);
//
// if (line == 1){
// System.out.println();
// line++;
// }else {
// strings = StrUtil.split(tempString, ",");
//
// BilldownloadData billdownloadData = new BilldownloadData();
// billdownloadData.setBillDateTime(strings[0]);
// billdownloadData.setAppid(strings[1]);
// billdownloadData.setMchid(strings[2]);
// billdownloadData.setTyMchid(strings[3]);
// billdownloadData.setDeviceInfoCode(strings[4]);
// billdownloadData.setWxPayOrder(strings[5]);
// billdownloadData.setOrderno(strings[6]);
// billdownloadData.setUserIdentify(strings[7]);
// billdownloadData.setPayType(strings[8]);
// billdownloadData.setPayStatus(strings[9]);
// billdownloadData.setPayBank(strings[10]);
// billdownloadData.setCurrencyType(strings[11]);
// billdownloadData.setPayTotal(strings[12]);
// billdownloadData.setVoucherTotal(strings[13]);
// billdownloadData.setDescription(strings[14]);
// billdownloadData.setPayertotal(strings[15]);
// billdownloadData.setServiceCharge(strings[16]);
// billdownloadData.setRate(strings[17]);
// billdownloadData.setOrdernoTotal(strings[18]);
// billdownloadData.setRateRemark(strings[19]);
//
// list.add(billdownloadData);
// line++;
// }
//
// }
// reader.close();
// } catch (IOException e) {
// e.printStackTrace();
// } finally {
// if (reader != null){
// try {
// reader.close();
// } catch (IOException e1) {
// }
// }
// }*/
billdownloadModel.setListData(list);
eventManager.publishHandle(billdownloadModel);
// //System.out.println(list);
} else {
throw new Exception(jsonRescontent.getStr("message"));
}
}
/**
* 获取签名串
*
* @param type GET/POST
* @param url 接口地址:GET请求要带query参数(/v3/bill/tradebill?bill_date=2023-07-31&bill_type=ALL),POST参数只接口参数(/v3/pay/transactions/native)
* timestamp 时间戳(System.currentTimeMillis() / 1000)
* nonce_str 32位随机字符串
* @param bodystr 接口入参:GET请求的这个参数为空, POST请求的这个字段为接口入参
* @return
*/
public static Map getsign(String type, String url, String bodystr) throws Exception {
Map<String, Object> map = new HashMap<>();
//时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
//拼签名串
StringBuffer sb = new StringBuffer();
sb.append(type).append("\n");
sb.append(url).append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append(bodystr).append("\n");
System.out.println("签名原串:" + sb.toString());
String sign = new String(Base64.encodeBase64(signRSA(sb.toString(), rsaPrivateKey)));
String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
map.put("signStr", sb);
map.put("sign", sign);
map.put("authorization", authorization);
return map;
}
public static byte[] signRSA(String data, String priKey) throws Exception {
//签名的类型
Signature sign = Signature.getInstance("SHA256withRSA");
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes("UTF-8"));
return sign.sign();
}
public static byte[] InputStreamTOByte(InputStream in) throws IOException {
int BUFFER_SIZE = 4096;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
byte[] outByte = outStream.toByteArray();
outStream.close();
return outByte;
}
/**
* 以行为单位读取文件,常用于读面向行的格式化文件
* @param fileName 文件名
*/
public static void readFileByLines(String fileName){
File file = new File(fileName);
BufferedReader reader = null;
try {
System.out.println("以行为单位读取文件内容,一次读一整行:");
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line = 1;
//一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null){
//显示行号
System.out.println("line " + line + ": " + tempString);
line++;
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
}
3, 微信支付对接service
package com.datalook.nxykd.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.datalook.daointerface.IAccountInfoDAO;
import com.datalook.daointerface.ISysUserDAO;
import com.datalook.daointerface.IUserInfoDAO;
import com.datalook.exception.BusinessException;
import com.datalook.model.SysUser;
import com.datalook.model.UserInfo;
import com.datalook.model.WxNativePay;
import com.datalook.nxykd.dao.INativePayDao;
import com.datalook.nxykd.model.BilldownloadData;
import com.datalook.nxykd.model.NativePayCallback;
import com.datalook.nxykd.service.WXnativePayService;
import com.datalook.nxykd.util.PayUtils;
import com.datalook.nxykd.util.http.v3.PayConstants;
import com.datalook.nxykd.util.http.v3.TreadeStateEnum;
import com.datalook.service.BaseService;
import com.datalook.service.UserInfoService;
import com.datalook.util.common.WriteLog;
import com.datalook.util.redis.RedisClient;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.swetake.util.Qrcode;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class WXnativePayServiceImpl extends BaseService implements WXnativePayService {
private static final Logger logger = LoggerFactory.getLogger(WXnativePayServiceImpl.class);
@Autowired
private IAccountInfoDAO accountInfoDAO;
@Autowired
private INativePayDao nativePayDao;
@Autowired
private RedisClient redisClient;
@Autowired
private IUserInfoDAO userInfoDAO;//档案信息dao
@Autowired
private UserInfoService userInfoService;
@Override
@Transactional(rollbackFor = Exception.class)
public String getWxPayQRcode(WxNativePay nativePay) {
String idserial = nativePay.getIdserial();//工号
String cardid = nativePay.getCardid();//获取卡物理号
Integer total = nativePay.getTotal();//单位:分
List<UserInfo> userInfoList = userInfoDAO.findByProperty("idserial", idserial);
UserInfo userInfo = new UserInfo();
if (userInfoList != null && userInfoList.size() > 0) {
userInfo = userInfoList.get(0);
nativePay.setUserid(userInfo.getId());
}
//1, 保存订单 NativePay-订单表
nativePay.setOrderno("YKT-" + RandomUtil.randomString(6) + Long.toString(System.currentTimeMillis() / 1000));//一卡通生成订单号
nativePay.setDescription("人员:" + idserial + "一卡通微信充值");
nativePay.setStatus("0");//订单初始化状态
nativePay.setAppid(PayConstants.APP_ID);
nativePay.setMechid(PayConstants.MCH_ID);
try {
nativePayDao.save(nativePay);
} catch (Exception e) {
System.out.println(e);
}
//将cardid与订单表的主键id缓存redis
redisClient.hset(PayConstants.NATIVE_ORDER_KEY, nativePay.getCardid(), Convert.toStr(nativePay.getId()), 60 * 60);
String resBody = "";
String codeUrl = "";
try {
resBody = PayUtils.nativePayOrder(nativePay);
nativePay.setCreateOrderTime(new Timestamp(System.currentTimeMillis()));
nativePayDao.update(nativePay);
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException("微信下单接口调用出错");
}
if (StrUtil.isNotBlank(resBody)) {
JSONObject parse = JSONUtil.parseObj(resBody);
if (parse.containsKey("code") && !StrUtil.equals(parse.getStr("code"), "SUCCESS")) {
logger.error(parse.getStr("message"));
throw new BusinessException("微信下单接口返回错误");
} else {
codeUrl = parse.containsKey("code_url") ? parse.getStr("code_url") : "";
}
}
//3, 将返回值链接转化为二维码返回
//String content = "www.baidu.com";//测试使用链接
String content = codeUrl;//将微信订单下单成功链接转化为二维码展示
String imgType = "jpg";
int size = 15;
BufferedImage bufferedImage = qRCodeCommon(content, imgType, size);
return BufferedImageToBase64(bufferedImage);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String wxNativeCallback(NativePayCallback nativePayCallback) {
String flag = "0";
//拼装待签名串
StringBuffer ss = new StringBuffer();
ss.append(nativePayCallback.getTimestamp()).append("\n");
ss.append(nativePayCallback.getNonce()).append("\n");
ss.append(nativePayCallback.getJsonObjectData()).append("\n");
//验签
logger.info("微信支付回调接口验签...");
boolean b = PayUtils.signVerify(nativePayCallback.getSerial(), ss.toString(), nativePayCallback.getSignature());
if (!b) {
logger.error("wxNativeCallback:微信支付回调接口验签失败!nativePayCallback验签数据:" + nativePayCallback);
flag = "1";//验签失败
return flag;
}
logger.info("微信支付回调接口验签成功...下面进行数据解密");
//数据解密,如果这里报错,就一定是APIv3密钥错误
String resource = nativePayCallback.getJsonObjectData().containsKey("resource") ? nativePayCallback.getJsonObjectData().getStr("resource") : "";
JSONObject jsonObject = JSONUtil.parseObj(resource);
String associated_data = jsonObject.containsKey("associated_data") ? jsonObject.getStr("associated_data") : "";
String nonce = jsonObject.containsKey("nonce") ? jsonObject.getStr("nonce") : "";
String ciphertext = jsonObject.containsKey("ciphertext") ? jsonObject.getStr("ciphertext") : "";
// String associated_data = requestMap2.get("associated_data");
// String nonce = requestMap2.get("nonce");
// String ciphertext = requestMap2.get("ciphertext");
AesUtil aesUtil = new AesUtil(PayConstants.API_V3KEY.getBytes());
String aes = "";
try {
aes = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
} catch (GeneralSecurityException e) {
e.printStackTrace();
logger.error("数据解密出错(检查APIv3密钥是否错误)");
flag = "2";//解密数据出错
return flag;
}
logger.info("数据解密成功aes:" + aes);
JSONObject jsonaes = JSONUtil.parseObj(aes);
WxNativePay nativePay = new WxNativePay();
Map<String, Object> map = new HashMap<>();
map.put("order", jsonaes.getStr("out_trade_no"));
List<WxNativePay> nativePays = nativePayDao.sqlExecuteFind(" SELECT * FROM wx_native_pay where order = :order ", map);
if (nativePays != null && nativePays.size() > 0) {
nativePay = nativePays.get(0);
} else {
logger.error("订单不存在");
flag = "3";
return flag;
}
nativePay.setWxtransaction(jsonaes.getStr("transaction_id"));
nativePay.setSuccessTime(new Timestamp(DateTime.parse(jsonaes.getStr("success_time"))));
String payerTotal = JSONUtil.parseObj(jsonaes.getStr("amount")).getStr("payer_total");//用户支付金额
String total = JSONUtil.parseObj(jsonaes.getStr("amount")).getStr("total");//订单总金额
if (!StrUtil.equals(payerTotal, total)) {
logger.info("支付金额与订单金额不一致||用户支付金额:" + payerTotal + "==订单总金额:" + total);
}
nativePay.setTotal(Convert.toInt(total));
nativePay.setPayertotal(Convert.toInt(payerTotal));
nativePay.setStatus(TreadeStateEnum.getStatus(jsonaes.getStr("trade_state")));
//若支付成功, 根据订单号将订单状态修改
nativePayDao.update(nativePay);
redisClient.hset(PayConstants.NATIVE_ORDER_ID_KEY, Convert.toStr(nativePay.getId()), TreadeStateEnum.getStatus(jsonaes.getStr("trade_state")), 60 * 60);
if (!StrUtil.equals(TreadeStateEnum.getStatus(jsonaes.getStr("trade_state")), "1")) {
flag = jsonaes.getStr("trade_state");
}
logger.info("nativePay修改订单状态|flag=" + flag);
return flag;
}
@Override
@Transactional
public JSONObject findWxPayQRcodeStatus(String cardid) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("flag", "0");
String hgetOrderId = "";
String hgetOrderStatus = "";
if (StrUtil.isNotBlank(cardid)) {
hgetOrderId = redisClient.hget(PayConstants.NATIVE_ORDER_KEY, cardid);//根据卡物理号获取订单id
if (redisClient.exists(PayConstants.NATIVE_ORDER_ID_KEY) && StrUtil.isNotBlank(hgetOrderId)) {
hgetOrderStatus = redisClient.hget(PayConstants.NATIVE_ORDER_ID_KEY, hgetOrderId);//根据订单号id获取订单状态
if (StrUtil.equals(hgetOrderStatus, "1")) {
jsonObject.put("flag", "1");
}
}
}
if (StrUtil.isNotBlank(hgetOrderId)) {
boolean payStatus = false;
WxNativePay wxNativePay = nativePayDao.find(Convert.toLong(hgetOrderId));
try {
payStatus = PayUtils.findPayStatusByOuttradeno(wxNativePay);
} catch (Exception e) {
e.printStackTrace();
logger.error("订单查询失败");
}
if (payStatus){
redisClient.hdel(PayConstants.NATIVE_ORDER_KEY, cardid);
wxNativePay.setStatus("1");//支付成功
nativePayDao.update(wxNativePay);
jsonObject.put("flag", "1");
}
}
return jsonObject;
}
@Autowired
private ISysUserDAO sysUserInfo;
@Override
@Transactional
public void wxTradebillService(String organcode, String date) {
String message = null;
String mes = null;
SysUser sysUser = new SysUser();
try {
List<SysUser> byOrgancode = sysUserInfo.findByOrgancode(organcode);
if (byOrgancode != null && byOrgancode.size() > 0) {
sysUser = byOrgancode.get(0);
}
Map<String, Object> map = new HashMap<>();
if (StrUtil.isNotBlank(date)){
map.put("startTime", date + " 00:00:00");
map.put("endTime", DateUtil.offsetDay(DateUtil.parse(date), 1));
}else {
map.put("startTime", DateUtil.format(DateUtil.yesterday(), DatePattern.NORM_DATE_PATTERN) + " 00:00:00");
map.put("endTime", DateUtil.format(new DateTime(), DatePattern.NORM_DATE_PATTERN) + " 00:00:00");
}
List<WxNativePay> wxNativePays = nativePayDao.sqlExecuteFind(" SELECT * from wx_native_pay where `STATUS` != '1' and createOrderTime >= :startTime AND createOrderTime < :endTime ", map);
List<BilldownloadData> billdownloadDataList = PayUtils.wxTradebill(date);
Map<String, BilldownloadData> dataMap = new HashMap<>();
for (BilldownloadData billdownloadData : billdownloadDataList) {
dataMap.put(billdownloadData.getOrderno(), billdownloadData);
}
for (WxNativePay wxNativePay : wxNativePays) {
BilldownloadData billdownloadData = dataMap.get(wxNativePay.getOrderno());
if (billdownloadData != null) {
UserInfo userInfobyId = userInfoDAO.findById(wxNativePay.getUserid());
try {
mes = userInfoService.saveCardNum(userInfobyId, sysUser, Convert.toStr(wxNativePay.getCashPledge()), Convert.toStr(wxNativePay.getRechargeAmount()), wxNativePay.getPledgecode(), wxNativePay.getCardid());
if (StrUtil.equals(mes, "1")) {
mes = userInfoService.saveMoney(userInfobyId, sysUser, Convert.toStr(wxNativePay.getCashPledge()), Convert.toStr(wxNativePay.getRechargeAmount()), wxNativePay.getPledgecode(), wxNativePay.getCardid());
if (StrUtil.equals(mes, "1")) {
wxNativePay.setPayertotal(Convert.toInt(billdownloadData.getPayertotal()));
wxNativePay.setWxtransaction(billdownloadData.getWxPayOrder());
wxNativePay.setStatus("1");
wxNativePay.setSuccessTime(new Timestamp(DateUtil.parse(billdownloadData.getBillDateTime(), DatePattern.NORM_DATETIME_PATTERN).getTime()));
nativePayDao.update(wxNativePay);
}
}
} catch (Exception e) {
logger.error("微信对账充值加款失败!");
WriteLog.writeLog("wx", "wxTradebill", "微信对账充值加款失败!失败商品描述:" + billdownloadData.getDescription());
}
}
}
} catch (Exception e) {
e.printStackTrace();
logger.error("对账出错!");
}
}
/**
* 生成二维码
*
* @param content
* @param imgType
* @param size
* @return
*/
public BufferedImage qRCodeCommon(String content, String imgType, int size) {
BufferedImage bufImg = null;
try {
Qrcode qrcodeHandler = new Qrcode();
qrcodeHandler.setQrcodeErrorCorrect('M');
qrcodeHandler.setQrcodeEncodeMode('B');
qrcodeHandler.setQrcodeVersion(size);
byte[] contentBytes = content.getBytes("utf-8");
int imgSize = 67 + 12 * (size - 1);
bufImg = new BufferedImage(imgSize, imgSize,
BufferedImage.TYPE_INT_RGB);
Graphics2D gs = bufImg.createGraphics();
gs.setBackground(Color.WHITE);
gs.clearRect(0, 0, imgSize, imgSize);
gs.setColor(Color.BLACK);
int pixoff = 2;
if (contentBytes.length > 0 && contentBytes.length < 800) {
boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
for (int i = 0; i < codeOut.length; i++) {
for (int j = 0; j < codeOut.length; j++) {
if (codeOut[j][i]) {
gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
}
}
}
} else {
throw new Exception("QRCode content bytes length = "
+ contentBytes.length + " not in [0, 800].");
}
gs.dispose();
bufImg.flush();
} catch (Exception e) {
e.printStackTrace();
}
return bufImg;
}
/**
* 给二维码下方附加说明文字
*
* @param pressText 文字
* @param newImg 带文字的图片
* @param image 需要添加文字的图片
* @为图片添加文字
*/
public void pressText(String pressText, String newImg, BufferedImage image, int fontStyle,
Color color, int fontSize) {
//计算文字开始的位置
//x开始的位置:(图片宽度-字体大小*字的个数)/2
int startX = (image.getWidth(null) - (fontSize * pressText.length())) / 2;
//y开始的位置:图片高度-(图片高度-图片宽度)/2
int startY = image.getHeight() - (fontSize * 4) / 2;
try {
int imageW = image.getWidth();
int imageH = image.getHeight();
Graphics g = image.createGraphics();
g.drawImage(image, 0, 0, imageW, imageH, null);
g.setColor(color);
g.setFont(new Font("粗体", Font.BOLD, 16));
g.drawString(pressText, startX, startY - 100);
g.dispose();
FileOutputStream out = new FileOutputStream(newImg);
ImageIO.write(image, "png", out);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(image);
out.close();
System.out.println("image press success");
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
/**
* BufferedImage 编码转换为 base64
*
* @param bufferedImage
* @return
*/
private static String BufferedImageToBase64(BufferedImage bufferedImage) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
try {
ImageIO.write(bufferedImage, "png", baos);//写入流中
} catch (IOException e) {
e.printStackTrace();
}
byte[] bytes = baos.toByteArray();//转换成字节
BASE64Encoder encoder = new BASE64Encoder();
String png_base64 = encoder.encodeBuffer(bytes).trim();//转换成base64串
png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
System.out.println("值为:" + "data:image/jpg;base64," + png_base64);
return "data:image/jpg;base64," + png_base64;
}
/**
* base64 编码转换为 BufferedImage
*
* @param base64
* @return
*/
private static BufferedImage base64ToBufferedImage(String base64) {
BASE64Decoder decoder = new sun.misc.BASE64Decoder();
try {
byte[] bytes1 = decoder.decodeBuffer(base64);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
return ImageIO.read(bais);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
4, 微信支付payUtils
package com.datalook.nxykd.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.datalook.model.WxNativePay;
import com.datalook.nxykd.model.BilldownloadData;
import com.datalook.nxykd.util.http.v3.PayConstants;
import com.datalook.util.common.WriteLog;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.After;
import org.junit.Before;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PayUtils {
public static CloseableHttpClient httpClient = HttpClients.createDefault();
public static AutoUpdateCertificatesVerifier verifier;
@Before
public static void setup() {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(PayConstants.PRIVATE_KEY);
//使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
@After
public static void after() throws IOException {
httpClient.close();
}
/**
* 验签
*
* @param serial 请求头中的Wechatpay-Serial
* @param message
* @param signature 请求头中的Wechatpay-Signature
* @return
*/
public static boolean signVerify(String serial, String message, String signature) {
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes("utf-8")));
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_V3KEY.getBytes("utf-8"));
return verifier.verify(serial, message.getBytes("utf-8"), signature);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return false;
}
public static String nativePayOrder(WxNativePay nativePay) throws Exception {
//2, 微信native下单接口
String url = PayConstants.NATIVE_ORDER_URL;//微信下单接口
//时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
//2, 微信下单
JSONObject jsonObject = new JSONObject();
jsonObject.put("mchid", PayConstants.MCH_ID);
jsonObject.put("appid", PayConstants.APP_ID);
jsonObject.put("notify_url", PayConstants.NOTIFY_URL);
jsonObject.put("description", nativePay.getDescription());
jsonObject.put("out_trade_no", nativePay.getOrderno());//随机订单号
JSONObject json = new JSONObject();
json.put("total", nativePay.getTotal());//订单金额, 单位:分
json.put("currency", "CNY");//币种, 境内商户默认都是人民币
jsonObject.put("amount", json);//随机订单号
//拼签名串
StringBuffer sb = new StringBuffer();
sb.append("POST").append("\n");
sb.append("/v3/pay/transactions/native").append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append(jsonObject).append("\n");
System.out.println("签名原串:" + sb.toString());
String privateKey = PayConstants.PRIVATE_KEY.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
//计算签名
String sign = new String(Base64.encodeBase64(signRSA(sb.toString(), privateKey)));
System.out.println("签名sign值:" + sign);
//拼装http头的Authorization内容
String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
System.out.println("authorization值:" + authorization);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
//设置头部
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-Type", "application/json ");
httpPost.addHeader("Authorization", authorization);
//设置请求内容
HttpEntity Entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON);
httpPost.setEntity(Entity);
//获取返回内容
CloseableHttpResponse response = httpclient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
String rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
return rescontent;
}
public static List<BilldownloadData> wxTradebill(String yesterday) throws Exception {
List<BilldownloadData> billdownloadDataList = new ArrayList<>();
String reconciliationUrl = PayConstants.RECONCILIATION_URL;//微信对账接口(获取下载账单接口)
if (StrUtil.isEmpty(yesterday)){
yesterday = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd");//获取前一天的账单日期
}
//reconciliationUrl += "?bill_date=" + yesterday + "&bill_type=" + PayConstants.ALL;
String url = "?bill_date=" + yesterday + "&bill_type=" + PayConstants.SUCCESS;
reconciliationUrl += url;
System.out.println(reconciliationUrl);
//拼签名串
Map<String, Object> str = getsign("GET", "/v3/bill/tradebill" + url, "");
System.out.println("签名原串:" + str.get("signStr"));
WriteLog.writeLog("wx", "wxTradebill", "签名原串:" + str.get("signStr"));
//计算签名
//String sign = new String(Base64.encodeBase64(signRSA(Convert.toStr(str.get("sign")), rsaPrivateKey)));
System.out.println("签名sign值:" + str.get("sign"));
WriteLog.writeLog("wx", "wxTradebill", "签名sign值:" + str.get("sign"));
//拼装http头的Authorization内容
//String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
System.out.println("authorization值:" + str.get("authorization"));
WriteLog.writeLog("wx", "wxTradebill", "authorization值:" + str.get("authorization"));
//CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(reconciliationUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(str.get("authorization")));
//获取返回内容
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
/**
* //获取下载账单地址
* {
* "download_url": "https://api.mch.weixin.qq.com/v3/billdownload/file?token=6XIv5TUPto7pByrTQKhd6kwvyKLG2uY2wMMR8cNXqaA_Cv_isgaUtBzp4QtiozLO",
* "hash_type": "SHA1",
* "hash_value": "8823044c286bea726f149bfcfce0b0318122d755"
* }
*/
JSONObject jsonRescontent = JSONUtil.parseObj(rescontent);
if (jsonRescontent.containsKey("download_url")) {
String downloadUrl = jsonRescontent.getStr("download_url");
Map<String, Object> downloadStr = getsign("GET", downloadUrl.replace("https://api.mch.weixin.qq.com", ""), "");
//下载账单
System.out.println("签名原串:" + downloadStr.get("signStr"));
System.out.println("签名sign值:" + downloadStr.get("sign"));
System.out.println("authorization值:" + downloadStr.get("authorization"));
httpGet = new HttpGet(downloadUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(downloadStr.get("authorization")));
response = httpClient.execute(httpGet);
//获取返回内容
//CloseableHttpResponse response = httpClient.execute(httpGet);
httpEntity = response.getEntity();
rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
WriteLog.writeLog("wx", "wxTradebill", "对账数据:");
String[] split = StrUtil.split(rescontent, "\n");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
WriteLog.writeLog("wx", "wxTradebill", split[i]);
if (i == 0){
continue;
}
String[] splitBilldownload = StrUtil.split(split[i], ",");
if (splitBilldownload.length > 19) {
BilldownloadData billdownload = new BilldownloadData();
// 交易时间 格式为yyyy-MM-dd HH:MM:SS
billdownload.setBillDateTime(StrUtil.replace(splitBilldownload[0], "`", ""));
// 公众账号ID
billdownload.setAppid(StrUtil.replace(splitBilldownload[1], "`", ""));
// 商户号
billdownload.setMchid(StrUtil.replace(splitBilldownload[2], "`", ""));
// 特约商户号
billdownload.setTyMchid(StrUtil.replace(splitBilldownload[3], "`", ""));
// 设备号
billdownload.setDeviceInfoCode(StrUtil.replace(splitBilldownload[4], "`", ""));
// 微信订单号
billdownload.setWxPayOrder(StrUtil.replace(splitBilldownload[5], "`", ""));
// 商户订单号
billdownload.setOrderno(StrUtil.replace(splitBilldownload[6], "`", ""));
// 用户标识
billdownload.setUserIdentify(StrUtil.replace(splitBilldownload[7], "`", ""));
// 交易类型
billdownload.setPayType(StrUtil.replace(splitBilldownload[8], "`", ""));
// 交易状态
billdownload.setPayStatus(StrUtil.replace(splitBilldownload[9], "`", ""));
// 付款银行
billdownload.setPayBank(StrUtil.replace(splitBilldownload[10], "`", ""));
// 货币种类
billdownload.setCurrencyType(StrUtil.replace(splitBilldownload[11], "`", ""));
// 应结订单金额
billdownload.setPayTotal(StrUtil.replace(splitBilldownload[12], "`", ""));//(订单金额)
// 代金券金额
billdownload.setVoucherTotal(StrUtil.replace(splitBilldownload[13], "`", ""));
// 商品名称
billdownload.setDescription(StrUtil.replace(splitBilldownload[14], "`", ""));
// 商户数据包(商户传入的该笔订单)
billdownload.setPayertotal(StrUtil.replace(splitBilldownload[15], "`", ""));
// 手续费
billdownload.setServiceCharge(StrUtil.replace(splitBilldownload[16], "`", ""));
// 费率
billdownload.setRate(StrUtil.replace(splitBilldownload[17], "`", ""));
// 订单金额
billdownload.setOrdernoTotal(StrUtil.replace(splitBilldownload[18], "`", ""));
// 费率备注
billdownload.setRateRemark(StrUtil.replace(splitBilldownload[19], "`", ""));
billdownloadDataList.add(billdownload);
}
}
} else {
WriteLog.writeLog("wx", "wxTradebill", "对账出错:" + jsonRescontent.getStr("message"));
throw new Exception(jsonRescontent.getStr("message"));
}
return billdownloadDataList;
}
/**
* 获取签名串
*
* @param type GET/POST
* @param url 接口地址:GET请求要带query参数(/v3/bill/tradebill?bill_date=2023-07-31&bill_type=ALL),POST参数只接口参数(/v3/pay/transactions/native)
* timestamp 时间戳(System.currentTimeMillis() / 1000)
* nonce_str 32位随机字符串
* @param bodystr 接口入参:GET请求的这个参数为空, POST请求的这个字段为接口入参
* @return
*/
public static Map getsign(String type, String url, String bodystr) throws Exception {
String privateKey = PayConstants.PRIVATE_KEY.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
Map<String, Object> map = new HashMap<>();
//时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//随机数
String nonce_str = RandomUtil.randomString(32);
//拼签名串
StringBuffer sb = new StringBuffer();
sb.append(type).append("\n");
sb.append(url).append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append(bodystr).append("\n");
System.out.println("签名原串:" + sb.toString());
String sign = new String(Base64.encodeBase64(signRSA(sb.toString(), privateKey)));
String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + PayConstants.MCH_ID + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + sign + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + PayConstants.MCH_SERIAL_NO + "\"";
map.put("signStr", sb);
map.put("sign", sign);
map.put("authorization", authorization);
return map;
}
public static byte[] signRSA(String data, String priKey) throws Exception {
//签名的类型
Signature sign = Signature.getInstance("SHA256withRSA");
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes("UTF-8"));
return sign.sign();
}
public static byte[] InputStreamTOByte(InputStream in) throws IOException {
int BUFFER_SIZE = 4096;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
byte[] outByte = outStream.toByteArray();
outStream.close();
return outByte;
}
/**
* 商户订单号查询
* 请求URL: https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}
* out_trade_no 商户订单号
* 请求方式:GET
* @param wxNativePay 订单数据
*/
public static boolean findPayStatusByOuttradeno(WxNativePay wxNativePay) throws Exception {
String reconciliationUrl = PayConstants.TRANSACTIONS_URL;//查询订单接口
//String out_trade_no = "YKT-967wcp1691147868";
String out_trade_no = wxNativePay.getOrderno();
String url = "?mchid=" + PayConstants.MCH_ID;
reconciliationUrl = reconciliationUrl + out_trade_no + url;
System.out.println(reconciliationUrl);
//拼签名串
Map<String, Object> str = getsign("GET", "/v3/pay/transactions/out-trade-no/"+ out_trade_no + url, "");
System.out.println("签名原串:" + str.get("signStr"));
System.out.println("签名sign值:" + str.get("sign"));
System.out.println("authorization值:" + str.get("authorization"));
HttpGet httpGet = new HttpGet(reconciliationUrl);
//设置头部
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Authorization", Convert.toStr(str.get("authorization")));
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String rescontent = new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
JSONObject jsonObject = JSONUtil.parseObj(rescontent);
if (jsonObject.containsKey("trade_state") && StrUtil.equals(jsonObject.getStr("trade_state"), PayConstants.SUCCESS)){
return true;
}
return false;
}
}