Commit 66ed61c6416e36adcc7227202130b86737bc9dda
1 parent
7caf8e90
mall + pay:
1. 微信支付配置,补全 apiclient_cert.p12 证书
Showing
6 changed files
with
120 additions
and
59 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
| 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.bean.BeanUtil; | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | +import cn.hutool.core.codec.Base64; | ||
| 4 | import cn.hutool.core.date.LocalDateTimeUtil; | 5 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 5 | import cn.hutool.core.date.TemporalAccessorUtil; | 6 | import cn.hutool.core.date.TemporalAccessorUtil; |
| 6 | import cn.hutool.core.lang.Assert; | 7 | import cn.hutool.core.lang.Assert; |
| @@ -34,6 +35,7 @@ import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; | @@ -34,6 +35,7 @@ import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; | ||
| 34 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | 36 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| 36 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | 37 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 38 | +import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2; | ||
| 37 | 39 | ||
| 38 | /** | 40 | /** |
| 39 | * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款) | 41 | * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款) |
| @@ -59,12 +61,17 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | @@ -59,12 +61,17 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 59 | WxPayConfig payConfig = new WxPayConfig(); | 61 | WxPayConfig payConfig = new WxPayConfig(); |
| 60 | BeanUtil.copyProperties(config, payConfig, "keyContent"); | 62 | BeanUtil.copyProperties(config, payConfig, "keyContent"); |
| 61 | payConfig.setTradeType(tradeType); | 63 | payConfig.setTradeType(tradeType); |
| 64 | +// if (WxPayClientConfig.API_VERSION_V2.equals(config.getApiVersion())) { | ||
| 65 | +// payConfig.setSignType(WxPayConstants.SignType.MD5); | ||
| 66 | +// } | ||
| 67 | + // weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决 | ||
| 68 | + if (Base64.isBase64(config.getKeyContent())) { | ||
| 69 | + payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath()); | ||
| 70 | + } | ||
| 62 | if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { | 71 | if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { |
| 63 | - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 | ||
| 64 | payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); | 72 | payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); |
| 65 | } | 73 | } |
| 66 | if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { | 74 | if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { |
| 67 | - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 | ||
| 68 | payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); | 75 | payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); |
| 69 | } | 76 | } |
| 70 | 77 | ||
| @@ -77,7 +84,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | @@ -77,7 +84,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 77 | protected PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception { | 84 | protected PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception { |
| 78 | try { | 85 | try { |
| 79 | switch (config.getApiVersion()) { | 86 | switch (config.getApiVersion()) { |
| 80 | - case WxPayClientConfig.API_VERSION_V2: | 87 | + case API_VERSION_V2: |
| 81 | return doUnifiedOrderV2(reqDTO); | 88 | return doUnifiedOrderV2(reqDTO); |
| 82 | case WxPayClientConfig.API_VERSION_V3: | 89 | case WxPayClientConfig.API_VERSION_V3: |
| 83 | return doUnifiedOrderV3(reqDTO); | 90 | return doUnifiedOrderV3(reqDTO); |
| @@ -112,7 +119,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | @@ -112,7 +119,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 112 | protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { | 119 | protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { |
| 113 | try { | 120 | try { |
| 114 | switch (config.getApiVersion()) { | 121 | switch (config.getApiVersion()) { |
| 115 | - case WxPayClientConfig.API_VERSION_V2: | 122 | + case API_VERSION_V2: |
| 116 | return doUnifiedRefundV2(reqDTO); | 123 | return doUnifiedRefundV2(reqDTO); |
| 117 | case WxPayClientConfig.API_VERSION_V3: | 124 | case WxPayClientConfig.API_VERSION_V3: |
| 118 | return doUnifiedRefundV3(reqDTO); | 125 | return doUnifiedRefundV3(reqDTO); |
| @@ -160,7 +167,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | @@ -160,7 +167,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 160 | try { | 167 | try { |
| 161 | // 微信支付 v2 回调结果处理 | 168 | // 微信支付 v2 回调结果处理 |
| 162 | switch (config.getApiVersion()) { | 169 | switch (config.getApiVersion()) { |
| 163 | - case WxPayClientConfig.API_VERSION_V2: | 170 | + case API_VERSION_V2: |
| 164 | return parseOrderNotifyV2(body); | 171 | return parseOrderNotifyV2(body); |
| 165 | case WxPayClientConfig.API_VERSION_V3: | 172 | case WxPayClientConfig.API_VERSION_V3: |
| 166 | return parseOrderNotifyV3(body); | 173 | return parseOrderNotifyV3(body); |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
| @@ -37,16 +37,17 @@ public class WxPayClientConfig implements PayClientConfig { | @@ -37,16 +37,17 @@ public class WxPayClientConfig implements PayClientConfig { | ||
| 37 | * | 37 | * |
| 38 | * 只有公众号或小程序需要该字段 | 38 | * 只有公众号或小程序需要该字段 |
| 39 | */ | 39 | */ |
| 40 | + @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class}) | ||
| 40 | private String appId; | 41 | private String appId; |
| 41 | /** | 42 | /** |
| 42 | * 商户号 | 43 | * 商户号 |
| 43 | */ | 44 | */ |
| 44 | - @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class}) | 45 | + @NotBlank(message = "商户号不能为空", groups = {V2.class, V3.class}) |
| 45 | private String mchId; | 46 | private String mchId; |
| 46 | /** | 47 | /** |
| 47 | * API 版本 | 48 | * API 版本 |
| 48 | */ | 49 | */ |
| 49 | - @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class}) | 50 | + @NotBlank(message = "API 版本不能为空", groups = {V2.class, V3.class}) |
| 50 | private String apiVersion; | 51 | private String apiVersion; |
| 51 | 52 | ||
| 52 | // ========== V2 版本的参数 ========== | 53 | // ========== V2 版本的参数 ========== |
| @@ -54,36 +55,31 @@ public class WxPayClientConfig implements PayClientConfig { | @@ -54,36 +55,31 @@ public class WxPayClientConfig implements PayClientConfig { | ||
| 54 | /** | 55 | /** |
| 55 | * 商户密钥 | 56 | * 商户密钥 |
| 56 | */ | 57 | */ |
| 57 | - @NotBlank(message = "商户密钥 不能为空", groups = V2.class) | 58 | + @NotBlank(message = "商户密钥不能为空", groups = V2.class) |
| 58 | private String mchKey; | 59 | private String mchKey; |
| 59 | /** | 60 | /** |
| 60 | - * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||
| 61 | - * 对应的字符串 | 61 | + * apiclient_cert.p12 证书文件的对应字符串【base64 格式】 |
| 62 | * | 62 | * |
| 63 | - * 注意,可通过 {@link #main(String[])} 读取 | 63 | + * 为什么采用 base64 格式?因为 p12 读取后是二进制,需要转换成 base64 格式才好传输和存储 |
| 64 | */ | 64 | */ |
| 65 | - /// private String keyContent; | 65 | + @NotBlank(message = "apiclient_cert.p12 不能为空", groups = V2.class) |
| 66 | + private String keyContent; | ||
| 66 | 67 | ||
| 67 | // ========== V3 版本的参数 ========== | 68 | // ========== V3 版本的参数 ========== |
| 68 | /** | 69 | /** |
| 69 | - * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||
| 70 | - * 对应的字符串 | ||
| 71 | - * 注意,可通过 {@link #main(String[])} 读取 | 70 | + * apiclient_key.pem 证书文件的对应字符串 |
| 72 | */ | 71 | */ |
| 73 | @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) | 72 | @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) |
| 74 | private String privateKeyContent; | 73 | private String privateKeyContent; |
| 75 | /** | 74 | /** |
| 76 | - * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||
| 77 | - * 对应的字符串 | ||
| 78 | - * <p> | ||
| 79 | - * 注意,可通过 {@link #main(String[])} 读取 | 75 | + * apiclient_cert.pem 证书文件的对应的字符串 |
| 80 | */ | 76 | */ |
| 81 | @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) | 77 | @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) |
| 82 | private String privateCertContent; | 78 | private String privateCertContent; |
| 83 | /** | 79 | /** |
| 84 | * apiV3 密钥值 | 80 | * apiV3 密钥值 |
| 85 | */ | 81 | */ |
| 86 | - @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class) | 82 | + @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class) |
| 87 | private String apiV3Key; | 83 | private String apiV3Key; |
| 88 | 84 | ||
| 89 | /** | 85 | /** |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java
| @@ -44,7 +44,7 @@ public class WxBarPayClientIntegrationTest { | @@ -44,7 +44,7 @@ public class WxBarPayClientIntegrationTest { | ||
| 44 | .totalFee(1) // 单位分 | 44 | .totalFee(1) // 单位分 |
| 45 | .timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2)))) | 45 | .timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2)))) |
| 46 | .spbillCreateIp("127.0.0.1") | 46 | .spbillCreateIp("127.0.0.1") |
| 47 | - .authCode("131276541518138032") | 47 | + .authCode("134298744426278497") |
| 48 | .build(); | 48 | .build(); |
| 49 | System.out.println("========= request =========="); | 49 | System.out.println("========= request =========="); |
| 50 | System.out.println(JsonUtils.toJsonPrettyString(request)); | 50 | System.out.println(JsonUtils.toJsonPrettyString(request)); |
| @@ -63,7 +63,7 @@ public class WxBarPayClientIntegrationTest { | @@ -63,7 +63,7 @@ public class WxBarPayClientIntegrationTest { | ||
| 63 | 63 | ||
| 64 | // 执行发起退款 | 64 | // 执行发起退款 |
| 65 | WxPayRefundRequest request = new WxPayRefundRequest() | 65 | WxPayRefundRequest request = new WxPayRefundRequest() |
| 66 | - .setOutTradeNo("1689504162805") | 66 | + .setOutTradeNo("1689545667276") |
| 67 | .setOutRefundNo(String.valueOf(System.currentTimeMillis())) | 67 | .setOutRefundNo(String.valueOf(System.currentTimeMillis())) |
| 68 | .setRefundFee(1) | 68 | .setRefundFee(1) |
| 69 | .setRefundDesc("就是想退了") | 69 | .setRefundDesc("就是想退了") |
| @@ -103,8 +103,6 @@ public class WxBarPayClientIntegrationTest { | @@ -103,8 +103,6 @@ public class WxBarPayClientIntegrationTest { | ||
| 103 | config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); | 103 | config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); |
| 104 | config.setSignType(WxPayConstants.SignType.MD5); | 104 | config.setSignType(WxPayConstants.SignType.MD5); |
| 105 | config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); | 105 | config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); |
| 106 | - config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); | ||
| 107 | - config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); | ||
| 108 | return config; | 106 | return config; |
| 109 | } | 107 | } |
| 110 | 108 |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java
| @@ -58,7 +58,7 @@ public class WxNativePayClientIntegrationTest { | @@ -58,7 +58,7 @@ public class WxNativePayClientIntegrationTest { | ||
| 58 | 58 | ||
| 59 | // 执行发起退款 | 59 | // 执行发起退款 |
| 60 | WxPayRefundV3Request request = new WxPayRefundV3Request() | 60 | WxPayRefundV3Request request = new WxPayRefundV3Request() |
| 61 | - .setOutTradeNo("1689506153043") | 61 | + .setOutTradeNo("1689545729695") |
| 62 | .setOutRefundNo(String.valueOf(System.currentTimeMillis())) | 62 | .setOutRefundNo(String.valueOf(System.currentTimeMillis())) |
| 63 | .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY")) | 63 | .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY")) |
| 64 | .setReason("就是想退了"); | 64 | .setReason("就是想退了"); |
| @@ -73,10 +73,8 @@ public class WxNativePayClientIntegrationTest { | @@ -73,10 +73,8 @@ public class WxNativePayClientIntegrationTest { | ||
| 73 | WxPayConfig config = new WxPayConfig(); | 73 | WxPayConfig config = new WxPayConfig(); |
| 74 | config.setAppId("wx62056c0d5e8db250"); | 74 | config.setAppId("wx62056c0d5e8db250"); |
| 75 | config.setMchId("1545083881"); | 75 | config.setMchId("1545083881"); |
| 76 | - config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); | ||
| 77 | config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4"); | 76 | config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4"); |
| 78 | // config.setCertSerialNo(serialNo); | 77 | // config.setCertSerialNo(serialNo); |
| 79 | - config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); | ||
| 80 | config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); | 78 | config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); |
| 81 | config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); | 79 | config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); |
| 82 | return config; | 80 | return config; |
yudao-ui-admin/src/views/pay/app/components/wechatChannelForm.vue
| @@ -8,8 +8,8 @@ | @@ -8,8 +8,8 @@ | ||
| 8 | <template slot="append">%</template> | 8 | <template slot="append">%</template> |
| 9 | </el-input> | 9 | </el-input> |
| 10 | </el-form-item> | 10 | </el-form-item> |
| 11 | - <el-form-item label-width="180px" label="公众号APPID" prop="weChatConfig.appId"> | ||
| 12 | - <el-input v-model="form.weChatConfig.appId" placeholder="请输入公众号APPID" clearable :style="{width: '100%'}"> | 11 | + <el-form-item label-width="180px" label="公众号 APPID" prop="weChatConfig.appId"> |
| 12 | + <el-input v-model="form.weChatConfig.appId" placeholder="请输入公众号 APPID" clearable :style="{width: '100%'}"> | ||
| 13 | </el-input> | 13 | </el-input> |
| 14 | </el-form-item> | 14 | </el-form-item> |
| 15 | <el-form-item label-width="180px" label="商户号" prop="weChatConfig.mchId"> | 15 | <el-form-item label-width="180px" label="商户号" prop="weChatConfig.mchId"> |
| @@ -29,29 +29,41 @@ | @@ -29,29 +29,41 @@ | ||
| 29 | </el-radio> | 29 | </el-radio> |
| 30 | </el-radio-group> | 30 | </el-radio-group> |
| 31 | </el-form-item> | 31 | </el-form-item> |
| 32 | - <el-form-item label-width="180px" label="商户密钥" prop="weChatConfig.mchKey" | ||
| 33 | - v-if="form.weChatConfig.apiVersion === 'v2'"> | ||
| 34 | - <el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户密钥" clearable | ||
| 35 | - :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input> | ||
| 36 | - </el-form-item> | 32 | + <div v-if="form.weChatConfig.apiVersion === 'v2'"> |
| 33 | + <el-form-item label-width="180px" label="商户密钥" prop="weChatConfig.mchKey"> | ||
| 34 | + <el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户密钥" clearable | ||
| 35 | + :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input> | ||
| 36 | + </el-form-item> | ||
| 37 | + <el-form-item label-width="180px" label="apiclient_cert.p12 证书" prop="weChatConfig.keyContent"> | ||
| 38 | + <el-input v-model="form.weChatConfig.keyContent" type="textarea" | ||
| 39 | + placeholder="请上传 apiclient_cert.p12 证书" | ||
| 40 | + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input> | ||
| 41 | + </el-form-item> | ||
| 42 | + <el-form-item label-width="180px" label=""> | ||
| 43 | + <el-upload :limit="1" accept=".p12" action="" | ||
| 44 | + :before-upload="p12FileBeforeUpload" | ||
| 45 | + :http-request="keyContentUpload"> | ||
| 46 | + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||
| 47 | + </el-upload> | ||
| 48 | + </el-form-item> | ||
| 49 | + </div> | ||
| 37 | <div v-if="form.weChatConfig.apiVersion === 'v3'"> | 50 | <div v-if="form.weChatConfig.apiVersion === 'v3'"> |
| 38 | - <el-form-item label-width="180px" label="API V3密钥" prop="weChatConfig.apiV3Key"> | ||
| 39 | - <el-input v-model="form.weChatConfig.apiV3Key" placeholder="请输入API V3密钥" clearable | 51 | + <el-form-item label-width="180px" label="API V3 密钥" prop="weChatConfig.apiV3Key"> |
| 52 | + <el-input v-model="form.weChatConfig.apiV3Key" placeholder="请输入 API V3 密钥" clearable | ||
| 40 | :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input> | 53 | :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input> |
| 41 | </el-form-item> | 54 | </el-form-item> |
| 42 | - <el-form-item label-width="180px" label="apiclient_key.perm证书" prop="weChatConfig.privateKeyContent"> | 55 | + <el-form-item label-width="180px" label="apiclient_key.perm 证书" prop="weChatConfig.privateKeyContent"> |
| 43 | <el-input v-model="form.weChatConfig.privateKeyContent" type="textarea" | 56 | <el-input v-model="form.weChatConfig.privateKeyContent" type="textarea" |
| 44 | - placeholder="请上传apiclient_key.perm证书" | 57 | + placeholder="请上传 apiclient_key.perm 证书" |
| 45 | readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input> | 58 | readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input> |
| 46 | </el-form-item> | 59 | </el-form-item> |
| 47 | <el-form-item label-width="180px" label="" prop="privateKeyContentFile"> | 60 | <el-form-item label-width="180px" label="" prop="privateKeyContentFile"> |
| 48 | <el-upload ref="privateKeyContentFile" | 61 | <el-upload ref="privateKeyContentFile" |
| 49 | :limit="1" | 62 | :limit="1" |
| 50 | - :accept="fileAccept" | ||
| 51 | - :headers="header" | 63 | + accept=".pem" |
| 52 | action="" | 64 | action="" |
| 53 | :before-upload="pemFileBeforeUpload" | 65 | :before-upload="pemFileBeforeUpload" |
| 54 | - :http-request="privateKeyUpload" | 66 | + :http-request="privateKeyContentUpload" |
| 55 | > | 67 | > |
| 56 | <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | 68 | <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> |
| 57 | </el-upload> | 69 | </el-upload> |
| @@ -64,18 +76,17 @@ | @@ -64,18 +76,17 @@ | ||
| 64 | <el-form-item label-width="180px" label="" prop="privateCertContentFile"> | 76 | <el-form-item label-width="180px" label="" prop="privateCertContentFile"> |
| 65 | <el-upload ref="privateCertContentFile" | 77 | <el-upload ref="privateCertContentFile" |
| 66 | :limit="1" | 78 | :limit="1" |
| 67 | - :accept="fileAccept" | ||
| 68 | - :headers="header" | 79 | + accept=".pem" |
| 69 | action="" | 80 | action="" |
| 70 | :before-upload="pemFileBeforeUpload" | 81 | :before-upload="pemFileBeforeUpload" |
| 71 | - :http-request="privateCertUpload" | 82 | + :http-request="privateCertContentUpload" |
| 72 | > | 83 | > |
| 73 | <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | 84 | <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> |
| 74 | </el-upload> | 85 | </el-upload> |
| 75 | </el-form-item> | 86 | </el-form-item> |
| 76 | </div> | 87 | </div> |
| 77 | <el-form-item label-width="180px" label="备注" prop="remark"> | 88 | <el-form-item label-width="180px" label="备注" prop="remark"> |
| 78 | - <el-input v-model="form.remark" :style="{width: '100%'}"></el-input> | 89 | + <el-input v-model="form.remark" :style="{width: '100%'}" /> |
| 79 | </el-form-item> | 90 | </el-form-item> |
| 80 | </el-form> | 91 | </el-form> |
| 81 | <div slot="footer" class="dialog-footer"> | 92 | <div slot="footer" class="dialog-footer"> |
| @@ -100,6 +111,7 @@ const defaultForm = { | @@ -100,6 +111,7 @@ const defaultForm = { | ||
| 100 | mchId: '', | 111 | mchId: '', |
| 101 | apiVersion: '', | 112 | apiVersion: '', |
| 102 | mchKey: '', | 113 | mchKey: '', |
| 114 | + keyContent: '', | ||
| 103 | privateKeyContent: '', | 115 | privateKeyContent: '', |
| 104 | privateCertContent: '', | 116 | privateCertContent: '', |
| 105 | apiV3Key:'', | 117 | apiV3Key:'', |
| @@ -159,27 +171,27 @@ export default { | @@ -159,27 +171,27 @@ export default { | ||
| 159 | message: '请输入商户密钥', | 171 | message: '请输入商户密钥', |
| 160 | trigger: 'blur' | 172 | trigger: 'blur' |
| 161 | }], | 173 | }], |
| 174 | + 'weChatConfig.keyContent': [{ | ||
| 175 | + required: true, | ||
| 176 | + message: '请上传 apiclient_cert.p12 证书', | ||
| 177 | + trigger: 'blur' | ||
| 178 | + }], | ||
| 162 | 'weChatConfig.privateKeyContent': [{ | 179 | 'weChatConfig.privateKeyContent': [{ |
| 163 | required: true, | 180 | required: true, |
| 164 | - message: '请上传apiclient_key.perm证书', | 181 | + message: '请上传 apiclient_key.perm 证书', |
| 165 | trigger: 'blur' | 182 | trigger: 'blur' |
| 166 | }], | 183 | }], |
| 167 | 'weChatConfig.privateCertContent': [{ | 184 | 'weChatConfig.privateCertContent': [{ |
| 168 | required: true, | 185 | required: true, |
| 169 | - message: '请上传apiclient_cert.perm证书', | 186 | + message: '请上传 apiclient_cert.perm证 书', |
| 170 | trigger: 'blur' | 187 | trigger: 'blur' |
| 171 | }], | 188 | }], |
| 172 | 'weChatConfig.apiV3Key': [{ | 189 | 'weChatConfig.apiV3Key': [{ |
| 173 | required: true, | 190 | required: true, |
| 174 | - message: '请上传apiV3密钥值', | 191 | + message: '请上传 api V3 密钥值', |
| 175 | trigger: 'blur' | 192 | trigger: 'blur' |
| 176 | }], | 193 | }], |
| 177 | }, | 194 | }, |
| 178 | - // 文件上传的header | ||
| 179 | - header: { | ||
| 180 | - "Authorization": null | ||
| 181 | - }, | ||
| 182 | - fileAccept: ".pem", | ||
| 183 | // 渠道状态 数据字典 | 195 | // 渠道状态 数据字典 |
| 184 | statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS), | 196 | statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS), |
| 185 | versionDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_WECHAT_VERSION), | 197 | versionDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_WECHAT_VERSION), |
| @@ -218,6 +230,7 @@ export default { | @@ -218,6 +230,7 @@ export default { | ||
| 218 | this.form.weChatConfig.apiVersion = config.apiVersion; | 230 | this.form.weChatConfig.apiVersion = config.apiVersion; |
| 219 | this.form.weChatConfig.mchId = config.mchId; | 231 | this.form.weChatConfig.mchId = config.mchId; |
| 220 | this.form.weChatConfig.mchKey = config.mchKey; | 232 | this.form.weChatConfig.mchKey = config.mchKey; |
| 233 | + this.form.weChatConfig.keyContent = config.keyContent; | ||
| 221 | this.form.weChatConfig.privateKeyContent = config.privateKeyContent; | 234 | this.form.weChatConfig.privateKeyContent = config.privateKeyContent; |
| 222 | this.form.weChatConfig.privateCertContent = config.privateCertContent; | 235 | this.form.weChatConfig.privateCertContent = config.privateCertContent; |
| 223 | this.form.weChatConfig.apiV3Key = config.apiV3Key; | 236 | this.form.weChatConfig.apiV3Key = config.apiV3Key; |
| @@ -241,7 +254,6 @@ export default { | @@ -241,7 +254,6 @@ export default { | ||
| 241 | this.$modal.msgSuccess("修改成功"); | 254 | this.$modal.msgSuccess("修改成功"); |
| 242 | this.close(); | 255 | this.close(); |
| 243 | } | 256 | } |
| 244 | - | ||
| 245 | }) | 257 | }) |
| 246 | } else { | 258 | } else { |
| 247 | 259 | ||
| @@ -255,10 +267,14 @@ export default { | @@ -255,10 +267,14 @@ export default { | ||
| 255 | } | 267 | } |
| 256 | }); | 268 | }); |
| 257 | }, | 269 | }, |
| 258 | - pemFileBeforeUpload(file) { | 270 | + /** |
| 271 | + * apiclient_cert.p12、apiclient_cert.pem、apiclient_key.pem 上传前的校验 | ||
| 272 | + */ | ||
| 273 | + fileBeforeUpload(file, fileAccept) { | ||
| 259 | let format = '.' + file.name.split(".")[1]; | 274 | let format = '.' + file.name.split(".")[1]; |
| 260 | - if (format !== this.fileAccept) { | ||
| 261 | - this.$message.error('请上传指定格式"' + this.fileAccept + '"文件'); | 275 | + if (format !== fileAccept) { |
| 276 | + debugger | ||
| 277 | + this.$message.error('请上传指定格式"' + fileAccept + '"文件'); | ||
| 262 | return false; | 278 | return false; |
| 263 | } | 279 | } |
| 264 | let isRightSize = file.size / 1024 / 1024 < 2 | 280 | let isRightSize = file.size / 1024 / 1024 < 2 |
| @@ -267,19 +283,41 @@ export default { | @@ -267,19 +283,41 @@ export default { | ||
| 267 | } | 283 | } |
| 268 | return isRightSize | 284 | return isRightSize |
| 269 | }, | 285 | }, |
| 270 | - privateKeyUpload(event) { | 286 | + p12FileBeforeUpload(file) { |
| 287 | + this.fileBeforeUpload(file, '.p12') | ||
| 288 | + }, | ||
| 289 | + pemFileBeforeUpload(file) { | ||
| 290 | + this.fileBeforeUpload(file, '.pem') | ||
| 291 | + }, | ||
| 292 | + /** | ||
| 293 | + * 读取 apiclient_key.pem 到 privateKeyContent 字段 | ||
| 294 | + */ | ||
| 295 | + privateKeyContentUpload(event) { | ||
| 271 | const readFile = new FileReader() | 296 | const readFile = new FileReader() |
| 272 | readFile.onload = (e) => { | 297 | readFile.onload = (e) => { |
| 273 | this.form.weChatConfig.privateKeyContent = e.target.result | 298 | this.form.weChatConfig.privateKeyContent = e.target.result |
| 274 | } | 299 | } |
| 275 | readFile.readAsText(event.file); | 300 | readFile.readAsText(event.file); |
| 276 | }, | 301 | }, |
| 277 | - privateCertUpload(event) { | 302 | + /** |
| 303 | + * 读取 apiclient_cert.pem 到 privateCertContent 字段 | ||
| 304 | + */ | ||
| 305 | + privateCertContentUpload(event) { | ||
| 278 | const readFile = new FileReader() | 306 | const readFile = new FileReader() |
| 279 | readFile.onload = (e) => { | 307 | readFile.onload = (e) => { |
| 280 | this.form.weChatConfig.privateCertContent = e.target.result | 308 | this.form.weChatConfig.privateCertContent = e.target.result |
| 281 | } | 309 | } |
| 282 | readFile.readAsText(event.file); | 310 | readFile.readAsText(event.file); |
| 311 | + }, | ||
| 312 | + /** | ||
| 313 | + * 读取 apiclient_cert.p12 到 keyContent 字段 | ||
| 314 | + */ | ||
| 315 | + keyContentUpload(event) { | ||
| 316 | + const readFile = new FileReader() | ||
| 317 | + readFile.onload = (e) => { | ||
| 318 | + this.form.weChatConfig.keyContent = e.target.result.split(',')[1] | ||
| 319 | + } | ||
| 320 | + readFile.readAsDataURL(event.file); // 读成 base64 | ||
| 283 | } | 321 | } |
| 284 | } | 322 | } |
| 285 | } | 323 | } |
yudao-ui-admin/src/views/pay/app/index.vue
| @@ -143,6 +143,30 @@ | @@ -143,6 +143,30 @@ | ||
| 143 | </el-button> | 143 | </el-button> |
| 144 | </template> | 144 | </template> |
| 145 | </el-table-column> | 145 | </el-table-column> |
| 146 | + <el-table-column :label="payChannelEnum.WX_NATIVE.name" align="center"> | ||
| 147 | + <template v-slot="scope"> | ||
| 148 | + <el-button type="success" icon="el-icon-check" circle | ||
| 149 | + v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_NATIVE.code)" | ||
| 150 | + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_NATIVE.code,payType.WECHAT)"> | ||
| 151 | + </el-button> | ||
| 152 | + <el-button v-else | ||
| 153 | + type="danger" icon="el-icon-close" circle | ||
| 154 | + @click="handleCreateChannel(scope.row,payChannelEnum.WX_NATIVE.code,payType.WECHAT)"> | ||
| 155 | + </el-button> | ||
| 156 | + </template> | ||
| 157 | + </el-table-column> | ||
| 158 | + <el-table-column :label="payChannelEnum.WX_BAR.name" align="center"> | ||
| 159 | + <template v-slot="scope"> | ||
| 160 | + <el-button type="success" icon="el-icon-check" circle | ||
| 161 | + v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_BAR.code)" | ||
| 162 | + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_BAR.code,payType.WECHAT)"> | ||
| 163 | + </el-button> | ||
| 164 | + <el-button v-else | ||
| 165 | + type="danger" icon="el-icon-close" circle | ||
| 166 | + @click="handleCreateChannel(scope.row,payChannelEnum.WX_BAR.code,payType.WECHAT)"> | ||
| 167 | + </el-button> | ||
| 168 | + </template> | ||
| 169 | + </el-table-column> | ||
| 146 | </el-table-column> | 170 | </el-table-column> |
| 147 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | 171 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 148 | <template v-slot="scope"> | 172 | <template v-slot="scope"> |