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,15 +19,6 @@ import java.util.Set;
19 public class AlipayPayClientConfig implements PayClientConfig { 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 public static final Integer MODE_PUBLIC_KEY = 1; 24 public static final Integer MODE_PUBLIC_KEY = 1;
@@ -43,8 +34,9 @@ public class AlipayPayClientConfig implements PayClientConfig { @@ -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 @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) 41 @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
50 private String serverUrl; 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,6 +24,8 @@ import java.io.FileNotFoundException;
24 @Disabled 24 @Disabled
25 public class PayClientFactoryImplIntegrationTest { 25 public class PayClientFactoryImplIntegrationTest {
26 26
  27 + private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
  28 +
27 private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl(); 29 private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl();
28 30
29 /** 31 /**
@@ -79,7 +81,7 @@ public class PayClientFactoryImplIntegrationTest { @@ -79,7 +81,7 @@ public class PayClientFactoryImplIntegrationTest {
79 // 创建配置 81 // 创建配置
80 AlipayPayClientConfig config = new AlipayPayClientConfig(); 82 AlipayPayClientConfig config = new AlipayPayClientConfig();
81 config.setAppId("2021000118634035"); 83 config.setAppId("2021000118634035");
82 - config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX); 84 + config.setServerUrl(SERVER_URL_SANDBOX);
83 config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); 85 config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
84 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="); 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 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); 87 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
@@ -103,7 +105,7 @@ public class PayClientFactoryImplIntegrationTest { @@ -103,7 +105,7 @@ public class PayClientFactoryImplIntegrationTest {
103 // 创建配置 105 // 创建配置
104 AlipayPayClientConfig config = new AlipayPayClientConfig(); 106 AlipayPayClientConfig config = new AlipayPayClientConfig();
105 config.setAppId("2021000118634035"); 107 config.setAppId("2021000118634035");
106 - config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX); 108 + config.setServerUrl(SERVER_URL_SANDBOX);
107 config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); 109 config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
108 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="); 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 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); 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,9 +82,9 @@ public class TenantUtils {
82 * 将多租户编号,添加到 header 中 82 * 将多租户编号,添加到 header 中
83 * 83 *
84 * @param headers HTTP 请求 headers 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 if (tenantId != null) { 88 if (tenantId != null) {
89 headers.put(HEADER_TENANT_ID, tenantId.toString()); 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 package cn.iocoder.yudao.module.pay.dal.dataobject.notify; 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 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
@@ -25,7 +25,7 @@ import java.time.LocalDateTime; @@ -25,7 +25,7 @@ import java.time.LocalDateTime;
25 @Data 25 @Data
26 @EqualsAndHashCode(callSuper = true) 26 @EqualsAndHashCode(callSuper = true)
27 @Accessors(chain = true) 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,10 +24,11 @@ import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
24 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 24 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.context.annotation.Lazy; 26 import org.springframework.context.annotation.Lazy;
27 -import org.springframework.scheduling.annotation.Async;  
28 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 27 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
29 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
30 import org.springframework.transaction.annotation.Transactional; 29 import org.springframework.transaction.annotation.Transactional;
  30 +import org.springframework.transaction.support.TransactionSynchronization;
  31 +import org.springframework.transaction.support.TransactionSynchronizationManager;
31 32
32 import javax.annotation.Resource; 33 import javax.annotation.Resource;
33 import javax.validation.Valid; 34 import javax.validation.Valid;
@@ -86,6 +87,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -86,6 +87,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
86 private PayNotifyServiceImpl self; 87 private PayNotifyServiceImpl self;
87 88
88 @Override 89 @Override
  90 + @Transactional(rollbackFor = Exception.class)
89 public void createPayNotifyTask(PayNotifyTaskCreateReqDTO reqDTO) { 91 public void createPayNotifyTask(PayNotifyTaskCreateReqDTO reqDTO) {
90 PayNotifyTaskDO task = new PayNotifyTaskDO(); 92 PayNotifyTaskDO task = new PayNotifyTaskDO();
91 task.setType(reqDTO.getType()).setDataId(reqDTO.getDataId()); 93 task.setType(reqDTO.getType()).setDataId(reqDTO.getDataId());
@@ -105,8 +107,13 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -105,8 +107,13 @@ public class PayNotifyServiceImpl implements PayNotifyService {
105 // 执行插入 107 // 执行插入
106 payNotifyTaskMapper.insert(task); 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 @Override 119 @Override
@@ -121,7 +128,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -121,7 +128,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
121 CountDownLatch latch = new CountDownLatch(tasks.size()); 128 CountDownLatch latch = new CountDownLatch(tasks.size());
122 tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> { 129 tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> {
123 try { 130 try {
124 - executeNotifySync(task); 131 + executeNotify(task);
125 } finally { 132 } finally {
126 latch.countDown(); 133 latch.countDown();
127 } 134 }
@@ -151,21 +158,11 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -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 * @param task 通知任务 163 * @param task 通知任务
167 */ 164 */
168 - public void executeNotifySync(PayNotifyTaskDO task) { 165 + public void executeNotify(PayNotifyTaskDO task) {
169 // 分布式锁,避免并发问题 166 // 分布式锁,避免并发问题
170 payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { 167 payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> {
171 // 校验,当前任务是否已经被通知过 168 // 校验,当前任务是否已经被通知过
@@ -178,12 +175,12 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -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 @Transactional 182 @Transactional
186 - public void executeNotify(PayNotifyTaskDO task) { 183 + public void executeNotify0(PayNotifyTaskDO task) {
187 // 发起回调 184 // 发起回调
188 CommonResult<?> invokeResult = null; 185 CommonResult<?> invokeResult = null;
189 Throwable invokeException = null; 186 Throwable invokeException = null;
@@ -223,7 +220,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -223,7 +220,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
223 } 220 }
224 // 拼接 header 参数 221 // 拼接 header 参数
225 Map<String, String> headers = new HashMap<>(); 222 Map<String, String> headers = new HashMap<>();
226 - TenantUtils.addTenantHeader(headers); 223 + TenantUtils.addTenantHeader(headers, task.getTenantId());
227 224
228 // 发起请求 225 // 发起请求
229 try (HttpResponse response = HttpUtil.createPost(task.getNotifyUrl()) 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,6 +33,8 @@ import static org.junit.jupiter.api.Assertions.*;
33 @Import({PayChannelServiceImpl.class}) 33 @Import({PayChannelServiceImpl.class})
34 public class PayChannelServiceTest extends BaseDbUnitTest { 34 public class PayChannelServiceTest extends BaseDbUnitTest {
35 35
  36 + private static final String ALIPAY_SERVER_URL = "https://openapi.alipay.com/gateway.do";
  37 +
36 @Resource 38 @Resource
37 private PayChannelServiceImpl channelService; 39 private PayChannelServiceImpl channelService;
38 40
@@ -106,7 +108,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -106,7 +108,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
106 assertPojoEquals(reqVO, channel, "config"); 108 assertPojoEquals(reqVO, channel, "config");
107 // 关于config 对象应该拿出来重新对比 109 // 关于config 对象应该拿出来重新对比
108 assertPojoEquals(payClientConfig, channel.getConfig()); 110 assertPojoEquals(payClientConfig, channel.getConfig());
109 -  
110 } 111 }
111 112
112 @Test 113 @Test
@@ -291,7 +292,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -291,7 +292,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
291 292
292 public AlipayPayClientConfig getPublicKeyConfig() { 293 public AlipayPayClientConfig getPublicKeyConfig() {
293 return new AlipayPayClientConfig() 294 return new AlipayPayClientConfig()
294 - .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD) 295 + .setServerUrl(ALIPAY_SERVER_URL)
295 .setAppId("APP00001") 296 .setAppId("APP00001")
296 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) 297 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
297 .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY) 298 .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
@@ -304,7 +305,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -304,7 +305,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
304 305
305 public AlipayPayClientConfig getCertificateConfig() { 306 public AlipayPayClientConfig getCertificateConfig() {
306 return new AlipayPayClientConfig() 307 return new AlipayPayClientConfig()
307 - .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD) 308 + .setServerUrl(ALIPAY_SERVER_URL)
308 .setAppId("APP00001") 309 .setAppId("APP00001")
309 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) 310 .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
310 .setMode(AlipayPayClientConfig.MODE_CERTIFICATE) 311 .setMode(AlipayPayClientConfig.MODE_CERTIFICATE)
yudao-ui-admin/src/plugins/tab.js
@@ -36,6 +36,9 @@ export default { @@ -36,6 +36,9 @@ export default {
36 return router.push(lastPath || '/'); 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 return store.dispatch('tagsView/delView', obj); 42 return store.dispatch('tagsView/delView', obj);
40 }, 43 },
41 // 关闭所有tab页签 44 // 关闭所有tab页签
yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue
@@ -41,17 +41,17 @@ @@ -41,17 +41,17 @@
41 </el-radio-group> 41 </el-radio-group>
42 </el-form-item> 42 </el-form-item>
43 <div v-if="form.aliPayConfig.mode === 1"> 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 <el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey" 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 </el-input> 47 </el-input>
48 </el-form-item> 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 <el-input 50 <el-input
51 type="textarea" 51 type="textarea"
52 :autosize="{minRows: 8, maxRows: 8}" 52 :autosize="{minRows: 8, maxRows: 8}"
53 v-model="form.aliPayConfig.alipayPublicKey" 53 v-model="form.aliPayConfig.alipayPublicKey"
54 - placeholder="请输入支付宝公钥字符串" clearable 54 + placeholder="请输入支付宝公钥" clearable
55 :style="{width: '100%'}"> 55 :style="{width: '100%'}">
56 </el-input> 56 </el-input>
57 </el-form-item> 57 </el-form-item>
yudao-ui-admin/src/views/pay/cashier/index.vue
@@ -48,15 +48,6 @@ @@ -48,15 +48,6 @@
48 <qrcode-vue :value="qrCode.url" size="310" level="L" /> 48 <qrcode-vue :value="qrCode.url" size="310" level="L" />
49 </el-dialog> 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 <!-- 展示形式:BarCode 条形码 --> 51 <!-- 展示形式:BarCode 条形码 -->
61 <el-dialog :title="barCode.title" :visible.sync="barCode.visible" width="500px" append-to-body 52 <el-dialog :title="barCode.title" :visible.sync="barCode.visible" width="500px" append-to-body
62 :close-on-press-escape="false"> 53 :close-on-press-escape="false">
@@ -146,14 +137,6 @@ export default { @@ -146,14 +137,6 @@ export default {
146 title: '', 137 title: '',
147 visible: false, 138 visible: false,
148 }, 139 },
149 - iframe: { // 展示形式:iframe  
150 - url: '',  
151 - title: '',  
152 - visible: false  
153 - },  
154 - form: { // 展示形式:form  
155 - html: '',  
156 - },  
157 barCode: { // 展示形式:条形码 140 barCode: { // 展示形式:条形码
158 channelCode: '', 141 channelCode: '',
159 value: '', 142 value: '',
@@ -175,21 +158,24 @@ export default { @@ -175,21 +158,24 @@ export default {
175 // 1.1 未传递订单编号 158 // 1.1 未传递订单编号
176 if (!this.id) { 159 if (!this.id) {
177 this.$message.error('未传递支付单号,无法查看对应的支付信息'); 160 this.$message.error('未传递支付单号,无法查看对应的支付信息');
178 - this.goBackToList(); 161 + this.goReturnUrl('cancel');
179 return; 162 return;
180 } 163 }
181 getOrder(this.id).then(response => { 164 getOrder(this.id).then(response => {
182 // 1.2 无法查询到支付信息 165 // 1.2 无法查询到支付信息
183 if (!response.data) { 166 if (!response.data) {
184 this.$message.error('支付订单不存在,请检查!'); 167 this.$message.error('支付订单不存在,请检查!');
185 - this.goBackToList(); 168 + this.goReturnUrl('cancel');
186 return; 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 return; 179 return;
194 } 180 }
195 181
@@ -218,15 +204,12 @@ export default { @@ -218,15 +204,12 @@ export default {
218 submitOrder({ 204 submitOrder({
219 id: this.id, 205 id: this.id,
220 channelCode: channelCode, 206 channelCode: channelCode,
  207 + returnUrl: location.href, // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址
221 ...this.buildSubmitParam(channelCode) 208 ...this.buildSubmitParam(channelCode)
222 }).then(response => { 209 }).then(response => {
223 const data = response.data 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 this.displayUrl(channelCode, data) 212 this.displayUrl(channelCode, data)
228 - } else if (data.displayMode === PayDisplayModeEnum.FORM.mode) {  
229 - this.displayForm(channelCode, data)  
230 } else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode) { 213 } else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode) {
231 this.displayQrCode(channelCode, data) 214 this.displayQrCode(channelCode, data)
232 } 215 }
@@ -239,53 +222,7 @@ export default { @@ -239,53 +222,7 @@ export default {
239 }, 222 },
240 /** 构建提交支付的额外参数 */ 223 /** 构建提交支付的额外参数 */
241 buildSubmitParam(channelCode) { 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 if (channelCode === PayChannelEnum.ALIPAY_BAR.code) { 226 if (channelCode === PayChannelEnum.ALIPAY_BAR.code) {
290 return { 227 return {
291 "channelExtras": { 228 "channelExtras": {
@@ -295,37 +232,12 @@ export default { @@ -295,37 +232,12 @@ export default {
295 } 232 }
296 return {} 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 /** 提交支付后,URL 的展示形式 */ 235 /** 提交支付后,URL 的展示形式 */
309 displayUrl(channelCode, data) { 236 displayUrl(channelCode, data) {
310 - // window.open(data.displayContent)window 237 + // window.open(data.displayContent)
311 location.href = data.displayContent 238 location.href = data.displayContent
312 this.submitLoading = false 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 displayQrCode(channelCode, data) { 242 displayQrCode(channelCode, data) {
331 let title = '请使用手机浏览器“扫一扫”'; 243 let title = '请使用手机浏览器“扫一扫”';
@@ -354,13 +266,13 @@ export default { @@ -354,13 +266,13 @@ export default {
354 if (response.data.status === PayOrderStatusEnum.SUCCESS.status) { 266 if (response.data.status === PayOrderStatusEnum.SUCCESS.status) {
355 this.clearQueryInterval(); 267 this.clearQueryInterval();
356 this.$message.success('支付成功!'); 268 this.$message.success('支付成功!');
357 - this.goBackToList(); 269 + this.goReturnUrl();
358 } 270 }
359 // 已取消 271 // 已取消
360 if (response.data.status === PayOrderStatusEnum.CLOSED.status) { 272 if (response.data.status === PayOrderStatusEnum.CLOSED.status) {
361 this.clearQueryInterval(); 273 this.clearQueryInterval();
362 this.$message.error('支付已关闭!'); 274 this.$message.error('支付已关闭!');
363 - this.goBackToList(); 275 + this.goReturnUrl();
364 } 276 }
365 }) 277 })
366 }, 1000 * 2) 278 }, 1000 * 2)
@@ -377,14 +289,34 @@ export default { @@ -377,14 +289,34 @@ export default {
377 clearInterval(this.interval) 289 clearInterval(this.interval)
378 this.interval = undefined 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 };