替换掉okhttp 使用 spring rest

This commit is contained in:
xiafang
2020-11-20 18:00:51 +08:00
parent ccd2304a7b
commit a4e5384430
33 changed files with 613 additions and 951 deletions

View File

@@ -72,11 +72,6 @@
<artifactId>jackson-dataformat-xml</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 <M> the type parameter
* @param model the model
* @return the base model
*/
public <M extends BaseModel> BaseModel model(M model) {
WechatPayProperties.V3 v3 = wechatPayProperties.getV3();
return model.appId(v3.getAppId())
.mchId(v3.getMchId())
.appSecret(v3.getAppSecret());
}
}

View File

@@ -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;
}

View File

@@ -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 <T> the type parameter
* @param t the t
* @return the map
*/
@SneakyThrows
private <T> 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");
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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<ObjectNode> 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");

View File

@@ -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<String> responseBodyConsumer;
public V3PayTypeBodyAndConsumer(V3PayType payType, String jsonBody, Consumer<String> responseBodyConsumer) {
this.payType = payType;
this.jsonBody = jsonBody;
this.responseBodyConsumer = responseBodyConsumer;
}
public V3PayType getPayType() {
return payType;
}
public String getJsonBody() {
return jsonBody;
}
public Consumer<String> getResponseBodyConsumer() {
return responseBodyConsumer;
}
}

View File

@@ -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;

View File

@@ -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<String, String> headers = new HashMap<>();
private Consumer<String> 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<String> 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);
}
}

View File

@@ -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<ObjectNode> 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<String, String> 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<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
wechatPayV3Client.withPayType(V3PayType.MARKETING_FAVOR_STOCKS_MERCHANTS, params)
.function((v3PayType, par) -> {
MultiValueMap<String, String> 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<ObjectNode> 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;
}
}

View File

@@ -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 <M> the type parameter
* @param v3PayType the v 3 pay type
* @param m the m
* @return the executor
*/
public <M> Executor<M> withPayType(V3PayType v3PayType,M m) {
return new Executor<>(v3PayType,m ,this.payFilterChain);
}
/**
* The type Executor.
*
* @param <M> the type parameter
*/
public static class Executor<M> {
/**
* 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<V3PayType, M, RequestEntity<?>> requestEntityBiFunction;
/**
* The Response body consumer.
*/
private Consumer<ResponseEntity<ObjectNode>> 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<M> function(BiFunction<V3PayType, M, RequestEntity<?>> requestEntityBiFunction) {
this.requestEntityBiFunction = requestEntityBiFunction;
return this;
}
/**
* Consumer executor.
*
* @param responseBodyConsumer the response body consumer
* @return the executor
*/
public Executor<M> consumer(Consumer<ResponseEntity<ObjectNode>> 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));
}
}
}

View File

@@ -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 <M> the type parameter
* @param <R> the type parameter
* @param requestFunction the request function
* @return the executor
*/
public <M extends BaseModel, R extends V3PayTypeBodyAndConsumer> Executor<M, R> request(Function<M, R> requestFunction) {
return new Executor<>(this.payFilterChain, requestFunction);
}
/**
* The type Executor.
*
* @param <M> the type parameter
* @param <R> the type parameter
*/
public static class Executor<M extends BaseModel, R extends V3PayTypeBodyAndConsumer> {
/**
* The Pay filter chain.
*/
PayFilterChain payFilterChain;
/**
* The Request function.
*/
Function<M, R> requestFunction;
/**
* Instantiates a new Executor.
*
* @param payFilterChain the pay filter chain
* @param requestFunction the request function
*/
public Executor(PayFilterChain payFilterChain, Function<M, R> 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);
}
}
}

View File

@@ -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 <T> the type parameter
* @author Dax
* @since 14 :01
*/
@Getter
public class WechatRequestEntity<T> extends RequestEntity<T> {
private final Consumer<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<String, String> headers, HttpMethod method, URI url, Consumer<ResponseEntity<ObjectNode>> 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<String, String> headers, HttpMethod method, URI url, Consumer<ResponseEntity<ObjectNode>> 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<String, String> headers, HttpMethod method, URI url, Type type, Consumer<ResponseEntity<ObjectNode>> 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<T> 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<ResponseEntity<ObjectNode>> responseBodyConsumer) {
return new WechatRequestEntity<>(requestEntity.getBody(),
requestEntity.getHeaders(),
requestEntity.getMethod(),
requestEntity.getUrl(),
requestEntity.getType(),
responseBodyConsumer);
}
}

View File

@@ -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<T> {
private int httpStatus;
private T body;
public void convert(ResponseEntity<T> responseEntity) {
if (log.isDebugEnabled()) {
log.info("wechat response {}", responseEntity);
}
this.httpStatus = responseEntity.getStatusCodeValue();
this.body = responseEntity.getBody();
}
}

View File

@@ -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));
}
}
}

View File

@@ -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));
}

View File

@@ -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<ObjectNode> 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<ResponseEntity<ObjectNode>> 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<String> 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");
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,9 +1,12 @@
package com.enongm.dianji.payment.wechat.v3.model;
import lombok.Data;
/**
* @author Dax
* @since 17:02
*/
@Data
public class Goods {
/**
* 商户侧商品编码

View File

@@ -1,9 +1,12 @@
package com.enongm.dianji.payment.wechat.v3.model;
import lombok.Data;
/**
* @author Dax
* @since 17:05
*/
@Data
public class SettleInfo {
/**
* 是否指定分账

View File

@@ -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;
}

View File

@@ -42,10 +42,6 @@
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>

View File

@@ -28,7 +28,6 @@
<lombok.verison>1.18.12</lombok.verison>
<jackson.version>2.9.10</jackson.version>
<bcprov.version>1.66</bcprov.version>
<okhttp3.version>3.14.9</okhttp3.version>
</properties>
@@ -74,11 +73,6 @@
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>