diff --git a/payment-spring-boot-autoconfigure/pom.xml b/payment-spring-boot-autoconfigure/pom.xml index a662222..22b38b4 100644 --- a/payment-spring-boot-autoconfigure/pom.xml +++ b/payment-spring-boot-autoconfigure/pom.xml @@ -72,11 +72,6 @@ jackson-dataformat-xml true - - com.squareup.okhttp3 - okhttp - true - org.bouncycastle bcprov-jdk15to18 diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java index 32e9a7f..1dfc671 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java @@ -1,12 +1,7 @@ package com.enongm.dianji.payment.wechat; - -import com.enongm.dianji.payment.wechat.v2.WechatPayV2Service; -import com.enongm.dianji.payment.wechat.v3.KeyPairFactory; -import com.enongm.dianji.payment.wechat.v3.SignatureProvider; -import com.enongm.dianji.payment.wechat.v3.WechatPayV3Service; -import com.enongm.dianji.payment.wechat.v3.model.WechatMetaBean; +import com.enongm.dianji.payment.wechat.v3.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -50,24 +45,25 @@ public class WechatPayConfiguration { /** - * 微信支付V2 只实现V3支付没有的支付业务。 - * - * @param wechatPayProperties the wechat pay properties - * @return the wechat pay v 2 service - */ - @Bean - public WechatPayV2Service wechatPayV2Service(WechatPayProperties wechatPayProperties) { - return new WechatPayV2Service(wechatPayProperties); - } - - /** - * 微信支付V3 全量支持. + * 微信支付V3 客户端. * * @param signatureProvider the signature provider * @return the wechat pay service */ @Bean - public WechatPayV3Service wechatPayService(SignatureProvider signatureProvider) { - return new WechatPayV3Service(signatureProvider); + public WechatPayV3Client wechatPayService(SignatureProvider signatureProvider) { + return new WechatPayV3Client(signatureProvider); + } + + /** + * Wechat pay v3 api. + * + * @param wechatPayV3Client the wechat pay v 3 client + * @param wechatMetaBean the wechat meta bean + * @return the wechat pay v 3 api + */ + @Bean + public WechatPayV3Api wechatPayV3Api(WechatPayV3Client wechatPayV3Client,WechatMetaBean wechatMetaBean) { + return new WechatPayV3Api(wechatPayV3Client,wechatMetaBean); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayResponseErrorHandler.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayResponseErrorHandler.java new file mode 100644 index 0000000..63be7e4 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayResponseErrorHandler.java @@ -0,0 +1,18 @@ +package com.enongm.dianji.payment.wechat; + +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.DefaultResponseErrorHandler; + +import java.io.IOException; + +/** + * @author Dax + * @since 12:57 + */ +public class WechatPayResponseErrorHandler extends DefaultResponseErrorHandler { + + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return false; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V2PayType.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V2PayType.java deleted file mode 100644 index affb538..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V2PayType.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.enongm.dianji.payment.wechat.enumeration; - -/** - * @author Dax - * @since 10:33 - */ -public enum V2PayType { - - /** - * 企业向微信用户个人付款,目前支持向指定微信用户的openid付款。 - */ - PAY_TO_WECHAT("POST", "%s%s/mmpaymkttransfers/promotion/transfers"); - - private final String method; - private final String pattern; - - V2PayType(String method, String pattern) { - this.method = method; - this.pattern = pattern; - } - - /** - * Method string. - * - * @return the string - */ - public String method() { - return this.method; - } - - - /** - * 默认支付URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String defaultUri(WeChatServer weChatServer) { - return uri(weChatServer, false, false); - } - - /** - * 默认支付沙盒URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String defaultSandboxUri(WeChatServer weChatServer) { - return uri(weChatServer, true, false); - } - - /** - * 合作商支付URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String partnerUri(WeChatServer weChatServer) { - return uri(weChatServer, false, true); - } - - /** - * 合作商支付沙盒URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String partnerSandboxUri(WeChatServer weChatServer) { - return uri(weChatServer, true, true); - } - - /** - * @param isSandbox 是否是沙盒测试 - * @param isPartner 是否是合作商 - * @return uri - */ - private String uri(WeChatServer weChatServer, boolean isSandbox, boolean isPartner) { - - final String sandboxKey = isSandbox ? "/sandboxnew" : ""; - final String partnerKey = isPartner ? "/partner" : ""; - return String.format(this.pattern, weChatServer.domain(), sandboxKey, partnerKey); - } -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V3PayType.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V3PayType.java index 5013de5..ec41a2a 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V3PayType.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/V3PayType.java @@ -1,5 +1,7 @@ package com.enongm.dianji.payment.wechat.enumeration; +import org.springframework.http.HttpMethod; + /** * The enum Pay type. * @@ -10,32 +12,45 @@ public enum V3PayType { /** * 获取证书. */ - CERT("GET", "%s/v3/certificates"), + CERT(HttpMethod.GET, "%s/v3/certificates"), /** * 微信公众号支付或者小程序支付 */ - JSAPI("POST", "%s%s/v3/pay%s/transactions/jsapi"), + JSAPI(HttpMethod.POST, "%s/v3/pay/transactions/jsapi"), /** * 微信扫码支付 */ - NATIVE("POST", "%s%s/v3/pay%s/transactions/native"), + NATIVE(HttpMethod.POST, "%s/v3/pay/transactions/native"), /** * 微信APP支付 */ - APP("POST", "%s%s/v3/pay%s/transactions/app"), + APP(HttpMethod.POST, "%s/v3/pay/transactions/app"), /** * H5支付 */ - MWEB("POST", "%s%s/v3/pay%s/transactions/h5"); + MWEB(HttpMethod.POST, "%s/v3/pay/transactions/h5"), + + + /** + * 激活代金券批次API + */ + MARKETING_FAVOR_STOCKS_START(HttpMethod.POST,"%s/v3/marketing/favor/stocks/{stock_id}/start"), + /** + * 查询代金券可用商户 + */ + MARKETING_FAVOR_STOCKS_MERCHANTS(HttpMethod.GET, "%s/v3/marketing/favor/stocks/{stock_id}/merchants"); + + + private final String pattern; - private final String method; + private final HttpMethod method; - V3PayType(String method, String pattern) { + V3PayType(HttpMethod method, String pattern) { this.method = method; this.pattern = pattern; } @@ -45,7 +60,7 @@ public enum V3PayType { * * @return the string */ - public String method() { + public HttpMethod method() { return this.method; } @@ -56,53 +71,8 @@ public enum V3PayType { * @param weChatServer the we chat server * @return the string */ - public String defaultUri(WeChatServer weChatServer) { - return uri(weChatServer, false, false); + public String uri(WeChatServer weChatServer) { + return String.format(this.pattern,weChatServer.domain()); } - /** - * 默认支付沙盒URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String defaultSandboxUri(WeChatServer weChatServer) { - return uri(weChatServer, true, false); - } - - /** - * 合作商支付URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String partnerUri(WeChatServer weChatServer) { - return uri(weChatServer, false, true); - } - - /** - * 合作商支付沙盒URI. - * - * @param weChatServer the we chat server - * @return the string - */ - public String partnerSandboxUri(WeChatServer weChatServer) { - return uri(weChatServer, true, true); - } - - /** - * @param isSandbox 是否是沙盒测试 - * @param isPartner 是否是合作商 - * @return uri - */ - private String uri(WeChatServer weChatServer, boolean isSandbox, boolean isPartner) { - - if (this.equals(V3PayType.CERT)) { - return String.format(this.pattern, WeChatServer.CHINA.domain()); - } - - final String sandboxKey = isSandbox ? "/sandboxnew" : ""; - final String partnerKey = isPartner ? "/partner" : ""; - return String.format(this.pattern, weChatServer.domain(), sandboxKey, partnerKey); - } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatPayV2Service.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatPayV2Service.java deleted file mode 100644 index cb7f40d..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatPayV2Service.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.enongm.dianji.payment.wechat.v2; - - -import com.enongm.dianji.payment.wechat.WechatPayProperties; -import com.enongm.dianji.payment.wechat.v2.model.BaseModel; - -/** - * The type Wechat pay v 2 service. - * - * @author Dax - * @since 15 :15 - */ -public class WechatPayV2Service { - - private final WechatPayProperties wechatPayProperties; - - /** - * Instantiates a new Wechat pay v 2 service. - * - * @param wechatPayProperties the wechat pay properties - */ - public WechatPayV2Service(WechatPayProperties wechatPayProperties) { - this.wechatPayProperties = wechatPayProperties; - } - - - /** - * Model base model. - * - * @param the type parameter - * @param model the model - * @return the base model - */ - public BaseModel model(M model) { - WechatPayProperties.V3 v3 = wechatPayProperties.getV3(); - return model.appId(v3.getAppId()) - .mchId(v3.getMchId()) - .appSecret(v3.getAppSecret()); - } - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatResponseBody.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatResponseBody.java deleted file mode 100644 index 7c2b601..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/WechatResponseBody.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.enongm.dianji.payment.wechat.v2; - -import lombok.Data; - -/** - * @author Dax - * @since 15:28 - */ -@Data -public class WechatResponseBody { - - private String returnCode; - private String returnMsg; - private String mchAppid; - private String mchid; - private String deviceInfo; - private String nonceStr; - private String resultCode; - private String errCode; - private String errCodeDes; - private String partnerTradeNo; - private String paymentNo; - private String paymentTime; -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/BaseModel.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/BaseModel.java deleted file mode 100644 index 47c005b..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/BaseModel.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.enongm.dianji.payment.wechat.v2.model; - - -import com.enongm.dianji.payment.PayException; -import com.enongm.dianji.payment.wechat.enumeration.V2PayType; -import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; -import com.enongm.dianji.payment.wechat.v2.WechatResponseBody; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import lombok.Getter; -import lombok.SneakyThrows; -import okhttp3.*; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.digests.MD5Digest; -import org.bouncycastle.util.encoders.Hex; -import org.springframework.util.AlternativeJdkIdGenerator; -import org.springframework.util.Assert; -import org.springframework.util.IdGenerator; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * The type Base model. - * - * @author Dax - * @since 16 :03 - */ -@Getter -public class BaseModel { - private static final XmlMapper MAPPER = new XmlMapper(); - - static { - // 忽略null - MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL) - // 属性使用 驼峰首字母小写 - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); - } - - - private static final OkHttpClient CLIENT = new OkHttpClient(); - private static final IdGenerator ID_GENERATOR = new AlternativeJdkIdGenerator(); - private final String nonceStr = ID_GENERATOR.generateId() - .toString() - .replaceAll("-", ""); - private String mchAppid; - private String mchid; - private String sign; - @JsonIgnore - private String appSecret; - @JsonIgnore - private V2PayType payType; - @JsonIgnore - private final WeChatServer weChatServer = WeChatServer.CHINA; - @JsonIgnore - private boolean sandboxMode; - @JsonIgnore - private boolean partnerMode; - - - public BaseModel appId(String appId) { - this.mchAppid = appId; - return this; - } - - public BaseModel mchId(String mchId) { - this.mchid = mchId; - return this; - } - - public BaseModel payType(V2PayType payType) { - this.payType = payType; - return this; - } - - public BaseModel appSecret(String appSecret) { - this.appSecret = appSecret; - return this; - } - - public BaseModel sandboxMode(boolean sandboxMode) { - this.sandboxMode = sandboxMode; - return this; - } - public BaseModel partnerMode(boolean partnerMode) { - this.partnerMode = partnerMode; - return this; - } - - /** - * Xml string. - * - * @return the string - */ - @SneakyThrows - public String xml() { - String link = link(this); - this.sign = this.bouncyCastleMD5(link); - return MAPPER.writer() - .withRootName("xml") - .writeValueAsString(this); - } - - /** - * md5摘要. - * - * @param src the src - * @return the string - */ - private String bouncyCastleMD5(String src) { - Digest digest = new MD5Digest(); - byte[] bytes = src.getBytes(StandardCharsets.UTF_8); - digest.update(bytes, 0, bytes.length); - byte[] md5Bytes = new byte[digest.getDigestSize()]; - digest.doFinal(md5Bytes, 0); - return Hex.toHexString(md5Bytes).toUpperCase(); - } - - /** - * 按照格式拼接参数以生成签名 - * - * @param the type parameter - * @param t the t - * @return the map - */ - @SneakyThrows - private String link(T t) { - Assert.hasText(this.mchAppid, "wechat pay appId is required"); - Assert.hasText(this.mchid, "wechat pay mchId is required"); - Assert.hasText(appSecret, "wechat pay appSecret is required"); - return new ObjectMapper() - .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .writer() - .writeValueAsString(t) - .replaceAll("\":\"", "=") - .replaceAll("\",\"", "&") - .replaceAll("\\{\"", "") - .replaceAll("\"}", "") - .concat("&key=").concat(this.appSecret); - } - - - @SneakyThrows - public WechatResponseBody request() { - Assert.notNull(payType, "wechat pay payType is required"); - - String url = payType.defaultUri(this.weChatServer); - if (sandboxMode) { - url = partnerMode ? this.payType.partnerSandboxUri(this.weChatServer) : - this.payType.defaultSandboxUri(this.weChatServer); - } - - Request request = new Request.Builder() - .method(payType.method(), RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), this.xml())) - .url(url) - .build(); - System.out.println("request.toString() = " + request.toString()); - Response response = CLIENT.newCall(request).execute(); - - ResponseBody body = response.body(); - if (Objects.nonNull(body)) { - return MAPPER.readValue(body.string(), WechatResponseBody.class); - } - throw new PayException("wechat pay response body is empty"); - } - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/PayToWechatModel.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/PayToWechatModel.java deleted file mode 100644 index c1641ff..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v2/model/PayToWechatModel.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.enongm.dianji.payment.wechat.v2.model; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * @author Dax - * @since 15:48 - */ -@EqualsAndHashCode(callSuper = true) -@Data -public class PayToWechatModel extends BaseModel { - private String deviceInfo; - private String partnerTradeNo; - private String openid; - private String checkName = "NO_CHECK"; - private String reUserName; - private String amount; - private String desc; - private String spbillCreateIp; - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/DefaultPayFilterChain.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/DefaultPayFilterChain.java index e20cb20..d688bfb 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/DefaultPayFilterChain.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/DefaultPayFilterChain.java @@ -1,7 +1,6 @@ package com.enongm.dianji.payment.wechat.v3; - import java.util.ArrayList; import java.util.List; @@ -19,11 +18,11 @@ public class DefaultPayFilterChain implements PayFilterChain { } @Override - public void doChain(WechatPayRequest request) { + public void doChain(WechatRequestEntity requestEntity) { int size = filters.size(); if (pos < size) { PayFilter payFilter = filters.get(pos++); - payFilter.doFilter(request, this); + payFilter.doFilter(requestEntity, this); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/KeyPairFactory.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/KeyPairFactory.java index 0cb5a5c..8b5ca8c 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/KeyPairFactory.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/KeyPairFactory.java @@ -2,7 +2,6 @@ package com.enongm.dianji.payment.wechat.v3; import com.enongm.dianji.payment.PayException; -import com.enongm.dianji.payment.wechat.v3.model.WechatMetaBean; import org.springframework.core.io.ClassPathResource; import java.security.KeyPair; diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilter.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilter.java index 072aef9..20be17d 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilter.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilter.java @@ -7,15 +7,12 @@ package com.enongm.dianji.payment.wechat.v3; * @author Dax * @since 15 :08 */ - public interface PayFilter { /** * Do filter. - * - * @param request the request - * @param chain the chain - */ - void doFilter(WechatPayRequest request, PayFilterChain chain); + * @param requestEntity the request entity + * @param chain the chain*/ + void doFilter(WechatRequestEntity requestEntity, PayFilterChain chain); } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilterChain.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilterChain.java index f685e50..0930b9f 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilterChain.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/PayFilterChain.java @@ -12,9 +12,9 @@ public interface PayFilterChain { /** * Do chain. * - * @param request the request + * @param requestEntity the request entity */ - void doChain(WechatPayRequest request); + void doChain(WechatRequestEntity requestEntity); /** * Register. diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/SignatureProvider.java index 452b6f1..28e6657 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/SignatureProvider.java @@ -4,19 +4,18 @@ package com.enongm.dianji.payment.wechat.v3; import com.enongm.dianji.payment.PayException; import com.enongm.dianji.payment.wechat.enumeration.V3PayType; import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; -import com.enongm.dianji.payment.wechat.v3.model.WechatMetaBean; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.SneakyThrows; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; +import org.springframework.http.*; import org.springframework.util.AlternativeJdkIdGenerator; import org.springframework.util.Base64Utils; import org.springframework.util.IdGenerator; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; @@ -29,6 +28,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Arrays; +import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -53,6 +53,7 @@ public class SignatureProvider { public static final String APPLICATION_JSON = "application/json"; private static final IdGenerator ID_GENERATOR = new AlternativeJdkIdGenerator(); private static final String SCHEMA = "WECHATPAY2-SHA256-RSA2048 "; + private final RestOperations restOperations = new RestTemplate(); /** * The constant TOKEN_PATTERN. */ @@ -135,39 +136,33 @@ public class SignatureProvider { */ @SneakyThrows private synchronized void refreshCertificate() { - String url = V3PayType.CERT.defaultUri(WeChatServer.CHINA); + String url = V3PayType.CERT.uri(WeChatServer.CHINA); - HttpUrl httpUrl = HttpUrl.get(url); + UriComponents uri = UriComponentsBuilder.fromHttpUrl(url).build(); + + String canonicalUrl = uri.getPath(); + String encodedQuery = uri.getQuery(); - String canonicalUrl = httpUrl.encodedPath(); - String encodedQuery = httpUrl.encodedQuery(); if (encodedQuery != null) { canonicalUrl += "?" + encodedQuery; } // 签名 - String authorization = requestSign(V3PayType.CERT.method(), canonicalUrl, ""); + HttpMethod httpMethod = V3PayType.CERT.method(); + String authorization = requestSign(httpMethod.name(), canonicalUrl, ""); - Request request = new Request.Builder() - .get() - .url(httpUrl) - .addHeader("Accept", APPLICATION_JSON) - .addHeader("Authorization", authorization) - .addHeader("Content-Type", APPLICATION_JSON) - .addHeader("User-Agent", "pay-service") - .build(); + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add("Authorization", authorization); + headers.add("User-Agent", "pay-service"); + RequestEntity requestEntity = new RequestEntity<>(headers, httpMethod, uri.toUri()); + ResponseEntity responseEntity = restOperations.exchange(requestEntity, ObjectNode.class); + ObjectNode bodyObjectNode = responseEntity.getBody(); - - Response response = new OkHttpClient().newCall(request).execute(); - - if (Objects.isNull(response.body())) { + if (Objects.isNull(bodyObjectNode)) { throw new PayException("cant obtain the response body"); } - - String body = response.body().string(); - ObjectMapper mapper = new ObjectMapper(); - ObjectNode bodyObjectNode = mapper.readValue(body, ObjectNode.class); ArrayNode certificates = bodyObjectNode.withArray("data"); - if (certificates.isArray() && certificates.size() > 0) { CERTIFICATE_MAP.clear(); final CertificateFactory cf = CertificateFactory.getInstance("X509"); diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/V3PayTypeBodyAndConsumer.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/V3PayTypeBodyAndConsumer.java deleted file mode 100644 index 7b10a08..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/V3PayTypeBodyAndConsumer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3; - -import com.enongm.dianji.payment.wechat.enumeration.V3PayType; - -import java.util.function.Consumer; - -/** - * @author Dax - * @since 15:27 - */ -public class V3PayTypeBodyAndConsumer { - private final V3PayType payType; - private final String jsonBody; - private final Consumer responseBodyConsumer; - - public V3PayTypeBodyAndConsumer(V3PayType payType, String jsonBody, Consumer responseBodyConsumer) { - this.payType = payType; - this.jsonBody = jsonBody; - this.responseBodyConsumer = responseBodyConsumer; - } - - public V3PayType getPayType() { - return payType; - } - - public String getJsonBody() { - return jsonBody; - } - - public Consumer getResponseBodyConsumer() { - return responseBodyConsumer; - } -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/WechatMetaBean.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatMetaBean.java similarity index 93% rename from payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/WechatMetaBean.java rename to payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatMetaBean.java index e128490..33ae035 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/WechatMetaBean.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatMetaBean.java @@ -1,4 +1,4 @@ -package com.enongm.dianji.payment.wechat.v3.model; +package com.enongm.dianji.payment.wechat.v3; import com.enongm.dianji.payment.wechat.WechatPayProperties; diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayRequest.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayRequest.java deleted file mode 100644 index 230849f..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayRequest.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3; - - -import com.enongm.dianji.payment.wechat.enumeration.V3PayType; -import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; -import lombok.Getter; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -/** - * The type Wechat pay request. - * - * @author Dax - * @since 9 :18 - */ -@Getter -public class WechatPayRequest { - private V3PayType v3PayType; - private boolean isSandbox; - private boolean isPartner; - private WeChatServer weChatServer; - private String body; - private final Map headers = new HashMap<>(); - private Consumer responseBodyConsumer; - - - /** - * Pay type wechat pay request. - * - * @param v3PayType the pay type - * @return the wechat pay request - */ - public WechatPayRequest v3PayType(V3PayType v3PayType) { - this.v3PayType = v3PayType; - return this; - } - - /** - * Is sandbox wechat pay request. - * - * @param isSandbox the is sandbox - * @return the wechat pay request - */ - public WechatPayRequest isSandbox(boolean isSandbox) { - this.isSandbox = isSandbox; - return this; - } - - /** - * Is partner wechat pay request. - * - * @param isPartner the is partner - * @return the wechat pay request - */ - public WechatPayRequest isPartner(boolean isPartner) { - this.isPartner = isPartner; - return this; - } - - /** - * We chat server. - * - * @param weChatServer the we chat server - */ - public void weChatServer(WeChatServer weChatServer) { - this.weChatServer = weChatServer; - } - - - /** - * Body wechat pay request. - * - * @param body the body - * @return the wechat pay request - */ - public WechatPayRequest body(String body) { - this.body = body; - return this; - } - - /** - * Add header wechat pay request. - * - * @param name the name - * @param value the value - * @return the wechat pay request - */ - public WechatPayRequest addHeader(String name, String value) { - headers.put(name, value); - return this; - } - - /** - * Response consumer wechat pay request. - * - * @param responseConsumer the response consumer - * @return the wechat pay request - */ - public WechatPayRequest responseConsumer(Consumer responseConsumer) { - this.responseBodyConsumer = responseConsumer; - return this; - } - - /** - * Url string. - * - * @return the string - */ - public String url() { - if (isSandbox) { - return isPartner ? this.v3PayType.partnerSandboxUri(this.weChatServer): - this.v3PayType.defaultSandboxUri(this.weChatServer); - } - return this.v3PayType.defaultUri(this.weChatServer); - } - - /** - * Default sandbox uri string. - * - * @return the string - */ - public String defaultSandboxUri() { - return this.v3PayType.defaultSandboxUri(this.weChatServer); - } - - /** - * Partner uri string. - * - * @return the string - */ - public String partnerUri() { - return this.v3PayType.partnerUri(this.weChatServer); - } - - /** - * Partner sandbox uri string. - * - * @return the string - */ - public String partnerSandboxUri() { - return this.v3PayType.partnerSandboxUri(this.weChatServer); - } - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java new file mode 100644 index 0000000..824dc6b --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java @@ -0,0 +1,133 @@ +package com.enongm.dianji.payment.wechat.v3; + +import com.enongm.dianji.payment.PayException; +import com.enongm.dianji.payment.wechat.WechatPayProperties; +import com.enongm.dianji.payment.wechat.enumeration.V3PayType; +import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; +import com.enongm.dianji.payment.wechat.v3.model.AppPayParams; +import com.enongm.dianji.payment.wechat.v3.model.StocksMchQueryParams; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.RequestEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +/** + * The type Wechat pay v 3 api. + * + * @author Dax + * @since 16 :15 + */ +public class WechatPayV3Api { + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final WechatPayV3Client wechatPayV3Client; + private final WechatMetaBean wechatMetaBean; + + static { + MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + } + + + /** + * Wechat Pay V3 Api. + * + * @param wechatPayV3Client the wechat pay v 3 client + * @param wechatMetaBean the wechat meta bean + */ + public WechatPayV3Api(WechatPayV3Client wechatPayV3Client, WechatMetaBean wechatMetaBean) { + this.wechatPayV3Client = wechatPayV3Client; + this.wechatMetaBean = wechatMetaBean; + } + + + /** + * 激活代金券批次API + * + * @param stockId the stock id + */ + public WechatResponseEntity startStocks(String stockId) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + wechatPayV3Client.withPayType(V3PayType.MARKETING_FAVOR_STOCKS_START, stockId) + .function((v3PayType, s) -> { + WechatPayProperties.V3 v3 = wechatMetaBean.getWechatPayProperties().getV3(); + String mchId = v3.getMchId(); + String httpUrl = v3PayType.uri(WeChatServer.CHINA); + URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().expand(stockId).toUri(); + + Map map = new HashMap<>(); + map.put("stock_creator_mchid", mchId); + try { + return RequestEntity.post(uri) + .body(MAPPER.writeValueAsString(map)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + throw new PayException("wechat app pay json failed"); + }).consumer(wechatResponseEntity::convert).request(); + + return wechatResponseEntity; + } + + /** + * 查询代金券可用商户API + * + * @param params the params + */ + public WechatResponseEntity queryMerchantsByStockId(StocksMchQueryParams params) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + wechatPayV3Client.withPayType(V3PayType.MARKETING_FAVOR_STOCKS_MERCHANTS, params) + .function((v3PayType, par) -> { + + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("offset", String.valueOf(par.getOffset())); + queryParams.add("limit", String.valueOf(par.getLimit())); + queryParams.add("stock_creator_mchid", par.getStockCreatorMchid()); + String httpUrl = v3PayType.uri(WeChatServer.CHINA); + + URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl) + .queryParams(queryParams) + .build() + .expand(par.getStockId()).toUri(); + + return RequestEntity.get(uri).build(); + }).consumer(wechatResponseEntity::convert).request(); + + return wechatResponseEntity; + + } + + /** + * APP下单API. + * + * @param payParams the pay params + */ + public WechatResponseEntity appPay(AppPayParams payParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + wechatPayV3Client.withPayType(V3PayType.APP, payParams) + .function((v3PayType, par) -> { + WechatPayProperties.V3 v3 = wechatMetaBean.getWechatPayProperties().getV3(); + par.setAppid(v3.getAppId()); + par.setMchid(v3.getMchId()); + String httpUrl = v3PayType.uri(WeChatServer.CHINA); + URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().toUri(); + try { + return RequestEntity.post(uri) + .body(MAPPER.writeValueAsString(par)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + throw new PayException("wechat app pay json failed"); + }).consumer(wechatResponseEntity::convert).request(); + + return wechatResponseEntity; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Client.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Client.java new file mode 100644 index 0000000..ff41862 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Client.java @@ -0,0 +1,126 @@ +package com.enongm.dianji.payment.wechat.v3; + + +import com.enongm.dianji.payment.wechat.enumeration.V3PayType; +import com.enongm.dianji.payment.wechat.v3.filter.HeaderFilter; +import com.enongm.dianji.payment.wechat.v3.filter.HttpRequestFilter; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.SneakyThrows; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; + +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/** + * The type Wechat pay client. + * + * @author Dax + * @since 11 :43 + */ +public class WechatPayV3Client { + private final PayFilterChain payFilterChain; + + /** + * Instantiates a new Wechat pay service. + * + * @param signatureProvider the signature provider + */ + public WechatPayV3Client(SignatureProvider signatureProvider) { + DefaultPayFilterChain defaultPayFilterChain = new DefaultPayFilterChain(); + // 构造私钥签名 + defaultPayFilterChain.register(new HeaderFilter(signatureProvider)); + defaultPayFilterChain.register(new HttpRequestFilter(signatureProvider)); + this.payFilterChain = defaultPayFilterChain; + } + + + /** + * 构造 {@link WechatRequestEntity}. + * + * @param the type parameter + * @param v3PayType the v 3 pay type + * @param m the m + * @return the executor + */ + public Executor withPayType(V3PayType v3PayType,M m) { + return new Executor<>(v3PayType,m ,this.payFilterChain); + } + + + /** + * The type Executor. + * + * @param the type parameter + */ + public static class Executor { + /** + * The V 3 pay type. + */ + private final V3PayType v3PayType; + + /** + * The Pay filter chain. + */ + private final PayFilterChain payFilterChain; + private final M model; + + /** + * The Request entity bi function. + */ + private BiFunction> requestEntityBiFunction; + + /** + * The Response body consumer. + */ + private Consumer> responseBodyConsumer; + + /** + * Instantiates a new Executor. + * + * @param v3PayType the v 3 pay type + * @param model the model + * @param payFilterChain the pay filter chain + */ + public Executor(V3PayType v3PayType, + M model, + PayFilterChain payFilterChain) { + this.v3PayType = v3PayType; + this.model = model; + this.payFilterChain = payFilterChain; + } + + /** + * Function executor. + * + * @param requestEntityBiFunction the request entity bi function + * @return the executor + */ + public Executor function(BiFunction> requestEntityBiFunction) { + this.requestEntityBiFunction = requestEntityBiFunction; + return this; + } + + /** + * Consumer executor. + * + * @param responseBodyConsumer the response body consumer + * @return the executor + */ + public Executor consumer(Consumer> responseBodyConsumer) { + this.responseBodyConsumer = responseBodyConsumer; + return this; + } + + + /** + * Request. + */ + @SneakyThrows + public void request() { + RequestEntity requestEntity = this.requestEntityBiFunction.apply(this.v3PayType, this.model); + payFilterChain.doChain(WechatRequestEntity.of(requestEntity, this.responseBodyConsumer)); + } + } + +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Service.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Service.java deleted file mode 100644 index fd831e3..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Service.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3; - - - -import com.enongm.dianji.payment.wechat.v3.filter.*; -import com.enongm.dianji.payment.wechat.v3.model.BaseModel; -import com.enongm.dianji.payment.wechat.v3.model.WechatMetaBean; - -import java.util.function.Function; - -/** - * The type Wechat pay service. - * - * @author Dax - * @since 11 :43 - */ -public class WechatPayV3Service { - private final PayFilterChain payFilterChain; - - /** - * Instantiates a new Wechat pay service. - * - * @param payFilterChain the pay filter chain - */ - public WechatPayV3Service(PayFilterChain payFilterChain) { - this.payFilterChain = payFilterChain; - } - - /** - * Instantiates a new Wechat pay service. - * - * @param signatureProvider the signature provider - */ - public WechatPayV3Service(SignatureProvider signatureProvider) { - WechatMetaBean wechatMetaBean = signatureProvider.getWechatMetaBean(); - DefaultPayFilterChain defaultPayFilterChain = new DefaultPayFilterChain(); - // 微信服务器选择 - defaultPayFilterChain.register(new WechatServerFilter()); - // 对请求体注入业务无关参数 - defaultPayFilterChain.register(new BodyMergeFilter(wechatMetaBean)); - // 构造私钥签名 - defaultPayFilterChain.register(new HeaderFilter(signatureProvider)); - defaultPayFilterChain.register(new HttpRequestFilter(signatureProvider)); - - this.payFilterChain = defaultPayFilterChain; - } - - - /** - * Exec executor. - * - * @param the type parameter - * @param the type parameter - * @param requestFunction the request function - * @return the executor - */ - public Executor request(Function requestFunction) { - return new Executor<>(this.payFilterChain, requestFunction); - } - - - /** - * The type Executor. - * - * @param the type parameter - * @param the type parameter - */ - public static class Executor { - /** - * The Pay filter chain. - */ - PayFilterChain payFilterChain; - /** - * The Request function. - */ - Function requestFunction; - - /** - * Instantiates a new Executor. - * - * @param payFilterChain the pay filter chain - * @param requestFunction the request function - */ - public Executor(PayFilterChain payFilterChain, Function requestFunction) { - this.payFilterChain = payFilterChain; - this.requestFunction = requestFunction; - } - - /** - * With model. - * - * @param model the model - */ - public void withModel(M model) { - R apply = requestFunction.apply(model); - WechatPayRequest wechatPayRequest = new WechatPayRequest().v3PayType(apply.getPayType()) - .body(apply.getJsonBody()) - .responseConsumer(apply.getResponseBodyConsumer()); - payFilterChain.doChain(wechatPayRequest); - } - } - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java new file mode 100644 index 0000000..be9ae1b --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java @@ -0,0 +1,140 @@ +package com.enongm.dianji.payment.wechat.v3; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Getter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; + +import java.lang.reflect.Type; +import java.net.URI; +import java.util.function.Consumer; + +/** + * The type Wechat request entity. + * + * @param the type parameter + * @author Dax + * @since 14 :01 + */ +@Getter +public class WechatRequestEntity extends RequestEntity { + + private final Consumer> responseBodyConsumer; + + + /** + * Instantiates a new Wechat request entity. + * + * @param method the method + * @param url the url + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(HttpMethod method, URI url, Consumer> responseBodyConsumer) { + super(method, url); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Instantiates a new Wechat request entity. + * + * @param body the body + * @param method the method + * @param url the url + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(T body, HttpMethod method, URI url, Consumer> responseBodyConsumer) { + super(body, method, url); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Instantiates a new Wechat request entity. + * + * @param body the body + * @param method the method + * @param url the url + * @param type the type + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(T body, HttpMethod method, URI url, Type type, Consumer> responseBodyConsumer) { + super(body, method, url, type); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Instantiates a new Wechat request entity. + * + * @param headers the headers + * @param method the method + * @param url the url + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(MultiValueMap headers, HttpMethod method, URI url, Consumer> responseBodyConsumer) { + super(headers, method, url); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Instantiates a new Wechat request entity. + * + * @param body the body + * @param headers the headers + * @param method the method + * @param url the url + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(T body, MultiValueMap headers, HttpMethod method, URI url, Consumer> responseBodyConsumer) { + super(body, headers, method, url); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Instantiates a new Wechat request entity. + * + * @param body the body + * @param headers the headers + * @param method the method + * @param url the url + * @param type the type + * @param responseBodyConsumer the response body consumer + */ + public WechatRequestEntity(T body, MultiValueMap headers, HttpMethod method, URI url, Type type, Consumer> responseBodyConsumer) { + super(body, headers, method, url, type); + this.responseBodyConsumer = responseBodyConsumer; + } + + /** + * Headers wechat request entity. + * + * @param httpHeaders the http headers + * @return the wechat request entity + */ + public WechatRequestEntity headers(HttpHeaders httpHeaders) { + return new WechatRequestEntity<>(this.getBody(), + httpHeaders, + this.getMethod(), + this.getUrl(), + this.getType(), + this.responseBodyConsumer); + } + + + /** + * Of wechat request entity. + * + * @param requestEntity the request entity + * @param responseBodyConsumer the response body consumer + * @return the wechat request entity + */ + public static WechatRequestEntity of(RequestEntity requestEntity, Consumer> responseBodyConsumer) { + return new WechatRequestEntity<>(requestEntity.getBody(), + requestEntity.getHeaders(), + requestEntity.getMethod(), + requestEntity.getUrl(), + requestEntity.getType(), + responseBodyConsumer); + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatResponseEntity.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatResponseEntity.java new file mode 100644 index 0000000..e7f7435 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatResponseEntity.java @@ -0,0 +1,25 @@ +package com.enongm.dianji.payment.wechat.v3; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; + +/** + * @author Dax + * @since 13:20 + */ +@Slf4j +@Data +public class WechatResponseEntity { + private int httpStatus; + private T body; + + + public void convert(ResponseEntity responseEntity) { + if (log.isDebugEnabled()) { + log.info("wechat response {}", responseEntity); + } + this.httpStatus = responseEntity.getStatusCodeValue(); + this.body = responseEntity.getBody(); + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/BodyMergeFilter.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/BodyMergeFilter.java deleted file mode 100644 index df70abe..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/BodyMergeFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3.filter; - - -import com.enongm.dianji.payment.wechat.WechatPayProperties; -import com.enongm.dianji.payment.wechat.v3.PayFilter; -import com.enongm.dianji.payment.wechat.v3.PayFilterChain; -import com.enongm.dianji.payment.wechat.v3.WechatPayRequest; -import com.enongm.dianji.payment.wechat.v3.model.WechatMetaBean; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.SneakyThrows; -import org.springframework.util.StringUtils; - -/** - * The type Body merge filter. - * 2 - * - * @author Dax - * @since 11 :13 - */ -public class BodyMergeFilter implements PayFilter { - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final WechatMetaBean wechatMetaBean; - - static { - MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); - MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); - } - - public BodyMergeFilter(WechatMetaBean wechatMetaBean) { - this.wechatMetaBean = wechatMetaBean; - } - - @Override - public void doFilter(WechatPayRequest request, PayFilterChain chain) { - mergeAppIdAndMchIdIntoBody(request); - chain.doChain(request); - } - - - /** - * 将直连商户申请的公众号或移动应用appid 、商户号mchid 放入请求体 - */ - @SneakyThrows - private void mergeAppIdAndMchIdIntoBody(WechatPayRequest request) { - - String requestBody = request.getBody(); - if (StringUtils.hasText(requestBody)) { - WechatPayProperties wechatPayProperties = wechatMetaBean.getWechatPayProperties(); - String appId = wechatPayProperties.getV3().getAppId(); - String mchId = wechatPayProperties.getV3().getMchId(); - - ObjectNode jsonNodes = MAPPER.readValue(requestBody, ObjectNode.class); - JsonNode notify = jsonNodes.get("notify_url"); - String notifyUrl = wechatPayProperties.getV3().getDomain() - .concat(notify.asText()); - // ^https?://([^\\s/?#\\[\\]\\@]+\\@)?([^\\s/?#\\@:]+)(?::\\d{2,5})?([^\\s?#\\[\\]]*)$ - jsonNodes.put("notify_url", notifyUrl); - jsonNodes.put("appid", appId); - jsonNodes.put("mchid", mchId); - - request.body(MAPPER.writeValueAsString(jsonNodes)); - } - } - - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HeaderFilter.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HeaderFilter.java index 4aef6bc..6afa006 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HeaderFilter.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HeaderFilter.java @@ -4,8 +4,16 @@ package com.enongm.dianji.payment.wechat.v3.filter; import com.enongm.dianji.payment.wechat.v3.PayFilter; import com.enongm.dianji.payment.wechat.v3.PayFilterChain; import com.enongm.dianji.payment.wechat.v3.SignatureProvider; -import com.enongm.dianji.payment.wechat.v3.WechatPayRequest; -import okhttp3.HttpUrl; +import com.enongm.dianji.payment.wechat.v3.WechatRequestEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Collections; +import java.util.Objects; /** * 微信支付 给请求添加必要的请求头. @@ -15,7 +23,6 @@ import okhttp3.HttpUrl; * @since 15 :12 */ public class HeaderFilter implements PayFilter { - private static final String APPLICATION_JSON = "application/json"; private final SignatureProvider signatureProvider; public HeaderFilter(SignatureProvider signatureProvider) { @@ -23,25 +30,28 @@ public class HeaderFilter implements PayFilter { } @Override - public void doFilter(WechatPayRequest request, PayFilterChain chain) { + public void doFilter(WechatRequestEntity requestEntity, PayFilterChain chain) { - // 签名 - HttpUrl url = HttpUrl.get(request.url()); + UriComponents uri = UriComponentsBuilder.fromUri(requestEntity.getUrl()).build(); + String canonicalUrl = uri.getPath(); + String encodedQuery = uri.getQuery(); - String canonicalUrl = url.encodedPath(); - String encodedQuery = url.encodedQuery(); if (encodedQuery != null) { canonicalUrl += "?" + encodedQuery; } - String method = request.getV3PayType().method(); - String body = "GET".equals(method) ? "" : request.getBody(); + // 签名 + HttpMethod httpMethod = requestEntity.getMethod(); + Assert.notNull(httpMethod, "httpMethod is required"); + String body = httpMethod.matches("GET") ? "" : Objects.requireNonNull(requestEntity.getBody()).toString(); + String authorization = signatureProvider.requestSign(httpMethod.name(), canonicalUrl, body); - String authorization = signatureProvider.requestSign(method, canonicalUrl, body); - request.addHeader("Accept", APPLICATION_JSON) - .addHeader("Authorization", authorization) - .addHeader("Content-Type", APPLICATION_JSON) - .addHeader("User-Agent", "pay-service"); - chain.doChain(request); + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add("Authorization", authorization); + headers.add("User-Agent", "pay-service"); + + chain.doChain(requestEntity.headers(headers)); } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HttpRequestFilter.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HttpRequestFilter.java index 7735a2c..420ae9a 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HttpRequestFilter.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/HttpRequestFilter.java @@ -2,14 +2,19 @@ package com.enongm.dianji.payment.wechat.v3.filter; import com.enongm.dianji.payment.PayException; +import com.enongm.dianji.payment.wechat.WechatPayResponseErrorHandler; import com.enongm.dianji.payment.wechat.v3.PayFilter; import com.enongm.dianji.payment.wechat.v3.PayFilterChain; import com.enongm.dianji.payment.wechat.v3.SignatureProvider; -import com.enongm.dianji.payment.wechat.v3.WechatPayRequest; +import com.enongm.dianji.payment.wechat.v3.WechatRequestEntity; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import okhttp3.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; -import java.io.IOException; import java.util.Objects; import java.util.function.Consumer; @@ -21,69 +26,51 @@ import java.util.function.Consumer; */ @Slf4j public class HttpRequestFilter implements PayFilter { - private final OkHttpClient client; + private final RestOperations restOperations; private final SignatureProvider signatureProvider; public HttpRequestFilter(SignatureProvider signatureProvider) { this.signatureProvider = signatureProvider; - this.client = new OkHttpClient(); - + RestTemplate restTemplate = new RestTemplate(); + DefaultResponseErrorHandler errorHandler = new WechatPayResponseErrorHandler(); + restTemplate.setErrorHandler(errorHandler); + this.restOperations = restTemplate; } - public HttpRequestFilter(SignatureProvider signatureProvider, OkHttpClient client) { - this.signatureProvider = signatureProvider; - this.client = client; - } - - @Override - public void doFilter(WechatPayRequest request, PayFilterChain chain) { + public void doFilter(WechatRequestEntity requestEntity, PayFilterChain chain) { - Request.Builder builder = new Request.Builder() - .url(request.url()) - .headers(Headers.of(request.getHeaders())); + ResponseEntity responseEntity = restOperations.exchange(requestEntity, ObjectNode.class); + HttpHeaders headers = responseEntity.getHeaders(); + ObjectNode body = responseEntity.getBody(); - if (!request.getV3PayType().method().equals("GET")) { - RequestBody requestBody = - RequestBody.create(MediaType.parse("application/json"), request.getBody()); - builder.method(request.getV3PayType().method(), requestBody); + if (Objects.isNull(body)) { + throw new IllegalStateException("cant obtain response body"); } + // 微信请求回调id + // String RequestId = response.header("Request-ID"); + // 微信平台证书序列号 用来取微信平台证书 + String wechatpaySerial = headers.getFirst("Wechatpay-Serial"); + //获取应答签名 + String wechatpaySignature = headers.getFirst("Wechatpay-Signature"); + //构造验签名串 + String wechatpayTimestamp = headers.getFirst("Wechatpay-Timestamp"); + String wechatpayNonce = headers.getFirst("Wechatpay-Nonce"); - Request httpRequest = builder.build(); - - try { - Response response = client.newCall(httpRequest).execute(); - ResponseBody responseBody = response.body(); - - if (Objects.isNull(responseBody)) { - throw new IllegalStateException("cant obtain response body"); + // 验证微信服务器签名 + if (signatureProvider.responseSignVerify(wechatpaySerial, + wechatpaySignature, + wechatpayTimestamp, + wechatpayNonce, + body.toString())) { + Consumer> responseConsumer = requestEntity.getResponseBodyConsumer(); + if (Objects.nonNull(responseConsumer)) { + // 验证通过消费 + responseConsumer.accept(responseEntity); } - // 微信请求回调id -// String RequestId = response.header("Request-ID"); - // 微信平台证书序列号 用来取微信平台证书 - String wechatpaySerial = response.header("Wechatpay-Serial"); - //获取应答签名 - String wechatpaySignature = response.header("Wechatpay-Signature"); - String body = responseBody.string(); - - //构造验签名串 - String wechatpayTimestamp = response.header("Wechatpay-Timestamp"); - String wechatpayNonce = response.header("Wechatpay-Nonce"); - - // 验证微信服务器签名 - if (signatureProvider.responseSignVerify(wechatpaySerial, wechatpaySignature, wechatpayTimestamp, wechatpayNonce, body)) { - Consumer responseConsumer = request.getResponseBodyConsumer(); - if (Objects.nonNull(responseConsumer)) { - // 验证通过消费 - responseConsumer.accept(body); - } - } else { - throw new PayException("wechat pay signature failed"); - } - - } catch (IOException e) { - throw new PayException("wechat pay http request failed"); + } else { + throw new PayException("wechat pay signature failed"); } } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/WechatServerFilter.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/WechatServerFilter.java deleted file mode 100644 index a6e96fc..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/filter/WechatServerFilter.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3.filter; - - -import com.enongm.dianji.payment.wechat.v3.PayFilter; -import com.enongm.dianji.payment.wechat.v3.PayFilterChain; -import com.enongm.dianji.payment.wechat.enumeration.V3PayType; -import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; -import com.enongm.dianji.payment.wechat.v3.WechatPayRequest; - -/** - * 根据{@link V3PayType} 组装URL - * 1 - * - * @author Dax - * @since 9:43 - */ -public class WechatServerFilter implements PayFilter { - - @Override - public void doFilter(WechatPayRequest request, PayFilterChain chain) { - request.weChatServer(WeChatServer.CHINA); - chain.doChain(request); - } - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayModel.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayModel.java deleted file mode 100644 index 4a44fa4..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayModel.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3.model; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * @author Dax - * @since 17:10 - */ -@EqualsAndHashCode(callSuper = true) -@Data -public class AppPayModel extends BaseModel { - private Amount amount; - private Detail detail; - private SceneInfo sceneInfo; - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/BaseModel.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayParams.java similarity index 62% rename from payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/BaseModel.java rename to payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayParams.java index 97e8640..64bf386 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/BaseModel.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/AppPayParams.java @@ -1,18 +1,18 @@ package com.enongm.dianji.payment.wechat.v3.model; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; + import lombok.Data; -import lombok.SneakyThrows; /** + * The type App pay model. + * * @author Dax - * @since 16:34 + * @since 17 :10 */ @Data -public class BaseModel { - +public class AppPayParams { + private String appid; + private String mchid; /** * 商品描述 * Image形象店-深圳腾大-QQ公仔 @@ -44,11 +44,8 @@ public class BaseModel { */ private Amount amount; - @SneakyThrows - public String jsonBody() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - return objectMapper.writeValueAsString(this); - } + private Detail detail; + private SceneInfo sceneInfo; + + } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/Goods.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/Goods.java index b48d3f4..d4492b6 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/Goods.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/Goods.java @@ -1,9 +1,12 @@ package com.enongm.dianji.payment.wechat.v3.model; +import lombok.Data; + /** * @author Dax * @since 17:02 */ +@Data public class Goods { /** * 商户侧商品编码 diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/SettleInfo.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/SettleInfo.java index 6daa722..9c60f8a 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/SettleInfo.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/SettleInfo.java @@ -1,9 +1,12 @@ package com.enongm.dianji.payment.wechat.v3.model; +import lombok.Data; + /** * @author Dax * @since 17:05 */ +@Data public class SettleInfo { /** * 是否指定分账 diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java new file mode 100644 index 0000000..5bab7f6 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java @@ -0,0 +1,16 @@ +package com.enongm.dianji.payment.wechat.v3.model; + +import lombok.Data; + +/** + * @author Dax + * @since 16:22 + */ +@Data +public class StocksMchQueryParams { + private int offset =1; + private int limit = 10; + private String stockCreatorMchid; + private String stockId; + +} diff --git a/payment-spring-boot-starter/pom.xml b/payment-spring-boot-starter/pom.xml index 9bf8b98..af1d171 100644 --- a/payment-spring-boot-starter/pom.xml +++ b/payment-spring-boot-starter/pom.xml @@ -42,10 +42,6 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - - com.squareup.okhttp3 - okhttp - org.bouncycastle bcprov-jdk15to18 diff --git a/pom.xml b/pom.xml index 9067990..1d8ca0b 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,6 @@ 1.18.12 2.9.10 1.66 - 3.14.9 @@ -74,11 +73,6 @@ jackson-dataformat-xml ${jackson.version} - - com.squareup.okhttp3 - okhttp - ${okhttp3.version} - org.bouncycastle bcprov-jdk15to18