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并存的场景,对原有的进行了增强开发出了多租户版本。
|
||||
|
||||
## 支持类型
|
||||
|
||||
- [x] **微信支付V3** 全量支持。
|
||||
- [x] **微信支付V3** 全量支持,并支持多租户。
|
||||
- [x] **支付宝** 提供所有实现,具体以签约项目为准。
|
||||
## 进度
|
||||
- 微信支付营销-代金券 `WechatMarketingFavorApi` 100%
|
||||
- 微信支付 支付功能 `WechatPayApi` app预支付
|
||||
- 微信支付 支付功能 `WechatPayApi` 100%
|
||||
|
||||
## 采用技术
|
||||
- Spring
|
||||
|
||||
@@ -73,15 +73,4 @@ public class WechatPayConfiguration {
|
||||
public WechatApiProvider wechatApiProvider(WechatPayClient 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支付.
|
||||
*/
|
||||
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;
|
||||
|
||||
import cn.felord.payment.PayException;
|
||||
import cn.felord.payment.wechat.WechatPayProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* The type Abstract api.
|
||||
*
|
||||
* @author Dax
|
||||
* @since 18:23
|
||||
* @since 18 :23
|
||||
*/
|
||||
public abstract class AbstractApi {
|
||||
private final ObjectMapper mapper;
|
||||
@@ -19,43 +23,94 @@ public abstract class AbstractApi {
|
||||
private final String tenantId;
|
||||
|
||||
|
||||
|
||||
|
||||
public AbstractApi(WechatPayClient wechatPayClient,String tenantId) {
|
||||
/**
|
||||
* Instantiates a new Abstract api.
|
||||
*
|
||||
* @param wechatPayClient the wechat pay client
|
||||
* @param tenantId the tenant id
|
||||
*/
|
||||
public AbstractApi(WechatPayClient wechatPayClient, String tenantId) {
|
||||
this.mapper = new ObjectMapper();
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
this.wechatPayClient = wechatPayClient;
|
||||
|
||||
Assert.hasText(tenantId, "tenantId is required");
|
||||
if (!container().getTenantIds().contains(tenantId)) {
|
||||
throw new PayException("tenantId is not in wechatMetaContainer ");
|
||||
throw new PayException("tenantId is not in wechatMetaContainer");
|
||||
}
|
||||
this.tenantId = tenantId;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mapper.
|
||||
*
|
||||
* @return the mapper
|
||||
*/
|
||||
public ObjectMapper getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client wechat pay client.
|
||||
*
|
||||
* @return the wechat pay client
|
||||
*/
|
||||
public WechatPayClient client() {
|
||||
return wechatPayClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant id string.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String tenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container wechat meta container.
|
||||
*
|
||||
* @return the wechat meta container
|
||||
*/
|
||||
public WechatMetaContainer container() {
|
||||
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 {
|
||||
return RequestEntity.post(uri).header("Pay-TenantId",tenantId)
|
||||
return RequestEntity.post(uri).header("Pay-TenantId", tenantId)
|
||||
.body(mapper.writeValueAsString(params));
|
||||
} catch (JsonProcessingException e) {
|
||||
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;
|
||||
|
||||
/**
|
||||
* The type Wechat api provider.
|
||||
*
|
||||
* @author Dax
|
||||
* @since 17:32
|
||||
* @since 17 :32
|
||||
*/
|
||||
public class WechatApiProvider {
|
||||
private final WechatPayClient wechatPayClient;
|
||||
|
||||
/**
|
||||
* Instantiates a new Wechat api provider.
|
||||
*
|
||||
* @param wechatPayClient the wechat pay client
|
||||
*/
|
||||
public WechatApiProvider(WechatPayClient wechatPayClient) {
|
||||
this.wechatPayClient = wechatPayClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代金券.
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return the wechat marketing favor api
|
||||
*/
|
||||
public WechatMarketingFavorApi favorApi(String tenantId){
|
||||
return new WechatMarketingFavorApi(this.wechatPayClient,tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付.
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @return the wechat pay api
|
||||
*/
|
||||
public WechatPayApi payApi(String 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;
|
||||
|
||||
import cn.felord.payment.PayException;
|
||||
import cn.felord.payment.wechat.WechatPayProperties;
|
||||
import cn.felord.payment.wechat.enumeration.StockStatus;
|
||||
import cn.felord.payment.wechat.enumeration.WeChatServer;
|
||||
@@ -59,14 +58,14 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
}
|
||||
|
||||
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();
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.toUri();
|
||||
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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
params.setAppid(v3.getApp().getAppId());
|
||||
params.setStockCreatorMchid(v3.getMchId());
|
||||
@@ -111,7 +110,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.expand(params.getOpenid())
|
||||
.toUri();
|
||||
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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
String mchId = v3.getMchId();
|
||||
Map<String, String> body = new HashMap<>();
|
||||
body.put("stock_creator_mchid", mchId);
|
||||
@@ -154,7 +153,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.build()
|
||||
.expand(stockId)
|
||||
.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<>();
|
||||
queryParams.add("offset", String.valueOf(params.getOffset()));
|
||||
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());
|
||||
LocalDateTime createStartTime = params.getCreateStartTime();
|
||||
@@ -211,7 +210,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
|
||||
URI uri = uriComponents
|
||||
.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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("stock_creator_mchid", v3.getMchId());
|
||||
@@ -242,7 +240,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.build()
|
||||
.expand(stockId)
|
||||
.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) {
|
||||
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("appid", v3.getApp().getAppId());
|
||||
|
||||
|
||||
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
||||
pathParams.add("openid", params.getOpenId());
|
||||
pathParams.add("coupon_id", params.getCouponId());
|
||||
@@ -279,7 +275,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.build()
|
||||
.expand(pathParams)
|
||||
.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) {
|
||||
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("appid", v3.getApp().getAppId());
|
||||
@@ -363,7 +359,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.build()
|
||||
.expand(params.getOpenId())
|
||||
.toUri();
|
||||
return RequestEntity.get(uri).header("Pay-TenantId", tenantId()).build();
|
||||
return Get(uri);
|
||||
|
||||
}
|
||||
|
||||
@@ -406,7 +402,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
.build()
|
||||
.expand(stockId)
|
||||
.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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
Map<String, Object> body = new HashMap<>(3);
|
||||
body.put("mchid", v3.getMchId());
|
||||
@@ -476,7 +472,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.toUri();
|
||||
return post(uri, body, tenantId());
|
||||
return Post(uri, body);
|
||||
}
|
||||
|
||||
public String billDownload(String link) {
|
||||
@@ -490,7 +486,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(link)
|
||||
.build()
|
||||
.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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
payParams.setAppid(v3.getAppId());
|
||||
payParams.setMchid(v3.getMchId());
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.build()
|
||||
.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) {
|
||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
|
||||
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.add("mchid", params.getMchId());
|
||||
queryParams.add("mchid", v3.getMchId());
|
||||
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||
.queryParams(queryParams)
|
||||
.build()
|
||||
.expand(params.getTransactionIdOrOutTradeNo())
|
||||
.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.PayException;
|
||||
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.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
@@ -11,12 +12,14 @@ 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;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The type Wechat pay callback.
|
||||
* 微信支付回调工具.
|
||||
*
|
||||
* @author Dax
|
||||
* @since 10 :21
|
||||
@@ -25,6 +28,7 @@ import java.util.function.Consumer;
|
||||
public class WechatPayCallback {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private final SignatureProvider signatureProvider;
|
||||
private final String tenantId;
|
||||
|
||||
static {
|
||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
@@ -35,40 +39,91 @@ public class WechatPayCallback {
|
||||
* Instantiates a new Wechat pay callback.
|
||||
*
|
||||
* @param signatureProvider the signature provider
|
||||
* @param tenantId the tenant id
|
||||
*/
|
||||
public WechatPayCallback(SignatureProvider signatureProvider) {
|
||||
public WechatPayCallback(SignatureProvider signatureProvider, String tenantId) {
|
||||
this.signatureProvider = signatureProvider;
|
||||
Assert.hasText(tenantId, "tenantId is required");
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 微信支付代金券核销回调工具.
|
||||
* 微信支付代金券核销回调.
|
||||
*
|
||||
* @param tenantId the tenant id
|
||||
* @param params the params
|
||||
* @param couponConsumeDataConsumer the coupon consume data consumer
|
||||
* @return the map
|
||||
*/
|
||||
@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)) {
|
||||
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();
|
||||
String associatedData = resource.getAssociatedData();
|
||||
String nonce = resource.getNonce();
|
||||
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");
|
||||
CouponConsumeData couponConsumeData = MAPPER.readValue(data, CouponConsumeData.class);
|
||||
couponConsumeDataConsumer.accept(couponConsumeData);
|
||||
Map<String, Object> responseBody = new HashMap<>();
|
||||
responseBody.put("code", 200);
|
||||
responseBody.put("message", "核销成功");
|
||||
return responseBody;
|
||||
return data;
|
||||
}
|
||||
throw new PayException("invalid wechat pay callback");
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件类型用于处理回调.
|
||||
*/
|
||||
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