Commit 0ac599a3d948a172cbda1216ee04f15a84e691be

Authored by YunaiV
1 parent f1c4c796

mall + pay:

1. 修复支付宝沙箱地址的调整
2. 收银台的前端 URL 支付走整个网页跳转
3. 收银台的支付成功后,增加 returnUrl 回跳
4. 修复 PayNotifyTask 首次通知时,可能会失败的情况;原因:租户未传递;
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 };
... ...