多租户

This commit is contained in:
xiafang
2020-12-07 18:57:26 +08:00
parent cbf652d8d8
commit ac6a5fffd9
8 changed files with 156 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
# 移动支付 Spring Boot 组件 # 移动支付 Spring Boot 组件 多租户版
提供聚合支付能力 为了满足业务中出现app支付、公众号支付、小程序支付等多appid并存的场景对原有的进行了增强开发出了多租户版本
## 支持类型 ## 支持类型
@@ -31,6 +31,8 @@
wechat: wechat:
pay: pay:
v3: v3:
# 租户id
<tentantID>:
# 应用appId 必填 # 应用appId 必填
app-id: xxxxxxxx app-id: xxxxxxxx
# api 密钥 必填 # api 密钥 必填
@@ -62,9 +64,7 @@ public class PayConfig {
微信支付V3开放接口引入 微信支付V3开放接口引入
```java ```java
@Autowired @Autowired
WechatPayApi wechatPayV3Api; WechatApiProvider wechatApiProvider;
@Autowired
WechatMarketingFavorApi wechatMarketingFavorApi;
``` ```
###### V3 ###### V3
例如V3 查询商户下的优惠券 例如V3 查询商户下的优惠券
@@ -73,11 +73,13 @@ public class PayConfig {
// 查询商户下的优惠券 // 查询商户下的优惠券
@Test @Test
public void v3MchStocks() { public void v3MchStocks() {
StocksQueryParams params = new StocksQueryParams(); // 配置文件中对应的tenantID:
params.setOffset(0); String tenantId =<tenantID>;
params.setLimit(10); StocksQueryParams params = new StocksQueryParams();
WechatResponseEntity<ObjectNode> objectNodeWechatResponseEntity = wechatMarketingFavorApi.queryStocksByMch(params); params.setOffset(0);
System.out.println("objectNodeWechatResponseEntity = " + objectNodeWechatResponseEntity); params.setLimit(10);
WechatResponseEntity<ObjectNode> objectNodeWechatResponseEntity = wechatApiProvider.favorApi(tenantId).queryStocksByMch(params);
System.out.println("objectNodeWechatResponseEntity = " + objectNodeWechatResponseEntity);
} }
``` ```

View File

@@ -14,6 +14,11 @@ public enum WechatPayV3Type {
*/ */
CERT(HttpMethod.GET, "%s/v3/certificates"), CERT(HttpMethod.GET, "%s/v3/certificates"),
/**
* 文件下载
*/
FILE_DOWNLOAD(HttpMethod.GET,"%s/v3/billdownload/file"),
/** /**
* 微信公众号支付或者小程序支付. * 微信公众号支付或者小程序支付.
*/ */
@@ -33,6 +38,14 @@ public enum WechatPayV3Type {
* H5支付. * H5支付.
*/ */
MWEB(HttpMethod.POST, "%s/v3/pay/transactions/h5"), MWEB(HttpMethod.POST, "%s/v3/pay/transactions/h5"),
/**
* 微信支付订单号查询.
*/
TRANSACTION_TRANSACTION_ID(HttpMethod.GET, "%s/v3/pay/transactions/id/{transaction_id}"),
/**
* 商户订单号查询.
*/
TRANSACTION_OUT_TRADE_NO(HttpMethod.GET, "%s/v3/pay/transactions/out-trade-no/{out_trade_no}"),
/** /**
@@ -53,7 +66,7 @@ public enum WechatPayV3Type {
*/ */
MARKETING_FAVOR_USERS_COUPONS(HttpMethod.POST,"%s/v3/marketing/favor/users/{openid}/coupons"), MARKETING_FAVOR_USERS_COUPONS(HttpMethod.POST,"%s/v3/marketing/favor/users/{openid}/coupons"),
/** /**
* 重启代金券API * 重启代金券API.
*/ */
MARKETING_FAVOR_STOCKS_RESTART(HttpMethod.POST,"%s/v3/marketing/favor/stocks/{stock_id}/restart"), MARKETING_FAVOR_STOCKS_RESTART(HttpMethod.POST,"%s/v3/marketing/favor/stocks/{stock_id}/restart"),
/** /**

View File

@@ -50,9 +50,9 @@ public abstract class AbstractApi {
return wechatPayClient.signatureProvider().wechatMetaContainer(); return wechatPayClient.signatureProvider().wechatMetaContainer();
} }
protected RequestEntity<?> post(URI uri, Object params) { protected RequestEntity<?> post(URI uri, Object params,String tenantId) {
try { try {
return RequestEntity.post(uri) 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");

View File

@@ -63,11 +63,10 @@ public class WechatMarketingFavorApi extends AbstractApi {
String mchId = v3.getMchId(); String mchId = v3.getMchId();
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.toUri(); .toUri();
params.setBelongMerchant(mchId); params.setBelongMerchant(mchId);
return post(uri, params); return post(uri, params,tenantId());
} }
/** /**
@@ -108,12 +107,11 @@ public class WechatMarketingFavorApi extends AbstractApi {
params.setAppid(v3.getMp().getAppId()); params.setAppid(v3.getMp().getAppId());
params.setStockCreatorMchid(v3.getMchId()); params.setStockCreatorMchid(v3.getMchId());
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.expand(params.getOpenid()) .expand(params.getOpenid())
.toUri(); .toUri();
params.setOpenid(null); params.setOpenid(null);
return post(uri, params); return post(uri, params,tenantId());
} }
/** /**
@@ -153,11 +151,10 @@ public class WechatMarketingFavorApi extends AbstractApi {
body.put("stock_creator_mchid", mchId); body.put("stock_creator_mchid", mchId);
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.expand(stockId) .expand(stockId)
.toUri(); .toUri();
return post(uri, body); return post(uri, body,tenantId());
} }
/** /**
@@ -180,7 +177,6 @@ public class WechatMarketingFavorApi extends AbstractApi {
private RequestEntity<?> queryStocksFunction(WechatPayV3Type type, StocksQueryParams params) { private RequestEntity<?> queryStocksFunction(WechatPayV3Type type, StocksQueryParams params) {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("pay_tenantId", this.tenantId());
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.container().getWechatMeta(tenantId()).getV3();
@@ -215,7 +211,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
URI uri = uriComponents URI uri = uriComponents
.toUri(); .toUri();
return RequestEntity.get(uri).build(); return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
} }
/** /**
@@ -240,14 +236,13 @@ public class WechatMarketingFavorApi extends AbstractApi {
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());
queryParams.add("pay_tenantId", this.tenantId());
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParams(queryParams) .queryParams(queryParams)
.build() .build()
.expand(stockId) .expand(stockId)
.toUri(); .toUri();
return RequestEntity.get(uri).build(); return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
} }
@@ -274,7 +269,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("appid", v3.getMp().getAppId()); queryParams.add("appid", v3.getMp().getAppId());
queryParams.add("pay_tenantId", this.tenantId());
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>(); MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
pathParams.add("openid", params.getOpenId()); pathParams.add("openid", params.getOpenId());
@@ -284,7 +279,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
.build() .build()
.expand(pathParams) .expand(pathParams)
.toUri(); .toUri();
return RequestEntity.get(uri).build(); return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
} }
@@ -339,10 +334,8 @@ public class WechatMarketingFavorApi extends AbstractApi {
final String ignore = "available_mchid"; final String ignore = "available_mchid";
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("appid", v3.getMp().getAppId()); queryParams.add("appid", v3.getMp().getAppId());
queryParams.add("pay_tenantId", this.tenantId());
String stockId = params.getStockId(); String stockId = params.getStockId();
if (StringUtils.hasText(stockId)) { if (StringUtils.hasText(stockId)) {
queryParams.add("stock_id", stockId); queryParams.add("stock_id", stockId);
@@ -371,7 +364,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
.build() .build()
.expand(params.getOpenId()) .expand(params.getOpenId())
.toUri(); .toUri();
return RequestEntity.get(uri).build(); return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
} }
@@ -408,11 +401,10 @@ public class WechatMarketingFavorApi extends AbstractApi {
private RequestEntity<?> downloadFlowFunction(WechatPayV3Type type, String stockId) { private RequestEntity<?> downloadFlowFunction(WechatPayV3Type type, String stockId) {
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.expand(stockId) .expand(stockId)
.toUri(); .toUri();
return RequestEntity.get(uri).build(); return RequestEntity.get(uri).header("Pay-TenantId",tenantId()).build();
} }
/** /**
@@ -452,6 +444,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
return RequestEntity.post(uri) return RequestEntity.post(uri)
.header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE) .header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE)
.header("Meta-Info", metaStr) .header("Meta-Info", metaStr)
.header("Pay-TenantId",tenantId())
.body(body); .body(body);
} }
@@ -479,10 +472,9 @@ public class WechatMarketingFavorApi extends AbstractApi {
body.put("notify_url", notifyUrl); body.put("notify_url", notifyUrl);
body.put("switch", true); body.put("switch", true);
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.toUri(); .toUri();
return post(uri, body); return post(uri, body,tenantId());
} }
} }

View File

@@ -1,12 +1,14 @@
package cn.felord.payment.wechat.v3; package cn.felord.payment.wechat.v3;
import cn.felord.payment.PayException;
import cn.felord.payment.wechat.enumeration.WeChatServer; import cn.felord.payment.wechat.enumeration.WeChatServer;
import cn.felord.payment.wechat.WechatPayProperties; import cn.felord.payment.wechat.WechatPayProperties;
import cn.felord.payment.wechat.enumeration.WechatPayV3Type; import cn.felord.payment.wechat.enumeration.WechatPayV3Type;
import cn.felord.payment.wechat.v3.model.PayParams; import cn.felord.payment.wechat.v3.model.PayParams;
import cn.felord.payment.wechat.v3.model.TransactionQueryParams;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.http.RequestEntity; import org.springframework.http.RequestEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI; import java.net.URI;
@@ -19,6 +21,12 @@ import java.net.URI;
*/ */
public class WechatPayApi extends AbstractApi { public class WechatPayApi extends AbstractApi {
/**
* Instantiates a new Wechat pay api.
*
* @param wechatPayClient the wechat pay client
* @param tenantId the tenant id
*/
public WechatPayApi(WechatPayClient wechatPayClient, String tenantId) { public WechatPayApi(WechatPayClient wechatPayClient, String tenantId) {
super(wechatPayClient, tenantId); super(wechatPayClient, tenantId);
} }
@@ -29,10 +37,10 @@ public class WechatPayApi extends AbstractApi {
* @param payParams the pay params * @param payParams the pay params
* @return the wechat response entity * @return the wechat response entity
*/ */
public WechatResponseEntity<ObjectNode> app(PayParams payParams) { public WechatResponseEntity<ObjectNode> appPay(PayParams payParams) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>(); WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.APP, payParams) this.client().withType(WechatPayV3Type.APP, payParams)
.function(this::appPayFunction) .function(this::payFunction)
.consumer(wechatResponseEntity::convert) .consumer(wechatResponseEntity::convert)
.request(); .request();
return wechatResponseEntity; return wechatResponseEntity;
@@ -44,23 +52,97 @@ public class WechatPayApi extends AbstractApi {
* @param payParams the pay params * @param payParams the pay params
* @return wechat response entity * @return wechat response entity
*/ */
public WechatResponseEntity<ObjectNode> js(PayParams payParams) { public WechatResponseEntity<ObjectNode> jsPay(PayParams payParams) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>(); WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.JSAPI, payParams) this.client().withType(WechatPayV3Type.JSAPI, payParams)
.function(this::appPayFunction) .function(this::payFunction)
.consumer(wechatResponseEntity::convert) .consumer(wechatResponseEntity::convert)
.request(); .request();
return wechatResponseEntity; return wechatResponseEntity;
} }
private RequestEntity<?> appPayFunction(WechatPayV3Type type, PayParams payParams) { /**
* Native下单API
*
* @param payParams the pay params
* @return wechat response entity
*/
public WechatResponseEntity<ObjectNode> nativePay(PayParams payParams) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.NATIVE, payParams)
.function(this::payFunction)
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
/**
* H5下单API
*
* @param payParams the pay params
* @return wechat response entity
*/
public WechatResponseEntity<ObjectNode> h5Pay(PayParams payParams) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.MWEB, payParams)
.function(this::payFunction)
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
private RequestEntity<?> payFunction(WechatPayV3Type type, PayParams payParams) {
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).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))
.queryParam("pay_tenantId", this.tenantId())
.build() .build()
.toUri(); .toUri();
return post(uri, payParams); return post(uri, payParams,tenantId());
} }
/**
* 微信支付订单号查询API
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> queryTransactionById(TransactionQueryParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.TRANSACTION_TRANSACTION_ID, params)
.function(this::queryTransactionFunction)
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
/**
* 商户订单号查询API
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> queryTransactionByOutTradeNo(TransactionQueryParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.TRANSACTION_OUT_TRADE_NO, params)
.function(this::queryTransactionFunction)
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
private RequestEntity<?> queryTransactionFunction(WechatPayV3Type type, TransactionQueryParams params) {
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("mchid", params.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();
}
} }

View File

@@ -158,9 +158,8 @@ public class WechatPayClient {
if (WechatPayV3Type.MARKETING_IMAGE_UPLOAD.pattern().contains(canonicalUrl)) { if (WechatPayV3Type.MARKETING_IMAGE_UPLOAD.pattern().contains(canonicalUrl)) {
body = Objects.requireNonNull(headers.get("Meta-Info")).get(0); body = Objects.requireNonNull(headers.get("Meta-Info")).get(0);
} }
MultiValueMap<String, String> queryParams = uri.getQueryParams();
String tenantId = queryParams.getFirst("pay_tenantId"); String tenantId = Objects.requireNonNull(headers.get("Pay-TenantId")).get(0);
Assert.notNull(tenantId, "tenantId is required");
String authorization = signatureProvider.requestSign(tenantId,httpMethod.name(), canonicalUrl, body); String authorization = signatureProvider.requestSign(tenantId,httpMethod.name(), canonicalUrl, body);
HttpHeaders httpHeaders = new HttpHeaders(); HttpHeaders httpHeaders = new HttpHeaders();
@@ -172,7 +171,8 @@ public class WechatPayClient {
} }
httpHeaders.add("Authorization", authorization); httpHeaders.add("Authorization", authorization);
httpHeaders.add("User-Agent", "X-Pay-Service"); httpHeaders.add("User-Agent", "X-Pay-Service");
httpHeaders.remove("Meta-Info");
httpHeaders.remove("Pay-TenantId");
return requestEntity.headers(httpHeaders); return requestEntity.headers(httpHeaders);
} }

View File

@@ -43,8 +43,17 @@ public class PayParams {
* 支付金额 * 支付金额
*/ */
private Amount amount; private Amount amount;
/**
* 支付者 JSAPI/小程序下单 必传
*/
private Payer payer;
/**
* 优惠功能
*/
private Detail detail; private Detail detail;
/**
* 场景信息
*/
private SceneInfo sceneInfo; private SceneInfo sceneInfo;

View File

@@ -0,0 +1,13 @@
package cn.felord.payment.wechat.v3.model;
import lombok.Data;
/**
* @author Dax
* @since 17:42
*/
@Data
public class TransactionQueryParams {
private String mchId;
private String transactionIdOrOutTradeNo;
}