mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-14 05:43:46 +08:00
Compare commits
29 Commits
1.0.6.RELE
...
1.0.9.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54a0822c73 | ||
|
|
8d130e5df1 | ||
|
|
e0eacd523e | ||
|
|
d1068c1fb6 | ||
|
|
82bcc11dad | ||
|
|
911f986d80 | ||
|
|
b913698884 | ||
|
|
34e5443e88 | ||
|
|
f4dc70e828 | ||
|
|
39716c30e4 | ||
|
|
ab13d90b9b | ||
|
|
4f71f419a7 | ||
|
|
ec77604b99 | ||
|
|
ff5911205b | ||
|
|
22d83edb89 | ||
|
|
64d8294758 | ||
|
|
cecee81b08 | ||
|
|
6276b24c9f | ||
|
|
282a60e3bb | ||
|
|
2e143302e8 | ||
|
|
9fba48d52d | ||
|
|
2489edbd6c | ||
|
|
e3b386e4c6 | ||
|
|
095fff5c69 | ||
|
|
467f4d74ae | ||
|
|
ff1e0fd473 | ||
|
|
dd4a8ed565 | ||
|
|
ffd18a7496 | ||
|
|
92104e67ec |
33
.github/workflows/main.yml
vendored
Normal file
33
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Maven Central Repo Deployment
|
||||
# 当 release 的时候触发
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Maven Central Repo
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
server-id: sonatype-nexus-staging
|
||||
server-username: MAVEN_USERNAME
|
||||
server-password: MAVEN_PASSWORD
|
||||
- id: install-secret-key
|
||||
name: Install GPG Secret Key
|
||||
run: |
|
||||
cat <(echo -e "${{ secrets.GPG_PUB }}") | gpg --batch --import
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
- id: publish-to-central
|
||||
name: Publish to Maven Central Repo
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
run: |
|
||||
mvn \
|
||||
--no-transfer-progress \
|
||||
--batch-mode \
|
||||
-Dgpg.passphrase=${{ secrets.GPG_PASSWORD }} \
|
||||
clean deploy
|
||||
@@ -11,7 +11,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
## 采用技术
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
## 1.0.8.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: 对基础支付-服务商支付进行支持
|
||||
- refactor: 在异常返回时对非2xx状态返回的元信息进行包装方便序列化([#16](https://github.com/NotFound403/payment-spring-boot/issues/16))
|
||||
- fix: 修复退款数据中时间无法解析的异常([#13](https://github.com/NotFound403/payment-spring-boot/issues/13))
|
||||
- fix: 修复类成员的属性([#14](https://github.com/NotFound403/payment-spring-boot/issues/14))
|
||||
- fix: 查询并下载转账电子回单API接口,下载文件接口签名失败([#18](https://github.com/NotFound403/payment-spring-boot/issues/18))
|
||||
|
||||
## 1.0.7.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- refactor: X509证书加载优化。
|
||||
- refactor: 移除过期的`WechatPayRefundApi`。
|
||||
- refactor: 优化RestTemplate在低版本引起的一个I/O异常,详见 [spring-framework#21321](https://github.com/spring-projects/spring-framework/issues/21321)。
|
||||
- refactor: 在请求头Content-Type中声明字符集UTF-8,避免中文乱码。
|
||||
- fix: 修复退款回调中退款状态枚举无法正确被解析的异常([#11](https://github.com/NotFound403/payment-spring-boot/issues/11))。
|
||||
|
||||
## 1.0.6.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat:实现微信支付V3批量转账到零钱所有API(WechatBatchTransferApi),助力抗击新冠疫情。
|
||||
- feat:实现微信支付V3退款以及退款通知等所有退款相关的API,推荐使用新的V3退款。
|
||||
- refactor: V2退款进入过期模式,由于V3已经推出了退款功能,所以V2退款 WechatPayRefundApi 被标记为 Deprecated 未来会被移除。
|
||||
- refactor: 交易状态增加等待扣款状态,根据微信最新的业务变动增加 “ACCEPT” 字段用来标记“已接收,等待扣款”状态。
|
||||
|
||||
## 1.0.5.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat:增加V2退款接口
|
||||
- feat:增加V2企业付款到零钱接口
|
||||
@@ -15,6 +36,7 @@
|
||||
- fix: 关单接口调用异常
|
||||
|
||||
## 1.0.4.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: 增加微信支付商家券相关接口`WechatMarketingBusiFavorApi`,商家券请阅读相关产品文档。
|
||||
- feat: 代金券功能增加发放消费卡接口。
|
||||
@@ -24,6 +46,7 @@
|
||||
- fix: 支付分`RiskFund`下枚举无法使用的问题[(#2)](https://github.com/NotFound403/payment-spring-boot/issues/2)。
|
||||
|
||||
## 1.0.3.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: 完善合单支付账单
|
||||
1. 增加合单支付-申请交易账单API。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
> 基于 **Spring Boot 2.x**
|
||||
@@ -48,13 +48,13 @@ wechat:
|
||||
v3:
|
||||
# 租户id
|
||||
<tentantID>:
|
||||
# 应用appId 必填
|
||||
# 应用appId 服务商模式下为服务商的appid 必填
|
||||
app-id: xxxxxxxx
|
||||
# v2 api 密钥 1.0.5版本以后如果用到V2的接口时必填
|
||||
app-secret: xxxxxxxxxxx
|
||||
# api v3 密钥 必填
|
||||
app-v3-secret: xxxxxxxx
|
||||
# 微信支付商户号 必填
|
||||
# 微信支付商户号 服务商模式下为服务商的mchid 必填
|
||||
mch-id: xxxxxxx
|
||||
# 商户服务器域名 用于回调 需要放开回调接口的安全策略 必填
|
||||
domain: https://felord.cn
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>payment-spring-boot-autoconfigure</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
<packaging>jar</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
*/
|
||||
package cn.felord.payment;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* @author felord.cn
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
public class PayException extends RuntimeException {
|
||||
/**
|
||||
* response maybe null
|
||||
*/
|
||||
private ResponseEntity<?> response;
|
||||
|
||||
public PayException() {
|
||||
}
|
||||
@@ -42,4 +48,12 @@ public class PayException extends RuntimeException {
|
||||
public PayException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public ResponseEntity<?> getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(ResponseEntity<?> response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,46 +18,30 @@
|
||||
package cn.felord.payment.wechat.enumeration;
|
||||
|
||||
/**
|
||||
* 退款成功事件.
|
||||
* 退款状态.
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.6.RELEASE
|
||||
* @since 1.0.7.RELEASE
|
||||
*/
|
||||
public enum RefundStatus {
|
||||
/**
|
||||
* 退款异常事件.
|
||||
* 退款异常.
|
||||
*
|
||||
* @since 1.0.6.RELEASE
|
||||
* @since 1.0.7.RELEASE
|
||||
*/
|
||||
ABNORMAL("REFUND.ABNORMAL"),
|
||||
ABNORMAL,
|
||||
|
||||
/**
|
||||
* 退款关闭事件.
|
||||
* 退款关闭.
|
||||
*
|
||||
* @since 1.0.6.RELEASE
|
||||
* @since 1.0.7.RELEASE
|
||||
*/
|
||||
CLOSED("REFUND.CLOSED"),
|
||||
CLOSED,
|
||||
/**
|
||||
* 支付成功事件.
|
||||
* 退款成功.
|
||||
*
|
||||
* @since 1.0.0.RELEASE
|
||||
* @since 1.0.7.RELEASE
|
||||
*/
|
||||
TRANSACTION("TRANSACTION.SUCCESS");
|
||||
/**
|
||||
* The Event.
|
||||
*/
|
||||
private final String refundStatus;
|
||||
SUCCESS
|
||||
|
||||
/**
|
||||
* Instantiates a new Event type.
|
||||
*
|
||||
* @param refundStatus the event
|
||||
*/
|
||||
RefundStatus(String refundStatus) {
|
||||
this.refundStatus = refundStatus;
|
||||
}
|
||||
|
||||
public String getRefundStatus() {
|
||||
return refundStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +486,57 @@ public enum WechatPayV3Type {
|
||||
*
|
||||
* @since 1.0.6.RELEASES
|
||||
*/
|
||||
BATCH_TRANSFER_DOWNLOAD_BILL(HttpMethod.GET, "%s/v3/transfer/bill-receipt/{out_batch_no}");
|
||||
BATCH_TRANSFER_DOWNLOAD_BILL(HttpMethod.GET, "%s/v3/transfer/bill-receipt/{out_batch_no}"),
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
* 服务商APP下单API.
|
||||
*
|
||||
* @since 1.0.8.RELEASES
|
||||
*/
|
||||
APP_PARTNER(HttpMethod.POST, "%s/v3/pay/partner/transactions/app"),
|
||||
|
||||
/**
|
||||
* 微信公众号支付或者小程序支付.
|
||||
*
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
JSAPI_PARTNER(HttpMethod.POST, "%s/v3/pay/partner/transactions/jsapi"),
|
||||
|
||||
/**
|
||||
* 微信扫码支付.
|
||||
*
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
NATIVE_PARTNER(HttpMethod.POST, "%s/v3/pay/partner/transactions/native"),
|
||||
|
||||
/**
|
||||
* H5支付.
|
||||
*
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
MWEB_PARTNER(HttpMethod.POST, "%s/v3/pay/partner/transactions/h5"),
|
||||
|
||||
/**
|
||||
* 关闭订单.
|
||||
*
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
CLOSE_PARTNER(HttpMethod.POST, "%s/v3/pay/partner/transactions/out-trade-no/{out_trade_no}/close"),
|
||||
/**
|
||||
* 微信支付订单号查询API.
|
||||
*
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
TRANSACTION_TRANSACTION_ID_PARTNER(HttpMethod.GET, "%s/v3/pay/partner/transactions/id/{transaction_id}"),
|
||||
/**
|
||||
* 商户订单号查询API.
|
||||
*
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
TRANSACTION_OUT_TRADE_NO_PARTNER(HttpMethod.GET, "%s/v3/pay/partner/transactions/out-trade-no/{out_trade_no}"),
|
||||
;
|
||||
/**
|
||||
* The Pattern.
|
||||
*
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.v2;
|
||||
|
||||
import cn.felord.payment.wechat.WechatPayProperties;
|
||||
import cn.felord.payment.wechat.v2.model.RefundModel;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* 退款相关API.
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.5.RELEASE
|
||||
*/
|
||||
@Deprecated
|
||||
public class WechatPayRefundApi {
|
||||
private final WechatV2Client wechatV2Client;
|
||||
|
||||
/**
|
||||
* Instantiates a new Wechat pay refund api.
|
||||
*
|
||||
* @param wechatV2Client the wechat v 2 client
|
||||
*/
|
||||
public WechatPayRefundApi(WechatV2Client wechatV2Client) {
|
||||
this.wechatV2Client = wechatV2Client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*
|
||||
* @param refundModel the refund model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode transfer(RefundModel refundModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
refundModel.setAppid(v3.getAppId());
|
||||
refundModel.setMchId(v3.getMchId());
|
||||
return wechatV2Client.wechatPayRequest(refundModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/secapi/pay/refund");
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -155,6 +156,25 @@ public abstract class AbstractApi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Post请求对象.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param params the params
|
||||
* @param httpHeaders the http headers
|
||||
* @return request entity
|
||||
*/
|
||||
protected RequestEntity<?> Post(URI uri, Object params, HttpHeaders httpHeaders) {
|
||||
try {
|
||||
return RequestEntity.post(uri)
|
||||
.header("Pay-TenantId", tenantId)
|
||||
.headers(httpHeaders)
|
||||
.body(mapper.writeValueAsString(params));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new PayException("wechat app pay json failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Get请求对象.
|
||||
*
|
||||
@@ -165,7 +185,19 @@ public abstract class AbstractApi {
|
||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Get请求对象.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param httpHeaders the http headers
|
||||
* @return the request entity
|
||||
*/
|
||||
protected RequestEntity<?> Get(URI uri, HttpHeaders httpHeaders) {
|
||||
return RequestEntity.get(uri)
|
||||
.header("Pay-TenantId", tenantId)
|
||||
.headers(httpHeaders)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对账单内容下载,非流文件。
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import sun.security.x509.X509CertImpl;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
@@ -46,11 +45,16 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -116,6 +120,7 @@ public class SignatureProvider {
|
||||
/**
|
||||
* 我方请求前用 SHA256withRSA 加签,使用API证书.
|
||||
*
|
||||
* @param newLine the new line
|
||||
* @param tenantId the properties key
|
||||
* @param method the method
|
||||
* @param canonicalUrl the canonical url
|
||||
@@ -123,7 +128,7 @@ public class SignatureProvider {
|
||||
* @return the string
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String requestSign(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()
|
||||
@@ -131,7 +136,7 @@ public class SignatureProvider {
|
||||
.replaceAll("-", "");
|
||||
WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId);
|
||||
PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate();
|
||||
String encode = this.doRequestSign(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
|
||||
@@ -145,16 +150,17 @@ 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(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(orderedComponents);
|
||||
final String signatureStr = createSign(newLine,orderedComponents);
|
||||
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64Utils.encodeToString(signer.sign());
|
||||
}
|
||||
@@ -174,7 +180,7 @@ public class SignatureProvider {
|
||||
}
|
||||
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);
|
||||
|
||||
final String signatureStr = createSign(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));
|
||||
@@ -202,7 +208,7 @@ public class SignatureProvider {
|
||||
}
|
||||
// 签名
|
||||
HttpMethod httpMethod = WechatPayV3Type.CERT.method();
|
||||
String authorization = requestSign(tenantId, httpMethod.name(), canonicalUrl, "");
|
||||
String authorization = requestSign(true,tenantId, httpMethod.name(), canonicalUrl, "");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
@@ -219,7 +225,7 @@ public class SignatureProvider {
|
||||
ArrayNode certificates = bodyObjectNode.withArray("data");
|
||||
if (certificates.isArray() && certificates.size() > 0) {
|
||||
CERTIFICATE_MAP.clear();
|
||||
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
|
||||
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509",BC_PROVIDER);
|
||||
certificates.forEach(objectNode -> {
|
||||
JsonNode encryptCertificate = objectNode.get("encrypt_certificate");
|
||||
String associatedData = encryptCertificate.get("associated_data").asText();
|
||||
@@ -274,7 +280,8 @@ public class SignatureProvider {
|
||||
/**
|
||||
* 对请求敏感字段进行加密
|
||||
*
|
||||
* @param message the message
|
||||
* @param message the message
|
||||
* @param certificate the certificate
|
||||
* @return encrypt message
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
@@ -284,20 +291,28 @@ public class SignatureProvider {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
|
||||
|
||||
byte[] data = message.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] cipherdata = cipher.doFinal(data);
|
||||
return Base64Utils.encodeToString(cipherdata);
|
||||
byte[] cipherData = cipher.doFinal(data);
|
||||
return Base64Utils.encodeToString(cipherData);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PayException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public X509CertImpl getCertificate(){
|
||||
/**
|
||||
* Get certificate x 509 wechat certificate info.
|
||||
*
|
||||
* @return the x 509 wechat certificate info
|
||||
*/
|
||||
public X509WechatCertificateInfo getCertificate(){
|
||||
for (String serial : CERTIFICATE_MAP.keySet()) {
|
||||
X509CertImpl x509Cert = (X509CertImpl) CERTIFICATE_MAP.get(serial);
|
||||
X509Certificate x509Cert = (X509Certificate) CERTIFICATE_MAP.get(serial);
|
||||
try {
|
||||
x509Cert.checkValidity();
|
||||
return x509Cert;
|
||||
X509WechatCertificateInfo x509WechatCertificateInfo = new X509WechatCertificateInfo();
|
||||
x509WechatCertificateInfo.setWechatPaySerial(serial);
|
||||
x509WechatCertificateInfo.setX509Certificate(x509Cert);
|
||||
return x509WechatCertificateInfo;
|
||||
} catch (Exception e) {
|
||||
log.warn("the wechat certificate is invalid , {}", e.getMessage());
|
||||
// Async?
|
||||
@@ -333,9 +348,11 @@ public class SignatureProvider {
|
||||
* @param components the components
|
||||
* @return string string
|
||||
*/
|
||||
private static String createSign(String... components) {
|
||||
private static String createSign(boolean newLine,String... components) {
|
||||
|
||||
String suffix = newLine? "\n":"";
|
||||
return Arrays.stream(components)
|
||||
.collect(Collectors.joining("\n", "", "\n"));
|
||||
.collect(Collectors.joining("\n", "", suffix));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package cn.felord.payment.wechat.v3;
|
||||
|
||||
import cn.felord.payment.wechat.v2.WechatPayRedpackApi;
|
||||
import cn.felord.payment.wechat.v2.WechatPayRefundApi;
|
||||
import cn.felord.payment.wechat.v2.WechatPayTransfersApi;
|
||||
import cn.felord.payment.wechat.v2.WechatV2Client;
|
||||
|
||||
@@ -66,6 +65,18 @@ public class WechatApiProvider {
|
||||
return new WechatDirectPayApi(wechatPayClient, tenantId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 普通支付-服务商模式.
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return the wechat pay api
|
||||
* @since 1.0.9.RELEASE
|
||||
*/
|
||||
public WechatPartnerPayApi partnerPayApi(String tenantId) {
|
||||
return new WechatPartnerPayApi(wechatPayClient, tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合单支付.
|
||||
*
|
||||
@@ -136,22 +147,6 @@ public class WechatApiProvider {
|
||||
return new WechatPayCallback(wechatPayClient.signatureProvider(), tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款,基于V2
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return the wechat pay refund api
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
@Deprecated
|
||||
public WechatPayRefundApi refund(String tenantId) {
|
||||
WechatMetaBean wechatMeta = wechatPayClient.signatureProvider()
|
||||
.wechatMetaContainer()
|
||||
.getWechatMeta(tenantId);
|
||||
WechatV2Client wechatV2Client = new WechatV2Client(wechatMeta);
|
||||
return new WechatPayRefundApi(wechatV2Client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 现金红包,基于V2
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@ import cn.felord.payment.wechat.v3.model.batchtransfer.QueryBatchTransferDetailP
|
||||
import cn.felord.payment.wechat.v3.model.batchtransfer.QueryBatchTransferParams;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -31,9 +32,9 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import sun.security.x509.X509CertImpl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -76,15 +77,16 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
List<CreateBatchTransferParams.TransferDetailListItem> transferDetailList = createBatchTransferParams.getTransferDetailList();
|
||||
|
||||
SignatureProvider signatureProvider = this.client().signatureProvider();
|
||||
final X509CertImpl certificate = signatureProvider.getCertificate();
|
||||
final X509WechatCertificateInfo certificate = signatureProvider.getCertificate();
|
||||
List<CreateBatchTransferParams.TransferDetailListItem> encrypted = transferDetailList.stream()
|
||||
.peek(transferDetailListItem -> {
|
||||
String userName = transferDetailListItem.getUserName();
|
||||
String encryptedUserName = signatureProvider.encryptRequestMessage(userName, certificate);
|
||||
X509Certificate x509Certificate = certificate.getX509Certificate();
|
||||
String encryptedUserName = signatureProvider.encryptRequestMessage(userName, x509Certificate);
|
||||
transferDetailListItem.setUserName(encryptedUserName);
|
||||
String userIdCard = transferDetailListItem.getUserIdCard();
|
||||
if (StringUtils.hasText(userIdCard)) {
|
||||
String encryptedUserIdCard = signatureProvider.encryptRequestMessage(userIdCard, certificate);
|
||||
String encryptedUserIdCard = signatureProvider.encryptRequestMessage(userIdCard, x509Certificate);
|
||||
transferDetailListItem.setUserIdCard(encryptedUserIdCard);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
@@ -93,7 +95,9 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.toUri();
|
||||
return Post(uri, createBatchTransferParams);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Wechatpay-Serial", certificate.getWechatPaySerial());
|
||||
return Post(uri, createBatchTransferParams, httpHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +253,7 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
String downloadUrl = wechatResponseEntity.getBody().get("download_url").asText();
|
||||
Assert.hasText(downloadUrl,"download url has no text");
|
||||
Assert.hasText(downloadUrl, "download url has no text");
|
||||
return this.billResource(downloadUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class WechatDirectPayApi extends AbstractApi {
|
||||
.toString()
|
||||
.replaceAll("-", "");
|
||||
String prepayId = body.get("prepay_id").asText();
|
||||
String paySign = signatureProvider.doRequestSign(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(privateKey, appId, timestamp, nonceStr, packageStr);
|
||||
String paySign = signatureProvider.doRequestSign(true,privateKey, appId, timestamp, nonceStr, packageStr);
|
||||
|
||||
body.put("appId", appId);
|
||||
body.put("timeStamp", timestamp);
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 微信支付商家券.
|
||||
@@ -146,7 +147,10 @@ public class WechatMarketingBusiFavorApi extends AbstractApi {
|
||||
queryParams.add("appid", appid);
|
||||
}
|
||||
queryParams.add("stock_id", userBusiFavorQueryParams.getStockId());
|
||||
queryParams.add("coupon_state", userBusiFavorQueryParams.getCouponState().name());
|
||||
|
||||
Optional.ofNullable(userBusiFavorQueryParams.getCouponState())
|
||||
.ifPresent(state-> queryParams.add("coupon_state", state.name()));
|
||||
|
||||
queryParams.add("creator_merchant", userBusiFavorQueryParams.getCreatorMerchant());
|
||||
queryParams.add("belong_merchant", userBusiFavorQueryParams.getBelongMerchant());
|
||||
queryParams.add("sender_merchant", userBusiFavorQueryParams.getSenderMerchant());
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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;
|
||||
|
||||
import cn.felord.payment.PayException;
|
||||
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.TransactionQueryParams;
|
||||
import cn.felord.payment.wechat.v3.model.partner.CloseTransParams;
|
||||
import cn.felord.payment.wechat.v3.model.partner.PartnerPayParams;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.PrivateKey;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 普通支付-服务商模式
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public class WechatPartnerPayApi extends AbstractApi {
|
||||
/**
|
||||
* Instantiates a new Abstract api.
|
||||
*
|
||||
* @param wechatPayClient the wechat pay client
|
||||
* @param tenantId the tenant id
|
||||
*/
|
||||
public WechatPartnerPayApi(WechatPayClient wechatPayClient, String tenantId) {
|
||||
super(wechatPayClient, tenantId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* APP下单API
|
||||
*
|
||||
* @param partnerPayParams the partner pay params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> appPay(PartnerPayParams partnerPayParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.APP_PARTNER, partnerPayParams)
|
||||
.function(this::payFunction)
|
||||
.consumer(responseEntity -> {
|
||||
ObjectNode body = responseEntity.getBody();
|
||||
if (Objects.isNull(body)) {
|
||||
throw new PayException("response body cannot be resolved");
|
||||
}
|
||||
|
||||
SignatureProvider signatureProvider = this.client().signatureProvider();
|
||||
WechatMetaContainer wechatMetaContainer = signatureProvider.wechatMetaContainer();
|
||||
WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId());
|
||||
PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate();
|
||||
|
||||
String subAppid = partnerPayParams.getSubAppid();
|
||||
long epochSecond = LocalDateTime.now()
|
||||
.toEpochSecond(ZoneOffset.of("+8"));
|
||||
String timestamp = String.valueOf(epochSecond);
|
||||
String nonceStr = signatureProvider.nonceStrGenerator()
|
||||
.generateId()
|
||||
.toString()
|
||||
.replaceAll("-", "");
|
||||
String prepayId = body.get("prepay_id").asText();
|
||||
String paySign = signatureProvider.doRequestSign(true, privateKey, subAppid, timestamp, nonceStr, prepayId);
|
||||
|
||||
body.put("appid", subAppid);
|
||||
body.put("partnerid", partnerPayParams.getSubMchid());
|
||||
body.put("prepayid", prepayId);
|
||||
body.put("package", "Sign=WXPay");
|
||||
body.put("nonceStr", nonceStr);
|
||||
body.put("timeStamp", timestamp);
|
||||
body.put("signType", "RSA");
|
||||
body.put("paySign", paySign);
|
||||
|
||||
wechatResponseEntity.setHttpStatus(responseEntity.getStatusCodeValue());
|
||||
wechatResponseEntity.setBody(body);
|
||||
})
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSAPI/小程序下单API
|
||||
*
|
||||
* @param partnerPayParams the pay params
|
||||
* @return wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> jsPay(PartnerPayParams partnerPayParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.JSAPI_PARTNER, partnerPayParams)
|
||||
.function(this::payFunction)
|
||||
.consumer(responseEntity -> {
|
||||
ObjectNode body = responseEntity.getBody();
|
||||
if (Objects.isNull(body)) {
|
||||
throw new PayException("response body cannot be resolved");
|
||||
}
|
||||
|
||||
SignatureProvider signatureProvider = this.client().signatureProvider();
|
||||
WechatMetaContainer wechatMetaContainer = signatureProvider.wechatMetaContainer();
|
||||
WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId());
|
||||
PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate();
|
||||
|
||||
String subAppid = partnerPayParams.getSubAppid();
|
||||
long epochSecond = LocalDateTime.now()
|
||||
.toEpochSecond(ZoneOffset.of("+8"));
|
||||
String timestamp = String.valueOf(epochSecond);
|
||||
String nonceStr = signatureProvider.nonceStrGenerator()
|
||||
.generateId()
|
||||
.toString()
|
||||
.replaceAll("-", "");
|
||||
String packageStr = "prepay_id=" + body.get("prepay_id").asText();
|
||||
String paySign = signatureProvider.doRequestSign(true, privateKey, subAppid, timestamp, nonceStr, packageStr);
|
||||
|
||||
body.put("appId", subAppid);
|
||||
body.put("timeStamp", timestamp);
|
||||
body.put("nonceStr", nonceStr);
|
||||
body.put("package", packageStr);
|
||||
body.put("signType", "RSA");
|
||||
body.put("paySign", paySign);
|
||||
|
||||
wechatResponseEntity.setHttpStatus(responseEntity.getStatusCodeValue());
|
||||
wechatResponseEntity.setBody(body);
|
||||
})
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Native下单API
|
||||
*
|
||||
* @param partnerPayParams the pay params
|
||||
* @return wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> nativePay(PartnerPayParams partnerPayParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.NATIVE_PARTNER, partnerPayParams)
|
||||
.function(this::payFunction)
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* H5下单API
|
||||
*
|
||||
* @param partnerPayParams the partner pay params
|
||||
* @return wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> h5Pay(PartnerPayParams partnerPayParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.MWEB_PARTNER, partnerPayParams)
|
||||
.function(this::payFunction)
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
private RequestEntity<?> payFunction(WechatPayV3Type type, PartnerPayParams partnerPayParams) {
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
partnerPayParams.setSpAppid(v3.getAppId());
|
||||
partnerPayParams.setSpMchid(v3.getMchId());
|
||||
String notifyUrl = v3.getDomain().concat(partnerPayParams.getNotifyUrl());
|
||||
partnerPayParams.setNotifyUrl(notifyUrl);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.toUri();
|
||||
return Post(uri, partnerPayParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付订单号查询API
|
||||
*
|
||||
* @param params the params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryTransactionById(TransactionQueryParams params) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.TRANSACTION_TRANSACTION_ID_PARTNER, params)
|
||||
.function(this::queryTransactionFunction)
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 商户订单号查询API
|
||||
*
|
||||
* @param params the params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryTransactionByOutTradeNo(TransactionQueryParams params) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.TRANSACTION_OUT_TRADE_NO_PARTNER, params)
|
||||
.function(this::queryTransactionFunction)
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
private RequestEntity<?> queryTransactionFunction(WechatPayV3Type type, TransactionQueryParams params) {
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("sp_mchid", v3.getMchId());
|
||||
queryParams.add("sub_mchid", params.getMchId());
|
||||
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.queryParams(queryParams)
|
||||
.build()
|
||||
.expand(params.getTransactionIdOrOutTradeNo())
|
||||
.toUri();
|
||||
return Get(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关单API
|
||||
*
|
||||
* @param closeTransParams the closeTransParams
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> close(CloseTransParams closeTransParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.CLOSE_PARTNER, closeTransParams)
|
||||
.function(this::closeByOutTradeNoFunction)
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
return wechatResponseEntity;
|
||||
}
|
||||
|
||||
private RequestEntity<?> closeByOutTradeNoFunction(WechatPayV3Type type, CloseTransParams closeTransParams) {
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
Map<String, String> queryParams = new HashMap<>(1);
|
||||
queryParams.put("sp_mchid", v3.getMchId());
|
||||
queryParams.put("sub_mchid", closeTransParams.getSubMchid());
|
||||
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.expand(closeTransParams.getOutTradeNo())
|
||||
.toUri();
|
||||
return Post(uri, queryParams);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,8 +31,11 @@ import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPaidConsumeData;
|
||||
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPermissionConsumeData;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
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.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -71,6 +74,9 @@ public class WechatPayCallback {
|
||||
static {
|
||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
|
||||
SimpleModule module = new JavaTimeModule();
|
||||
MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +113,7 @@ public class WechatPayCallback {
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付成功回调.
|
||||
* 微信支付成功回调,在1.0.8.RELEASE时支持服务商模式支付回调通知
|
||||
* <p>
|
||||
* 无需开发者判断,只有扣款成功微信才会回调此接口
|
||||
*
|
||||
|
||||
@@ -26,6 +26,7 @@ import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -163,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(wechatRequestEntity));
|
||||
this.doExecute(this.header(true,wechatRequestEntity));
|
||||
}
|
||||
|
||||
|
||||
@@ -175,18 +176,19 @@ 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(wechatRequestEntity));
|
||||
return this.doDownload(this.header(true,wechatRequestEntity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download string.
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
public ResponseEntity<Resource> resource() {
|
||||
public ResponseEntity<Resource> resource() {
|
||||
RequestEntity<?> requestEntity = this.requestEntityBiFunction.apply(this.wechatPayV3Type, this.model);
|
||||
WechatRequestEntity<?> wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer);
|
||||
return this.doResource(this.header(wechatRequestEntity));
|
||||
return this.doResource(this.header(false,wechatRequestEntity));
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +199,7 @@ public class WechatPayClient {
|
||||
* @param requestEntity the request entity
|
||||
* @return the wechat request entity
|
||||
*/
|
||||
private <T> WechatRequestEntity<T> header(WechatRequestEntity<T> requestEntity) {
|
||||
private <T> WechatRequestEntity<T> header(boolean newLine,WechatRequestEntity<T> requestEntity) {
|
||||
|
||||
UriComponents uri = UriComponentsBuilder.fromUri(requestEntity.getUrl()).build();
|
||||
String canonicalUrl = uri.getPath();
|
||||
@@ -218,14 +220,15 @@ public class WechatPayClient {
|
||||
}
|
||||
|
||||
String tenantId = Objects.requireNonNull(headers.get("Pay-TenantId")).get(0);
|
||||
String authorization = signatureProvider.requestSign(tenantId, httpMethod.name(), canonicalUrl, body);
|
||||
String authorization = signatureProvider.requestSign(newLine,tenantId, httpMethod.name(), canonicalUrl, body);
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.addAll(headers);
|
||||
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
// for upload
|
||||
if (Objects.isNull(httpHeaders.getContentType())) {
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
// 避免出现因为中文导致的 HttpRetryException
|
||||
httpHeaders.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
|
||||
}
|
||||
httpHeaders.add("Authorization", authorization);
|
||||
httpHeaders.add("User-Agent", "X-Pay-Service");
|
||||
@@ -251,7 +254,9 @@ public class WechatPayClient {
|
||||
// 微信请求id
|
||||
String requestId = headers.getFirst("Request-ID");
|
||||
if (!statusCode.is2xxSuccessful()) {
|
||||
throw new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + body);
|
||||
PayException payException = new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + body);
|
||||
payException.setResponse(responseEntity);
|
||||
throw payException;
|
||||
}
|
||||
|
||||
ResponseSignVerifyParams params = new ResponseSignVerifyParams();
|
||||
@@ -274,7 +279,9 @@ public class WechatPayClient {
|
||||
responseConsumer.accept(responseEntity);
|
||||
}
|
||||
} else {
|
||||
throw new PayException("wechat pay signature verify failed, Request-ID " + requestId);
|
||||
PayException payException = new PayException("wechat pay signature verify failed, Request-ID " + requestId);
|
||||
payException.setResponse(responseEntity);
|
||||
throw payException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +300,9 @@ public class WechatPayClient {
|
||||
// 微信请求id
|
||||
String requestId = requestEntity.getHeaders().getFirst("Request-ID");
|
||||
if (!statusCode.is2xxSuccessful()) {
|
||||
throw new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + responseEntity);
|
||||
PayException payException = new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + responseEntity);
|
||||
payException.setResponse(responseEntity);
|
||||
throw payException;
|
||||
}
|
||||
return Optional.ofNullable(responseEntity.getBody()).orElse("");
|
||||
}
|
||||
@@ -307,7 +316,7 @@ public class WechatPayClient {
|
||||
* @return the resource
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
private <T> ResponseEntity<Resource> doResource(WechatRequestEntity<T> requestEntity) {
|
||||
private <T> ResponseEntity<Resource> doResource(WechatRequestEntity<T> requestEntity) {
|
||||
|
||||
ResponseEntity<Resource> responseEntity = restOperations.exchange(requestEntity, Resource.class);
|
||||
|
||||
@@ -315,7 +324,9 @@ public class WechatPayClient {
|
||||
// 微信请求id
|
||||
String requestId = requestEntity.getHeaders().getFirst("Request-ID");
|
||||
if (!statusCode.is2xxSuccessful()) {
|
||||
throw new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + responseEntity);
|
||||
PayException payException = new PayException("wechat pay server error, Request-ID " + requestId + " , statusCode " + statusCode + ",result : " + responseEntity);
|
||||
payException.setResponse(responseEntity);
|
||||
throw payException;
|
||||
}
|
||||
return responseEntity;
|
||||
}
|
||||
@@ -336,6 +347,7 @@ public class WechatPayClient {
|
||||
*/
|
||||
private void applyDefaultRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
|
||||
DefaultResponseErrorHandler errorHandler = new WechatPayResponseErrorHandler();
|
||||
restTemplate.setErrorHandler(errorHandler);
|
||||
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* 微信X509证书
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public class X509WechatCertificateInfo {
|
||||
/**
|
||||
* wechatPaySerial
|
||||
*/
|
||||
private String wechatPaySerial;
|
||||
/**
|
||||
* X509Certificate
|
||||
*/
|
||||
private X509Certificate x509Certificate;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author felord.cn
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public abstract class AbstractPayParams {
|
||||
|
||||
/**
|
||||
* 商品描述
|
||||
* Image形象店-深圳腾大-QQ公仔
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。
|
||||
* 示例值:1217752501201407033233368018
|
||||
*/
|
||||
private String outTradeNo;
|
||||
/**
|
||||
* 订单失效时间 YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
private String timeExpire;
|
||||
/**
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
*/
|
||||
private String attach;
|
||||
/**
|
||||
* 通知URL必须为直接可访问的URL,不允许携带查询串。
|
||||
*/
|
||||
private String notifyUrl;
|
||||
/**
|
||||
* 订单优惠标记
|
||||
*/
|
||||
private String goodsTag;
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private Amount amount;
|
||||
/**
|
||||
* 优惠功能
|
||||
*/
|
||||
private Detail detail;
|
||||
/**
|
||||
* 支付者 JSAPI/小程序下单 必传
|
||||
*/
|
||||
private Payer payer;
|
||||
/**
|
||||
* 场景信息
|
||||
*/
|
||||
private SceneInfo sceneInfo;
|
||||
/**
|
||||
* 结算信息
|
||||
*/
|
||||
private SettleInfo settleInfo;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class Amount {
|
||||
/**
|
||||
* 金额,单位【分】。
|
||||
*/
|
||||
private int total;
|
||||
private Integer total;
|
||||
/**
|
||||
* 货币单位,固定为 CNY 。
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ package cn.felord.payment.wechat.v3.model;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 支付请求参数.
|
||||
@@ -27,8 +28,9 @@ import lombok.Data;
|
||||
* @author felord.cn
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PayParams {
|
||||
public class PayParams extends AbstractPayParams {
|
||||
/**
|
||||
* The Appid.
|
||||
*/
|
||||
@@ -37,48 +39,5 @@ public class PayParams {
|
||||
* The Mchid.
|
||||
*/
|
||||
private String mchid;
|
||||
/**
|
||||
* 商品描述
|
||||
* Image形象店-深圳腾大-QQ公仔
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。
|
||||
* 示例值:1217752501201407033233368018
|
||||
*/
|
||||
private String outTradeNo;
|
||||
/**
|
||||
* 订单失效时间 YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
private String timeExpire;
|
||||
/**
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
*/
|
||||
private String attach;
|
||||
/**
|
||||
* 通知URL必须为直接可访问的URL,不允许携带查询串。
|
||||
*/
|
||||
private String notifyUrl;
|
||||
/**
|
||||
* 订单优惠标记
|
||||
*/
|
||||
private String goodsTag;
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private Amount amount;
|
||||
/**
|
||||
* 支付者 JSAPI/小程序下单 必传
|
||||
*/
|
||||
private Payer payer;
|
||||
/**
|
||||
* 优惠功能
|
||||
*/
|
||||
private Detail detail;
|
||||
/**
|
||||
* 场景信息
|
||||
*/
|
||||
private SceneInfo sceneInfo;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -59,12 +59,17 @@ public class RefundConsumeData {
|
||||
/**
|
||||
* 退款成功时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private OffsetDateTime successTime;
|
||||
/**
|
||||
* 退款入账账户
|
||||
*/
|
||||
private String userReceivedAccount;
|
||||
/**
|
||||
* 金额信息
|
||||
*/
|
||||
private Amount amount;
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付退款金额信息
|
||||
@@ -85,7 +90,6 @@ public class RefundConsumeData {
|
||||
/**
|
||||
* 用户实际支付金额,单位为分
|
||||
*/
|
||||
|
||||
private Integer payerTotal;
|
||||
/**
|
||||
* 退款给用户的金额,单位为分,不包含所有优惠券金额
|
||||
|
||||
@@ -44,12 +44,12 @@ public class StocksCreateParams {
|
||||
*/
|
||||
private String belongMerchant;
|
||||
/**
|
||||
* 批次开始时间 rfc 3339 yyyy-MM-ddTHH:mm:ss+TIMEZONE
|
||||
* 批次开始时间 rfc 3339 yyyy-MM-ddTHH:mm:ss.sss+TIMEZONE
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8")
|
||||
private OffsetDateTime availableBeginTime;
|
||||
/**
|
||||
* 批次结束时间 rfc 3339 yyyy-MM-ddTHH:mm:ss+TIMEZONE
|
||||
* 批次结束时间 rfc 3339 yyyy-MM-ddTHH:mm:ss.sss+TIMEZONE
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8")
|
||||
private OffsetDateTime availableEndTime;
|
||||
|
||||
@@ -20,8 +20,10 @@ package cn.felord.payment.wechat.v3.model;
|
||||
|
||||
import cn.felord.payment.wechat.enumeration.TradeState;
|
||||
import cn.felord.payment.wechat.enumeration.TradeType;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -38,9 +40,29 @@ public class TransactionConsumeData {
|
||||
*/
|
||||
private Amount amount;
|
||||
/**
|
||||
* 应用ID
|
||||
* 直连模式应用ID,服务商模式请解析spAppid
|
||||
*/
|
||||
private String appid;
|
||||
/**
|
||||
* 直连模式商户号,服务商模式请解析spMchid
|
||||
*/
|
||||
private String mchid;
|
||||
/**
|
||||
* 服务商模式-服务商APPID
|
||||
*/
|
||||
private String spAppid;
|
||||
/**
|
||||
* 服务商模式-服务商户号
|
||||
*/
|
||||
private String spMchid;
|
||||
/**
|
||||
* 服务商模式-子商户appid
|
||||
*/
|
||||
private String subAppid;
|
||||
/**
|
||||
* 服务商模式-子商户商户id
|
||||
*/
|
||||
private String subMchid;
|
||||
/**
|
||||
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
*/
|
||||
@@ -49,10 +71,6 @@ public class TransactionConsumeData {
|
||||
* 银行类型,采用字符串类型的银行标识。银行标识请参考 <a target= "_blank" href= "https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-6">《银行类型对照表》</a>
|
||||
*/
|
||||
private String bankType;
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
private String mchid;
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
@@ -72,7 +90,8 @@ public class TransactionConsumeData {
|
||||
/**
|
||||
* 支付完成时间 YYYY-MM-DDTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
private String successTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private OffsetDateTime successTime;
|
||||
/**
|
||||
* 在 1.0.0.RELEASE 直接返回了枚举字符串,1.0.2.RELEASE 中变更为枚举
|
||||
*
|
||||
|
||||
@@ -49,7 +49,7 @@ public class BusiFavorReceiveConsumeData {
|
||||
/**
|
||||
* 发放时间 rfc 3339 yyyy-MM-ddTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "GMT+8")
|
||||
private OffsetDateTime sendTime;
|
||||
/**
|
||||
* 微信用户在appid下的唯一标识。
|
||||
|
||||
@@ -40,7 +40,7 @@ public class CouponAvailableTime {
|
||||
* <p>
|
||||
* 需配合{@link #availableDayAfterReceive} 一同填写,不可单独填写。
|
||||
*/
|
||||
private int waitDaysAfterReceive;
|
||||
private Integer waitDaysAfterReceive;
|
||||
/**
|
||||
* 生效后N天内有效
|
||||
* <p>
|
||||
@@ -50,7 +50,7 @@ public class CouponAvailableTime {
|
||||
* <p>
|
||||
* 可配合{@link #waitDaysAfterReceive}一同填写,也可单独填写。单独填写时,有效期内领券后立即生效,生效后x天内有效。
|
||||
*/
|
||||
private int availableDayAfterReceive;
|
||||
private Integer availableDayAfterReceive;
|
||||
/**
|
||||
* 批次开始时间 rfc 3339 yyyy-MM-ddTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.partner;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 服务商模式-关闭订单请求参数
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public class CloseTransParams {
|
||||
/**
|
||||
* 子商户号
|
||||
*/
|
||||
private String subMchid;
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String outTradeNo;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.partner;
|
||||
|
||||
import cn.felord.payment.wechat.v3.model.AbstractPayParams;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author felord.cn
|
||||
* @since 1.0.8.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PartnerPayParams extends AbstractPayParams {
|
||||
/**
|
||||
* 服务商公众号ID
|
||||
*/
|
||||
private String spAppid;
|
||||
/**
|
||||
* 服务商户号
|
||||
*/
|
||||
private String spMchid;
|
||||
/**
|
||||
* 子商户appid.
|
||||
*/
|
||||
private String subAppid;
|
||||
/**
|
||||
* 子商户号
|
||||
*/
|
||||
private String subMchid;
|
||||
|
||||
}
|
||||
@@ -98,6 +98,10 @@ public class PayScoreUserPaidConsumeData {
|
||||
* The Time range.
|
||||
*/
|
||||
private TimeRange timeRange;
|
||||
/**
|
||||
* stateDescription
|
||||
*/
|
||||
private String stateDescription;
|
||||
/**
|
||||
* The Total amount.
|
||||
*/
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
<packaging>jar</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
4
pom.xml
4
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">
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot</artifactId>
|
||||
<version>1.0.6.RELEASE</version>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<tag>payment-spring-boot-1.0.6.RELEASE</tag>
|
||||
<tag>payment-spring-boot-1.0.9.RELEASE</tag>
|
||||
<url>https://github.com/NotFound403/payment-spring-boot</url>
|
||||
<connection>scm:git:https://github.com/NotFound403/payment-spring-boot.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/NotFound403/payment-spring-boot.git</developerConnection>
|
||||
|
||||
Reference in New Issue
Block a user