feat:weixin-java from 4.7.2.B => 4.7.4.B

feat:微信支付 v3 从平台证书切换成微信支付公钥
feat:微信支付 v3 增加 header 解析
pull/190/head
YunaiV 2025-05-05 22:35:53 +08:00
parent a079633355
commit a27e0ec757
18 changed files with 94 additions and 73 deletions

View File

@ -82,7 +82,7 @@
<justauth.version>1.16.7</justauth.version> <justauth.version>1.16.7</justauth.version>
<justauth-starter.version>1.4.0</justauth-starter.version> <justauth-starter.version>1.4.0</justauth-starter.version>
<jimureport.version>1.9.4</jimureport.version> <jimureport.version>1.9.4</jimureport.version>
<weixin-java.version>4.7.2.B</weixin-java.version> <weixin-java.version>4.7.4.B</weixin-java.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>

View File

@ -66,7 +66,8 @@ public class PayNotifyController {
@TenantIgnore @TenantIgnore
public String notifyOrder(@PathVariable("channelId") Long channelId, public String notifyOrder(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map<String, String> params, @RequestParam(required = false) Map<String, String> params,
@RequestBody(required = false) String body) { @RequestBody(required = false) String body,
@RequestHeader Map<String, String> headers) {
log.info("[notifyOrder][channelId({}) 回调数据({}/{})]", channelId, params, body); log.info("[notifyOrder][channelId({}) 回调数据({}/{})]", channelId, params, body);
// 1. 校验支付渠道是否存在 // 1. 校验支付渠道是否存在
PayClient payClient = channelService.getPayClient(channelId); PayClient payClient = channelService.getPayClient(channelId);
@ -76,7 +77,7 @@ public class PayNotifyController {
} }
// 2. 解析通知数据 // 2. 解析通知数据
PayOrderRespDTO notify = payClient.parseOrderNotify(params, body); PayOrderRespDTO notify = payClient.parseOrderNotify(params, body, headers);
orderService.notifyOrder(channelId, notify); orderService.notifyOrder(channelId, notify);
return "success"; return "success";
} }
@ -87,7 +88,8 @@ public class PayNotifyController {
@TenantIgnore @TenantIgnore
public String notifyRefund(@PathVariable("channelId") Long channelId, public String notifyRefund(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map<String, String> params, @RequestParam(required = false) Map<String, String> params,
@RequestBody(required = false) String body) { @RequestBody(required = false) String body,
@RequestHeader Map<String, String> headers) {
log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body); log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body);
// 1. 校验支付渠道是否存在 // 1. 校验支付渠道是否存在
PayClient payClient = channelService.getPayClient(channelId); PayClient payClient = channelService.getPayClient(channelId);
@ -97,7 +99,7 @@ public class PayNotifyController {
} }
// 2. 解析通知数据 // 2. 解析通知数据
PayRefundRespDTO notify = payClient.parseRefundNotify(params, body); PayRefundRespDTO notify = payClient.parseRefundNotify(params, body, headers);
refundService.notifyRefund(channelId, notify); refundService.notifyRefund(channelId, notify);
return "success"; return "success";
} }
@ -108,7 +110,8 @@ public class PayNotifyController {
@TenantIgnore @TenantIgnore
public String notifyTransfer(@PathVariable("channelId") Long channelId, public String notifyTransfer(@PathVariable("channelId") Long channelId,
@RequestParam(required = false) Map<String, String> params, @RequestParam(required = false) Map<String, String> params,
@RequestBody(required = false) String body) { @RequestBody(required = false) String body,
@RequestHeader Map<String, String> headers) {
log.info("[notifyTransfer][channelId({}) 回调数据({}/{})]", channelId, params, body); log.info("[notifyTransfer][channelId({}) 回调数据({}/{})]", channelId, params, body);
// 1. 校验支付渠道是否存在 // 1. 校验支付渠道是否存在
PayClient payClient = channelService.getPayClient(channelId); PayClient payClient = channelService.getPayClient(channelId);
@ -118,7 +121,7 @@ public class PayNotifyController {
} }
// 2. 解析通知数据 // 2. 解析通知数据
PayTransferRespDTO notify = payClient.parseTransferNotify(params, body); PayTransferRespDTO notify = payClient.parseTransferNotify(params, body, headers);
payTransferService.notifyTransfer(channelId, notify); payTransferService.notifyTransfer(channelId, notify);
return "success"; return "success";
} }

View File

