包
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.3.0</version>
</dependency>
整个下单的类
/**
* @author xyl
* @date 2022/7/11 16:39
* @explain
*/
@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {
@Value("${spring.profiles.active}")
private String active;
private static final String PROD = "prod";
private static final String APP_ID = "appid";
private static final String MCH_ID = "mchid";
private static final String GET = "GET";
private static final String PACKAGE = "Sign=WXPay";
private static final String SUCCESS = "SUCCESS";
private static final String TRADE_STATE = "trade_state";
private static final String OUT_TRADE_NO = "out_trade_no";
private static final String ACCOUNT_NO = "account_no";
private static final String TRANSACTION_ID = "transaction_id";
private static final String TOTAL = "total";
@Resource
private CloseableHttpClient wxPayClient;
@Autowired
private WxPayConfig wxPayConfig;
@Autowired
private ScheduledUpdateCertificatesVerifier verifier;
@Autowired
private RefundOrderService refundOrderService;
@Autowired
private ScoreOrderService scoreOrderService;
@Autowired
private TyGoodsAuthorityService goodsAuthorityService;
@Autowired
private MemberOrderService memberOrderService;
@Autowired
private StoreSettledOrderService storeSettledOrderService;
@Autowired
private ConsignmentSalesService consignmentSalesService;
@Autowired
private RechargeOrderService rechargeOrderService;
@Autowired
private AfterPayUtil afterPayUtil;
@Autowired
private OrderService orderService;
/**
* 微信下单
*
* @param dto
* @return {@link {@link HashMap< String, String>}}
* @author xyl
* @date 2022/7/12 16:22
* @explain
*/
@Override
public HashMap<String, String> buy(BuyCreateDto dto) {
//下单map
BuyUrlMapBo bo = getBuyUrlMapBo(dto);
HashMap<String, Object> buyMap = bo.getBuyMap();
//支付金额封装map
HashMap<String, Object> amountMap = new HashMap<>();
amountMap.put("total", dto.getTotal());
amountMap.put("currency", "CNY");
//加入金额
buyMap.put("amount", amountMap);
try {
//生成签名
Gson gson = new Gson();
String json = gson.toJson(buyMap);
HttpUrl httpurl = HttpUrl.parse(wxPayConfig.getDomain().concat(WeChatApiTypeEnum.GET_CERTIFICATES.getType()));
HashMap<String, String> token = getToken(GET, httpurl, json);
HttpPost httpPost = new HttpPost(bo.getUrl());
StringEntity entity = new StringEntity(json, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
CloseableHttpResponse response = wxPayClient.execute(httpPost);
//响应体
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(bodyAsString);
System.out.println("响应体" + bodyAsString);
//响应状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("状态码" + statusCode);
if (statusCode != 200) {
throw new BackendException(bodyAsString);
}
//封装返回参数
return getResMap(dto.getPayType(), buyMap, token, jsonObject);
} catch (Exception e) {
log.warn("微信支付失败,错误信息:{}", e.getMessage());
System.out.println(("微信支付失败,错误信息:{}" + e.getMessage()));
throw new BackendException(CommonEnum.PARAMETER_ERROR);
}
}
private boolean verifiedSign(String serialNo, String signStr, String signature) throws UnsupportedEncodingException {
return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
}
/**
* 微信下单组装返回
*
* @param buyMap 支付map
* @param token 签名
* @param jsonObject 下单返回结果
* @return {@link {@link HashMap< String, String>}}
* @author xyl
* @date 2022/7/12 16:05
* @explain
*/
private HashMap<String, String> getResMap(Integer payType, HashMap<String, Object> buyMap, HashMap<String, String> token, JSONObject jsonObject) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
HashMap<String, String> resMap = new HashMap<>();
StringBuilder stringBuilder = new StringBuilder();
String appid = buyMap.get("appid").toString();
resMap.put("appId", appid);
stringBuilder.append(appid).append("\n")
.append(token.get("timestamp")).append("\n")
.append(token.get("nonce_str")).append("\n");
if (payType.equals(PayTypeEnum.WX_APP_WECHAT_PAY.getCode())) {
resMap.put("package", "prepay_id=" + jsonObject.get("prepay_id").toString());
stringBuilder.append(resMap.get("package")).append("\n");
} else {
resMap.put("package", PACKAGE);
resMap.put("prepayId", jsonObject.get("prepay_id").toString());
resMap.put("partnerId", token.get("mchid"));
stringBuilder.append(jsonObject.get("prepay_id").toString()).append("\n");
}
resMap.put("nonceStr", token.get("nonce_str"));
resMap.put("timeStamp", token.get("timestamp"));
log.warn("签名字符串:" + stringBuilder);
String sign = sign(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
log.warn("获取的签名:" + sign);
resMap.put("signType", "RSA");
resMap.put("paySign", sign);
System.out.println(resMap);
return resMap;
}
/**
* 构建下单基础信息
*
* @param dto
* @return {@link {@link HashMap< String, Object>}}
* @author xyl
* @date 2022/7/12 9:18
* @explain
*/
private BuyUrlMapBo getBuyUrlMapBo(BuyCreateDto dto) {
BuyUrlMapBo bo = new BuyUrlMapBo();
HashMap<String, Object> buyMap = new HashMap<>();
//微信小程序
if (dto.getPayType() == PayTypeEnum.WX_APP_WECHAT_PAY.getCode()) {
//小程序appId
buyMap.put(APP_ID, wxPayConfig.getAppId());
//用户openId
HashMap<String, Object> payerMap = new HashMap<>();
payerMap.put("openid", dto.getOpenId());
buyMap.put("payer", payerMap);
//微信api接口
String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.JSAPI_PAY.getType());
bo.setUrl(url);
//微信app
} else if (dto.getPayType() == PayTypeEnum.APP_WECHAT_PAY.getCode()) {
buyMap.put(APP_ID, wxPayConfig.getAppAppId());
//微信api接口
String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.APP_PAY.getType());
bo.setUrl(url);
//微信H5
} else if (dto.getPayType() == PayTypeEnum.WX_H5_WECHAT_PAY.getCode()) {
//todo 暂无配置
buyMap.put(APP_ID, wxPayConfig.getAppAppId());
//微信api接口
String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.H5_PAY.getType());
bo.setUrl(url);
} else {
log.warn("发起支付失败,支付方式错误,请求参数:{}", dto);
throw new BackendException(CommonEnum.PARAMETER_ERROR);
}
buyMap.put(MCH_ID, wxPayConfig.getMchId());
buyMap.put("description", "商品购买");
buyMap.put("out_trade_no", dto.getOutTradeNo());
if (PROD.equals(active)) {
//线上回调地址
buyMap.put("notify_url", wxPayConfig.getPayNotifyUrl().concat(PayNotifyEnum.NATIVE_NOTIFY.getType()));
} else {
//测试线上回调地址
buyMap.put("notify_url", wxPayConfig.getPayNotifyTestUrl().concat(PayNotifyEnum.NATIVE_NOTIFY.getType()));
}
bo.setBuyMap(buyMap);
return bo;
}
/**
* 获取微信支付平台证书
*
* @param method 提交方式
* @param url 接口
* @param body 主体
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:45
* @explain
*/
public HashMap<String, String> getToken(String method, HttpUrl url, String body) throws UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
//随机字符串
String nonceStr = PayUtil.createNonceStr();
//当前时间戳
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"));
HashMap<String, String> map = new HashMap<>();
map.put("mchid", wxPayConfig.getMchId());
map.put("nonce_str", nonceStr);
map.put("timestamp", String.valueOf(timestamp));
map.put("serial_no", wxPayConfig.getMchSerialNo());
map.put("signature", signature);
return map;
}
/**
* 构建消息
*
* @param method 方式
* @param url 请求连接
* @param timestamp 时间戳
* @param nonceStr 随机字符串
* @param body 传递的主体
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:51
* @explain
*/
String buildMessage(String method, HttpUrl url, long 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";
}
/**
* 生成签名
*
* @param message
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 10:47
* @explain
*/
String sign(byte[] message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature sign = null;
try {
PrivateKey merchantPrivateKey = wxPayConfig.getPrivateKey();
sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
try {
sign.update(message);
} catch (SignatureException e) {
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 微信支付回调
*
* @param request 请求体
* @param response 响应体
* @return {@link {@link HashMap< String, String>}}
* @author xyl
* @date 2022/7/12 16:39
* @explain
*/
@Override
public HashMap<String, String> weChatPay(HttpServletRequest request, HttpServletResponse response) {
//获取报文
String body = getRequestBody(request);
log.info("微信支付回调,获得报文:{}", body);
//随机串
String nonceStr = request.getHeader("Wechatpay-Nonce");
//微信传递过来的签名
String signature = request.getHeader("Wechatpay-Signature");
//证书序列号(微信平台)
String serialNo = request.getHeader("Wechatpay-Serial");
//时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
//构造签名串
//应答时间戳\n
//应答随机串\n
//应答报文主体\n
String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
log.warn("微信支付回调,构建签名串:{}", signStr);
HashMap<String, String> resultMap = new HashMap<>(2);
try {
//验证签名是否通过
boolean result = verifiedSign(serialNo, signStr, signature);
log.warn("微信支付回调,验证签名结果:{}", result);
if (result) {
//解密数据
String plainBody = decryptBody(body);
log.warn("微信支付回调,解密后的明文:{}", plainBody);
Map<String, String> map = convertWeChatPayMsgToMap(plainBody);
//处理业务逻辑 TODO
if (map.get(TRADE_STATE).equals(SUCCESS)) {
log.warn("支付回调进入业务支付流程");
//获取订单号
String orderNo = map.get(OUT_TRADE_NO);
/购买会员支付回调
if (orderNo.startsWith("H")) {
MemberOrder memberOrder = memberOrderService.getByOrderNo(orderNo);
if (memberOrder == null || memberOrder.getOrderStatus() != 0) {
log.error("微信支付回调-----订单已处理;订单{}", memberOrder);
resultMap.put("code", "SUCCESS");
resultMap.put("message", "成功");
return resultMap;
}
memberOrderService.afterPaid(memberOrder,
System.currentTimeMillis(),
Long.parseLong(map.get(TOTAL)),
map.get(TRANSACTION_ID));
}
/积分商城支付回调
if (orderNo.startsWith("JF")) {
ScoreOrder scoreOrder = scoreOrderService.getByOrderNo(orderNo);
if (scoreOrder == null || scoreOrder.getStatus() != 0) {
log.error("微信支付回调-----订单已处理;订单{}", scoreOrder);
resultMap.put("code", "SUCCESS");
resultMap.put("message", "成功");
return resultMap;
}
scoreOrderService.afterPaid(null, scoreOrder,
System.currentTimeMillis(),
map.get(TRANSACTION_ID),
Long.parseLong(map.get(TOTAL)));
}
/充值订单支付回调
if (orderNo.startsWith("R")) {
RechargeOrder rechargeOrder = rechargeOrderService.getByOrderNo(orderNo);
if (rechargeOrder == null || rechargeOrder.getOrderStatus() != 0) {
log.error("微信支付回调-----订单已处理;订单{}", rechargeOrder);
map.put("code", "SUCCESS");
map.put("message", "成功");
return resultMap;
}
LambdaUpdateWrapper<RechargeOrder> updateWrapper = new UpdateWrapper<RechargeOrder>()
.lambda()
.eq(RechargeOrder::getId, rechargeOrder.getId())
.set(RechargeOrder::getOrderStatus, OrderStatusEnum.PENDING_DELIVERY.getCode())
.set(RechargeOrder::getActualAmount, map.get(TOTAL))
.set(RechargeOrder::getPayTime, System.currentTimeMillis())
.set(RechargeOrder::getPayId, map.get(TRANSACTION_ID));
if (rechargeOrderService.update(updateWrapper)) {
//记录用户账单
afterPayUtil.writeInUserBills(rechargeOrder, "充值");
}
}
/鉴定费支付回调
if (orderNo.startsWith("A")) {
TyGoodsAuthority goodsAuthority = goodsAuthorityService.findByOrderNo(orderNo);
if (goodsAuthority == null || !goodsAuthority.getAppraisalPayState().equals(TyGoodsAuthority.PAY_STATE_DZF)) {
log.error("微信支付回调-----订单已处理;订单{}", goodsAuthority);
map.put("code", "SUCCESS");
map.put("message", "成功");
return resultMap;
}
//付款完成修改状态
TyGoodsAuthority upGoodsAuthority = new TyGoodsAuthority();
upGoodsAuthority.setPayTime(new Date());
upGoodsAuthority.setPayId(map.get(TRANSACTION_ID));
upGoodsAuthority.setAppraisalPayState(TyGoodsAuthority.PAY_STATE_YZF);
AliyunSMSUtil.remand("(鉴定相关:用户已支付鉴定费,订单号:orderNo)");
goodsAuthorityService.updateById(upGoodsAuthority);
}
/二手交易支付回调
if (orderNo.startsWith(OrderConstant.M)) {
log.warn("微信支付回调,进入二手交易订单处理,订单号:{}", orderNo);
Order order = orderService.findOrderByOrderNo(orderNo);
if (order == null || order.getStatus() != OrderStatusEnum.PENDING_PAY.getCode()) {
log.warn("微信支付回调-----订单已处理;订单{}", order);
map.put("code", "SUCCESS");
map.put("message", "成功");
return resultMap;
}
Long payTime = System.currentTimeMillis();
Long actualPayAmount = Long.parseLong(map.get(TOTAL));
String payId = map.get(TRANSACTION_ID);
order.setPayTime(payTime);
order.setActualPayAmount(actualPayAmount);
order.setPayId(payId);
orderService.paymentSuccessfulOrderProcessing(order);
}
/商家入驻回调
if (orderNo.startsWith(OrderConstant.K)) {
log.warn("微信支付回调,进入商家入驻订单处理,订单号:{}", orderNo);
StoreSettledOrder order = storeSettledOrderService.findByOrderNo(orderNo);
if (order == null || order.getStatus() != 0) {
log.warn("微信支付回调-----订单已处理;订单{}", order);
map.put("code", "SUCCESS");
map.put("message", "成功");
return resultMap;
}
Long payTime = System.currentTimeMillis();
String payId = map.get(TRANSACTION_ID);
order.setPayTime(payTime);
order.setPayId(payId);
//处理商家入驻
storeSettledOrderService.settled(order);
}
/寄卖鉴定费回调
if (orderNo.startsWith(OrderConstant.C)) {
log.warn("微信支付回调,进入商家入驻订单处理,订单号:{}", orderNo);
ConsignmentSales order = consignmentSalesService.findByOrderNo(orderNo);
if (order == null || order.getStatus() != 4) {
log.warn("微信支付回调-----订单已处理;订单{}", order);
map.put("code", "SUCCESS");
map.put("message", "成功");
return resultMap;
}
Long payTime = System.currentTimeMillis();
String payId = map.get(TRANSACTION_ID);
order.setPayTime(payTime);
order.setPayId(payId);
order.setStatus(5);
boolean b = consignmentSalesService.updateById(order);
if (!b) {
log.warn("寄卖失败,支付鉴定费写入失败,写入信息:{}", order);
}
AliyunSMSUtil.remand("(寄卖鉴定费:用户已经支付,商品名称:{" + order.getName() + "})");
}
}
//响应微信
map.put("code", "SUCCESS");
map.put("message", "成功");
}
} catch (Exception e) {
log.warn("微信支付回调异常:{}", e.getMessage());
}
return resultMap;
}
/**
* 解密body的密文
* <p>
* "resource": {
* "original_type": "transaction",
* "algorithm": "AEAD_AES_256_GCM",
* "ciphertext": "",
* "associated_data": "",
* "nonce": ""
* }
*
* @param body
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 16:36
* @explain
*/
private String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {
AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
JSONObject object = JSONObject.parseObject(body);
JSONObject resource = object.getJSONObject("resource");
String ciphertext = resource.getString("ciphertext");
String associatedData = resource.getString("associated_data");
String nonce = resource.getString("nonce");
return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
}
/**
* 转换body为map
*
* @param plainBody
* @return {@link {@link Map< String, String>}}
* @author xyl
* @date 2022/7/12 16:35
* @explain
*/
private Map<String, String> convertWeChatPayMsgToMap(String plainBody) {
Map<String, String> paramsMap = new HashMap<>(2);
JSONObject jsonObject = JSONObject.parseObject(plainBody);
//商户订单号
paramsMap.put(OUT_TRADE_NO, jsonObject.getString(OUT_TRADE_NO));
//交易状态
paramsMap.put(TRADE_STATE, jsonObject.getString(TRADE_STATE));
//微信交易单号
paramsMap.put(TRANSACTION_ID, jsonObject.getString(TRANSACTION_ID));
//交易金额
String amount = jsonObject.get("amount").toString();
JSONObject amountJson = JSONObject.parseObject(amount);
paramsMap.put(TOTAL, amountJson.getString(TOTAL));
log.warn("微信V3支付回调,封装返回参数:{}", paramsMap);
return paramsMap;
}
/**
* 读取数据流
*
* @param request
* @return {@link {@link String}}
* @author xyl
* @date 2022/7/12 16:33
* @explain
*/
private String getRequestBody(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try (ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.warn("读取数据流异常:{}", e.getMessage());
}
return sb.toString();
}
}
下单dto
/**
* @author xyl
* @date 2022/7/11 17:16
* @explain
*/
@Data
@ApiModel("下单dto")
public class BuyCreateDto {
@ApiModelProperty(value = "商户订单号(自己的订单号)")
private String outTradeNo;
@ApiModelProperty(value = "支付的类型字典11")
private Integer payType;
@ApiModelProperty(value = "支付金额")
private Long total;
@ApiModelProperty(value = "用户OpenId")
private String openId;
public BuyCreateDto() {
}
public BuyCreateDto(String outTradeNo, Integer payType, Long total, String openId) {
this.outTradeNo = outTradeNo;
this.payType = payType;
this.total = total;
this.openId = openId;
}
}
构建下单的基础信息返回的bo
/**
* @author xyl
* @date 2022/7/12 13:13
* @explain
*/
@Data
@ApiModel("获取URL和构建支付mapBo")
public class BuyUrlMapBo {
@ApiModelProperty(value = "微信支付API接口")
private String url;
@ApiModelProperty(value = "构建的支付信息map")
private HashMap<String,Object> buyMap;
}
PayUtil工具类
/**
* @author xyl
*/
public class PayUtil {
/**
* 获取随机字符串
*
* @return 2016年8月4日 上午9:26:07
* @author Xuehao
*/
public static String createNonceStr() {
String s = UUID.randomUUID().toString();
// 去掉“-”符号
return s.replaceAll("\\-", "").replaceAll("-", "").toUpperCase();
}
/**
* 获取时间戳
*
* @return 2016年8月4日 上午9:26:20
* @author Xuehao
*/
public static String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
/**
* 构造签名
*
* @param params
* @param encode
* @return
* @throws UnsupportedEncodingException
*/
public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
Set<String> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
StringBuffer temp = new StringBuffer();
boolean first = true;
for (Object key : keys) {
if (first) {
first = false;
} else {
temp.append("&");
}
temp.append(key).append("=");
Object value = params.get(key);
String valueString = "";
if (null != value) {
valueString = value.toString();
}
if (encode) {
temp.append(URLEncoder.encode(valueString, "UTF-8"));
} else {
temp.append(valueString);
}
}
return temp.toString();
}
/**
* 生成签名
*
* @param parameters
* @param appSecret
* @return
*/
public static String createSign(SortedMap<String, Object> parameters, String appSecret) {
StringBuilder sb = new StringBuilder();
Set es = parameters.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k).append("=").append(v).append("&");
}
}
sb.append("key=").append(appSecret);
System.out.println(sb.toString());
return Md5Util.md5EncoderByCharset(sb.toString(), "UTF-8").toUpperCase();
}
//请求xml组装
public static String getRequestXml(SortedMap<String, Object> parameters) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
Set es = parameters.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
sb.append("<").append(key).append(">").append("<![CDATA[").append(value).append("]]></").append(key).append(">");
} else {
sb.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* xml解析
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
for (Object aList : list) {
Element e = (Element) aList;
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
private static String getChildrenText(List children) {
StringBuilder sb = new StringBuilder();
if (!children.isEmpty()) {
for (Object aChildren : children) {
Element e = (Element) aChildren;
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<").append(name).append(">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</").append(name).append(">");
}
}
return sb.toString();
}
/**
* 验证回调签名
*
* @param map
* @return
*/
public static boolean isTenpaySign(Map<String, String> map, String appSecret) {
String charset = "utf-8";
String signFromAPIResponse = map.get("sign");
if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
//过滤空 设置 TreeMap
SortedMap<String, String> packageParams = new TreeMap<>();
for (String parameter : map.keySet()) {
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
StringBuilder sb = new StringBuilder();
Set es = packageParams.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k).append("=").append(v).append("&");
}
}
sb.append("key=").append(appSecret);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//算出签名
String tobesign = sb.toString();
String resultSign = Md5Util.md5EncoderByCharset(tobesign, "UTF-8").toUpperCase();
String tenpaySign = packageParams.get("sign").toUpperCase();
return tenpaySign.equals(resultSign);
}
}
位置支付配置
@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
/**
* 微信支付
* @author xyl
* @date 2022/7/11 16:59
* @explain
*/
public class WxPayConfig {
/**
* 微信小程序appId
*/
private String appId;
/**
* 微信小程序秘钥
*/
private String appSmallSecret;
/**
* 微信appAppId
*/
private String appAppId;
/**
* 微信appApp秘钥
*/
private String appAppSecret;
/**
* 测试回调
*/
private String payNotifyTestUrl;
/**
* 正式回调
*/
private String payNotifyUrl;
/**
* 商户私钥文件
*/
private String mchSerialNo;
/**
* APIv3密钥
*/
private String apiV3Key;
/**
* 微信服务器地址
*/
private String domain;
/**
* 微信商户号
*/
private String mchId;
/**
* 微信秘钥
*/
private String mchKey;
/**
* 获取商户的私钥文件
*
* @return
*/
public PrivateKey getPrivateKey() {
InputStream resourceAsStream = this.getClass().getResourceAsStream("/cert/apiclient_key.pem");
return PemUtil.loadPrivateKey(resourceAsStream);
}
/**
* 获取签名验证器
*
* @return
*/
@Bean
public ScheduledUpdateCertificatesVerifier getVerifier() {
log.info("获取签名验证器");
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
System.out.println(privateKey);
//私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
//身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 使用定时更新的签名验证器,不需要传入证书
ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
wechatPay2Credentials,
apiV3Key.getBytes(StandardCharsets.UTF_8));
return verifier;
}
/**
* 获取http请求对象
*
* @param verifier
* @return
*/
@Bean(name = "wxPayClient")
public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {
log.info("获取httpClient");
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
/**
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
*/
@Bean(name = "wxPayNoSignClient")
public CloseableHttpClient getWxPayNoSignClient() {
//获取商户私钥
PrivateKey privateKey = getPrivateKey();
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//设置商户信息
.withMerchant(mchId, mchSerialNo, privateKey)
//无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
log.info("== getWxPayNoSignClient END ==");
return httpClient;
}
}
文件存放的位置
这个问坑爹的支付整整坑了我一天时间,希望各位不需要经历这种苦难,另外V3支付秘钥需要去微信商户里开通,此方法APP支付和微信小程序已经跑通但是H5还没有对接过,不过JSAPI支付的话与小程序应该差不多