style 支付宝功能对接

This commit is contained in:
DaxPay Dev
2026-07-02 12:18:16 +08:00
parent aa567c242f
commit ce7eaddbd7
5 changed files with 147 additions and 61 deletions

View File

@@ -0,0 +1,67 @@
package cn.daxpay.open.channel.alipay.service.isv;
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
import cn.daxpay.open.channel.alipay.dao.isv.AlipayIsvAppManager;
import cn.daxpay.open.channel.alipay.dao.isv.AlipayIsvChannelMerchantManager;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvApp;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvAppKeyConfig;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvChannelMerchant;
import cn.daxpay.open.payment.masterdata.constants.product.dao.PayProductConfigManager;
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
import cn.daxpay.open.platform.core.enums.pay.config.PayEnvEnum;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/// # 支付宝服务商通道凭证组装器
///
/// 从服务商应用([AlipayIsvApp] + [AlipayIsvAppKeyConfig])与子商户授权绑定([AlipayIsvChannelMerchant])
/// 组装下发给子应用的通道调用凭证 [AlipaySdkCredential]。
///
/// 服务商代调用模式: appId / 私钥 / 证书取自服务商应用, 应用授权令牌(appAuthToken)取自子商户授权绑定,
/// 子应用据此以服务商身份代子商户发起支付宝请求。
///
/// 供服务商支付/同步/关闭策略组装通道调用凭证。
@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayIsvConfigAssembler {
private final AlipayIsvChannelMerchantManager alipayIsvChannelMerchantManager;
private final AlipayIsvAppManager alipayIsvAppManager;
private final AlipayIsvAppKeyConfigService alipayIsvAppKeyConfigService;
private final PayProductConfigManager payProductConfigManager;
/// 组装服务商模式的通道调用凭证(下发给子应用)
///
/// @param mchNo 平台商户号(定位子商户授权绑定)
/// @return 支付宝 SDK 凭证, 字段对齐子应用 AlipaySdkCredential
public AlipaySdkCredential buildConfig(String mchNo) {
// 子商户授权绑定(含 appAuthToken)
AlipayIsvChannelMerchant isvMerchant = alipayIsvChannelMerchantManager.findByMchNo(mchNo)
.orElseThrow(() -> new DataNotExistException("error.channel.alipay.mchAppNotFound"));
// 服务商应用(appId 来源)
AlipayIsvApp isvApp = alipayIsvAppManager.findById(isvMerchant.getIsvAppId())
.orElseThrow(() -> new DataNotExistException("error.channel.alipay.appNotFound"));
// 服务商应用密钥/证书配置
AlipayIsvAppKeyConfig keyConfig = alipayIsvAppKeyConfigService.findByAlipayIsvAppId(isvMerchant.getIsvAppId());
AlipaySdkCredential credential = new AlipaySdkCredential();
credential.setAliAppId(isvApp.getAliAppId());
credential.setPrivateKey(keyConfig.getPrivateKey());
credential.setAlipayPublicKey(keyConfig.getAlipayPublicKey());
credential.setAuthType(keyConfig.getAuthType());
credential.setAppCert(keyConfig.getAppCert());
credential.setAlipayCert(keyConfig.getAlipayCert());
credential.setAlipayRootCert(keyConfig.getAlipayRootCert());
// 应用授权令牌(服务商代子商户调用接口的凭据)
credential.setAppAuthToken(isvMerchant.getAppAuthToken());
// 读取服务商支付产品当前生效环境, 判断是否沙箱
boolean sandbox = payProductConfigManager.findByProduct(ProductEnum.ALIPAY_ISV.getCode())
.map(c -> PayEnvEnum.SANDBOX.getCode().equals(c.getActiveEnv()))
.orElse(false);
credential.setSandbox(sandbox);
return credential;
}
}

View File

@@ -0,0 +1,37 @@
package cn.daxpay.open.channel.alipay.strategy.isv;
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
import cn.daxpay.open.channel.alipay.service.isv.AlipayIsvConfigAssembler;
import cn.daxpay.open.channel.alipay.service.payment.close.AlipayCloseService;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.strategy.pay.AbsPayCloseStrategy;
import cn.daxpay.open.platform.core.enums.pay.pay.CloseTypeEnum;
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/// # 支付宝服务商支付关闭策略
///
/// 支付宝服务商模式(ProductEnum.ALIPAY_ISV)下的支付关闭策略。
/// 组装通道凭证(委托 [AlipayIsvConfigAssembler]), 关闭执行委托给 [AlipayCloseService]。
@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayIsvCloseStrategy extends AbsPayCloseStrategy {
private final AlipayCloseService alipayCloseService;
private final AlipayIsvConfigAssembler alipayIsvConfigAssembler;
@Override
public ProductEnum getProduct() {
return ProductEnum.ALIPAY_ISV;
}
@Override
public CloseTypeEnum doClose(PayTrade trade, boolean useCancel) {
// 组装服务商模式通道调用凭证(含应用授权令牌)
AlipaySdkCredential credential = alipayIsvConfigAssembler.buildConfig(trade.getMchNo());
return alipayCloseService.close(trade, credential, useCancel);
}
}

View File

