|
@@ -115,7 +115,7 @@ public class AlipayBatchPayService {
|
|
|
.orderByDesc(BatchAuthorizeEntity::getId)
|
|
.orderByDesc(BatchAuthorizeEntity::getId)
|
|
|
.last("LIMIT 1"));
|
|
.last("LIMIT 1"));
|
|
|
if (existing != null) {
|
|
if (existing != null) {
|
|
|
- if ("AUTHED".equals(existing.getStatus()))
|
|
|
|
|
|
|
+ if (isAuthorizedStatus(existing.getStatus()))
|
|
|
throw new BusinessException(400, "该企业已存在生效授权,无需重新生成");
|
|
throw new BusinessException(400, "该企业已存在生效授权,无需重新生成");
|
|
|
existing.setStatus("UNBIND");
|
|
existing.setStatus("UNBIND");
|
|
|
batchAuthorizeMapper.updateById(existing);
|
|
batchAuthorizeMapper.updateById(existing);
|
|
@@ -141,7 +141,7 @@ public class AlipayBatchPayService {
|
|
|
.ne(BatchAuthorizeEntity::getStatus, "UNBIND"));
|
|
.ne(BatchAuthorizeEntity::getStatus, "UNBIND"));
|
|
|
String outBizNo = SnowflakeIdGenerator.nextIdStr();
|
|
String outBizNo = SnowflakeIdGenerator.nextIdStr();
|
|
|
if (existing != null) {
|
|
if (existing != null) {
|
|
|
- if ("AUTHED".equals(existing.getStatus()))
|
|
|
|
|
|
|
+ if (isAuthorizedStatus(existing.getStatus()))
|
|
|
throw new BusinessException(400, "该付款方已存在制单授权申请,无需重复授权");
|
|
throw new BusinessException(400, "该付款方已存在制单授权申请,无需重复授权");
|
|
|
if (!isAuthorizeExpired(existing))
|
|
if (!isAuthorizeExpired(existing))
|
|
|
throw new BusinessException(400, "存在未完成的授权申请,请先完成授权或稍后重试");
|
|
throw new BusinessException(400, "存在未完成的授权申请,请先完成授权或稍后重试");
|
|
@@ -224,8 +224,9 @@ public class AlipayBatchPayService {
|
|
|
throw new BusinessException(400, "查询授权状态失败: " + response.getMsg());
|
|
throw new BusinessException(400, "查询授权状态失败: " + response.getMsg());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 仅 AUTHED 才回写: UNBIND 也返回协议号, 直接回写会破坏本地状态机(UNBIND 由异步通知回写)
|
|
|
|
|
- if ("AUTHED".equals(response.getStatus())
|
|
|
|
|
|
|
+ // 仅生效授权才回写(AUTHED/NORMAL 均视同生效,通知侧 NORMAL 已归一 AUTHED);
|
|
|
|
|
+ // UNBIND 也返回协议号, 直接回写会破坏本地状态机(UNBIND 由异步通知回写)
|
|
|
|
|
+ if (isAuthorizedStatus(response.getStatus())
|
|
|
&& response.getAgreementNo() != null && !response.getAgreementNo().isBlank()) {
|
|
&& response.getAgreementNo() != null && !response.getAgreementNo().isBlank()) {
|
|
|
BatchAuthorizeEntity entity = batchAuthorizeMapper.selectOne(
|
|
BatchAuthorizeEntity entity = batchAuthorizeMapper.selectOne(
|
|
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<BatchAuthorizeEntity>()
|
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<BatchAuthorizeEntity>()
|
|
@@ -306,13 +307,13 @@ public class AlipayBatchPayService {
|
|
|
// 付款方 + 制单授权协议(Ruling 19/22: 不接受客户端指定,付款方身份遵循系统惯例 identity 优先回退 enterprise_id)
|
|
// 付款方 + 制单授权协议(Ruling 19/22: 不接受客户端指定,付款方身份遵循系统惯例 identity 优先回退 enterprise_id)
|
|
|
EnterpriseEntity ent = requireEnterprise(dto.getEnterpriseId());
|
|
EnterpriseEntity ent = requireEnterprise(dto.getEnterpriseId());
|
|
|
String payerUid = payerIdentity(ent);
|
|
String payerUid = payerIdentity(ent);
|
|
|
- // 未完成授权不允许制单: 该付款方无 AUTHED 授权记录 → 本地预检拦截;
|
|
|
|
|
|
|
+ // 未完成授权不允许制单: 该付款方无生效授权记录(AUTHED/NORMAL)→ 本地预检拦截;
|
|
|
// 支付宝侧 AUTH_INFO_NOT_EXISTS 兜底保留(防本地与支付宝状态不一致的竞态)
|
|
// 支付宝侧 AUTH_INFO_NOT_EXISTS 兜底保留(防本地与支付宝状态不一致的竞态)
|
|
|
Long authedCount = batchAuthorizeMapper.selectCount(
|
|
Long authedCount = batchAuthorizeMapper.selectCount(
|
|
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<BatchAuthorizeEntity>()
|
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<BatchAuthorizeEntity>()
|
|
|
.eq(BatchAuthorizeEntity::getEnterpriseId, dto.getEnterpriseId())
|
|
.eq(BatchAuthorizeEntity::getEnterpriseId, dto.getEnterpriseId())
|
|
|
.eq(BatchAuthorizeEntity::getParticipantId, payerUid)
|
|
.eq(BatchAuthorizeEntity::getParticipantId, payerUid)
|
|
|
- .eq(BatchAuthorizeEntity::getStatus, "AUTHED"));
|
|
|
|
|
|
|
+ .in(BatchAuthorizeEntity::getStatus, "AUTHED", "NORMAL"));
|
|
|
if (authedCount == null || authedCount == 0)
|
|
if (authedCount == null || authedCount == 0)
|
|
|
throw new BusinessException(400, "该企业尚未完成制单授权,请先在「制单授权」中生成授权链接并完成授权");
|
|
throw new BusinessException(400, "该企业尚未完成制单授权,请先在「制单授权」中生成授权链接并完成授权");
|
|
|
Participant payer = new Participant();
|
|
Participant payer = new Participant();
|
|
@@ -599,6 +600,14 @@ public class AlipayBatchPayService {
|
|
|
return ent.getIdentityType() != null ? ent.getIdentityType() : "ALIPAY_USER_ID";
|
|
return ent.getIdentityType() != null ? ent.getIdentityType() : "ALIPAY_USER_ID";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 授权状态是否生效: 本地值域 AUTHED(已授权)与 NORMAL(生效中,支付宝授权签约通知回写的原始状态)均视同生效。
|
|
|
|
|
+ * 源头归一在 BatchPayHandler(通知 NORMAL → 落库 AUTHED),此处兼容存量 NORMAL 记录。
|
|
|
|
|
+ */
|
|
|
|
|
+ private static boolean isAuthorizedStatus(String status) {
|
|
|
|
|
+ return "AUTHED".equals(status) || "NORMAL".equals(status);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** 租户隔离: 企业 ID 是业务必需参数,为空直接拒绝(防御 Controller body 路径的零校验) */
|
|
/** 租户隔离: 企业 ID 是业务必需参数,为空直接拒绝(防御 Controller body 路径的零校验) */
|
|
|
private static void requireEnterpriseId(String enterpriseId) {
|
|
private static void requireEnterpriseId(String enterpriseId) {
|
|
|
if (enterpriseId == null || enterpriseId.isBlank())
|
|
if (enterpriseId == null || enterpriseId.isBlank())
|