From 74707874419049d66e21fe9b7e0f78644839a7a8 Mon Sep 17 00:00:00 2001 From: "felord.cn" Date: Sun, 9 May 2021 22:13:10 +0800 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=E6=97=A5=E5=B8=B8=E7=BB=B4?= =?UTF-8?q?=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支付宝增加证书路径的选择方式 - 微信支付 《支付通知API》新增优惠功能(promotion_detail)字段 --- README.md | 2 +- docs/README.md | 2 +- docs/quick_start.md | 2 +- payment-spring-boot-autoconfigure/pom.xml | 4 +- .../payment/alipay/AliPayConfiguration.java | 39 +++++++++---------- .../payment/alipay/AliPayProperties.java | 4 ++ .../CombineTransactionConsumeData.java | 8 ++++ payment-spring-boot-starter/pom.xml | 4 +- pom.xml | 4 +- 9 files changed, 40 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index a4ddeee..1d7c7df 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ cn.felord payment-spring-boot-starter - 1.0.10.RELEASE + 1.0.11.RELEASE ``` diff --git a/docs/README.md b/docs/README.md index 302d83f..2e5d488 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,7 +35,7 @@ cn.felord payment-spring-boot-starter - 1.0.10.RELEASE + 1.0.11.RELEASE ``` ## 采用技术 diff --git a/docs/quick_start.md b/docs/quick_start.md index c251fb0..f44643c 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -4,7 +4,7 @@ cn.felord payment-spring-boot-starter - 1.0.10.RELEASE + 1.0.11.RELEASE ``` > 基于 **Spring Boot 2.x** diff --git a/payment-spring-boot-autoconfigure/pom.xml b/payment-spring-boot-autoconfigure/pom.xml index da2fb9b..83fb1c6 100644 --- a/payment-spring-boot-autoconfigure/pom.xml +++ b/payment-spring-boot-autoconfigure/pom.xml @@ -5,11 +5,11 @@ cn.felord payment-spring-boot - 1.0.10.RELEASE + 1.0.11.RELEASE payment-spring-boot-autoconfigure - 1.0.10.RELEASE + 1.0.11.RELEASE jar 4.0.0 diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayConfiguration.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayConfiguration.java index bfc50c5..38e6a86 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayConfiguration.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayConfiguration.java @@ -33,9 +33,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.function.Function; /** * @author felord.cn @@ -56,34 +56,33 @@ public class AliPayConfiguration { CertAlipayRequest certAlipayRequest = new CertAlipayRequest(); propertyMapper.from(v1::getServerUrl).to(certAlipayRequest::setServerUrl); propertyMapper.from(v1::getAppId).to(certAlipayRequest::setAppId); - propertyMapper.from(v1::getAppPrivateKeyPath).as(this::appRSAPrivateKey).to(certAlipayRequest::setPrivateKey); + propertyMapper.from(v1::getAppPrivateKeyPath) + .as(this::loadFile) + .to(certAlipayRequest::setPrivateKey); + propertyMapper.from(v1::getFormat).to(certAlipayRequest::setFormat); propertyMapper.from(v1::getCharset).to(certAlipayRequest::setCharset); propertyMapper.from(v1::getSignType).to(certAlipayRequest::setSignType); - propertyMapper.from(v1::getAppCertPublicKeyPath).as(this::getContentFromClassPath).to(certAlipayRequest::setCertContent); - propertyMapper.from(v1::getAlipayPublicCertPath).as(this::getContentFromClassPath).to(certAlipayRequest::setAlipayPublicCertContent); - propertyMapper.from(v1::getAlipayRootCertPath).as(this::getContentFromClassPath).to(certAlipayRequest::setRootCertContent); + + Function certStrategyFunc = v1.isClasspathUsed()?this::loadFile:s -> s; + + propertyMapper.from(v1::getAppCertPublicKeyPath) + .as(certStrategyFunc) + .to(certAlipayRequest::setCertContent); + propertyMapper.from(v1::getAlipayPublicCertPath) + .as(certStrategyFunc) + .to(certAlipayRequest::setAlipayPublicCertContent); + propertyMapper.from(v1::getAlipayRootCertPath) + .as(certStrategyFunc) + .to(certAlipayRequest::setRootCertContent); return new DefaultAlipayClient(certAlipayRequest); } - private String getContentFromClassPath(String classPath) { - ClassPathResource resource = new ClassPathResource(classPath); - try (InputStreamReader in = new InputStreamReader(resource.getInputStream())) { - return IOUtils.toString(in); - } catch (IOException e) { - log.error("ali pay root cert is invalid ,{}", e.getMessage()); - throw new PayException("ali pay root cert path is invalid"); - } - } - - - private String appRSAPrivateKey(String classPath) { + private String loadFile(String classPath) { ClassPathResource resource = new ClassPathResource(classPath); try (InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream())) { - try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { - return bufferedReader.readLine(); - } + return IOUtils.toString(inputStreamReader); } catch (IOException e) { log.error("ali pay app private key is required ,{}", e.getMessage()); throw new PayException("ali pay app private key is required"); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayProperties.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayProperties.java index f19b5de..4c2b4e2 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayProperties.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/alipay/AliPayProperties.java @@ -70,6 +70,10 @@ public class AliPayProperties { * charset default utf-8 */ private String charset = "utf-8"; + /** + * use classpath or not ,default true + */ + private boolean classpathUsed = true; /** * alipay public cert path */ diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/combine/CombineTransactionConsumeData.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/combine/CombineTransactionConsumeData.java index 38682ce..279c2f5 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/combine/CombineTransactionConsumeData.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/combine/CombineTransactionConsumeData.java @@ -19,6 +19,7 @@ package cn.felord.payment.wechat.v3.model.combine; import cn.felord.payment.wechat.enumeration.TradeState; import cn.felord.payment.wechat.enumeration.TradeType; +import cn.felord.payment.wechat.v3.model.PromotionDetail; import cn.felord.payment.wechat.v3.model.SceneInfo; import lombok.Data; @@ -125,6 +126,13 @@ public class CombineTransactionConsumeData { */ private String transactionId; + /** + * 优惠功能,子单有核销优惠券时有返回 + * + * @since 1.0.11.RELEASE + */ + private List promotionDetail; + } /** diff --git a/payment-spring-boot-starter/pom.xml b/payment-spring-boot-starter/pom.xml index dd4975d..6e742a5 100644 --- a/payment-spring-boot-starter/pom.xml +++ b/payment-spring-boot-starter/pom.xml @@ -5,11 +5,11 @@ cn.felord payment-spring-boot - 1.0.10.RELEASE + 1.0.11.RELEASE payment-spring-boot-starter - 1.0.10.RELEASE + 1.0.11.RELEASE jar 4.0.0 diff --git a/pom.xml b/pom.xml index 5d1a7c8..f827c72 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> cn.felord payment-spring-boot - 1.0.10.RELEASE + 1.0.11.RELEASE pom 4.0.0 @@ -76,7 +76,7 @@ 1.0.0.RELEASE 1.18.12 2.9.10 - 1.66 + 1.67 2.11.4 4.5.13 From 78cbce016d116f69c21d8b56659ca0cc9553e9a3 Mon Sep 17 00:00:00 2001 From: felord Date: Mon, 10 May 2021 10:56:43 +0800 Subject: [PATCH 02/13] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://gitee.com/felord/payment-spring-boot/issues/I3NGSB Closes I3NGSB --- .../java/cn/felord/payment/wechat/v3/SignatureProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java index 1d37b4d..37f526b 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java @@ -224,7 +224,7 @@ public class SignatureProvider { } ArrayNode certificates = bodyObjectNode.withArray("data"); if (certificates.isArray() && certificates.size() > 0) { - CERTIFICATE_MAP.clear(); + CERTIFICATE_MAP.remove(tenantId); final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509",BC_PROVIDER); certificates.forEach(objectNode -> { JsonNode encryptCertificate = objectNode.get("encrypt_certificate"); From 9d01a0fa3a726a8e3b5e4b6784ac59b6384139b3 Mon Sep 17 00:00:00 2001 From: felord Date: Thu, 13 May 2021 16:20:47 +0800 Subject: [PATCH 03/13] =?UTF-8?q?refactor:=20=20=E5=88=86=E8=B4=A6?= =?UTF-8?q?=E6=A0=87=E8=AE=B0=E9=BB=98=E8=AE=A4=E6=94=B9=E4=B8=BA=20?= =?UTF-8?q?=E4=B8=8D=E5=88=86=E8=B4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/v3/model/payscore/CompleteServiceOrderParams.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/payscore/CompleteServiceOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/payscore/CompleteServiceOrderParams.java index b37d697..876bfc3 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/payscore/CompleteServiceOrderParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/payscore/CompleteServiceOrderParams.java @@ -78,7 +78,7 @@ public class CompleteServiceOrderParams { * false:不分账,默认:false * true:分账。 */ - private Boolean profitSharing = Boolean.TRUE; + private Boolean profitSharing = Boolean.FALSE; /** * 订单优惠标记,选填 *

