mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-13 21:33:41 +08:00
微信支付完善
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
# 移动支付 Spring Boot 组件 多租户版
|
# 移动支付 Spring Boot 组件
|
||||||
为了满足业务中出现app支付、公众号支付、小程序支付等多appid并存的场景,对原有的进行了增强开发出了多租户版本。
|
为了满足业务中出现app支付、公众号支付、小程序支付等多appid并存的场景,对原有的进行了增强开发出了多租户版本。
|
||||||
|
|
||||||
## 支持类型
|
## 支持类型
|
||||||
|
|
||||||
- [x] **微信支付V3** 全量支持。
|
- [x] **微信支付V3** 全量支持,并支持多租户。
|
||||||
- [x] **支付宝** 提供所有实现,具体以签约项目为准。
|
- [x] **支付宝** 提供所有实现,具体以签约项目为准。
|
||||||
## 进度
|
## 进度
|
||||||
- 微信支付营销-代金券 `WechatMarketingFavorApi` 100%
|
- 微信支付营销-代金券 `WechatMarketingFavorApi` 100%
|
||||||
- 微信支付 支付功能 `WechatPayApi` app预支付
|
- 微信支付 支付功能 `WechatPayApi` 100%
|
||||||
|
|
||||||
## 采用技术
|
## 采用技术
|
||||||
- Spring
|
- Spring
|
||||||
|
|||||||
@@ -73,15 +73,4 @@ public class WechatPayConfiguration {
|
|||||||
public WechatApiProvider wechatApiProvider(WechatPayClient wechatPayClient){
|
public WechatApiProvider wechatApiProvider(WechatPayClient wechatPayClient){
|
||||||
return new WechatApiProvider(wechatPayClient);
|
return new WechatApiProvider(wechatPayClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信支付回调工具.
|
|
||||||
*
|
|
||||||
* @param signatureProvider the signature provider
|
|
||||||
* @return the wechat pay callback
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public WechatPayCallback wechatPayCallback(SignatureProvider signatureProvider) {
|
|
||||||
return new WechatPayCallback(signatureProvider);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package cn.felord.payment.wechat.enumeration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信侧返回交易状态
|
||||||
|
*
|
||||||
|
* @author Dax
|
||||||
|
* @since 11:37
|
||||||
|
*/
|
||||||
|
public enum TradeState {
|
||||||
|
/**
|
||||||
|
* 支付成功
|
||||||
|
*/
|
||||||
|
SUCCESS,
|
||||||
|
/**
|
||||||
|
* 转入退款
|
||||||
|
*/
|
||||||
|
REFUND,
|
||||||
|
/**
|
||||||
|
* 未支付
|
||||||
|
*/
|
||||||
|
NOTPAY,
|
||||||
|
/**
|
||||||
|
* 已关闭
|
||||||
|
*/
|
||||||
|
CLOSED,
|
||||||
|
/**
|
||||||
|
* 已撤销(付款码支付)
|
||||||
|
*/
|
||||||
|
REVOKED,
|
||||||
|
/**
|
||||||
|
* 用户支付中(付款码支付)
|
||||||
|
*/
|
||||||
|
USERPAYING,
|
||||||
|
/**
|
||||||
|
* 支付失败(其他原因,如银行返回失败)
|
||||||
|
*/
|
||||||
|
PAYERROR,
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package cn.felord.payment.wechat.enumeration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信侧返回交易类型
|
||||||
|
*
|
||||||
|
* @author Dax
|
||||||
|
* @since 11:34
|
||||||
|
*/
|
||||||
|
public enum TradeType {
|
||||||
|
/**
|
||||||
|
* 公众号支付
|
||||||
|
*/
|
||||||
|
JSAPI,
|
||||||
|
/**
|
||||||
|
* 扫码支付
|
||||||
|
*/
|
||||||
|
NATIVE,
|
||||||
|
/**
|
||||||
|
* APP支付
|
||||||
|
*/
|
||||||
|
APP,
|
||||||
|
/**
|
||||||
|
* 付款码支付
|
||||||
|
*/
|
||||||
|
MICROPAY,
|
||||||
|
/**
|
||||||
|
* H5支付
|
||||||
|
*/
|
||||||
|
MWEB,
|
||||||
|
/**
|
||||||
|
* 刷脸支付
|
||||||
|
*/
|
||||||
|
FACEPAY,
|
||||||
|
}
|
||||||
@@ -38,6 +38,10 @@ public enum WechatPayV3Type {
|
|||||||
* H5支付.
|
* H5支付.
|
||||||
*/
|
*/
|
||||||
MWEB(HttpMethod.POST, "%s/v3/pay/transactions/h5"),
|
MWEB(HttpMethod.POST, "%s/v3/pay/transactions/h5"),
|
||||||
|
/**
|
||||||
|
* 关闭订单.
|
||||||
|
*/
|
||||||
|
CLOSE(HttpMethod.POST, "%s/v3/pay/transactions/out-trade-no/{out_trade_no}/close"),
|
||||||
/**
|
/**
|
||||||
* 微信支付订单号查询.
|
* 微信支付订单号查询.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
package cn.felord.payment.wechat.v3;
|
package cn.felord.payment.wechat.v3;
|
||||||
|
|
||||||
import cn.felord.payment.PayException;
|
import cn.felord.payment.PayException;
|
||||||
|
import cn.felord.payment.wechat.WechatPayProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The type Abstract api.
|
||||||
|
*
|
||||||
* @author Dax
|
* @author Dax
|
||||||
* @since 18:23
|
* @since 18 :23
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractApi {
|
public abstract class AbstractApi {
|
||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
@@ -19,43 +23,94 @@ public abstract class AbstractApi {
|
|||||||
private final String tenantId;
|
private final String tenantId;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Abstract api.
|
||||||
public AbstractApi(WechatPayClient wechatPayClient,String tenantId) {
|
*
|
||||||
|
* @param wechatPayClient the wechat pay client
|
||||||
|
* @param tenantId the tenant id
|
||||||
|
*/
|
||||||
|
public AbstractApi(WechatPayClient wechatPayClient, String tenantId) {
|
||||||
this.mapper = new ObjectMapper();
|
this.mapper = new ObjectMapper();
|
||||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
this.wechatPayClient = wechatPayClient;
|
this.wechatPayClient = wechatPayClient;
|
||||||
|
Assert.hasText(tenantId, "tenantId is required");
|
||||||
if (!container().getTenantIds().contains(tenantId)) {
|
if (!container().getTenantIds().contains(tenantId)) {
|
||||||
throw new PayException("tenantId is not in wechatMetaContainer ");
|
throw new PayException("tenantId is not in wechatMetaContainer");
|
||||||
}
|
}
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets mapper.
|
||||||
|
*
|
||||||
|
* @return the mapper
|
||||||
|
*/
|
||||||
public ObjectMapper getMapper() {
|
public ObjectMapper getMapper() {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client wechat pay client.
|
||||||
|
*
|
||||||
|
* @return the wechat pay client
|
||||||
|
*/
|
||||||
public WechatPayClient client() {
|
public WechatPayClient client() {
|
||||||
return wechatPayClient;
|
return wechatPayClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tenant id string.
|
||||||
|
*
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
public String tenantId() {
|
public String tenantId() {
|
||||||
return tenantId;
|
return tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container wechat meta container.
|
||||||
|
*
|
||||||
|
* @return the wechat meta container
|
||||||
|
*/
|
||||||
public WechatMetaContainer container() {
|
public WechatMetaContainer container() {
|
||||||
return wechatPayClient.signatureProvider().wechatMetaContainer();
|
return wechatPayClient.signatureProvider().wechatMetaContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RequestEntity<?> post(URI uri, Object params,String tenantId) {
|
/**
|
||||||
|
* Wechat meta bean wechat meta bean.
|
||||||
|
*
|
||||||
|
* @return the wechat meta bean
|
||||||
|
*/
|
||||||
|
public WechatMetaBean wechatMetaBean() {
|
||||||
|
return container().getWechatMeta(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post request entity.
|
||||||
|
*
|
||||||
|
* @param uri the uri
|
||||||
|
* @param params the params
|
||||||
|
* @return the request entity
|
||||||
|
*/
|
||||||
|
protected RequestEntity<?> Post(URI uri, Object params) {
|
||||||
try {
|
try {
|
||||||
return RequestEntity.post(uri).header("Pay-TenantId",tenantId)
|
return RequestEntity.post(uri).header("Pay-TenantId", tenantId)
|
||||||
.body(mapper.writeValueAsString(params));
|
.body(mapper.writeValueAsString(params));
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new PayException("wechat app pay json failed");
|
throw new PayException("wechat app pay json failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get request entity.
|
||||||
|
*
|
||||||
|
* @param uri the uri
|
||||||
|
* @return the request entity
|
||||||
|
*/
|
||||||
|
protected RequestEntity<?> Get(URI uri) {
|
||||||
|
return RequestEntity.get(uri).header("Pay-TenantId", tenantId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,51 @@
|
|||||||
package cn.felord.payment.wechat.v3;
|
package cn.felord.payment.wechat.v3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The type Wechat api provider.
|
||||||
|
*
|
||||||
* @author Dax
|
* @author Dax
|
||||||
* @since 17:32
|
* @since 17 :32
|
||||||
*/
|
*/
|
||||||
public class WechatApiProvider {
|
public class WechatApiProvider {
|
||||||
private final WechatPayClient wechatPayClient;
|
private final WechatPayClient wechatPayClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Wechat api provider.
|
||||||
|
*
|
||||||
|
* @param wechatPayClient the wechat pay client
|
||||||
|
*/
|
||||||
public WechatApiProvider(WechatPayClient wechatPayClient) {
|
public WechatApiProvider(WechatPayClient wechatPayClient) {
|
||||||
this.wechatPayClient = wechatPayClient;
|
this.wechatPayClient = wechatPayClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代金券.
|
||||||
|
*
|
||||||
|
* @param tenantId the tenant id
|
||||||
|
* @return the wechat marketing favor api
|
||||||
|
*/
|
||||||
public WechatMarketingFavorApi favorApi(String tenantId){
|
public WechatMarketingFavorApi favorApi(String tenantId){
|
||||||
return new WechatMarketingFavorApi(this.wechatPayClient,tenantId);
|
return new WechatMarketingFavorApi(this.wechatPayClient,tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付.
|
||||||
|
*
|
||||||
|
* @param tenantId the tenant id
|
||||||
|
* @return the wechat pay api
|
||||||
|
*/
|
||||||
public WechatPayApi payApi(String tenantId){
|
public WechatPayApi payApi(String tenantId){
|
||||||
return new WechatPayApi(wechatPayClient,tenantId);
|
return new WechatPayApi(wechatPayClient,tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调.
|
||||||
|
*
|
||||||
|
* @param tenantId the tenant id
|
||||||
|
* @return the wechat pay callback
|
||||||
|
*/
|
||||||
|
public WechatPayCallback callback(String tenantId){
|
||||||
|
return new WechatPayCallback(wechatPayClient.signatureProvider(),tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cn.felord.payment.wechat.v3;
|
package cn.felord.payment.wechat.v3;
|
||||||
|
|
||||||
import cn.felord.payment.PayException;
|
|
||||||
import cn.felord.payment.wechat.WechatPayProperties;
|
import cn.felord.payment.wechat.WechatPayProperties;
|
||||||
import cn.felord.payment.wechat.enumeration.StockStatus;
|
import cn.felord.payment.wechat.enumeration.StockStatus;
|
||||||
import cn.felord.payment.wechat.enumeration.WeChatServer;
|
import cn.felord.payment.wechat.enumeration.WeChatServer;
|
||||||
@@ -59,14 +58,14 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> createStocksFunction(WechatPayV3Type type, StocksCreateParams params) {
|
private RequestEntity<?> createStocksFunction(WechatPayV3Type type, StocksCreateParams params) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
String mchId = v3.getMchId();
|
String mchId = v3.getMchId();
|
||||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.build()
|
.build()
|
||||||
.toUri();
|
.toUri();
|
||||||
params.setBelongMerchant(mchId);
|
params.setBelongMerchant(mchId);
|
||||||
return post(uri, params, tenantId());
|
return Post(uri, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,7 +101,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
|
|
||||||
private RequestEntity<?> sendStocksFunction(WechatPayV3Type type, StocksSendParams params) {
|
private RequestEntity<?> sendStocksFunction(WechatPayV3Type type, StocksSendParams params) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
params.setAppid(v3.getApp().getAppId());
|
params.setAppid(v3.getApp().getAppId());
|
||||||
params.setStockCreatorMchid(v3.getMchId());
|
params.setStockCreatorMchid(v3.getMchId());
|
||||||
@@ -111,7 +110,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.expand(params.getOpenid())
|
.expand(params.getOpenid())
|
||||||
.toUri();
|
.toUri();
|
||||||
params.setOpenid(null);
|
params.setOpenid(null);
|
||||||
return post(uri, params, tenantId());
|
return Post(uri, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,7 +144,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> startAndRestartAndPauseStockFunction(WechatPayV3Type type, String stockId) {
|
private RequestEntity<?> startAndRestartAndPauseStockFunction(WechatPayV3Type type, String stockId) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
String mchId = v3.getMchId();
|
String mchId = v3.getMchId();
|
||||||
Map<String, String> body = new HashMap<>();
|
Map<String, String> body = new HashMap<>();
|
||||||
body.put("stock_creator_mchid", mchId);
|
body.put("stock_creator_mchid", mchId);
|
||||||
@@ -154,7 +153,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.build()
|
.build()
|
||||||
.expand(stockId)
|
.expand(stockId)
|
||||||
.toUri();
|
.toUri();
|
||||||
return post(uri, body, tenantId());
|
return Post(uri, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,7 +178,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("offset", String.valueOf(params.getOffset()));
|
queryParams.add("offset", String.valueOf(params.getOffset()));
|
||||||
queryParams.add("limit", String.valueOf(params.getLimit()));
|
queryParams.add("limit", String.valueOf(params.getLimit()));
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
queryParams.add("stock_creator_mchid", v3.getMchId());
|
queryParams.add("stock_creator_mchid", v3.getMchId());
|
||||||
LocalDateTime createStartTime = params.getCreateStartTime();
|
LocalDateTime createStartTime = params.getCreateStartTime();
|
||||||
@@ -211,7 +210,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
URI uri = uriComponents
|
URI uri = uriComponents
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,8 +230,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
|
|
||||||
private RequestEntity<?> stockDetailFunction(WechatPayV3Type type, String stockId) {
|
private RequestEntity<?> stockDetailFunction(WechatPayV3Type type, String stockId) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("stock_creator_mchid", v3.getMchId());
|
queryParams.add("stock_creator_mchid", v3.getMchId());
|
||||||
@@ -242,7 +240,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.build()
|
.build()
|
||||||
.expand(stockId)
|
.expand(stockId)
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +262,11 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
private RequestEntity<?> couponDetailFunction(WechatPayV3Type type, CouponDetailsQueryParams params) {
|
private RequestEntity<?> couponDetailFunction(WechatPayV3Type type, CouponDetailsQueryParams params) {
|
||||||
|
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("appid", v3.getApp().getAppId());
|
queryParams.add("appid", v3.getApp().getAppId());
|
||||||
|
|
||||||
|
|
||||||
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
||||||
pathParams.add("openid", params.getOpenId());
|
pathParams.add("openid", params.getOpenId());
|
||||||
pathParams.add("coupon_id", params.getCouponId());
|
pathParams.add("coupon_id", params.getCouponId());
|
||||||
@@ -279,7 +275,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.build()
|
.build()
|
||||||
.expand(pathParams)
|
.expand(pathParams)
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +328,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
private RequestEntity<?> queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) {
|
private RequestEntity<?> queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) {
|
||||||
|
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("appid", v3.getApp().getAppId());
|
queryParams.add("appid", v3.getApp().getAppId());
|
||||||
@@ -363,7 +359,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.build()
|
.build()
|
||||||
.expand(params.getOpenId())
|
.expand(params.getOpenId())
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +402,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
.build()
|
.build()
|
||||||
.expand(stockId)
|
.expand(stockId)
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -467,7 +463,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> setMarketingFavorCallbackFunction(WechatPayV3Type type, String notifyUrl) {
|
private RequestEntity<?> setMarketingFavorCallbackFunction(WechatPayV3Type type, String notifyUrl) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
Map<String, Object> body = new HashMap<>(3);
|
Map<String, Object> body = new HashMap<>(3);
|
||||||
body.put("mchid", v3.getMchId());
|
body.put("mchid", v3.getMchId());
|
||||||
@@ -476,7 +472,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.build()
|
.build()
|
||||||
.toUri();
|
.toUri();
|
||||||
return post(uri, body, tenantId());
|
return Post(uri, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String billDownload(String link) {
|
public String billDownload(String link) {
|
||||||
@@ -490,7 +486,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
URI uri = UriComponentsBuilder.fromHttpUrl(link)
|
URI uri = UriComponentsBuilder.fromHttpUrl(link)
|
||||||
.build()
|
.build()
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
return Get(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,13 +92,13 @@ public class WechatPayApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> payFunction(WechatPayV3Type type, PayParams payParams) {
|
private RequestEntity<?> payFunction(WechatPayV3Type type, PayParams payParams) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
payParams.setAppid(v3.getAppId());
|
payParams.setAppid(v3.getAppId());
|
||||||
payParams.setMchid(v3.getMchId());
|
payParams.setMchid(v3.getMchId());
|
||||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.build()
|
.build()
|
||||||
.toUri();
|
.toUri();
|
||||||
return post(uri, payParams,tenantId());
|
return Post(uri, payParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,17 +132,46 @@ public class WechatPayApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> queryTransactionFunction(WechatPayV3Type type, TransactionQueryParams params) {
|
private RequestEntity<?> queryTransactionFunction(WechatPayV3Type type, TransactionQueryParams params) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("mchid", params.getMchId());
|
queryParams.add("mchid", v3.getMchId());
|
||||||
|
|
||||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.queryParams(queryParams)
|
.queryParams(queryParams)
|
||||||
.build()
|
.build()
|
||||||
.expand(params.getTransactionIdOrOutTradeNo())
|
.expand(params.getTransactionIdOrOutTradeNo())
|
||||||
.toUri();
|
.toUri();
|
||||||
return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
|
return Get(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关单API
|
||||||
|
*
|
||||||
|
* @param outTradeNo the out trade no
|
||||||
|
* @return the wechat response entity
|
||||||
|
*/
|
||||||
|
public WechatResponseEntity<ObjectNode> closeByOutTradeNo(String outTradeNo) {
|
||||||
|
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
|
||||||
|
this.client().withType(WechatPayV3Type.CLOSE, outTradeNo)
|
||||||
|
.function(this::closeByOutTradeNoFunction)
|
||||||
|
.consumer(wechatResponseEntity::convert)
|
||||||
|
.request();
|
||||||
|
return wechatResponseEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestEntity<?> closeByOutTradeNoFunction(WechatPayV3Type type, String outTradeNo) {
|
||||||
|
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||||
|
|
||||||
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
|
queryParams.add("mchid", v3.getMchId());
|
||||||
|
|
||||||
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
|
.queryParams(queryParams)
|
||||||
|
.build()
|
||||||
|
.expand(outTradeNo)
|
||||||
|
.toUri();
|
||||||
|
return Post(uri,queryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.felord.payment.wechat.v3.model.CouponConsumeData;
|
|||||||
import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
|
import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
|
||||||
import cn.felord.payment.PayException;
|
import cn.felord.payment.PayException;
|
||||||
import cn.felord.payment.wechat.v3.model.CallbackParams;
|
import cn.felord.payment.wechat.v3.model.CallbackParams;
|
||||||
|
import cn.felord.payment.wechat.v3.model.TransactionConsumeData;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||||
@@ -11,12 +12,14 @@ import lombok.SneakyThrows;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type Wechat pay callback.
|
* 微信支付回调工具.
|
||||||
*
|
*
|
||||||
* @author Dax
|
* @author Dax
|
||||||
* @since 10 :21
|
* @since 10 :21
|
||||||
@@ -25,6 +28,7 @@ import java.util.function.Consumer;
|
|||||||
public class WechatPayCallback {
|
public class WechatPayCallback {
|
||||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
private final SignatureProvider signatureProvider;
|
private final SignatureProvider signatureProvider;
|
||||||
|
private final String tenantId;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||||
@@ -35,40 +39,91 @@ public class WechatPayCallback {
|
|||||||
* Instantiates a new Wechat pay callback.
|
* Instantiates a new Wechat pay callback.
|
||||||
*
|
*
|
||||||
* @param signatureProvider the signature provider
|
* @param signatureProvider the signature provider
|
||||||
|
* @param tenantId the tenant id
|
||||||
*/
|
*/
|
||||||
public WechatPayCallback(SignatureProvider signatureProvider) {
|
public WechatPayCallback(SignatureProvider signatureProvider, String tenantId) {
|
||||||
this.signatureProvider = signatureProvider;
|
this.signatureProvider = signatureProvider;
|
||||||
|
Assert.hasText(tenantId, "tenantId is required");
|
||||||
|
this.tenantId = tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信支付代金券核销回调工具.
|
* 微信支付代金券核销回调.
|
||||||
*
|
*
|
||||||
* @param tenantId the tenant id
|
|
||||||
* @param params the params
|
* @param params the params
|
||||||
* @param couponConsumeDataConsumer the coupon consume data consumer
|
* @param couponConsumeDataConsumer the coupon consume data consumer
|
||||||
* @return the map
|
* @return the map
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Map<String, ?> wechatPayCouponCallback(String tenantId, ResponseSignVerifyParams params, Consumer<CouponConsumeData> couponConsumeDataConsumer) {
|
public Map<String, ?> couponCallback(ResponseSignVerifyParams params, Consumer<CouponConsumeData> couponConsumeDataConsumer) {
|
||||||
|
String data = callback(params, EventType.COUPON);
|
||||||
|
CouponConsumeData couponConsumeData = MAPPER.readValue(data, CouponConsumeData.class);
|
||||||
|
couponConsumeDataConsumer.accept(couponConsumeData);
|
||||||
|
Map<String, Object> responseBody = new HashMap<>();
|
||||||
|
responseBody.put("code", 200);
|
||||||
|
responseBody.put("message", "SUCCESS");
|
||||||
|
return responseBody;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付成功回调.
|
||||||
|
* <p>
|
||||||
|
* 无需开发者判断,只有扣款成功微信才会回调此接口
|
||||||
|
*
|
||||||
|
* @param params the params
|
||||||
|
* @param couponConsumeDataConsumer the coupon consume data consumer
|
||||||
|
* @return the map
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public Map<String, ?> transactionCallback(ResponseSignVerifyParams params, Consumer<TransactionConsumeData> couponConsumeDataConsumer) {
|
||||||
|
String data = callback(params, EventType.TRANSACTION);
|
||||||
|
TransactionConsumeData transactionConsumeData = MAPPER.readValue(data, TransactionConsumeData.class);
|
||||||
|
couponConsumeDataConsumer.accept(transactionConsumeData);
|
||||||
|
return Collections.singletonMap("code", "SUCCESS");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private String callback(ResponseSignVerifyParams params, EventType eventType) {
|
||||||
if (signatureProvider.responseSignVerify(params)) {
|
if (signatureProvider.responseSignVerify(params)) {
|
||||||
CallbackParams callbackParams = MAPPER.readValue(params.getBody(), CallbackParams.class);
|
CallbackParams callbackParams = MAPPER.readValue(params.getBody(), CallbackParams.class);
|
||||||
|
|
||||||
|
if (!Objects.equals(callbackParams.getEventType(), eventType.event)) {
|
||||||
|
log.error("wechat pay event type is not matched, callbackParams {}", callbackParams);
|
||||||
|
throw new PayException(" wechat pay event type is not matched");
|
||||||
|
}
|
||||||
CallbackParams.Resource resource = callbackParams.getResource();
|
CallbackParams.Resource resource = callbackParams.getResource();
|
||||||
String associatedData = resource.getAssociatedData();
|
String associatedData = resource.getAssociatedData();
|
||||||
String nonce = resource.getNonce();
|
String nonce = resource.getNonce();
|
||||||
String ciphertext = resource.getCiphertext();
|
String ciphertext = resource.getCiphertext();
|
||||||
String data = signatureProvider.decryptResponseBody(tenantId,associatedData, nonce, ciphertext);
|
String data = signatureProvider.decryptResponseBody(tenantId, associatedData, nonce, ciphertext);
|
||||||
Assert.hasText(data, "decryptData is required");
|
Assert.hasText(data, "decryptData is required");
|
||||||
CouponConsumeData couponConsumeData = MAPPER.readValue(data, CouponConsumeData.class);
|
return data;
|
||||||
couponConsumeDataConsumer.accept(couponConsumeData);
|
}
|
||||||
Map<String, Object> responseBody = new HashMap<>();
|
throw new PayException("invalid wechat pay callback");
|
||||||
responseBody.put("code", 200);
|
}
|
||||||
responseBody.put("message", "核销成功");
|
|
||||||
return responseBody;
|
/**
|
||||||
|
* 事件类型用于处理回调.
|
||||||
|
*/
|
||||||
|
enum EventType {
|
||||||
|
/**
|
||||||
|
* 优惠券核销事件.
|
||||||
|
*/
|
||||||
|
COUPON("COUPON.USE"),
|
||||||
|
/**
|
||||||
|
* 支付成功事件.
|
||||||
|
*/
|
||||||
|
TRANSACTION("TRANSACTION.SUCCESS");
|
||||||
|
|
||||||
|
private final String event;
|
||||||
|
|
||||||
|
EventType(String event) {
|
||||||
|
this.event = event;
|
||||||
}
|
}
|
||||||
throw new PayException("invalid wechat pay coupon callback");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
package cn.felord.payment.wechat.v3.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PromotionDetail {
|
||||||
|
|
||||||
|
private Long amount;
|
||||||
|
private String couponId;
|
||||||
|
private String currency;
|
||||||
|
private List<GoodsDetail> goodsDetail;
|
||||||
|
private Long merchantContribute;
|
||||||
|
private String name;
|
||||||
|
private Long otherContribute;
|
||||||
|
private String scope;
|
||||||
|
private String stockId;
|
||||||
|
private String type;
|
||||||
|
private Long wechatpayContribute;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class GoodsDetail {
|
||||||
|
|
||||||
|
private String goodsId;
|
||||||
|
private Long quantity;
|
||||||
|
private Long unitPrice;
|
||||||
|
private Long discountAmount;
|
||||||
|
private String goodsRemark;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
package cn.felord.payment.wechat.v3.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TransactionConsumeData {
|
||||||
|
|
||||||
|
private Amount amount;
|
||||||
|
private String appid;
|
||||||
|
private String attach;
|
||||||
|
private String bankType;
|
||||||
|
private String mchid;
|
||||||
|
private String outTradeNo;
|
||||||
|
private Payer payer;
|
||||||
|
private List<PromotionDetail> promotionDetail;
|
||||||
|
private SceneInfo sceneInfo;
|
||||||
|
private String successTime;
|
||||||
|
private String tradeState;
|
||||||
|
private String tradeStateDesc;
|
||||||
|
private String tradeType;
|
||||||
|
private String transactionId;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Payer {
|
||||||
|
private String openid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class SceneInfo {
|
||||||
|
private String deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Amount {
|
||||||
|
private int total;
|
||||||
|
private int payerTotal;
|
||||||
|
private String currency;
|
||||||
|
private String payerCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user