diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/service/isv/AlipayIsvConfigAssembler.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/service/isv/AlipayIsvConfigAssembler.java new file mode 100644 index 000000000..b8d872464 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/service/isv/AlipayIsvConfigAssembler.java @@ -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; + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvCloseStrategy.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvCloseStrategy.java new file mode 100644 index 000000000..a069d9698 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvCloseStrategy.java @@ -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); + } +} diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvPayStrategy.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvPayStrategy.java index 16e7ea3a7..810726078 100644 --- a/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvPayStrategy.java +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvPayStrategy.java @@ -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 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 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())); } } diff --git a/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvSyncStrategy.java b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvSyncStrategy.java new file mode 100644 index 000000000..16f6f8c15 --- /dev/null +++ b/daxpay-channel/daxpay-channel-alipay/src/main/java/cn/daxpay/open/channel/alipay/strategy/isv/AlipayIsvSyncStrategy.java @@ -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); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PayStrategyContext.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PayStrategyContext.java index bb4d463a1..411b9aa9b 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PayStrategyContext.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PayStrategyContext.java @@ -29,10 +29,4 @@ public class PayStrategyContext { public PayStrategyContext(NormalPayParam payParam) { this.payParam = payParam; } - - /// 读取通道配置(各通道策略 cast 自己的配置类型) - @SuppressWarnings("unchecked") - public T getChannelConfig() { - return (T) channelConfig; - } }