maven
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
controller
/**
* 微信支付回调
*/
@PostMapping(value = "weChartPayBack/{userType}")
@SkipLoginVerify
@NoBindingFilter //这个注解是用来不走过滤器的,不然body会验签失败
public String weChartPayBack(@PathVariable("userType") String userType, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = weChatService.weChatCallback(request, response, userType,"payment");
return new Gson().toJson(map);
}
/**
* 微信支付退款回调
*/
@PostMapping(value = "weChartRefundBack/{userType}")
@SkipLoginVerify
@NoBindingFilter
public String weChartRefundBack(@PathVariable("userType") String userType, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = weChatService.weChatCallback(request, response, userType,"refund");
return new Gson().toJson(map);
}
service 需要自己改一下逻辑,微信支付一块基本上没问题 订单逻辑需要自己更改
package com.yilin.platform.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
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.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.yilin.platform.base_core.application.MathUtil;
import com.yilin.platform.base_core.commons.exception.BusinessException;
import com.yilin.platform.base_core.commons.utils.*;
import com.yilin.platform.base_core.tools.wechat.WeChatConfig;
import com.yilin.platform.base_core.tools.wechat.WeChatUtils;
import com.yilin.platform.entity.capital.TradeFlow;
import com.yilin.platform.entity.user.AccountConfigure;
import com.yilin.platform.entity.weChat.WeChatTradeRecord;
import com.yilin.platform.mapper.weChat.WeChatTradeRecordMapper;
import com.yilin.platform.service.IWeChatService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class WeChatService extends ServiceImpl<WeChatTradeRecordMapper, WeChatTradeRecord> implements IWeChatService {
private Logger logger = LoggerFactory.getLogger(WeChatService.class);
@Resource
private WeChatConfig weChatConfig;
@Resource
private WeChatTradeRecordMapper weChatTradeRecordMapper;
@Resource
private UserService userService;
@Resource
private TradeFlowService tradeFlowService;
/*微信app下单*/
@Override
public Map<String, Object> wxChartAppTransactions(WeChatTradeRecord weChatTradeRecord) {
Map<String, Object> amountJson = new HashMap<>();
Integer amt = new BigDecimal(String.valueOf(weChatTradeRecord.getTranAmount())).multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_EVEN).intValue();
amountJson.put("total", amt);
amountJson.put("currency", "CNY");
Map<String, Object> param = new HashMap<>();
String userType = String.valueOf(weChatTradeRecord.getUserType());
param.put("appid", weChatConfig.getAppId(userType));
param.put("mchid", weChatConfig.getMchId(userType));
param.put("description", weChatTradeRecord.getTradeName());
param.put("out_trade_no", weChatTradeRecord.getTradeNo());
param.put("notify_url", weChatConfig.getCallbackUrl() + "/platform/weChat/weChartPayBack/" + userType);
param.put("amount", amountJson);
try {
logger.info("微信app支付请求数据" + new Gson().toJson(param));
Map<String, Object> response = WeChatUtils.weChatV3Post(weChatConfig, userType, param, "https://api.mch.weixin.qq.com/v3/pay/transactions/app");
logger.info("微信app支付返回数据" + new Gson().toJson(response));
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
weChatTradeRecord.setInitiateTime(new Date());
weChatTradeRecord.setWeChatTradeType(WeChatTradeRecord.WeChatTradeType.payment);
weChatTradeRecord.setWeChatType(WeChatTradeRecord.WeChatType.app);
weChatTradeRecord.setQueryWaitCount(0);
weChatTradeRecord.setSyncTaskCount(0);
weChatTradeRecord.setNextWaitTime(new Date());
weChatTradeRecord.setNextNotificationTime(new Date());
saveOrUpdate(weChatTradeRecord);
String timestamp = System.currentTimeMillis() / 1000 + ""; //时间戳
String nonceStr = UUID.randomUUID().toString().replace("-", ""); //随机串
String sign = WeChatUtils.getSign(nonceStr, timestamp, new Gson().toJson(param), weChatConfig.getPrivateKeyUrl(userType), "GET");
Map<String, Object> map = new HashMap<>();
map.put("prepayid", response.get("prepay_id"));
map.put("timestamp", timestamp);
map.put("noncestr", nonceStr);
map.put("sign", sign);
map.put("appid", weChatConfig.getAppId(userType));
map.put("package", "Sign=WXPay");
map.put("partnerid", weChatConfig.getMchId(userType));
map.put("universalLink", weChatConfig.getUniversalLink(userType));//用于唤起APP
return map;
} catch (Exception e) {
logger.info("微信支付APP发起支付异常" + e.getMessage());
throw new BusinessException("微信支付APP发起支付异常" + e.getMessage());
}
}
/*微信app下单*/
@Override
public Map<String, Object> wxChartAppTransactionsH5(WeChatTradeRecord weChatTradeRecord) {
Map<String, Object> amountJson = new HashMap<>();
Integer amt = new BigDecimal(String.valueOf(weChatTradeRecord.getTranAmount())).multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_EVEN).intValue();
amountJson.put("total", amt);
amountJson.put("currency", "CNY");
Map<String, Object> param = new HashMap<>();
String userType = String.valueOf(weChatTradeRecord.getUserType());
param.put("appid", weChatConfig.getAppId(userType));
param.put("mchid", weChatConfig.getMchId(userType));
param.put("description", weChatTradeRecord.getTradeName());
param.put("out_trade_no", weChatTradeRecord.getTradeNo());
param.put("notify_url", weChatConfig.getCallbackUrl() + "/platform/weChat/weChartPayBack/" + userType);
param.put("amount", amountJson);
try {
logger.info("微信H5支付请求数据" + new Gson().toJson(param));
Map<String, Object> response = WeChatUtils.weChatV3Post(weChatConfig, userType, param, "https://api.mch.weixin.qq.com/v3/pay/transactions/native");
logger.info("微信H5支付返回数据" + new Gson().toJson(response));
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
weChatTradeRecord.setWeChatTradeType(WeChatTradeRecord.WeChatTradeType.payment);
weChatTradeRecord.setInitiateTime(new Date());
weChatTradeRecord.setWeChatType(WeChatTradeRecord.WeChatType.app);
weChatTradeRecord.setQueryWaitCount(0);
weChatTradeRecord.setSyncTaskCount(0);
weChatTradeRecord.setNextWaitTime(new Date());
weChatTradeRecord.setNextNotificationTime(new Date());
saveOrUpdate(weChatTradeRecord);
Map<String, Object> map = new HashMap<>();
map.put("code_url", response.get("code_url"));
return map;
} catch (Exception e) {
logger.error("微信支付H5发起支付异常" + e.getMessage());
throw new BusinessException("微信支付H5发起支付异常" + e.getMessage());
}
}
/*微信app支付查询
* 备注:调这个方法之后需要掉用订单通知接口
* */
@Override
public WeChatTradeRecord wxChartAppQueryTransactions(String tradeNo) {
try {
WeChatTradeRecord weChatTradeRecord = getWeChatTradeRecordByTradeNo(tradeNo);
ParamUtil.isNull(weChatTradeRecord, "获支付单为null");
String userType = String.valueOf(weChatTradeRecord.getUserType());
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + weChatTradeRecord.getTradeNo() + "?mchid=" + weChatConfig.getMchId(userType);
logger.info("微信app支付查询请求数据" + new Gson().toJson(url));
Map<String, Object> response = WeChatUtils.weChatV3Get(weChatConfig, userType, url);
logger.info("微信app支付查询返回数据" + new Gson().toJson(response));
String state = MapUtil.getMapString(response, "trade_state");
if (MapUtil.getMapString(response, "transaction_id") != null)
weChatTradeRecord.setBankFlowNo(MapUtil.getMapString(response, "transaction_id"));
if (MapUtil.getMapString(response, "trade_state_desc") != null && weChatTradeRecord.getRemark() == null)
weChatTradeRecord.setRemark(MapUtil.getMapString(response, "trade_state_desc"));
if (MapUtil.getMapString(response, "success_time") != null)
weChatTradeRecord.setCompleteTime(DateTimeUtil.wxStringToDate(MapUtil.getMapString(response, "success_time"), null));
Map<String, Object> payer = (Map<String, Object>) response.get("payer");
if (weChatTradeRecord.getOutAcctNo() == null)
weChatTradeRecord.setOutAcctNo(MapUtil.getMapString(payer, "openid"));
if (weChatTradeRecord.getInAcctNo() == null) weChatTradeRecord.setInAcctNo(weChatConfig.getMchId(userType));
if (state.equals("SUCCESS") || state.equals("REFUND")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.success);
} else if (state.equals("CLOSED")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.close);
} else if (state.equals("PAYERROR")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.fail);
} else if (state.equals("NOTPAY")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.notpay);
} else {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
}
saveTradeFlow(weChatTradeRecord.getTradeName(), weChatTradeRecord);
saveOrUpdate(weChatTradeRecord);
return weChatTradeRecord;
} catch (Exception e) {
logger.error("微信支付APP支付查询异常" + e.getMessage());
throw new BusinessException("微信支付APP支付查询异常" + e.getMessage());
}
}
/*微信app发起退款*/
@Override
public WeChatTradeRecord wxChartAppRefund(WeChatTradeRecord weChatTradeRecord, WeChatTradeRecord tradeRecordOrgin) {
try {
Map<String, Object> amountJson = new HashMap<>();
amountJson.put("refund", CommonUtil.intValue(MathUtil.multiply(weChatTradeRecord.getTranAmount(), 100D)));
amountJson.put("total", CommonUtil.intValue(MathUtil.multiply(tradeRecordOrgin.getTranAmount(), 100D)));
amountJson.put("currency", "CNY");
String userType = String.valueOf(weChatTradeRecord.getUserType());
Map<String, Object> param = MapUtil.generate(map -> {
map.put("out_trade_no", tradeRecordOrgin.getTradeNo());
map.put("out_refund_no", weChatTradeRecord.getTradeNo());
map.put("reason", weChatTradeRecord.getRemark());
map.put("notify_url", weChatConfig.getCallbackUrl() + "/platform/weChat/weChartPayBack/" + userType);
map.put("amount", amountJson);
});
logger.info("微信app支付退款请求数据" + new Gson().toJson(param));
Map<String, Object> response = WeChatUtils.weChatV3Post(weChatConfig, userType, param, "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
logger.info("微信app支付退款返回数据" + new Gson().toJson(response));
String state = MapUtil.getMapString(response, "status");
weChatTradeRecord.setBankFlowNo(MapUtil.getMapString(response, "refund_id"));
weChatTradeRecord.setWeChatTradeType(WeChatTradeRecord.WeChatTradeType.refund);
weChatTradeRecord.setUserType(tradeRecordOrgin.getUserType());
if (weChatTradeRecord.getOutAcctNo() == null)
weChatTradeRecord.setOutAcctName(tradeRecordOrgin.getInAcctNo());
if (weChatTradeRecord.getInAcctNo() == null) weChatTradeRecord.setInAcctNo(tradeRecordOrgin.getOutAcctNo());
if (state.equals("SUCCESS")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.success);
weChatTradeRecord.setBankMsg(MapUtil.getMapString(response, "user_received_account"));
} else if (state.equals("CLOSED")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.close);
} else if (state.equals("ABNORMAL")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.fail);
} else {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
}
weChatTradeRecord.setQueryWaitCount(0);
weChatTradeRecord.setSyncTaskCount(0);
weChatTradeRecord.setNextWaitTime(new Date());
weChatTradeRecord.setNextNotificationTime(new Date());
saveOrUpdate(weChatTradeRecord);
return weChatTradeRecord;
} catch (Exception e) {
logger.error("微信支付APP发起退款异常" + e.getMessage());
throw new BusinessException("微信支付APP发起退款异常" + e.getMessage());
}
}
//公共: 保存流水
private void saveTradeFlow(String tradeName, WeChatTradeRecord weChatTradeRecord) {
if (weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.success) return;
TradeFlow[] tradeFlows = tradeFlowService.generateTradeFlows(tradeName, TradeFlow.PayType.weixin, weChatTradeRecord);
tradeFlowService.saveOrUpdateBatch(ArrayUtil.initList(tradeFlows));
}
/*微信app退款查询
* 备注:调这个方法之后需要掉用订单通知接口
* */
@Override
public WeChatTradeRecord wxChartAppQueryRefund(String tradeNo) {
try {
WeChatTradeRecord weChatTradeRecord = getWeChatTradeRecordByTradeNo(tradeNo);
String userType = String.valueOf(weChatTradeRecord.getUserType());
ParamUtil.isNull(weChatTradeRecord, "获支付单为null");
String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + weChatTradeRecord.getTradeNo();
logger.info("微信app支付退款查询请求数据" + new Gson().toJson(url));
Map<String, Object> response = WeChatUtils.weChatV3Get(weChatConfig, userType, url);
logger.info("微信app支付退款查询返回数据" + new Gson().toJson(response));
String state = MapUtil.getMapString(response, "status");
if (MapUtil.getMapString(response, "transaction_id") != null)
weChatTradeRecord.setBankFlowNo(MapUtil.getMapString(response, "transaction_id"));
if (MapUtil.getMapString(response, "success_time") != null)
weChatTradeRecord.setCompleteTime(DateTimeUtil.wxStringToDate(MapUtil.getMapString(response, "success_time"), null));
if (state.equals("SUCCESS")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.success);
weChatTradeRecord.setBankMsg(MapUtil.getMapString(response, "user_received_account"));
saveTradeFlow(weChatTradeRecord.getTradeName(), weChatTradeRecord);
} else if (state.equals("CLOSED")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.close);
} else if (state.equals("ABNORMAL")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.fail);
} else {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
}
saveOrUpdate(weChatTradeRecord);
return weChatTradeRecord;
} catch (Exception e) {
logger.error("微信支付APP查询退款异常" + e.getMessage());
throw new BusinessException("微信支付APP查询退款异常" + e.getMessage());
}
}
@Override
public Map<String, Object> weChatCallback(HttpServletRequest request, HttpServletResponse response, String userType, String type) {
Map<String, Object> map = new HashMap<>();
try {
String body = readData(request);
logger.info("微信支付付款回调数据" + body);
String nonce = request.getHeader("Wechatpay-Nonce"); //随机串
String signature = request.getHeader("Wechatpay-Signature"); //微信传递过来的签名
String wechatPaySerial = request.getHeader("Wechatpay-Serial"); //证书序列号(微信平台)
String timestamp = request.getHeader("Wechatpay-Timestamp"); //时间戳
String mchId = weChatConfig.getMchId(userType);
String privateKey = weChatConfig.getPrivateKeyUrl(userType);
String serialNo = weChatConfig.getSerialNo(userType);
String apiV3Key = weChatConfig.getApiV3Key(userType);
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(Files.newInputStream(Paths.get(privateKey)));
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(serialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
Verifier verifier = certificatesManager.getVerifier(mchId);
NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
.withNonce(nonce)
.withTimestamp(timestamp)
.withSignature(signature)
.withBody(body)
.build();
// 这个解析器是微信支付SDK里的,他会帮我们做验签等一系列的事,不需要我们去做
NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
Notification notification = handler.parse(notificationRequest);
String decryptData = notification.getDecryptData();
JSONObject jsonObject = JSONObject.parseObject(decryptData);
if (type.equals("payment")) {
map = wxCallPay(jsonObject, mchId);
} else if (type.equals("refund")) {
map = wxCallPayRefund(jsonObject);
} else {
throw new BusinessException("回调地址错误平地址" + type);
}
} catch (Exception e) {
map.put("code", "FAIL");
map.put("message", "失败");
logger.info("微信APP支付" + (type.equals("payment") ? "支付" : "退款") + "回调异常请检查:" + e.getMessage());
}
return map;
}
/*微信支付回调*/
public Map<String, Object> wxCallPay(JSONObject jsonObject, String mchId) {
Map<String, Object> map = new HashMap<>();
String state = String.valueOf(jsonObject.get("trade_state"));
String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
WeChatTradeRecord weChatTradeRecord = getWeChatTradeRecordByTradeNo(orderNumber);
if (weChatTradeRecord.getRemark() == null)
weChatTradeRecord.setRemark(String.valueOf(jsonObject.get("attach")));
weChatTradeRecord.setBankFlowNo(String.valueOf(jsonObject.get("transaction_id")));
weChatTradeRecord.setCompleteTime(DateTimeUtil.wxStringToDate(String.valueOf(jsonObject.get("success_time")), null));
Map<String, Object> payer = (Map<String, Object>) jsonObject.get("payer");
if (weChatTradeRecord.getOutAcctNo() == null)
weChatTradeRecord.setOutAcctNo(MapUtil.getMapString(payer, "openid"));
if (weChatTradeRecord.getInAcctNo() == null) weChatTradeRecord.setInAcctNo(mchId);
if (state.equals("SUCCESS") || state.equals("REFUND")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.success);
} else if (state.equals("CLOSED")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.close);
} else if (state.equals("PAYERROR")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.fail);
} else if (state.equals("NOTPAY")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.notpay);
} else {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
}
try {
saveTradeFlow(weChatTradeRecord.getTradeName(), weChatTradeRecord);
} catch (Exception e) {
logger.error("微信支付退款回调通知保存流水失败" + e.getMessage());
}
try {
if (weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.create
&& weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.handle
&& weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.notpay) {
processNotificationStatus(Collections.singletonList(weChatTradeRecord));
}
} catch (Exception e) {
logger.error("微信支付回调通知失败" + e.getMessage());
}
map.put("code", "SUCCESS");
map.put("message", "成功");
return map;
}
/*微信退款回调*/
public Map<String, Object> wxCallPayRefund(JSONObject jsonObject) {
Map<String, Object> map = new HashMap<>();
String state = String.valueOf(jsonObject.get("refund_status"));
String orderNumber = String.valueOf(jsonObject.get("out_refund_no"));
WeChatTradeRecord weChatTradeRecord = getWeChatTradeRecordByTradeNo(orderNumber);
weChatTradeRecord.setBankFlowNo(String.valueOf(jsonObject.get("refund_id")));
weChatTradeRecord.setCompleteTime(DateTimeUtil.wxStringToDate(String.valueOf(jsonObject.get("success_time")), null));
if (state.equals("SUCCESS")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.success);
} else if (state.equals("CLOSED")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.close);
} else if (state.equals("ABNORMAL")) {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.fail);
} else {
weChatTradeRecord.setWeChatTradeStatus(WeChatTradeRecord.WeChatTradeStatus.handle);
}
try {
saveTradeFlow(weChatTradeRecord.getTradeName(), weChatTradeRecord);
} catch (Exception e) {
logger.error("微信支付退款回调通知保存流水失败" + e.getMessage());
}
try {
if (weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.create
&& weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.handle
&& weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.notpay) {
processNotificationStatus(Collections.singletonList(weChatTradeRecord));
}
} catch (Exception e) {
logger.error("微信支付退款回调通知失败" + e.getMessage());
}
map.put("code", "SUCCESS");
map.put("message", "成功");
return map;
}
@Override
public WeChatTradeRecord getWeChatTradeRecordById(String tradeRecordId) {
return weChatTradeRecordMapper.selectById(tradeRecordId);
}
@Override
public WeChatTradeRecord getWeChatTradeRecordByTradeNo(String tradeNo) {
QueryWrapper<WeChatTradeRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("trade_no", tradeNo);
queryWrapper.eq("is_deleted", 0);
return weChatTradeRecordMapper.selectOne(queryWrapper);
}
@Override
public List<WeChatTradeRecord> getNotificationList() {
QueryWrapper<WeChatTradeRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.nested(i ->
i.or(j -> j.in("we_chat_trade_status", 3,5).le("next_wait_time", new Date()))
.or(j -> j.eq("we_chat_trade_status", 1).eq("notified_yilin", 0))
.or(j -> j.eq("we_chat_trade_status", 2).eq("notified_yilin", 0))
.or(j -> j.eq("we_chat_trade_status", 4).eq("notified_yilin", 0)))
.eq("is_deleted", 0)
.le("next_notification_time", new Date());
return weChatTradeRecordMapper.selectList(queryWrapper);
}
@Override
public void myTaskOneMinute() {
List<WeChatTradeRecord> recordFlowList = getNotificationList();
if (recordFlowList == null || recordFlowList.isEmpty()) return;
List<WeChatTradeRecord> recordSuccess = new ArrayList<>();
List<WeChatTradeRecord> recordHandle = new ArrayList<>();
for (WeChatTradeRecord weChatTradeRecord : recordFlowList) {
try {
if (weChatTradeRecord.getWeChatTradeStatus() == WeChatTradeRecord.WeChatTradeStatus.handle) {
if (weChatTradeRecord.getWeChatTradeType() == WeChatTradeRecord.WeChatTradeType.payment) {
weChatTradeRecord = wxChartAppQueryTransactions(weChatTradeRecord.getTradeNo());
} else if (weChatTradeRecord.getWeChatTradeType() == WeChatTradeRecord.WeChatTradeType.payment) {
weChatTradeRecord = wxChartAppQueryRefund(weChatTradeRecord.getTradeNo());
}
}
} catch (Exception e) {
logger.info("微信支付定时函数查询单笔转账异常:" + e.getMessage());
weChatTradeRecord.setNextWaitTime(DateTimeUtil.getWeChataAliPayTime(new Date(), weChatTradeRecord.getQueryWaitCount()));
weChatTradeRecord.setQueryWaitCount(weChatTradeRecord.getQueryWaitCount() + 1);
recordHandle.add(weChatTradeRecord);
continue;
}
if (weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.handle
&&weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.create
&& weChatTradeRecord.getWeChatTradeStatus() != WeChatTradeRecord.WeChatTradeStatus.notpay)
recordSuccess.add(weChatTradeRecord);
else if(weChatTradeRecord.getWeChatTradeStatus() == WeChatTradeRecord.WeChatTradeStatus.handle|| weChatTradeRecord.getWeChatTradeStatus() == WeChatTradeRecord.WeChatTradeStatus.notpay) {
weChatTradeRecord.setNextWaitTime(DateTimeUtil.getWeChataAliPayTime(new Date(), weChatTradeRecord.getQueryWaitCount()));
weChatTradeRecord.setQueryWaitCount(weChatTradeRecord.getQueryWaitCount() + 1);
recordHandle.add(weChatTradeRecord);
}
}
if (!recordHandle.isEmpty()) saveOrUpdateBatch(recordHandle);
if (!recordSuccess.isEmpty()) processNotificationStatus(recordSuccess);
}
@Override
public void processNotificationStatus(List<WeChatTradeRecord> recordList) {
if (!recordList.isEmpty()) {
AccountConfigure accountConfigure = userService.getAccountConfigure();
Map<String, Object> paramMap = MapUtil.generate(map -> {
map.put("jsonResult", new Gson().toJson(recordList));
map.put("apiKey", accountConfigure.getSafeKey());
});
String status = null;
try {
status = userService.request("/capital/callback/wePayResult", null, paramMap, new TypeToken<String>() {
});
} catch (Exception e) {
for (WeChatTradeRecord recordFlow : recordList) {
recordFlow.setNextNotificationTime(DateTimeUtil.addTimeByCount(new Date(), recordFlow.getSyncTaskCount()));
recordFlow.setSyncTaskCount(recordFlow.getSyncTaskCount() + 1);
}
saveOrUpdateBatch(recordList);
logger.error("微信通知支付订单失败:错误信息:" + e.getMessage());
throw new BusinessException(e.getMessage());
}
if (status == null) return;
for (WeChatTradeRecord recordFlow : recordList) {
if (status.equals("SUCCESS")) {
recordFlow.setSyncTaskCount(0);
recordFlow.setNotifiedYilin(true);
recordFlow.setNotifyTime(new Date());
} else {
recordFlow.setNextNotificationTime(DateTimeUtil.addTimeByCount(new Date(), recordFlow.getSyncTaskCount()));
recordFlow.setSyncTaskCount(recordFlow.getSyncTaskCount() + 1);
}
}
saveOrUpdateBatch(recordList);
}
}
public static String readData(HttpServletRequest request) {
try (BufferedReader reader = request.getReader()) {
String line;
StringBuilder result = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
WeChatConfig 自己在yml里面配置 或者自己定义WeChatConfig
package com.yilin.platform.base_core.tools.wechat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class WeChatConfig {
@Value("${we-chat-enterprise.callback-url}")
private String callbackUrl;//回调地址
private String mchId; //商户号
private String appId; //ppid
private String universalLink;//IOS跳转链接
private String certFolderUrl; //微信SSL证书路径
private String privateKeyUrl; //商户私钥路径
private String serialNo; //证书序列号
private String apiV3Key;//apiV3秘钥
@Value("${we-chat-enterprise.shipper-mchId}")
private String shipperMchId; //商户号
@Value("${we-chat-enterprise.shipper-appId}")
private String shipperAppId; //Appid
@Value("${we-chat-enterprise.shipper-universal-link}")
private String shipperUniversalLink;//IOS跳转链接
@Value("${we-chat-enterprise.shipper-cert-url}")
private String shipperCertUrl; //微信SSL证书路径
@Value("${we-chat-enterprise.shipper-serial-no}")
private String shipperSerialNo; //证书序列号
@Value("${we-chat-enterprise.shipper-api-v3-key}")
private String shipperApiV3Key;//apiV3秘钥
@Value("${we-chat-enterprise.trucker-mchId}")
private String truckerMchId;//商户号
@Value("${we-chat-enterprise.trucker-appId}")
private String truckerAppId;//Appid
@Value("${we-chat-enterprise.trucker-universal-link}")
private String truckerUniversalLink;//IOS跳转链接
@Value("${we-chat-enterprise.trucker-cert-url}")
private String truckCertUrl;//微信SSL证书路径
@Value("${we-chat-enterprise.trucker-serial-no}")
private String truckerSerialNo; //证书序列号
@Value("${we-chat-enterprise.trucker-api-v3-key}")
private String truckerApiV3Key; //apiV3秘钥
public String getCallbackUrl() {
return callbackUrl;
}
public String getMchId(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperMchId;
} else if ("truckerUser".equals(userType)) {
return this.truckerMchId;
} else {
return null;
}
}
public String getAppId(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperAppId;
} else if ("truckerUser".equals(userType)) {
return this.truckerAppId;
} else {
return null;
}
}
public String getUniversalLink(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperUniversalLink;
} else if ("truckerUser".equals(userType)) {
return this.truckerUniversalLink;
} else {
return null;
}
}
public String getCertFolderUrl(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperCertUrl+"\\apiclient_cert.pem";
} else if ("truckerUser".equals(userType)) {
return this.truckCertUrl+"\\apiclient_cert.pem";
} else {
return null;
}
}
public String getPrivateKeyUrl(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperCertUrl+"\\apiclient_key.pem";
} else if ("truckerUser".equals(userType)) {
return this.truckCertUrl+"\\apiclient_key.pem";
} else {
return null;
}
}
public String getSerialNo(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperSerialNo;
} else if ("truckerUser".equals(userType)) {
return this.truckerSerialNo;
} else {
return null;
}
}
public String getApiV3Key(String userType) {
if ("shipperUser".equals(userType)) {
return this.shipperApiV3Key;
} else if ("truckerUser".equals(userType)) {
return this.truckerApiV3Key;
} else {
return null;
}
}
}
WeChatUtils
package com.yilin.platform.base_core.tools.wechat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
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.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.yilin.platform.base_core.application.AppUtil;
import com.yilin.platform.base_core.commons.exception.BusinessException;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Base64;
import okhttp3.HttpUrl;
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.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.util.HashMap;
import java.util.Map;
public class WeChatUtils {
static HttpUrl httpurl = HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates");
private static CloseableHttpClient httpClient;
private static CertificatesManager certificatesManager;
private static Verifier verifier;
/*微信支付POSt请求*/
public static Map<String, Object> weChatV3Post(WeChatConfig weChatConfig, String userType, Map<String, Object> param, String url) {
if (httpClient == null) setup(weChatConfig, userType);
ObjectMapper objectMapper = new ObjectMapper();
String paramJson = null;
try {
paramJson = objectMapper.writeValueAsString(param);
} catch (JsonProcessingException e) {
throw new BusinessException("微信请求Map转换失败!" + e.getMessage());
}
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(paramJson, "UTF-8")); // 设置请求实体为 paramJson
CloseableHttpResponse response;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
throw new BusinessException("微信请求GET请求失败:" + e.getMessage());
}
return getResponse(response);
}
/*微信支付GEt请求*/
public static Map<String, Object> weChatV3Get(WeChatConfig weChatConfig, String userType, String url) {
if (httpClient == null) setup(weChatConfig, userType);
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-type", "application/json; charset=utf-8");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
} catch (IOException e) {
throw new BusinessException("微信请求GET请求失败:" + e.getMessage());
}
return getResponse(response);
}
/*微信支付GEt下载请求*/
public static Map<String, Object> weChatV3GetDown(WeChatConfig weChatConfig, String userType, String url) {
// 假设这是你的请求参数
if (httpClient == null) setup(weChatConfig, userType);
String mchid = weChatConfig.getMchId(userType);
String nonce_str = AppUtil.getUUID();
String timestamp = System.currentTimeMillis()/1000+"";
String serial_no = weChatConfig.getSerialNo(userType);
String privateKeyFilePath = weChatConfig.getPrivateKeyUrl(userType);
// 生成签名
String signature = getSign(nonce_str, timestamp, url, privateKeyFilePath,"GET");
// 组装Authorization头部值
String authorizationHeader = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchid + "\", " +
"nonce_str=\"" + nonce_str + "\", " +
"signature=\"" + signature + "\", " +
"timestamp=\"" + timestamp + "\", " +
"serial_no=\"" + serial_no + "\"";
// 发起HTTP请求
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Authorization", authorizationHeader);
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
} catch (IOException e) {
throw new BusinessException("微信请求GET请求失败:" + e.getMessage());
}
return getResponse(response);
}
// 获取帧数
private static void setup(WeChatConfig weChatConfig, String userType) {
try {
String mchId =weChatConfig.getMchId(userType);
String serialNo = weChatConfig.getSerialNo(userType);
String privateKeyFilePath = weChatConfig.getPrivateKeyUrl(userType);
String apiV3Key = weChatConfig.getApiV3Key(userType);
byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKeyFilePath));
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKeyBytes));
certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(serialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
verifier = certificatesManager.getVerifier(mchId);
httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, serialNo, merchantPrivateKey).withValidator(new WechatPay2Validator(certificatesManager.getVerifier(mchId))).build();
} catch (IOException | NotFoundException | HttpCodeException | GeneralSecurityException e) {
e.printStackTrace();
}
}
// 请求返回处理
private static Map<String, Object> getResponse(CloseableHttpResponse response) {
try {
int statusCode = response.getStatusLine().getStatusCode();
Map<String, Object> map = new HashMap<>();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
if (entity != null) {
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>() {
}.getType();
map = gson.fromJson(EntityUtils.toString(entity, "UTF-8"), type);
}
return map;
} else {
throw new BusinessException("微信请求失败,请求失败码:" + statusCode);
}
} catch (IOException e) {
throw new BusinessException("微信请求失败,失败信息" + e.getMessage());
}
}
//##############################################内部方法#########################################################
public static String getSign(String nonceStr, String timestamp, String body, String privateKey,String method) {
String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
return sign(message.getBytes(StandardCharsets.UTF_8), privateKey);
}
private static String sign(byte[] message, String privateKey) {
try {
byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKey));
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKeyBytes));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException | IOException | SignatureException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
private static String buildMessage(String method, HttpUrl url, String timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
}