mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-13 21:33:41 +08:00
替换掉okhttp 使用 spring rest
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.enongm.dianji.payment.wechat.v3.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Dax
|
||||
* @since 17:02
|
||||
*/
|
||||
@Data
|
||||
public class Goods {
|
||||
/**
|
||||
* 商户侧商品编码
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.enongm.dianji.payment.wechat.v3.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Dax
|
||||
* @since 17:05
|
||||
*/
|
||||
@Data
|
||||
public class SettleInfo {
|
||||
/**
|
||||
* 是否指定分账
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user