Commit 0ac599a3d948a172cbda1216ee04f15a84e691be
1 parent
f1c4c796
mall + pay:
1. 修复支付宝沙箱地址的调整 2. 收银台的前端 URL 支付走整个网页跳转 3. 收银台的支付成功后,增加 returnUrl 回跳 4. 修复 PayNotifyTask 首次通知时,可能会失败的情况;原因:租户未传递;
Showing
9 changed files
with
81 additions
and
154 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java
| ... | ... | @@ -19,15 +19,6 @@ import java.util.Set; |
| 19 | 19 | public class AlipayPayClientConfig implements PayClientConfig { |
| 20 | 20 | |
| 21 | 21 | /** |
| 22 | - * 网关地址 - 线上 | |
| 23 | - */ | |
| 24 | - public static final String SERVER_URL_PROD = "https://openapi.alipay.com/gateway.do"; | |
| 25 | - /** | |
| 26 | - * 网关地址 - 沙箱 | |
| 27 | - */ | |
| 28 | - public static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do"; | |
| 29 | - | |
| 30 | - /** | |
| 31 | 22 | * 公钥类型 - 公钥模式 |
| 32 | 23 | */ |
| 33 | 24 | public static final Integer MODE_PUBLIC_KEY = 1; |
| ... | ... | @@ -43,8 +34,9 @@ public class AlipayPayClientConfig implements PayClientConfig { |
| 43 | 34 | |
| 44 | 35 | /** |
| 45 | 36 | * 网关地址 |
| 46 | - * 1. {@link #SERVER_URL_PROD} | |
| 47 | - * 2. {@link #SERVER_URL_SANDBOX} | |
| 37 | + * | |
| 38 | + * 1. <a href="https://openapi.alipay.com/gateway.do">生产环境</a> | |
| 39 | + * 2. <a href="https://openapi-sandbox.dl.alipaydev.com/gateway.do">沙箱环境</a> | |
| 48 | 40 | */ |
| 49 | 41 | @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) |
| 50 | 42 | private String serverUrl; | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java
| ... | ... | @@ -24,6 +24,8 @@ import java.io.FileNotFoundException; |
| 24 | 24 | @Disabled |
| 25 | 25 | public class PayClientFactoryImplIntegrationTest { |
| 26 | 26 | |
| 27 | + private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do"; | |
| 28 | + | |
| 27 | 29 | private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl(); |
| 28 | 30 | |
| 29 | 31 | /** |
| ... | ... | @@ -79,7 +81,7 @@ public class PayClientFactoryImplIntegrationTest { |
| 79 | 81 | // 创建配置 |
| 80 | 82 | AlipayPayClientConfig config = new AlipayPayClientConfig(); |
| 81 | 83 | config.setAppId("2021000118634035"); |
| 82 | - config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX); | |
| 84 | + config.setServerUrl(SERVER_URL_SANDBOX); | |
| 83 | 85 | config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); |
| 84 | 86 | config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8="); |
| 85 | 87 | config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); |
| ... | ... | @@ -103,7 +105,7 @@ public class PayClientFactoryImplIntegrationTest { |
| 103 | 105 | // 创建配置 |
| 104 | 106 | AlipayPayClientConfig config = new AlipayPayClientConfig(); |
| 105 | 107 | config.setAppId("2021000118634035"); |
| 106 | - config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX); | |
| 108 | + config.setServerUrl(SERVER_URL_SANDBOX); | |
| 107 | 109 | config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); |
| 108 | 110 | config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8="); |
| 109 | 111 | config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
| ... | ... | @@ -82,9 +82,9 @@ public class TenantUtils { |
| 82 | 82 | * 将多租户编号,添加到 header 中 |
| 83 | 83 | * |
| 84 | 84 | * @param headers HTTP 请求 headers |
| 85 | + * @param tenantId 租户编号 | |
| 85 | 86 | */ |
| 86 | - public static void addTenantHeader(Map<String, String> headers) { | |
| 87 | - Long tenantId = TenantContextHolder.getTenantId(); | |
| 87 | + public static void addTenantHeader(Map<String, String> headers, Long tenantId) { | |
| 88 | 88 | if (tenantId != null) { |
| 89 | 89 | headers.put(HEADER_TENANT_ID, tenantId.toString()); |
| 90 | 90 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.dal.dataobject.notify; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | |
| 4 | 4 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| ... | ... | @@ -25,7 +25,7 @@ import java.time.LocalDateTime; |
| 25 | 25 | @Data |
| 26 | 26 | @EqualsAndHashCode(callSuper = true) |
| 27 | 27 | @Accessors(chain = true) |
| 28 | -public class PayNotifyTaskDO extends BaseDO { | |
| 28 | +public class PayNotifyTaskDO extends TenantBaseDO { | |
| 29 | 29 | |
| 30 | 30 | /** |
| 31 | 31 | * 通知频率,单位为秒。 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
| ... | ... | @@ -24,10 +24,11 @@ import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 24 | 24 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 25 | 25 | import lombok.extern.slf4j.Slf4j; |
| 26 | 26 | import org.springframework.context.annotation.Lazy; |
| 27 | -import org.springframework.scheduling.annotation.Async; | |
| 28 | 27 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| 29 | 28 | import org.springframework.stereotype.Service; |
| 30 | 29 | import org.springframework.transaction.annotation.Transactional; |
| 30 | +import org.springframework.transaction.support.TransactionSynchronization; | |
| 31 | +import org.springframework.transaction.support.TransactionSynchronizationManager; | |
| 31 | 32 | |
| 32 | 33 | import javax.annotation.Resource; |
| 33 | 34 | import javax.validation.Valid; |
| ... | ... | @@ -86,6 +87,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 86 | 87 | private PayNotifyServiceImpl self; |
| 87 | 88 | |
| 88 | 89 | @Override |
| 90 | + @Transactional(rollbackFor = Exception.class) | |
| 89 | 91 | public void createPayNotifyTask(PayNotifyTaskCreateReqDTO reqDTO) { |
| 90 | 92 | PayNotifyTaskDO task = new PayNotifyTaskDO(); |
| 91 | 93 | task.setType(reqDTO.getType()).setDataId(reqDTO.getDataId()); |
| ... | ... | @@ -105,8 +107,13 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 105 | 107 | // 执行插入 |
| 106 | 108 | payNotifyTaskMapper.insert(task); |
| 107 | 109 | |
| 108 | - // 异步直接发起任务。虽然会有定时任务扫描,但是会导致延迟 | |
| 109 | - self.executeNotifyAsync(task); | |
| 110 | + // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务 | |
| 111 | + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { | |
| 112 | + @Override | |
| 113 | + public void afterCommit() { | |
| 114 | + executeNotify(task); | |
| 115 | + } | |
| 116 | + }); | |
| 110 | 117 | } |
| 111 | 118 | |
| 112 | 119 | @Override |
| ... | ... | @@ -121,7 +128,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 121 | 128 | CountDownLatch latch = new CountDownLatch(tasks.size()); |
| 122 | 129 | tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> { |
| 123 | 130 | try { |
| 124 | - executeNotifySync(task); | |
| 131 | + executeNotify(task); | |
| 125 | 132 | } finally { |
| 126 | 133 | latch.countDown(); |
| 127 | 134 | } |
| ... | ... | @@ -151,21 +158,11 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 151 | 158 | } |
| 152 | 159 | |
| 153 | 160 | /** |
| 154 | - * 异步执行单个支付通知 | |
| 155 | - * | |
| 156 | - * @param task 通知任务 | |
| 157 | - */ | |
| 158 | - @Async | |
| 159 | - public void executeNotifyAsync(PayNotifyTaskDO task) { | |
| 160 | - self.executeNotifySync(task); // 使用 self,避免事务不发起 | |
| 161 | - } | |
| 162 | - | |
| 163 | - /** | |
| 164 | 161 | * 同步执行单个支付通知 |
| 165 | 162 | * |
| 166 | 163 | * @param task 通知任务 |
| 167 | 164 | */ |
| 168 | - public void executeNotifySync(PayNotifyTaskDO task) { | |
| 165 | + public void executeNotify(PayNotifyTaskDO task) { | |
| 169 | 166 | // 分布式锁,避免并发问题 |
| 170 | 167 | payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { |
| 171 | 168 | // 校验,当前任务是否已经被通知过 |
| ... | ... | @@ -178,12 +175,12 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 178 | 175 | } |
| 179 | 176 | |
| 180 | 177 | // 执行通知 |
| 181 | - self.executeNotify(dbTask); | |
| 178 | + self.executeNotify0(dbTask); | |
| 182 | 179 | }); |
| 183 | 180 | } |
| 184 | 181 | |
| 185 | 182 | @Transactional |
| 186 | - public void executeNotify(PayNotifyTaskDO task) { | |
| 183 | + public void executeNotify0(PayNotifyTaskDO task) { | |
| 187 | 184 | // 发起回调 |
| 188 | 185 | CommonResult<?> invokeResult = null; |
| 189 | 186 | Throwable invokeException = null; |
| ... | ... | @@ -223,7 +220,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 223 | 220 | } |
| 224 | 221 | // 拼接 header 参数 |
| 225 | 222 | Map<String, String> headers = new HashMap<>(); |
| 226 | - TenantUtils.addTenantHeader(headers); | |
| 223 | + TenantUtils.addTenantHeader(headers, task.getTenantId()); | |
| 227 | 224 | |
| 228 | 225 | // 发起请求 |
| 229 | 226 | try (HttpResponse response = HttpUtil.createPost(task.getNotifyUrl()) | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
| ... | ... | @@ -33,6 +33,8 @@ import static org.junit.jupiter.api.Assertions.*; |
| 33 | 33 | @Import({PayChannelServiceImpl.class}) |
| 34 | 34 | public class PayChannelServiceTest extends BaseDbUnitTest { |
| 35 | 35 | |
| 36 | + private static final String ALIPAY_SERVER_URL = "https://openapi.alipay.com/gateway.do"; | |
| 37 | + | |
| 36 | 38 | @Resource |
| 37 | 39 | private PayChannelServiceImpl channelService; |
| 38 | 40 | |
| ... | ... | @@ -106,7 +108,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest { |
| 106 | 108 | assertPojoEquals(reqVO, channel, "config"); |
| 107 | 109 | // 关于config 对象应该拿出来重新对比 |
| 108 | 110 | assertPojoEquals(payClientConfig, channel.getConfig()); |
| 109 | - | |
| 110 | 111 | } |
| 111 | 112 | |
| 112 | 113 | @Test |
| ... | ... | @@ -291,7 +292,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { |
| 291 | 292 | |
| 292 | 293 | public AlipayPayClientConfig getPublicKeyConfig() { |
| 293 | 294 | return new AlipayPayClientConfig() |
| 294 | - .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD) | |
| 295 | + .setServerUrl(ALIPAY_SERVER_URL) | |
| 295 | 296 | .setAppId("APP00001") |
| 296 | 297 | .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) |
| 297 | 298 | .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY) |
| ... | ... | @@ -304,7 +305,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { |
| 304 | 305 | |
| 305 | 306 | public AlipayPayClientConfig getCertificateConfig() { |
| 306 | 307 | return new AlipayPayClientConfig() |
| 307 | - .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD) | |
| 308 | + .setServerUrl(ALIPAY_SERVER_URL) | |
| 308 | 309 | .setAppId("APP00001") |
| 309 | 310 | .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) |
| 310 | 311 | .setMode(AlipayPayClientConfig.MODE_CERTIFICATE) | ... | ... |
yudao-ui-admin/src/plugins/tab.js
| ... | ... | @@ -36,6 +36,9 @@ export default { |
| 36 | 36 | return router.push(lastPath || '/'); |
| 37 | 37 | }); |
| 38 | 38 | } |
| 39 | + if (typeof obj === "function") { | |
| 40 | + return store.dispatch('tagsView/delView', router.currentRoute).then(obj); | |
| 41 | + } | |
| 39 | 42 | return store.dispatch('tagsView/delView', obj); |
| 40 | 43 | }, |
| 41 | 44 | // 关闭所有tab页签 | ... | ... |
yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue
| ... | ... | @@ -41,17 +41,17 @@ |
| 41 | 41 | </el-radio-group> |
| 42 | 42 | </el-form-item> |
| 43 | 43 | <div v-if="form.aliPayConfig.mode === 1"> |
| 44 | - <el-form-item label-width="180px" label="商户私钥" prop="aliPayConfig.privateKey"> | |
| 44 | + <el-form-item label-width="180px" label="应用私钥" prop="aliPayConfig.privateKey"> | |
| 45 | 45 | <el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey" |
| 46 | - placeholder="请输入商户私钥" clearable :style="{width: '100%'}"> | |
| 46 | + placeholder="请输入应用私钥" clearable :style="{width: '100%'}"> | |
| 47 | 47 | </el-input> |
| 48 | 48 | </el-form-item> |
| 49 | - <el-form-item label-width="180px" label="支付宝公钥字符串" prop="aliPayConfig.alipayPublicKey"> | |
| 49 | + <el-form-item label-width="180px" label="支付宝公钥" prop="aliPayConfig.alipayPublicKey"> | |
| 50 | 50 | <el-input |
| 51 | 51 | type="textarea" |
| 52 | 52 | :autosize="{minRows: 8, maxRows: 8}" |
| 53 | 53 | v-model="form.aliPayConfig.alipayPublicKey" |
| 54 | - placeholder="请输入支付宝公钥字符串" clearable | |
| 54 | + placeholder="请输入支付宝公钥" clearable | |
| 55 | 55 | :style="{width: '100%'}"> |
| 56 | 56 | </el-input> |
| 57 | 57 | </el-form-item> | ... | ... |
yudao-ui-admin/src/views/pay/cashier/index.vue
| ... | ... | @@ -48,15 +48,6 @@ |
| 48 | 48 | <qrcode-vue :value="qrCode.url" size="310" level="L" /> |
| 49 | 49 | </el-dialog> |
| 50 | 50 | |
| 51 | - <!-- 展示形式:IFrame --> | |
| 52 | - <el-dialog :title="iframe.title" :visible.sync="iframe.visible" width="800px" height="800px" append-to-body | |
| 53 | - :close-on-press-escape="false"> | |
| 54 | - <iframe :src="iframe.url" width="100%" /> | |
| 55 | - </el-dialog> | |
| 56 | - | |
| 57 | - <!-- 展示形式:Form --> | |
| 58 | - <div ref="formRef" v-html="form.value" /> | |
| 59 | - | |
| 60 | 51 | <!-- 展示形式:BarCode 条形码 --> |
| 61 | 52 | <el-dialog :title="barCode.title" :visible.sync="barCode.visible" width="500px" append-to-body |
| 62 | 53 | :close-on-press-escape="false"> |
| ... | ... | @@ -146,14 +137,6 @@ export default { |
| 146 | 137 | title: '', |
| 147 | 138 | visible: false, |
| 148 | 139 | }, |
| 149 | - iframe: { // 展示形式:iframe | |
| 150 | - url: '', | |
| 151 | - title: '', | |
| 152 | - visible: false | |
| 153 | - }, | |
| 154 | - form: { // 展示形式:form | |
| 155 | - html: '', | |
| 156 | - }, | |
| 157 | 140 | barCode: { // 展示形式:条形码 |
| 158 | 141 | channelCode: '', |
| 159 | 142 | value: '', |
| ... | ... | @@ -175,21 +158,24 @@ export default { |
| 175 | 158 | // 1.1 未传递订单编号 |
| 176 | 159 | if (!this.id) { |
| 177 | 160 | this.$message.error('未传递支付单号,无法查看对应的支付信息'); |
| 178 | - this.goBackToList(); | |
| 161 | + this.goReturnUrl('cancel'); | |
| 179 | 162 | return; |
| 180 | 163 | } |
| 181 | 164 | getOrder(this.id).then(response => { |
| 182 | 165 | // 1.2 无法查询到支付信息 |
| 183 | 166 | if (!response.data) { |
| 184 | 167 | this.$message.error('支付订单不存在,请检查!'); |
| 185 | - this.goBackToList(); | |
| 168 | + this.goReturnUrl('cancel'); | |
| 186 | 169 | return; |
| 187 | 170 | } |
| 188 | - // 1.3 订单已支付 | |
| 189 | - // TODO 芋艿:已支付 | |
| 190 | - if (response.data.status !== PayOrderStatusEnum.WAITING.status) { | |
| 191 | - this.$message.error('支付订单不处于待支付状态,请检查!'); | |
| 192 | - this.goBackToList(); | |
| 171 | + // 1.3 如果已支付、或者已关闭,则直接跳转 | |
| 172 | + if (response.data.status === PayOrderStatusEnum.SUCCESS.status) { | |
| 173 | + this.$message.success('支付成功'); | |
| 174 | + this.goReturnUrl('success'); | |
| 175 | + return; | |
| 176 | + } else if (response.data.status === PayOrderStatusEnum.CLOSED.status) { | |
| 177 | + this.$message.error('无法支付,原因:订单已关闭'); | |
| 178 | + this.goReturnUrl('close'); | |
| 193 | 179 | return; |
| 194 | 180 | } |
| 195 | 181 | |
| ... | ... | @@ -218,15 +204,12 @@ export default { |
| 218 | 204 | submitOrder({ |
| 219 | 205 | id: this.id, |
| 220 | 206 | channelCode: channelCode, |
| 207 | + returnUrl: location.href, // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址 | |
| 221 | 208 | ...this.buildSubmitParam(channelCode) |
| 222 | 209 | }).then(response => { |
| 223 | 210 | const data = response.data |
| 224 | - if (data.displayMode === PayDisplayModeEnum.IFRAME.mode) { | |
| 225 | - this.displayIFrame(channelCode, data) | |
| 226 | - } else if (data.displayMode === PayDisplayModeEnum.URL.mode) { | |
| 211 | + if (data.displayMode === PayDisplayModeEnum.URL.mode) { | |
| 227 | 212 | this.displayUrl(channelCode, data) |
| 228 | - } else if (data.displayMode === PayDisplayModeEnum.FORM.mode) { | |
| 229 | - this.displayForm(channelCode, data) | |
| 230 | 213 | } else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode) { |
| 231 | 214 | this.displayQrCode(channelCode, data) |
| 232 | 215 | } |
| ... | ... | @@ -239,53 +222,7 @@ export default { |
| 239 | 222 | }, |
| 240 | 223 | /** 构建提交支付的额外参数 */ |
| 241 | 224 | buildSubmitParam(channelCode) { |
| 242 | - // ① 支付宝 PC 支付时,有多种展示形态 | |
| 243 | - if (channelCode === PayChannelEnum.ALIPAY_PC.code) { | |
| 244 | - // 情况【前置模式】:将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以 iframe 方式请求支付宝页面。具体支持的枚举值有以下几种: | |
| 245 | - // 0:订单码-简约前置模式,对应 iframe 宽度不能小于 600px,高度不能小于 300px | |
| 246 | - // return { | |
| 247 | - // "channelExtras": { | |
| 248 | - // "qr_pay_mode": "0" | |
| 249 | - // } | |
| 250 | - // } | |
| 251 | - // 1:订单码-前置模式,对应iframe 宽度不能小于 300px,高度不能小于 600px | |
| 252 | - // return { | |
| 253 | - // "channelExtras": { | |
| 254 | - // "qr_pay_mode": "1" | |
| 255 | - // } | |
| 256 | - // } | |
| 257 | - // 3:订单码-迷你前置模式,对应 iframe 宽度不能小于 75px,高度不能小于 75px | |
| 258 | - // return { | |
| 259 | - // "channelExtras": { | |
| 260 | - // "qr_pay_mode": "3" | |
| 261 | - // } | |
| 262 | - // } | |
| 263 | - // 4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小 | |
| 264 | - // return { | |
| 265 | - // "channelExtras": { | |
| 266 | - // "qr_pay_mode": "4" | |
| 267 | - // } | |
| 268 | - // } | |
| 269 | - // 情况【跳转模式】:跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。支持传入的枚举值有 | |
| 270 | - return { | |
| 271 | - "channelExtras": { | |
| 272 | - "qr_pay_mode": "2" | |
| 273 | - } | |
| 274 | - } | |
| 275 | - // 情况【表单模式】:直接提交当前页面到支付宝 | |
| 276 | - // return { | |
| 277 | - // displayMode: PayDisplayModeEnum.FORM.mode | |
| 278 | - // } | |
| 279 | - } | |
| 280 | - | |
| 281 | - // ② 支付宝 Wap 支付时,引导手机扫码支付 | |
| 282 | - if (channelCode === PayChannelEnum.ALIPAY_WAP.code) { | |
| 283 | - return { | |
| 284 | - displayMode: PayDisplayModeEnum.QR_CODE.mode | |
| 285 | - } | |
| 286 | - } | |
| 287 | - | |
| 288 | - // ③ 支付宝 BarCode 支付时,需要传递 authCode 条形码 | |
| 225 | + // ① 支付宝 BarCode 支付时,需要传递 authCode 条形码 | |
| 289 | 226 | if (channelCode === PayChannelEnum.ALIPAY_BAR.code) { |
| 290 | 227 | return { |
| 291 | 228 | "channelExtras": { |
| ... | ... | @@ -295,37 +232,12 @@ export default { |
| 295 | 232 | } |
| 296 | 233 | return {} |
| 297 | 234 | }, |
| 298 | - /** 提交支付后,IFrame 内置 URL 的展示形式 */ | |
| 299 | - displayIFrame(channelCode, data) { | |
| 300 | - // TODO 芋艿:目前有点奇怪,支付宝总是会显示“支付环境存在风险” | |
| 301 | - this.iframe = { | |
| 302 | - title: '支付窗口', | |
| 303 | - url: data.displayContent, | |
| 304 | - visible: true | |
| 305 | - } | |
| 306 | - this.submitLoading = false | |
| 307 | - }, | |
| 308 | 235 | /** 提交支付后,URL 的展示形式 */ |
| 309 | 236 | displayUrl(channelCode, data) { |
| 310 | - // window.open(data.displayContent)window | |
| 237 | + // window.open(data.displayContent) | |
| 311 | 238 | location.href = data.displayContent |
| 312 | 239 | this.submitLoading = false |
| 313 | 240 | }, |
| 314 | - /** 提交支付后,Form 的展示形式 */ | |
| 315 | - displayForm(channelCode, data) { | |
| 316 | - // 渲染支付页面 | |
| 317 | - this.form = { | |
| 318 | - value: data.displayContent | |
| 319 | - } | |
| 320 | - // 防抖避免重复支付 | |
| 321 | - this.$nextTick(() => { | |
| 322 | - // 提交支付表单 | |
| 323 | - this.$refs.formRef.children[0].submit(); | |
| 324 | - setTimeout(() => { | |
| 325 | - this.submitLoading = false | |
| 326 | - }, 1000); | |
| 327 | - }); | |
| 328 | - }, | |
| 329 | 241 | /** 提交支付后(支付宝扫码支付) */ |
| 330 | 242 | displayQrCode(channelCode, data) { |
| 331 | 243 | let title = '请使用手机浏览器“扫一扫”'; |
| ... | ... | @@ -354,13 +266,13 @@ export default { |
| 354 | 266 | if (response.data.status === PayOrderStatusEnum.SUCCESS.status) { |
| 355 | 267 | this.clearQueryInterval(); |
| 356 | 268 | this.$message.success('支付成功!'); |
| 357 | - this.goBackToList(); | |
| 269 | + this.goReturnUrl(); | |
| 358 | 270 | } |
| 359 | 271 | // 已取消 |
| 360 | 272 | if (response.data.status === PayOrderStatusEnum.CLOSED.status) { |
| 361 | 273 | this.clearQueryInterval(); |
| 362 | 274 | this.$message.error('支付已关闭!'); |
| 363 | - this.goBackToList(); | |
| 275 | + this.goReturnUrl(); | |
| 364 | 276 | } |
| 365 | 277 | }) |
| 366 | 278 | }, 1000 * 2) |
| ... | ... | @@ -377,14 +289,34 @@ export default { |
| 377 | 289 | clearInterval(this.interval) |
| 378 | 290 | this.interval = undefined |
| 379 | 291 | }, |
| 380 | - /** 回到列表 **/ | |
| 381 | - goBackToList() { | |
| 382 | - this.$tab.closePage(); | |
| 383 | - this.$router.go(-1); | |
| 384 | - // TODO 芋艿:需要优化; | |
| 385 | - // this.$router.push({ | |
| 386 | - // path: this.returnUrl | |
| 387 | - // }); | |
| 292 | + /** | |
| 293 | + * 回到业务的 URL | |
| 294 | + * | |
| 295 | + * @param payResult 支付结果 | |
| 296 | + * ① success:支付成功 | |
| 297 | + * ② cancel:取消支付 | |
| 298 | + * ③ close:支付已关闭 | |
| 299 | + */ | |
| 300 | + goReturnUrl(payResult) { | |
| 301 | + // 未配置的情况下,只能关闭 | |
| 302 | + if (!this.returnUrl) { | |
| 303 | + this.$tab.closePage(); | |
| 304 | + return | |
| 305 | + } | |
| 306 | + | |
| 307 | + const url = this.returnUrl.indexOf('?') >= 0 | |
| 308 | + ? this.returnUrl + '&payResult=' + payResult | |
| 309 | + : this.returnUrl + '?payResult=' + payResult | |
| 310 | + // 如果有配置,且是 http 开头,则浏览器跳转 | |
| 311 | + if (this.returnUrl.indexOf('http') === 0) { | |
| 312 | + location.href = url; | |
| 313 | + } else { | |
| 314 | + this.$tab.closePage(() => { | |
| 315 | + this.$router.push({ | |
| 316 | + path: url | |
| 317 | + }); | |
| 318 | + }); | |
| 319 | + } | |
| 388 | 320 | } |
| 389 | 321 | } |
| 390 | 322 | }; | ... | ... |