mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-17 23:43:41 +08:00
Compare commits
104 Commits
1.0.7.RELE
...
1.0.12.REL
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dd7c67f9a | ||
|
|
fa337b2997 | ||
|
|
f0d69600f7 | ||
|
|
eb8c68b084 | ||
|
|
b8ea383460 | ||
|
|
61e98ffbe9 | ||
|
|
a5596e17bb | ||
|
|
194937025f | ||
|
|
90233b8e32 | ||
|
|
7459039cfd | ||
|
|
9eb58860f1 | ||
|
|
e38167e748 | ||
|
|
5a3f258390 | ||
|
|
ddf9a449d1 | ||
|
|
21ba6feee0 | ||
|
|
554a93d41a | ||
|
|
c1d2cfce13 | ||
|
|
ee8d7d0d92 | ||
|
|
60fe80e615 | ||
|
|
53a34e6a71 | ||
|
|
398df66ec5 | ||
|
|
e387e91e38 | ||
|
|
10a91c51be | ||
|
|
453844bd9a | ||
|
|
3871e05ea7 | ||
|
|
16440d01cf | ||
|
|
91dc8e8f8a | ||
|
|
8cd3f63d68 | ||
|
|
a46590d0a9 | ||
|
|
0c230547a9 | ||
|
|
591eec7f65 | ||
|
|
ed190cc875 | ||
|
|
82941dffcb | ||
|
|
9d01a0fa3a | ||
|
|
78cbce016d | ||
|
|
7470787441 | ||
|
|
65cfbaf3b2 | ||
|
|
3be70e7d61 | ||
|
|
5646bab7e6 | ||
|
|
40ffb286d3 | ||
|
|
5047be7fd0 | ||
|
|
285356600a | ||
|
|
277a1223c5 | ||
|
|
6086dd5101 | ||
|
|
c9ed68fa80 | ||
|
|
e9307bf490 | ||
|
|
b4f8aa48b1 | ||
|
|
0bfa439c4e | ||
|
|
ee5522e980 | ||
|
|
53e6016c4c | ||
|
|
538e11dabe | ||
|
|
ae618da2d3 | ||
|
|
9dfdb2b295 | ||
|
|
d95f38fb0a | ||
|
|
00dc8da556 | ||
|
|
f1c9a873b5 | ||
|
|
65b3035c55 | ||
|
|
d1f3de7906 | ||
|
|
ef172471c5 | ||
|
|
f62e8eb921 | ||
|
|
291e73b34c | ||
|
|
39ec2ef4f5 | ||
|
|
71d0342eee | ||
|
|
7ed3582c12 | ||
|
|
adfb37a71a | ||
|
|
7ffcc47255 | ||
|
|
237851c7c5 | ||
|
|
8914d7272d | ||
|
|
cea3395aed | ||
|
|
77d80588fb | ||
|
|
f0c0b64a93 | ||
|
|
02ce523cde | ||
|
|
01b9fc3aa0 | ||
|
|
73b19fcc0a | ||
|
|
aae5096349 | ||
|
|
ace5a900d2 | ||
|
|
24f748a197 | ||
|
|
9880a1e561 | ||
|
|
0f8aa8f2d0 | ||
|
|
7eac8dcdc4 | ||
|
|
7c95c8ec7c | ||
|
|
34b65b6417 | ||
|
|
03fba59c49 | ||
|
|
54a0822c73 | ||
|
|
8d130e5df1 | ||
|
|
82bcc11dad | ||
|
|
e0eacd523e | ||
|
|
911f986d80 | ||
|
|
d1068c1fb6 | ||
|
|
b913698884 | ||
|
|
34e5443e88 | ||
|
|
f4dc70e828 | ||
|
|
39716c30e4 | ||
|
|
ab13d90b9b | ||
|
|
4f71f419a7 | ||
|
|
ec77604b99 | ||
|
|
ff5911205b | ||
|
|
22d83edb89 | ||
|
|
64d8294758 | ||
|
|
cecee81b08 | ||
|
|
6276b24c9f | ||
|
|
282a60e3bb | ||
|
|
2e143302e8 | ||
|
|
9fba48d52d |
35
.github/workflows/main.yml
vendored
Normal file
35
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# 相当于脚本用途的一个声明
|
||||
name: Maven Central Repo Deployment
|
||||
# 触发脚本的事件 这里为发布release之后触发
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
# 定义一个发行任务
|
||||
jobs:
|
||||
publish:
|
||||
# 任务运行的环境
|
||||
runs-on: ubuntu-latest
|
||||
# 任务的步骤
|
||||
steps:
|
||||
# 1. 声明 checkout 仓库代码到工作区
|
||||
- name: Checkout Git Repo
|
||||
uses: actions/checkout@v2
|
||||
# 2. 安装Java 环境 这里会用到的参数就是 Git Action secrets中配置的,
|
||||
# 取值要在key前面加 secrets.
|
||||
- name: Set up Maven Central Repo
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
server-id: sonatype-nexus-staging
|
||||
server-username: ${{ secrets.OSSRH_USER }}
|
||||
server-password: ${{ secrets.OSSRH_PASSWORD }}
|
||||
gpg-passphrase: ${{ secrets.GPG_PASSWORD }}
|
||||
# 3. 发布到Maven中央仓库
|
||||
- name: Publish to Maven Central Repo
|
||||
# 这里用到了其他人写的action脚本,详细可以去看他的文档。
|
||||
uses: samuelmeuli/action-maven-publish@v1
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_SECRET }}
|
||||
gpg_passphrase: ${{ secrets.GPG_PASSWORD }}
|
||||
nexus_username: ${{ secrets.OSSRH_USER }}
|
||||
nexus_password: ${{ secrets.OSSRH_PASSWORD }}
|
||||
@@ -11,7 +11,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
- 实现微信支付V3 商家券
|
||||
- 实现微信支付V3 批量转账到零钱
|
||||
|
||||
更多参考[changelog](https://notfound403.github.io/payment-spring-boot/#/changelog)
|
||||
|
||||
## 核心API结构
|
||||

|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
## 采用技术
|
||||
|
||||
@@ -1,10 +1,72 @@
|
||||
## 1.0.12.RELEASE
|
||||
- refector: 变更依赖管理方式,交由spring-boot-dependencies管理依赖([#40](https://github.com/NotFound403/payment-spring-boot/pull/40))
|
||||
- 微信支付
|
||||
- fix: 修复多租户配置下,平台证书刷新错误的问题([#49](https://github.com/NotFound403/payment-spring-boot/issues/49))
|
||||
- fix: 分账API描述符错误([#48](https://github.com/NotFound403/payment-spring-boot/issues/48)) 。
|
||||
- refactor: 避免受jackson类库xml模块的影响
|
||||
- refactor: V2签名优化
|
||||
|
||||
## 1.0.11.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: bcprov-jdk15to18算法库从1.66 升级到 1.67
|
||||
- feat: 微信支付 《支付通知API》新增优惠功能(promotion_detail)字段
|
||||
- feat: 微信支付基础支付《申请退款API》、《查询退款API》新增字段 from(退款出资账户及金额)
|
||||
- feat: 现在支持V3分账功能,目前只支持直连商户分账`WechatProfitsharingApi`和服务商分账`WechatPartnerProfitsharingApi`。
|
||||
- feat: 完善V3批量转账到零钱API,增加实现:转账明细电子回单受理API、查询转账明细电子回单受理结果API、查询账户实时余额API、查询账户日终余额API、商户银行来账查询API
|
||||
- refactor: 微信支付分分账标记默认改为不分账
|
||||
- refactor: 平台证书刷新逻辑优化 ([#I3NGSB](https://gitee.com/felord/payment-spring-boot/issues/I3NGSB))
|
||||
- refactor: 交易账单和资金账单现在能够正常的下载文件了,可以根据参数自动选择下载为gzip或者txt文件
|
||||
- fix: 批量转账到零钱:微信明细单号查询明细单API,商家明细单号查询明细单API 参数错误
|
||||
- fix: 修复查询代金券参数的错误
|
||||
- 支付宝
|
||||
- feat: 支付宝增加字段`classpathUsed`来标识是否使用类路径,默认`true`。证书路径可依此来决定是使用绝对路径还是类路径
|
||||
|
||||
## 1.0.10.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: 微信支付V2分账接口实现,感谢**zacone**同学贡献的PR
|
||||
- factor: 优化证书加载方式
|
||||
- factor: 商家券修改API的请求方式变更为`Patch`
|
||||
- fix: 修复微信支付V3中native支付通知回调`successTime`字段无时区信息的问题([#I3ED43](https://gitee.com/felord/payment-spring-boot/issues/I3ED43))
|
||||
- 支付宝
|
||||
- fix: 修复支付宝Maven打包无法读取证书的问题([#24](https://github.com/NotFound403/payment-spring-boot/issues/24))
|
||||
|
||||
## 1.0.9.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- refactor: `WechatPartnerPayApi` 加入**Spring IOC**
|
||||
- fix: 支付分支付成功回调反序列化异常 ([#21](https://github.com/NotFound403/payment-spring-boot/issues/21))
|
||||
- fix: 修复枚举空指针问题 ([#22](https://github.com/NotFound403/payment-spring-boot/issues/22))
|
||||
|
||||
## 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 +77,7 @@
|
||||
- fix: 关单接口调用异常
|
||||
|
||||
## 1.0.4.RELEASE
|
||||
|
||||
- 微信支付
|
||||
- feat: 增加微信支付商家券相关接口`WechatMarketingBusiFavorApi`,商家券请阅读相关产品文档。
|
||||
- feat: 代金券功能增加发放消费卡接口。
|
||||
@@ -24,6 +87,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.7.RELEASE</version>
|
||||
<version>1.0.12.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
|
||||
@@ -92,7 +92,7 @@ wechat:
|
||||
|
||||
### 支付宝
|
||||
|
||||
在Spring Boot项目中的`application.yaml`中配置`ali.pay.v1`相关参数。
|
||||
在Spring Boot项目中的`application.yaml`中配置`ali.pay.v1`相关参数。证书细节参见【日常踩坑】
|
||||
```yaml
|
||||
ali:
|
||||
pay:
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>payment-spring-boot-autoconfigure</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
package cn.felord.payment.alipay;
|
||||
|
||||
|
||||
import cn.felord.payment.PayException;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayClient;
|
||||
import com.alipay.api.CertAlipayRequest;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import cn.felord.payment.PayException;
|
||||
import com.alipay.api.internal.util.file.IOUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -32,7 +33,9 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author felord.cn
|
||||
@@ -53,39 +56,37 @@ 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::getFileAbsolutePath).to(certAlipayRequest::setCertPath);
|
||||
propertyMapper.from(v1::getAlipayPublicCertPath).as(this::getFileAbsolutePath).to(certAlipayRequest::setAlipayPublicCertPath);
|
||||
propertyMapper.from(v1::getAlipayRootCertPath).as(this::getFileAbsolutePath).to(certAlipayRequest::setRootCertPath);
|
||||
|
||||
Function<String,String> 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 getFileAbsolutePath(String classPath) {
|
||||
try {
|
||||
return new ClassPathResource(classPath).getFile().getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
log.error("ali pay cert path is not exist ,{}", e.getMessage());
|
||||
throw new PayException("ali pay cert path is not exist");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private String appRSAPrivateKey(String classPath) {
|
||||
private String loadFile(String classPath) {
|
||||
ClassPathResource resource = new ClassPathResource(classPath);
|
||||
try {
|
||||
FileReader in = new FileReader(resource.getFile());
|
||||
try(BufferedReader bufferedReader = new BufferedReader(in)){
|
||||
return bufferedReader.readLine();
|
||||
}
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream())) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -60,7 +60,7 @@ public class WechatPayConfiguration {
|
||||
WechatPayProperties.V3 v3 = v3Map.get(tenantId);
|
||||
String certPath = v3.getCertPath();
|
||||
String mchId = v3.getMchId();
|
||||
WechatMetaBean wechatMetaBean = keyPairFactory.createPKCS12(certPath, CERT_ALIAS, mchId);
|
||||
WechatMetaBean wechatMetaBean = keyPairFactory.initWechatMetaBean(certPath, CERT_ALIAS, mchId);
|
||||
wechatMetaBean.setV3(v3);
|
||||
wechatMetaBean.setTenantId(tenantId);
|
||||
container.addWechatMeta(tenantId, wechatMetaBean);
|
||||
|
||||
@@ -29,64 +29,42 @@ public enum CouponBgColor {
|
||||
/**
|
||||
* Color 010 coupon bg color.
|
||||
*/
|
||||
COLOR010("#63B359"),
|
||||
Color010,
|
||||
/**
|
||||
* Color 020 coupon bg color.
|
||||
*/
|
||||
COLOR020("#2C9F67"),
|
||||
Color020,
|
||||
/**
|
||||
* Color 030 coupon bg color.
|
||||
*/
|
||||
COLOR030("#509FC9"),
|
||||
Color030,
|
||||
/**
|
||||
* Color 040 coupon bg color.
|
||||
*/
|
||||
COLOR040("#5885CF"),
|
||||
Color040,
|
||||
/**
|
||||
* Color 050 coupon bg color.
|
||||
*/
|
||||
COLOR050("#9062C0"),
|
||||
Color050,
|
||||
/**
|
||||
* Color 060 coupon bg color.
|
||||
*/
|
||||
COLOR060("#D09A45"),
|
||||
Color060,
|
||||
/**
|
||||
* Color 070 coupon bg color.
|
||||
*/
|
||||
COLOR070("#E4B138"),
|
||||
Color070,
|
||||
/**
|
||||
* Color 080 coupon bg color.
|
||||
*/
|
||||
COLOR080("#EE903C"),
|
||||
Color080,
|
||||
/**
|
||||
* Color 090 coupon bg color.
|
||||
*/
|
||||
COLOR090("#DD6549"),
|
||||
Color090,
|
||||
/**
|
||||
* Color 100 coupon bg color.
|
||||
*/
|
||||
COLOR100("#CC463D");
|
||||
Color100
|
||||
|
||||
/**
|
||||
* The Color.
|
||||
*/
|
||||
private final String color;
|
||||
|
||||
/**
|
||||
* Instantiates a new Coupon bg color.
|
||||
*
|
||||
* @param color the color
|
||||
*/
|
||||
CouponBgColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Color string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String color() {
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -431,7 +431,7 @@ public enum WechatPayV3Type {
|
||||
*
|
||||
* @since 1.0.4.RELEASES
|
||||
*/
|
||||
MARKETING_BUSI_FAVOR_UPDATE(HttpMethod.POST, "%s/v3/marketing/busifavor/stocks/{stock_id}"),
|
||||
MARKETING_BUSI_FAVOR_UPDATE(HttpMethod.PATCH, "%s/v3/marketing/busifavor/stocks/{stock_id}"),
|
||||
/**
|
||||
* 申请退券API.
|
||||
*
|
||||
@@ -486,7 +486,143 @@ 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}"),
|
||||
/**
|
||||
* 转账明细电子回单受理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"),
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
* 服务商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}"),
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/**
|
||||
* 请求分账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_MCH_CONFIG(HttpMethod.GET, "%s/v3/profitsharing/merchant-configs/{sub_mchid}"),
|
||||
/**
|
||||
* 添加分账接收方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/delete");
|
||||
/**
|
||||
* The Pattern.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.BaseModel;
|
||||
import cn.felord.payment.wechat.v2.model.allocation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信支付分账
|
||||
* <p>
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@Slf4j
|
||||
public class WechatAllocationApi {
|
||||
/**
|
||||
* The constant MAPPER.
|
||||
*/
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
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)
|
||||
.registerModule(new JavaTimeModule());
|
||||
}
|
||||
|
||||
private final WechatV2Client wechatV2Client;
|
||||
|
||||
/**
|
||||
* Instantiates a new Wechat allocation api.
|
||||
*
|
||||
* @param wechatV2Client the wechat v 2 client
|
||||
*/
|
||||
public WechatAllocationApi(WechatV2Client wechatV2Client) {
|
||||
this.wechatV2Client = wechatV2Client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求单次分账
|
||||
*
|
||||
* @param profitSharingModel the profit sharing model
|
||||
* @return json node
|
||||
*/
|
||||
@SneakyThrows
|
||||
public JsonNode profitSharing(ProfitSharingModel profitSharingModel) {
|
||||
ProfitSharingSModel model = new ProfitSharingSModel();
|
||||
List<Receiver> receivers = profitSharingModel.getReceivers();
|
||||
model.setReceivers(MAPPER.writeValueAsString(receivers));
|
||||
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
model.setAppid(v3.getAppId());
|
||||
model.setMchId(v3.getMchId());
|
||||
|
||||
model.setTransactionId(profitSharingModel.getTransactionId());
|
||||
model.setOutOrderNo(profitSharingModel.getOutOrderNo());
|
||||
|
||||
model.certPath(v3.getCertPath());
|
||||
model.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(model,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/secapi/pay/profitsharing");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求单次分账
|
||||
*
|
||||
* @param multiProfitSharingModel the multi profit sharing model
|
||||
* @return json node
|
||||
*/
|
||||
@SneakyThrows
|
||||
public JsonNode multiProfitSharing(MultiProfitSharingModel multiProfitSharingModel) {
|
||||
MultiProfitSharingSModel multiProfitSharingSModel = new MultiProfitSharingSModel();
|
||||
List<Receiver> receivers = multiProfitSharingModel.getReceivers();
|
||||
multiProfitSharingSModel.setReceivers(MAPPER.writeValueAsString(receivers));
|
||||
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
multiProfitSharingSModel.setAppid(v3.getAppId());
|
||||
multiProfitSharingSModel.setMchId(v3.getMchId());
|
||||
|
||||
multiProfitSharingSModel.setTransactionId(multiProfitSharingModel.getTransactionId());
|
||||
multiProfitSharingSModel.setOutOrderNo(multiProfitSharingModel.getOutOrderNo());
|
||||
|
||||
multiProfitSharingSModel.certPath(v3.getCertPath());
|
||||
multiProfitSharingSModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(multiProfitSharingSModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/secapi/pay/multiprofitsharing");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询分账结果
|
||||
*
|
||||
* @param profitSharingQueryModel the profit sharing query model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode profitSharingQuery(ProfitSharingQueryModel profitSharingQueryModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingQueryModel.setMchId(v3.getMchId());
|
||||
profitSharingQueryModel.certPath(v3.getCertPath());
|
||||
profitSharingQueryModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingQueryModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/pay/profitsharingquery");
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加分账接收方
|
||||
*
|
||||
* @param profitSharingAddReceiverModel the profit sharing add receiver model
|
||||
* @return json node
|
||||
*/
|
||||
@SneakyThrows
|
||||
public JsonNode profitSharingAddReceiver(ProfitSharingAddReceiverModel profitSharingAddReceiverModel) {
|
||||
ProfitSharingAddReceiverSModel profitSharingAddReceiverSModel = new ProfitSharingAddReceiverSModel();
|
||||
ProfitSharingAddReceiverModel.Receiver receiver = profitSharingAddReceiverModel.getReceiver();
|
||||
profitSharingAddReceiverSModel.setReceiver(MAPPER.writeValueAsString(receiver));
|
||||
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingAddReceiverSModel.setAppid(v3.getAppId());
|
||||
profitSharingAddReceiverSModel.setMchId(v3.getMchId());
|
||||
|
||||
profitSharingAddReceiverSModel.certPath(v3.getCertPath());
|
||||
profitSharingAddReceiverSModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingAddReceiverSModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分账接收方
|
||||
*
|
||||
* @param profitSharingRemoveReceiverModel the profit sharing remove receiver model
|
||||
* @return json node
|
||||
*/
|
||||
@SneakyThrows
|
||||
public JsonNode profitSharingRemoveReceiver(ProfitSharingRemoveReceiverModel profitSharingRemoveReceiverModel) {
|
||||
ProfitSharingRemoveReceiverSModel profitSharingRemoveReceiverSModel = new ProfitSharingRemoveReceiverSModel();
|
||||
ProfitSharingRemoveReceiverModel.Receiver receiver = profitSharingRemoveReceiverModel.getReceiver();
|
||||
profitSharingRemoveReceiverSModel.setReceiver(MAPPER.writeValueAsString(receiver));
|
||||
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingRemoveReceiverSModel.setAppid(v3.getAppId());
|
||||
profitSharingRemoveReceiverSModel.setMchId(v3.getMchId());
|
||||
|
||||
profitSharingRemoveReceiverSModel.certPath(v3.getCertPath());
|
||||
profitSharingRemoveReceiverSModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingRemoveReceiverSModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver");
|
||||
}
|
||||
|
||||
/**
|
||||
* 完结分账
|
||||
*
|
||||
* @param profitSharingFinishModel the profit sharing finish model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode profitSharingFinish(ProfitSharingFinishModel profitSharingFinishModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingFinishModel.setAppid(v3.getAppId());
|
||||
profitSharingFinishModel.setMchId(v3.getMchId());
|
||||
profitSharingFinishModel.certPath(v3.getCertPath());
|
||||
profitSharingFinishModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingFinishModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单待分账金额
|
||||
*
|
||||
* @param profitSharingOrderAmountQueryModel the profit sharing order amount query model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode profitSharingOrderAmountQuery(ProfitSharingOrderAmountQueryModel profitSharingOrderAmountQueryModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingOrderAmountQueryModel.setMchId(v3.getMchId());
|
||||
profitSharingOrderAmountQueryModel.certPath(v3.getCertPath());
|
||||
profitSharingOrderAmountQueryModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingOrderAmountQueryModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/pay/profitsharingorderamountquery");
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账回退
|
||||
*
|
||||
* @param profitSharingReturnModel the profit sharing return model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode profitSharingReturn(ProfitSharingReturnModel profitSharingReturnModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingReturnModel.setAppid(v3.getAppId());
|
||||
profitSharingReturnModel.setMchId(v3.getMchId());
|
||||
profitSharingReturnModel.certPath(v3.getCertPath());
|
||||
profitSharingReturnModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingReturnModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/secapi/pay/profitsharingreturn");
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退结果查询
|
||||
*
|
||||
* @param profitSharingReturnQueryModel the profit sharing return query model
|
||||
* @return json node
|
||||
*/
|
||||
public JsonNode profitSharingReturnQuery(ProfitSharingReturnQueryModel profitSharingReturnQueryModel) {
|
||||
WechatPayProperties.V3 v3 = wechatV2Client.getWechatMetaBean().getV3();
|
||||
profitSharingReturnQueryModel.setAppid(v3.getAppId());
|
||||
profitSharingReturnQueryModel.setMchId(v3.getMchId());
|
||||
profitSharingReturnQueryModel.certPath(v3.getCertPath());
|
||||
profitSharingReturnQueryModel.signType(BaseModel.HMAC_SHA256);
|
||||
return wechatV2Client.wechatPayRequest(profitSharingReturnQueryModel,
|
||||
HttpMethod.POST,
|
||||
"https://api.mch.weixin.qq.com/pay/profitsharingreturnquery");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,8 +21,8 @@ package cn.felord.payment.wechat.v2.model;
|
||||
import cn.felord.payment.PayException;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
@@ -48,12 +48,16 @@ import org.springframework.util.IdGenerator;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The type Base model.
|
||||
@@ -63,6 +67,7 @@ import java.security.cert.CertificateException;
|
||||
*/
|
||||
@Getter
|
||||
public abstract class BaseModel {
|
||||
public static final String HMAC_SHA256="HMAC-SHA256";
|
||||
private static final XmlMapper XML_MAPPER = new XmlMapper();
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
@@ -71,7 +76,8 @@ public abstract class BaseModel {
|
||||
XML_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
// 属性使用 驼峰首字母小写
|
||||
.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
OBJECT_MAPPER.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
|
||||
OBJECT_MAPPER
|
||||
// .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
}
|
||||
@@ -84,13 +90,27 @@ public abstract class BaseModel {
|
||||
private String sign;
|
||||
@JsonIgnore
|
||||
private String appSecret;
|
||||
|
||||
@JsonIgnore
|
||||
private String certPath;
|
||||
@JsonIgnore
|
||||
private String signType;
|
||||
|
||||
public BaseModel appSecret(String appSecret) {
|
||||
this.appSecret = appSecret;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseModel certPath(String certPath) {
|
||||
this.certPath = certPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BaseModel signType(String signType) {
|
||||
this.signType = signType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Xml string.
|
||||
*
|
||||
@@ -99,7 +119,11 @@ public abstract class BaseModel {
|
||||
@SneakyThrows
|
||||
private String xml() {
|
||||
String link = link(this);
|
||||
this.sign = this.md5(link);
|
||||
if (HMAC_SHA256.equals(signType)) {
|
||||
this.sign = this.hmacSha256(link);
|
||||
} else {
|
||||
this.sign = this.md5(link);
|
||||
}
|
||||
return XML_MAPPER.writer()
|
||||
.withRootName("xml")
|
||||
.writeValueAsString(this);
|
||||
@@ -120,6 +144,22 @@ public abstract class BaseModel {
|
||||
return Hex.toHexString(md5Bytes).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256.
|
||||
*
|
||||
* @param src the src
|
||||
* @return the string
|
||||
*/
|
||||
@SneakyThrows
|
||||
private String hmacSha256(String src) {
|
||||
String algorithm = "HmacSHA256";
|
||||
Mac sha256HMAC = Mac.getInstance(algorithm,"BC");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(appSecret.getBytes(), algorithm);
|
||||
sha256HMAC.init(secretKeySpec);
|
||||
byte[] bytes = sha256HMAC.doFinal(src.getBytes(StandardCharsets.UTF_8));
|
||||
return Hex.toHexString(bytes).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照格式拼接参数以生成签名
|
||||
*
|
||||
@@ -130,14 +170,17 @@ public abstract class BaseModel {
|
||||
@SneakyThrows
|
||||
private <T> String link(T t) {
|
||||
Assert.hasText(appSecret, "wechat pay appSecret is required");
|
||||
return OBJECT_MAPPER
|
||||
.writer()
|
||||
.writeValueAsString(t)
|
||||
.replaceAll("\":\"", "=")
|
||||
.replaceAll("\",\"", "&")
|
||||
.replaceAll("\\{\"", "")
|
||||
.replaceAll("\"}", "")
|
||||
.concat("&key=").concat(this.appSecret);
|
||||
String json = OBJECT_MAPPER
|
||||
.writeValueAsString(t);
|
||||
|
||||
TreeMap<String, String> map = OBJECT_MAPPER.readValue(json, new TypeReference<TreeMap<String, String>>() {
|
||||
});
|
||||
|
||||
String query = map.entrySet()
|
||||
.stream()
|
||||
.map(entry -> entry.getKey().concat("=").concat(entry.getValue()))
|
||||
.collect(Collectors.joining("&"));
|
||||
return query.concat("&key=").concat(this.appSecret);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +207,7 @@ public abstract class BaseModel {
|
||||
private RestTemplate getRestTemplateClientAuthentication(String mchId)
|
||||
throws IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException,
|
||||
KeyStoreException, KeyManagementException {
|
||||
ClassPathResource resource = new ClassPathResource("wechat/apiclient_cert.p12");
|
||||
ClassPathResource resource = new ClassPathResource(certPath == null ? "wechat/apiclient_cert.p12" : certPath);
|
||||
|
||||
char[] pem = mchId.toCharArray();
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Base profit sharing model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class BaseProfitSharingModel extends BaseModel {
|
||||
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
private String appid;
|
||||
private String transactionId;
|
||||
private String outOrderNo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Base profit sharing receiver model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class BaseProfitSharingReceiverModel extends BaseModel {
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 公众账号ID.
|
||||
* <p>
|
||||
* 微信分配的公众账号ID
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
/**
|
||||
* The type Multi profit sharing model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
public class MultiProfitSharingModel extends ProfitSharingModel {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
/**
|
||||
* The type Multi profit sharing s model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
public class MultiProfitSharingSModel extends ProfitSharingSModel {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing add receiver model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingAddReceiverModel extends BaseProfitSharingReceiverModel {
|
||||
|
||||
private Receiver receiver;
|
||||
|
||||
/**
|
||||
* The type Receiver.
|
||||
*/
|
||||
@Data
|
||||
public static class Receiver {
|
||||
|
||||
/**
|
||||
* 分账接收方类型.
|
||||
* <p>
|
||||
* MERCHANT_ID:商户号(mch_id或者sub_mch_id)
|
||||
* PERSONAL_OPENID:个人openid
|
||||
*/
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* 分账接收方帐号.
|
||||
* <p>
|
||||
* 类型是MERCHANT_ID时,是商户号(mch_id或者sub_mch_id)
|
||||
* 类型是PERSONAL_OPENID时,是个人openid
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 分账接收方全称.
|
||||
* <p>
|
||||
* 分账接收方类型是MERCHANT_ID时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名
|
||||
* 分账接收方类型是PERSONAL_OPENID时,是个人姓名(选传,传则校验)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 与分账方的关系类型.
|
||||
* <p>
|
||||
* 子商户与接收方的关系。
|
||||
* 本字段值为枚举:
|
||||
* SERVICE_PROVIDER:服务商
|
||||
* STORE:门店
|
||||
* STAFF:员工
|
||||
* STORE_OWNER:店主
|
||||
* PARTNER:合作伙伴
|
||||
* HEADQUARTER:总部
|
||||
* BRAND:品牌方
|
||||
* DISTRIBUTOR:分销商
|
||||
* USER:用户
|
||||
* SUPPLIER:供应商
|
||||
* CUSTOM:自定义
|
||||
*/
|
||||
private RelationType relationType;
|
||||
|
||||
/**
|
||||
* 自定义的分账关系.
|
||||
* <p>
|
||||
* 子商户与接收方具体的关系,本字段最多10个字。
|
||||
* 当字段relation_type的值为CUSTOM时,本字段必填
|
||||
* 当字段relation_type的值不为CUSTOM时,本字段无需填写
|
||||
*/
|
||||
private String customRelation;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing add receiver s model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingAddReceiverSModel extends BaseProfitSharingReceiverModel {
|
||||
|
||||
private String receiver;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing finish model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingFinishModel extends BaseModel {
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 公众账号ID.
|
||||
* <p>
|
||||
* 微信分配的公众账号ID
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 商户分账单号.
|
||||
* <p>
|
||||
* 查询分账结果,输入申请分账时的商户分账单号; 查询分账完结执行的结果,输入发起分账完结时的商户分账单号
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* 分账完结描述.
|
||||
* <p>
|
||||
* 分账完结的原因描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单次分账请求按照传入的分账接收方账号和资金进行分账,同时会将订单剩余的待分账金额解冻给本商户。
|
||||
* 故操作成功后,订单不能再进行分账,也不能进行分账完结。
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingModel extends BaseProfitSharingModel {
|
||||
|
||||
/**
|
||||
* 分账接收方列表,不超过50个json对象,不能设置分账方作为分账接受方
|
||||
*/
|
||||
private List<Receiver> receivers;
|
||||
|
||||
}
|
||||
@@ -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.v2.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing order amount query model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingOrderAmountQueryModel extends BaseModel {
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing query model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingQueryModel extends BaseModel {
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 商户分账单号.
|
||||
* <p>
|
||||
* 查询分账结果,输入申请分账时的商户分账单号; 查询分账完结执行的结果,输入发起分账完结时的商户分账单号
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing remove receiver model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingRemoveReceiverModel extends BaseProfitSharingReceiverModel {
|
||||
|
||||
private Receiver receiver;
|
||||
|
||||
/**
|
||||
* The type Receiver.
|
||||
*/
|
||||
@Data
|
||||
public static class Receiver {
|
||||
|
||||
private Type type;
|
||||
private String account;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing remove receiver s model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingRemoveReceiverSModel extends BaseProfitSharingReceiverModel {
|
||||
|
||||
private String receiver;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing return model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingReturnModel extends BaseModel {
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 公众账号ID.
|
||||
* <p>
|
||||
* 微信分配的公众账号ID
|
||||
*/
|
||||
private String appid;
|
||||
private String orderId;
|
||||
private String outOrderNo;
|
||||
private String outReturnNo;
|
||||
private String returnAccountType;
|
||||
private String returnAccount;
|
||||
private String returnAmount;
|
||||
private String description;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing return query model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingReturnQueryModel extends BaseModel {
|
||||
|
||||
/**
|
||||
* 商户号.
|
||||
* <p>
|
||||
* 微信支付分配的商户号
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 公众账号ID.
|
||||
* <p>
|
||||
* 微信分配的公众账号ID
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 微信分账订单号.
|
||||
* <p>
|
||||
* 原发起分账请求时,微信返回的微信分账单号,与商户分账单号一一对应。
|
||||
* 微信分账单号与商户分账单号二选一填写
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 商户分账单号.
|
||||
* <p>
|
||||
* 原发起分账请求时使用的商户系统内部的分账单号。
|
||||
* 微信分账单号与商户分账单号二选一填写
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* 商户回退单号.
|
||||
* <p>
|
||||
* 调用回退接口提供的商户系统内部的回退单号
|
||||
*/
|
||||
private String outReturnNo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* The type Profit sharing s model.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProfitSharingSModel extends BaseProfitSharingModel {
|
||||
|
||||
/**
|
||||
* 分账接收方列表,不超过50个json对象,不能设置分账方作为分账接受方。
|
||||
*/
|
||||
private String receivers;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* The type Receiver.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public class Receiver {
|
||||
|
||||
/**
|
||||
* 分账接收方类型.
|
||||
* <p>
|
||||
* MERCHANT_ID:商户号(mch_id或者sub_mch_id)
|
||||
* PERSONAL_OPENID:个人openid
|
||||
*/
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* 分账接收方帐号.
|
||||
* <p>
|
||||
* 类型是MERCHANT_ID时,是商户号(mch_id或者sub_mch_id)
|
||||
* 类型是PERSONAL_OPENID时,是个人openid
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 分账金额.
|
||||
* <p>
|
||||
* 单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额
|
||||
*/
|
||||
private Integer amount;
|
||||
|
||||
/**
|
||||
* 分账描述.
|
||||
* <p>
|
||||
* 分账的原因描述,分账账单中需要体现
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 分账个人接收方姓名.
|
||||
* <p>
|
||||
* 可选项,在接收方类型为个人的时可选填,若有值,会检查与 name 是否实名匹配,不匹配会拒绝分账请求.
|
||||
* 1、分账接收方类型是PERSONAL_OPENID时,是个人姓名(选传,传则校验)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.model.allocation;
|
||||
|
||||
/**
|
||||
* The enum Relation type.
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
public enum RelationType {
|
||||
|
||||
/**
|
||||
* Service provider relation type.
|
||||
*/
|
||||
SERVICE_PROVIDER,
|
||||
/**
|
||||
* Store relation type.
|
||||
*/
|
||||
STORE,
|
||||
/**
|
||||
* Staff relation type.
|
||||
*/
|
||||
STAFF,
|
||||
/**
|
||||
* Store owner relation type.
|
||||
*/
|
||||
STORE_OWNER,
|
||||
/**
|
||||
* Partner relation type.
|
||||
*/
|
||||
PARTNER,
|
||||
/**
|
||||
* Headquarter relation type.
|
||||
*/
|
||||
HEADQUARTER,
|
||||
/**
|
||||
* Brand relation type.
|
||||
*/
|
||||
BRAND,
|
||||
/**
|
||||
* Distributor relation type.
|
||||
*/
|
||||
DISTRIBUTOR,
|
||||
/**
|
||||
* User relation type.
|
||||
*/
|
||||
USER,
|
||||
/**
|
||||
* Supplier relation type.
|
||||
*/
|
||||
SUPPLIER,
|
||||
/**
|
||||
* Custom relation type.
|
||||
*/
|
||||
CUSTOM
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.felord.payment.wechat.v2.model.allocation;
|
||||
|
||||
/**
|
||||
* The enum Type.
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
/**
|
||||
* 商户号(mch_id或者sub_mch_id)
|
||||
*/
|
||||
MERCHANT_ID,
|
||||
|
||||
/**
|
||||
* 个人openid
|
||||
*/
|
||||
PERSONAL_OPENID
|
||||
}
|
||||
@@ -24,11 +24,13 @@ import cn.felord.payment.wechat.v3.model.FundFlowBillParams;
|
||||
import cn.felord.payment.wechat.v3.model.TradeBillParams;
|
||||
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.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;
|
||||
@@ -88,10 +90,12 @@ public abstract class AbstractApi {
|
||||
* @param mapper the mapper
|
||||
*/
|
||||
private void applyObjectMapper(ObjectMapper mapper) {
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
SimpleModule module = new JavaTimeModule();
|
||||
mapper.registerModule(module);
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
// empty string error
|
||||
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
.registerModule(new JavaTimeModule());
|
||||
}
|
||||
|
||||
|
||||
@@ -185,10 +189,11 @@ public abstract class AbstractApi {
|
||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Get请求对象.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param uri the uri
|
||||
* @param httpHeaders the http headers
|
||||
* @return the request entity
|
||||
*/
|
||||
@@ -200,12 +205,29 @@ public abstract class AbstractApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* 对账单内容下载,非流文件。
|
||||
* 构建Post请求对象.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param params the params
|
||||
* @return the request entity
|
||||
*/
|
||||
protected RequestEntity<?> Patch(URI uri, Object params) {
|
||||
try {
|
||||
return RequestEntity.patch(uri).header("Pay-TenantId", tenantId)
|
||||
.body(mapper.writeValueAsString(params));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new PayException("wechat app pay json failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对账单CSV内容下载,非流文件。
|
||||
*
|
||||
* @param link the link
|
||||
* @return 对账单内容 ,有可能为空字符 “”
|
||||
* @see AbstractApi#billResource(String) AbstractApi#billResource(String)AbstractApi#billResource(String)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)
|
||||
@@ -216,26 +238,12 @@ public abstract class AbstractApi {
|
||||
.download();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对账单下载,流文件。
|
||||
*
|
||||
* @param link the link
|
||||
* @return response entity
|
||||
*/
|
||||
protected ResponseEntity<Resource> 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
|
||||
* <p>
|
||||
* 返回值直接返回前端,会下载tradebill-前缀加上日期的txt文件(默认)或者gzip文件。
|
||||
* <p>
|
||||
* 微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况。
|
||||
* <p>
|
||||
* 注意:
|
||||
@@ -247,10 +255,13 @@ public abstract class AbstractApi {
|
||||
* </ul>
|
||||
*
|
||||
* @param tradeBillParams tradeBillParams
|
||||
* @return the response entity
|
||||
* @since 1.0.3.RELEASE
|
||||
*/
|
||||
public final void downloadTradeBill(TradeBillParams tradeBillParams) {
|
||||
this.client().withType(WechatPayV3Type.TRADEBILL, tradeBillParams)
|
||||
public ResponseEntity<Resource> downloadTradeBill(TradeBillParams tradeBillParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
|
||||
this.client().withType(WechatPayV3Type.TRADE_BILL, tradeBillParams)
|
||||
.function((wechatPayV3Type, params) -> {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
LocalDate billDate = params.getBillDate();
|
||||
@@ -273,8 +284,15 @@ 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();
|
||||
|
||||
String ext = Objects.equals(TarType.GZIP, tradeBillParams.getTarType()) ? ".gzip" : ".txt";
|
||||
String filename = "tradebill-" + tradeBillParams.getBillDate().toString() + ext;
|
||||
return downloadBillResponse(downloadUrl, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,10 +307,12 @@ public abstract class AbstractApi {
|
||||
* </ul>
|
||||
*
|
||||
* @param fundFlowBillParams fundFlowBillParams
|
||||
* @return the response entity
|
||||
* @since 1.0.3.RELEASE
|
||||
*/
|
||||
public final void downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) {
|
||||
this.client().withType(WechatPayV3Type.FUNDFLOWBILL, fundFlowBillParams)
|
||||
public ResponseEntity<Resource> downloadFundFlowBill(FundFlowBillParams fundFlowBillParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.FUND_FLOW_BILL, fundFlowBillParams)
|
||||
.function((wechatPayV3Type, params) -> {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
LocalDate billDate = params.getBillDate();
|
||||
@@ -310,7 +330,46 @@ 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();
|
||||
|
||||
String ext = Objects.equals(TarType.GZIP, fundFlowBillParams.getTarType()) ? ".gzip" : ".txt";
|
||||
String filename = "fundflowbill-" + fundFlowBillParams.getBillDate().toString() + ext;
|
||||
return this.downloadBillResponse(downloadUrl, 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<Resource> downloadBillResponse(String downloadUrl, String filename) {
|
||||
ResponseEntity<Resource> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用{@code /v3/billdownload/file}下载返回的原始文件流
|
||||
*
|
||||
* @param link the link
|
||||
* @return response entity
|
||||
*/
|
||||
protected ResponseEntity<Resource> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,7 @@ package cn.felord.payment.wechat.v3;
|
||||
import cn.felord.payment.PayException;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.*;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
@@ -36,9 +33,16 @@ import java.security.cert.X509Certificate;
|
||||
**/
|
||||
public class KeyPairFactory {
|
||||
|
||||
private KeyStore store;
|
||||
private static final KeyStore PKCS12_KEY_STORE;
|
||||
|
||||
static {
|
||||
try {
|
||||
PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12");
|
||||
} catch (KeyStoreException e) {
|
||||
throw new PayException(" wechat pay keystore initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
/**
|
||||
* 获取公私钥.
|
||||
@@ -48,23 +52,16 @@ public class KeyPairFactory {
|
||||
* @param keyPass password
|
||||
* @return the key pair
|
||||
*/
|
||||
public WechatMetaBean createPKCS12(String keyPath, String keyAlias, String keyPass) {
|
||||
public WechatMetaBean initWechatMetaBean(String keyPath, String keyAlias, String keyPass) {
|
||||
ClassPathResource resource = new ClassPathResource(keyPath);
|
||||
char[] pem = keyPass.toCharArray();
|
||||
try {
|
||||
synchronized (lock) {
|
||||
if (store == null) {
|
||||
synchronized (lock) {
|
||||
store = KeyStore.getInstance("PKCS12");
|
||||
store.load(resource.getInputStream(), pem);
|
||||
}
|
||||
}
|
||||
}
|
||||
X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
|
||||
PKCS12_KEY_STORE.load(resource.getInputStream(), pem);
|
||||
X509Certificate certificate = (X509Certificate) PKCS12_KEY_STORE.getCertificate(keyAlias);
|
||||
certificate.checkValidity();
|
||||
String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
|
||||
PublicKey publicKey = certificate.getPublicKey();
|
||||
PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);
|
||||
PrivateKey storeKey = (PrivateKey) PKCS12_KEY_STORE.getKey(keyAlias, pem);
|
||||
WechatMetaBean wechatMetaBean = new WechatMetaBean();
|
||||
wechatMetaBean.setKeyPair(new KeyPair(publicKey, storeKey));
|
||||
wechatMetaBean.setSerialNumber(serialNumber);
|
||||
|
||||
@@ -30,7 +30,10 @@ import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||
import org.springframework.util.AlternativeJdkIdGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.IdGenerator;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
@@ -51,10 +54,7 @@ import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class SignatureProvider {
|
||||
/**
|
||||
* 微信平台证书容器 key = 序列号 value = 证书对象
|
||||
*/
|
||||
private static final Map<String, Certificate> CERTIFICATE_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, X509WechatCertificateInfo> CERTIFICATE_MAP = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 加密算法提供方 - BouncyCastle
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ public class SignatureProvider {
|
||||
/**
|
||||
* The Rest operations.
|
||||
*/
|
||||
private final RestOperations restOperations = new RestTemplate();
|
||||
private final RestOperations restOperations;
|
||||
/**
|
||||
* The Wechat meta container.
|
||||
*/
|
||||
@@ -112,6 +112,11 @@ public class SignatureProvider {
|
||||
public SignatureProvider(WechatMetaContainer wechatMetaContainer) {
|
||||
Provider bouncyCastleProvider = new BouncyCastleProvider();
|
||||
Security.addProvider(bouncyCastleProvider);
|
||||
RestTemplate restOperations = new RestTemplate();
|
||||
List<HttpMessageConverter<?>> messageConverters = restOperations.getMessageConverters();
|
||||
messageConverters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2XmlHttpMessageConverter);
|
||||
restOperations.setMessageConverters(messageConverters);
|
||||
this.restOperations = restOperations;
|
||||
this.wechatMetaContainer = wechatMetaContainer;
|
||||
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
|
||||
}
|
||||
@@ -158,7 +163,7 @@ public class SignatureProvider {
|
||||
public String doRequestSign(PrivateKey privateKey, String... orderedComponents) {
|
||||
Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER);
|
||||
signer.initSign(privateKey);
|
||||
final String signatureStr = createSign(orderedComponents);
|
||||
final String signatureStr = createSign(true, orderedComponents);
|
||||
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64Utils.encodeToString(signer.sign());
|
||||
}
|
||||
@@ -176,10 +181,10 @@ public class SignatureProvider {
|
||||
if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
|
||||
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
|
||||
}
|
||||
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);
|
||||
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial).getX509Certificate();
|
||||
|
||||
final String signatureStr = createSign(params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody());
|
||||
Signature signer = Signature.getInstance("SHA256withRSA");
|
||||
final String signatureStr = createSign(true, params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody());
|
||||
Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER);
|
||||
signer.initVerify(certificate);
|
||||
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
@@ -222,8 +227,8 @@ 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);
|
||||
CERTIFICATE_MAP.remove(tenantId);
|
||||
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509", BC_PROVIDER);
|
||||
certificates.forEach(objectNode -> {
|
||||
JsonNode encryptCertificate = objectNode.get("encrypt_certificate");
|
||||
String associatedData = encryptCertificate.get("associated_data").asText();
|
||||
@@ -236,7 +241,11 @@ public class SignatureProvider {
|
||||
try {
|
||||
Certificate certificate = certificateFactory.generateCertificate(inputStream);
|
||||
String responseSerialNo = objectNode.get("serial_no").asText();
|
||||
CERTIFICATE_MAP.put(responseSerialNo, certificate);
|
||||
X509WechatCertificateInfo x509WechatCertificateInfo = new X509WechatCertificateInfo();
|
||||
x509WechatCertificateInfo.setWechatPaySerial(responseSerialNo);
|
||||
x509WechatCertificateInfo.setTenantId(tenantId);
|
||||
x509WechatCertificateInfo.setX509Certificate((X509Certificate) certificate);
|
||||
CERTIFICATE_MAP.put(responseSerialNo, x509WechatCertificateInfo);
|
||||
} catch (CertificateException e) {
|
||||
throw new PayException("An error occurred while generating the wechat v3 certificate, reason : " + e.getMessage());
|
||||
}
|
||||
@@ -254,6 +263,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();
|
||||
@@ -278,38 +296,45 @@ public class SignatureProvider {
|
||||
/**
|
||||
* 对请求敏感字段进行加密
|
||||
*
|
||||
* @param message the message
|
||||
* @param message the message
|
||||
* @param certificate the certificate
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
public X509WechatCertificateInfo getCertificate(){
|
||||
/**
|
||||
* Get certificate x 509 wechat certificate info.
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return the x 509 wechat certificate info
|
||||
*/
|
||||
public X509WechatCertificateInfo getCertificate(String tenantId) {
|
||||
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);
|
||||
return x509WechatCertificateInfo;
|
||||
} catch (Exception e) {
|
||||
log.warn("the wechat certificate is invalid , {}", e.getMessage());
|
||||
// Async?
|
||||
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
|
||||
X509WechatCertificateInfo wechatCertificateInfo = CERTIFICATE_MAP.get(serial);
|
||||
X509Certificate x509Cert = wechatCertificateInfo.getX509Certificate();
|
||||
if (wechatCertificateInfo.getTenantId().equals(tenantId)){
|
||||
try {
|
||||
x509Cert.checkValidity();
|
||||
|
||||
return wechatCertificateInfo;
|
||||
} catch (Exception e) {
|
||||
log.warn("the wechat certificate is invalid , {}", e.getMessage());
|
||||
// Async?
|
||||
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new PayException("failed to obtain wechat pay x509Certificate ");
|
||||
@@ -341,9 +366,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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package cn.felord.payment.wechat.v3;
|
||||
|
||||
import cn.felord.payment.wechat.v2.WechatAllocationApi;
|
||||
import cn.felord.payment.wechat.v2.WechatPayRedpackApi;
|
||||
import cn.felord.payment.wechat.v2.WechatPayTransfersApi;
|
||||
import cn.felord.payment.wechat.v2.WechatV2Client;
|
||||
@@ -65,6 +66,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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合单支付.
|
||||
*
|
||||
@@ -110,7 +123,7 @@ public class WechatApiProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转账到零钱.
|
||||
* 批量转账到零钱.
|
||||
* <p>
|
||||
* 批量转账到零钱提供商户同时向多个用户微信零钱转账的能力。商户可以使用批量转账到零钱用于费用报销、员工福利发放、合作伙伴货款或服务款项支付等场景,提高转账效率。
|
||||
*
|
||||
@@ -165,4 +178,38 @@ public class WechatApiProvider {
|
||||
return new WechatPayTransfersApi(wechatV2Client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付分账,基于V2
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return wechat allocation api
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
public WechatAllocationApi allocationApi(String tenantId) {
|
||||
WechatMetaBean wechatMeta = wechatPayClient.signatureProvider()
|
||||
.wechatMetaContainer()
|
||||
.getWechatMeta(tenantId);
|
||||
WechatV2Client wechatV2Client = new WechatV2Client(wechatMeta);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -77,11 +77,11 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
List<CreateBatchTransferParams.TransferDetailListItem> transferDetailList = createBatchTransferParams.getTransferDetailList();
|
||||
|
||||
SignatureProvider signatureProvider = this.client().signatureProvider();
|
||||
final X509WechatCertificateInfo certificate = signatureProvider.getCertificate();
|
||||
X509WechatCertificateInfo certificate = signatureProvider.getCertificate(this.wechatMetaBean().getTenantId());
|
||||
final X509Certificate x509Certificate = certificate.getX509Certificate();
|
||||
List<CreateBatchTransferParams.TransferDetailListItem> 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();
|
||||
@@ -140,13 +140,13 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_WECHAT, queryBatchTransferDetailParams)
|
||||
.function((type, params) -> {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("batch_id", params.getBatchIdOrOutBatchNo());
|
||||
queryParams.add("detail_id", params.getDetailIdOrOutDetailNo());
|
||||
Map<String, String> 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<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.BATCH_TRANSFER_DETAIL_MCH, queryBatchTransferDetailParams)
|
||||
.function((type, params) -> {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("out_batch_no", params.getBatchIdOrOutBatchNo());
|
||||
queryParams.add("out_detail_no", params.getDetailIdOrOutDetailNo());
|
||||
Map<String, String> 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);
|
||||
})
|
||||
@@ -256,4 +256,150 @@ public class WechatBatchTransferApi extends AbstractApi {
|
||||
Assert.hasText(downloadUrl, "download url has no text");
|
||||
return this.billResource(downloadUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转账明细电子回单受理API
|
||||
* <p>
|
||||
* 受理转账明细电子回单接口,商户通过该接口可以申请受理转账明细单电子回单服务。
|
||||
* <p>
|
||||
* 返回的下载链接可调用{@link #downloadBillResponse(String, String)}下载文件
|
||||
*
|
||||
* @param params the params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> transferElectronic(TransferDetailElectronicParams params) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 查询转账明细电子回单受理结果接口,商户通过该接口可以查询电子回单受理进度信息,
|
||||
* 包括电子回单据信息,电子回单文件的hash值,电子回单文件的下载地址等。
|
||||
* <p>
|
||||
* 返回的下载链接可调用{@link #downloadBillResponse(String, String)}下载文件
|
||||
*
|
||||
* @param params the params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryTransferElectronicResult(TransferDetailElectronicParams params) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.BATCH_TRANSFER_ELECTRONIC_DETAIL, params)
|
||||
.function((type, transferDetailElectronic) -> {
|
||||
MultiValueMap<String, String> 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
|
||||
* <p>
|
||||
* 商户通过此接口可以查询本商户号的账号余额情况。
|
||||
*
|
||||
* @param accountType the account type
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryFundBalance(FundFlowAccountType accountType) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 通过此接口可以查询本商户号指定日期当天24点的账户余额。
|
||||
*
|
||||
* @param queryDayBalanceParams the transfer day balance params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryDayFundBalance(QueryDayBalanceParams queryDayBalanceParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 商户通过本接口查询指定日期内本商户银行来账记录列表。
|
||||
* 列表内包含本商户银行来账相关的业务单号、金额、完成时间等信息,用于查询和核对。
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ol>
|
||||
* <li>如需检索,请在前端缓存所有银行来账记录数据并自行完成检索功能;</li>
|
||||
* <li>调用该接口前,商户需提前开通“来账识别”产品权限;</li>
|
||||
* <li>本接口对可查询的商户范围有所规定,仅支持对本商户进行查询;</li>
|
||||
* <li>本接口仅提供近90天内的银行来账记录查询,且一次只能查询一天,商户需确保查询记录日期在此范围内;</li>
|
||||
* <li>本接口返回的记录字段后续可能会有所扩充,商户需做好接口兼容准备;</li>
|
||||
* <li>单商户单接口最大请求频率不超过50TPS。</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param queryIncomeRecordParams the transfer day balance params
|
||||
* @return the wechat response entity
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryIncomeRecords(QueryIncomeRecordParams queryIncomeRecordParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.BATCH_TRANSFER_FUND_INCOME_RECORDS, queryIncomeRecordParams)
|
||||
.function((type, params) -> {
|
||||
MultiValueMap<String, String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -380,7 +384,7 @@ public class WechatMarketingBusiFavorApi extends AbstractApi {
|
||||
.expand(updateParams.getStockId())
|
||||
.toUri();
|
||||
updateParams.setStockId(null);
|
||||
return Post(uri, updateParams);
|
||||
return Patch(uri, updateParams);
|
||||
})
|
||||
.consumer(wechatResponseEntity::convert)
|
||||
.request();
|
||||
|
||||
@@ -353,9 +353,9 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("appid", v3.getAppId());
|
||||
|
||||
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
||||
pathParams.add("openid", params.getOpenId());
|
||||
pathParams.add("coupon_id", params.getCouponId());
|
||||
Map<String, String> 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()
|
||||
@@ -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<ObjectNode> downloadStockUseFlow(String stockId) {
|
||||
WechatResponseEntity<ObjectNode> 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<ObjectNode> downloadStockRefundFlow(String stockId) {
|
||||
WechatResponseEntity<ObjectNode> 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;
|
||||
}
|
||||
|
||||
@@ -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(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(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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
* <p>
|
||||
* 微信订单支付成功后,商户发起分账请求,将结算后的资金分到分账接收方
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>对同一笔订单最多能发起20次分账请求,每次请求最多分给50个接收方</li>
|
||||
* <li>此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param profitSharingOrder the profit sharing order
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> profitsharingOrders(PartnerProfitSharingOrder profitSharingOrder) {
|
||||
WechatResponseEntity<ObjectNode> 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(this.wechatMetaBean().getTenantId());
|
||||
final X509Certificate x509Certificate = certificate.getX509Certificate();
|
||||
params.setAppid(v3.getAppId());
|
||||
List<Receiver> receivers = params.getReceivers();
|
||||
if (!CollectionUtils.isEmpty(receivers)) {
|
||||
List<Receiver> 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
|
||||
* <p>
|
||||
* 发起分账请求后,可调用此接口查询分账结果
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>发起解冻剩余资金请求后,可调用此接口查询解冻剩余资金的结果</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param queryOrderParams the query order params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryProfitsharingOrder(PartnerQueryOrderParams queryOrderParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.PROFITSHARING_ORDERS_RESULT, queryOrderParams)
|
||||
.function((wechatPayV3Type, params) -> {
|
||||
MultiValueMap<String, String> 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
|
||||
* <p>
|
||||
* 如果订单已经分账,在退款时,可以先调此接口,将已分账的资金从分账接收方的账户回退给分账方,再发起退款
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>分账回退以原分账单为依据,支持多次回退,申请回退总金额不能超过原分账单分给该接收方的金额</li>
|
||||
* <li>此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果</li>
|
||||
* <li>对同一笔分账单最多能发起20次分账回退请求</li>
|
||||
* <li>退款和分账回退没有耦合,分账回退可以先于退款请求,也可以后于退款请求</li>
|
||||
* <li>此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param returnOrdersParams the return orders params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> returnOrders(PartnerReturnOrdersParams returnOrdersParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 商户需要核实回退结果,可调用此接口查询回退结果
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>如果分账回退接口返回状态为处理中,可调用此接口查询回退结果</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param queryReturnOrderParams the query return order params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryReturnOrders(PartnerQueryReturnOrderParams queryReturnOrderParams) {
|
||||
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||
this.client().withType(WechatPayV3Type.PROFITSHARING_RETURN_ORDERS_RESULT, queryReturnOrderParams)
|
||||
.function((wechatPayV3Type, params) -> {
|
||||
MultiValueMap<String, String> 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
|
||||
* <p>
|
||||
* 不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>调用分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户</li>
|
||||
* <li>此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param unfreezeParams the unfreeze params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> unfreeze(PartnerUnfreezeParams unfreezeParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 可调用此接口查询订单剩余待分金额
|
||||
*
|
||||
* @param transactionId the transaction id
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryAmounts(String transactionId) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 可调用此接口查询订单剩余待分金额
|
||||
*
|
||||
* @param subMchid the sub mchid
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryMchConfigs(String subMchid) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 商户发起添加分账接收方请求,建立分账接收方列表。后续可通过发起分账请求,将分账方商户结算后的资金,分到该分账接收方
|
||||
*
|
||||
* @param addReceiversParams the add receivers params
|
||||
* @return wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> addReceivers(PartnerAddReceiversParams addReceiversParams) {
|
||||
WechatResponseEntity<ObjectNode> 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(this.wechatMetaBean().getTenantId());
|
||||
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
|
||||
* <p>
|
||||
* 商户发起删除分账接收方请求。删除后,不支持将分账方商户结算后的资金,分到该分账接收方
|
||||
*
|
||||
* @param delReceiversParams the del receivers params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> deleteReceivers(PartnerDelReceiversParams delReceiversParams) {
|
||||
WechatResponseEntity<ObjectNode> 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;
|
||||
}
|
||||
}
|
||||
@@ -29,15 +29,17 @@ 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;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
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;
|
||||
@@ -69,8 +71,11 @@ public class WechatPayCallback {
|
||||
private final String tenantId;
|
||||
|
||||
static {
|
||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
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)
|
||||
.registerModule(new JavaTimeModule());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +91,24 @@ public class WechatPayCallback {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付分账V2回调.
|
||||
*
|
||||
* @param params the params
|
||||
* @param consumeDataConsumer the consume data consumer
|
||||
* @return the map
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, String> profitSharingCallback(ResponseSignVerifyParams params, Consumer<ProfitSharingConsumeData> consumeDataConsumer) {
|
||||
String data = this.callback(params, EventType.TRANSACTION);
|
||||
ProfitSharingConsumeData consumeData = MAPPER.readValue(data, ProfitSharingConsumeData.class);
|
||||
consumeDataConsumer.accept(consumeData);
|
||||
return response();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付代金券核销回调.
|
||||
*
|
||||
@@ -95,19 +118,16 @@ public class WechatPayCallback {
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> couponCallback(ResponseSignVerifyParams params, Consumer<CouponConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> couponCallback(ResponseSignVerifyParams params, Consumer<CouponConsumeData> consumeDataConsumer) {
|
||||
String data = this.callback(params, EventType.COUPON_USE);
|
||||
CouponConsumeData consumeData = MAPPER.readValue(data, CouponConsumeData.class);
|
||||
consumeDataConsumer.accept(consumeData);
|
||||
Map<String, Object> responseBody = new HashMap<>(2);
|
||||
responseBody.put("code", 200);
|
||||
responseBody.put("message", "SUCCESS");
|
||||
return responseBody;
|
||||
return response();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付成功回调.
|
||||
* 微信支付成功回调,在1.0.8.RELEASE时支持服务商模式支付回调通知
|
||||
* <p>
|
||||
* 无需开发者判断,只有扣款成功微信才会回调此接口
|
||||
*
|
||||
@@ -117,12 +137,11 @@ public class WechatPayCallback {
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> transactionCallback(ResponseSignVerifyParams params, Consumer<TransactionConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> transactionCallback(ResponseSignVerifyParams params, Consumer<TransactionConsumeData> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,12 +155,11 @@ public class WechatPayCallback {
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> combineTransactionCallback(ResponseSignVerifyParams params, Consumer<CombineTransactionConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> combineTransactionCallback(ResponseSignVerifyParams params, Consumer<CombineTransactionConsumeData> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +173,7 @@ public class WechatPayCallback {
|
||||
* @since 1.0.2.RELEASE
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> payscoreUserOrderCallback(ResponseSignVerifyParams params, PayScoreConsumer payScoreConsumer) {
|
||||
public Map<String, String> payscoreUserOrderCallback(ResponseSignVerifyParams params, PayScoreConsumer payScoreConsumer) {
|
||||
CallbackParams callbackParams = resolve(params);
|
||||
String eventType = callbackParams.getEventType();
|
||||
|
||||
@@ -171,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +204,7 @@ public class WechatPayCallback {
|
||||
* @return the map
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> permissionCallback(ResponseSignVerifyParams params, Consumer<PayScoreUserPermissionConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> permissionCallback(ResponseSignVerifyParams params, Consumer<PayScoreUserPermissionConsumeData> consumeDataConsumer) {
|
||||
CallbackParams callbackParams = resolve(params);
|
||||
String eventType = callbackParams.getEventType();
|
||||
boolean closed;
|
||||
@@ -203,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +271,7 @@ public class WechatPayCallback {
|
||||
* @return the map
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> busiFavorReceiveCallback(ResponseSignVerifyParams params, Consumer<BusiFavorReceiveConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> busiFavorReceiveCallback(ResponseSignVerifyParams params, Consumer<BusiFavorReceiveConsumeData> consumeDataConsumer) {
|
||||
CallbackParams callbackParams = resolve(params);
|
||||
String eventType = callbackParams.getEventType();
|
||||
|
||||
@@ -266,7 +283,7 @@ public class WechatPayCallback {
|
||||
BusiFavorReceiveConsumeData consumeData = MAPPER.readValue(data, BusiFavorReceiveConsumeData.class);
|
||||
|
||||
consumeDataConsumer.accept(consumeData);
|
||||
return Collections.singletonMap("code", "SUCCESS");
|
||||
return response();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,10 +293,10 @@ public class WechatPayCallback {
|
||||
*
|
||||
* @param params the params
|
||||
* @param consumeDataConsumer the consume data consumer
|
||||
* @return map
|
||||
* @return map map
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, ?> refundCallback(ResponseSignVerifyParams params, Consumer<RefundConsumeData> consumeDataConsumer) {
|
||||
public Map<String, String> refundCallback(ResponseSignVerifyParams params, Consumer<RefundConsumeData> consumeDataConsumer) {
|
||||
CallbackParams callbackParams = resolve(params);
|
||||
String eventType = callbackParams.getEventType();
|
||||
|
||||
@@ -293,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<String, String> profitsharingCallback(ResponseSignVerifyParams params, Consumer<ProfitsharingConsumeData> profitsharingConsumeDataConsumer) {
|
||||
String callback = this.callback(params, EventType.TRANSACTION);
|
||||
ProfitsharingConsumeData consumeData = MAPPER.readValue(callback, ProfitsharingConsumeData.class);
|
||||
profitsharingConsumeDataConsumer.accept(consumeData);
|
||||
return response();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback.
|
||||
@@ -347,6 +378,18 @@ public class WechatPayCallback {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调应答
|
||||
*
|
||||
* @return response
|
||||
*/
|
||||
private Map<String, String> response() {
|
||||
Map<String, String> responseBody = new HashMap<>(2);
|
||||
responseBody.put("code", "SUCCESS");
|
||||
responseBody.put("message", "SUCCESS");
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 事件类型用于处理回调.
|
||||
@@ -424,7 +467,7 @@ public class WechatPayCallback {
|
||||
COUPON_SEND("COUPON.SEND"),
|
||||
|
||||
/**
|
||||
* 支付成功事件.
|
||||
* 支付成功、分账、分账回退事件.
|
||||
*
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
|
||||
@@ -29,6 +29,7 @@ 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.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
@@ -178,13 +179,14 @@ public class WechatPayClient {
|
||||
WechatRequestEntity<?> wechatRequestEntity = WechatRequestEntity.of(requestEntity, this.responseBodyConsumer);
|
||||
return this.doDownload(this.header(wechatRequestEntity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download string.
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.0.6.RELEASE
|
||||
*/
|
||||
public ResponseEntity<Resource> resource() {
|
||||
protected 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));
|
||||
@@ -253,7 +255,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();
|
||||
@@ -276,7 +280,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +301,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("");
|
||||
}
|
||||
@@ -309,7 +317,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);
|
||||
|
||||
@@ -317,7 +325,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;
|
||||
}
|
||||
@@ -342,7 +352,7 @@ public class WechatPayClient {
|
||||
DefaultResponseErrorHandler errorHandler = new WechatPayResponseErrorHandler();
|
||||
restTemplate.setErrorHandler(errorHandler);
|
||||
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
|
||||
|
||||
messageConverters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2XmlHttpMessageConverter);
|
||||
messageConverters.removeIf(httpMessageConverter -> httpMessageConverter instanceof AllEncompassingFormHttpMessageConverter);
|
||||
messageConverters.add(new ExtensionFormHttpMessageConverter());
|
||||
restTemplate.setMessageConverters(messageConverters);
|
||||
|
||||
@@ -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,14 +65,14 @@ public class WechatPayScoreApi extends AbstractApi {
|
||||
.function((wechatPayV3Type, userServiceStateParams) -> {
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> expandParams = new LinkedMultiValueMap<>();
|
||||
expandParams.add("appid", v3.getAppId());
|
||||
expandParams.add("service_id", params.getServiceId());
|
||||
expandParams.add("openid", params.getOpenId());
|
||||
Map<String, String> 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);
|
||||
})
|
||||
|
||||
@@ -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
|
||||
* <p>
|
||||
* 微信订单支付成功后,商户发起分账请求,将结算后的资金分到分账接收方
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>对同一笔订单最多能发起20次分账请求,每次请求最多分给50个接收方</li>
|
||||
* <li>此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param profitSharingOrder the profit sharing order
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> profitsharingOrders(ProfitSharingOrder profitSharingOrder) {
|
||||
WechatResponseEntity<ObjectNode> 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(this.wechatMetaBean().getTenantId());
|
||||
final X509Certificate x509Certificate = certificate.getX509Certificate();
|
||||
params.setAppid(v3.getAppId());
|
||||
List<Receiver> receivers = params.getReceivers();
|
||||
if (!CollectionUtils.isEmpty(receivers)) {
|
||||
List<Receiver> 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
|
||||
* <p>
|
||||
* 发起分账请求后,可调用此接口查询分账结果
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>发起解冻剩余资金请求后,可调用此接口查询解冻剩余资金的结果</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param queryOrderParams the query order params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryProfitsharingOrder(QueryOrderParams queryOrderParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 如果订单已经分账,在退款时,可以先调此接口,将已分账的资金从分账接收方的账户回退给分账方,再发起退款
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>分账回退以原分账单为依据,支持多次回退,申请回退总金额不能超过原分账单分给该接收方的金额</li>
|
||||
* <li>此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果</li>
|
||||
* <li>对同一笔分账单最多能发起20次分账回退请求</li>
|
||||
* <li>退款和分账回退没有耦合,分账回退可以先于退款请求,也可以后于退款请求</li>
|
||||
* <li>此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param returnOrdersParams the return orders params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> returnOrders(ReturnOrdersParams returnOrdersParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 商户需要核实回退结果,可调用此接口查询回退结果
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>如果分账回退接口返回状态为处理中,可调用此接口查询回退结果</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param queryReturnOrderParams the query return order params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryReturnOrders(QueryReturnOrderParams queryReturnOrderParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户
|
||||
* <p>
|
||||
* 注意:
|
||||
* <ul>
|
||||
* <li>调用分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户</li>
|
||||
* <li>此接口采用异步处理模式,即在接收到商户请求后,优先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param unfreezeParams the unfreeze params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> unfreeze(UnfreezeParams unfreezeParams) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 可调用此接口查询订单剩余待分金额
|
||||
*
|
||||
* @param transactionId the transaction id
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> queryAmounts(String transactionId) {
|
||||
WechatResponseEntity<ObjectNode> 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
|
||||
* <p>
|
||||
* 商户发起添加分账接收方请求,建立分账接收方列表。后续可通过发起分账请求,将分账方商户结算后的资金,分到该分账接收方
|
||||
*
|
||||
* @param addReceiversParams the add receivers params
|
||||
* @return wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> addReceivers(AddReceiversParams addReceiversParams) {
|
||||
WechatResponseEntity<ObjectNode> 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(this.wechatMetaBean().getTenantId());
|
||||
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
|
||||
* <p>
|
||||
* 商户发起删除分账接收方请求。删除后,不支持将分账方商户结算后的资金,分到该分账接收方
|
||||
*
|
||||
* @param delReceiversParams the del receivers params
|
||||
* @return the wechat response entity
|
||||
*/
|
||||
public WechatResponseEntity<ObjectNode> deleteReceivers(DelReceiversParams delReceiversParams) {
|
||||
WechatResponseEntity<ObjectNode> 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;
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@ public class X509WechatCertificateInfo {
|
||||
* wechatPaySerial
|
||||
*/
|
||||
private String wechatPaySerial;
|
||||
/**
|
||||
* tenantId
|
||||
* @since 1.0.12.RELEASE
|
||||
*/
|
||||
private String tenantId;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
*
|
||||
* 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;
|
||||
|
||||
import cn.felord.payment.wechat.v2.model.allocation.Receiver;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 微信支付分账通知参数
|
||||
*
|
||||
* @author wangzecheng
|
||||
* @since 1.0.10.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public class ProfitSharingConsumeData {
|
||||
|
||||
/**
|
||||
* 直连商户号.
|
||||
* <p>
|
||||
* 直连模式分账发起和出资商户
|
||||
*/
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 微信分账/回退单号.
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 商户分账/回退单号.
|
||||
* <p>
|
||||
* 分账方系统内部的分账/回退单号
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* 分账接收方.
|
||||
* <p>
|
||||
* 分账接收方对象
|
||||
*/
|
||||
private Receiver receiver;
|
||||
|
||||
/**
|
||||
* 成功时间.
|
||||
* <p>
|
||||
* Rfc3339标准
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private LocalDateTime successTime;
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import cn.felord.payment.wechat.enumeration.RefundStatus;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 微信支付退款结果通知解密
|
||||
@@ -59,12 +59,17 @@ public class RefundConsumeData {
|
||||
/**
|
||||
* 退款成功时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "GMT+8")
|
||||
private OffsetDateTime successTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private LocalDateTime successTime;
|
||||
/**
|
||||
* 退款入账账户
|
||||
*/
|
||||
private String userReceivedAccount;
|
||||
/**
|
||||
* 金额信息
|
||||
*/
|
||||
private Amount amount;
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付退款金额信息
|
||||
@@ -85,7 +90,6 @@ public class RefundConsumeData {
|
||||
/**
|
||||
* 用户实际支付金额,单位为分
|
||||
*/
|
||||
|
||||
private Integer payerTotal;
|
||||
/**
|
||||
* 退款给用户的金额,单位为分,不包含所有优惠券金额
|
||||
|
||||
@@ -75,6 +75,12 @@ public class RefundParams {
|
||||
*/
|
||||
@Data
|
||||
public static class RefundAmount {
|
||||
/**
|
||||
* 退款出资账户及金额
|
||||
*
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
private List<RefundForm> form;
|
||||
/**
|
||||
* 原订单金额,币种的最小单位,只能为整数,不能超过原订单支付金额。
|
||||
*/
|
||||
@@ -89,4 +95,41 @@ public class RefundParams {
|
||||
private Integer refund;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款出资账户及金额
|
||||
* <p>
|
||||
* 退款需要从指定账户出资时,传递此参数指定出资金额(币种的最小单位,只能为整数)。
|
||||
* <p>
|
||||
* 同时指定多个账户出资退款的使用场景需要满足以下条件:
|
||||
* <ol>
|
||||
* <li>未开通退款支出分离产品功能;</li>
|
||||
* <li>订单属于分账订单,且分账处于待分账或分账中状态。</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 参数传递需要满足条件:
|
||||
* <ol>
|
||||
* <li>基本账户可用余额出资金额与基本账户不可用余额出资金额之和等于退款金额;</li>
|
||||
* <li>账户类型不能重复。</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 上述任一条件不满足将返回错误
|
||||
*
|
||||
* @author felord.cn
|
||||
* @since 1.0.11.RELEASE
|
||||
*/
|
||||
@Data
|
||||
public static class RefundForm {
|
||||
/**
|
||||
* 出资账户类型
|
||||
* <p>
|
||||
* {@code AVAILABLE} 可用余额
|
||||
* {@code UNAVAILABLE} 不可用余额
|
||||
*/
|
||||
private String account;
|
||||
/**
|
||||
* 对应账户出资金额
|
||||
*/
|
||||
private String amount;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.LocalDateTime;
|
||||
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 LocalDateTime successTime;
|
||||
/**
|
||||
* 在 1.0.0.RELEASE 直接返回了枚举字符串,1.0.2.RELEASE 中变更为枚举
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
/**
|
||||
* 本次查询偏移量,选填
|
||||
* <p>
|
||||
* 表示该次请求资源的起始位置,从0开始计数。调用方选填,默认为0。offset为20,limit为100时,查询第20-119条数据
|
||||
*/
|
||||
private Integer offset;
|
||||
/**
|
||||
* 本次请求最大查询条数,必填
|
||||
* <p>
|
||||
* 非0非负的整数,该次请求可返回的最大资源条数,最大支持100条。
|
||||
*/
|
||||
private Integer limit;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
/**
|
||||
* 商家转账批次单号,选填。
|
||||
* <p>
|
||||
* 需要电子回单的批量转账明细单所在的转账批次单号,该单号为商户申请转账时生成的商户单号。
|
||||
* 受理类型为{@code BATCH_TRANSFER}时该单号必填,否则该单号留空。
|
||||
*/
|
||||
private String outBatchNo;
|
||||
/**
|
||||
* 商家转账明细单号,必填。
|
||||
* <ul>
|
||||
* <li>受理类型为{@code BATCH_TRANSFER}时填写商家批量转账明细单号。</li>
|
||||
* <li>受理类型为{@code TRANSFER_TO_POCKET}或{@code TRANSFER_TO_BANK}时填写商家转账单号。</li>
|
||||
* </ul>
|
||||
*/
|
||||
private String outDetailNo;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package cn.felord.payment.wechat.v3.model.busifavor;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 商家券领券事件回调通知解密
|
||||
@@ -50,7 +50,7 @@ public class BusiFavorReceiveConsumeData {
|
||||
* 发放时间 rfc 3339 yyyy-MM-ddTHH:mm:ss+TIMEZONE
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private OffsetDateTime sendTime;
|
||||
private LocalDateTime 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
|
||||
*/
|
||||
|
||||
@@ -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> promotionDetail;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class CompleteServiceOrderParams {
|
||||
* false:不分账,默认:false
|
||||
* true:分账。
|
||||
*/
|
||||
private Boolean profitSharing = Boolean.TRUE;
|
||||
private Boolean profitSharing = Boolean.FALSE;
|
||||
/**
|
||||
* 订单优惠标记,选填
|
||||
* <p>
|
||||
|
||||
@@ -98,6 +98,10 @@ public class PayScoreUserPaidConsumeData {
|
||||
* The Time range.
|
||||
*/
|
||||
private TimeRange timeRange;
|
||||
/**
|
||||
* stateDescription
|
||||
*/
|
||||
private String stateDescription;
|
||||
/**
|
||||
* The Total amount.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
/**
|
||||
* 分账个人接收方姓名,选填
|
||||
* <p>
|
||||
* 分账接收方类型是{@code MERCHANT_ID}时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名 分账接收方类型是{@code PERSONAL_OPENID}时,是个人姓名(选传,传则校验)
|
||||
* <ol>
|
||||
* <li>分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明</li>
|
||||
* <li>使用微信支付平台证书中的公钥</li>
|
||||
* <li>使用RSAES-OAEP算法进行加密</li>
|
||||
* <li>将请求中HTTP头部的Wechatpay-Serial设置为证书序列号</li>
|
||||
* </ol>
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 与分账方的关系类型,必填
|
||||
*/
|
||||
private RelationType relationType;
|
||||
/**
|
||||
* 自定义的分账关系,选填
|
||||
*/
|
||||
private String customRelation;
|
||||
|
||||
/**
|
||||
* 子商户与接收方的关系
|
||||
*/
|
||||
public enum RelationType {
|
||||
/**
|
||||
* 门店.
|
||||
*/
|
||||
STORE,
|
||||
/**
|
||||
* 员工.
|
||||
*/
|
||||
STAFF,
|
||||
/**
|
||||
* 店主.
|
||||
*/
|
||||
STORE_OWNER,
|
||||
/**
|
||||
* 合作伙伴.
|
||||
*/
|
||||
PARTNER,
|
||||
/**
|
||||
* 总部.
|
||||
*/
|
||||
HEADQUARTER,
|
||||
/**
|
||||
* 品牌方.
|
||||
*/
|
||||
BRAND,
|
||||
/**
|
||||
* 分销商.
|
||||
*/
|
||||
DISTRIBUTOR,
|
||||
/**
|
||||
* 用户.
|
||||
*/
|
||||
USER,
|
||||
/**
|
||||
* 供应商.
|
||||
*/
|
||||
SUPPLIER,
|
||||
/**
|
||||
* 自定义.
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,选填
|
||||
* <p>
|
||||
* 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填
|
||||
*/
|
||||
private String subAppid;
|
||||
/**
|
||||
* 分账接收方类型,必填
|
||||
*/
|
||||
private ReceiverType type;
|
||||
/**
|
||||
* 分账接收方帐号,必填
|
||||
*/
|
||||
private String account;
|
||||
/**
|
||||
* 分账个人接收方姓名,选填
|
||||
* <p>
|
||||
* 分账接收方类型是{@code MERCHANT_ID}时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名 分账接收方类型是{@code PERSONAL_OPENID}时,是个人姓名(选传,传则校验)
|
||||
* <ol>
|
||||
* <li>分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明</li>
|
||||
* <li>使用微信支付平台证书中的公钥</li>
|
||||
* <li>使用RSAES-OAEP算法进行加密</li>
|
||||
* <li>将请求中HTTP头部的Wechatpay-Serial设置为证书序列号</li>
|
||||
* </ol>
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 与分账方的关系类型,必填
|
||||
*/
|
||||
private RelationType relationType;
|
||||
/**
|
||||
* 自定义的分账关系,选填
|
||||
*/
|
||||
private String customRelation;
|
||||
|
||||
/**
|
||||
* 子商户与接收方的关系
|
||||
*/
|
||||
public enum RelationType {
|
||||
/**
|
||||
* 门店.
|
||||
*/
|
||||
STORE,
|
||||
/**
|
||||
* 员工.
|
||||
*/
|
||||
STAFF,
|
||||
/**
|
||||
* 店主.
|
||||
*/
|
||||
STORE_OWNER,
|
||||
/**
|
||||
* 合作伙伴.
|
||||
*/
|
||||
PARTNER,
|
||||
/**
|
||||
* 总部.
|
||||
*/
|
||||
HEADQUARTER,
|
||||
/**
|
||||
* 品牌方.
|
||||
*/
|
||||
BRAND,
|
||||
/**
|
||||
* 分销商.
|
||||
*/
|
||||
DISTRIBUTOR,
|
||||
/**
|
||||
* 用户.
|
||||
*/
|
||||
USER,
|
||||
/**
|
||||
* 供应商.
|
||||
*/
|
||||
SUPPLIER,
|
||||
/**
|
||||
* 自定义.
|
||||
*/
|
||||
CUSTOM
|
||||
}
|
||||
}
|
||||
@@ -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,选填
|
||||
* <p>
|
||||
* 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填
|
||||
*/
|
||||
private String subAppid;
|
||||
/**
|
||||
* 分账接收方类型,必填
|
||||
*/
|
||||
private ReceiverType type;
|
||||
/**
|
||||
* 分账接收方帐号,必填
|
||||
*/
|
||||
private String account;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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,选填
|
||||
* <p>
|
||||
* 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填
|
||||
*/
|
||||
private String subAppid;
|
||||
/**
|
||||
* 微信订单号,必填
|
||||
*/
|
||||
private String transactionId;
|
||||
/**
|
||||
* 商户分账单号,必填
|
||||
* <p>
|
||||
* 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。
|
||||
* 只能是数字、大小写字母_-|*@
|
||||
*/
|
||||
private String outOrderNo;
|
||||
/**
|
||||
* 分账接收方列表,选填
|
||||
* <p>
|
||||
* 可以设置出资商户作为分账接受方,最多可有50个分账接收方
|
||||
*/
|
||||
private List<Receiver> receivers;
|
||||
/**
|
||||
* 是否解冻剩余未分资金,必填
|
||||
* <ol>
|
||||
* <li>如果为{@code true},该笔订单剩余未分账的金额会解冻回分账方商户;</li>
|
||||
* <li>如果为{@code false},该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。</li>
|
||||
* </ol>
|
||||
*/
|
||||
private Boolean unfreezeUnsplit;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
* 服务商商户号.
|
||||
* <p>
|
||||
* 服务商模式分账发起商户
|
||||
*/
|
||||
private String mchid;
|
||||
/**
|
||||
* 子商户号
|
||||
* <p>
|
||||
* 服务商模式分账出资商户
|
||||
*/
|
||||
private String subMchid;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 微信分账/回退单号.
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 商户分账/回退单号.
|
||||
* <p>
|
||||
* 分账方系统内部的分账/回退单号
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* 分账接收方.
|
||||
* <p>
|
||||
* 分账接收方对象
|
||||
*/
|
||||
private List<Receiver> receivers;
|
||||
|
||||
/**
|
||||
* 成功时间.
|
||||
* <p>
|
||||
* Rfc3339标准
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private LocalDateTime successTime;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
/**
|
||||
* 回退商户号,必填
|
||||
* <p>
|
||||
* 分账回退的出资商户,只能对原分账请求中成功分给商户接收方进行回退
|
||||
*/
|
||||
private String returnMchid;
|
||||
/**
|
||||
* 回退金额,必填
|
||||
* <p>
|
||||
* 需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额
|
||||
*/
|
||||
private Integer amount;
|
||||
/**
|
||||
* 回退描述,必填
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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;
|
||||
/**
|
||||
* 商户分账单号,必填
|
||||
* <p>
|
||||
* 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。
|
||||
* 只能是数字、大小写字母_-|*@
|
||||
*/
|
||||
private String outOrderNo;
|
||||
/**
|
||||
* 分账接收方列表,选填
|
||||
* <p>
|
||||
* 可以设置出资商户作为分账接受方,最多可有50个分账接收方
|
||||
*/
|
||||
private List<Receiver> receivers;
|
||||
/**
|
||||
* 是否解冻剩余未分资金,必填
|
||||
* <ol>
|
||||
* <li>如果为{@code true},该笔订单剩余未分账的金额会解冻回分账方商户;</li>
|
||||
* <li>如果为{@code false},该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。</li>
|
||||
* </ol>
|
||||
*/
|
||||
private Boolean unfreezeUnsplit;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
* 直连商户号.
|
||||
* <p>
|
||||
* 直连模式分账发起和出资商户
|
||||
*/
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* 微信订单号.
|
||||
* <p>
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 微信分账/回退单号.
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 商户分账/回退单号.
|
||||
* <p>
|
||||
* 分账方系统内部的分账/回退单号
|
||||
*/
|
||||
private String outOrderNo;
|
||||
|
||||
/**
|
||||
* 分账接收方.
|
||||
* <p>
|
||||
* 分账接收方对象
|
||||
*/
|
||||
private List<Receiver> receivers;
|
||||
|
||||
/**
|
||||
* 成功时间.
|
||||
* <p>
|
||||
* Rfc3339标准
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX", timezone = "GMT+8")
|
||||
private LocalDateTime successTime;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
/**
|
||||
* 分账个人接收方姓名,选填
|
||||
* <p>
|
||||
* 在接收方类型为个人的时可选填,若有值,会检查与 name 是否实名匹配,不匹配会拒绝分账请求
|
||||
* <ol>
|
||||
* <li>分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明</li>
|
||||
* <li>使用微信支付平台证书中的公钥</li>
|
||||
* <li>使用RSAES-OAEP算法进行加密</li>
|
||||
* <li>将请求中HTTP头部的Wechatpay-Serial设置为证书序列号</li>
|
||||
* </ol>
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分账金额,必填
|
||||
* <p>
|
||||
* 单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额
|
||||
*/
|
||||
private Integer amount;
|
||||
/**
|
||||
* 分账的原因描述,必填。分账账单中需要体现
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -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;
|
||||
/**
|
||||
* 回退商户号,必填
|
||||
* <p>
|
||||
* 分账回退的出资商户,只能对原分账请求中成功分给商户接收方进行回退
|
||||
*/
|
||||
private String returnMchid;
|
||||
/**
|
||||
* 回退金额,必填
|
||||
* <p>
|
||||
* 需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额
|
||||
*/
|
||||
private Integer amount;
|
||||
/**
|
||||
* 回退描述,必填
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>cn.felord</groupId>
|
||||
<artifactId>payment-spring-boot</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>payment-spring-boot-starter</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
<packaging>jar</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
91
pom.xml
91
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.7.RELEASE</version>
|
||||
<version>1.0.12.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -30,12 +30,35 @@
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<tag>payment-spring-boot-1.0.7.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>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
<!-- Deployment profile (required so these plugins are only used when deploying) -->
|
||||
<profile>
|
||||
<id>deploy</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- GPG plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<modules>
|
||||
<module>payment-spring-boot-autoconfigure</module>
|
||||
@@ -53,14 +76,13 @@
|
||||
<oss-starter.version>1.0.0.RELEASE</oss-starter.version>
|
||||
<lombok.verison>1.18.12</lombok.verison>
|
||||
<jackson.version>2.9.10</jackson.version>
|
||||
<bcprov.version>1.66</bcprov.version>
|
||||
<bcprov.version>1.67</bcprov.version>
|
||||
<jackson.version>2.11.4</jackson.version>
|
||||
<httpclient.version>4.5.13</httpclient.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<id>ossrh</id>
|
||||
<name>Nexus Release Repository</name>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
|
||||
</repository>
|
||||
@@ -73,31 +95,15 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
@@ -118,16 +124,6 @@
|
||||
<artifactId>payment-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -173,10 +169,18 @@
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- Prevent `gpg` from using pinentry programs -->
|
||||
<gpgArguments>
|
||||
<arg>--pinentry-mode</arg>
|
||||
<arg>loopback</arg>
|
||||
</gpgArguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
@@ -186,25 +190,14 @@
|
||||
<version>1.6.8</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>sonatype-nexus-staging</serverId>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>false</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user