@@ -1,85 +1,36 @@
package cn.daxpay.open.channel.alipay.strategy.isv;
import cn.daxpay.open.channel.alipay.dao.isv.AlipayIsvAppManager;
import cn.daxpay.open.channel.alipay.dao.isv.AlipayIsvChannelMerchantManager;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvApp;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvAppKeyConfig;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvChannelMerchant;
import cn.daxpay.open.channel.alipay.service.isv.AlipayIsvAppKeyConfigService;
import cn.daxpay.open.channel.alipay.service.isv.AlipayIsvConfigAssembler;
import cn.daxpay.open.channel.alipay.service.payment.pay.AlipayPayService;
import cn.daxpay.open.payment.common.context.PayStrategyContext;
import cn.daxpay.open.payment.pay.bo.PayTradeResultBo;
import cn.daxpay.open.payment.strategy.pay.AbsPayStrategy;
import cn.daxpay.open.platform.core.enums.pay.channel.PayMethodEnum;
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/// # 支付宝服务商支付策略
///
/// 支付宝服务商模式(ProductEnum.ALIPAY_ISV)下发起支付的具体执行策略。
/// 负责服务商配置组装(含应用授权令牌), 支付执行委托给 [AlipayPayService]。
///
/// 负责服务商配置组装(含应用授权令牌, 委托 [AlipayIsvConfigAssembler]), 支付执行委托给 [AlipayPayService]。
@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayIsvPayStrategy extends AbsPayStrategy {
private final AlipayPayService alipayPayService;
private final AlipayIsvChannelMerchantManager alipayIsvChannelMerchantManager;
private final AlipayIsvAppManager alipayIsvAppManager;
private final AlipayIsvAppKeyConfigService alipayIsvAppKeyConfigService;
private final AlipayIsvConfigAssembler alipayIsvConfigAssembler;
@Override
public ProductEnum getProduct() {
return ProductEnum.ALIPAY_ISV;
}
@Override
public void doBeforePay(PayStrategyContext context) {
mapMethod(context.getPayParam().getMethod());
// 前置阶段构建配置,存入上下文供 doPay 使用
context.setChannelConfig(buildConfig(context.getTrade().getMchNo()));
}
@Override
public PayTradeResultBo doPay(PayStrategyContext context) {
return alipayPayService.pay(context.getTrade(), context.getPayParam(), context.getChannelConfig());
}
private Map<String, Object> buildConfig(String mchNo) {
AlipayIsvChannelMerchant isvMerchant = alipayIsvChannelMerchantManager.findByMchNo(mchNo)
.orElseThrow(() -> new DataNotExistException("error.channel.alipay.mchAppNotFound"));
AlipayIsvApp isvApp = alipayIsvAppManager.findById(isvMerchant.getIsvAppId())
.orElseThrow(() -> new DataNotExistException("error.channel.alipay.appNotFound"));
AlipayIsvAppKeyConfig keyConfig = alipayIsvAppKeyConfigService.findByAlipayIsvAppId(isvMerchant.getIsvAppId());
Map<String, Object> config = new HashMap<>();
config.put("aliAppId", isvApp.getAliAppId());
config.put("privateKey", keyConfig.getPrivateKey());
config.put("alipayPublicKey", keyConfig.getAlipayPublicKey());
config.put("authType", keyConfig.getAuthType());
config.put("appCert", keyConfig.getAppCert());
config.put("alipayCert", keyConfig.getAlipayCert());
config.put("alipayRootCert", keyConfig.getAlipayRootCert());
config.put("appAuthToken", isvMerchant.getAppAuthToken());
return config;
}
private static String mapMethod(String methodCode) {
PayMethodEnum m = PayMethodEnum.findByCode(methodCode);
return switch (m) {
case ALIPAY_PC -> "alipay_page";
case ALIPAY_H5 -> "alipay_wap";
case ALIPAY_APP -> "alipay_app";
case ALIPAY_QR, ALIPAY_ORDER_QR -> "alipay_qr";
default -> throw new UnsupportedOperationException(
"暂不支持的支付宝支付方式: " + methodCode);
};
// 组装服务商模式通道调用凭证(含应用授权令牌), 支付执行委托 AlipayPayService
return alipayPayService.pay(context.getTrade(), context.getPayParam(),
alipayIsvConfigAssembler.buildConfig(context.getTrade().getMchNo()));
}
}

View File

@@ -0,0 +1,37 @@
package cn.daxpay.open.channel.alipay.strategy.isv;
import cn.daxpay.open.channel.alipay.client.credential.AlipaySdkCredential;
import cn.daxpay.open.channel.alipay.service.isv.AlipayIsvConfigAssembler;
import cn.daxpay.open.channel.alipay.service.payment.sync.AlipaySyncService;
import cn.daxpay.open.payment.pay.bo.PaySyncResultBo;
import cn.daxpay.open.payment.pay.order.entity.PayTrade;
import cn.daxpay.open.payment.strategy.sync.AbsSyncPayOrderStrategy;
import cn.daxpay.open.platform.core.enums.pay.channel.ProductEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/// # 支付宝服务商支付同步策略
///
/// 支付宝服务商模式(ProductEnum.ALIPAY_ISV)下的支付同步策略。
/// 组装通道凭证(委托 [AlipayIsvConfigAssembler]), 同步执行委托给 [AlipaySyncService]。
@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayIsvSyncStrategy extends AbsSyncPayOrderStrategy {
private final AlipaySyncService alipaySyncService;
private final AlipayIsvConfigAssembler alipayIsvConfigAssembler;
@Override
public ProductEnum getProduct() {
return ProductEnum.ALIPAY_ISV;
}
@Override
public PaySyncResultBo doSync(PayTrade trade) {
// 组装服务商模式通道调用凭证(含应用授权令牌)
AlipaySdkCredential credential = alipayIsvConfigAssembler.buildConfig(trade.getMchNo());
return alipaySyncService.sync(trade, credential);
}
}

View File

@@ -29,10 +29,4 @@ public class PayStrategyContext {
public PayStrategyContext(NormalPayParam payParam) {
this.payParam = payParam;
}
/// 读取通道配置(各通道策略 cast 自己的配置类型)
@SuppressWarnings("unchecked")
public <T> T getChannelConfig() {
return (T) channelConfig;
}
}