@ -89,7 +89,7 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
} }
@Override @Override
protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) { protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("钱包支付无支付回调"); throw new UnsupportedOperationException("钱包支付无支付回调");
} }
@ -144,7 +144,7 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
} }
@Override @Override
protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) { protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("钱包支付无退款回调"); throw new UnsupportedOperationException("钱包支付无退款回调");
} }
@ -178,7 +178,7 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
} }
@Override @Override
protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("未实现"); throw new UnsupportedOperationException("未实现");
} }

View File

@ -39,9 +39,10 @@ public interface PayClient {
* *
* @param params HTTP content type application/x-www-form-urlencoded * @param params HTTP content type application/x-www-form-urlencoded
* @param body HTTP request body * @param body HTTP request body
* @param headers HTTP request headers
* @return * @return
*/ */
PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body); PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body, Map<String, String> headers);
/** /**
* *
@ -66,9 +67,10 @@ public interface PayClient {
* *
* @param params HTTP content type application/x-www-form-urlencoded * @param params HTTP content type application/x-www-form-urlencoded
* @param body HTTP request body * @param body HTTP request body
* @param headers HTTP request headers
* @return * @return
*/ */
PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body); PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body, Map<String, String> headers);
/** /**
* 退 * 退
@ -103,8 +105,9 @@ public interface PayClient {
* *
* @param params HTTP content type application/x-www-form-urlencoded * @param params HTTP content type application/x-www-form-urlencoded
* @param body HTTP request body * @param body HTTP request body
* @param headers HTTP request headers
* @return * @return
*/ */
PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body); PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body, Map<String, String> headers);
} }

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client; package cn.iocoder.yudao.framework.pay.core.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import jakarta.validation.Validator; import jakarta.validation.Validator;
/** /**
@ -14,6 +14,7 @@ import jakarta.validation.Validator;
// @JsonTypeInfo 注解的作用Jackson 多态 // @JsonTypeInfo 注解的作用Jackson 多态
// 1. 序列化到时数据库时,增加 @class 属性。 // 1. 序列化到时数据库时,增加 @class 属性。
// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
@JsonIgnoreProperties(ignoreUnknown = true) // 目的:忽略未知的属性,避免反序列化失败
public interface PayClientConfig { public interface PayClientConfig {
/** /**

View File

@ -1,13 +1,13 @@
package cn.iocoder.yudao.framework.pay.core.client.dto.order; package cn.iocoder.yudao.framework.pay.core.client.dto.order;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Map; import java.util.Map;

View File

@ -1,5 +1,8 @@
package cn.iocoder.yudao.framework.pay.core.client.dto.refund; package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -7,10 +10,6 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
/** /**
* 退 Request DTO * 退 Request DTO
* *

View File

@ -2,18 +2,18 @@ package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*; import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.Alipay;
import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.WxPay;
/** /**
* Request DTO * Request DTO

View File

@ -101,19 +101,19 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
throws Throwable; throws Throwable;
@Override @Override
public final PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) { public final PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) {
try { try {
return doParseOrderNotify(params, body); return doParseOrderNotify(params, body, headers);
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
throw ex; throw ex;
} catch (Throwable ex) { } catch (Throwable ex) {
log.error("[parseOrderNotify][客户端({}) params({}) body({}) 解析失败]", log.error("[parseOrderNotify][客户端({}) params({}) body({}) headers({}) 解析失败]",
getId(), params, body, ex); getId(), params, body, headers, ex);
throw buildPayException(ex); throw buildPayException(ex);
} }
} }
protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers)
throws Throwable; throws Throwable;
@Override @Override
@ -155,19 +155,19 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
@Override @Override
public final PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) { public final PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) {
try { try {
return doParseRefundNotify(params, body); return doParseRefundNotify(params, body, headers);
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
throw ex; throw ex;
} catch (Throwable ex) { } catch (Throwable ex) {
log.error("[parseRefundNotify][客户端({}) params({}) body({}) 解析失败]", log.error("[parseRefundNotify][客户端({}) params({}) body({}) headers({}) 解析失败]",
getId(), params, body, ex); getId(), params, body, headers, ex);
throw buildPayException(ex); throw buildPayException(ex);
} }
} }
protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers)
throws Throwable; throws Throwable;
@Override @Override
@ -220,19 +220,19 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
} }
@Override @Override
public final PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body) { public final PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
try { try {
return doParseTransferNotify(params, body); return doParseTransferNotify(params, body, headers);
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
throw ex; throw ex;
} catch (Throwable ex) { } catch (Throwable ex) {
log.error("[doParseTransferNotify][客户端({}) params({}) body({}) 解析失败]", log.error("[doParseTransferNotify][客户端({}) params({}) body({}) headers({}) 解析失败]",
getId(), params, body, ex); getId(), params, body, headers, ex);
throw buildPayException(ex); throw buildPayException(ex);
} }
} }
protected abstract PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) protected abstract PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers)
throws Throwable; throws Throwable;
@Override @Override

View File

@ -1,9 +1,8 @@
package cn.iocoder.yudao.framework.pay.core.client.impl; package cn.iocoder.yudao.framework.pay.core.client.impl;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import lombok.Data;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import lombok.Data;
/** /**
* PayClientConfig * PayClientConfig

View File

@ -79,7 +79,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
} }
@Override @Override
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable { public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
// 1. 校验回调数据 // 1. 校验回调数据
Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(), AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
@ -175,7 +175,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
} }
@Override @Override
public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) { public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) {
// 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。 // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
// ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调 // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
// ② 全部退款Wap 支付有订单状态的同步回调,但是 PC/扫码又没有 // ② 全部退款Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
@ -327,7 +327,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
// TODO @chihuo这里是不是也要实现支付宝的。 // TODO @chihuo这里是不是也要实现支付宝的。
@Override @Override
protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("未实现"); throw new UnsupportedOperationException("未实现");
} }

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayApiException;

View File

@ -5,7 +5,6 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayApiException;

View File

@ -2,11 +2,10 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import lombok.Data;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data;
/** /**
* PayClientConfig * PayClientConfig

View File

@ -4,7 +4,6 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Method; import cn.hutool.http.Method;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayApiException;

View File

@ -58,17 +58,17 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
} }
@Override @Override
protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable { protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("未实现"); throw new UnsupportedOperationException("未实现");
} }
@Override @Override
protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) { protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("模拟支付无退款回调"); throw new UnsupportedOperationException("模拟支付无退款回调");
} }
@Override @Override
protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) { protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) {
throw new UnsupportedOperationException("模拟支付无支付回调"); throw new UnsupportedOperationException("模拟支付无支付回调");
} }

View File

@ -18,10 +18,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.WxPayTransferPart
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result; import com.github.binarywang.wxpay.bean.notify.*;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest; import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
@ -67,13 +64,14 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
protected void doInit(String tradeType) { protected void doInit(String tradeType) {
// 创建 config 配置 // 创建 config 配置
WxPayConfig payConfig = new WxPayConfig(); WxPayConfig payConfig = new WxPayConfig();
BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent"); BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent", "publicKeyContent");
payConfig.setTradeType(tradeType); payConfig.setTradeType(tradeType);
// weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决 // weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决
if (Objects.equals(config.getApiVersion(), API_VERSION_V2)) { if (Objects.equals(config.getApiVersion(), API_VERSION_V2)) {
payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath()); payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath());
} else if (Objects.equals(config.getApiVersion(), API_VERSION_V3)) { } else if (Objects.equals(config.getApiVersion(), API_VERSION_V3)) {
payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
payConfig.setPublicKeyPath(FileUtils.createTempFile(config.getPublicKeyContent()).getPath());
} }
// 创建 client 客户端 // 创建 client 客户端
@ -157,12 +155,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} }
@Override @Override
public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws WxPayException { public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws WxPayException {
switch (config.getApiVersion()) { switch (config.getApiVersion()) {
case API_VERSION_V2: case API_VERSION_V2:
return doParseOrderNotifyV2(body); return doParseOrderNotifyV2(body);
case API_VERSION_V3: case API_VERSION_V3:
return doParseOrderNotifyV3(body); return doParseOrderNotifyV3(body, headers);
default: default:
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
} }
@ -179,9 +177,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
response.getOutTradeNo(), body); response.getOutTradeNo(), body);
} }
private PayOrderRespDTO doParseOrderNotifyV3(String body) throws WxPayException { private PayOrderRespDTO doParseOrderNotifyV3(String body, Map<String, String> headers) throws WxPayException {
// 1. 解析回调 // 1. 解析回调
WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, null); SignatureHeader signatureHeader = getRequestHeader(headers);
WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader);
WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult(); WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult();
// 2. 构建结果 // 2. 构建结果
Integer status = parseStatus(result.getTradeState()); Integer status = parseStatus(result.getTradeState());
@ -321,12 +320,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} }
@Override @Override
public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) throws WxPayException { public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body, Map<String, String> headers) throws WxPayException {
switch (config.getApiVersion()) { switch (config.getApiVersion()) {
case API_VERSION_V2: case API_VERSION_V2:
return doParseRefundNotifyV2(body); return doParseRefundNotifyV2(body);
case API_VERSION_V3: case API_VERSION_V3:
return parseRefundNotifyV3(body); return parseRefundNotifyV3(body, headers);
default: default:
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
} }
@ -344,9 +343,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response); return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
} }
private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException { private PayRefundRespDTO parseRefundNotifyV3(String body, Map<String, String> headers) throws WxPayException {
// 1. 解析回调 // 1. 解析回调
WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null); SignatureHeader signatureHeader = getRequestHeader(headers);
WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, signatureHeader);
WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult(); WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult();
// 2. 构建结果 // 2. 构建结果
if (Objects.equals("SUCCESS", result.getRefundStatus())) { if (Objects.equals("SUCCESS", result.getRefundStatus())) {
@ -357,10 +357,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} }
@Override @Override
public PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws WxPayException { public PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) throws WxPayException {
switch (config.getApiVersion()) { switch (config.getApiVersion()) {
case API_VERSION_V3: case API_VERSION_V3:
return parseTransferNotifyV3(body); return parseTransferNotifyV3(body, headers);
case API_VERSION_V2: case API_VERSION_V2:
throw new UnsupportedOperationException("V2 版本暂不支持,建议使用 V3 版本"); throw new UnsupportedOperationException("V2 版本暂不支持,建议使用 V3 版本");
default: default:
@ -368,10 +368,11 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} }
} }
private PayTransferRespDTO parseTransferNotifyV3(String body) throws WxPayException { private PayTransferRespDTO parseTransferNotifyV3(String body, Map<String, String> headers) throws WxPayException {
// 1. 解析回调 // 1. 解析回调
SignatureHeader signatureHeader = getRequestHeader(headers);
// TODO @luchi这个可以复用 wxjava 里的类么? // TODO @luchi这个可以复用 wxjava 里的类么?
WxPayTransferPartnerNotifyV3Result response = client.baseParseOrderNotifyV3Result(body, null, WxPayTransferPartnerNotifyV3Result.class, WxPayTransferPartnerNotifyV3Result.TransferNotifyResult.class); WxPayTransferPartnerNotifyV3Result response = client.baseParseOrderNotifyV3Result(body, signatureHeader, WxPayTransferPartnerNotifyV3Result.class, WxPayTransferPartnerNotifyV3Result.TransferNotifyResult.class);
WxPayTransferPartnerNotifyV3Result.TransferNotifyResult result = response.getResult(); WxPayTransferPartnerNotifyV3Result.TransferNotifyResult result = response.getResult();
// 2. 构建结果 // 2. 构建结果
if (Objects.equals("FINISHED", result.getBatchStatus())) { if (Objects.equals("FINISHED", result.getBatchStatus())) {
@ -513,6 +514,20 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
// ========== 各种工具方法 ========== // ========== 各种工具方法 ==========
/**
*
*
* @see <a href="https://github.com/binarywang/weixin-java-pay-demo/blob/master/src/main/java/com/github/binarywang/demo/wx/pay/controller/WxPayV3Controller.java#L202-L221"></a>
*/
private SignatureHeader getRequestHeader(Map<String, String> headers) {
return SignatureHeader.builder()
.signature(headers.get("wechatpay-signature"))
.nonce(headers.get("wechatpay-nonce"))
.serial(headers.get("wechatpay-serial"))
.timeStamp(headers.get("wechatpay-timestamp"))
.build();
}
static String formatDateV2(LocalDateTime time) { static String formatDateV2(LocalDateTime time) {
return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN); return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
} }

View File

@ -73,13 +73,18 @@ public class WxPayClientConfig implements PayClientConfig {
@NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class) @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class)
private String apiV3Key; private String apiV3Key;
/** /**
* * merchantSerialNumber
*/ */
@NotBlank(message = "证书序列号不能为空", groups = V3.class) @NotBlank(message = "证书序列号不能为空", groups = V3.class)
private String certSerialNo; private String certSerialNo;
@Deprecated // TODO 芋艿V2.3.0 进行移除 /**
private String privateCertContent; * pub_key.pem
*/
@NotBlank(message = "pub_key.pem 不能为空", groups = V3.class)
private String publicKeyContent;
@NotBlank(message = "publicKeyId 不能为空", groups = V3.class)
private String publicKeyId;
/** /**
* v2 * v2