From ed190cc8753caed48891221b169221aa9f886de6 Mon Sep 17 00:00:00 2001 From: "felord.cn" Date: Sun, 16 May 2021 23:44:08 +0800 Subject: [PATCH 04/13] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E8=A7=A3?= =?UTF-8?q?=E5=AF=86=E5=8F=82=E6=95=B0=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/wechat/v3/SignatureProvider.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java index 1d37b4d..4fff442 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java @@ -30,9 +30,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.http.*; -import org.springframework.util.AlternativeJdkIdGenerator; -import org.springframework.util.Base64Utils; -import org.springframework.util.IdGenerator; +import org.springframework.util.*; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponents; @@ -128,7 +126,7 @@ public class SignatureProvider { * @return the string */ @SneakyThrows - public String requestSign(boolean newLine,String tenantId, String method, String canonicalUrl, String body) { + public String requestSign(boolean newLine, String tenantId, String method, String canonicalUrl, String body) { long timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); String nonceStr = nonceStrGenerator.generateId() @@ -136,7 +134,7 @@ public class SignatureProvider { .replaceAll("-", ""); WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId); PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate(); - String encode = this.doRequestSign(newLine,privateKey, method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); + String encode = this.doRequestSign(newLine, privateKey, method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); // 序列号 String serialNo = wechatMetaBean.getSerialNumber(); // 生成token @@ -157,10 +155,10 @@ public class SignatureProvider { * @since 1.0.4.RELEASE */ @SneakyThrows - public String doRequestSign(boolean newLine,PrivateKey privateKey, String... orderedComponents) { + public String doRequestSign(boolean newLine, PrivateKey privateKey, String... orderedComponents) { Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER); signer.initSign(privateKey); - final String signatureStr = createSign(newLine,orderedComponents); + final String signatureStr = createSign(newLine, orderedComponents); signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(signer.sign()); } @@ -180,7 +178,7 @@ public class SignatureProvider { } Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial); - final String signatureStr = createSign(true,params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody()); + final String signatureStr = createSign(true, params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody()); Signature signer = Signature.getInstance("SHA256withRSA"); signer.initVerify(certificate); signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); @@ -208,7 +206,7 @@ public class SignatureProvider { } // 签名 HttpMethod httpMethod = WechatPayV3Type.CERT.method(); - String authorization = requestSign(true,tenantId, httpMethod.name(), canonicalUrl, ""); + String authorization = requestSign(true, tenantId, httpMethod.name(), canonicalUrl, ""); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); @@ -225,7 +223,7 @@ public class SignatureProvider { ArrayNode certificates = bodyObjectNode.withArray("data"); if (certificates.isArray() && certificates.size() > 0) { CERTIFICATE_MAP.clear(); - final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509",BC_PROVIDER); + final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509", BC_PROVIDER); certificates.forEach(objectNode -> { JsonNode encryptCertificate = objectNode.get("encrypt_certificate"); String associatedData = encryptCertificate.get("associated_data").asText(); @@ -256,6 +254,15 @@ public class SignatureProvider { * @return the string */ public String decryptResponseBody(String tenantId, String associatedData, String nonce, String ciphertext) { + + try { + Assert.hasText(associatedData, "associatedData is invalid"); + Assert.hasText(nonce, "nonce is invalid"); + Assert.hasText(ciphertext, "ciphertext is invalid"); + } catch (Exception e) { + throw new PayException(e.getMessage()); + } + try { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", BC_PROVIDER); String apiV3Key = wechatMetaContainer.getWechatMeta(tenantId).getV3().getAppV3Secret(); @@ -285,14 +292,14 @@ public class SignatureProvider { * @return encrypt message * @since 1.0.6.RELEASE */ - public String encryptRequestMessage(String message,Certificate certificate) { + public String encryptRequestMessage(String message, Certificate certificate) { try { Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", BC_PROVIDER); cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey()); byte[] data = message.getBytes(StandardCharsets.UTF_8); byte[] cipherData = cipher.doFinal(data); - return Base64Utils.encodeToString(cipherData); + return Base64Utils.encodeToString(cipherData); } catch (Exception e) { throw new PayException(e); @@ -304,14 +311,14 @@ public class SignatureProvider { * * @return the x 509 wechat certificate info */ - public X509WechatCertificateInfo getCertificate(){ + public X509WechatCertificateInfo getCertificate() { for (String serial : CERTIFICATE_MAP.keySet()) { X509Certificate x509Cert = (X509Certificate) CERTIFICATE_MAP.get(serial); try { x509Cert.checkValidity(); X509WechatCertificateInfo x509WechatCertificateInfo = new X509WechatCertificateInfo(); - x509WechatCertificateInfo.setWechatPaySerial(serial); - x509WechatCertificateInfo.setX509Certificate(x509Cert); + x509WechatCertificateInfo.setWechatPaySerial(serial); + x509WechatCertificateInfo.setX509Certificate(x509Cert); return x509WechatCertificateInfo; } catch (Exception e) { log.warn("the wechat certificate is invalid , {}", e.getMessage()); @@ -348,9 +355,9 @@ public class SignatureProvider { * @param components the components * @return string string */ - private static String createSign(boolean newLine,String... components) { + private static String createSign(boolean newLine, String... components) { - String suffix = newLine? "\n":""; + String suffix = newLine ? "\n" : ""; return Arrays.stream(components) .collect(Collectors.joining("\n", "", suffix)); } From 0c230547a9238fb88b67da61926ef3009b99c17b Mon Sep 17 00:00:00 2001 From: felord Date: Tue, 25 May 2021 12:38:13 +0800 Subject: [PATCH 05/13] =?UTF-8?q?refactor:=20=20=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E8=B4=A6=E5=8D=95=E5=92=8C=E8=B5=84=E9=87=91=E8=B4=A6=E5=8D=95?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E8=83=BD=E5=A4=9F=E6=AD=A3=E5=B8=B8=E7=9A=84?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E6=96=87=E4=BB=B6=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../felord/payment/wechat/v3/AbstractApi.java | 62 ++++++++++++------- .../payment/wechat/v3/WechatDirectPayApi.java | 4 +- .../wechat/v3/WechatMarketingFavorApi.java | 8 +-- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java index 50615ec..64c34c8 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; @@ -89,7 +90,7 @@ public abstract class AbstractApi { */ private void applyObjectMapper(ObjectMapper mapper) { mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // empty string error .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) .setSerializationInclusion(JsonInclude.Include.NON_NULL) @@ -217,13 +218,15 @@ public abstract class AbstractApi { throw new PayException("wechat app pay json failed"); } } + /** - * 对账单内容下载,非流文件。 + * 对账单CSV内容下载,非流文件。 * * @param link the link * @return 对账单内容 ,有可能为空字符 “” + * @see AbstractApi#billResource(String) */ - protected String billDownload(String link) { + protected String billCsvDownload(String link) { return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) .function((type, downloadUrl) -> { URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl) @@ -234,22 +237,6 @@ public abstract class AbstractApi { .download(); } - /** - * 对账单下载,流文件。 - * - * @param link the link - * @return response entity - */ - protected ResponseEntity billResource(String link) { - return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) - .function((type, downloadUrl) -> { - URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl) - .build() - .toUri(); - return Get(uri); - }) - .resource(); - } /** * 申请交易账单API @@ -267,7 +254,9 @@ public abstract class AbstractApi { * @param tradeBillParams tradeBillParams * @since 1.0.3.RELEASE */ - public final void downloadTradeBill(TradeBillParams tradeBillParams) { + public ResponseEntity downloadTradeBill(TradeBillParams tradeBillParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.TRADEBILL, tradeBillParams) .function((wechatPayV3Type, params) -> { MultiValueMap queryParams = new LinkedMultiValueMap<>(); @@ -291,8 +280,12 @@ public abstract class AbstractApi { .build().toUri(); return Get(uri); }) - .consumer(response -> this.billDownload(Objects.requireNonNull(response.getBody()).get("download_url").asText())) + .consumer(wechatResponseEntity::convert) .request(); + String downloadUrl = Objects.requireNonNull(wechatResponseEntity.getBody()) + .get("download_url") + .asText(); + return this.billResource(downloadUrl); } /** @@ -309,7 +302,8 @@ public abstract class AbstractApi { * @param fundFlowBillParams fundFlowBillParams * @since 1.0.3.RELEASE */ - public final void downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) { + public ResponseEntity downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.FUNDFLOWBILL, fundFlowBillParams) .function((wechatPayV3Type, params) -> { MultiValueMap queryParams = new LinkedMultiValueMap<>(); @@ -328,7 +322,29 @@ public abstract class AbstractApi { .build().toUri(); return Get(uri); }) - .consumer(response -> this.billDownload(Objects.requireNonNull(response.getBody()).get("download_url").asText())) + .consumer(wechatResponseEntity::convert) .request(); + String downloadUrl = Objects.requireNonNull(wechatResponseEntity.getBody()) + .get("download_url") + .asText(); + return this.billResource(downloadUrl); + } + + + /** + * 对账单下载,流文件。 + * + * @param link the link + * @return response entity + */ + protected ResponseEntity billResource(String link) { + return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) + .function((type, downloadUrl) -> { + URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl) + .build() + .toUri(); + return Get(uri); + }) + .resource(); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java index 7467dfe..bab1ce0 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java @@ -87,7 +87,7 @@ public class WechatDirectPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String prepayId = body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true,privateKey, appId, timestamp, nonceStr, prepayId); + String paySign = signatureProvider.doRequestSign(true, privateKey, appId, timestamp, nonceStr, prepayId); String mchId = wechatMetaBean.getV3().getMchId(); body.put("appid", appId); @@ -136,7 +136,7 @@ public class WechatDirectPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String packageStr = "prepay_id=" + body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true,privateKey, appId, timestamp, nonceStr, packageStr); + String paySign = signatureProvider.doRequestSign(true, privateKey, appId, timestamp, nonceStr, packageStr); body.put("appId", appId); body.put("timeStamp", timestamp); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java index 87d63fe..6f27922 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java @@ -493,7 +493,7 @@ public class WechatMarketingFavorApi extends AbstractApi { * * @param stockId the stock id * @return the wechat response entity - * @see AbstractApi#billDownload(String) AbstractApi#billDownload(String)对账单下载api + * @see AbstractApi#billCsvDownload(String) AbstractApi#billDownload(String)对账单下载api */ public WechatResponseEntity downloadStockUseFlow(String stockId) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); @@ -501,7 +501,7 @@ public class WechatMarketingFavorApi extends AbstractApi { .function(this::downloadFlowFunction) .consumer(wechatResponseEntity::convert) .request(); - String csv = this.billDownload(wechatResponseEntity.getBody().get("url").asText()); + String csv = this.billCsvDownload(wechatResponseEntity.getBody().get("url").asText()); wechatResponseEntity.getBody().put("csv", csv); return wechatResponseEntity; } @@ -515,7 +515,7 @@ public class WechatMarketingFavorApi extends AbstractApi { * * @param stockId the stock id * @return the wechat response entity - * @see AbstractApi#billDownload(String) AbstractApi#billDownload(String)对账单下载api + * @see AbstractApi#billCsvDownload(String) AbstractApi#billDownload(String)对账单下载api */ public WechatResponseEntity downloadStockRefundFlow(String stockId) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); @@ -523,7 +523,7 @@ public class WechatMarketingFavorApi extends AbstractApi { .function(this::downloadFlowFunction) .consumer(wechatResponseEntity::convert) .request(); - String csv = this.billDownload(wechatResponseEntity.getBody().get("url").asText()); + String csv = this.billCsvDownload(wechatResponseEntity.getBody().get("url").asText()); wechatResponseEntity.getBody().put("csv", csv); return wechatResponseEntity; } From 8cd3f63d68363ef9b90e9cac4d4bed70b4b3d363 Mon Sep 17 00:00:00 2001 From: felord Date: Wed, 26 May 2021 10:19:53 +0800 Subject: [PATCH 06/13] refactor: Style --- .../cn/felord/payment/wechat/enumeration/WechatPayV3Type.java | 4 ++-- .../main/java/cn/felord/payment/wechat/v3/AbstractApi.java | 4 ++-- .../java/cn/felord/payment/wechat/v3/SignatureProvider.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java index 3bcbca3..a112747 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java @@ -47,13 +47,13 @@ public enum WechatPayV3Type { * * @since 1.0.3.RELEASE */ - TRADEBILL(HttpMethod.GET, "%s/v3/bill/tradebill"), + TRADE_BILL(HttpMethod.GET, "%s/v3/bill/tradebill"), /** * 申请资金账单API. * * @since 1.0.3.RELEASE */ - FUNDFLOWBILL(HttpMethod.GET, "%s/v3/bill/fundflowbill"), + FUND_FLOW_BILL(HttpMethod.GET, "%s/v3/bill/fundflowbill"), //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java index 64c34c8..56c15ac 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java @@ -257,7 +257,7 @@ public abstract class AbstractApi { public ResponseEntity downloadTradeBill(TradeBillParams tradeBillParams) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); - this.client().withType(WechatPayV3Type.TRADEBILL, tradeBillParams) + this.client().withType(WechatPayV3Type.TRADE_BILL, tradeBillParams) .function((wechatPayV3Type, params) -> { MultiValueMap queryParams = new LinkedMultiValueMap<>(); LocalDate billDate = params.getBillDate(); @@ -304,7 +304,7 @@ public abstract class AbstractApi { */ public ResponseEntity downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); - this.client().withType(WechatPayV3Type.FUNDFLOWBILL, fundFlowBillParams) + this.client().withType(WechatPayV3Type.FUND_FLOW_BILL, fundFlowBillParams) .function((wechatPayV3Type, params) -> { MultiValueMap queryParams = new LinkedMultiValueMap<>(); LocalDate billDate = params.getBillDate(); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java index 87b011c..60e24c0 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java @@ -118,7 +118,7 @@ public class SignatureProvider { /** * 我方请求前用 SHA256withRSA 加签,使用API证书. * - * @param newLine the new line + * @param newLine 签名协议不兼容而增加的开关 github issues#18 * @param tenantId the properties key * @param method the method * @param canonicalUrl the canonical url From 91dc8e8f8a7ef428ba42ad9a17eb7dc8061c610d Mon Sep 17 00:00:00 2001 From: felord Date: Thu, 27 May 2021 18:37:16 +0800 Subject: [PATCH 07/13] =?UTF-8?q?refactor:=20=20=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E4=BA=A4=E6=98=93=E8=B4=A6=E5=8D=95=E5=92=8C?= =?UTF-8?q?=E8=B5=84=E9=87=91=E8=B4=A6=E5=8D=95=E6=9B=B4=E5=8A=A0=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E4=BA=86=20-=20=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=B5=81=EF=BC=8C=E5=8F=AF=E4=BB=A5=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=BB=99=E5=89=8D=E7=AB=AF=EF=BC=8C?= =?UTF-8?q?=E4=BC=9A=E8=87=AA=E5=8A=A8=E4=B8=8B=E8=BD=BD=E6=88=90txt?= =?UTF-8?q?=E6=88=96=E8=80=85gzip=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../felord/payment/wechat/v3/AbstractApi.java | 45 +++++++++++++++++-- .../wechat/v3/WechatBatchTransferApi.java | 2 +- .../payment/wechat/v3/WechatPayClient.java | 4 +- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java index 56c15ac..0792178 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.core.io.Resource; +import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; @@ -224,7 +225,7 @@ public abstract class AbstractApi { * * @param link the link * @return 对账单内容 ,有可能为空字符 “” - * @see AbstractApi#billResource(String) + * @see AbstractApi#billResource(String) AbstractApi#billResource(String)AbstractApi#billResource(String) */ protected String billCsvDownload(String link) { return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) @@ -241,6 +242,8 @@ public abstract class AbstractApi { /** * 申请交易账单API *

+ * 返回值直接返回前端,会下载tradebill-前缀加上日期的txt文件(默认)或者gzip文件。 + *

* 微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况。 *

* 注意: @@ -252,6 +255,7 @@ public abstract class AbstractApi { * * * @param tradeBillParams tradeBillParams + * @return the response entity * @since 1.0.3.RELEASE */ public ResponseEntity downloadTradeBill(TradeBillParams tradeBillParams) { @@ -285,7 +289,10 @@ public abstract class AbstractApi { String downloadUrl = Objects.requireNonNull(wechatResponseEntity.getBody()) .get("download_url") .asText(); - return this.billResource(downloadUrl); + + String ext = Objects.equals(TarType.GZIP, tradeBillParams.getTarType()) ? ".gzip" : ".txt"; + String filename = "tradebill-" + tradeBillParams.getBillDate().toString() + ext; + return fileEntity(downloadUrl, filename); } /** @@ -300,6 +307,7 @@ public abstract class AbstractApi { * * * @param fundFlowBillParams fundFlowBillParams + * @return the response entity * @since 1.0.3.RELEASE */ public ResponseEntity downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) { @@ -327,9 +335,19 @@ public abstract class AbstractApi { String downloadUrl = Objects.requireNonNull(wechatResponseEntity.getBody()) .get("download_url") .asText(); - return this.billResource(downloadUrl); + + String ext = Objects.equals(TarType.GZIP, fundFlowBillParams.getTarType()) ? ".gzip" : ".txt"; + String filename = "fundflowbill-" + fundFlowBillParams.getBillDate().toString() + ext; + return fileEntity(downloadUrl, filename); } + private ResponseEntity fileEntity(String downloadUrl, String filename) { + ResponseEntity responseEntity = this.billResource(downloadUrl); + HttpHeaders httpHeaders = new HttpHeaders(); + // utf is not support + httpHeaders.setContentDisposition(ContentDisposition.attachment().filename(filename).build()); + return ResponseEntity.ok().headers(httpHeaders).body(responseEntity.getBody()); + } /** * 对账单下载,流文件。 @@ -345,6 +363,25 @@ public abstract class AbstractApi { .toUri(); return Get(uri); }) - .resource(); + .resource(true); + } + + + /** + * todo 对账单下载,流文件。 + * + * @param link the link + * @param newLine the new line + * @return response entity + */ + protected ResponseEntity billResource(String link, boolean newLine) { + return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) + .function((type, downloadUrl) -> { + URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl) + .build() + .toUri(); + return Get(uri); + }) + .resource(newLine); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java index 5cf77af..25dd953 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java @@ -254,6 +254,6 @@ public class WechatBatchTransferApi extends AbstractApi { .request(); String downloadUrl = wechatResponseEntity.getBody().get("download_url").asText(); Assert.hasText(downloadUrl, "download url has no text"); - return this.billResource(downloadUrl); + return this.billResource(downloadUrl, false); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java index b1b9957..27a6d16 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java @@ -185,10 +185,10 @@ public class WechatPayClient { * @return the string * @since 1.0.6.RELEASE */ - public ResponseEntity resource() { + protected ResponseEntity resource(boolean newLine) { RequestEntity requestEntity = this.requestEntityBiFunction.apply(this.wechatPayV3Type, this.model); WechatRequestEntity wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer); - return this.doResource(this.header(false,wechatRequestEntity)); + return this.doResource(this.header(newLine,wechatRequestEntity)); } From 16440d01cf2483ba7739a6b553c2792d264f8d17 Mon Sep 17 00:00:00 2001 From: felord Date: Fri, 28 May 2021 17:00:52 +0800 Subject: [PATCH 08/13] =?UTF-8?q?refactor:=20=E7=AD=BE=E5=90=8D=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BF=AE=E6=94=B9=EF=BC=8C=E5=85=A8=E9=83=A8=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=8D=A2=E8=A1=8C=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../felord/payment/wechat/v3/AbstractApi.java | 38 +++++++------------ .../payment/wechat/v3/SignatureProvider.java | 14 +++---- .../payment/wechat/v3/WechatDirectPayApi.java | 4 +- .../wechat/v3/WechatPartnerPayApi.java | 4 +- .../payment/wechat/v3/WechatPayClient.java | 12 +++--- 5 files changed, 29 insertions(+), 43 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java index 0792178..693da19 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java @@ -225,7 +225,7 @@ public abstract class AbstractApi { * * @param link the link * @return 对账单内容 ,有可能为空字符 “” - * @see AbstractApi#billResource(String) AbstractApi#billResource(String)AbstractApi#billResource(String) + * @see AbstractApi#billResource(String) AbstractApi#billResource(String)AbstractApi#billResource(String)AbstractApi#billResource(String) */ protected String billCsvDownload(String link) { return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) @@ -292,7 +292,7 @@ public abstract class AbstractApi { String ext = Objects.equals(TarType.GZIP, tradeBillParams.getTarType()) ? ".gzip" : ".txt"; String filename = "tradebill-" + tradeBillParams.getBillDate().toString() + ext; - return fileEntity(downloadUrl, filename); + return downloadBillResponse(downloadUrl, filename); } /** @@ -338,10 +338,17 @@ public abstract class AbstractApi { String ext = Objects.equals(TarType.GZIP, fundFlowBillParams.getTarType()) ? ".gzip" : ".txt"; String filename = "fundflowbill-" + fundFlowBillParams.getBillDate().toString() + ext; - return fileEntity(downloadUrl, filename); + return this.downloadBillResponse(downloadUrl, filename); } - private ResponseEntity fileEntity(String downloadUrl, String filename) { + /** + * 调用{@code /v3/billdownload/file}直接下载为文件. + * + * @param downloadUrl 格式为 {@code https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx} + * @param filename 文件名称包含扩展名 + * @return the response entity + */ + public ResponseEntity downloadBillResponse(String downloadUrl, String filename) { ResponseEntity responseEntity = this.billResource(downloadUrl); HttpHeaders httpHeaders = new HttpHeaders(); // utf is not support @@ -350,7 +357,7 @@ public abstract class AbstractApi { } /** - * 对账单下载,流文件。 + * 调用{@code /v3/billdownload/file}下载返回的原始文件流 * * @param link the link * @return response entity @@ -363,25 +370,6 @@ public abstract class AbstractApi { .toUri(); return Get(uri); }) - .resource(true); - } - - - /** - * todo 对账单下载,流文件。 - * - * @param link the link - * @param newLine the new line - * @return response entity - */ - protected ResponseEntity billResource(String link, boolean newLine) { - return this.client().withType(WechatPayV3Type.FILE_DOWNLOAD, link) - .function((type, downloadUrl) -> { - URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl) - .build() - .toUri(); - return Get(uri); - }) - .resource(newLine); + .resource(); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java index 60e24c0..f3af733 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java @@ -118,7 +118,6 @@ public class SignatureProvider { /** * 我方请求前用 SHA256withRSA 加签,使用API证书. * - * @param newLine 签名协议不兼容而增加的开关 github issues#18 * @param tenantId the properties key * @param method the method * @param canonicalUrl the canonical url @@ -126,7 +125,7 @@ public class SignatureProvider { * @return the string */ @SneakyThrows - public String requestSign(boolean newLine, String tenantId, String method, String canonicalUrl, String body) { + public String requestSign(String tenantId, String method, String canonicalUrl, String body) { long timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); String nonceStr = nonceStrGenerator.generateId() @@ -134,7 +133,7 @@ public class SignatureProvider { .replaceAll("-", ""); WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId); PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate(); - String encode = this.doRequestSign(newLine, privateKey, method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); + String encode = this.doRequestSign(privateKey, method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); // 序列号 String serialNo = wechatMetaBean.getSerialNumber(); // 生成token @@ -148,17 +147,16 @@ public class SignatureProvider { /** * Do request sign. * - * @param newLine the has suffix * @param privateKey the private key * @param orderedComponents the orderedComponents * @return the string * @since 1.0.4.RELEASE */ @SneakyThrows - public String doRequestSign(boolean newLine, PrivateKey privateKey, String... orderedComponents) { + public String doRequestSign(PrivateKey privateKey, String... orderedComponents) { Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER); signer.initSign(privateKey); - final String signatureStr = createSign(newLine, orderedComponents); + final String signatureStr = createSign(true, orderedComponents); signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(signer.sign()); } @@ -206,7 +204,7 @@ public class SignatureProvider { } // 签名 HttpMethod httpMethod = WechatPayV3Type.CERT.method(); - String authorization = requestSign(true, tenantId, httpMethod.name(), canonicalUrl, ""); + String authorization = requestSign(tenantId, httpMethod.name(), canonicalUrl, ""); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); @@ -223,7 +221,7 @@ public class SignatureProvider { ArrayNode certificates = bodyObjectNode.withArray("data"); if (certificates.isArray() && certificates.size() > 0) { CERTIFICATE_MAP.remove(tenantId); - final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509",BC_PROVIDER); + final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509", BC_PROVIDER); certificates.forEach(objectNode -> { JsonNode encryptCertificate = objectNode.get("encrypt_certificate"); String associatedData = encryptCertificate.get("associated_data").asText(); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java index bab1ce0..f3ba6a7 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatDirectPayApi.java @@ -87,7 +87,7 @@ public class WechatDirectPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String prepayId = body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true, privateKey, appId, timestamp, nonceStr, prepayId); + String paySign = signatureProvider.doRequestSign(privateKey, appId, timestamp, nonceStr, prepayId); String mchId = wechatMetaBean.getV3().getMchId(); body.put("appid", appId); @@ -136,7 +136,7 @@ public class WechatDirectPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String packageStr = "prepay_id=" + body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true, privateKey, appId, timestamp, nonceStr, packageStr); + String paySign = signatureProvider.doRequestSign(privateKey, appId, timestamp, nonceStr, packageStr); body.put("appId", appId); body.put("timeStamp", timestamp); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerPayApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerPayApi.java index 908c15b..a25cda6 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerPayApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerPayApi.java @@ -87,7 +87,7 @@ public class WechatPartnerPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String prepayId = body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true, privateKey, subAppid, timestamp, nonceStr, prepayId); + String paySign = signatureProvider.doRequestSign(privateKey, subAppid, timestamp, nonceStr, prepayId); body.put("appid", subAppid); body.put("partnerid", partnerPayParams.getSubMchid()); @@ -136,7 +136,7 @@ public class WechatPartnerPayApi extends AbstractApi { .toString() .replaceAll("-", ""); String packageStr = "prepay_id=" + body.get("prepay_id").asText(); - String paySign = signatureProvider.doRequestSign(true, privateKey, subAppid, timestamp, nonceStr, packageStr); + String paySign = signatureProvider.doRequestSign(privateKey, subAppid, timestamp, nonceStr, packageStr); body.put("appId", subAppid); body.put("timeStamp", timestamp); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java index 27a6d16..fb69c71 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java @@ -164,7 +164,7 @@ public class WechatPayClient { public void request() { RequestEntity requestEntity = this.requestEntityBiFunction.apply(this.wechatPayV3Type, this.model); WechatRequestEntity wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer); - this.doExecute(this.header(true,wechatRequestEntity)); + this.doExecute(this.header(wechatRequestEntity)); } @@ -176,7 +176,7 @@ public class WechatPayClient { public String download() { RequestEntity requestEntity = this.requestEntityBiFunction.apply(this.wechatPayV3Type, this.model); WechatRequestEntity wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer); - return this.doDownload(this.header(true,wechatRequestEntity)); + return this.doDownload(this.header(wechatRequestEntity)); } /** @@ -185,10 +185,10 @@ public class WechatPayClient { * @return the string * @since 1.0.6.RELEASE */ - protected ResponseEntity resource(boolean newLine) { + protected ResponseEntity resource() { RequestEntity requestEntity = this.requestEntityBiFunction.apply(this.wechatPayV3Type, this.model); WechatRequestEntity wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer); - return this.doResource(this.header(newLine,wechatRequestEntity)); + return this.doResource(this.header(wechatRequestEntity)); } @@ -199,7 +199,7 @@ public class WechatPayClient { * @param requestEntity the request entity * @return the wechat request entity */ - private WechatRequestEntity header(boolean newLine,WechatRequestEntity requestEntity) { + private WechatRequestEntity header(WechatRequestEntity requestEntity) { UriComponents uri = UriComponentsBuilder.fromUri(requestEntity.getUrl()).build(); String canonicalUrl = uri.getPath(); @@ -220,7 +220,7 @@ public class WechatPayClient { } String tenantId = Objects.requireNonNull(headers.get("Pay-TenantId")).get(0); - String authorization = signatureProvider.requestSign(newLine,tenantId, httpMethod.name(), canonicalUrl, body); + String authorization = signatureProvider.requestSign(tenantId, httpMethod.name(), canonicalUrl, body); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.addAll(headers); From 3871e05ea7f48765f2bc2a82f62f8af6fed96e1c Mon Sep 17 00:00:00 2001 From: felord Date: Fri, 28 May 2021 17:01:04 +0800 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 转账明细电子回单受理API - 查询转账明细电子回单受理结果API - 查询账户实时余额API - 查询账户日终余额API - 商户银行来账查询API --- .../enumeration/TransferAcceptType.java | 22 +++ .../wechat/enumeration/WechatPayV3Type.java | 29 ++++ .../wechat/v3/WechatBatchTransferApi.java | 154 +++++++++++++++++- .../batchtransfer/QueryDayBalanceParams.java | 25 +++ .../QueryIncomeRecordParams.java | 37 +++++ .../TransferDetailElectronicParams.java | 36 ++++ 6 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/TransferAcceptType.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryDayBalanceParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryIncomeRecordParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/TransferDetailElectronicParams.java diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/TransferAcceptType.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/TransferAcceptType.java new file mode 100644 index 0000000..2020bb9 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/TransferAcceptType.java @@ -0,0 +1,22 @@ +package cn.felord.payment.wechat.enumeration; + +/** + * 批量转账到零钱 - 电子回单受理类型 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +public enum TransferAcceptType { + /** + * 批量转账明细电子回单 + */ + BATCH_TRANSFER, + /** + * 企业付款至零钱电子回单 + */ + TRANSFER_TO_POCKET, + /** + * 企业付款至银行卡电子回单 + */ + TRANSFER_TO_BANK +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java index a112747..c4ea443 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java @@ -487,6 +487,35 @@ public enum WechatPayV3Type { * @since 1.0.6.RELEASES */ BATCH_TRANSFER_DOWNLOAD_BILL(HttpMethod.GET, "%s/v3/transfer/bill-receipt/{out_batch_no}"), + /** + * 转账明细电子回单受理API. + * + * @since 1.0.11.RELEASES + */ + BATCH_TRANSFER_ELECTRONIC(HttpMethod.POST, "%s/v3/transfer-detail/electronic-receipts"), + /** + * 查询转账明细电子回单受理结果API. + * 请求方式同{@link WechatPayV3Type#BATCH_TRANSFER_ELECTRONIC}不同 + * @since 1.0.11.RELEASES + */ + BATCH_TRANSFER_ELECTRONIC_DETAIL(HttpMethod.GET, "%s/v3/transfer-detail/electronic-receipts"), + /** + * 查询账户实时余额API + * + * @since 1.0.11.RELEASES + */ + BATCH_TRANSFER_FUND_BALANCE(HttpMethod.GET, "%s/v3/merchant/fund/balance/{account_type}"), + /** + * 查询账户日终余额API + * + * @since 1.0.11.RELEASES + */ + BATCH_TRANSFER_FUND_DAY_BALANCE(HttpMethod.GET, "%s/v3/merchant/fund/dayendbalance/{account_type}"), /** + * 商户银行来账查询API + * + * @since 1.0.11.RELEASES + */ + BATCH_TRANSFER_FUND_INCOME_RECORDS(HttpMethod.GET, "%s/v3/merchantfund/merchant/income-records"), //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java index 25dd953..9e0bf9a 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java @@ -17,11 +17,10 @@ package cn.felord.payment.wechat.v3; +import cn.felord.payment.wechat.enumeration.FundFlowAccountType; import cn.felord.payment.wechat.enumeration.WeChatServer; import cn.felord.payment.wechat.enumeration.WechatPayV3Type; -import cn.felord.payment.wechat.v3.model.batchtransfer.CreateBatchTransferParams; -import cn.felord.payment.wechat.v3.model.batchtransfer.QueryBatchTransferDetailParams; -import cn.felord.payment.wechat.v3.model.batchtransfer.QueryBatchTransferParams; +import cn.felord.payment.wechat.v3.model.batchtransfer.*; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; @@ -38,6 +37,7 @@ import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -254,6 +254,152 @@ public class WechatBatchTransferApi extends AbstractApi { .request(); String downloadUrl = wechatResponseEntity.getBody().get("download_url").asText(); Assert.hasText(downloadUrl, "download url has no text"); - return this.billResource(downloadUrl, false); + return this.billResource(downloadUrl); + } + + /** + * 转账明细电子回单受理API + *

+ * 受理转账明细电子回单接口,商户通过该接口可以申请受理转账明细单电子回单服务。 + *

+ * 返回的下载链接可调用{@link this#downloadBillResponse(String, String)}下载文件 + * + * @param params the params + * @return the wechat response entity + * @since 1.0.11.RELEASE + */ + public WechatResponseEntity transferElectronic(TransferDetailElectronicParams params) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.BATCH_TRANSFER_ELECTRONIC, params) + .function((type, transferDetailElectronic) -> { + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, transferDetailElectronic); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询转账明细电子回单受理结果API + *

+ * 查询转账明细电子回单受理结果接口,商户通过该接口可以查询电子回单受理进度信息, + * 包括电子回单据信息,电子回单文件的hash值,电子回单文件的下载地址等。 + *

+ * 返回的下载链接可调用{@link this#downloadBillResponse(String, String)}下载文件 + * + * @param params the params + * @return the wechat response entity + * @since 1.0.11.RELEASE + */ + public WechatResponseEntity queryTransferElectronicResult(TransferDetailElectronicParams params) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.BATCH_TRANSFER_ELECTRONIC_DETAIL, params) + .function((type, transferDetailElectronic) -> { + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("accept_type", transferDetailElectronic.getAcceptType().name()); + queryParams.add("out_batch_no", transferDetailElectronic.getOutBatchNo()); + queryParams.add("out_detail_no", transferDetailElectronic.getOutDetailNo()); + + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParams(queryParams) + .build() + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询账户实时余额API + *

+ * 商户通过此接口可以查询本商户号的账号余额情况。 + * + * @param accountType the account type + * @return the wechat response entity + * @since 1.0.11.RELEASE + */ + public WechatResponseEntity queryFundBalance(FundFlowAccountType accountType) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.BATCH_TRANSFER_FUND_BALANCE, accountType) + .function((type, flowAccountType) -> { + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .build() + .expand(flowAccountType.name()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询账户日终余额API + *

+ * 通过此接口可以查询本商户号指定日期当天24点的账户余额。 + * + * @param queryDayBalanceParams the transfer day balance params + * @return the wechat response entity + * @since 1.0.11.RELEASE + */ + public WechatResponseEntity queryDayFundBalance(QueryDayBalanceParams queryDayBalanceParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.BATCH_TRANSFER_FUND_DAY_BALANCE, queryDayBalanceParams) + .function((type, params) -> { + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("date", params.getDate().toString()) + .build() + .expand(params.getAccountType().name()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 商户银行来账查询API + *

+ * 商户通过本接口查询指定日期内本商户银行来账记录列表。 + * 列表内包含本商户银行来账相关的业务单号、金额、完成时间等信息,用于查询和核对。 + *

+ * 注意: + *

    + *
  1. 如需检索,请在前端缓存所有银行来账记录数据并自行完成检索功能;
  2. + *
  3. 调用该接口前,商户需提前开通“来账识别”产品权限;
  4. + *
  5. 本接口对可查询的商户范围有所规定,仅支持对本商户进行查询;
  6. + *
  7. 本接口仅提供近90天内的银行来账记录查询,且一次只能查询一天,商户需确保查询记录日期在此范围内;
  8. + *
  9. 本接口返回的记录字段后续可能会有所扩充,商户需做好接口兼容准备;
  10. + *
  11. 单商户单接口最大请求频率不超过50TPS。
  12. + *
+ * + * @param queryIncomeRecordParams the transfer day balance params + * @return the wechat response entity + * @since 1.0.11.RELEASE + */ + public WechatResponseEntity queryIncomeRecords(QueryIncomeRecordParams queryIncomeRecordParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.BATCH_TRANSFER_FUND_INCOME_RECORDS, queryIncomeRecordParams) + .function((type, params) -> { + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("account_type", params.getAccountType().name()); + queryParams.add("date", params.getDate().toString()); + queryParams.add("offset", Optional.ofNullable(params.getOffset()).orElse(0).toString()); + queryParams.add("limit", params.getLimit().toString()); + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParams(queryParams) + .build() + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryDayBalanceParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryDayBalanceParams.java new file mode 100644 index 0000000..c816806 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryDayBalanceParams.java @@ -0,0 +1,25 @@ +package cn.felord.payment.wechat.v3.model.batchtransfer; + +import cn.felord.payment.wechat.enumeration.FundFlowAccountType; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 查询账户日终余额API 请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class QueryDayBalanceParams { + /** + * 账户类型,必填 + * @see FundFlowAccountType + */ + private FundFlowAccountType accountType; + /** + * 日期,必填 + */ + private LocalDate date; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryIncomeRecordParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryIncomeRecordParams.java new file mode 100644 index 0000000..1ffad1e --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/QueryIncomeRecordParams.java @@ -0,0 +1,37 @@ +package cn.felord.payment.wechat.v3.model.batchtransfer; + +import cn.felord.payment.wechat.enumeration.FundFlowAccountType; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 商户银行来账查询API 请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class QueryIncomeRecordParams { + /** + * 账户类型,必填 + * @see FundFlowAccountType + */ + private FundFlowAccountType accountType; + /** + * 日期,必填 + */ + private LocalDate date; + /** + * 本次查询偏移量,选填 + *

+ * 表示该次请求资源的起始位置,从0开始计数。调用方选填,默认为0。offset为20,limit为100时,查询第20-119条数据 + */ + private Integer offset; + /** + * 本次请求最大查询条数,必填 + *

+ * 非0非负的整数,该次请求可返回的最大资源条数,最大支持100条。 + */ + private Integer limit; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/TransferDetailElectronicParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/TransferDetailElectronicParams.java new file mode 100644 index 0000000..79e1da6 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/batchtransfer/TransferDetailElectronicParams.java @@ -0,0 +1,36 @@ +package cn.felord.payment.wechat.v3.model.batchtransfer; + +import cn.felord.payment.wechat.enumeration.TransferAcceptType; +import lombok.Data; + +/** + * 转账明细电子回单受理API请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class TransferDetailElectronicParams { + /** + * 电子回单受理类型,必填。 + * + * @see TransferAcceptType + */ + private TransferAcceptType acceptType; + /** + * 商家转账批次单号,选填。 + *

+ * 需要电子回单的批量转账明细单所在的转账批次单号,该单号为商户申请转账时生成的商户单号。 + * 受理类型为{@code BATCH_TRANSFER}时该单号必填,否则该单号留空。 + */ + private String outBatchNo; + /** + * 商家转账明细单号,必填。 + *

+ *

    + *
  • 受理类型为{@code BATCH_TRANSFER}时填写商家批量转账明细单号。
  • + *
  • 受理类型为{@code TRANSFER_TO_POCKET}或{@code TRANSFER_TO_BANK}时填写商家转账单号。
  • + *
+ */ + private String outDetailNo; +} From e387e91e38384e442cfdfedb3620a018687326f3 Mon Sep 17 00:00:00 2001 From: felord Date: Mon, 31 May 2021 18:45:26 +0800 Subject: [PATCH 10/13] =?UTF-8?q?feat:=20=E7=9B=B4=E8=BF=9E=E5=95=86?= =?UTF-8?q?=E6=88=B7V3=E5=88=86=E8=B4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/enumeration/ReceiverType.java | 23 ++ .../wechat/enumeration/WechatPayV3Type.java | 55 +++- .../wechat/v2/WechatAllocationApi.java | 19 +- .../wechat/v3/WechatBatchTransferApi.java | 6 +- .../payment/wechat/v3/WechatPayCallback.java | 81 +++-- .../wechat/v3/WechatProfitsharingApi.java | 285 ++++++++++++++++++ .../profitsharing/AddReceiversParams.java | 92 ++++++ .../profitsharing/DelReceiversParams.java | 26 ++ .../profitsharing/ProfitSharingOrder.java | 45 +++ .../ProfitsharingConsumeData.java | 78 +++++ .../model/profitsharing/QueryOrderParams.java | 21 ++ .../profitsharing/QueryReturnOrderParams.java | 21 ++ .../v3/model/profitsharing/Receiver.java | 44 +++ .../profitsharing/ReturnOrdersParams.java | 41 +++ .../model/profitsharing/UnfreezeParams.java | 25 ++ 15 files changed, 816 insertions(+), 46 deletions(-) create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/ReceiverType.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatProfitsharingApi.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/Receiver.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/ReceiverType.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/ReceiverType.java new file mode 100644 index 0000000..6c1c8be --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/ReceiverType.java @@ -0,0 +1,23 @@ +package cn.felord.payment.wechat.enumeration; + + +/** + * 分账接收方类型 + * + * @author n1 + * @since 2021/5/31 17:47 + */ +public enum ReceiverType { + /** + * 商户号 + */ + MERCHANT_ID, + /** + * 个人openid(由父商户APPID转换得到) + */ + PERSONAL_OPENID, + /** + * 个人sub_openid(由子商户APPID转换得到),服务商模式 + */ + PERSONAL_SUB_OPENID +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java index c4ea443..b21734d 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java @@ -496,6 +496,7 @@ public enum WechatPayV3Type { /** * 查询转账明细电子回单受理结果API. * 请求方式同{@link WechatPayV3Type#BATCH_TRANSFER_ELECTRONIC}不同 + * * @since 1.0.11.RELEASES */ BATCH_TRANSFER_ELECTRONIC_DETAIL(HttpMethod.GET, "%s/v3/transfer-detail/electronic-receipts"), @@ -510,7 +511,8 @@ public enum WechatPayV3Type { * * @since 1.0.11.RELEASES */ - BATCH_TRANSFER_FUND_DAY_BALANCE(HttpMethod.GET, "%s/v3/merchant/fund/dayendbalance/{account_type}"), /** + BATCH_TRANSFER_FUND_DAY_BALANCE(HttpMethod.GET, "%s/v3/merchant/fund/dayendbalance/{account_type}"), + /** * 商户银行来账查询API * * @since 1.0.11.RELEASES @@ -565,7 +567,56 @@ public enum WechatPayV3Type { * @since 1.0.0.RELEASE */ TRANSACTION_OUT_TRADE_NO_PARTNER(HttpMethod.GET, "%s/v3/pay/partner/transactions/out-trade-no/{out_trade_no}"), - ; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * 请求分账API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_ORDERS(HttpMethod.POST, "%s/v3/profitsharing/orders"), + /** + * 查询分账结果API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_ORDERS_RESULT(HttpMethod.GET, "%s/v3/profitsharing/orders/{out_order_no}"), + /** + * 请求分账回退API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_RETURN_ORDERS(HttpMethod.POST, "%s/v3/profitsharing/return-orders"), + /** + * 查询分账回退结果API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_RETURN_ORDERS_RESULT(HttpMethod.GET, "%s/v3/profitsharing/return-orders"), + /** + * 解冻剩余资金API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_ORDERS_UNFREEZE(HttpMethod.POST, "%s/v3/profitsharing/orders/unfreeze"), + /** + * 查询剩余待分金额API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_AMOUNTS(HttpMethod.GET, "%s/v3/profitsharing/transactions/{transaction_id}/amounts"), + /** + * 添加分账接收方API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_RECEIVERS_ADD(HttpMethod.POST, "%s/v3/profitsharing/receivers/add"), + /** + * 删除分账接收方API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_RECEIVERS_DELETE(HttpMethod.POST, "%s/v3/profitsharing/receivers/add"); /** * The Pattern. * diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v2/WechatAllocationApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v2/WechatAllocationApi.java index 6c815ca..e7c050b 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v2/WechatAllocationApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v2/WechatAllocationApi.java @@ -36,6 +36,7 @@ import java.util.List; * 微信支付分账 *

* + * @author felord.cn * @since 1.0.10.RELEASE */ @Slf4j @@ -72,20 +73,20 @@ public class WechatAllocationApi { */ @SneakyThrows public JsonNode profitSharing(ProfitSharingModel profitSharingModel) { - ProfitSharingSModel profitSharingSModel = new ProfitSharingSModel(); + ProfitSharingSModel model = new ProfitSharingSModel(); List receivers = profitSharingModel.getReceivers(); - profitSharingSModel.setReceivers(MAPPER.writeValueAsString(receivers)); + model.setReceivers(MAPPER.writeValueAsString(receivers)); WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3(); - profitSharingSModel.setAppid(v3.getAppId()); - profitSharingSModel.setMchId(v3.getMchId()); + model.setAppid(v3.getAppId()); + model.setMchId(v3.getMchId()); - profitSharingSModel.setTransactionId(profitSharingModel.getTransactionId()); - profitSharingSModel.setOutOrderNo(profitSharingModel.getOutOrderNo()); + model.setTransactionId(profitSharingModel.getTransactionId()); + model.setOutOrderNo(profitSharingModel.getOutOrderNo()); - profitSharingSModel.certPath(v3.getCertPath()); - profitSharingSModel.signType(BaseModel.HMAC_SHA256); - return wechatV2Client.wechatPayRequest(profitSharingSModel, + model.certPath(v3.getCertPath()); + model.signType(BaseModel.HMAC_SHA256); + return wechatV2Client.wechatPayRequest(model, HttpMethod.POST, "https://api.mch.weixin.qq.com/secapi/pay/profitsharing"); } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java index a6602a5..01f6189 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java @@ -77,11 +77,11 @@ public class WechatBatchTransferApi extends AbstractApi { List transferDetailList = createBatchTransferParams.getTransferDetailList(); SignatureProvider signatureProvider = this.client().signatureProvider(); - final X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + final X509Certificate x509Certificate = certificate.getX509Certificate(); List encrypted = transferDetailList.stream() .peek(transferDetailListItem -> { String userName = transferDetailListItem.getUserName(); - X509Certificate x509Certificate = certificate.getX509Certificate(); String encryptedUserName = signatureProvider.encryptRequestMessage(userName, x509Certificate); transferDetailListItem.setUserName(encryptedUserName); String userIdCard = transferDetailListItem.getUserIdCard(); @@ -262,7 +262,7 @@ public class WechatBatchTransferApi extends AbstractApi { *

* 受理转账明细电子回单接口,商户通过该接口可以申请受理转账明细单电子回单服务。 *

- * 返回的下载链接可调用{@link this#downloadBillResponse(String, String)}下载文件 + * 返回的下载链接可调用{@link #downloadBillResponse(String, String)}下载文件 * * @param params the params * @return the wechat response entity diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java index f6789a8..604abed 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java @@ -29,6 +29,7 @@ import cn.felord.payment.wechat.v3.model.payscore.PayScoreConsumer; import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserConfirmConsumeData; import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPaidConsumeData; import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPermissionConsumeData; +import cn.felord.payment.wechat.v3.model.profitsharing.ProfitsharingConsumeData; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -39,7 +40,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -73,8 +73,8 @@ public class WechatPayCallback { static { MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false) - .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) .registerModule(new JavaTimeModule()); } @@ -92,7 +92,7 @@ public class WechatPayCallback { /** - * 微信支付分账回调. + * 微信支付分账V2回调. * * @param params the params * @param consumeDataConsumer the consume data consumer @@ -100,14 +100,11 @@ public class WechatPayCallback { * @since 1.0.10.RELEASE */ @SneakyThrows - public Map profitSharingCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map profitSharingCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { String data = this.callback(params, EventType.TRANSACTION); ProfitSharingConsumeData consumeData = MAPPER.readValue(data, ProfitSharingConsumeData.class); consumeDataConsumer.accept(consumeData); - Map responseBody = new HashMap<>(2); - responseBody.put("code", 200); - responseBody.put("message", "SUCCESS"); - return responseBody; + return response(); } @@ -121,14 +118,11 @@ public class WechatPayCallback { * @since 1.0.0.RELEASE */ @SneakyThrows - public Map couponCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map couponCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { String data = this.callback(params, EventType.COUPON_USE); CouponConsumeData consumeData = MAPPER.readValue(data, CouponConsumeData.class); consumeDataConsumer.accept(consumeData); - Map responseBody = new HashMap<>(2); - responseBody.put("code", 200); - responseBody.put("message", "SUCCESS"); - return responseBody; + return response(); } @@ -143,12 +137,11 @@ public class WechatPayCallback { * @since 1.0.0.RELEASE */ @SneakyThrows - public Map transactionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map transactionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { String data = this.callback(params, EventType.TRANSACTION); TransactionConsumeData consumeData = MAPPER.readValue(data, TransactionConsumeData.class); consumeDataConsumer.accept(consumeData); - return Collections.singletonMap("code", "SUCCESS"); - + return response(); } /** @@ -162,12 +155,11 @@ public class WechatPayCallback { * @since 1.0.0.RELEASE */ @SneakyThrows - public Map combineTransactionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map combineTransactionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { String data = this.callback(params, EventType.TRANSACTION); CombineTransactionConsumeData consumeData = MAPPER.readValue(data, CombineTransactionConsumeData.class); consumeDataConsumer.accept(consumeData); - return Collections.singletonMap("code", "SUCCESS"); - + return response(); } /** @@ -181,7 +173,7 @@ public class WechatPayCallback { * @since 1.0.2.RELEASE */ @SneakyThrows - public Map payscoreUserOrderCallback(ResponseSignVerifyParams params, PayScoreConsumer payScoreConsumer) { + public Map payscoreUserOrderCallback(ResponseSignVerifyParams params, PayScoreConsumer payScoreConsumer) { CallbackParams callbackParams = resolve(params); String eventType = callbackParams.getEventType(); @@ -197,8 +189,7 @@ public class WechatPayCallback { log.error("wechat pay event type is not matched, callbackParams {}", callbackParams); throw new PayException(" wechat pay event type is not matched"); } - - return Collections.singletonMap("code", "SUCCESS"); + return response(); } /** @@ -213,7 +204,7 @@ public class WechatPayCallback { * @return the map */ @SneakyThrows - public Map permissionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map permissionCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { CallbackParams callbackParams = resolve(params); String eventType = callbackParams.getEventType(); boolean closed; @@ -229,7 +220,7 @@ public class WechatPayCallback { PayScoreUserPermissionConsumeData consumeData = MAPPER.readValue(data, PayScoreUserPermissionConsumeData.class); consumeData.setClosed(closed); consumeDataConsumer.accept(consumeData); - return Collections.singletonMap("code", "SUCCESS"); + return response(); } /** @@ -265,7 +256,7 @@ public class WechatPayCallback { log.error("wechat pay event type is not matched, callbackParams {}", callbackParams); throw new PayException(" wechat pay event type is not matched"); } - return Collections.singletonMap("code", "SUCCESS"); + return response(); } /** @@ -280,7 +271,7 @@ public class WechatPayCallback { * @return the map */ @SneakyThrows - public Map busiFavorReceiveCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map busiFavorReceiveCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { CallbackParams callbackParams = resolve(params); String eventType = callbackParams.getEventType(); @@ -292,7 +283,7 @@ public class WechatPayCallback { BusiFavorReceiveConsumeData consumeData = MAPPER.readValue(data, BusiFavorReceiveConsumeData.class); consumeDataConsumer.accept(consumeData); - return Collections.singletonMap("code", "SUCCESS"); + return response(); } /** @@ -302,10 +293,10 @@ public class WechatPayCallback { * * @param params the params * @param consumeDataConsumer the consume data consumer - * @return map + * @return map map */ @SneakyThrows - public Map refundCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { + public Map refundCallback(ResponseSignVerifyParams params, Consumer consumeDataConsumer) { CallbackParams callbackParams = resolve(params); String eventType = callbackParams.getEventType(); @@ -319,9 +310,23 @@ public class WechatPayCallback { RefundConsumeData consumeData = MAPPER.readValue(data, RefundConsumeData.class); consumeDataConsumer.accept(consumeData); - return Collections.singletonMap("code", "SUCCESS"); + return response(); } + /** + * 微信支付分账V3动账通知 + * + * @param params the params + * @param profitsharingConsumeDataConsumer the profitsharing consume data consumer + * @return map map + */ + @SneakyThrows + public Map profitsharingCallback(ResponseSignVerifyParams params, Consumer profitsharingConsumeDataConsumer) { + String callback = this.callback(params, EventType.TRANSACTION); + ProfitsharingConsumeData consumeData = MAPPER.readValue(callback, ProfitsharingConsumeData.class); + profitsharingConsumeDataConsumer.accept(consumeData); + return response(); + } /** * Callback. @@ -373,6 +378,18 @@ public class WechatPayCallback { return data; } + /** + * 回调应答 + * + * @return response + */ + private Map response() { + Map responseBody = new HashMap<>(2); + responseBody.put("code", "SUCCESS"); + responseBody.put("message", "SUCCESS"); + return responseBody; + } + /** * 事件类型用于处理回调. @@ -450,7 +467,7 @@ public class WechatPayCallback { COUPON_SEND("COUPON.SEND"), /** - * 支付成功事件. + * 支付成功、分账、分账回退事件. * * @since 1.0.0.RELEASE */ diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatProfitsharingApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatProfitsharingApi.java new file mode 100644 index 0000000..3158ae6 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatProfitsharingApi.java @@ -0,0 +1,285 @@ +package cn.felord.payment.wechat.v3; + +import cn.felord.payment.wechat.WechatPayProperties; +import cn.felord.payment.wechat.enumeration.WeChatServer; +import cn.felord.payment.wechat.enumeration.WechatPayV3Type; +import cn.felord.payment.wechat.v3.model.profitsharing.*; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.HttpHeaders; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 微信V3直联商户分账 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +public class WechatProfitsharingApi extends AbstractApi { + + /** + * Instantiates a new Abstract api. + * + * @param wechatPayClient the wechat pay client + * @param tenantId the tenant id + */ + public WechatProfitsharingApi(WechatPayClient wechatPayClient, String tenantId) { + super(wechatPayClient, tenantId); + } + + /** + * 请求分账API + *

+ * 微信订单支付成功后,商户发起分账请求,将结算后的资金分到分账接收方 + *

+ * 注意: + *

    + *
  • 对同一笔订单最多能发起20次分账请求,每次请求最多分给50个接收方
  • + *
  • 此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取
  • + *
+ * + * @param profitSharingOrder the profit sharing order + * @return the wechat response entity + */ + public WechatResponseEntity profitsharingOrders(ProfitSharingOrder profitSharingOrder) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS, profitSharingOrder) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + SignatureProvider signatureProvider = this.client().signatureProvider(); + X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + final X509Certificate x509Certificate = certificate.getX509Certificate(); + params.setAppid(v3.getAppId()); + List receivers = params.getReceivers(); + if (!CollectionUtils.isEmpty(receivers)) { + List encrypted = receivers.stream() + .peek(receiversItem -> { + String name = receiversItem.getName(); + if (StringUtils.hasText(name)) { + String encryptedName = signatureProvider.encryptRequestMessage(name, x509Certificate); + receiversItem.setName(encryptedName); + } + }).collect(Collectors.toList()); + params.setReceivers(encrypted); + } + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Wechatpay-Serial", certificate.getWechatPaySerial()); + return Post(uri, params, httpHeaders); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询分账结果API + *

+ * 发起分账请求后,可调用此接口查询分账结果 + *

+ * 注意: + *

    + *
  • 发起解冻剩余资金请求后,可调用此接口查询解冻剩余资金的结果
  • + *
+ * + * @param queryOrderParams the query order params + * @return the wechat response entity + */ + public WechatResponseEntity queryProfitsharingOrder(QueryOrderParams queryOrderParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS_RESULT, queryOrderParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .queryParam("transaction_id", params.getTransactionId()) + .build() + .expand(params.getOutOrderNo()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 请求分账回退API + *

+ * 如果订单已经分账,在退款时,可以先调此接口,将已分账的资金从分账接收方的账户回退给分账方,再发起退款 + *

+ * 注意: + *

    + *
  • 分账回退以原分账单为依据,支持多次回退,申请回退总金额不能超过原分账单分给该接收方的金额
  • + *
  • 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果
  • + *
  • 对同一笔分账单最多能发起20次分账回退请求
  • + *
  • 退款和分账回退没有耦合,分账回退可以先于退款请求,也可以后于退款请求
  • + *
  • 此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用
  • + *
+ * + * @param returnOrdersParams the return orders params + * @return the wechat response entity + */ + public WechatResponseEntity returnOrders(ReturnOrdersParams returnOrdersParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RETURN_ORDERS, returnOrdersParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询分账回退结果API + *

+ * 商户需要核实回退结果,可调用此接口查询回退结果 + *

+ * 注意: + *

    + *
  • 如果分账回退接口返回状态为处理中,可调用此接口查询回退结果
  • + *
+ * + * @param queryReturnOrderParams the query return order params + * @return the wechat response entity + */ + public WechatResponseEntity queryReturnOrders(QueryReturnOrderParams queryReturnOrderParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RETURN_ORDERS_RESULT, queryReturnOrderParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .queryParam("out_order_no", params.getOutOrderNo()) + .build() + .expand(params.getOutReturnNo()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 解冻剩余资金API + *

+ * 不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户 + *

+ * 注意: + *

    + *
  • 调用分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户
  • + *
  • 此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取
  • + *
+ * + * @param unfreezeParams the unfreeze params + * @return the wechat response entity + */ + public WechatResponseEntity unfreeze(UnfreezeParams unfreezeParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS_UNFREEZE, unfreezeParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询剩余待分金额API + *

+ * 可调用此接口查询订单剩余待分金额 + * + * @param transactionId the transaction id + * @return the wechat response entity + */ + public WechatResponseEntity queryAmounts(String transactionId) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_AMOUNTS, transactionId) + .function((wechatPayV3Type, id) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .expand(id) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 添加分账接收方API + *

+ * 商户发起添加分账接收方请求,建立分账接收方列表。后续可通过发起分账请求,将分账方商户结算后的资金,分到该分账接收方 + * + * @param addReceiversParams the add receivers params + * @return wechat response entity + */ + public WechatResponseEntity addReceivers(AddReceiversParams addReceiversParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RECEIVERS_ADD, addReceiversParams) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + SignatureProvider signatureProvider = this.client().signatureProvider(); + X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + final X509Certificate x509Certificate = certificate.getX509Certificate(); + params.setAppid(v3.getAppId()); + String name = params.getName(); + if (StringUtils.hasText(name)) { + String encryptedName = signatureProvider.encryptRequestMessage(name, x509Certificate); + params.setName(encryptedName); + } + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Wechatpay-Serial", certificate.getWechatPaySerial()); + return Post(uri, params, httpHeaders); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 删除分账接收方API + *

+ * 商户发起删除分账接收方请求。删除后,不支持将分账方商户结算后的资金,分到该分账接收方 + * + * @param delReceiversParams the del receivers params + * @return the wechat response entity + */ + public WechatResponseEntity deleteReceivers(DelReceiversParams delReceiversParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RECEIVERS_DELETE, delReceiversParams) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + params.setAppid(v3.getAppId()); + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java new file mode 100644 index 0000000..5a9bf30 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java @@ -0,0 +1,92 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.enumeration.ReceiverType; +import lombok.Data; + +/** + * 添加分账接收方API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class AddReceiversParams { + /** + * 应用ID,自动注入 + */ + private String appid; + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; + /** + * 分账个人接收方姓名,选填 + *

+ * 分账接收方类型是{@code MERCHANT_ID}时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名 分账接收方类型是{@code PERSONAL_OPENID}时,是个人姓名(选传,传则校验) + *

    + *
  1. 分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明
  2. + *
  3. 使用微信支付平台证书中的公钥
  4. + *
  5. 使用RSAES-OAEP算法进行加密
  6. + *
  7. 将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
  8. + *
+ */ + private String name; + /** + * 与分账方的关系类型,必填 + */ + private RelationType relationType; + /** + * 自定义的分账关系,选填 + */ + private String customRelation; + + /** + * 子商户与接收方的关系 + */ + public enum RelationType { + /** + * 门店. + */ + STORE, + /** + * 员工. + */ + STAFF, + /** + * 店主. + */ + STORE_OWNER, + /** + * 合作伙伴. + */ + PARTNER, + /** + * 总部. + */ + HEADQUARTER, + /** + * 品牌方. + */ + BRAND, + /** + * 分销商. + */ + DISTRIBUTOR, + /** + * 用户. + */ + USER, + /** + * 供应商. + */ + SUPPLIER, + /** + * 自定义. + */ + CUSTOM + } +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java new file mode 100644 index 0000000..e306eb7 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java @@ -0,0 +1,26 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.enumeration.ReceiverType; +import lombok.Data; + +/** + * 删除分账接收方API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class DelReceiversParams { + /** + * 应用ID,自动注入 + */ + private String appid; + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java new file mode 100644 index 0000000..4dd55fe --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java @@ -0,0 +1,45 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +import java.util.List; + +/** + * 直连商户请求分账API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class ProfitSharingOrder { + /** + * 应用ID,自动注入 + */ + private String appid; + /** + * 微信订单号,必填 + */ + private String transactionId; + /** + * 商户分账单号,必填 + *

+ * 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。 + * 只能是数字、大小写字母_-|*@ + */ + private String outOrderNo; + /** + * 分账接收方列表,选填 + *

+ * 可以设置出资商户作为分账接受方,最多可有50个分账接收方 + */ + private List receivers; + /** + * 是否解冻剩余未分资金,必填 + *

+ *

    + *
  1. 如果为{@code true},该笔订单剩余未分账的金额会解冻回分账方商户;
  2. + *
  3. 如果为{@code false},该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。
  4. + *
+ */ + private Boolean unfreezeUnsplit; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java new file mode 100644 index 0000000..3b65f51 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java @@ -0,0 +1,78 @@ +/* + * + * Copyright 2019-2020 felord.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * Website: + * https://felord.cn + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.v2.model.allocation.Receiver; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 微信支付分账通知参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class ProfitsharingConsumeData { + + /** + * 直连商户号. + *

+ * 直连模式分账发起和出资商户 + */ + private String mchid; + + /** + * 微信订单号. + *

+ * 微信支付订单号 + */ + private String transactionId; + + /** + * 微信分账/回退单号. + */ + private String orderId; + + /** + * 商户分账/回退单号. + *

+ * 分账方系统内部的分账/回退单号 + */ + private String outOrderNo; + + /** + * 分账接收方. + *

+ * 分账接收方对象 + */ + private List receivers; + + /** + * 成功时间. + *

+ * Rfc3339标准 + */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8") + private LocalDateTime successTime; + +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java new file mode 100644 index 0000000..8a25740 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java @@ -0,0 +1,21 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 查询分账结果API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class QueryOrderParams { + /** + * 商户分账单号,必填 + */ + private String outOrderNo; + /** + * 微信订单号,必填 + */ + private String transactionId; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java new file mode 100644 index 0000000..182e72e --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java @@ -0,0 +1,21 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 查询分账回退结果API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class QueryReturnOrderParams { + /** + * 商户回退单号,必填 + */ + private String outReturnNo; + /** + * 商户分账单号,必填 + */ + private String outOrderNo; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/Receiver.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/Receiver.java new file mode 100644 index 0000000..3e9432b --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/Receiver.java @@ -0,0 +1,44 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.enumeration.ReceiverType; +import lombok.Data; + +/** + * 分账接收方信息 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class Receiver { + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; + /** + * 分账个人接收方姓名,选填 + *

+ * 在接收方类型为个人的时可选填,若有值,会检查与 name 是否实名匹配,不匹配会拒绝分账请求 + *

    + *
  1. 分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明
  2. + *
  3. 使用微信支付平台证书中的公钥
  4. + *
  5. 使用RSAES-OAEP算法进行加密
  6. + *
  7. 将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
  8. + *
+ */ + private String name; + /** + * 分账金额,必填 + *

+ * 单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额 + */ + private Integer amount; + /** + * 分账的原因描述,必填。分账账单中需要体现 + */ + private String description; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java new file mode 100644 index 0000000..981c3f2 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java @@ -0,0 +1,41 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 请求分账回退API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class ReturnOrdersParams { + /** + * 微信分账单号,同{@link #outOrderNo} 二选一 + */ + private String orderId; + /** + * 商户分账单号,同{@link #orderId} 二选一 + */ + private String outOrderNo; + /** + * 商户回退单号,必填 + */ + private String outReturnNo; + /** + * 回退商户号,必填 + *

+ * 分账回退的出资商户,只能对原分账请求中成功分给商户接收方进行回退 + */ + private String returnMchid; + /** + * 回退金额,必填 + *

+ * 需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额 + */ + private Integer amount; + /** + * 回退描述,必填 + */ + private String description; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java new file mode 100644 index 0000000..a53f789 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java @@ -0,0 +1,25 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 解冻剩余资金API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class UnfreezeParams { + /** + * 微信订单号,必填 + */ + private String transactionId; + /** + * 商户分账单号,必填 + */ + private String outOrderNo; + /** + * 分账描述,必填 + */ + private String description; +} \ No newline at end of file From 398df66ec5b3eb3dfa68ef46bb7ff6b8c65f7a5c Mon Sep 17 00:00:00 2001 From: felord Date: Tue, 1 Jun 2021 15:36:40 +0800 Subject: [PATCH 11/13] =?UTF-8?q?feat:=20=E6=9C=8D=E5=8A=A1=E5=95=86V3?= =?UTF-8?q?=E5=88=86=E8=B4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/quick_start.md | 2 +- .../wechat/enumeration/WechatPayV3Type.java | 6 + .../payment/wechat/v3/WechatApiProvider.java | 21 +- .../wechat/v3/WechatBatchTransferApi.java | 4 +- .../wechat/v3/WechatMarketingFavorApi.java | 6 +- .../v3/WechatPartnerProfitsharingApi.java | 318 ++++++++++++++++++ .../payment/wechat/v3/WechatPayScoreApi.java | 10 +- .../profitsharing/AddReceiversParams.java | 2 +- .../profitsharing/DelReceiversParams.java | 2 +- .../PartnerAddReceiversParams.java | 102 ++++++ .../PartnerDelReceiversParams.java | 36 ++ .../PartnerProfitSharingOrder.java | 55 +++ .../PartnerProfitsharingConsumeData.java | 84 +++++ .../PartnerQueryOrderParams.java | 25 ++ .../PartnerQueryReturnOrderParams.java | 25 ++ .../PartnerReturnOrdersParams.java | 45 +++ .../profitsharing/PartnerUnfreezeParams.java | 29 ++ .../profitsharing/ProfitSharingOrder.java | 2 +- .../ProfitsharingConsumeData.java | 2 +- .../model/profitsharing/QueryOrderParams.java | 2 +- .../profitsharing/QueryReturnOrderParams.java | 2 +- .../profitsharing/ReturnOrdersParams.java | 2 +- .../model/profitsharing/UnfreezeParams.java | 2 +- 23 files changed, 765 insertions(+), 19 deletions(-) create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerProfitsharingApi.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerAddReceiversParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerDelReceiversParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitSharingOrder.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitsharingConsumeData.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryOrderParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryReturnOrderParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerReturnOrdersParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerUnfreezeParams.java diff --git a/docs/quick_start.md b/docs/quick_start.md index f44643c..c61df5f 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -92,7 +92,7 @@ wechat: ### 支付宝 -在Spring Boot项目中的`application.yaml`中配置`ali.pay.v1`相关参数。 +在Spring Boot项目中的`application.yaml`中配置`ali.pay.v1`相关参数。证书细节参见【日常踩坑】 ```yaml ali: pay: diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java index b21734d..654ef68 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/enumeration/WechatPayV3Type.java @@ -605,6 +605,12 @@ public enum WechatPayV3Type { * @since 1.0.11.RELEASE */ PROFITSHARING_AMOUNTS(HttpMethod.GET, "%s/v3/profitsharing/transactions/{transaction_id}/amounts"), + /** + * 服务商专用-查询最大分账比例API. + * + * @since 1.0.11.RELEASE + */ + PROFITSHARING_MCH_CONFIG(HttpMethod.GET, "%s/v3/profitsharing/merchant-configs/{sub_mchid}"), /** * 添加分账接收方API. * diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java index b7c9778..cb9e442 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java @@ -123,7 +123,7 @@ public class WechatApiProvider { } /** - * 批量转账到零钱. + * 批量转账到零钱. *

* 批量转账到零钱提供商户同时向多个用户微信零钱转账的能力。商户可以使用批量转账到零钱用于费用报销、员工福利发放、合作伙伴货款或服务款项支付等场景,提高转账效率。 * @@ -193,4 +193,23 @@ public class WechatApiProvider { return new WechatAllocationApi(wechatV2Client); } + /** + * 直连商户微信支付分账,基于V3 + * + * @param tenantId the tenant id + * @return the wechat profitsharing api + */ + public WechatProfitsharingApi profitsharingApi(String tenantId) { + return new WechatProfitsharingApi(wechatPayClient, tenantId); + } + + /** + * 服务商微信支付分账,基于V3 + * + * @param tenantId the tenant id + * @return the wechat partner profitsharing api + */ + public WechatPartnerProfitsharingApi partnerProfitsharingApi(String tenantId) { + return new WechatPartnerProfitsharingApi(wechatPayClient, tenantId); + } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java index 01f6189..5f4ef78 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java @@ -140,7 +140,7 @@ public class WechatBatchTransferApi extends AbstractApi { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_WECHAT, queryBatchTransferDetailParams) .function((type, params) -> { - Map queryParams = new HashMap<>(); + Map queryParams = new HashMap<>(2); queryParams.put("batch_id", params.getBatchIdOrOutBatchNo()); queryParams.put("detail_id", params.getDetailIdOrOutDetailNo()); @@ -195,7 +195,7 @@ public class WechatBatchTransferApi extends AbstractApi { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_MCH, queryBatchTransferDetailParams) .function((type, params) -> { - Map queryParams = new HashMap<>(); + Map queryParams = new HashMap<>(2); queryParams.put("batch_id", params.getBatchIdOrOutBatchNo()); queryParams.put("detail_id", params.getDetailIdOrOutDetailNo()); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java index 6f27922..8bba96f 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java @@ -353,9 +353,9 @@ public class WechatMarketingFavorApi extends AbstractApi { MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("appid", v3.getAppId()); - MultiValueMap pathParams = new LinkedMultiValueMap<>(); - pathParams.add("openid", params.getOpenId()); - pathParams.add("coupon_id", params.getCouponId()); + Map pathParams = new HashMap<>(2); + pathParams.put("openid", params.getOpenId()); + pathParams.put("coupon_id", params.getCouponId()); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) .queryParams(queryParams) .build() diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerProfitsharingApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerProfitsharingApi.java new file mode 100644 index 0000000..3504cf5 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPartnerProfitsharingApi.java @@ -0,0 +1,318 @@ +package cn.felord.payment.wechat.v3; + +import cn.felord.payment.wechat.WechatPayProperties; +import cn.felord.payment.wechat.enumeration.WeChatServer; +import cn.felord.payment.wechat.enumeration.WechatPayV3Type; +import cn.felord.payment.wechat.v3.model.profitsharing.*; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.HttpHeaders; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 微信V3服务商分账 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +public class WechatPartnerProfitsharingApi extends AbstractApi { + + /** + * Instantiates a new Abstract api. + * + * @param wechatPayClient the wechat pay client + * @param tenantId the tenant id + */ + public WechatPartnerProfitsharingApi(WechatPayClient wechatPayClient, String tenantId) { + super(wechatPayClient, tenantId); + } + + /** + * 请求分账API + *

+ * 微信订单支付成功后,商户发起分账请求,将结算后的资金分到分账接收方 + *

+ * 注意: + *

    + *
  • 对同一笔订单最多能发起20次分账请求,每次请求最多分给50个接收方
  • + *
  • 此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取
  • + *
+ * + * @param profitSharingOrder the profit sharing order + * @return the wechat response entity + */ + public WechatResponseEntity profitsharingOrders(PartnerProfitSharingOrder profitSharingOrder) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS, profitSharingOrder) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + SignatureProvider signatureProvider = this.client().signatureProvider(); + X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + final X509Certificate x509Certificate = certificate.getX509Certificate(); + params.setAppid(v3.getAppId()); + List receivers = params.getReceivers(); + if (!CollectionUtils.isEmpty(receivers)) { + List encrypted = receivers.stream() + .peek(receiversItem -> { + String name = receiversItem.getName(); + if (StringUtils.hasText(name)) { + String encryptedName = signatureProvider.encryptRequestMessage(name, x509Certificate); + receiversItem.setName(encryptedName); + } + }).collect(Collectors.toList()); + params.setReceivers(encrypted); + } + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Wechatpay-Serial", certificate.getWechatPaySerial()); + return Post(uri, params, httpHeaders); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询分账结果API + *

+ * 发起分账请求后,可调用此接口查询分账结果 + *

+ * 注意: + *

    + *
  • 发起解冻剩余资金请求后,可调用此接口查询解冻剩余资金的结果
  • + *
+ * + * @param queryOrderParams the query order params + * @return the wechat response entity + */ + public WechatResponseEntity queryProfitsharingOrder(PartnerQueryOrderParams queryOrderParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS_RESULT, queryOrderParams) + .function((wechatPayV3Type, params) -> { + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("transaction_id", params.getTransactionId()); + Optional.ofNullable(params.getSubMchid()) + .ifPresent(mchId -> queryParams.add("sub_mchid", params.getSubMchid())); + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .queryParams(queryParams) + .build() + .expand(params.getOutOrderNo()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 请求分账回退API + *

+ * 如果订单已经分账,在退款时,可以先调此接口,将已分账的资金从分账接收方的账户回退给分账方,再发起退款 + *

+ * 注意: + *

    + *
  • 分账回退以原分账单为依据,支持多次回退,申请回退总金额不能超过原分账单分给该接收方的金额
  • + *
  • 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果
  • + *
  • 对同一笔分账单最多能发起20次分账回退请求
  • + *
  • 退款和分账回退没有耦合,分账回退可以先于退款请求,也可以后于退款请求
  • + *
  • 此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用
  • + *
+ * + * @param returnOrdersParams the return orders params + * @return the wechat response entity + */ + public WechatResponseEntity returnOrders(PartnerReturnOrdersParams returnOrdersParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RETURN_ORDERS, returnOrdersParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询分账回退结果API + *

+ * 商户需要核实回退结果,可调用此接口查询回退结果 + *

+ * 注意: + *

    + *
  • 如果分账回退接口返回状态为处理中,可调用此接口查询回退结果
  • + *
+ * + * @param queryReturnOrderParams the query return order params + * @return the wechat response entity + */ + public WechatResponseEntity queryReturnOrders(PartnerQueryReturnOrderParams queryReturnOrderParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RETURN_ORDERS_RESULT, queryReturnOrderParams) + .function((wechatPayV3Type, params) -> { + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("out_order_no", params.getOutOrderNo()); + Optional.ofNullable(params.getSubMchid()) + .ifPresent(mchId -> queryParams.add("sub_mchid", params.getSubMchid())); + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .queryParams(queryParams) + .build() + .expand(params.getOutReturnNo()) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 解冻剩余资金API + *

+ * 不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户 + *

+ * 注意: + *

    + *
  • 调用分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户
  • + *
  • 此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取
  • + *
+ * + * @param unfreezeParams the unfreeze params + * @return the wechat response entity + */ + public WechatResponseEntity unfreeze(PartnerUnfreezeParams unfreezeParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS_UNFREEZE, unfreezeParams) + .function((wechatPayV3Type, params) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 查询剩余待分金额API + *

+ * 可调用此接口查询订单剩余待分金额 + * + * @param transactionId the transaction id + * @return the wechat response entity + */ + public WechatResponseEntity queryAmounts(String transactionId) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_AMOUNTS, transactionId) + .function((wechatPayV3Type, id) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .expand(id) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * (服务商)查询剩余待分金额API + *

+ * 可调用此接口查询订单剩余待分金额 + * + * @param subMchid the sub mchid + * @return the wechat response entity + */ + public WechatResponseEntity queryMchConfigs(String subMchid) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_MCH_CONFIG, subMchid) + .function((wechatPayV3Type, id) -> { + + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .expand(id) + .toUri(); + return Get(uri); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 添加分账接收方API + *

+ * 商户发起添加分账接收方请求,建立分账接收方列表。后续可通过发起分账请求,将分账方商户结算后的资金,分到该分账接收方 + * + * @param addReceiversParams the add receivers params + * @return wechat response entity + */ + public WechatResponseEntity addReceivers(PartnerAddReceiversParams addReceiversParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RECEIVERS_ADD, addReceiversParams) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + SignatureProvider signatureProvider = this.client().signatureProvider(); + X509WechatCertificateInfo certificate = signatureProvider.getCertificate(); + final X509Certificate x509Certificate = certificate.getX509Certificate(); + params.setAppid(v3.getAppId()); + String name = params.getName(); + if (StringUtils.hasText(name)) { + String encryptedName = signatureProvider.encryptRequestMessage(name, x509Certificate); + params.setName(encryptedName); + } + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Wechatpay-Serial", certificate.getWechatPaySerial()); + return Post(uri, params, httpHeaders); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + /** + * 删除分账接收方API + *

+ * 商户发起删除分账接收方请求。删除后,不支持将分账方商户结算后的资金,分到该分账接收方 + * + * @param delReceiversParams the del receivers params + * @return the wechat response entity + */ + public WechatResponseEntity deleteReceivers(PartnerDelReceiversParams delReceiversParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.PROFITSHARING_RECEIVERS_DELETE, delReceiversParams) + .function((wechatPayV3Type, params) -> { + WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); + params.setAppid(v3.getAppId()); + URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) + .build() + .toUri(); + return Post(uri, params); + }) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java index 86068ae..aef65eb 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java @@ -29,6 +29,8 @@ import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; +import java.util.HashMap; +import java.util.Map; /** * 微信支付分API. @@ -63,10 +65,10 @@ public class WechatPayScoreApi extends AbstractApi { .function((wechatPayV3Type, userServiceStateParams) -> { WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); - MultiValueMap expandParams = new LinkedMultiValueMap<>(); - expandParams.add("appid", v3.getAppId()); - expandParams.add("service_id", params.getServiceId()); - expandParams.add("openid", params.getOpenId()); + Map expandParams = new HashMap<>(3); + expandParams.put("appid", v3.getAppId()); + expandParams.put("service_id", params.getServiceId()); + expandParams.put("openid", params.getOpenId()); URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) .build() diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java index 5a9bf30..14f3a67 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/AddReceiversParams.java @@ -4,7 +4,7 @@ import cn.felord.payment.wechat.enumeration.ReceiverType; import lombok.Data; /** - * 添加分账接收方API-请求参数 + * 直连商户-添加分账接收方API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java index e306eb7..a08cc45 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/DelReceiversParams.java @@ -4,7 +4,7 @@ import cn.felord.payment.wechat.enumeration.ReceiverType; import lombok.Data; /** - * 删除分账接收方API-请求参数 + * 直连商户-删除分账接收方API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerAddReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerAddReceiversParams.java new file mode 100644 index 0000000..25e04d4 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerAddReceiversParams.java @@ -0,0 +1,102 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.enumeration.ReceiverType; +import lombok.Data; + +/** + * 服务商-添加分账接收方API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerAddReceiversParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 应用ID,自动注入 + */ + private String appid; + /** + * 子商户应用ID,选填 + *

+ * 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填 + */ + private String subAppid; + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; + /** + * 分账个人接收方姓名,选填 + *

+ * 分账接收方类型是{@code MERCHANT_ID}时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名 分账接收方类型是{@code PERSONAL_OPENID}时,是个人姓名(选传,传则校验) + *

    + *
  1. 分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明
  2. + *
  3. 使用微信支付平台证书中的公钥
  4. + *
  5. 使用RSAES-OAEP算法进行加密
  6. + *
  7. 将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
  8. + *
+ */ + private String name; + /** + * 与分账方的关系类型,必填 + */ + private RelationType relationType; + /** + * 自定义的分账关系,选填 + */ + private String customRelation; + + /** + * 子商户与接收方的关系 + */ + public enum RelationType { + /** + * 门店. + */ + STORE, + /** + * 员工. + */ + STAFF, + /** + * 店主. + */ + STORE_OWNER, + /** + * 合作伙伴. + */ + PARTNER, + /** + * 总部. + */ + HEADQUARTER, + /** + * 品牌方. + */ + BRAND, + /** + * 分销商. + */ + DISTRIBUTOR, + /** + * 用户. + */ + USER, + /** + * 供应商. + */ + SUPPLIER, + /** + * 自定义. + */ + CUSTOM + } +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerDelReceiversParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerDelReceiversParams.java new file mode 100644 index 0000000..cd66d4b --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerDelReceiversParams.java @@ -0,0 +1,36 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.enumeration.ReceiverType; +import lombok.Data; + +/** + * 服务商-删除分账接收方API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerDelReceiversParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 应用ID,自动注入 + */ + private String appid; + /** + * 子商户应用ID,选填 + *

+ * 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填 + */ + private String subAppid; + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitSharingOrder.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitSharingOrder.java new file mode 100644 index 0000000..7407529 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitSharingOrder.java @@ -0,0 +1,55 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +import java.util.List; + +/** + * 服务商请求分账API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerProfitSharingOrder { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 服务商应用ID,自动注入 + */ + private String appid; + /** + * 子商户应用ID,选填 + *

+ * 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填 + */ + private String subAppid; + /** + * 微信订单号,必填 + */ + private String transactionId; + /** + * 商户分账单号,必填 + *

+ * 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。 + * 只能是数字、大小写字母_-|*@ + */ + private String outOrderNo; + /** + * 分账接收方列表,选填 + *

+ * 可以设置出资商户作为分账接受方,最多可有50个分账接收方 + */ + private List receivers; + /** + * 是否解冻剩余未分资金,必填 + *

+ *

    + *
  1. 如果为{@code true},该笔订单剩余未分账的金额会解冻回分账方商户;
  2. + *
  3. 如果为{@code false},该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。
  4. + *
+ */ + private Boolean unfreezeUnsplit; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitsharingConsumeData.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitsharingConsumeData.java new file mode 100644 index 0000000..6c4b67d --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerProfitsharingConsumeData.java @@ -0,0 +1,84 @@ +/* + * + * Copyright 2019-2020 felord.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * Website: + * https://felord.cn + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import cn.felord.payment.wechat.v2.model.allocation.Receiver; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 服务商-微信支付分账动账通知参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerProfitsharingConsumeData { + + /** + * 服务商商户号. + *

+ * 服务商模式分账发起商户 + */ + private String mchid; + /** + * 子商户号 + *

+ * 服务商模式分账出资商户 + */ + private String subMchid; + + /** + * 微信订单号. + *

+ * 微信支付订单号 + */ + private String transactionId; + + /** + * 微信分账/回退单号. + */ + private String orderId; + + /** + * 商户分账/回退单号. + *

+ * 分账方系统内部的分账/回退单号 + */ + private String outOrderNo; + + /** + * 分账接收方. + *

+ * 分账接收方对象 + */ + private List receivers; + + /** + * 成功时间. + *

+ * Rfc3339标准 + */ + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8") + private LocalDateTime successTime; + +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryOrderParams.java new file mode 100644 index 0000000..fa12dc9 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryOrderParams.java @@ -0,0 +1,25 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 服务商-查询分账结果API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerQueryOrderParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 商户分账单号,必填 + */ + private String outOrderNo; + /** + * 微信订单号,必填 + */ + private String transactionId; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryReturnOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryReturnOrderParams.java new file mode 100644 index 0000000..3f6ce01 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerQueryReturnOrderParams.java @@ -0,0 +1,25 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 服务商-查询分账回退结果API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerQueryReturnOrderParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 商户回退单号,必填 + */ + private String outReturnNo; + /** + * 商户分账单号,必填 + */ + private String outOrderNo; +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerReturnOrdersParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerReturnOrdersParams.java new file mode 100644 index 0000000..65cf588 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerReturnOrdersParams.java @@ -0,0 +1,45 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 服务商-请求分账回退API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerReturnOrdersParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 微信分账单号,同{@link #outOrderNo} 二选一 + */ + private String orderId; + /** + * 商户分账单号,同{@link #orderId} 二选一 + */ + private String outOrderNo; + /** + * 商户回退单号,必填 + */ + private String outReturnNo; + /** + * 回退商户号,必填 + *

+ * 分账回退的出资商户,只能对原分账请求中成功分给商户接收方进行回退 + */ + private String returnMchid; + /** + * 回退金额,必填 + *

+ * 需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额 + */ + private Integer amount; + /** + * 回退描述,必填 + */ + private String description; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerUnfreezeParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerUnfreezeParams.java new file mode 100644 index 0000000..0b3f192 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/PartnerUnfreezeParams.java @@ -0,0 +1,29 @@ +package cn.felord.payment.wechat.v3.model.profitsharing; + +import lombok.Data; + +/** + * 服务商-解冻剩余资金API-请求参数 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ +@Data +public class PartnerUnfreezeParams { + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 微信订单号,必填 + */ + private String transactionId; + /** + * 商户分账单号,必填 + */ + private String outOrderNo; + /** + * 分账描述,必填 + */ + private String description; +} \ No newline at end of file diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java index 4dd55fe..b58174a 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitSharingOrder.java @@ -5,7 +5,7 @@ import lombok.Data; import java.util.List; /** - * 直连商户请求分账API-请求参数 + * 直连商户-请求分账API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java index 3b65f51..f02951c 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ProfitsharingConsumeData.java @@ -26,7 +26,7 @@ import java.time.LocalDateTime; import java.util.List; /** - * 微信支付分账通知参数 + * 直连商户-微信支付分账动账通知参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java index 8a25740..9778454 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryOrderParams.java @@ -3,7 +3,7 @@ package cn.felord.payment.wechat.v3.model.profitsharing; import lombok.Data; /** - * 查询分账结果API-请求参数 + * 直连商户-查询分账结果API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java index 182e72e..fd5420c 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/QueryReturnOrderParams.java @@ -3,7 +3,7 @@ package cn.felord.payment.wechat.v3.model.profitsharing; import lombok.Data; /** - * 查询分账回退结果API-请求参数 + * 直连商户-查询分账回退结果API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java index 981c3f2..23e375b 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/ReturnOrdersParams.java @@ -3,7 +3,7 @@ package cn.felord.payment.wechat.v3.model.profitsharing; import lombok.Data; /** - * 请求分账回退API-请求参数 + * 直连商户-请求分账回退API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java index a53f789..a2d3ed2 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/profitsharing/UnfreezeParams.java @@ -3,7 +3,7 @@ package cn.felord.payment.wechat.v3.model.profitsharing; import lombok.Data; /** - * 解冻剩余资金API-请求参数 + * 直连商户-解冻剩余资金API-请求参数 * * @author felord.cn * @since 1.0.11.RELEASE From 53a34e6a7105c499203eecff30ca2d262f16a44e Mon Sep 17 00:00:00 2001 From: felord Date: Tue, 1 Jun 2021 16:00:02 +0800 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=EF=BC=88=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=9B=B4=E6=96=B0=EF=BC=89=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=8A=E7=94=B3=E8=AF=B7=E9=80=80=E6=AC=BE?= =?UTF-8?q?API=E3=80=8B=E3=80=81=E3=80=8A=E6=9F=A5=E8=AF=A2=E9=80=80?= =?UTF-8?q?=E6=AC=BEAPI=E3=80=8B=E6=96=B0=E5=A2=9E=E5=AD=97=E6=AE=B5=20fro?= =?UTF-8?q?m=EF=BC=88=E9=80=80=E6=AC=BE=E5=87=BA=E8=B5=84=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E5=8F=8A=E9=87=91=E9=A2=9D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/wechat/v3/model/RefundParams.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/RefundParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/RefundParams.java index 5adb83d..25e31d1 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/RefundParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/RefundParams.java @@ -75,6 +75,12 @@ public class RefundParams { */ @Data public static class RefundAmount { + /** + * 退款出资账户及金额 + * + * @since 1.0.11.RELEASE + */ + private List form; /** * 原订单金额,币种的最小单位,只能为整数,不能超过原订单支付金额。 */ @@ -89,4 +95,43 @@ public class RefundParams { private Integer refund; } + /** + * 退款出资账户及金额 + *

+ * 退款需要从指定账户出资时,传递此参数指定出资金额(币种的最小单位,只能为整数)。 + *

+ * 同时指定多个账户出资退款的使用场景需要满足以下条件: + *

+ *

    + *
  1. 未开通退款支出分离产品功能;
  2. + *
  3. 订单属于分账订单,且分账处于待分账或分账中状态。
  4. + *
+ *

+ * 参数传递需要满足条件: + *

+ *

    + *
  1. 基本账户可用余额出资金额与基本账户不可用余额出资金额之和等于退款金额;
  2. + *
  3. 账户类型不能重复。
  4. + *
+ *

+ * 上述任一条件不满足将返回错误 + * + * @author felord.cn + * @since 1.0.11.RELEASE + */ + @Data + public static class RefundForm { + /** + * 出资账户类型 + *

+ * {@code AVAILABLE} 可用余额 + * {@code UNAVAILABLE} 不可用余额 + */ + private String account; + /** + * 对应账户出资金额 + */ + private String amount; + + } } \ No newline at end of file From 60fe80e615583680c88600bddcfa342f3d8cd5eb Mon Sep 17 00:00:00 2001 From: felord Date: Sat, 5 Jun 2021 13:28:37 +0800 Subject: [PATCH 13/13] refactor: Update pathParams --- .../wechat/v3/WechatBatchTransferApi.java | 16 ++++++++-------- .../payment/wechat/v3/WechatPayScoreApi.java | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java index 5f4ef78..32da85d 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatBatchTransferApi.java @@ -140,13 +140,13 @@ public class WechatBatchTransferApi extends AbstractApi { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_WECHAT, queryBatchTransferDetailParams) .function((type, params) -> { - Map queryParams = new HashMap<>(2); - queryParams.put("batch_id", params.getBatchIdOrOutBatchNo()); - queryParams.put("detail_id", params.getDetailIdOrOutDetailNo()); + Map pathParams = new HashMap<>(2); + pathParams.put("batch_id", params.getBatchIdOrOutBatchNo()); + pathParams.put("detail_id", params.getDetailIdOrOutDetailNo()); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) .build() - .expand(queryParams) + .expand(pathParams) .toUri(); return Get(uri); }) @@ -195,13 +195,13 @@ public class WechatBatchTransferApi extends AbstractApi { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_MCH, queryBatchTransferDetailParams) .function((type, params) -> { - Map queryParams = new HashMap<>(2); - queryParams.put("batch_id", params.getBatchIdOrOutBatchNo()); - queryParams.put("detail_id", params.getDetailIdOrOutDetailNo()); + Map pathParams = new HashMap<>(2); + pathParams.put("batch_id", params.getBatchIdOrOutBatchNo()); + pathParams.put("detail_id", params.getDetailIdOrOutDetailNo()); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) .build() - .expand(queryParams) + .expand(pathParams) .toUri(); return Get(uri); }) diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java index aef65eb..8113088 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayScoreApi.java @@ -65,14 +65,14 @@ public class WechatPayScoreApi extends AbstractApi { .function((wechatPayV3Type, userServiceStateParams) -> { WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3(); - Map expandParams = new HashMap<>(3); - expandParams.put("appid", v3.getAppId()); - expandParams.put("service_id", params.getServiceId()); - expandParams.put("openid", params.getOpenId()); + Map pathParams = new HashMap<>(3); + pathParams.put("appid", v3.getAppId()); + pathParams.put("service_id", params.getServiceId()); + pathParams.put("openid", params.getOpenId()); URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA)) .build() - .expand(expandParams) + .expand(pathParams) .toUri(); return Get(uri); })